Help with script optimization

Discussion on the Lua Scripting capabilities for RaceCapture/Pro. Also see the <a href="http://autosportlabs.net/RaceCapturePro_Lua_Scripting">Lua Scripting Guide</a>

Moderators: JeffC, stieg

Post Reply
TXBDan
Posts: 68
Joined: Sat Mar 25, 2017 1:43 am

Help with script optimization

Post by TXBDan »

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: Select all

setTickRate&#40;50&#41; --50Hz 
initCAN&#40;0, 1000000&#41; -- 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 &#40;if ADR1 jumper is cut&#41;
sxId=0
--Brightness, 0-100. 0=automatic brightness
sxBright=0
sxBrightScale = 45 --0-255 default is 51

sxCanId = 0xE3600 + &#40;256 * sxId&#41;
println&#40;'shiftx2 base id ' ..sxCanId&#41;

--virtual channels 
--addChannel&#40;"name",SR,prec,min,max,"unit"&#41; 
speeddiff_id = addChannel&#40;"Speed_",10,0,0,160,"MPH"&#41; 
gear_id = addChannel&#40;"Gear",5,0,0,5,"gear"&#41; 

--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&#40;&#41; 
   rpm_diff = getTimerRpm&#40;1&#41; 
   speed = rpm_diff*tirediameter*0.002975 
   speed = speed + 0.5 -- round because 0 prec. truncates 
   setChannel&#40;speeddiff_id, speed&#41; 
end 

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

function sendCAN&#40;can, id, data&#41;
	local res = txCAN&#40;can, id, 0, data,100&#41;
	if res == 0 then println&#40;'txCAN failed'&#41; end
end

function sendHaltech&#40;&#41;
	local rpm = getTimerRpm&#40;0&#41;
	local tps = getAnalog&#40;3&#41;*10 --convert % to 0.10% units
	local op = getAnalog&#40;2&#41;*6.894757*10 --convert psi to 0.1 kPa units
	local ot = &#40;&#40;&#40;getAnalog&#40;1&#41; - 32&#41;*5&#41;/9 + 273.15&#41;*10 --convert F to 0.1Kelvin units
	local wt = &#40;&#40;&#40;getAnalog&#40;0&#41; - 32&#41;*5&#41;/9 + 273.15&#41;*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 = &#123;spu&#40;rpm&#41;, spl&#40;rpm&#41;, 0, 0, spu&#40;tps&#41;, spl&#40;tps&#41;, 0, 0&#125;
	local res = sendCAN&#40;0, id, data&#41;

	id = 865 --fuel pressure, oil pressure, accel pedal pos, wastegate pressure
	data = &#123;0, 0, spu&#40;op&#41;, spl&#40;op&#41;, 0, 0, 0, 0&#125;
	res = sendCAN&#40;0, id, data&#41;
	
	id = 880 --wheel speed gen., gear, 
	data = &#123;spu&#40;sped&#41;, spl&#40;sped&#41;, spu&#40;gear&#41;, spl&#40;gear&#41;, 0, 0, 0, 0&#125;
	res = sendCAN&#40;0, id, data&#41;
	
	id = 992 --coolant temp, air temp, fuel temp, oil temp
	data = &#123;spu&#40;wt&#41;, spl&#40;wt&#41;, 0, 0, 0, 0, spu&#40;ot&#41;, spl&#40;ot&#41;&#125;
	res = sendCAN&#40;0, id, data&#41;

end


function sxOnUpdate&#40;&#41;
	sxUpdateLinearGraph&#40;getTimerRpm&#40;0&#41;&#41; --RPM
	sxUpdateAlert&#40;0, getAnalog&#40;0&#41;&#41; --water temp
	sxUpdateAlert&#40;1, getAnalog&#40;2&#41;&#41; --oil pressure
end


function sxOnInit&#40;&#41;
  --config shift light
  sxCfgLinearGraph&#40;0,0,0,7000&#41; --left to right graph, linear style, 0 - 7000 RPM range

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

  --configure second alert &#40;left LED&#41; as oil pressure &#40;PSI&#41;
  sxSetAlertThresh&#40;1,0,0,255,0,0,10&#41; --red flash below 10 psi
  sxSetAlertThresh&#40;1,1,10,255,255,0,0&#41; --yellow 10-20 PSI
  sxSetAlertThresh&#40;1,2,20,0,0,0,0&#41; --above 20, no alert
end


---ShiftX2 functions
function sxSetLinearThresh&#40;id,s,th,r,g,b,f&#41;
  sxTx&#40;41,&#123;id,s,spl&#40;th&#41;,spu&#40;th&#41;,r,g,b,f&#125;&#41;
end

function sxSetAlertThresh&#40;id,tid,th,r,g,b,f&#41;
  sxTx&#40;21,&#123;id,tid,spl&#40;th&#41;,spu&#40;th&#41;,r,g,b,f&#125;&#41;
end

function setBaseConfig&#40;bright, brightscale&#41;
  sxTx&#40;3,&#123;bright, brightscale&#125;&#41;
end

function sxUpdateAlert&#40;id,v&#41;
  if v~=nil then sxTx&#40;22,&#123;id,spl&#40;v&#41;,spu&#40;v&#41;&#125;&#41; end
end

function sxCfgLinearGraph&#40;rs,ls,lr,hr&#41; 
  sxTx&#40;40,&#123;rs,ls,spl&#40;lr&#41;,spu&#40;lr&#41;,spl&#40;hr&#41;,spu&#40;hr&#41;&#125;&#41;
end

function sxUpdateLinearGraph&#40;v&#41;
  if v ~= nil then sxTx&#40;42,&#123;spl&#40;v&#41;,spu&#40;v&#41;&#125;&#41; end
end

function sxInit&#40;&#41;
  println&#40;'config shiftX2'&#41;
  setBaseConfig&#40;sxBright,sxBrightScale&#41;
  if sxOnInit~=nil then sxOnInit&#40;&#41; end
end

function sxChkCan&#40;&#41;
  id,ext,data=rxCAN&#40;sxCan,0&#41;
  if id==sxCanId then sxInit&#40;&#41; end
  if id==sxCanId+60 and sxOnBut~=nil then sxOnBut&#40;data&#91;1&#93;&#41; end
end

function sxProcess&#40;&#41;
  sxChkCan&#40;&#41;
  if sxOnUpdate~=nil then sxOnUpdate&#40;&#41; end
end

function sxTx&#40;offset, data&#41;
  txCAN&#40;sxCan, sxCanId + offset, 1, data&#41;
end

function spl&#40;v&#41; return bit.band&#40;v,0xFF&#41; end
function spu&#40;v&#41; return bit.rshift&#40;bit.band&#40;v,0xFF00&#41;,8&#41; end

function onTick&#40;&#41;
  updateSpeedDiff&#40;&#41;
  updateGear&#40;&#41;
  sendHaltech&#40;&#41;
  sxProcess&#40;&#41;
end

sxInit&#40;&#41;

tfriest
Posts: 32
Joined: Wed Nov 27, 2013 4:00 am

Post by tfriest »

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

TXBDan
Posts: 68
Joined: Sat Mar 25, 2017 1:43 am

Post by TXBDan »

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: Select all

setTickRate&#40;100&#41; --100Hz 
tick = 0
initCAN&#40;0, 1000000&#41; -- 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 &#40;if ADR1 jumper is cut&#41;
sxId=0
--Brightness, 0-100. 0=automatic brightness
sxBright=0
sxBrightScale = 45 --0-255 default is 51

sxCanId = 0xE3600 + &#40;256 * sxId&#41;
println&#40;'shiftx2 base id ' ..sxCanId&#41;

--virtual channels 
--addChannel&#40;"name",SR,prec,min,max,"unit"&#41; 
speeddiff_id = addChannel&#40;"Speed_",10,0,0,160,"MPH"&#41; 
gear_id = addChannel&#40;"Gear",5,0,0,5,"gear"&#41; 

--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&#40;&#41; 
   rpm_diff = getTimerRpm&#40;1&#41; 
   speed = rpm_diff*tirediameter*0.002975 
   speed = speed + 0.5 -- round because 0 prec. truncates 
   setChannel&#40;speeddiff_id, speed&#41; 
end 

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


function sendCAN&#40;can, id, data&#41;
	local res = txCAN&#40;can, id, 0, data,100&#41;
	if res == 0 then println&#40;'txCAN failed, tick&#58;' ..tick&#41; end
end

function sendHaltech_50&#40;&#41;
	local rpm = getTimerRpm&#40;0&#41;
	local tps = getAnalog&#40;3&#41;*10 --convert % to 0.10% units
	local op = getAnalog&#40;2&#41;*6.89476*10 --convert psi to 0.1 kPa units

	local id = 864 --RPM, manifold pressure, TPS, coolant pressure
	local data = &#123;spu&#40;rpm&#41;, spl&#40;rpm&#41;, 0, 0, spu&#40;tps&#41;, spl&#40;tps&#41;, 0, 0&#125;
	sendCAN&#40;0, id, data&#41;

	id = 865 --fuel pressure, oil pressure, accel pedal pos, wastegate pressure
	data = &#123;0, 0, spu&#40;op&#41;, spl&#40;op&#41;, 0, 0, 0, 0&#125;
	sendCAN&#40;0, id, data&#41;
end

function sendHaltech_20&#40;&#41;
	local sped = speed*1.609344*10 --convert mph to 0.1km/h units
	
	local id = 880 --wheel speed gen., gear, 
	local data = &#123;spu&#40;sped&#41;, spl&#40;sped&#41;, spu&#40;gear&#41;, spl&#40;gear&#41;, 0, 0, 0, 0&#125;
	sendCAN&#40;0, id, data&#41;
end

function sendHaltech_5&#40;&#41;
	local ot = &#40;&#40;&#40;getAnalog&#40;1&#41; - 32&#41;*5&#41;/9 + 273.15&#41;*10 --convert F to 0.1Kelvin units
	local wt = &#40;&#40;&#40;getAnalog&#40;0&#41; - 32&#41;*5&#41;/9 + 273.15&#41;*10 --convert F to 0.1Kelvin units
	
	local id = 992 --coolant temp, air temp, fuel temp, oil temp
	local data = &#123;spu&#40;wt&#41;, spl&#40;wt&#41;, 0, 0, 0, 0, spu&#40;ot&#41;, spl&#40;ot&#41;&#125;
	sendCAN&#40;0, id, data&#41;
end


function sxOnUpdate&#40;&#41;
	sxUpdateLinearGraph&#40;getTimerRpm&#40;0&#41;&#41; --RPM
	sxUpdateAlert&#40;0, getAnalog&#40;0&#41;&#41; --water temp
	sxUpdateAlert&#40;1, getAnalog&#40;2&#41;&#41; --oil pressure
end

function sxOnInit&#40;&#41;
  --config shift light
  sxCfgLinearGraph&#40;0,0,0,7000&#41; --left to right graph, linear style, 0 - 7000 RPM range

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

  --configure second alert &#40;left LED&#41; as oil pressure &#40;PSI&#41;
  sxSetAlertThresh&#40;1,0,0,255,0,0,10&#41; --red flash below 10 psi
  sxSetAlertThresh&#40;1,1,10,255,255,0,0&#41; --yellow 10-20 PSI
  sxSetAlertThresh&#40;1,2,20,0,0,0,0&#41; --above 20, no alert
end

function sxSetLinearThresh&#40;id,s,th,r,g,b,f&#41;
  sxTx&#40;41,&#123;id,s,spl&#40;th&#41;,spu&#40;th&#41;,r,g,b,f&#125;&#41;
end

function sxSetAlertThresh&#40;id,tid,th,r,g,b,f&#41;
  sxTx&#40;21,&#123;id,tid,spl&#40;th&#41;,spu&#40;th&#41;,r,g,b,f&#125;&#41;
end

function setBaseConfig&#40;bright, brightscale&#41;
  sxTx&#40;3,&#123;bright, brightscale&#125;&#41;
end

function sxUpdateAlert&#40;id,v&#41;
  if v~=nil then sxTx&#40;22,&#123;id,spl&#40;v&#41;,spu&#40;v&#41;&#125;&#41; end
end

function sxCfgLinearGraph&#40;rs,ls,lr,hr&#41; 
  sxTx&#40;40,&#123;rs,ls,spl&#40;lr&#41;,spu&#40;lr&#41;,spl&#40;hr&#41;,spu&#40;hr&#41;&#125;&#41;
end

function sxUpdateLinearGraph&#40;v&#41;
  if v ~= nil then sxTx&#40;42,&#123;spl&#40;v&#41;,spu&#40;v&#41;&#125;&#41; end
end

function sxInit&#40;&#41;
  println&#40;'config shiftX2'&#41;
  setBaseConfig&#40;sxBright,sxBrightScale&#41;
  if sxOnInit~=nil then sxOnInit&#40;&#41; end
end

function sxChkCan&#40;&#41;
  id,ext,data=rxCAN&#40;sxCan,0&#41;
  if id==sxCanId then sxInit&#40;&#41; end
  if id==sxCanId+60 and sxOnBut~=nil then sxOnBut&#40;data&#91;1&#93;&#41; end
end

function sxProcess&#40;&#41;
  sxChkCan&#40;&#41;
  if sxOnUpdate~=nil then sxOnUpdate&#40;&#41; end
end

function sxTx&#40;offset, data&#41;
  txCAN&#40;sxCan, sxCanId + offset, 1, data&#41;
end

function spl&#40;v&#41; return bit.band&#40;v,0xFF&#41; end
function spu&#40;v&#41; return bit.rshift&#40;bit.band&#40;v,0xFF00&#41;,8&#41; end


function onTick&#40;&#41;
  
  if tick%2 == 0 then --50Hz
	sendHaltech_50&#40;&#41;
  end
  
  if tick%5 == 0 then --20Hz
    updateSpeedDiff&#40;&#41;
    updateGear&#40;&#41;
	sendHaltech_20&#40;&#41;
  end
  
  if tick%10 == 0 then --10Hz
    sxProcess&#40;&#41;
  end
  
  if tick%20 == 0 then --5Hz
	sendHaltech_5&#40;&#41;
  end
  
  tick = tick +1
  if tick > 99 then tick = 0 end
end

sxInit&#40;&#41;


brentp
Site Admin
Posts: 6274
Joined: Wed Jan 24, 2007 6:36 am

Post by brentp »

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

TXBDan
Posts: 68
Joined: Sat Mar 25, 2017 1:43 am

Post by TXBDan »

Good ideas! I'll implement them and test tomorrow. Thanks.

psfp
Posts: 49
Joined: Mon Aug 21, 2017 10:40 pm
Location: DF - Brazil

Post by psfp »

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

brentp
Site Admin
Posts: 6274
Joined: Wed Jan 24, 2007 6:36 am

Post by brentp »

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

Post Reply