RegisterSearchFAQMemberlistUsergroupsLog in
Reply to topic Page 1 of 1
Help with script optimization
Author Message
Reply with quote
Post Help with script optimization 
Hello,

First, what is the LUA memory limit (fw 2.13.5) and how can i measure the utilization of my script in order to compare? Is the memory usage a realtime memory thing or more of a storage thing? I ask because LUA optimizers seems to simply remove whitespace and comments.

This script does a few things:
1. Runs the ShiftX2 as a shift light and the two alarm LEDs
2. Runs functions to calculate my speed based on my diff speed sensor (E36) and gear
3. Sends data out on the CANBUS emulating an Haltech ECU. This goes to my Aim Solo 2 DL.

I run the script through this LUA minifier to shrink it a bit: https://mothereff.in/lua-minifier

About half the time this runs fine, but the other half the ShiftX2 doesn't work properly and goes into the multi-color demo mode. When this happens i have to either cycle power or rerun the scrip in the RaceCapture app to get it to work right again. I found this happens when i'm close to the memory limit as deleting things will fix it.

The other issue is that i get steady txCAN fails (from sendCAN() function). I'm not sure if that's a problem or not, it generally seems to work and the Solo 2DL reads ok.

Any suggestions on how to optimize this code and/or make it more robust would be much appreciated!

Thanks,
Dan


Code:

setTickRate(50) --50Hz
initCAN(0, 1000000) -- CANBUS 1, 1mbps, for sending data to Solo2DL

-- What CAN bus ShiftX2 is connected to. 0=CAN1, 1=CAN2
sxCan = 1
-- 0=first ShiftX2 on bus, 1=second ShiftX2 (if ADR1 jumper is cut)
sxId=0
--Brightness, 0-100. 0=automatic brightness
sxBright=0
sxBrightScale = 45 --0-255 default is 51

sxCanId = 0xE3600 + (256 * sxId)
println('shiftx2 base id ' ..sxCanId)

--virtual channels
--addChannel("name",SR,prec,min,max,"unit")
speeddiff_id = addChannel("Speed_",10,0,0,160,"MPH")
gear_id = addChannel("Gear",5,0,0,5,"gear")

--global constants
first = 4.20
second = 2.49
third = 1.66
fourth = 1.24
fifth = 1.00
final = 3.46
tirediameter = 24.7

--global variables
rpm = 0 -- engine speed
rpm_diff = 0 --diff speed sensor in E36
speed = 0
gear = 0

function updateSpeedDiff()
   rpm_diff = getTimerRpm(1)
   speed = rpm_diff*tirediameter*0.002975
   speed = speed + 0.5 -- round because 0 prec. truncates
   setChannel(speeddiff_id, speed)
end

function updateGear()
   rpm = getTimerRpm(0)
   local gearErr = 0.15
   gear = 0
   
   if speed > 2 then
      local ratio = rpm/(rpm_diff*final)
      if ((first  - ratio)^2) < (gearErr^2) then gear = 1 end
      if ((second - ratio)^2) < (gearErr^2) then gear = 2 end
      if ((third  - ratio)^2) < (gearErr^2) then gear = 3 end
      if ((fourth - ratio)^2) < (gearErr^2) then gear = 4 end
      if ((fifth  - ratio)^2) < (gearErr^2) then gear = 5 end
   end
   setChannel(gear_id, gear)
end

function sendCAN(can, id, data)
   local res = txCAN(can, id, 0, data,100)
   if res == 0 then println('txCAN failed') end
end

function sendHaltech()
   local rpm = getTimerRpm(0)
   local tps = getAnalog(3)*10 --convert % to 0.10% units
   local op = getAnalog(2)*6.894757*10 --convert psi to 0.1 kPa units
   local ot = (((getAnalog(1) - 32)*5)/9 + 273.15)*10 --convert F to 0.1Kelvin units
   local wt = (((getAnalog(0) - 32)*5)/9 + 273.15)*10 --convert F to 0.1Kelvin units
   local sped = speed*1.609344*10 --convert mph to 0.1km/h units
   
   local id = 864 --RPM, manifold pressure, TPS, coolant pressure
   local data = {spu(rpm), spl(rpm), 0, 0, spu(tps), spl(tps), 0, 0}
   local res = sendCAN(0, id, data)

   id = 865 --fuel pressure, oil pressure, accel pedal pos, wastegate pressure
   data = {0, 0, spu(op), spl(op), 0, 0, 0, 0}
   res = sendCAN(0, id, data)
   
   id = 880 --wheel speed gen., gear,
   data = {spu(sped), spl(sped), spu(gear), spl(gear), 0, 0, 0, 0}
   res = sendCAN(0, id, data)
   
   id = 992 --coolant temp, air temp, fuel temp, oil temp
   data = {spu(wt), spl(wt), 0, 0, 0, 0, spu(ot), spl(ot)}
   res = sendCAN(0, id, data)

end


function sxOnUpdate()
   sxUpdateLinearGraph(getTimerRpm(0)) --RPM
   sxUpdateAlert(0, getAnalog(0)) --water temp
   sxUpdateAlert(1, getAnalog(2)) --oil pressure
end


function sxOnInit()
  --config shift light
  sxCfgLinearGraph(0,0,0,7000) --left to right graph, linear style, 0 - 7000 RPM range

  sxSetLinearThresh(0,0,5000,0,255,0,0) --green at 5000 RPM
  sxSetLinearThresh(1,0,6000,255,255,0,0) --yellow at 6000 RPM
  sxSetLinearThresh(2,0,6500,255,0,0,0) --red  at 6500 RPM
  sxSetLinearThresh(3,0,6800,255,0,0,10) --red+flash at 6800 RPM
 
  --configure first alert (right LED) as engine temperature (F)
  sxSetAlertThresh(0,0,210,255,255,0,0) --yellow warning at 205F
  sxSetAlertThresh(0,1,225,255,0,0,10) -- red flash at 225F

  --configure second alert (left LED) as oil pressure (PSI)
  sxSetAlertThresh(1,0,0,255,0,0,10) --red flash below 10 psi
  sxSetAlertThresh(1,1,10,255,255,0,0) --yellow 10-20 PSI
  sxSetAlertThresh(1,2,20,0,0,0,0) --above 20, no alert
end


---ShiftX2 functions
function sxSetLinearThresh(id,s,th,r,g,b,f)
  sxTx(41,{id,s,spl(th),spu(th),r,g,b,f})
end

function sxSetAlertThresh(id,tid,th,r,g,b,f)
  sxTx(21,{id,tid,spl(th),spu(th),r,g,b,f})
end

function setBaseConfig(bright, brightscale)
  sxTx(3,{bright, brightscale})
end

function sxUpdateAlert(id,v)
  if v~=nil then sxTx(22,{id,spl(v),spu(v)}) end
end

function sxCfgLinearGraph(rs,ls,lr,hr)
  sxTx(40,{rs,ls,spl(lr),spu(lr),spl(hr),spu(hr)})
end

function sxUpdateLinearGraph(v)
  if v ~= nil then sxTx(42,{spl(v),spu(v)}) end
end

function sxInit()
  println('config shiftX2')
  setBaseConfig(sxBright,sxBrightScale)
  if sxOnInit~=nil then sxOnInit() end
end

function sxChkCan()
  id,ext,data=rxCAN(sxCan,0)
  if id==sxCanId then sxInit() end
  if id==sxCanId+60 and sxOnBut~=nil then sxOnBut(data[1]) end
end

function sxProcess()
  sxChkCan()
  if sxOnUpdate~=nil then sxOnUpdate() end
end

function sxTx(offset, data)
  txCAN(sxCan, sxCanId + offset, 1, data)
end

function spl(v) return bit.band(v,0xFF) end
function spu(v) return bit.rshift(bit.band(v,0xFF00),8) end

function onTick()
  updateSpeedDiff()
  updateGear()
  sendHaltech()
  sxProcess()
end

sxInit()


View user's profile Send private message
Reply with quote
Post  
Interesting questions, hopefully you get a reply about memory usage and how much the optimizer helps.

Looking at the script, I suspect the printlns adds overhead that you can probably live without. I doubt the ones that it only does once matter, but there is one in sendCAN that would print many times.

Also, you are going 50x normal speed. My script has a thing where it runs at 10 hertz but only does warning lights every second. Maybe throw a counter and if in there to do something like


tickCount = 0
setTickRate(50)
function onTick()
if tickCount % 50 == 0 then
-- do stuff that should only be done once per second
tickCount = 0
end
tickCount = tickCount + 1
end

View user's profile Send private message
Reply with quote
Post  
Interestingly the Haltech CANBUS protocol sends data at different rates. So using the modulus method, i made it so different functions are updated at either 50, 20, 10, or 5hz so that i could match the Haltech protocol and also put other things in more sensible update rate bins.

This seems to work, except i get Out of Memory errors in the LUA scripting window and then the CANBUS crashes.

Code:

setTickRate(100) --100Hz
tick = 0
initCAN(0, 1000000) -- CANBUS 1, 1mbps, for sending data to Solo2DL

-- What CAN bus ShiftX2 is connected to. 0=CAN1, 1=CAN2
sxCan = 1
-- 0=first ShiftX2 on bus, 1=second ShiftX2 (if ADR1 jumper is cut)
sxId=0
--Brightness, 0-100. 0=automatic brightness
sxBright=0
sxBrightScale = 45 --0-255 default is 51

sxCanId = 0xE3600 + (256 * sxId)
println('shiftx2 base id ' ..sxCanId)

--virtual channels
--addChannel("name",SR,prec,min,max,"unit")
speeddiff_id = addChannel("Speed_",10,0,0,160,"MPH")
gear_id = addChannel("Gear",5,0,0,5,"gear")

--global constants
first = 4.20
second = 2.49
third = 1.66
fourth = 1.24
fifth = 1.00
final = 3.46
tirediameter = 24.7

--global variables
rpm = 0 -- engine speed
rpm_diff = 0 --diff speed sensor in E36
speed = 0
gear = 0

function updateSpeedDiff()
   rpm_diff = getTimerRpm(1)
   speed = rpm_diff*tirediameter*0.002975
   speed = speed + 0.5 -- round because 0 prec. truncates
   setChannel(speeddiff_id, speed)
end

function updateGear()
   rpm = getTimerRpm(0)
   local gearErr = 0.15
   gear = 0
   
   if speed > 2 then
      local ratio = rpm/(rpm_diff*final)
      if ((first  - ratio)^2) < (gearErr^2) then gear = 1 end
      if ((second - ratio)^2) < (gearErr^2) then gear = 2 end
      if ((third  - ratio)^2) < (gearErr^2) then gear = 3 end
      if ((fourth - ratio)^2) < (gearErr^2) then gear = 4 end
      if ((fifth  - ratio)^2) < (gearErr^2) then gear = 5 end
   end
   setChannel(gear_id, gear)
end


function sendCAN(can, id, data)
   local res = txCAN(can, id, 0, data,100)
   if res == 0 then println('txCAN failed, tick:' ..tick) end
end

function sendHaltech_50()
   local rpm = getTimerRpm(0)
   local tps = getAnalog(3)*10 --convert % to 0.10% units
   local op = getAnalog(2)*6.89476*10 --convert psi to 0.1 kPa units

   local id = 864 --RPM, manifold pressure, TPS, coolant pressure
   local data = {spu(rpm), spl(rpm), 0, 0, spu(tps), spl(tps), 0, 0}
   sendCAN(0, id, data)

   id = 865 --fuel pressure, oil pressure, accel pedal pos, wastegate pressure
   data = {0, 0, spu(op), spl(op), 0, 0, 0, 0}
   sendCAN(0, id, data)
end

function sendHaltech_20()
   local sped = speed*1.609344*10 --convert mph to 0.1km/h units
   
   local id = 880 --wheel speed gen., gear,
   local data = {spu(sped), spl(sped), spu(gear), spl(gear), 0, 0, 0, 0}
   sendCAN(0, id, data)
end

function sendHaltech_5()
   local ot = (((getAnalog(1) - 32)*5)/9 + 273.15)*10 --convert F to 0.1Kelvin units
   local wt = (((getAnalog(0) - 32)*5)/9 + 273.15)*10 --convert F to 0.1Kelvin units
   
   local id = 992 --coolant temp, air temp, fuel temp, oil temp
   local data = {spu(wt), spl(wt), 0, 0, 0, 0, spu(ot), spl(ot)}
   sendCAN(0, id, data)
end


function sxOnUpdate()
   sxUpdateLinearGraph(getTimerRpm(0)) --RPM
   sxUpdateAlert(0, getAnalog(0)) --water temp
   sxUpdateAlert(1, getAnalog(2)) --oil pressure
end

function sxOnInit()
  --config shift light
  sxCfgLinearGraph(0,0,0,7000) --left to right graph, linear style, 0 - 7000 RPM range

  sxSetLinearThresh(0,0,5000,0,255,0,0) --green at 5000 RPM
  sxSetLinearThresh(1,0,6000,255,255,0,0) --yellow at 6000 RPM
  sxSetLinearThresh(2,0,6500,255,0,0,0) --red  at 6500 RPM
  sxSetLinearThresh(3,0,6800,255,0,0,10) --red+flash at 6800 RPM
 
  --configure first alert (right LED) as engine temperature (F)
  sxSetAlertThresh(0,0,210,255,255,0,0) --yellow warning at 205F
  sxSetAlertThresh(0,1,225,255,0,0,10) -- red flash at 225F

  --configure second alert (left LED) as oil pressure (PSI)
  sxSetAlertThresh(1,0,0,255,0,0,10) --red flash below 10 psi
  sxSetAlertThresh(1,1,10,255,255,0,0) --yellow 10-20 PSI
  sxSetAlertThresh(1,2,20,0,0,0,0) --above 20, no alert
end

function sxSetLinearThresh(id,s,th,r,g,b,f)
  sxTx(41,{id,s,spl(th),spu(th),r,g,b,f})
end

function sxSetAlertThresh(id,tid,th,r,g,b,f)
  sxTx(21,{id,tid,spl(th),spu(th),r,g,b,f})
end

function setBaseConfig(bright, brightscale)
  sxTx(3,{bright, brightscale})
end

function sxUpdateAlert(id,v)
  if v~=nil then sxTx(22,{id,spl(v),spu(v)}) end
end

function sxCfgLinearGraph(rs,ls,lr,hr)
  sxTx(40,{rs,ls,spl(lr),spu(lr),spl(hr),spu(hr)})
end

function sxUpdateLinearGraph(v)
  if v ~= nil then sxTx(42,{spl(v),spu(v)}) end
end

function sxInit()
  println('config shiftX2')
  setBaseConfig(sxBright,sxBrightScale)
  if sxOnInit~=nil then sxOnInit() end
end

function sxChkCan()
  id,ext,data=rxCAN(sxCan,0)
  if id==sxCanId then sxInit() end
  if id==sxCanId+60 and sxOnBut~=nil then sxOnBut(data[1]) end
end

function sxProcess()
  sxChkCan()
  if sxOnUpdate~=nil then sxOnUpdate() end
end

function sxTx(offset, data)
  txCAN(sxCan, sxCanId + offset, 1, data)
end

function spl(v) return bit.band(v,0xFF) end
function spu(v) return bit.rshift(bit.band(v,0xFF00),8) end


function onTick()
 
  if tick%2 == 0 then --50Hz
   sendHaltech_50()
  end
 
  if tick%5 == 0 then --20Hz
    updateSpeedDiff()
    updateGear()
   sendHaltech_20()
  end
 
  if tick%10 == 0 then --10Hz
    sxProcess()
  end
 
  if tick%20 == 0 then --5Hz
   sendHaltech_5()
  end
 
  tick = tick +1
  if tick > 99 then tick = 0 end
end

sxInit()



View user's profile Send private message
Reply with quote
Post  
Some ideas. Try these one at a time so you can know where the changes make improvements - and also aids in debugging.



* If you're having sporadic out of memory issues, try placing a call to the garbage collector right at the beginning of the onTick() function:

function onTick()
collectgarbage()



* You can try eliminating more global variables by combining functions like updateSpeedDiff() and updateGear() into one function, making rpm, rpm_diff, speed and gear local variables.



* You can remove the global variables for your gear ratios and just put the constants in the function.



* You can get rid of the initCAN() and rely on the native CAN setup.



* Modify this function to make variables local

function sxChkCan()
local id, ext, data
id,ext,data=rxCAN(sxCan,0)
if id==sxCanId then sxInit() end
if id==sxCanId+60 and sxOnBut~=nil then sxOnBut(data[1]) end
end



* Try removing comments


Hope this helps, let us know how it goes!


_________________
Brent Picasso
CEO and Founder, Autosport Labs
Facebook | Twitter
View user's profile Send private message Send e-mail
Reply with quote
Post  
Good ideas! I'll implement them and test tomorrow. Thanks.

View user's profile Send private message
Reply with quote
Post  
You can also try the following:
1) Make all your functions "local", Minifier will shrink the function names if you do so;
2) Remove as much ShiftX2 functions as you can and use sxTx function directly everytime you can. sxTx is the only ShiftX2 function that I use in my own script, my script is huge and I don't have memory issues;
3) Instead of using many functions in onTick (updateSpeedDiff(), updateGear(), sendHaltech(), sxProcess() ), try to make one big function that covers all you need. It may sound unclever, but it will actually consume less memory;
4) Remove all the variables with constant values and write the values directly where you need them (sxCan, sxId, sxBright).


_________________
--Paulo
View user's profile Send private message
Reply with quote
Post  
So what ended up working for you? I'm sure people would benefit from the solution.

Thanks!


_________________
Brent Picasso
CEO and Founder, Autosport Labs
Facebook | Twitter
View user's profile Send private message Send e-mail
Display posts from previous:
Reply to topic Page 1 of 1
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You cannot download files in this forum