Here is code for an automotive display - so far only AFR / Lambda and RPM. Plan to include GPS speed & odo & running fuel consumption, rpm based on actual scoped frequency from an aftermarket ecu, narrowband AFR formula courtesy of Google. Criticism, comments and improvements welcome.
#include <LiquidCrystal.h>
// ---------------- LCD ----------------
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
// ---------------- PINS ----------------
const byte RPM_PIN = 2; // ECU RPM output (INT0)
const int afrPin = A0; // AFR analog input
// ---------------- RPM ----------------
volatile unsigned long lastPulseMicros = 0;
volatile unsigned long periodMicros = 0;
volatile bool newPeriod = false;
unsigned int rpm = 0;
unsigned long lastRPMUpdate = 0;
const unsigned long RPM_TIMEOUT = 300000; // 300 ms no pulses → RPM = 0
const unsigned long LCD_REFRESH = 200; // ms (5 Hz)
unsigned long lastLCDUpdate = 0;
unsigned int lastDisplayedRPM = 65535;
// ---------------- AFR / Lambda ----------------
float afr = 14.7;
float lambda = 1.0;
// ---------------- Warm-up ----------------
const unsigned long WARMUP_TIME = 180000; // 3 minutes
unsigned long startMillis;
void rpmISR() {
unsigned long now = micros();
periodMicros = now - lastPulseMicros;
lastPulseMicros = now;
newPeriod = true;
}
void setup() {
lcd.begin(16, 2);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("RPM:");
lcd.setCursor(0, 1);
lcd.print("AFR:");
lcd.setCursor(10, 1);
lcd.print("L:");
pinMode(RPM_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(RPM_PIN), rpmISR, FALLING);
startMillis = millis();
}
void loop() {
unsigned long nowMillis = millis();
// -------- RPM FROM FREQUENCY --------
if (newPeriod) {
noInterrupts();
unsigned long p = periodMicros;
newPeriod = false;
interrupts();
if (p > 0) {
float frequency = 1000000.0 / p; // Hz
rpm = (unsigned int)(frequency * 40.0);
lastRPMUpdate = micros();
}
}
// RPM timeout → engine stopped
if (micros() - lastRPMUpdate > RPM_TIMEOUT) {
rpm = 0;
}
// -------- DISPLAY RPM (SLOW REFRESH) --------
if (millis() - lastLCDUpdate >= LCD_REFRESH) {
lastLCDUpdate = millis();
if (rpm != lastDisplayedRPM) {
lcd.setCursor(4, 0);
lcd.print(" ");
lcd.setCursor(4, 0);
lcd.print(rpm);
lastDisplayedRPM = rpm;
}
}