- Mon May 17, 2021 1:23 am
#50378
Just wanted to share a small project I have been working on, which translates the Serial3 protocol to the NBP protocol used by TrackAddict (https://racerender.com/TrackAddict/docs/Interop.html), allowing for data collection and overlay using RaceRender. Uses an Arduino Nano and a HC-06 Bluetooth module, configured to run at 115200 baud.
Power and ground are connected to the Speeduino 5V and ground respectively of course.
Nano TX goes to the HC-06 RC.
Nano RX goes to Speeduino Serial3 TX.
Nano A5 goes to Speeduino Serial3 RX - the Nano uses software serial to request the data.
Right now I have it running at ~20hz.
Power and ground are connected to the Speeduino 5V and ground respectively of course.
Nano TX goes to the HC-06 RC.
Nano RX goes to Speeduino Serial3 TX.
Nano A5 goes to Speeduino Serial3 RX - the Nano uses software serial to request the data.
Right now I have it running at ~20hz.
Code: Select all
// Speeduino Serial3 to TrackAddict NBP translation
// Uses Arduino Nano + HC-06 Bluetooth module configured to 115200 baud
// 5/15/2021
//SoftwareSerial, dedicated to requesting data from Serial3
#include <SoftwareSerial.h>
SoftwareSerial Request(18, 19); // RX, TX - D18 is A4, D19 is A5
//Setup Serial3 Data Array
byte S3Data[75];
//Setup Time variables
unsigned long StartTime=0;
unsigned long LoopTime=0;
void setup()
{
// Start serial connections at 115200 Baud
Serial.begin(115200);
Serial.setTimeout(10); //Set Serial3 read timeout to 10ms
//Start softserial for requests
Request.begin(115200);
//Initialize Serial3 Data Array empty
for ( int i = 0; i < 76; ++i ){S3Data[i]=0;}
}
void loop()
{
//Set Start Time
StartTime=millis();
//Read Serial3 Data into buffer, discarding first value as "A" response
if(Serial.available() > 0){Serial.read();Serial.readBytes(S3Data,75);}
//Request data using SoftwareSerial for next loop
Request.println("A");
//delay(1000);
//Output NBT
//Spec: https://racerender.com/TrackAddict/docs/NBP%20Specification.pdf
// "UPDATEALL" tells our consumers that we're sending fresh sample data for every channel we have
// "UPDATE" would be used when we only update certain channels but might not include all of our channels
// "ALL" would be if we send the current valuesfor every channel we have, but it isn't necessarily new data
// Dummy data for testing
// Name, Unit, Value -OR -Name, Value
//Generate Header
static char header[64];sprintf(header, "*NBP1,UPDATEALL,%d.%03d", (int)(StartTime / 1000), (int)(StartTime % 1000));
//Print Header
Serial.println(header);
//Print Data - Use bit numbers as array index, see documentation: https://wiki.speeduino.com/en/Secondary_Serial_IO_interface
/*
* 0 - currentStatus.secl - secl is simply a counter that increments each second
1 - currentStatus.squirt - Squirt Bitfield
2 - currentStatus.engine - Engine Status Bitfield
3 - (byte)(divu100(currentStatus.dwell)) - Dwell in ms * 10
4 - lowByte(currentStatus.MAP)
5 - highByte(currentStatus.MAP)
6 - (byte)(currentStatus.IAT + CALIBRATION_TEMPERATURE_OFFSET) - mat
7 - (byte)(currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET) - Coolant ADC
8 - currentStatus.batCorrection - Battery voltage correction (%)
9 - currentStatus.battery10 - battery voltage
10 - currentStatus.O2 - primary O2
11 - currentStatus.egoCorrection - Exhaust gas correction (%)
12 - currentStatus.iatCorrection - Air temperature Correction (%)
13 - currentStatus.wueCorrection - Warmup enrichment (%)
14 - lowByte(currentStatus.RPM) - rpm LB
15 - highByte(currentStatus.RPM) - rpm HB
16 - currentStatus.TAEamount - acceleration enrichment (%)
17 - currentStatus.corrections - Total GammaE (%)
18 - currentStatus.VE - Current VE 1 (%)
19 - currentStatus.afrTarget - chosen afr target
20 - lowByte(currentStatus.PW1) - Pulsewidth 1 multiplied by 10 in ms. Have to convert from uS to mS.
21 - highByte(currentStatus.PW1) - Pulsewidth 1 multiplied by 10 in ms. Have to convert from uS to mS.
22 - currentStatus.tpsDOT - TPS DOT
23 - currentStatus.advance - Current spark advance
24 - currentStatus.TPS - TPS (0% to 100%)
25 - lowByte(currentStatus.loopsPerSecond) - loops per second LB
26 - highByte(currentStatus.loopsPerSecond) - loops per second HB
27 - lowByte(currentStatus.freeRAM) - freeRam LB
28 - highByte(currentStatus.freeRAM) - freeRam HB
29 - currentStatus.boostTarget - Target boost pressure
30 - currentStatus.boostDuty - current pwm boost dutycycle
31 - currentStatus.spark - Spark related bitfield
32 - lowByte(currentStatus.rpmDOT) - rpmDOT must be sent as a signed integer
33 - highByte(currentStatus.rpmDOT) - rpmDOT HB
34 - currentStatus.ethanolPct - Flex sensor value (or 0 if not used)
35 - currentStatus.flexCorrection - Flex fuel correction (% above or below 100)
36 - currentStatus.flexIgnCorrection - Ignition correction (Increased degrees of advance) for flex fuel
37 - currentStatus.idleLoad - idleload
38 - currentStatus.testOutputs - testoutputs bitfield
39 - currentStatus.O2_2 - Second O2
40 - currentStatus.baro - Barometer value
41 - lowByte(currentStatus.canin[0]);
42 - highByte(currentStatus.canin[0]);
43 - lowByte(currentStatus.canin[1]);
44 - highByte(currentStatus.canin[1]);
45 - lowByte(currentStatus.canin[2]);
46 - highByte(currentStatus.canin[2]);
47 - lowByte(currentStatus.canin[3]);
48 - highByte(currentStatus.canin[3]);
49 - lowByte(currentStatus.canin[4]);
50 - highByte(currentStatus.canin[4]);
51 - lowByte(currentStatus.canin[5]);
52 - highByte(currentStatus.canin[5]);
53 - lowByte(currentStatus.canin[6]);
54 - highByte(currentStatus.canin[6]);
55 - lowByte(currentStatus.canin[7]);
56 - highByte(currentStatus.canin[7]);
57 - lowByte(currentStatus.canin[8]);
58 - highByte(currentStatus.canin[8]);
59 - lowByte(currentStatus.canin[9]);
60 - highByte(currentStatus.canin[9]);
61 - lowByte(currentStatus.canin[10]);
62 - highByte(currentStatus.canin[10]);
63 - lowByte(currentStatus.canin[11]);
64 - highByte(currentStatus.canin[11]);
65 - lowByte(currentStatus.canin[12]);
66 - highByte(currentStatus.canin[12]);
67 - lowByte(currentStatus.canin[13]);
68 - highByte(currentStatus.canin[13]);
69 - lowByte(currentStatus.canin[14]);
70 - highByte(currentStatus.canin[14]);
71 - lowByte(currentStatus.canin[15]);
72 - highByte(currentStatus.canin[15]);
73 - currentStatus.tpsADC - TPS (Raw 0-255)
74 - getNextError() - Error codes
*/
Serial.print("\"RPM\":");Serial.println((S3Data[15] << 8) + S3Data[14]); //Convert 2byte RPM to Int and print
Serial.print("\"Throttle\",\"%\":");Serial.println(S3Data[24]); //Print TPS
Serial.print("\"MAP\",\"kpa\":");Serial.println((S3Data[5] << 8) + S3Data[4]);; //Convert 2byte MAP to Int and print
Serial.print("\"AFR\":");Serial.println(S3Data[10]/10.0,1); //Print AFR
Serial.print("\"Coolant\",\"deg f\":");Serial.println(((S3Data[7]-40)*1.8)+32,0); //Subtract offset of 40 and convert deg C to F, Print CLT
Serial.println("#");
//delay until loop has taken 50ms
LoopTime=((millis()-StartTime)/10);
delay(37-LoopTime);// 50ms interval = 20 Hz, custom tuned from results in this case
}