FluxBench.AIShared project · read-only

Round Gauge Speedometer

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

Flagship demo: a GC9A01 1.28" round display drawing a smooth analog speed gauge from a potentiometer on GPIO34. Flicker-free canvas rendering, colored zone arcs and live serial telemetry for the plotter. Fork it and make it yours.

sketch.ino
// FluxBench demo — Round Gauge Speedometer
// Board: ESP32 DevKit (WROOM-32)
// Display: GC9A01 1.28" round TFT, 240x240, SPI
//   SCK -> GPIO18, MOSI(SDA) -> GPIO23, CS -> GPIO5, DC -> GPIO2, RST -> GPIO4, VCC -> 3V3, GND -> GND
// Input: 10k potentiometer wiper -> GPIO34 (outer legs to 3V3 and GND)
//
// Turn the pot and watch the needle sweep. Serial prints "speed:<kmh>" —
// open the Monitor tab to see the live plotter graph it automatically.

#include <Arduino_GFX_Library.h>

#define PIN_SCK   18
#define PIN_MOSI  23
#define PIN_CS     5
#define PIN_DC     2
#define PIN_RST    4
#define PIN_POT   34


// RGB565 colors (defined locally - works across all Arduino_GFX versions)
#define C_BLACK   0x0000
#define C_WHITE   0xFFFF
#define C_RED     0xF800
#define C_ORANGE  0xFD20
#define C_TEAL    0x0410
#define C_LGREY   0xC618
#define C_DGREY   0x7BEF

#define MAX_KMH  180
#define CX 120
#define CY 120

// GC9A01 over hardware SPI; all pins configured right here (no User_Setup.h needed)
Arduino_DataBus *bus = new Arduino_ESP32SPI(PIN_DC, PIN_CS, PIN_SCK, PIN_MOSI, GFX_NOT_DEFINED /* no MISO */);
Arduino_GC9A01 *tft = new Arduino_GC9A01(bus, PIN_RST, 0 /* rotation */, true /* IPS */);
// Full-screen canvas = flicker-free drawing (240*240*2 = 112 KB, fits ESP32 heap)
Arduino_Canvas *gfx = new Arduino_Canvas(240, 240, tft);

float smoothKmh = 0;

// Gauge sweep: 270 degrees, from 135 deg (lower-left) clockwise to 405 deg (lower-right)
float kmhToRad(float kmh) {
  float deg = 135.0f + (kmh / MAX_KMH) * 270.0f;
  return deg * DEG_TO_RAD;
}

void drawGaugeFace() {
  gfx->fillScreen(C_BLACK);
  // colored zone arcs
  for (int k = 0; k <= MAX_KMH; k += 2) {
    float a = kmhToRad((float)k);
    uint16_t c = k < 100 ? C_TEAL : (k < 140 ? C_ORANGE : C_RED);
    int x0 = CX + (int)(104 * cosf(a)), y0 = CY + (int)(104 * sinf(a));
    int x1 = CX + (int)(112 * cosf(a)), y1 = CY + (int)(112 * sinf(a));
    gfx->drawLine(x0, y0, x1, y1, c);
  }
  // major ticks + labels every 20 km/h
  gfx->setTextSize(1);
  gfx->setTextColor(C_LGREY);
  for (int k = 0; k <= MAX_KMH; k += 20) {
    float a = kmhToRad((float)k);
    int x0 = CX + (int)(92 * cosf(a)), y0 = CY + (int)(92 * sinf(a));
    int x1 = CX + (int)(112 * cosf(a)), y1 = CY + (int)(112 * sinf(a));
    gfx->drawLine(x0, y0, x1, y1, C_WHITE);
    int lx = CX + (int)(78 * cosf(a)), ly = CY + (int)(78 * sinf(a));
    gfx->setCursor(lx - (k >= 100 ? 9 : 6), ly - 3);
    gfx->print(k);
  }
  gfx->setTextColor(C_DGREY);
  gfx->setCursor(CX - 14, CY + 46);
  gfx->print("km/h");
}

void drawNeedle(float kmh) {
  float a = kmhToRad(kmh);
  // needle with a short tail
  int x1 = CX + (int)(86 * cosf(a)), y1 = CY + (int)(86 * sinf(a));
  int x2 = CX - (int)(16 * cosf(a)), y2 = CY - (int)(16 * sinf(a));
  gfx->drawLine(x2, y2, x1, y1, C_RED);
  gfx->drawLine(x2, y2 + 1, x1, y1 + 1, C_RED);
  gfx->fillCircle(CX, CY, 5, C_LGREY);
  // digital readout
  gfx->setTextSize(3);
  gfx->setTextColor(C_WHITE);
  int v = (int)roundf(kmh);
  int w = (v >= 100 ? 3 : (v >= 10 ? 2 : 1)) * 18;
  gfx->setCursor(CX - w / 2, CY - 60);
  gfx->print(v);
}

void setup() {
  Serial.begin(115200);
  analogReadResolution(12); // 0..4095
  if (!gfx->begin(40000000)) { // 40 MHz SPI
    Serial.println("Display init failed");
    while (true) delay(1000);
  }
  drawGaugeFace();
  gfx->flush();
  Serial.println("Round gauge ready - turn the pot on GPIO34");
}

void loop() {
  int raw = analogRead(PIN_POT);
  float target = (raw / 4095.0f) * MAX_KMH;
  smoothKmh += (target - smoothKmh) * 0.15f; // exponential smoothing

  drawGaugeFace();
  drawNeedle(smoothKmh);
  gfx->flush();

  static uint32_t lastPrint = 0;
  if (millis() - lastPrint >= 100) {
    lastPrint = millis();
    Serial.print("speed:");
    Serial.println((int)roundf(smoothKmh));
  }
  delay(16);
}

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