-- BMW E30 (c) William Corredor - wcorredor -- 1.0 -- 2016-11-1 -- RPM -- 1.1 -- 2016-11-27 -- Shift Lights -- 1.2 -- 2016-12-04 -- Alarm LEDs -- Oil, Coolant, Gear, Battery, Fuel -- 1.3 -- 2016-12-06 -- Auto-Intensity with time of day -- 1.4 -- 2016-12-22 -- Blinking by DutyCicle not RPM -- 1.5 -- 2017-01/08 -- Fuel Sensor with average -- 1.6 -- 2017-01-10 -- Max Speed -- 1.7 -- 2017-01-20 -- Max RPM -- 2.0 2017-01-22 -- New Clean Code -- Add types to variable names: -- Modulo Tick for not running everything everytick -- Smoother FuelAvg, not every tick -- Total Distance -- 2.1 2017-01-26 -- ml = miles -- mn = mile number -- gl = gallons -- gr = gear -- tm = time -- id = Chanell id -- fq = Frequency -- cn = Counter -- dc = Duty Cycle -- tk = Tick Counter -- lt = Light Intensity -- bl = Boolean -- hr = hour -- mt = Minutes -- sc = Seconds -- al = Alarm -- fl = Fuel Level -- i = array index -- sm = Sum -- lp = Lap -- Fuel Rates -- Laps Left with curent fuel -- Fuel estimation when on Reserve -- 2.2 2017-02-19 -- Miles per Lap -- MPGLastMile idGr = addChannel("Gear", 5, 0, 0, 5) idFl = addChannel("AvgFuel", 1, 1, 0,100,"%") idSpMax = addChannel("MaxSpeed", 5, 1, 0,150) idRpMax = addChannel("MaxRPM", 5, 0, 0,7000) idTime = addChannel("Time", 1, 4, 0, 24) idMl = addChannel("Miles", 5, 3, 0, 1000) idMPG10 = addChannel("MPG10", 1, 2, 0, 20) idMPGAvg = addChannel("MPGAvg", 1, 2, 0, 20) idLPG = addChannel("LapsGallon", 1, 2, 0, 10) idLpLeft = addChannel("LapsLeft", 1, 2, 0, 100) idGlLeft = addChannel("GallonsLeft", 1, 2, 1, 17) idMlLap = addChannel("Miles per Lap", 1, 3, 1, 20) idMPGLast = addChannel("MPGLastMile", 1, 2, 0, 20) --Blinks at Freq(fq) with Dutycycle(dc) on Channel(ch) with intensity(lt) function Blink(fq, dc, ch, lt) local RATE = 25 local OFF = 5 local HGH = 0 local LOW = 4.5 if fq <= 0 or dc <= 0 or lt == OFF then setAnalogOut(ch, OFF) cnOn[ch+1] = 0 cnOff[ch+1] = 1 return end if dc >= 1 then setAnalogOut(ch, lt) cnOn[ch+1] = 1 cnOff[ch+1] = 0 return end --Ticks on from Duty Cycle local tkOn = RATE * dc / fq if cnOn[ch+1] > 0 then if cnOn[ch+1] < tkOn then cnOn[ch+1] = cnOn[ch+1] + 1 else cnOn[ch+1] = 0 setAnalogOut(ch, OFF) cnOff[ch+1] = 1 end return end --Ticks off from Duty Cycle local tkOff = RATE * (1-dc)/fq if cnOff[ch+1] > 0 then if cnOff[ch+1] < tkOff then cnOff[ch+1] = cnOff[ch+1] + 1 else cnOff[ch+1] = 0 setAnalogOut(ch, lt) cnOn[ch+1] = 1 end end end function Log() local OFF = 5 local HGH = 0 local LOW = 4.5 local GRN = 0 local YLW = 1 local RED = 2 if spGPS > 5 then tkStop = 0 if blNew then startLogging() spMax = 0 rpMax = 0 blNew = false Blink(0, 0, GRN, OFF) Blink(0, 0, YLW, OFF) Blink(0, 0, RED, OFF) end else tkStop = tkStop + 1 if tkStop > 20 * RATE then -- prevents new sessions in stops less than 20 secs blNew = true stopLogging() end end end function Time() local tm, y, m, d, hr, mt, sc local RATE = 25 local OFF = 5 local HGH = 0 local LOW = 4.5 if tk % RATE ~= 0 then -- only every second return end y, m, d, hr, mt, sc = getDateTime() -- adjust UTC to time zone hr = hr -5 if hr < 0 then hr = 24 + hr end tm = hr + mt/100 +sc/10000 -- Channel time of day in HH.MMSS format setChannel(idTime, tm) if tk % (60*RATE) ~= 0 then --only every minute return end -- lower LEDs intensity at night if (hr < 7 or hr > 17) then ltInt = LOW else ltInt = HGH end end -- Use shiftX for both alarms and RPM function Lghts() -- Analog Ins local TEMP = 1 local OIL = 2 local BAT = 7 local alTemp, alOil, alBat, alFl local highTEMP = 98 local lowOIL = 10 local lowBAT = 10 local lowFUEL = 5 --RPM Limits local GrnLOW = 4500 local YlwLOW = 5250 local RedLOW = 6000 local RedHGH = 6500 local OFF = 5 local HGH = 0 local LOW = 4.5 local GRN = 0 local YLW = 1 local RED = 2 alTemp = (getAnalog(TEMP) > highTEMP) alOil = (getAnalog(OIL) < lowOIL) alBat = (getAnalog(BAT) < lowBAT) alFl = (getChannel(idFl) < lowFUEL) if alOil then Blink(3, 0.5, RED, ltInt) end if alTemp then Blink(3, 0.5, YLW, ltInt) end if alOil or alTpm then return end if rpCar > GrnLOW then Blink(2, (rpCar - GrnLOW)/(YlwLOW - GrnLOW), GRN, ltInt) Blink(2, (rpCar - YlwLOW)/(RedLOW - YlwLOW), YLW, ltInt) Blink(2, (rpCar - RedLOW)/(RedHGH - RedLOW), RED, ltInt) else Blink(0, 0, GRN, OFF) Blink(0, 0, YLW, OFF) Blink(0, 0, RED, OFF) end if alBat or alFl then Blink(3, 0.5, GRN, ltInt) end end function Fuel() local i, fl, flLev local lpg, ml, lp local iGAS = 250 local iMPG = 10 local FUEL = 6 local glTANK = 16.64 -- tank size of BMW E30 1989 local flRES = 25 -- near bottom of sensor % local mpgInit = 7 -- aprox mpg for car function round(nu, decimals) return(math.floor (nu + 5 * 1 / 10^decimals)) end if #mpgAvg == 0 then for i = 1, iMPG do mpgAvg[i]= mpg end end if #flAvg == 0 then fl = getAnalog(FUEL) for i = 1, iGAS do flAvg[i]=fl end glPreMile = glTANK * (fl)/100 glLeft = glTANK * (fl)/100 mpg10 = mpgInit mpg = mpgInit setChannel(idMPG10, mpg10) setChannel(idMPGAvg, mpg) end -- average iGAS fuel samples every 2 tks if tk % 2 == 0 then fl = getAnalog(FUEL) flAvg[iFl] = fl iFl = iFl + 1 if iFl > iGAS then iFl = 1 end local sm = 0 for i = 1, iGAS do sm = sm + flAvg[i] end flLev = sm / iGAS -- flLev before reserve from sensor if flLev > flRES then blFlRes = false glLeft = glTANK * (flLev)/100 else -- after reserve from Gallons left flLev = glLeft / glTANK * 100 blFlRes = true end setChannel(idFl, flLev) setChannel(idGlLeft, glLeft) end --Calculates Total Distance adding Lap length, lpg and laps left with fuel lp = getLapCount() ml = getGpsDist() if lp > lpPre then -- new lap mlTot = mlTot + ml + mlDelta --Adjust distance of last tick setChannel(idMlLap, mlPreTick +mlDelta ) -- lap length lpg = mpg / mlPreTick setChannel(idLPG, lpg) setChannel(idLpLeft, glLeft*lpg) lpPre = lp else mlDelta = ml - mlPreTick mlTot = mlTot + mlDelta end setChannel(idMl, mlTot) -- Total Distance mlPreTick = ml -- Every mile calculates mpg from fuel used last iMPG miles if (round(mlTot, 10) > round(mlPreMile, 10)) then -- new mile local mlExact = mlTot - mlPreMile if blFlRes then glLeft = glLeft - mlExact / mpg -- on reserve subtrack used gallons in 1 mile end local glMile = glPreMile - glLeft local mpgMile = mpg if glMile > 0 and not blFlRes then mpgMile = mlExact / glMile end setChannel(idMPGLast, mpgMile) if mpgMile < (mpgInit / 2) or mpgMile > (mpgInit * 2) then -- ignore crazy values mpgMile = mpg end mpgAvg[iGl] = mpgMile glPre[iGl] = glLeft iGl = iGl + 1 if iGl > iMPG then iGl = 1 end glPreMile = glLeft local sm = 0 for i = 1, iMPG do sm = sm + mpgAvg[i] end mpg = sm / iMPG setChannel(idMPGAvg, mpg) mlPreMile = mlTot if (round(mlTot,10) < iMPG) then -- mpg10 only after iMPG miles return end glDelta = glPre[iGlPre] - glLeft -- Gallons used last iMPG-1 miles if glDelta > 0 and not blFlRes then mpg10 = (iMPG-1) / glDelta end if mpg10 < (mpgInit / 2) or mpg10 > (mpgInit * 2) then -- ignore crazy values mpg10 = mpg end setChannel(idMPG10, mpg10) iGlPre = iGlPre + 1 if iGlPre > iMPG then iGlPre = 1 end end end -- Init -- setTickRate(25) setPwmClockFreq(320) cnOn = {0, 0, 0} cnOff = {1, 1, 1} flAvg={} iFl = 1 glPre={} iGl = 1 iGlPre = 1 glLeft = 0 glPreMile = 0 mpgAvg={} tk = 0 tkStop = 0 ltInt = HGH blNew = true mlPreMile = 0 mlPreTick = 0 mlDelta = 0 mlTot = 0 lpPre = getLapCount() spMax = 0 rpMax = 0 -- RUN -- function onTick() local gr local grMIN = 1 local grMAX = 5 local grRATIO = 263 tk = tk + 1 rpCar = getTimerRpm(0) spGPS = getGpsSpeed() Log() Time() if spGPS > spMax then spMax = spGPS setChannel(idSpMax, spMax) end if rpCar > rpMax then rpMax = rpCar setChannel(idRpMax, rpMax) end gr = math.floor(spGPS/ rpCar * grRATIO) if gr < grMIN or gr > grMAX then gr = 0 end setChannel(idGr, gr) Fuel() Lghts() end