AIM Smartycam and Shiftx Integration

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
m3drvr
Posts: 6
Joined: Sun Jul 31, 2016 8:36 pm

AIM Smartycam and Shiftx Integration

Post by m3drvr »

I have an E92 M3 with a Solo DL and Smartycam. I tied the AIM CAN bus to my Racecapture and loaded your Smartycam script and it worked great. I now want to add the Shiftx lights. I took the Shiftx script and created a function in the Smartycam script. I then added the new function to the Smartycam onTick loop. The problem is that it looks like the processCAN function never terminates so the Shiftx script never executes. I am also concerned that I am not handling the RPM channel correctly. I would appreciate some help with this. I have also attached a picture of the Shiftx light mount I made for my dash and a picture of my Racecapture installation, Thanks

Code: Select all

--AIM Smarty Cam Stream For Race Capture

--how frequently we poll for CAN messages
tickRate = 30
--the CAN baud rate
CAN_baud = 1000000
--CAN channel to listen on. 0=first CAN channel, 1=second
CAN_chan = 0
--1 for Big Endian &#40;MSB&#41; mode; 0 for Little Endian mode &#40;LSB&#41;
be_mode = 0

--add your virtual channels here
tpsId = addChannel&#40;"TPS", 10, 0, 0, 100, "%"&#41;
tempId = addChannel&#40;"EngineTemp", 1, 0, 0, 120, 'C'&#41;
oilTempId = addChannel&#40;"OilTemp", 1, 0, 0, 170, 'C'&#41;
rpmId = addChannel&#40;"RPM", 10, 0, 0, 10000, 'RPM'&#41;
oilPresId = addChannel&#40;"OilPress", 10, 2, 0, 10, 'Bar'&#41;
fuellevelId = addChannel&#40;"FuelLevel", 1, 0, 0, 120, "L"&#41;
temp1Id = addChannel &#40;"HeadTemp" , 1, 0, 0, 170, 'C'&#41;

temp2Id = addChannel &#40;"EGT" , 10, 0, 0, 1000, 'C'&#41;
ch1Id = addChannel &#40;"BrakePos", 10, 0, 0, 100, "%"&#41; 
ch2Id = addChannel &#40;"ClutchPos", 10, 0, 0, 100, "%"&#41;
ch3Id = addChannel &#40;"Brake", 1, 0, 0, 150, "Bar"&#41;
ch4Id = addChannel &#40;"Steering", 1, 0, -300, 300, "Deg"&#41;
ch5Id = addChannel &#40;"Lambda", 10, 2, -1, 1, "C"&#41;
fuelPresId = addChannel &#40;"FuelPress", 10, 2, 0, 10, "Bar"&#41;

gearId = addChannel &#40;"Gear", 10, 0, 0, 7, "#"&#41;

--customize here for CAN channel mapping
--format is&#58; &#91;CAN Id&#93; = function&#40;data&#41; map_chan&#40;<channel id>, data, <CAN offset>, <CAN length>, <multiplier>, <adder>&#41;
CAN_map = &#123;
&#91;1056&#93; = function&#40;data&#41; map_chan&#40;rpmId, data, 0, 2, 1, 0&#41; map_chan&#40;gearId, data, 4, 2, 1, 0&#41; map_chan_le&#40;tempId, data, 6, 2, 0.1, 0&#41; end,
&#91;1057&#93; = function&#40;data&#41; map_chan&#40;temp1Id, data, 0, 2, 0.1, 0&#41; map_chan&#40;temp2Id, data, 2, 2, 0.1, 0&#41; map_chan&#40;oilTempId, data, 4, 2, 0.1, 0&#41; map_chan_le&#40;oilPresId, data, 6, 2, 0.01, 0&#41; end,
&#91;1058&#93; = function&#40;data&#41; map_chan&#40;ch3Id, data, 0, 2, 0.01, 0&#41; map_chan&#40;tpsId, data, 2, 2, 1, 0&#41; map_chan&#40;ch1Id, data, 4, 2, 1, 0&#41; map_chan&#40;ch2Id, data, 6, 2, 1, 0&#41; end
,
&#91;1059&#93; = function&#40;data&#41; map_chan&#40;ch4Id, data, 0, 2, 1, 0&#41; map_chan&#40;ch5Id, data, 2, 2, 0.01, 0&#41; end,
&#91;1060&#93; = function&#40;data&#41; map_chan&#40;fuellevelId, data, 0, 2, 1, 0&#41; map_chan&#40;fuelPresId, data, 2, 2, 0.1, 0&#41; end
&#125;

function onTick&#40;&#41;
    processCAN&#40;CAN_chan&#41;
 onLites&#40;&#41;
end

--===========do not edit below===========
function processCAN&#40;chan&#41;
    repeat
        local id, e, data = rxCAN&#40;chan&#41;
        if id ~= nil then
            local map = CAN_map&#91;id&#93;
            if map ~= nil then
                map&#40;data&#41;        
            end
        end
    until id == nil
end

--Map CAN channel, little endian format
function map_chan_le&#40;cid, data, offset, len, mult, add&#41;
    offset = offset + 1
    local value = 0
    local shift = 1
    while len > 0 do
        value = value + &#40;data&#91;offset&#93; * shift&#41;
        shift = shift * 256
        offset = offset + 1
        len = len - 1
    end
    setChannel&#40;cid, &#40;value * mult&#41; + add&#41;
end

--Map CAN channel, big endian format
function map_chan_be&#40;cid, data, offset, len, mult, add&#41;
    offset = offset + 1
    local value = 0
    while len > 0 do
        value = &#40;value * 256&#41; + data&#91;offset&#93;
        offset = offset + 1
        len = len - 1
    end
    setChannel&#40;cid, &#40;value * mult&#41; + add&#41;
end

function onLites&#40;&#41; 
 local r = RPM 
 if r > 1500 then setGpio&#40;2,1&#41; else setGpio&#40;2,0&#41; end 
 if r > 1800 then setGpio&#40;1,1&#41; else setGpio&#40;1,0&#41; end 
 if r > 2100 then setGpio&#40;0,1&#41; else setGpio&#40;0,0&#41; end
end


map_chan = &#40;be_mode == 1&#41; and map_chan_be or map_chan_le
initCAN&#40;CAN_chan, CAN_baud&#41;
setTickRate&#40;tickRate&#41;

Attachments
1470017943474.jpg
1470017943474.jpg (55.45 KiB) Viewed 3102 times
1470017943746.jpg
1470017943746.jpg (49.8 KiB) Viewed 3102 times

rdoherty
Posts: 215
Joined: Fri Mar 08, 2013 3:32 am

Post by rdoherty »

Hi m3drvr, awesome setup! Looking at your script, the onLites function tries to reference a variable named 'RPM', but from what I can see, it doesn't exist.

For your onLites function, I would use the getChannel() function (https://wiki.autosportlabs.com/RaceCapt ... ame.3E_.29), which allows you to read any channel value.

In order to debug this script, I would add some println calls to figure out if your script is working properly. It looks like it should be calling onLites(), how are you determining that the processCan() function never terminates?
Ryan Doherty
Autosports Labs

m3drvr
Posts: 6
Joined: Sun Jul 31, 2016 8:36 pm

Post by m3drvr »

Ryan, thanks for the response. Before I sent the original post, I used println to determine that the script never exited the processCAN function. Based on your recommendation, I added the println commands below to track the state of the ID variable.

function processCAN(chan)
repeat
local id, e, data = rxCAN(chan)
if id ~= nil then
local map = CAN_map[id]
if map ~= nil then
map(data)
end
println(id)
end
until id == nil
println("CAN")
end

Here is part of the debug log output:
1070.0
66.0
67.0
68.0
69.0
70.0
71.0
72.0
73.0
74.0
75.0
76.0
77.0
78.0
79.0
64.0
65.0
66.0
67.0
68.0
1059.0
69.0
70.0
71.0
72.0
73.0
74.0
75.0
76.0
77.0
78.0
79.0
1060.0
64.0
65.0
66.0
67.0
68.0
69.0
70.0
71.0
72.0
1061.0
73.0
74.0
75.0
76.0
77.0
78.0
79.0

It looks like the ID value never reaches nil so the loop never ends.

I have been looking through the forum for another post about this. Since the script works well at providing data to the dash, perhaps no one else has attempted to use it to process data for other uses. I did find one post where someone was trying to use the CAN data to obtain wheel speeds. viewtopic.php?p=23925 but it was never resolved.

Please take a look at this and let me know what you think. I don't fully understand how the CAN mapping works so I am out of ideas. Thanks

rdoherty
Posts: 215
Joined: Fri Mar 08, 2013 3:32 am

Post by rdoherty »

Hi m3drvr, your script looks correct to me. I chatted with our firmware engineers, what is most likely happening is the Lua script can't read messages from the CAN bus fast enough, so the id variable never is set to null (which happens when there are no more CAN messages to read).

What you can do is create a counter in the processCAN function that is incremented after each message, and have the function exit after a set number of messages. Something like this (untested):

function processCAN(chan)
counter = 0
repeat
local id, e, data = rxCAN(chan)
if id ~= nil then
local map = CAN_map[id]
if map ~= nil then
map(data)
end
println(id)
end
counter = counter + 1
until (id == nil or counter > 10)
println("CAN")
end

Give that a shot, hope it works!
Ryan Doherty
Autosports Labs

m3drvr
Posts: 6
Joined: Sun Jul 31, 2016 8:36 pm

Post by m3drvr »

It worked great! The lights are a little sluggish and erratic. I went through the script and removed all of the extra functions and two of the CAN map lines. That improved the performance. Next, I plan to cut the script down to only monitor or log the specific data I need. I'm looking forward to seeing how this works at the track. Thanks for your help.

rdoherty
Posts: 215
Joined: Fri Mar 08, 2013 3:32 am

Post by rdoherty »

Great! If it's sluggish, that may be because the processCAN function is exiting early because it is set to exit after 10 messages. You could try increasing it to 20, or you could increase the tickRate to 50 which may help.
Ryan Doherty
Autosports Labs

MikeD
Posts: 39
Joined: Sun May 29, 2016 11:55 am
Location: Austria
Contact:

Post by MikeD »

Hey guys,

I had the same problem with the script never exiting the processCAN function ... my workaround was a similar solution to yours but instead of using a simple counter I used the getUptime() function (which returns milliseconds from start of the unit IIRC).

With this solution I managed to exit the processCAN function at least after a max dedicated time intervall (in my example down below after a maximum of 40ms) and therefore hoped to not loose much of the CAN messages which is otherwise the case if you interrupt after just a few messages... on the other hand I still have the confidence to get back to the onTick function after the max time that I need for any other functions happening in the onTick loop.

I fed different sinus and other waveform CAN signals into the RCP and observed the datalogs over and over ... this took me lot's of hours trying different scripts until I could get nice and clean, non-interupted waveforms out of the datalogger, which was the final confirmation that I did not lost any sensible amount of CAN packets.

The script example down below is just MY way of reading a dedicated CAN stream that I wanted to receive from a system (wheelspeeds and such at 100Hz broadcast rate), but others may need to use different rates, the simple counter workaround described on a former post from Ryan, a combination of timer and counter or any other approach in case loosing CAN packets is a no-go.

I hope there will be a hardcoded CAN solution in the future for handling CAN messages in a way so that NOTHING gets lost even under high bus load conditions.

Cheers, Mike

EDIT: script example edited

function processCAN(chan)
local maxtime = getUptime() + 40 --max time in ms for loop interupt
repeat
local id, e, data = rxCAN(chan)
if id ~= nil then
local map = CAN_map[id]
if map ~= nil then
map(data)
end
end
until (id == nil) or (maxtime < getUptime())
end

rdoherty
Posts: 215
Joined: Fri Mar 08, 2013 3:32 am

Post by rdoherty »

Thanks Mike! I'm curious, what tickRate are you setting?

We are thinking about a method of custom CAN configuration that would allow for never losing CAN data, it would involve specifying the CAN ids and conversion formulas via the app. Then RCP can watch the CAN stream for you and log the values.
Ryan Doherty
Autosports Labs

MikeD
Posts: 39
Joined: Sun May 29, 2016 11:55 am
Location: Austria
Contact:

Post by MikeD »

Ryan,

I don't remeber for sure the various script versions that I wrote.
It was one or two months ago when I did these scripting tests and 100+ datalogs to make high traffic CAN reception work for me... tried 30, 50, 100, 200 500, 1000 tick rate... but don't remember which tick rates I used with the counter interupt and which ones with just the regular CAN script, I only remember that it took me endless hours until I was confident to receive the data I was looking for :-)

During my tests I also used the regular countdown way of doing the loop interupt (the script as you desribed above) and it was working fine with a message counter of 40-60 IIRC, so to add to my post from above the easier way for sure is the way you, Ryan, described above.
However I thought to post the timed variant of the script too for those who prefer this kind of doing it.

It may depend a lot on what you want to achieve: Receive as much as possible CAN data or have something very critical happening in the OnTick() function.
In my case I wanted to receive and log lots of CAN data as good as possible and only swap over to the regular OnTick function from time to time to start/stop logging and convert some parameters (GPS Speed from MPH to KPH for example). I'm sure there are various ways of doing what you want with the script... and the best would be the one you hopefully will have up and running in the future where you only have to specify CAN ID's, byte/bit position, factors, offsets and do the rest in the background by firmware...

-Mike

m3drvr
Posts: 6
Joined: Sun Jul 31, 2016 8:36 pm

Post by m3drvr »

Mike,

I finally had a chance to try your getUptime idea. It works perfectly. The shift lights turn on and off without delay. I am now willing to trust them on the track.

Thanks to you and Ryan for your help.

Bruce

Post Reply