Files
arduino_gauge_controller/CLAUDE.md
Adrian A. Baumann 8bdae1da9b Update docs and firmware for ESPHome bridge migration
- Replace gauge.py (MicroPython) references with gaugecontroller.yaml (ESPHome)
- Update CLAUDE.md and README.md to document ESPHome-native API integration
- Update LED wiring docs for separate main/indicator strips (D22/D36)
- Refactor Arduino firmware to drive two WS2812 strips independently
- Add per-gauge physical offset caching for main and indicator LEDs
- Frame-limit breathe effect (16ms) to reduce unnecessary strip refreshes
2026-04-29 19:03:22 +02:00

5.8 KiB
Raw Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Build & Upload

Main firmware lives in Gaugecontroller/Gaugecontroller.ino. Requires the FastLED library (arduino-cli lib install FastLED). Use the Arduino IDE or arduino-cli:

The ESP32 bridge runs ESPHome; the config is in gaugecontroller.yaml.

# Compile (replace board/port as needed)
arduino-cli compile --fqbn arduino:avr:mega Gaugecontroller

# Upload
arduino-cli upload -p /dev/ttyACM0 --fqbn arduino:avr:mega Gaugecontroller

Current default serial setup: CMD_PORT and DEBUG_PORT both point to Serial1 at 38400 baud.

Switching serial ports (debug → production)

Two #defines at the top of Gaugecontroller.ino control where commands and debug output go:

#define CMD_PORT   Serial1  // command channel (host sends SET, HOME, etc.)
#define DEBUG_PORT Serial1  // diagnostic prints (homing, boot messages)

Current default: both point to Serial1, so command and debug traffic share Mega pins TX1=18 / RX1=19 at 38400 baud.

USB-only debug setup: point both defines back at Serial if you want to talk to the sketch over the Arduino USB port instead:

#define CMD_PORT   Serial
#define DEBUG_PORT Serial

At that point the matching begin() call in setup() also needs to use the same baud rate you expect on the host side.

Split command/debug ports: if CMD_PORT and DEBUG_PORT do not point to the same serial port, setup() must initialise both. Right now it only calls:

DEBUG_PORT.begin(38400);

If you split them, add a second CMD_PORT.begin(...) call.

Arduino Mega hardware UARTs for reference:

Port TX pin RX pin
Serial1 18 19
Serial2 16 17
Serial3 14 15

Architecture

The sketch controls GAUGE_COUNT stepper-motor gauges using a trapezoidal velocity profile and a simple text serial protocol.

Key data structures

  • GaugePins — hardware pin mapping per gauge (dir, step, enable, active-high/low polarity flags, ledOrder string). Declared constexpr so TOTAL_LEDS can be computed from it at compile time. Configured in the gaugePins[] array at the top.
  • Gauge — per-gauge runtime state: position, target, velocity, accel, homing state machine, sweep mode.

Motion control (updateGauge)

Each call to updateGauge(id) in loop() computes dt since last call and updates velocity using a braking-distance check to produce smooth trapezoidal motion. Steps are accumulated as floating-point and emitted via doStep when the accumulator crosses ±1.

Homing sequence (updateHoming)

State machine: HS_START → HS_BACKING → HS_SETTLE → HS_DONE → HS_IDLE.
Backs up homingBackoffSteps at homingSpeed, waits 100 ms settle, then declares currentPos = 0. No physical end-stop is used; homing is purely time/step-count based.

Sweep mode

When sweepEnabled, updateSweepTarget bounces targetPos between minPos and maxPos autonomously.

LED strip

Two LED strips are driven: main backlight/status LEDs on LED_DATA_PIN (currently 22) and dial indicator LEDs on INDICATOR_LED_DATA_PIN (currently 36). The serial protocol still exposes one logical per-gauge LED segment: 0-2 backlight, 3-4 indicators, 5-6 status. gaugePins[i].ledOrder is a per-LED type string (one char per LED, 'G' = GRB-ordered, 'R' = RGB-ordered) and its length defines the logical LED count. TOTAL_LEDS, TOTAL_MAIN_LEDS, and TOTAL_INDICATOR_LEDS are computed at compile time. Per-gauge logical and physical offsets are cached in setup(). LED writes dirty only their physical strip, and the loop flushes each FastLED controller independently with showLeds().

Serial command protocol

Commands arrive as newline-terminated ASCII lines. Each parse* function in processLine handles one command family:

Command Syntax Effect
SET SET <id> <pos> Move gauge to absolute step position
SPEED SPEED <id> <steps/s> Set max speed
ACCEL ACCEL <id> <steps/s²> Set acceleration
ENABLE ENABLE <id> <0|1> Enable/disable driver output
ZERO ZERO <id> Mark current position as home without moving
HOME HOME <id> / HOMEALL Run homing sequence
SWEEP SWEEP <id> <accel> <speed> Start sweep (0/0 stops)
POS? POS? Query all gauges: POS <id> <cur> <tgt> <homed> <homingState> <sweep>
LED LED <id> <idx> <r> <g> <b> Set one LED (0-based index within gauge segment) to RGB colour (0255 each); <idx> may be a range N-M to set LEDs N through M in one command; also stops any active effect on those LEDs
LED? LED? Query all LEDs: one LED <id> <idx> <r> <g> <b> line per LED, then OK
BLINK BLINK <id> <idx> <on_ms> <off_ms> <r> <g> <b> Blink LED(s) at given colour; <idx> may be a range N-M; on_ms/off_ms both 0 stops blinking. 4-arg form (no colour) uses current LED colour
BREATHE BREATHE <id> <idx> <period_ms> <r> <g> <b> Smooth triangle-wave fade between black and the given colour; <idx> may be a range N-M
DFLASH DFLASH <id> <idx> <r> <g> <b> Two quick flashes (100 ms on/off each) followed by a 700 ms pause, then repeats; <idx> may be a range N-M
PING PING Responds PONG

All commands reply OK or ERR BAD_ID / ERR BAD_CMD etc.

Adding gauges

  1. Increment GAUGE_COUNT.
  2. Add a constexpr GaugePins entry to gaugePins[] (including the ledOrder string — one char per LED, 'G' for GRB or 'R' for RGB).
  3. Tune maxPos and homingBackoffSteps in the corresponding Gauge default or at runtime.
  4. TOTAL_LEDS, gaugeLedOffset[], and gaugeLedCount[] update automatically — no manual changes needed.