- 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
5.8 KiB
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,ledOrderstring). DeclaredconstexprsoTOTAL_LEDScan be computed from it at compile time. Configured in thegaugePins[]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 (0–255 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
- Increment
GAUGE_COUNT. - Add a
constexpr GaugePinsentry togaugePins[](including theledOrderstring — one char per LED,'G'for GRB or'R'for RGB). - Tune
maxPosandhomingBackoffStepsin the correspondingGaugedefault or at runtime. TOTAL_LEDS,gaugeLedOffset[], andgaugeLedCount[]update automatically — no manual changes needed.