Boost and oil pressure gauge on an Arduino Uno
Oil pressure from a 0.5 to 4.5 V sender and boost or vacuum from an absolute MAP sensor on a 20x4 LCD, with a resettable peak boost, a low oil pressure warning and SENSOR FAULT for a broken wire. The whole build, an Arduino Uno and 9 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Teaching? Assign it to your class in one click.
Intermediate About 35 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $65. On Mokxi they're free.
What you'll learn
- Sensors
- Displays
- Analog
- Sound
- Choosing and reading a resistor
- Difficulty: Intermediate
- Time: About 35 minutes
- Board: Arduino Uno R3
- Parts: 11
How it works
Oil pressure from a 0.5 to 4.5 V sender and boost or vacuum from an absolute MAP sensor on a 20x4 LCD, with a resettable peak boost, a low oil pressure warning and SENSOR FAULT for a broken wire.
Arduino Uno R3 runs the sketch below. The circuit around it: Pressure sender, 0.5-4.5 V, Pressure sensor, MPX5010 / MPX5700, Resistor, 100k Ω, Pushbutton, Buzzer, Resistor, 220 Ω. pinMode sets BUTTON_PIN as an input with its pull-up on. analogRead measures the voltage on pin as a number. Serial.begin opens the serial port at 9600 baud. millis reads the time since power-up, so the sketch can time things without stopping.
Parts list
11 parts, plus the jumper wires. They retail for about $65; on Mokxi they're free.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Half breadboard
- 1 × Pressure sender, 0.5-4.5 V
- 1 × Pressure sensor, MPX5010 / MPX5700
- 2 × Resistor, 100k ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 1 × PushbuttonIn Starter Kit, Christmas Lights Kit, Logic Lab Kit.
- 1 × BuzzerIn Starter Kit, Rocket Flight Computer Kit.
- 1 × Resistor, 220 ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 1 × LED, redIn Starter Kit, Halloween Prop Kit, Logic Lab Kit.
- 1 × Character LCD, 20x4, with I2C backpackIn Spaceship Panel Kit, Escape Box Kit.
Wiring table
Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.
| Part and pin | Connects to |
|---|---|
| Buzzer pin 1 | Arduino Uno R3 pin 6 |
| Resistor, 220 Ω pin 1 | Arduino Uno R3 pin 5 |
| Pushbutton pin 1a | Arduino Uno R3 pin 2 |
| Pressure sender, 0.5-4.5 V pin VCC | Arduino Uno R3 pin 5V |
| Pressure sensor, MPX5010 / MPX5700 pin VS | Arduino Uno R3 pin 5V |
| Character LCD, 20x4, with I2C backpack pin VCC | Arduino Uno R3 pin 5V |
| Pressure sender, 0.5-4.5 V pin GND | Arduino Uno R3 pin GND |
| Pressure sensor, MPX5010 / MPX5700 pin GND | Arduino Uno R3 pin GND |
| Resistor, 100k Ω (1) pin 2 | Arduino Uno R3 pin GND |
| Resistor, 100k Ω (2) pin 2 | Arduino Uno R3 pin GND |
| Pushbutton pin 2a | Arduino Uno R3 pin GND |
| Buzzer pin 2 | Arduino Uno R3 pin GND |
| LED, red pin C | Arduino Uno R3 pin GND |
| Character LCD, 20x4, with I2C backpack pin GND | Arduino Uno R3 pin GND |
| Pressure sender, 0.5-4.5 V pin SIG | Arduino Uno R3 pin A0 |
| Resistor, 100k Ω (1) pin 1 | Arduino Uno R3 pin A0 |
| Pressure sensor, MPX5010 / MPX5700 pin VOUT | Arduino Uno R3 pin A1 |
| Resistor, 100k Ω (2) pin 1 | Arduino Uno R3 pin A1 |
| Character LCD, 20x4, with I2C backpack pin SDA | Arduino Uno R3 pin A4 |
| Character LCD, 20x4, with I2C backpack pin SCL | Arduino Uno R3 pin A5 |
| LED, red pin A | Resistor, 220 Ω pin 2 |
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Boost and oil pressure gauge: two ratiometric pressure sensors on two
// analog pins, oil pressure and boost (or vacuum) on a 20x4 I2C LCD, a peak
// boost that holds until you press the button, a low oil pressure warning
// with a lamp and a buzzer, and SENSOR FAULT when a wire breaks or shorts.
//
// oil sender 0 to 100 psi, 0.5 to 4.5 V SIG -> A0, 100k from A0 to GND
// MAP sensor MPX5700AP, absolute VOUT -> A1, 100k from A1 to GND
// LCD 20x4 SDA -> A4 SCL -> A5 VCC -> 5 V
// button pin 2 to ground (the chip's own pull-up, interrupt 0): resets the peak
// oil lamp pin 5, through 220 ohms
// buzzer pin 6 (an active 5 V buzzer)
//
// SAFETY. This is a teaching simulation. Never connect a project like this
// to a running car's safety systems (brakes, airbags, steering, ECU wiring,
// anything that can strand or hurt someone). On the bench use a bench supply
// or a 12 V battery with a fuse. In a real vehicle the Uno needs a proper
// automotive-rated front end: a fused feed, a regulator that survives load
// dump, TVS clamps, and a divider or level shift for anything at 12 V; 12 V
// never goes straight onto a 5 V logic pin. A real oil sender must be an
// automotive-rated part with the right thread for the port, fitted by the
// book, and never teed into the factory sender's wiring or anything else
// whose failure can stall or starve the engine.
//
// RATIOMETRIC. Both sensors give out a fixed fraction of their 5 V supply,
// not a fixed voltage. The oil sender gives 10% of its supply at 0 psi and
// 90% at 100 psi (0.5 to 4.5 V on 5 V). analogRead() also measures as a
// fraction of the same 5 V, so if the supply sags to 4.8 V both move together
// and the pressure still comes out right. That is why the sketch works in
// fractions of the supply and never needs to know the exact 5 V.
//
// OFFSET AND SPAN. A straight-line sensor is two numbers: where it starts
// (the offset, 0.10 of the supply at zero) and how far it moves over its
// range (the span, 0.80 of the supply for 100 psi). So
//
// psi = (fraction - offset) / span * full scale
//
// The MAP sensor is an NXP MPX5700AP: Vout = Vs x (0.0012858 x kPa + 0.04),
// absolute pressure from 15 to 700 kPa (0.3 to 4.7 V). Absolute means it
// reads the air around it too, so at rest it shows about 101 kPa (14.7 psi
// at sea level). Gauge boost is absolute minus atmospheric; below
// atmospheric is vacuum, shown in inches of mercury (inHg) like a vacuum
// gauge. A 3-bar MAP sensor works the same way: put its own offset and slope
// from the datasheet in MAP_OFFSET and MAP_SLOPE.
//
// FAULT WINDOW. A healthy sensor never goes below 0.25 V or above 4.75 V
// (5% and 95% of the supply). An open signal wire is pulled to 0 V by the
// 100k resistor, and a signal shorted to 5 V reads 5 V, so anything outside
// the window is a wiring fault: the gauge shows SENSOR FAULT, never a number
// that looks real. Without the pull-down an open input floats and can read
// anything at all.
//
// THE WARNING IS AN EXAMPLE. Oil pressure rises with rpm. A common rule of
// thumb is at least 10 psi at idle and about 10 psi per 1,000 rpm (25 psi or
// more at 3,000 rpm), but your engine's book is the real number. This sketch
// uses one fixed limit, LOW OIL under 10 psi, because it does not know the
// rpm. With the engine off the oil pressure is zero, so it warns at key on,
// as the factory oil light does.
//
// Things to try. Drag the oil sender up past 10 psi: the warning stops. Drag
// the MAP sensor below 101 kPa for vacuum (idle is about 30 to 40 kPa, which
// is 18 to 21 inHg) and above it for boost; the peak stays until you press
// the button.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
const int OIL_PIN = A0;
const int MAP_PIN = A1;
const int BUTTON_PIN = 2;
const int LAMP_PIN = 5;
const int BUZZER_PIN = 6;
// The oil sender: 10% of the supply at zero, 90% at full scale.
const float OIL_OFFSET = 0.10;
const float OIL_SPAN = 0.80;
const float OIL_FULL_PSI = 100.0;
// The MPX5700AP: fraction = MAP_SLOPE x kPa + MAP_OFFSET.
const float MAP_OFFSET = 0.04;
const float MAP_SLOPE = 0.0012858;
// A standard atmosphere at sea level, 101.3 kPa or 14.7 psi.
const float ATMOSPHERE_KPA = 101.325;
const float PSI_PER_KPA = 0.145038;
const float INHG_PER_KPA = 0.2953;
// The fault window, as fractions of the supply: 0.25 V and 4.75 V on 5 V.
const float FAULT_LOW = 0.05;
const float FAULT_HIGH = 0.95;
// Example limit: warn under 10 psi of oil pressure.
const float LOW_OIL_PSI = 10.0;
const unsigned long READ_MS = 250;
const unsigned long DEBOUNCE_MS = 200;
LiquidCrystal_I2C lcd(0x27, 20, 4);
float peakPsi = 0; // the highest boost since the last reset, in psi
unsigned long lastRead = 0;
bool blink = false;
// Set by the button's interrupt, cleared by loop().
volatile bool resetAsked = false;
volatile unsigned long lastPress = 0;
// A pin's reading as a fraction of the supply, averaged over 8 reads.
float readFraction(int pin) {
long sum = 0;
for (int i = 0; i < 8; i++) sum += analogRead(pin);
return sum / 8.0 / 1023.0;
}
bool inWindow(float fraction) {
return fraction >= FAULT_LOW && fraction <= FAULT_HIGH;
}
// Pad a line out to the display's 20 characters.
void finishLine(int used) {
for (int i = used; i < 20; i++) lcd.print(' ');
}
// How many characters a number takes when printed with `decimals`.
int widthOf(float v, int decimals) {
int w = decimals > 0 ? decimals + 1 : 0;
if (v < 0) {
w++;
v = -v;
}
long whole = (long)v;
w++;
while (whole >= 10) {
whole /= 10;
w++;
}
return w;
}
// The button is on pin 2, which is external interrupt 0. Writing the whole
// LCD takes a while, and a quick press could fall between two looks at the
// pin; the interrupt catches it whenever it comes. Contacts bounce, so a
// second edge within 200 ms is the same press.
void buttonPressed() {
unsigned long now = millis();
if (now - lastPress >= DEBOUNCE_MS) resetAsked = true;
lastPress = now;
}
// Reset the peak if the button asked for it.
void checkButton() {
if (!resetAsked) return;
resetAsked = false;
peakPsi = 0;
Serial.println("Peak reset");
lastRead = millis() - READ_MS; // show it straight away
}
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonPressed, FALLING);
pinMode(LAMP_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
Serial.begin(9600);
lcd.init();
lcd.backlight();
lcd.print("Boost and oil gauge");
Serial.println("Boost and oil pressure gauge: LOW OIL under 10 psi (example limit)");
lastRead = millis() - READ_MS;
}
void loop() {
checkButton();
unsigned long now = millis();
if (now - lastRead < READ_MS) return;
lastRead = now;
blink = !blink;
// Oil: offset and span to psi, inside the fault window only.
float oilF = readFraction(OIL_PIN);
bool oilFault = !inWindow(oilF);
float oilPsi = (oilF - OIL_OFFSET) / OIL_SPAN * OIL_FULL_PSI;
if (oilPsi < 0) oilPsi = 0; // a few millivolts under 0.5 V is still zero
long oilShown = (long)(oilPsi + 0.5);
bool lowOil = !oilFault && oilPsi < LOW_OIL_PSI;
// Boost: absolute kPa, minus the atmosphere, to psi of boost or inHg of vacuum.
float mapF = readFraction(MAP_PIN);
bool mapFault = !inWindow(mapF);
float gaugeKpa = (mapF - MAP_OFFSET) / MAP_SLOPE - ATMOSPHERE_KPA;
float boostPsi = gaugeKpa * PSI_PER_KPA;
float vacuumInHg = -gaugeKpa * INHG_PER_KPA;
bool vacuum = gaugeKpa < 0;
if (!mapFault && boostPsi > peakPsi) peakPsi = boostPsi;
// The lamp is on for low oil and blinks for a broken oil sender; the
// buzzer sounds for low oil.
digitalWrite(LAMP_PIN, lowOil || (oilFault && blink) ? HIGH : LOW);
digitalWrite(BUZZER_PIN, lowOil ? HIGH : LOW);
// Line 1: oil.
lcd.setCursor(0, 0);
if (oilFault) {
lcd.print("OIL SENSOR FAULT");
finishLine(18);
} else {
lcd.print("OIL ");
lcd.print(oilShown);
lcd.print(" psi");
finishLine(6 + widthOf(oilShown, 0) + 4);
}
// Line 2: boost or vacuum.
lcd.setCursor(0, 1);
if (mapFault) {
lcd.print("BOOST SENSOR FAULT");
finishLine(18);
} else if (vacuum) {
lcd.print("VAC ");
lcd.print(vacuumInHg, 1);
lcd.print(" inHg");
finishLine(6 + widthOf(vacuumInHg, 1) + 5);
} else {
lcd.print("BOOST ");
lcd.print(boostPsi, 1);
lcd.print(" psi");
finishLine(6 + widthOf(boostPsi, 1) + 4);
}
// Line 3: the peak boost.
lcd.setCursor(0, 2);
lcd.print("PEAK ");
lcd.print(peakPsi, 1);
lcd.print(" psi");
finishLine(6 + widthOf(peakPsi, 1) + 4);
// Line 4: the status.
lcd.setCursor(0, 3);
if (lowOil) lcd.print("LOW OIL PRESSURE! ");
else if (oilFault || mapFault) lcd.print("CHECK SENSOR WIRING ");
else lcd.print("OK ");
// The same on the serial monitor, one line per reading.
if (oilFault) {
Serial.print("OIL SENSOR FAULT");
} else {
Serial.print("OIL ");
Serial.print(oilShown);
Serial.print(" psi");
}
if (mapFault) {
Serial.print(" BOOST SENSOR FAULT");
} else if (vacuum) {
Serial.print(" VAC ");
Serial.print(vacuumInHg, 1);
Serial.print(" inHg");
} else {
Serial.print(" BOOST ");
Serial.print(boostPsi, 1);
Serial.print(" psi");
}
Serial.print(" PEAK ");
Serial.print(peakPsi, 1);
Serial.print(" psi ");
Serial.println(lowOil ? "LOW OIL" : oilFault || mapFault ? "FAULT" : "OK");
}
Code walkthrough
The key functions of the sketch, and what each one does.
setup sketch.ino
Runs once when the board starts. It gets the pins and libraries ready.
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonPressed, FALLING);
pinMode(LAMP_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
Serial.begin(9600);
lcd.init();
lcd.backlight();
lcd.print("Boost and oil gauge");
Serial.println("Boost and oil pressure gauge: LOW OIL under 10 psi (example limit)");
lastRead = millis() - READ_MS;
}loop sketch.ino
Runs over and over for as long as the board has power. This is where the project does its work.
void loop() {
checkButton();
unsigned long now = millis();
if (now - lastRead < READ_MS) return;
lastRead = now;
blink = !blink;
// Oil: offset and span to psi, inside the fault window only.
float oilF = readFraction(OIL_PIN);
bool oilFault = !inWindow(oilF);
float oilPsi = (oilF - OIL_OFFSET) / OIL_SPAN * OIL_FULL_PSI;
if (oilPsi < 0) oilPsi = 0; // a few millivolts under 0.5 V is still zero
long oilShown = (long)(oilPsi + 0.5);
bool lowOil = !oilFault && oilPsi < LOW_OIL_PSI;
// Boost: absolute kPa, minus the atmosphere, to psi of boost or inHg of vacuum.
float mapF = readFraction(MAP_PIN);
bool mapFault = !inWindow(mapF);
float gaugeKpa = (mapF - MAP_OFFSET) / MAP_SLOPE - ATMOSPHERE_KPA;
float boostPsi = gaugeKpa * PSI_PER_KPA;
float vacuumInHg = -gaugeKpa * INHG_PER_KPA;
bool vacuum = gaugeKpa < 0;
if (!mapFault && boostPsi > peakPsi) peakPsi = boostPsi;
// The lamp is on for low oil and blinks for a broken oil sender; the
// buzzer sounds for low oil.
digitalWrite(LAMP_PIN, lowOil || (oilFault && blink) ? HIGH : LOW);
digitalWrite(BUZZER_PIN, lowOil ? HIGH : LOW);
// ... 67 more lines in the editorTroubleshooting
The usual mistakes with this circuit, and the fix for each.
- The reading is off at zero pressure. Pressure sender
The output is 0.5 V at no pressure, not 0 V. Subtract that offset, then scale 0.5 V to 4.5 V over the full range of the model. - The ADC saturates, or the board is damaged. Pressure sender
Power it from 5 V. On a 3.3 V board, the 4.5 V output is too high: use a divider. Check the three wires (supply, ground, signal), as colors vary by brand. - A 3.3 V board gives clipped or damaged readings. Pressure sensor
The output spans 0.2 to 4.7 V with a 5 V supply, which is above a 3.3 V ADC. Use a resistor divider and scale in code. The supply must be 5 V, about 4.75 to 5.25 V. - Pressure readings are wrong or reversed. Pressure sensor
Know which port is which. The differential versions have two ports, and pressure on the vacuum side reads lower. Gauge parts compare to atmosphere. Convert volts to kPa using the datasheet transfer function. - The LED is dim, burns out, or the pin reads wrong because the resistor value is off. Resistor
Read the color bands again. A 220 ohm to 330 ohm resistor suits a typical LED on 5 V. 10k is a common pull-up or pull-down value. Check with a multimeter if unsure. - A pull-up or pull-down resistor is missing, so the input floats and reads random values. Resistor
Tie the input to 5 V or ground through about 10k. Or use INPUT_PULLUP and wire the switch to ground. - The input reads random HIGH and LOW when the button is not pressed. Pushbutton
A floating pin picks up noise. Add a 10 k pull-down or pull-up resistor, or use INPUT_PULLUP and wire the button between the pin and GND. Then pressed reads LOW. - The button seems always pressed, or never changes. Pushbutton
A 4-leg tactile switch has legs joined in pairs. Place it across the breadboard center gap so the pairs sit on different rows. Wired along one side, the legs are already connected.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.