FluxBench.AIShared project · read-only

GPS Speedometer

ESP32 DevKit (WROOM-32)·Arduino C++·Updated Jul 27, 2026·by FluxBench TeamIncludes Lab + Schematic

A dashboard speedometer built on the TinyGPS++ NMEA parser: every byte the NEO-6M sends over UART2 (module TX → GPIO 16/RX2) is pumped through gps.encode(), which validates sentence checksums and commits parsed $GPGGA/$GPRMC fields into gps.location, gps.speed, gps.satellites and gps.altitude. When location.isUpdated() reports a fresh fix the OLED redraws with the speed in large digits, satellites in view, altitude and a running trip odometer summed from TinyGPSPlus::distanceBetween() between consecutive fixes. Above 50 km/h a SPEEDING banner appears and the green LED on GPIO 4 lights; the button on GPIO 14 resets the trip. In the Lab the module behaves like a real receiver — no fix for the first couple of seconds, then a satellite count that climbs as the almanac fills in — and clicking the GPS module reveals a speed slider so you can “drive” the simulated car yourself.

sketch.ino
// GPS Speedometer - live speed, trip odometer and sat count from a NEO-6M
// ESP32 DevKit v1. NEO-6M on UART2 (module TX -> GPIO16/RX2, RX -> GPIO17/TX2),
// SSD1306 OLED @0x3C on I2C (SDA=21, SCL=22). Green LED on GPIO 4 lights
// above 50 km/h, button on GPIO 14 (INPUT_PULLUP, other leg to GND) resets
// the trip counter.
//
// TinyGPS++ does the heavy lifting: feed every raw NMEA byte from Serial2
// into gps.encode() and the library checks sentence checksums and exposes the
// parsed fields as gps.location / gps.speed / gps.satellites objects.
// isUpdated() flips true the moment a fresh sentence commits - that is the
// cue to redraw. The trip odometer sums the haversine distance between
// consecutive fixes with TinyGPSPlus::distanceBetween().
// In the Lab, click the GPS module and drag the speed slider to "drive".

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <TinyGPSPlus.h>

Adafruit_SSD1306 display(128, 64, &Wire, -1);
TinyGPSPlus gps;

const int LED = 4;
const int BTN = 14;
const float SPEED_LIMIT = 50.0;   // km/h

double lastLat = 0, lastLng = 0;
double tripKm = 0;
bool hadFix = false;

void drawScreen(float kmh) {
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print("GPS SPEEDOMETER");
  display.setTextSize(3);
  display.setCursor(10, 16);
  display.print(kmh, 1);
  display.setTextSize(1);
  display.setCursor(0, 44);
  display.print("km/h   sats ");
  display.print(gps.satellites.value());
  display.print("  alt ");
  display.print(gps.altitude.meters(), 0);
  display.print("m");
  display.setCursor(0, 54);
  display.print("trip ");
  display.print(tripKm, 3);
  display.print(" km");
  if (kmh > SPEED_LIMIT) {
    display.setCursor(74, 54);
    display.print("SPEEDING!");
  }
  display.display();
}

void setup() {
  Serial.begin(115200);
  Serial2.begin(9600, SERIAL_8N1, 16, 17);   // NEO-6M on UART2
  pinMode(LED, OUTPUT);
  pinMode(BTN, INPUT_PULLUP);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.print("Waiting for GPS fix");
  display.display();
  Serial.println("Waiting for GPS fix...");
}

void loop() {
  // pump every byte from the module through the NMEA parser
  while (Serial2.available() > 0) {
    gps.encode(Serial2.read());
  }

  if (digitalRead(BTN) == LOW) {   // trip reset
    tripKm = 0;
    Serial.println("Trip reset");
    delay(250);
  }

  if (gps.location.isUpdated()) {
    double lat = gps.location.lat();
    double lng = gps.location.lng();
    if (!hadFix) {
      Serial.println("GPS fix acquired");
    } else {
      // odometer: haversine distance between consecutive fixes
      tripKm += TinyGPSPlus::distanceBetween(lastLat, lastLng, lat, lng) / 1000.0;
    }
    hadFix = true;
    lastLat = lat;
    lastLng = lng;

    float kmh = gps.speed.kmph();
    digitalWrite(LED, kmh > SPEED_LIMIT ? HIGH : LOW);
    drawScreen(kmh);

    Serial.print("Speed ");
    Serial.print(kmh, 1);
    Serial.print(" km/h  sats ");
    Serial.print(gps.satellites.value());
    Serial.print("  trip ");
    Serial.print(tripKm, 3);
    Serial.println(" km");
  }
  delay(20);
}

Built with FluxBench.AI — the AI workbench for embedded projects.