Introduction: Racing Datalogger With an Arduino

This is an old project of mine that I got asked a couple of times during a trackday and figured I'd post it up for people interested. There are some current limitations such as data resolution and data syncing from different inputs, but it's a good way to get your feet wet into data logging. There are many ways to do this, the most popular one is dumping data from the OBD port. This can all be done nowadays with an OBD bluetooth module and a smartphone since it has GPS. But, my motorcycle doesn't have OBD and OBD is missing a key input which is the brakes. So we'll go straight to the source and do it manually. The 3 main sensor inputs that I believe are the most important to look at for a beginner are 1: Throttle Position, 2: Brakes and 3: Position (GPS). These are where the most improvements can be made to improve your laptime. There's a big problem with the brake sensor though, most cars/motorcycles don't come with a pressure transducer sensor in the brake line, but we can tap into the brake light circuit. The downside to this is that the brake light only has a one bit resolution (ie. digital signal), its either on or off. You won't be able to tell from the data how hard you're pressing the brakes, only when you started to press it or when you let go. Other then that, let's get started!*

*You must be knowledgeable with the electronic system of the car/motorcycle you are trying to data log! Unfortunately, I cannot help you in this area since it varies from car to car / motorcycle to motorcycle. However, I will try my best to describe it to you and point you in the right direction.

Step 1: Find Brake Light Wire and Throttle Position Sensor

Before we get started you have to be able to find these two points on your car/motorcycle otherwise we cannot continue any further. You're going to need a multimeter and whatever tools you need to get to these parts, hopefully just screwdriver and ratchet set. The Factory Service Manual for your car/motorcycle definitely helps also!

Brake Light:

This is the easiest of the two so lets get started on that. Just find the plug that goes directly in your rear brake light. There should only be 3 wires. One is ground, one is for illumination for night time, and 3rd one is the actual brake light. Press and hold down the brake somehow (either have someone else do it, a brick, etc.), set your multimeter to VDC and start probing with reference to chassis ground. The one with +12V with the brake pressed will be the one you're interested in. Mark it down and we'll get back to it later.

Throttle Position Sensor (TPS):

This one I would recommend looking at your Factory Service Manual to see how it operates and where its located. In general it will be by your Intake Manifold on the Throttle Body (obviously). If you your Throttle Body is cable driven, you should be able to find it fairly easy by pressing the gas / pulling the throttle and tracing the cable. The sensor nearest to cable lever on the throttle body should the TPS and should have 3 wires. One is the reference/supply voltage, one is ground, and the other throttle position output. The last one is the one you are interested in. With the Key On/Engine Off (KO/EF) probe the 3 wires with reference to chassis ground. The reference will be the highest voltage of the three, Ground should be 0, and the throttle position output will be the one in the middle, typically very low <1V. You can confirm this with the KO/EF, and have someone press the gas / pull the throttle and you'll see the voltage change. Still with the KO/EF, record the idle voltage, and then with the gas pressed / throttle pulled all the way down record it again. These will be your min and max throttle values. Mark this sensor down and we'll get back to this later when we start to wire things up.

Step 2: Bill of Materials

  • 1 x Arduino Uno
  • 1 x USB Cable for Arduino
  • 1 x iTead Studio GPS Shield (NEO-6M model)
  • 1 x MicroSD Card (1GB is fine)
  • 1 x Protoshield
  • 1 x Optocoupler (PC817)
  • 1 x USB Power Port Socket for Motorcycles
  • 1 x 750 Ohm Resistor
  • 1 x 7.5 kOhm Resistor
  • 1 x 150 Ohm Resistor
  • 2 x Push Button Switches
  • Wires

Tools:

  • Multimeter
  • Soldering Iron
  • Electrical Crimpers
  • Wire Stripper
  • Screwdriver
  • Ratchet set

You can ignore the rest below if you just want to just buy the parts and have a working setup without any headache, but I'll list some ways you can save money and buy generic parts. The iTead Studio GPS Shield is just a NEO-6M module, an SD Card module, a u.FL to SMA adapter, and a GPS SMA Antenna. The USB Power Port Socket for Motorcycles is just some terminal connectors to the battery, an inline fuse holder, and USB car charger. By ordering the parts individually from China you can probably cut the cost in half for this project, but YMMV depending on your technical skills so just giving you a heads up.

Step 3: Code

Link to code.

Before flashing the code to the Arduino Uno find this function:

void getSensorData() {
// 0% Throttle reads approx 0.66 V = 135, 100% Throttle reads 3.87 V = 793
  tpsValue = map(analogRead(TPS_PIN), 135, 793, 0 , 100); 
  brakeValue = map(digitalRead(BRAKE_PIN), 0, 1, 0, 100);
}

Divide your idle voltage value you recorded from the TPS by 1023 and replace 135 in the code with the value calculated. Now do same with maximum voltage and replace the value 793 with it.

After that's complete, compile and flash the code above to your Arduino Uno, make sure nothing else is connected. Once successfully go ahead and disconnect for now, I will explain the code later.

Step 4: Schematic & Wiring It Up

As you can see the schematic is fairly straightforward.

  1. The USB Power Adapter for the Motorcycle hooks up directly to the battery on your car/motorcycle. Internally it is fused to protect the circuit (not drawn in schematic). From the adapter you can now connect it to your Arduino via USB, but don't do it yet.
  2. Stack the iTead Studio GPS Shield on top of your Arduino. Make sure you toggle the switch to the correct operating voltage, chances are it is 5V as shown in schematic. Set Digital Pin 0(D0) as TX and Digital Pin 1(D1) as RX with the jumpers that came with the kit. See picture of shield for confirmation. This is an easy trap for beginners because on the Arduino, D0 is RX and D1 is TX. This just means that the iTead Studio Shield is Transmitting data Out of D0 and the Arduino is going to Receive it on D0, and the reverse for D1. Now, put MicroSD card into slot and make sure it is formatted to either FAT or FAT32.
  3. From here on out, figure out where you want to place your Arduino along with where you want to place your pushbutton switches, LED, and GPS antenna so you can plan your wire lengths accordingly. The pushbutton switches should be easy to access, LED should be easily visible, and GPS antenna exposed so it has better reception.
  4. Once you figured that all out we can get to the fun stuff. Tap into the TPS wire you marked earlier, you can either use a T-Tap Connector or cut and resolder, I'll leave that up to you. Do the same for the Brake Light wire.
  5. Stack the Protoshield on top of the iTead Studio GPS Shield and wire up the optocoupler and resistors, see pictures/schematics/documentation for reference.
  6. Now hook up the reset push button switch to the reset pin(RST) from the Arduino and Ground.
  7. Do the same for the stop push button switch but to Digital Pin 6(D6) and Ground.
  8. Then, hook up the resistor and LED to Digital Pin 4 (D4) and Ground.
  9. Lastly, screw in the GPS antenna.

Step 5: Plug It In!

Plug in the USB cable from the USB Power Adapter to the Arduino. If everything is wired up correctly and there is no magic smoke you should see the LED go on. This means that the GPS currently DOES NOT have a lock. Depending on environmental conditions and antenna placement it can take a couple minutes. When it goes off this baby is logging! Press the stop switch and you'll see the LED flash three times then pause. Remove the MicroSD and plug it into your computer. There should be a file called "LOGGER01.CSV". Open it up and you should see something like this:

MM/DD/YYYY,HH:MM:SS.CC,Latitude,Longitude,MPH,TPS,Brake
04/06/2016,20:00:54.50,XXXXXXXX,YYYYYYYYY,0,1,0
04/06/2016,20:00:54.60,XXXXXXXX,YYYYYYYYY,0,0,0
04/06/2016,20:00:54.70,XXXXXXXX,YYYYYYYYY,0,3,0

The "XXXXXXXX" and "YYYYYYYY" are you GPS coordinates in radians×10^6. So multiply each one by 10^-6 to get the correct GPS value and plug it into Google Maps to verify it is correct coordinate. If it is take your baby out for a spin and do some data logging!

Step 6: I Got Some Data Now What?

This is the actually the hard part, what to actually do with the data. Easiest is plotting graphs with something like Excel and checking out your trends, seeing how big the time gaps are from transitioning from brake to throttle, see how consistent you are with the throttle, etc. Don't let the data fool you though. If you take a look at the sample data I posted you can see a lot of spikes, you would think that's noise, but is it? Nah, well depends on what you're trying to look for, but that's actually me blipping the the throttle while I'm downshifting. :) If you add a sensor input for the clutch and neutral switch you can weed that noise out, but other then that you'll have to learn how to analyze the data.

You can also use something like RaceRender and see your time, position, and sensor data all at once. You can even sync up a camera with it!

There's a big science behind data acquisition and data analysis and the options are endless!

Step 7: Explaining the Code

The code procedure is pretty simple. Initialize the pins, initialize the serial port for GPS, initialize the SD card, and then create a file. Once that's complete if GPS has a lock it will enter it's loop cycle and read data from the TPS sensor, brake light, and GPS values and then write that to the SD card and loop again. Pretty simple right? There's a couple gotchas along the way, particularly with the GPS and compiler. I'll describe it below.

// Can comment out this statement if it compiles. Bug on certain Arduino IDE versions.
// Read here for more info: <a href="http://subethasoftware.com/2013/04/09/arduino-compiler-problem-with-ifdefs-solved/" rel="nofollow"> http://subethasoftware.com/2013/04/09/arduino-com...</a>
#if 1
__asm volatile("nop"); 
#endif

This one's a bit of a weird one. If you can comment this out and it compiles then that's good. You don't have to worry about it. Chances are you can comment this out since it's been fixed by newer Arduino versions, I believe. You can go to the link in the comment if you want to read about the problem.

// Debug Condition
#define DUEMILANOVE true // Set to true if using Duemilanove
#define DEBUG false  // Set to true if want to debug

This one is for determining what type of Arduino you're using and if you plan on using the serial port for debug. The reason why is due to the serial port being occupied by the GPS during normal operation for the Duemilanove (old Arduino I know, like I said I did this project awhile back :)). The same thing occurs for the Uno since there's only one serial port which is why we left it the way it was here. If you plan on using the serial port to debug, set DEBUG to true and move the GPS RX and TX pins to Digital Pins 3, 2 respectively. If you're using the Mega which has 4 serial ports set DUEMILANOVE to false and set the GPS RX and TX pins to Digital Pins 19, and 20 respectively.

#define SERIAL_DEBUG if(DEBUG)Serial // If DEBUG is set to true then all SERIAL_DEBUG statements will work
#if DUEMILANOVE 
  #if DEBUG //If debugging on Duemilanove set GPS to 3,2 and use SoftwareSerial
    #include <SoftwareSerial.h>
    #define RX_GPS_PIN 3
    #define TX_GPS_PIN 2
    SoftwareSerial SERIAL_GPS(3,2);
  #else //If not debugging on Duemilanove put GPS pins on 0,1
    #define RX_GPS_PIN 0
    #define TX_GPS_PIN 1
    #define SERIAL_GPS Serial
  #endif
#else // If using Mega 2560 set GPS pins to Serial1
  #define RX_GPS_PIN 19
  #define TX_GPS_PIN 20
  #define SERIAL_GPS Serial1
#endif

This is the logic for the Debug condition. You typically don't have to touch this, but as you can see if we're debugging on the single serial Arduino's you must use the SoftwareSerial library for the GPS, which actually slows down your data resolution significantly.

// Initialize GPS and Debugging
// iTeadStudio GPS Shield set at 38400 bps @ 1 Hz by default (on my unit)
// GPS Shield uses NEO-6M GPS Module which can be configured by downloading uBlox uCenter on computer.
// Go to uBlox uCenter > Edit > Messages > NMEA > PUBX to change Baud Rate and get Hex.
// Go to uBlox uCenter > Edit > Messages > UBX > CFG > Rate to change Measurement Period and get Hex.
// Best results at 38400 bps @ 5 Hz on Arduino Hardware Serial.
// If using SoftwareSerial MUST set GPS Module to 4800 bps @ 1 Hz.
void setSerial() {
  char baudRate[] = {0x24, 0x50, 0x55, 0x42, 0x58, 0x2C, 0x34, 0x31, 0x2C, 0x31, 0x2C, 0x30,
            0x30, 0x30, 0x37, 0x2C, 0x30, 0x30, 0x30, 0x33, 0x2C, 0x34, 0x38, 0x30,
            0x30, 0x2C, 0x30, 0x2A, 0x31, 0x33, 0x0D, 0x0A}; // Hex to change to 4800 bps
  char fiveHz[] = {0xB5, 0x62, 0x06, 0x08, 0x06, 0x00, 0xC8, 0x00, 0x01, 0x00, 0x01, 0x00, 0xDE, 0x6A}; //Hex to change to 5Hz
  char tenHz[] = {0xB5, 0x62, 0x06, 0x08, 0x06, 0x00, 0x64, 0x00, 0x01, 0x00, 0x01, 0x00, 0x7A, 0x12}; // Hex to change to 10Hz
  
  #if DUEMILANOVE
    #if DEBUG
      SERIAL_DEBUG.begin(115200);
      SERIAL_GPS.begin(38400); 
      delay(1000);
      SERIAL_GPS.write(baudRate,sizeof(baudRate));
      SERIAL_GPS.begin(4800); // Set SoftwareSerial to 4800
    #else 
      SERIAL_GPS.begin(38400);
      delay(5000);
      SERIAL_GPS.write(tenHz,sizeof(tenHz));
      SERIAL_GPS.flush();
    #endif
  #else
    SERIAL_DEBUG.begin(115200);
    SERIAL_GPS.begin(38400);
    delay(5000);
    SERIAL_GPS.write(tenHz,sizeof(tenHz));
        SERIAL_GPS.flush();
  #endif  
}

This one sets the baudrate and measurement period for the GPS. The faster the better for us. The NEO-6M supports a measurement period up to 5Hz, but I've managed to get 10Hz and it works, but I think it might be interpolating the data. I haven't read the data sheet thoroughly about this though. Anyways, 10Hz translates one sample every 0.1 seconds. This is actually not that good when it comes to racing, however the fastest GPS units you can get off the shelf I believe are 20Hz. So regardless, you'll need to do some interpolating since the Arduino can sample a lot faster then that. I didn't however in this code, but it'll be a good add on for you!

If you plan on using debug mode on a single serial port Arduino (Duemilanove, Uno, Pro Mini, Nano, etc), you will notice that you will have to set the baudrate to 4800 bps and the measurement period to 1Hz. Moral of story? Use a Mega for developing.

You may be asking, where the hell did you get those Hex codes? Good question. A lot of Googling led me to the uBlox center program, documentation, and learning how to talk to it.

Everything else, I commented as much as I could so hopefully its self explanatory.

Step 8: Logging Limitations

The two biggest limitations with this current setup is data resolution and data syncing. I didn't go further into this, because I just felt like doing it for a proof of concept during the time.

Data Resolution

The current max data resolution is one sample for every 0.1 seconds. This can be further improved easily because the way the code is set up, it's limited by the measurement period of the GPS. A better way to write the code would be to log data regardless if we received a new GPS coordinate. Once the data is dumped, the data rows without GPS can be post processed and interpolated. Now the sampling rate will be limited to the Arduino and SD Card write speeds which should be more than fast enough for racing applications.

Data Syncing

An interesting one I noticed is the delay for the GPS. Mine was off by 1.5 seconds based off my throttle position. I noticed this because in the data I released the throttle and my MPH from the GPS kept on increasing. That doesn't make any sense! I just shifted my data accordingly and it matched up, but you may want to take a good look at the TinyGPS library and look at the fix_age parameter for further data evaluation.

Another one to consider is the delay between each procedure. Remember the Arduino is not capable of hardware threading so the time it takes to read one input, and then the next, and then actually write to the SD card does not all happen simultaneously. You must take this into consideration if you plan to add more sensor inputs and if you plan on having interrupt counters. If you plan on multi-threading you can have multiple Arduinos set to do a simple task and use a vehicle bus (I2C, SPI, CAN) to have them communicate between each other. A good read on this that I found was here. I would also have a look into FPGAs which excel in this.

Step 9: Addons, Improvements, and TODOs

Want to add more or improve on this design? Some easy ones I thought off my head are:

RPM/Gear Indicator

This one is fairly easy. Tap into the crankshaft position sensor and wheel speed sensor. From the crankshaft position sensor you can get RPM and with both you can calculate your gear ratio and figure out what gear you're in. See here for how: http://playground.arduino.cc/Main/ReadingRPM

Brake Pressure Transducer

Add a pressure transducer to your brake line to convert your brake signal from digital to analog and get much more resolution. This will behave the same as the TPS input essentially. Note however this sensor is not cheap ($100+) unless your are able to scavenge one from a vehicle that comes with one and improper installation can lead to a loss of brakes! So be careful with this one.

Add Shunt Regulator to the TPS Input Pin

Add a shunt regulator right before the TPS input pin to prevent the Arduino from getting damaged. The electrical system of any car/motorcycle is very noisy. I noticed while testing under real life conditions there were times the analog input would spike significantly. Best to put a resistor and a 5.1V zener diode in parallel just in case.

Neutral/Clutch Switch

Tapping into this can make it easier to analyze and filter out data depending on what you're trying to look at. It will be the same as setup as the brake light.

Calibration Mode

Have a separate function that gets triggered by an interrupt switch which goes into calibration mode. This can set your max and min values for the throttle position.

Stop Switch

This is an easy one, move the stop switch to an interrupt pin and set it up as an interrupt function. I didn't do this at the time since the interrupt pins were occupied by the SoftwareSerial when I was debugging.

Anymore? Feel free to leave in comments.

Step 10: References

Want to get more into datalogging? Here's some references.