Page 1 of 1
Innovate Serial protocol
Posted: Fri Jul 07, 2017 4:30 pm
by Intel
So our Lemons team already had 4 of these Innovate Gauges daisy chained together. Looking to leverage this and input it into our Racecapture mk3
Trying to just get an initial connection/look at the output data as it comes from the serial stream.
http://www.innovatemotorsports.com/supp ... llog-2.pdf
8 data bits
1 stop bit
no parity
19.2 kBaud.
Code: Select all
--initialize Aux serial port to 19200, 8, N, 1
initSer(4,19200, 8, 0, 1)
function onTick()
--read a line from the aux serial port with a 100ms timeout
value = readSer(4, 200)
if value ~= nil then
println('read value: ' ..value)
end
end
It is returning a blank read value over and over which seems to at least point to the connection taking place. Is there something easy I am missing here?
Currently I am just hooked up to one of these gauges on the out port. Once I get that one configured I will be adding the other 2 and the LC-2 wideband.
Posted: Fri Jul 07, 2017 7:17 pm
by brentp
It's hard to say without seeing the protocol directly, and we don't have experience with that protocol.
What I would recommend is using a basic script like you have, and then connecting it to your computer using a USB to serial adapter. Then, if you open a terminal program, like HyperTerminal or RealTerminal (both windows apps) - and then press a Key, like "A" - you should receive an "A" in the Lua script.
Once you can prove you can receive data correctly, then you can implement the innovate serial protocol.
Good luck!
Posted: Fri Jul 07, 2017 8:48 pm
by gizmodo
Just a word of caution here. I did the same thing with my Zeitronix ZT-2 and I found that even setting the tick rate at 30 it wasn't fast enough to keep up. Everything looked good with the car idling but revving and letting off the gas showed a noticeable lag in the data being output to the RaceCapture app vs the Zeitronix app and LCD. You might have better luck or I was doing something wrong, but that's what I saw.
Posted: Fri Jul 07, 2017 9:11 pm
by brentp
It really does depend on how the script is written. For example, if on every tick only one character is processed, it would be slower than having the script block until every character representing a message is received on the port - then that message can be parsed for values and virtual channels set.
Post the script as you make progress, we'll put an eye on it!
Posted: Fri Jul 07, 2017 10:32 pm
by gizmodo
Looking at the pdf it looks like you might be running into the same thing as I did at first. The readSer function assumes that each packet ends with a carriage return, which I don't see called out in the documentation. For my application the header is marked by the first three characters, which are always 0,1,2 and that sequence is guaranteed not to occur in each packet. The while loop is looking for those characters. I'd suggest just using readCSer to see if that gets you something meaningful.
Code: Select all
if initSer(4, 9600, 8, 0, 1) == true then
println('initialized')
else
println('failed to initialize')
end
setTickRate(30)
function onTick()
headerFound = false
while headerFound == false
do
first = readCSer(4, 10)
if first == 0 then
second = readCSer(4, 10)
if second == 1 then
third = readCSer(4, 10)
if third == 2 then
afr = readCSer(4, 10)
egtLow = readCSer(4, 10)
egtHigh = readCSer(4, 10)
rpmLow = readCSer(4, 10)
rpmHigh = readCSer(4, 10)
mapLow = readCSer(4, 10)
mapHigh = readCSer(4, 10)
tps = readCSer(4, 10)
user1 = readCSer(4, 10)
config1 = readCSer(4, 10)
config2 = readCSer(4, 10)
--user2Low = readCSer(4, 10)
--user2High = readCSer(4, 10)
--user2Config = readCSer(4, 10)
afrValue = afr / 10
egtValue = egtLow + egtHigh * 256
rpmValue = ((1000000 / (rpmLow + (rpmHigh * 256) ) ) * 4.59 ) / 4 -- 4 = number of cylinders
mapValue = (mapLow + mapHigh * 256) / 10
user1Value = ((5 / 256) * user1) * 37.5 - 18.7 -- values = 0 to 255, which correspond to 0Volts to 5Volts
println(first ..', ' ..second ..', ' ..third ..', AFR: ' ..afrValue ..', EGT: ' ..egtValue ..', RPM: ' ..rpmValue ..', MAP: ' ..mapValue ..', TPS: ' ..tps ..', Fuel Pressure: ' ..user1Value ..', Config1: ' ..config1 ..', config2: ' ..config2)
headerFound = true
end
end
end
end
end
Posted: Sat May 25, 2019 4:53 am
by lightningrod
It looks like I'm a couple of years too late to be helpful in getting ready for Lemons.
The aux serial port for my rcpMk3 is 6.
I expect this code should work at much slower tickRates than 60, but I haven't done much testing yet. It is designed to run with minimal delays at high tickRates, so never waits, nor needs to wait for serial input. While serial data is ready, it reads it all, so even at low tickRates it should keep up fine. I think 10 will be enough to keep up. It will depend on the size of the internal serial buffer. But it looks the Innovate AFM only generates about 6 bytes per update and looks like its going at about 10hz, so 60 bytes per second.
I'm tossing out all the data except the air fuel ratio, so if your Innovate is logging more data, you'll have to add code to read that.
I'm pretty new to lua, so feel free to offer suggestions for improvements.
Code: Select all
-- documentation for RCP serial interface here: https://wiki.autosportlabs.com/RaceCapturePro_Lua_Scripting
-- documentation for ISP2 here: http://www.innovatemotorsports.com/support/downloads/Seriallog-2.pdf
afSerNum=6
afInitd=false
afChan=-1
function afInit()
afInitd = initSer(afSerNum, 19200, 8, 0, 1)
if afInitd then
println('serial initialized')
ispInit()
else
println('failed to initialize serial')
end
end
-- identify header with bits 15,13,9,7
hMaskH=0xA2
hMaskL=0x80
ispAFR = nil
-- this is a non-blocking implementation of the isp2 reader.
-- the following functions, when triggered will put the subsequent function
-- into the ispNextByteHandler variable, so when everything goes smoothly they will each find the
-- byte they are expecting.
-- the ispHandleHh function is responsible for consuming any unused packets (there is packet count
-- detected, but I am ignoring it for now. If you expect or need more packets, you'll need to add
-- functions to this sequence to process them.
function ispInit()
ispHl = nil
ispHh = nil
ispNextByteHandler = ispHandleHh
end
function ispHandleHh( h )
if bit.band(h,hMaskH) == hMaskH then
ispHh = h
ispNextByteHandler = ispHandleHl
end
end
function ispHandleHl( l )
if bit.band(l,hMaskL) == hMaskL then
ispHl = l
ispNextByteHandler = ispHandleAFh
ispPkt = bit.band(l,0x7F) + 0x80*bit.band(ispHh,1)
else
ispInit()
end
end
function ispHandleAFh( h )
ispAFh = h
ispNextByteHandler = ispHandleAFl
end
function ispHandleAFl( l )
ispAFl = l
ispNextByteHandler = ispHandleLh
end
function ispHandleLh( h )
ispLh = h
ispNextByteHandler = ispHandleLl
end
function ispHandleLl( l )
ispLl = l
local func = bit.band(bit.rshift(ispAFh,2),7)
-- this is the afr multiplier (not the afr)
local af = bit.band( ispAFh, 1) * 0x80 +
bit.band( ispAFl, 0x7F)
local lambda = bit.band(ispLh,0x7F)*0x80 + bit.band(ispLl,0x7F)
if func == 0 then
ispAFR = (lambda+500)*af/10000
else
println( "afr: func"..func.." mult:"..af.." lambda:"..lambda )
end
ispInit()
end
function afReadISP2nb(serPort)
c = readCSer( serPort,0 )
while c ~= nil do
ispNextByteHandler( c )
c = readCSer( serPort,0 )
end
if ispAFR ~= nil then
setChannel( afChan, ispAFR )
ispAFR = nil
end
end
function onTick()
if afInitd then
afReadISP2nb( afSerNum )
end
end
afInit()
afChan=addChannel("AFR",10,1,8,23,"%")
setTickRate(60)
Posted: Sat May 25, 2019 2:58 pm
by brentp
Nice job! I'm sure people will find this handy.
Posted: Sun Jun 02, 2019 5:19 am
by lightningrod
After wrestling with memory issue while trying to integrate this module with some others, here is a slimmed down version. I apologise for the loss in legibility, but the memory footprint reduction is significant over the previous listing. The original was 2500 bytes (1350 after lua minifier). This version is 647 bytes (630 after minifier). It also uses less memory when loaded due to a reduction in function declarations. I'm happy to share my original code if anyone wants to use/modify it, but it may not be very usable without my hacky pipeline. This version has been run through cpp and my own very simplified cleaning code to remove the cpp clutter, comments and whitespace as well as reduce variable names from meaningful to single characters. (You can save another 17 bytes by running this through lua minifier, and a few more bytes I haven't counted by removing the check and print around the initSer call).
Code: Select all
A=nil
B=nil
C=nil
function G()
local b=readCSer(6,0)
while b~=nil do
D=D+1
if D==1 and bit.band(b,(0xA2))==(0xA2)then
elseif D==2 and bit.band(b,(0x80))==(0x80)then
elseif D==4 then
E=bit.band(bit.rshift(B,2),7)
C=bit.band(B,1)*0x80+
bit.band(b,0x7F)
elseif D==6 then
local F=bit.band(B,0x7F)*0x80+bit.band(b,0x7F)
if E==0 then
A=(F+500.0)*C*0.0001
elseif E==1 then
A=F*0.1
else
end
D=0
elseif D~=3 and D~=5 then
D=0
end
B=b
b=readCSer(6,0)
end
if A~=nil then
setChannel(afChan,A)
A=nil
end
end
if initSer(6,19200,8,0,1)then else println('initSer failed:port'..6)end afChan=addChannel("AFR",10,1,8,23)D=0
function onTick()
G()
end
setTickRate(60)