RaceCapturePro Lua Scripting Examples: Difference between revisions

Line 365: Line 365:
end
end


</pre>
==Max RPM detection with GPIO reset==
This script detects the peak RPM detected and stores it in a virtual channel. A GPIO is used to reset the channel to zero via a push button.
<pre>
maxRpmId = addChannel("MaxRPM", 10)
maxRpm = 0
function rpmReset()
local rpm = getTimerRpm(0)
local clear = getGpio(0)
if rpm > maxRpm then
maxRpm = rpm
setChannel(maxRpmId, maxRpm)
end
if clear == 0 then
maxRpm = 0
setChannel(maxRpmId, maxRpm)
end
end
function onTick()
rpmReset()
end
</pre>
</pre>



Revision as of 14:28, 26 November 2018

How to set a script in the RaceCapture app

HowTo video: set custom script

API Reference

Complete API Reference

OBDII tricks

Complete OBDII PID reference

Clear current trouble codes

This script clears any current trouble codes every 10 seconds. Clearing the code requires transmitting an OBDII command to ID 0x7DF with mode 4 specified. No other parameters are specified.

setTickRate(1) 
count = 0
function onTick()
 checkClearCodes()
end

function checkClearCodes()
  if count % 10 == 0 then
    print('clearing codes')
    txCAN(0, 0x7df, 0, {1,4})
  end
end

Query and Decode Diagnostic Trouble Code

This script queries for a DTC, and if found, prints the decoded trouble code and the raw data.

--This example queries for and reads the first available DTC, if present. 

setTickRate(1)

--P=Powertrain, C=Chassis, B=Body, U=Network
prefixes = {'P','C','B','U'}
digits = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'} 

function toDigit(v)
  return digits[v+1]
end

function printDTC(data)
  local dtc
  local a = data[3]
  local b = data[4]
  local prefix = prefixes[bit.band(bit.rshift(a, 6), 0x03) + 1]
  local c2 = bit.band(bit.rshift(a, 4), 0x03)
  local c3 = bit.band(a, 0x0f)
  local c4 = bit.band(bit.rshift(b, 4), 0x0F)
  local c5 = bit.band(b, 0x0F)

  local code = prefix ..toDigit(c2) ..toDigit(c3) ..toDigit(c4) ..toDigit(c5)
  println('Found DTC: ' ..code)  
end

function printRawDTC(data)
  print('Raw DTC bytes: ')
  --print the remaining 6 bytes, 
  --this raw data holds the trouble codes
  for i=1, #data do print(' ' ..data[i]) end
  println('')
end

function checkDTCResponse()
  local attempts = 0
  while attempts < 50 do
    local id,ext, data = rxCAN(0, 10)
    if id ~= nil and ext == 0 and id == 0x7e8 then
      if data[2] == 0x43 then
        if data[1] > 2 then
          printRawDTC(data)
          printDTC(data)
        else
          println('No DTC Found')
        end 
        return
      end
    end
    attempts = attempts + 1
  end
end

function sendCheckDTC()
  println('Sending check DTC')
  txCAN(0, 0x7df, 0, {1,3})
end

function onTick() 
  sendCheckDTC()
  checkDTCResponse()
end

Data filtering

Simple RPM Filter

Got a noisy RPM signal where big spikes show up? This will reject RPM values above a sane threshold.

Important - make sure the RPM channel in the main RaceCapture/Pro config is disabled, since this virtual channel replaces it.

--sample RPM at 10Hz
setTickRate(10)

--set this value to be your sane upper limit
rpmLimit = 10000

--In the RCP config, ensure the timer channels are disabled
--we will create the virtual channel here
rpmId = addChannel( "RPM", 10, 0, 0, rpmLimit)
rpm = 0

function doRPM()
  local r = getTimerRpm(0)
  if r < rpmLimit then 
    rpm = r
  end
  setChannel(rpmId, rpm)
end


function onTick()
  doRPM()
end

Automatic logging control

Automatically Start Logging When Powered On

This script will automatically start logging the moment RaceCapture/Pro turns on

  • Note, this will create a file with an incorrect timestamp if GPS lock is not established.
    • To get a properly time-stamped file, use the GPS speed triggered approach.
function onTick()
  startLogging()
end

Automatically Start Logging based on GPS Speed

This script will automatically start logging to SD card when speed exceeds 10MPH and stop when it falls below.

function onTick()
  if getGpsSpeed() > 10 then
    startLogging()
  else
    stopLogging()
  end
end

Automatically Start Logging when engine is running

This script will automatically start logging to SD card when battery voltage shows that the engine is running

  • When battery voltage is greater than 13 volts, we assume engine is running.
  • System battery voltage is tied to the last Analog channel (8) (Note, change this to 7 if RaceCapture/Pro MK2)
function onTick()
  if getAnalog(8) > 13 then
    startLogging()
  else
    stopLogging()
  end
end

Automatically start Logging upon Launch (AutoX / Rally / Hill Climb)

This script will start Logging when a dash mounted "ARM" switch is activated via an input and G-force exceeds a threshold Given:

  • GPIO 0 configured as input and connected to dash mounted "Arm" switch
  • Default RaceCapture/Pro mounting orientation (terminal block facing forward, mounted upright)
  • G-force launch threshold is -0.1 G
  • flipping the ARM switch to 'Off' will stop logging
 setTickRate(30)
 overRevId = addChannel("OverRev", 10)


 function onTick()
  local arm = getGpio(0)
  local g = getAccel(1)
  if arm == 0 then
    stopLogging()
  end
  if arm == 1 and g < 0.1 then
    startLogging()
  end
 end

Virtual Channels (AKA Math Channels)

Averaging a fuel level reading or other sensor

This script calculates a moving average to account for fuel slosh. Assumptions:

  • Fuel level is connected to the first analog channel and is calibrated; modify the script to base the reading on other channels as appropriate.
--The real analog channel should be named
--something other than FuelLevel
fuel2Id = addChannel("FuelLevel", 10, 0, 0,100,"%")

--change this to make a bigger averaging window
maxAvg = 600 
--600 = 20 seconds averaging at 30Hz tick rate

--do not change
fuelAvg={}
fuel2Index = 1

function updateFuelAvg(value)
  local i
  if #fuelAvg == 0 then
    --initialize averaging table
    for i = 1, maxAvg do fuelAvg[i]=0 end
  end
  fuelAvg[fuel2Index] = value
  fuel2Index = fuel2Index + 1
  if fuel2Index > maxAvg then fuel2Index = 1 end
  local sum = 0
  for i = 1, #fuelAvg do
    sum = sum + fuelAvg[i]
  end
  setChannel(fuel2Id, sum / maxAvg)
end


setTickRate(30)
function onTick()
  updateFuelAvg(getAnalog(0))
end

Average two sensor values and set to a virtual channel

--10Hz update
setTickRate(10)

--Create channel: "SensorAvg"
--Sample rate: 10Hz
--Logging precision: 2 decimal points
--min/max: 0/5
--Units: "Volts"
avgId = addChannel("SensorAvg", 10, 2, 0, 5, "Volts")

function onTick()
  local a1 = getAnalog(0)
  local a2 = getAnalog(1)
  setChannel(avgId, (a1 + a2) / 2)
end

Gear Calculation

Calculate gear position based on drive train ratios

--Developed by Luther Lloyd III 8/22/14 for use by Autosport Labs Community
--Copy this text into the scripting window of Race Analyzer
--Edit the gear ratios to mathc your car
--Add another gear row if needed for 6th gear or delete extraneous ones



--below values are constants for the vehicle
local _1stGear = 3.5
local _2ndGear = 2.7
local _3rdGear = 2.0
local _4thGear = 1.4
local _5thGear = 0.9
local FinalDrive = 3.21
--diameter in inches
local TireDia = 25.0  
--allowable error of gear ratio to allow for measurement variation
local gearErr = 0.1
local rpmSpeedRatio = 0
--initialized to 0 so if it doesn't work you know
local gearPos = 0 --this is the gear channel variable

function onTick() --updates gear position every second by default

--assumes Pulse Input channel one is for the RPM signal and speed in MPH
local speed = getGpsSpeed()
local rpm = getTimerRpm(0)

--this part only works for firmware version 2.0 per the RCP page
gearId = addChannel("Gear",5)

if speed > 10 then
    --makes sure your rolling so as not to divide by 0 
    
   rpmSpeedRatio = (rpm/speed)/(FinalDrive*1056/(TireDia*3.14159))

    if ((_1stGear - rpmSpeedRatio)^2) < (gearErr^2) then gearPos = 1 end
    if ((_2ndGear - rpmSpeedRatio)^2) < (gearErr^2) then gearPos = 2 end
    if ((_3rdGear - rpmSpeedRatio)^2) < (gearErr^2) then gearPos = 3 end
    if ((_4thGear - rpmSpeedRatio)^2) < (gearErr^2) then gearPos = 4 end
    if ((_5thGear - rpmSpeedRatio)^2) < (gearErr^2) then gearPos = 5 end

else gearPos = 0 end

setChannel(gearId, gearPos) --outputs to virtual channel

end

Throttle Position Calculation

Estimate throttle position based on G-Forces. 0-100%

--Developed by Scott Barton of ProFormance Coaching for use by Autosport Labs Community

MaxAccelG = 0.30   -- Look at your logs and see what your max acceleration is in 3rd or 4th gear.  Note that full acceleration in 5th will not show 100% throttle.
chThrottle = addChannel("vThrottle", 10, 0, 0, 100,'')

function updatevThrottle()
--AccelG = getChannel(LongAccel) --2.13 Firmware and up
 AccelG = getImu(0) --Prior to 2.13 Firmware
 TPS = (AccelG/MaxAccelG)*100
 
 if TPS < 0 then TPS = 0 end
 TPS = math.abs(TPS)
 if TPS > 99 then TPS = 100 end
 setChannel(chThrottle, TPS)

end

--10Hz update
setTickRate(10)

function onTick()
  updatevThrottle()
end

Brake Position Calculation

Estimate brake pedal position based on G-Forces. 0-100%.

 --Developed by Scott Barton of ProFormance Coaching for use by Autosport Labs Community

MaxDecelG = 1.20   --Look at your logs and see what your maximum g-forces under straight line braking is and round down. 
chBrake = addChannel("vBrake", 10, 0, 0, 100, '')

function updatevBrake()
 --BrakeG = getChannel(LongAccel) -- 2.13 Firmware and up
 BrakeG = getImu(0) --Prior to 2.13 Firmware
--if ReverseAccel then 
BrakeG = BrakeG *-1
 --BrakeG = -1.1
 BrakePos = (BrakeG/MaxDecelG)*100
 
 if BrakePos < 10 then BrakePos = 0 end
 BrakePos = math.abs(BrakePos)
 if BrakePos > 99 then BrakePos=100 end
 setChannel(chBrake, BrakePos)
end

--10Hz update
setTickRate(10)

function onTick()
  updatevBrake()
end

Max RPM detection with GPIO reset

This script detects the peak RPM detected and stores it in a virtual channel. A GPIO is used to reset the channel to zero via a push button.

maxRpmId = addChannel("MaxRPM", 10)
maxRpm = 0

function rpmReset()
local rpm = getTimerRpm(0)
local clear = getGpio(0)
if rpm > maxRpm then
maxRpm = rpm
setChannel(maxRpmId, maxRpm)
end
if clear == 0 then
maxRpm = 0
setChannel(maxRpmId, maxRpm)

end
end

function onTick()
rpmReset()
end 

Alerts/Notifications

Create an over-rev marker

This script will mark the datalog with an over-rev alert marker whenever RPMs are above a certain threshold. Given:

  • RPM is connected to the first Timer input channel
  • RPM over-rev condition is 8000
setTickRate(10)
overRevId = addChannel("OverRev", 10)

function onTick()
  local rpm = getTimerRpm(0)
  local overRev = 0
  if rpm > 8000 then overRev = 1 end
  setChannel(overRevId, overRev)
end

Alert driver via electro-shock if oil pressure below safe threshold

This will alert driver via a shock (using pet scat mat) if oil pressure drops below threshold while engine is running above certain RPM

Scat mat box.jpg

Script will check at 10Hz (10 times/sec) for threshold condition

Given:

  • Oil pressure on analog channel 0, and calibrated
  • Seat mounted pet scat mat installed in seat and controlled by GPIO 0 (GPIO switches ground when active)
  • GPIO 0 is configured for output mode
  • RPM is connected to timer input 0
--onTick() is called at 10Hz
setTickRate(10)
 

rpmThreshold = 2000
lowOilPressure = 15

function onTick()
  local shockDriver = 0
  if getTimerRpm(0) > rpmThreshold and getAnalog(0) < lowOilPressure then
    shockDriver = 1
  end
  setGpio(0, shockDriver)
end

Track Max RPM / Value

This script will track a channel's maximum value and set a new channel with that maximum. Given:

  • RPM is connected to the first Timer input channel
setTickRate(10)
maxRpmId = addChannel("MaxRPM", 10)
maxRpm = 0

function onTick()
  local rpm = getTimerRpm(0)
  if rpm > maxRpm then
    maxRpm = rpm
    setChannel(maxRpmId, maxRpm)
  end
end

Activate a GPIO when start finish line is crossed

This script will pulse one of the GPIO outputs when the start/finish line is detected. First, the onTick rate is set to 10hz, then setGpio() is called with the result of the call to getAtStartFinish()

setTickRate(10)

function onTick()
  if getAtStartFinish() == 1 then
    setGpio(0, 1)
  else
    setGpio(0, 0)
  end
end

or

setTickRate(10)

function onTick()
  setGpio(0, getAtStartFinish())
end

Temperature Warning Indicator Light

This script will activate an output if an analog input exceeds a threshold. It's assumed a temperature sensor is connected to the Analog input channel 0 and is calibrated.

More information: Installation Guide, Sensor Guide, Operation Guide

function onTick()
 if getAnalog(0) > 212 then
   setGpio(0, 1)
 else
   setGpio(0, 0)
 end
end

Multi warning light

A script to trigger a warning light if at least one condition occurs. This will activate the output if the engine temperature is greater than 212 or if oil pressure drops below 10.

  • Note Actual temperature and pressure sensors must be connected to the appropriate analog inputs and correctly calibrated.
--Analog 0 is engine temp, in degrees F
--Analog 1 is oil pressure, in PSI

function onTick()
 if getAnalog(0) > 212 or getAnalog(1) < 10 then
   setGpio(0, 1)
 else
   setGpio(0, 0)
 end
end

Enable an LED if fuel level drops below 10%

  • Using PWM/Analog output

only available on RaceCapture/Pro MK1 and MK2

    • Fuel sensor is on Analog 0, scaled 0-100%
    • LED indicator connected to PWM 0. When fuel level drops below 10%, Analog/PWM output 0 will show 5v; 0v if fuel level is higher
    • PWM channel settings are set to factory default
function onTick()
 local p = 0
 if getAnalog(0) < 10 then p = 100 end
 setPwmDutyCycle(0,p)
end
  • Using GPIO in output mode
    • When fuel level drops below 10%, the GPIO is activated (output is grounded). Can be used to drive a relay or other device up to 1A load
    • GPIO jumper set to output mode
    • GPIO setting in firmware set to match jumper setting
function onTick()
 local p = 0
 if getAnalog(0) < 10 then p = 1 end
 setGpio(0, p)
end

3 stage Sequential Shift Light

Activates a 3 stage sequential shift light. Also see the Sequential Shift Light project

Given:

  • RPM sensor on timer input 0


setTickRate(15)

function onTick() 
	local r = getTimerRpm(0) 
	if r > 5000 then setGpio(2,1) else setGpio(2,0) end 
	if r > 6000 then setGpio(1,1) else setGpio(1,0) end 
	if r > 7000 then setGpio(0,1) else setGpio(0,0) end
end

CAN bus scripts

Receive a CAN message on one bus and re-transmit on a different bus

setTickRate(30)
function onTick()
  id, ext, data = rxCAN(0)
  if (id ~= nil) then
    txCAN(1, id, ext, data)
  end
end

Send A CAN message with current GPS speed

Given:

  • Destination CAN device is looking for a message with ID 1234
  • Standard (11 bit) CAN identifer
  • CAN1 channel is used
function onTick()
  local speed = getGpsSpeed()
  --format the speed in a CAN message. Speed is in the first byte
  local msg = {speed} 
  txCAN(0, 1234, 0, msg)
end


Send A CAN message with a temperature value

available in future Firmware version 2.0

Given:

  • Analog 0 reads a calibrated temperature value between 0 and 255
  • Destination CAN device is looking for a message with ID 1234
  • Standard (11 bit) CAN identifer
  • CAN1 channel is used
function onTick()
  local tmp = getAnalog(0)
  local msg = {tmp}
  txCAN(0, 1234, 0, msg)
end


Use RCP as a general purpose CAN data logger

The following script will output any received CAN bus message to the log window.

  • You can access the log window by enabling polling in the script window under the RaceCapture app configuration view
  • You can also access the log window by connecting to RaceCapture/Pro from a terminal program (hyperterminal, Minicom, etc) and issuing the command: viewLog
setTickRate(30) --30Hz

function onTick()
    repeat --will drain CAN buffer on each tick
        id, e, data = rxCAN(0)
	if id ~= nil then
		print(id ..':')
		for i=1,#data do
			print(data[i] ..' ')
		end
		println('')
	end
    until id == nil
end

Receive a CAN message and set a virtual channel

Given:

  • creates a channel named "MyChannel" that logs at 10Hz
  • Sets tick rate to 10Hz
  • Receive a CAN message on CAN1 channel, with 100ms timeout
  • if data received is valid (by checking the CAN message ID is not nil), then set the virtual channel with the first element in the CAN message data
channelId = addChannel("MyChannel", 10)
setTickRate(10)

function onTick()
  id, ext, data = rxCAN(0, 100)
  if id ~= nil then
    setChannel(channelId, data[1])
  end
end

Note: To map real CAN bus data, consult your CAN bus protocol documentation for correctly mapping CAN bus data to virtual channels.

CAN bus loopback test

Use This script to test functionality of your CAN bus.

Wiring / Setup

  • Connect CAN1 high -> CAN2 high and CAN1 low -> CAN2 low. This can either be done though the main wiring harness or the RJ45 connector.
    • Note if your unit has CAN on the wiring harness and the RJ45 connector, you only need to bridge the connections on one harness, since they are internally wired together.
  • Set the CAN baud rates to the same value for CAN1 and CAN 2.

CAN loopback test.png

-- CAN loopback test
-- * Wire CAN1 high -> CAN2 high and CAN 1 low -> CAN 2 low
-- * Ensure baud rate is identical for both CAN1 and CAN2
can_id = 1234
count = 0
function onTick() 
  count = count + 1
  -- transmit a CAN message on CAN1 with count as the first byte
  local tx_result = txCAN(0, can_id, 0, {count})
  println('transmit result ' ..tx_result)

  -- receive a CAN message on CAN2  
  local id, ext, data = rxCAN(1)

  if id == nil then
    println('No CAN message received!')
  else
    println('Got CAN message ID: ' ..id ..' data: ' ..data[1])
  end
end

Serial Port

Read an analog sensor value and output it to the auxiliary serial port

This example reads an analog sensor and writes a string containing the value to the Auxiliary serial port. Repeats once per second.

--initialize Aux serial port to 115200, 8, N, 1
initSer(4, 115200, 8, 0, 1)

function onTick()
 value = getAnalog(0)
 writeSer(4, 'A=' ..value)
end

Read a line of serial data

This example reads a line of serial data and writes it to the internal RaceCapture log.

  • Note: You can monitor the log by enabling the log polling in the script configuration view.
--initialize Aux serial port to 115200, 8, N, 1
initSer(4, 115200, 8, 0, 1)

function onTick()
 --read a line from the aux serial port with a 100ms timeout
 value = readSer(4, 100)
 if value ~= nil then
  println('read value: ' ..value)
 end
end

Demo Scripts

RPM Sweep

This sweeps an RPM channel up and down through a pre-defined range. Edit the range at the top of the script.

-- 05 Jan 2016 RPM demo
-- F.Mirandola - B.Picasso
-- This code will show a demo RPM sweep on RaceCapturePro

thrRpmLo = 3000
 --Low RPM threshold for change direction
 
thrRpmHi = 10000
--Hi RPM threshold for change direction
incrementRpm = 300
--Step between each RPM increment/decrement, will affect the speed, higher = faster

-- Do not edit after this!!

setTickRate(10)

rpm = 0

direction = 0

rpmId = addChannel("RPM", 10, 0, 0, 10000)

function onTick()
  setChannel(rpmId, rpm)

  if (rpm<=thrRpmHi and direction == 0 ) then rpm = rpm + incrementRpm  
  	elseif (rpm>=thrRpmLo and direction == 1 ) then rpm = rpm - incrementRpm
  end

  if (rpm>thrRpmHi) then direction = 1
   	elseif (rpm<thrRpmLo) then direction = 0
  end

end

Simulating Laps

Here's a neat script that can simulate lap times and predicted times. We've used it to develop dashboard features for the app. Especially interesting is the use of the accelerometer to generate a bit of noise and variability in the data.

--Script to simulate lap and predicted times
--Without needing a moving car on race track!
--To use, disable Race Timing in RaceCapture/Pro configuration and
--use this script
etid = addChannel("ElapsedTime", 10, 4)
ltid = addChannel("LapTime", 10, 4)
ptid = addChannel("PredTime", 10, 4)
clid = addChannel("CurrentLap", 10, 0)
lcid = addChannel("LapCount", 10, 0)

et = 0 --ElapsedTime value
lt = 0 --LapTime value
pt = 0 --PredTime value
cl = 0 --CurrentLap value
lc = 0 --LapCount value

targetTime = 0.5 --target time for our fake laps

setTickRate(10)
function onTick()

local rnd = getImu(0) --use the IMU to create a random-ish number

--simulate a predicted time if we've already
--completed a lap
--rnd value makes the predicted time jiggle
if lc > 0 then 
 pt = targetTime + rnd
 setChannel(ptid, pt)
end

--increment elapsed time by 1/10 second
et = et + 0.00166666667
setChannel(etid, et)

if et > targetTime + rnd then
 lt = et
 setChannel(ltid, lt)
 et = 0
 cl = cl + 1
 setChannel(clid, cl)
 if cl > 1 then
  lc = lc + 1
  setChannel(lcid, lc)
 end
end


end