Most newer cars already have a display showing you how efficiently you are driving. Usually this takes the form of a digital readout showing you miles driven per gallon of gas over 2 time-frames: short term (instantaneous) and long term (average). Wouldn't it be cool to add this feature to older cars?

Here is a typical MPG readout from a typical car. Kinda lame. Let's make something cooler!

So how do you calculate MPG? We need to know how much fuel is being used and how far the vehicle travels by burning that fuel. With only 2 variables, Mass Air Flow (MAF) and vehicle speed (VSS), we can calculate fuel economy. Mass air flow (MAF) is a measurement of the amount of air going to the engine measured in grams/second. Your car’s ECU constantly tries to maintain the ideal ratio of air and fuel to make the most efficient combustion. For gasoline, this ratio is 14.7 grams of air per gram of fuel. Read more about that here. Vehicle speed (VSS) is a measurement of speed (duh) measured in Km/hour. Next, we convert grams of gasoline to gallons. This can vary depending on fuel grade, but we’ll use 6.17 pounds per gallon for this constant. Now it is just a matter of converting units and simplifying the math knowing that 454g = 1 pound, 3600 seconds = 1 hour, and 1 Km = 0.621371 miles. This yields the following formula to derive MPG:

The original source for the above math can be found in a 2005 article in Circuit Cellar magazine by Bruce D. Lightner. For a more in-depth explanation, refer to the article here. Thankfully, both of these values (and many more) are required to be available at the OBD2 port of a car. While we could have done this project with a newer CAN-bus equipped car, we decided to test with an older Pre-CAN car. In this case, we gave a sweet 2004 Toyota Rav4 a modern car feature! Hardware: The hardware setup is pretty straight-forward. Our test vehicle (the sweet 2004 Rav4) uses ISO9141 (aka K-Line) as the communication protocol. ISO9141 is similar to RS-232, but with different voltage levels. Read all about K-line in our documentation section. We use a Macchina M2 as the ISO9141 interface. To display MPG data, we could have used a standard digital readout like some cars have, but we thought it would be much cooler to build a more “relative” value gauge. While knowing the exact MPG value can be useful, we figured a display that gave us an instant visual feedback of how we are driving would easier to interpret while in motion. For this purpose, we chose the Adafruit NeoPixel Stick as the indicator - simply 8 addressable RGB LEDs in a row. Only 3 wires are needed to connect M2 to a NeoPixel. We connected +5V, GND and Data from the 26 pin connector to the NeoPixel.





Simple set up Simple set up



3 wires connected to 26 pin "expansion" connector



3 wires soldered to NeoPixel

For our demo, we used an OBD extension cable and shorter wire to the NeoPixel. Alternatively, we could have plugged M2 directly into the OBD2 port and ran longer wires to the LED display. For the test drive, we just stuck the LEDs and M2 to the dash with double-sided tape. Code: The sketch loaded onto M2 employs 2 excellent libraries to handle the ISO9141 communication and LED driving: https://github.com/iwanders/OBD9141 https://github.com/adafruit/Adafruit_NeoPixel The actual sketch code is here:



/* * MPG LED display using Adafruit NeoPixel and Macchina M2 with a pre-CAN bus * ISO9141 (K-line) based vehicle. * * These parts are used in this project: * * https://www.macchina.cc/content/m2-under-dash * https://www.adafruit.com/product/2869 * * Requires "Macchina M2" board to be installed and selected. For instructions, go here: * http://docs.macchina.cc/m2/getting-started/arduino.html * * This sketch requires the following libraries: * * https://github.com/iwanders/OBD9141 * https://github.com/adafruit/Adafruit_NeoPixel * */ #include <Adafruit_NeoPixel.h> #include "OBD9141.h" // The RX pin of the Serial1 connected to 9141 K RX of tranciever. #define RX_PIN LIN_KRX // The Tx pin of the Serial1 connected to 9141 K TX of tranciever. #define TX_PIN LIN_KTX // The Enable pin connected to 9141 SLP pin of tranciever. #define EN_PIN LIN_KSLP #define OBD9141_DEBUG #define PIXEL_PIN RXD3 // Digital IO pin connected to the NeoPixels. #define PIXEL_COUNT 8 Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, NEO_RGBW + NEO_KHZ800); OBD9141 obd; float MPG = 0; int MAF = 0; int VSS = 0; void setup() { SerialUSB.begin(115200); delay(2000); pinMode(EN_PIN, OUTPUT); digitalWrite(EN_PIN, HIGH); pinMode(PS_J1850_9141, OUTPUT); digitalWrite(PS_J1850_9141, HIGH); obd.begin(Serial1, RX_PIN, TX_PIN); // pinMode(BUTTON_PIN, INPUT_PULLUP); strip.begin(); strip.show(); // Initialize all pixels to 'off' } void loop() { SerialUSB.println("Looping"); // obd.set_port(false); bool init_success = obd.init(); SerialUSB.print("init_success:"); SerialUSB.println(init_success); // init_success = true; // Uncomment this line if you use the simulator to force the init to be // interpreted as successful. With an actual ECU; be sure that the init is // succesful before trying to request PID's. if (init_success) { bool res = true; while (res) { res = obd.getCurrentPID(0x10, 2); if (res) { // SerialUSB.print("Result 0x10 (MAF): "); // SerialUSB.println(obd.readUint16()); MAF = (int(obd.readUint16())); // SerialUSB.println(MAF); } res = obd.getCurrentPID(0x0D, 1); if (res) { // SerialUSB.print("Result 0x0D (speed): "); // SerialUSB.println(obd.readUint8()); VSS = int((obd.readUint8())); // SerialUSB.println(VSS); } MPG = 7107 * VSS; MPG = MPG / MAF ; MPG = MPG / 10; delay(50); SerialUSB.println(MPG); SerialUSB.println(); for ( int i = 0; i < 8; i++ ) { // turn them all off strip.setPixelColor( i, 0, 0, 0 ); } if (MPG > 10) { // GREEN LED for ( int i = 0; i < ((MPG / 8) - 1); i++ ) { // set solid pixels to whole numbers strip.setPixelColor( i, 255, 0, 0 ); } strip.setPixelColor((MPG / 8), int((255 * ((MPG / 8) - int(MPG / 8)))), 0, 0 ); // set partial brightness on last LED } if (MPG <= 10) { // RED LED for ( int i = 0; i < ((MPG / 8) - 1); i++ ) { // set solid pixels to whole numbers strip.setPixelColor( i, 0, 255, 0 ); } strip.setPixelColor((MPG / 8), 0, int((255 * ((MPG / 8) - int(MPG / 8)))), 0 ); // set partial brightness on last LED } strip.show(); } } delay(3000); }

https://gist.github.com/rocketjosh/1a2a3ed7f7c7c160d8ae84248f07e61a Demo:

An interesting discovery is that it is quite easy to “train” yourself to not make the red LEDs light up when driving. M2 was programmed to make the LED display show red light up when car goes below 10 MPG. It seems that even small gas pedal changes can make a large MPG change, while not making a large acceleration change. It would be interesting to monitor how using this gauge over time can change affect overall driving habits. Next steps, going further: - Do the same project with a newer CAN-equipped car. - Expand display by adding additional NeoPixel Stick for “Average” MPG. - Add more colors for more visual feedback and cooler display. Maybe more colors for different ranges of MPG. - Add a “trip cost” feature: Input how much you paid for a gallon of gas, M2 calculates actual cost of traveling from A to B. Discussion: Join the conversation at our forums. Share your thoughts, questions and ideas.