24 Commits

Author SHA1 Message Date
e525dba0c4 Changed timing to timer interrupts - scoping data showed inconsistencies 2026-05-19 00:50:12 +02:00
1b699352ce LEDs removed from Gaugecontroller.ino, backup in aptly named directory 2026-05-18 16:04:37 +02:00
c32d208854 Single colour LEDs, I think, will be removed anyway 2026-05-17 18:00:19 +02:00
db05bc0864 5th gauge added 2026-05-03 17:34:09 +02:00
5f73e75f5b Indicator LEDs are now bog-standard red and green LEDs. Looks more original. 2026-05-03 15:59:20 +02:00
5656986768 Gauges take precedence over LEDs. 2026-05-03 14:18:54 +02:00
aa029587a4 Timing optimised 2026-05-02 21:57:30 +02:00
abbbd16b5c Version with no VFD built 2026-05-02 21:34:29 +02:00
e63867ba5e Pins changed 2026-05-02 13:19:01 +02:00
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
361cf52252 More logs 2026-04-28 00:31:23 +02:00
2d63ec6006 Reverted - same problem again 2026-04-28 00:22:01 +02:00
511ee05712 check actual values for indicator LEDs 2026-04-28 00:18:32 +02:00
03ab8604ba write_action removed - worked before that... 2026-04-28 00:14:55 +02:00
edb973bb61 Reverted the whole AI bullshit 2026-04-27 23:46:45 +02:00
bffcf62cae Timing changed on Arduino script, latest version of ESP-Home script added 2026-04-27 19:20:30 +02:00
27597bceab configurable GRB/RGB LEDs per LED 2026-04-27 09:06:43 +02:00
016de2ccb4 4 Gauges 2026-04-26 23:26:00 +02:00
15257ae6f2 CFG? added 2026-04-23 00:31:17 +02:00
795eb0ecf3 Some fixes (integers for speed, Serial returns back to HA etc.) 2026-04-22 16:14:10 +02:00
558c5b18c2 extended to 4 gauges, zeroing for all gauges individually and collectively added 2026-04-22 15:54:53 +02:00
fa66dd70d4 Speed/Config and VFD added 2026-04-22 14:56:04 +02:00
b14bdf7fc3 Added "SET" implementation for gauge values 2026-04-22 14:49:59 +02:00
427dde8c72 Continued rewrite - Lights and light effects implemented 2026-04-22 14:39:01 +02:00
18 changed files with 7714 additions and 698 deletions

1
.gitignore vendored
View File

@@ -32,3 +32,4 @@
*.out *.out
*.app *.app
.codex

View File

@@ -6,6 +6,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
Main firmware lives in `Gaugecontroller/Gaugecontroller.ino`. Requires the **FastLED** library (`arduino-cli lib install FastLED`). Use the Arduino IDE or `arduino-cli`: 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`.
```bash ```bash
# Compile (replace board/port as needed) # Compile (replace board/port as needed)
arduino-cli compile --fqbn arduino:avr:mega Gaugecontroller arduino-cli compile --fqbn arduino:avr:mega Gaugecontroller
@@ -58,7 +60,7 @@ The sketch controls `GAUGE_COUNT` stepper-motor gauges using a trapezoidal veloc
### Key data structures ### Key data structures
- `GaugePins` — hardware pin mapping per gauge (dir, step, enable, active-high/low polarity flags, `ledCount`). Declared `constexpr` so `TOTAL_LEDS` can be computed from it at compile time. Configured in the `gaugePins[]` array at the top. - `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. - `Gauge` — per-gauge runtime state: position, target, velocity, accel, homing state machine, sweep mode.
### Motion control (`updateGauge`) ### Motion control (`updateGauge`)
@@ -76,7 +78,7 @@ When `sweepEnabled`, `updateSweepTarget` bounces `targetPos` between `minPos` an
### LED strip ### LED strip
One shared WS2812B strip is driven from `LED_DATA_PIN` (currently 22). Each gauge owns a contiguous segment of the strip; `gaugePins[i].ledCount` sets the segment length (0 = no LEDs). `TOTAL_LEDS` is computed at compile time via `constexpr sumLedCounts()` — no manual constant to keep in sync. Per-gauge offsets into the flat `leds[]` array are computed once in `setup()` into `gaugeLedOffset[]`. LED commands and effects mark the strip dirty, and `FastLED.show()` is called once per main-loop iteration if anything changed. 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 ### Serial command protocol
@@ -104,6 +106,6 @@ All commands reply `OK` or `ERR BAD_ID` / `ERR BAD_CMD` etc.
### Adding gauges ### Adding gauges
1. Increment `GAUGE_COUNT`. 1. Increment `GAUGE_COUNT`.
2. Add a `constexpr GaugePins` entry to `gaugePins[]` (including `ledCount`). 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. 3. Tune `maxPos` and `homingBackoffSteps` in the corresponding `Gauge` default or at runtime.
4. `TOTAL_LEDS` and `gaugeLedOffset[]` update automatically — no manual changes needed. 4. `TOTAL_LEDS`, `gaugeLedOffset[]`, and `gaugeLedCount[]` update automatically — no manual changes needed.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ A dedicated gauge controller for Arduinos.
This repository contains: This repository contains:
- `Gaugecontroller/Gaugecontroller.ino`: the Arduino Mega firmware for the stepper gauges, LEDs, and integrated HV5812-based VFD - `Gaugecontroller/Gaugecontroller.ino`: the Arduino Mega firmware for the stepper gauges, LEDs, and integrated HV5812-based VFD
- `gauge.py`: the ESP32 / MicroPython MQTT bridge that exposes the controller to Home Assistant - `gaugecontroller.yaml`: the ESPHome-based ESP32 firmware that exposes the gauges and VFD to Home Assistant via the native API
## VFD Support ## VFD Support
@@ -48,16 +48,19 @@ Rules:
- shorter values are right-aligned - shorter values are right-aligned
- leading zeroes are preserved if they are part of the input - leading zeroes are preserved if they are part of the input
## Home Assistant Entities ## Home Assistant Integration
The MQTT bridge publishes Home Assistant discovery entities for the VFD: The ESPHome firmware in `gaugecontroller.yaml` exposes entities to Home Assistant via the native API:
- `VFD Display` ### Gauge Controls
text entity for the displayed value - Number entities for each gauge's target value (with unit conversion)
- `VFD Decimal Point` - Number entities for speed and acceleration (diagnostic)
switch entity - Rezero buttons for each gauge and all gauges
- `VFD Alarm`
switch entity ### VFD Display
- `VFD Display`: text entity for the displayed value
- `VFD Decimal Point`: switch entity
- `VFD Alarm`: switch entity
The display is intentionally exposed as a text entity rather than a numeric entity so that: The display is intentionally exposed as a text entity rather than a numeric entity so that:
@@ -65,27 +68,12 @@ The display is intentionally exposed as a text entity rather than a numeric enti
- hexadecimal values like `DEAD` or `BEEF` work - hexadecimal values like `DEAD` or `BEEF` work
- clearing the display is possible with an empty value - clearing the display is possible with an empty value
## MQTT Topics ### LED Controls
- RGB light entity for each gauge's backlight with effects (Blink, Breathe, Double Flash)
- Binary light entities for each gauge's red/green indicators and status lights
Using the configured `mqtt_prefix` from `config.json`, the VFD topics are: ### Diagnostics
- WiFi signal sensor
- `<prefix>/vfd/set` - Uptime sensor
- `<prefix>/vfd/state` - IP address and SSID text sensors
- `<prefix>/vfd/decimal_point/set` - Arduino Last Message sensor
- `<prefix>/vfd/decimal_point/state`
- `<prefix>/vfd/alarm/set`
- `<prefix>/vfd/alarm/state`
Example with the default prefix `gauges`:
- `gauges/vfd/set`
- `gauges/vfd/decimal_point/set`
- `gauges/vfd/alarm/set`
Example payloads:
- publish `0123` to `gauges/vfd/set`
- publish `ON` to `gauges/vfd/decimal_point/set`
- publish `OFF` to `gauges/vfd/alarm/set`
The MQTT bridge then converts that into the correct Arduino serial command such as `VFD 0123.`.

View File

@@ -183,13 +183,14 @@ Then connect the motor side of that driver to:
according to the driver board you are using. according to the driver board you are using.
## 14. Wire The WS2812B LEDs ## 14. Wire The WS2812 LEDs
Connect: Connect:
- `Mega D22` -> `WS2812B DIN` - `Mega D22` -> main backlight/status strip `DIN`
- `5V LED supply` -> `WS2812B 5V` - `Mega D36` -> indicator strip `DIN`
- `WS2812B GND` -> common ground rail - `5V LED supply` -> both strip `5V` inputs
- both strip `GND` inputs -> common ground rail
If the LED chain is long or bright: If the LED chain is long or bright:

View File

@@ -205,9 +205,10 @@ If `D8` and `D9` come from separate fly wires to the stripboard, keep them in th
Route: Route:
- `D22` -> `WS2812 DIN` - `D22` -> main backlight/status strip `DIN`
- `5V` -> `WS2812 5V` - `D36` -> indicator strip `DIN`
- `GND` -> `WS2812 GND` - `5V` -> both strip `5V` inputs
- `GND` -> both strip `GND` inputs
Keep the LED connector in the low-voltage area. Keep the LED connector in the low-voltage area.

View File

@@ -930,6 +930,19 @@ _last_discovery_ms = 0
_DISCOVERY_INTERVAL_MS = 350 _DISCOVERY_INTERVAL_MS = 350
def _compact_discovery_payload(payload):
"""Trim optional HA discovery fields when RAM is tight."""
compact = dict(payload)
# Light entities are the largest payloads because they repeat effect metadata.
# Keep core functionality, but omit optional effect declarations to reduce heap use.
if compact.get("schema") == "json":
compact.pop("effect", None)
compact.pop("effect_list", None)
return compact
def check_mqtt(): def check_mqtt():
global client_ref, _mqtt_connected, _last_mqtt_check global client_ref, _mqtt_connected, _last_mqtt_check
now = utime.ticks_ms() now = utime.ticks_ms()
@@ -984,7 +997,7 @@ def check_mqtt():
def _publish_discovery_entity(client, topic, payload, log_msg): def _publish_discovery_entity(client, topic, payload, log_msg):
gc.collect() gc.collect()
client.publish(topic, ujson.dumps(payload), retain=True) client.publish(topic, ujson.dumps(_compact_discovery_payload(payload)), retain=True)
info(log_msg) info(log_msg)

46
changes.md Normal file
View File

@@ -0,0 +1,46 @@
# Changes
## 2026-04-27 — Arduino firmware refactor (`Gaugecontroller/Gaugecontroller.ino`)
### Non-blocking VFD multiplexer
`vfd::refresh()` previously held each digit for 2000 µs via `delayMicroseconds`,
which capped the effective stepper pulse rate at roughly 500 Hz regardless of
`maxSpeed`. It now tracks `phaseStartMicros`/`phaseActive` and returns
immediately while the digit is still being held; the main loop runs at
microsecond cadence again and the configured `maxSpeed = 4000.0f` steps/s is
actually achievable.
### Fixed-buffer command parser (no more `String` heap churn)
Replaced `String rxLine` with `char rxBuf[128]` and converted the entire
command pipeline to take `const char*`:
- `processLine`, `sendReply`, `vfd::parseCommand`
- All `parse*` functions: `parseSet`, `parseSpeed`, `parseAccel`, `parseEnable`,
`parseZero`, `parseHome`, `parseSweep`, `parsePosQuery`, `parseCfgQuery`,
`parseLedQuery`, `parseLed`, `parseBlink`, `parseBreathe`, `parseDflash`,
`parseVfd`, `parsePing`.
`parseSpeed` / `parseAccel` / `parseSweep` use `strncmp` + `atof` because the
default AVR-libc `sscanf` doesn't support `%f`. No allocations on the command
path; the Mega's heap no longer fragments over time.
### Cached `ledNeedsSwap[TOTAL_LEDS]`
Per-LED RGB-vs-GRB swap flag is now precomputed once in `setup()` from
`gaugePins[].ledOrder`. `encodeForStrip` is a single array index instead of
walking the gauge table on every LED read/write.
### Cached step direction per gauge
Added `Gauge.lastDir`. `setDir()` skips the DIR-pin `digitalWrite` when the
direction hasn't flipped (the common case during a step run) and adds a 1 µs
DIR-to-STEP setup delay only when it actually flips.
### Cleanups
- Removed the `absf` helper; use `fabsf` consistently.
- Removed the `+ 0.0001f` epsilon in the trapezoidal braking-distance divisor.
`parseAccel` already rejects `accel <= 0`, so the divisor is always positive.
- Fixed the `<r> <ig> <b>` typo to `<r> <g> <b>` in the protocol comment for
`DFLASH`.
### Build verification
`arduino-cli compile --fqbn arduino:avr:mega Gaugecontroller`:
17758 B flash (6%), 1845 B SRAM (22%).

2394
gaugecontroller.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -163,19 +163,22 @@ Also connect:
If your driver boards need separate motor power, supply that from the proper motor supply. Do not power motors from the Mega `5V` pin. If your driver boards need separate motor power, supply that from the proper motor supply. Do not power motors from the Mega `5V` pin.
## WS2812B LED Strip ## WS2812 LED Strips
The current sketch expects one shared WS2812B chain. The current sketch expects two LED data chains. Backlight and status LEDs stay
on the main strip; the red/green dial indicator LEDs are on their own strip.
| Mega Pin | WS2812B | | Mega Pin | LED Strip |
|---|---| |---|---|
| `D22` | `DIN` | | `D22` | main backlight/status `DIN` |
| `5V` | `5V` | | `D36` | indicator `DIN` |
| `GND` | `GND` | | `5V` | both strips `5V` |
| `GND` | both strips `GND` |
Notes: Notes:
- the code expects `7 LEDs per gauge`, so `21 LEDs total` - the command protocol still exposes `7 LEDs per gauge`
- logical indices `0-2` are backlight, `3-4` are indicators, and `5-6` are status
- use a proper 5V supply sized for the LED current - use a proper 5V supply sized for the LED current
- keep LED ground common with the Mega - keep LED ground common with the Mega