Building an Arduino fuel gauge and warning lights for LeMons racing

December 09, 2013

While preparing our race car for the 2013 Chubba Cheddar LeMons Race, it became apparent we were going to need to get creative when it came to implementing a fuel gauge as the stock gauge wasn’t working and debugging it wasn’t going well. Running out of fuel on the track due to the lack of a working gauge was clearly a failure mode we wanted to avoid.

With a little bit of research I was able to determine that the fuel level float sensor in the fuel tank was merely a variable resistor. For a DIY gauge we could practically just mount a multi-meter and watch the resistance of the sensor change as the fuel level changed. The thought of that seemed a little hack, even for LeMons racing.

Long story short, I decided it was time to see what this Arduino open source stuff was all about. With about an hour of Internet research I was able to find a reference example, a starter Arduino Uno kit, and the courage to jump in with both feet and place an order. Given my mechanical engineering background, the easy solution would have been to work with our team of sharp electrical engineers, as they would have came to the rescue and set me up with a great solution. But this open source stuff had me really intrigued to see how hard it would be to do this myself.

Much like any good development project there was iterative prototyping, lots of learning with each step, and plenty of satisfaction as the project progressed from idea to reality. Once I got my feet wet with an early breadboard and success looked more probable, the scope changed and I decided that in addition to monitoring the fuel level, there should be a warning light for low fuel, high coolant temp, and low oil pressure. The picture below is the “final version” that made it into the race car and fulfilled its duties quite remarkably. I can’t wait to work on version 2.0 for next year’s race season, which will include wireless communication between the car on track and crew in the pit area. The logical thing to do would probably be to purchase a race car monitoring system, but that doesn’t sound nearly as fun. Wish me luck!

Our team of sharp electrical engineers would have came to the rescue. But this open source stuff had me really intrigued to see how hard it would be to do this myself.

Want to learn more about how this Arduino-based gauge monitoring system went together? Keep reading!

Project Goal:

  1. Provide a gauge that accurately monitors fuel level in order to keep from running out of gas on the track, ideally with some level of graduations that allows for planning of fuel stops
  2. Provide a warning to driver if fuel level or oil pressure is low or coolant temp is high.

Theory of operation:

I was fortunate that all of the existing sensors that I wanted to monitor with the Arduino had variable resistors for sending units. The basic theory is that by measuring the resistance curve of the sending unit and characterizing that resistance curve for the targeted gauge, I could report out the appropriate value on the LCD or trigger a warning light as appropriate. For the fuel gauge I used the 5v output from the Arduino and a simple voltage divider circuit to determine the resistance of the sending unit. We had a factory service manual which provided the resistance level for a full tank and an empty tank and I just assumed that to be a linear relationship and used math to determine a percent full reading across the range.

Tapping into the existing Sunpro electric gauges for the H20 temp and oil pressure was a bit trickier. Essentially these Sunpro gauges also measure the variable resistance of their sending units and report out readings with a dial gauge. The Sunpro gauges use 12v from the vehicle power system so instead of applying power from the Arduino, I needed to measure voltage of the Sunpro sensor wire and characterize that against the dial gauge. I cranked up the impedance of the voltage divider circuit used to monitor the Sunpro circuit so as to not significantly change the voltage of the Sunpro circuit. If the monitoring circuit would have changed the voltage too much, that would have affected the accuracy of the Sunpro dial gauge. Given the voltage levels to be monitored were high enough that they may let the magic smoke out of the Arduino, cranking up the impedance of the voltage divider circuit also helped drop the voltage being input to a more appropriate level for the Arduino.

To characterize the Sunpro gauge voltage levels for the LED warning lights, I substituted a variable resistor for the Sunpro sending units and monitored the voltage of the sensor wire while observing the dial gauge. This provided me with the trigger point (voltage) for when to kick on the warning LEDs.

Bill of materials:

(1) Arduino Uno starter kit

(1) Microtivity IM162 LCD Module 1602

(1) Hammond 1591ESBK ABS Project Box Black

(3) Pre wired 6v 5mm Red LEDs

(5) Various resistors

(1) Seeed Studio prototype shield

For design references, I found these links very helpful:

The following is a wiring diagram for the gauge:

A peek inside the enclosure:

And finally, here is the code used for this gauge project:

const int numReadings = 10; // Need to do some smoothing of the readings for fuel level
// Define the number of samples to keep track of. The higher the number,
// the more the readings will be smoothed, but the slower the output will
// respond to the input. Using a constant rather than a normal variable lets
// use this value to determine the size of the readings array.
int readings[numReadings]; // the readings from the analog input
int index = 0; // the index of the current reading
int total = 0; // the running total
int averagefuel = 0; // the average of fuel reading as an analog input (calculated)
int percentage = 0; // percentage of tank capacity remaining (calculated)
int inputPin = A0; // analog input from fuel sendor (measured)
int fuelled = 10; // led write location for fuel led warning light (defined)
int vout = 0; // fuel sensor voltage from analog input (calculated)
int rtwo = 0; // resistance across fuel sending unit (calculated)
int oilled = 8; // led write location for oil led (defined)
int oilvin = A2; // analog input for oil sensor (defined)
int oilvolt = 0; // oil sensor voltage from analog input (actual analog input, not converted to an actual voltage)
int watervin = A1; // location for water sensor input (defined)
int waterled = 9; // led write location for water temp led (defined)
int watervolt=0; // water sensor voltage from analog input (actual analog input, not converted to an actual voltage)
#include // inclusion of lcd library
LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // initialize the library with the numbers of the interface pins
#include // inclusion of bargraph library
byte lcdNumCols = 16; // -- number of columns in the LCD
LcdBarGraph lbg(&lcd,16,0,1); // -- creating bargraph instance
void setup(){
Serial.begin(9600); // initialize serial communication with computer:
for (int thisReading = 0; thisReading < numReadings; thisReading++) // initialize all the readings to 0:
readings[thisReading] = 0;
lcd.begin(16, 2); // set up the LCD's number of columns and rows:
pinMode(0, INPUT); // configure analog pins as inputs
pinMode(1, INPUT);
pinMode(2, INPUT);
void loop() {
total= total - readings[index]; // subtract the last reading:
readings[index] = analogRead(inputPin); // read from the sensor:
total= total + readings[index]; // add the reading to the total:
index = index + 1; // advance to the next position in the array:
if (index >= numReadings) // if we're at the end of the array...
index = 0; // ...wrap around to the beginning:
averagefuel = total / numReadings; // calculate the average:
float vout = averagefuel * (5.0 / 1023.0); // calculate vout
float rtwo = 1000 / ((5/vout)-1); // calculate rtwo (R1 on board is 1000 ohms)
if(rtwo < 75) percentage = 100; // takes ohm range of tank (1050 to 70) and divides it up by 12 gallons or 8%
else if (rtwo < 151) percentage = 92; // starts checking to see if its full and works its way down
else if (rtwo
else if (rtwo < 315) percentage = 76;
else if (rtwo < 396) percentage = 68;
else if (rtwo < 478) percentage = 60;
else if (rtwo < 560) percentage = 52;
else if (rtwo < 642) percentage = 44;
else if (rtwo < 723) percentage = 36;
else if (rtwo < 805) percentage = 28;
else if (rtwo < 887) percentage = 20;
else if (rtwo < 968) percentage = 12;
else if (rtwo < 1050) percentage =4;
lcd.setCursor(0, 0); // Fuel status display on LCD
lcd.print("FUEL LEVEL");
lcd.print(" ");
lcd.setCursor(15, 0);
float oilvolt=analogRead(oilvin); // Read oil and water gauges analog input
float watervolt= analogRead(watervin);
if (percentage < 13)digitalWrite(fuelled, HIGH); // turns on fuel warning led if fuel under 13%
if (watervolt < 460) digitalWrite(waterled, HIGH); // turns on water temp led if temp above 210 F as determined in calibration
if (oilvolt > 410) digitalWrite(oilled, HIGH); // turns on oil pressure led if pressure below 12.5 psi
delay(1000); // flashes led/s
digitalWrite(fuelled, LOW);
digitalWrite(waterled, LOW);
digitalWrite(oilled, LOW);

I sincerely hope someone finds this post helpful as a design reference for a project, as I was amazed at the helpfulness of the open source design references I found.