I've just released my first build in open source. It's reference device to showcase the new Bluetooth LE "do it yourself" APIs for GPS and CAN-Bus data in RaceChrono Pro v6.0 (beta). Let me know what you think.
https://github.com/aollin/racechrono-ble-diy-device/
Comments
Thumbs up
I would like to build it this winter.
Thanks
Your code is very nice but complicated for me.
I read some documentations on the BLE Characteristics but I have also to understand the CAN Bus documentation.
Somes years ago, I read the Serial Stream on a old ECU with an Arduino, it was easier.
Thanks for your explanations, I will order the parts and try it.
I received my parts and I look at more your code.
The Arduino IDE doesn't want to compile because the Adafruit's library has been modify.
I made two corrections but I can't test it for the moment.
1°:
void canBusFilterWriteCallback(BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset) {
to
void canBusFilterWriteCallback(uint16_t offset,BLECharacteristic* chr, uint8_t* data, uint16_t len ) {
2°:
void bluetoothStart() {
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
Bluefruit.begin();
uint8_t mac[6] = { 0, 0, 0, 0, 0, 0 };
Bluefruit.Gap.getAddr(mac);
to
void bluetoothStart() {
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
Bluefruit.begin();
uint8_t mac[6] = { 0, 0, 0, 0, 0, 0 };
Bluefruit.getAddr(mac);
It compile now, I don't use the GPS'code.
If you can look at and say me if I am in the good way.
Best Regards
I would like to briefly introduce my project to you. I have an old Kawasaki ZX6R (2002, carburetor) which has neither OBD-II nor CAN-Bus. Instead I collect my data using some sensors and an Arduino with Bluetooth LE. I have a 9-DOF IMU to read accelerations and calculate lean angle. By counting pulses (frequency measurement) I get the pretended speedometer value and RPM and calculate the gear form that. There´s an external GPS board connected to the Arduino, so I know the real speed and can send a corrected pulse signal to the speedometer. The throttle grip position can be determined by measuring the voltage of throttle valve sensor and so on... quite a bit of stuff already.
Thanks to the documentation on GitHub from the first post, I was able to get all the Bluetooth LE stuff to work already! (I haven't tested GPS yet).
So far I have 3 questions/remarks:
1. OBD-II channels vs. CAN-Bus channels
I see in OBD-II channels I can define a postfix, so for example I can separate suspension travel front and rear.
Is that option missing for CAN-Bus channels? I couldn´t figure out how this can be done.
2. Combined acceleration
I know the combined acceleration is usually calculated by RaceChrono from the GPS data. As I can directly measure all accelerations very fast and reliable, I want to use it rather than GPS data. I can send longitudinal and lateral acceleration in separate channels, but how to combine them? There is an entry "combined acceleration" in the list of channels that can be created, but how is it meant to use?
3. Bluetooth LE payload
To increase the data throughput, I packed several data values under the same PID. For example Gear and RPM can be packed in only 2 bytes (3 bit for Gear, 13 bit for RPM/2). Throttle and Brake position need only one byte each and so on. You can put a lot of different data into a single 8-byte parameter. This way I achieve much higher update rate because I don´t have to send the BLE characteristic everytime for each single value.
Actually a BLE characteristic can transfer 20 byte, 4 bytes are used for the PID, so 16 bytes of payload would be possible. Unfortunately only 8 bytes can be accessed in the channel. Is it worth considering increasing the bytes per chanel?
Thanks for your great work and documentation!
Best regards
Sounds like a great project! I've been thinking adding a dedicated IMU input API, but it will work through CAN-Bus API too
1. I will check that there's no bug related to this. It should work just same as OBD-II...
2. Combined acceleration is basically sqrt(pow(lateralAcc) + pow(longitudinalAcc)). You can easily add that channel, if you have both accelerations available.
3. Yep, I don't see a reason why it could not be variable packet length. I will experiment with it, and if it works will roll out to next major version.
2. Ah now I see the combined acc channel is a scalar value. I misunderstood that.
I thought about the video overlay for combined acceleration which is a 2-D chart.
I can show longitudinal acc and lateral acc channel in two charts, then the red dot is bouncing up and down in one chart and bouncing side to side in the other chart.
When I add the combined acc channel from GPS to the overlay this is obviously a 2-D value.
So my problem is, I have 2 scalar values (longitudinal and lateral acc) from CAN-Bus channels and I want them to show up combined in the 2-D chart. I can calculate the vector length like you suggested, but then it is still a scalar (1-D value) value, not showing the direction of the force.
Best regards
2. That gauge uses three different channels. The number is "combined" and y-axis is "longitudinal" and x-axis is "lateral". What you need to do is to calculate lateral and longitudinal accelerations from your 3D IMU. For a car it would be easy, just point your sensor so that one axis is aligned to lateral and one is longitudinal. For a motorbike longitudinal would work same way, but to get lateral acceleration, you need to rotate the 3D matrix according to the lean angle...
PS. Both 1. and 3. will be addressed in the next release, minor or major which ever comes first.
I created 3 CAN-Bus channels, lateral acc, longitudinal acc and combined acc. I add a new analog gauge to the video overlay, device is CAN-Bus and channel is combined accerleration. What I get is an empty gauge (no red dot) with just the number G (combined). Additionally I can add a gauge for lateral and longitudinal acceleration. So I have 3 gauges confirming that I send 3 reasonalbe values but I can´t get them all into a single gauge.
When the device is changed to GPS and channel is combined acceleration, I get the numger G and the red dot moving around x and y axis according to the accelerations calculated form the GPS. This is what I want to achieve but with my CAN-Bus values, not from GPS.
I think the combined acceleration gauge has no idea where to get the x and y data from when the device is different from GPS.
@GiuseppeBinomi / @aol What kind of update rate can you do with the BLE solution (Android)? I am still using RC3 over RFCOMM with my solution but I am starting to figure out what I want to change data logger wise for next season. Trying to figure out if I want to do my 100Hz logging to SD Card still and just pass minimum data to RC. Or can I use RC as the main logging and pass all the data to it?
Thanks, Jeff
I'm _thinking_ about a RC4 protocol that would be RFCOMM (or TCP/IP) and ASCII just like RC3, but interpreted from hexadecimal values same way as the CAN-Bus messages - with an equation and channel settings. This way you could transmit anything and translate the data in RaceChrono just like you wish...
My current thinking is to continue with my path from last season. Log enough for timing and at track analysis over RC3 which already includes what I need at 10-20hz. And log high bandwidth (brake sensor, suspension movement) directly to SD Card in CSV or Circuit Tools format for later looking at if I see something I want to zoom into (ie chatter or specific issue in a corner). Thinking about possibilities of playing with GPS RTK modules to see if I can get GPS tracking into the sub-meter range.
Thanks for all the hard work you put into the app!
Jeff
Now my DIY roadmap is to add more Bluetooth LE APIs, and maybe introduce the new RC4 format.
Jeff
I would like to get rpm etc. signals from CBR 1000rr so I guess those ”optional” arduino components are for that purpose since that bike has only k-line? Can I get data from it with that MCP2515 board? Sorry for this. I’m interested in getting data to racechrono to support my driving but my wallet is far away from mr. Trumps wallet 😄
As for your wallet, the components to do much of this are not overly expensive. Its the development of the logic to gather the data and send it to RC that takes the time etc.
Jeff
And if you do not want to use the Adafruit nRF motherboard ($25), you'll need to replace the Bluetooth LE library (and usage), as this library is specific for the board. I selected Adafruit as the Bluetooth LE stack works very reliably on it.
I think all the selected parts are relatively inexpensive, with the exception of the GPS board. At $40 it's very expensive, and you should be able to find better and cheaper alternatives. You can also leave it out, as it was added just as an afterthought. You could use separate Bluetooth GPS instead that you already may own.
@aol Meanwhile I tried to transmit my GPS data. Thanks to the various connection options, I can read out the GPS module and send data to the serial monitor at the same time. So I can assure my GPS data look totally reasonable and valid. But no matter what I tried yet, RaceChrono tells me there is no connection to satellites and the GPS receiver displays "Waiting for data". At the same time the CAN Bus channels receive all their data.
Actually I thought feeding the gpsMainCharacteristic and gpsTimeCharacteristic should be the easiest part. I created main and time data exactely as you did in your code. Are there any requirements that I've overlooked so far? Like minimum update rate or minimum count of satellites? I even tried to send hard coded valid data with only timestamp updated via micros() function. If I could not successfully transmit my other data, I would doubt whether data will be received at all.
My service UUID is 00001ff8-0000-1000-8000-00805f9b34fb as you wrote in your protocol description. In your code it is 0x00000001000000fd8933990d6f411ff8. I have not yet checked what this is about because the two CanBus characteristics work fine (I also receive the filter messages even if I ignore them).
I am grateful for any hints.
Did you click the GPS checkbox when you configured the "DIY device" in RaceChrono settings? If you didn't then RaceChrono is not listening to the GPS characteristics.
But it stays connected all the time. This means RaceChrono can find my two GPS characteristics and listens to them, right? At least that's something. So I know that something must be wrong within my data.
Apart from a missing fix, what would cause the app to discard the data? Probably incorrect/missing time synchronization? It´s a bit of a shame that the packet limit is 20 bytes, 24 would be just perfect... Anyway I will double check everything in my code and use the nRF connect App to verify what is actually sent.
Thanks a lot!
You should see the "gauges" instead of the "Waiting for data..." when these preconditions are valid:
1) Last data from UUID 3 (GPS main) is exactly 20 bytes
2) Last data from UUID 4 (GPS time) is exactly 3 bytes
3) Sync bits (3 first bits of both data) match
RaceChrono does a read and registers for notifications for UUID 3. The UUID 4 is only read when needed. This saves bandwidth on nRF chipset for some reason, even if the UUID 4 is never notified.
Now this might be of some interest: When I delete my DIY device and create it again configured only with checkbox GPS, it works! I started several sessions, I did several reconnects, it works as intended. My Arduino code is the same for both DIY configurations, so what could be different when CAN-Bus is checked or not?
We use differenct hardware (Arduino nRF52840, Adafruit nRF52832) and BLE libraries, but in the end that shouldn´t matter.
My Arduino gets data from the GPS module with 10Hz Update rate. Everytime new set of GPS data is ready, it writes all 3 BLEcharacteristics, canBusMain, gpsMain and gpsTime at the same time (actually one after another with no delay). With only GPS checked in DIY device, RaceChrono can always find the GPS data and receives it reliable with 10Hz. After that I tried it over and over again with both GPS and CAN-Bus checked and it looks like it work once out of 10 or 15 attempts.
That means when RaceChrono does not show my GPS data from the beginning, it never will. But when GPS is found at the start, it´s running like a charm. Both GPS and CAN data came in exactly with 10Hz.
This connection lottery is the same when I write the characteristics in different order or when GPS and CAN-BUS data is written at different times (like in your code, writing CAN-Bus and GPS data individually when they are availalbe).
I am not sure if I understood how UUID 4 is read. I understand that it rarely contains necessary new data and is therefore not read continuously. Is it re-read whenever the sync bits differ?
1) Device built with HAS_CAN_BUS seems to work fine. This device configured in RaceChrono settings as "RaceChrono DIY" with "CAN-Bus" seems to work fine.
2) Device built with HAS_CAN_BUS and HAS_GPS seems to work fine. This device configured in RaceChrono settings as "RaceChrono DIY" with "CAN-Bus" and "GPS" seems to work fine. Also when configured with "CAN-Bus" alone and "GPS" alone it seems to work fine.
3) When adding DIY device in RaceChrono settings, make sure to delete the old one first. The GPS and CAN-Bus checkbox state is not changed, if you just add the new one on top of the old one. This is a bug and should be fixed.
Maybe you had problem 3) with your setup? Please try again. UUID 4 is read whenever the sync bits don't match.
PS. Updated the git repository for latest libraries and board software.
My Arduino is running all the time while I play around with RaceChrono and change the DIY device configuration. It always sends the 3 characteristics in the same way, as it can´t see which of them I actually configured in RaceChrono.
So for me CAN-Bus alone and GPS alone works always. When I configure both, then the CAN-Bus works always but GPS only in maybe 10% of the attempts. It works either directly from the start or never.
Anyway your answers are always helpful because everytime I understand a little bit more of what´s going on on the App side. So I´ll try what happens when I increment the sync bit just after RaceChrono has connected.
We are getting closer to it
I must assume it's some problem at your device side...