From 7c3068ff3adcee9af3d0b58285ddfe55a4364e9c Mon Sep 17 00:00:00 2001 From: "Adrian A. Baumann" Date: Thu, 21 May 2026 21:46:56 +0200 Subject: [PATCH] docs: add Gaugecontroller v2.0 design spec Describes the two-part refactor: gauge_config.h for centralised pin and motion defaults, and uniform sscanf parsing across all parse* functions. --- .../2026-05-21-gaugecontroller-v2-design.md | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-21-gaugecontroller-v2-design.md diff --git a/docs/superpowers/specs/2026-05-21-gaugecontroller-v2-design.md b/docs/superpowers/specs/2026-05-21-gaugecontroller-v2-design.md new file mode 100644 index 0000000..62701ec --- /dev/null +++ b/docs/superpowers/specs/2026-05-21-gaugecontroller-v2-design.md @@ -0,0 +1,162 @@ +# Gaugecontroller v2.0 Design + +**Date:** 2026-05-21 +**Branch:** Stepper-Only +**Scope:** Code quality / architecture — same features, better structure. No behaviour change. + +## Goal + +Eliminate scattered magic constants and inconsistent parsing patterns. A developer adding or tuning a gauge should only need to edit one file. + +## What is NOT changing + +- ISR logic, Q16 fixed-point stepping, trapezoidal velocity profile +- Serial protocol commands and responses +- Runtime `Gauge` struct fields (stay `float` for velocity, speed, accel) +- LED code (absent on this branch; out of scope) + +--- + +## Section 1: `gauge_config.h` + +Create `Gaugecontroller/gauge_config.h` alongside the sketch. + +### New struct + +```cpp +struct GaugeConfig { + // Hardware + uint8_t dirPin; + uint8_t stepPin; + int8_t enablePin; // -1 = no enable pin + bool dirInverted; + bool stepActiveHigh; + bool enableActiveLow; + + // Motion defaults (integers — cast to float in setup()) + long minPos; + long maxPos; + long homingBackoffSteps; + int maxSpeed; // steps/s + int accel; // steps/s² + int homingSpeed; // steps/s +}; +``` + +### Config table + +```cpp +constexpr GaugeConfig gaugeConfigs[] = { + // dir step en dirInv stepHi enLow min max backoff speed accel homeSpd + { 48, 49, -1, false, true, true, 0, 3780, 3800, 4000, 6000, 500 }, + { 8, 9, -1, true, true, true, 0, 3780, 3800, 4000, 6000, 500 }, + { 52, 53, -1, false, true, true, 0, 3780, 3800, 4000, 6000, 500 }, + { 50, 51, -1, false, true, true, 0, 3780, 3800, 4000, 6000, 500 }, +}; + +static const uint8_t GAUGE_COUNT = + sizeof(gaugeConfigs) / sizeof(gaugeConfigs[0]); +``` + +Adding gauge 5 is one new table row. `GAUGE_COUNT` updates automatically. + +### Changes to `Gaugecontroller.ino` + +- Remove `constexpr GaugePins gaugePins[]`, `struct GaugePins`, and the hardcoded `GAUGE_COUNT`. +- Add `#include "gauge_config.h"`. +- In `setup()`, initialise each `Gauge`'s motion defaults from `gaugeConfigs[i]`: + +```cpp +gauges[i].minPos = gaugeConfigs[i].minPos; +gauges[i].maxPos = gaugeConfigs[i].maxPos; +gauges[i].homingBackoffSteps = gaugeConfigs[i].homingBackoffSteps; +gauges[i].maxSpeed = (float)gaugeConfigs[i].maxSpeed; +gauges[i].accel = (float)gaugeConfigs[i].accel; +gauges[i].homingSpeed = (float)gaugeConfigs[i].homingSpeed; +``` + +- All existing references to `gaugePins[i].dirPin` etc. become `gaugeConfigs[i].dirPin` etc. (field names are identical). +- Remove the hardcoded default initialisers from the `Gauge` struct definition (`maxPos = 3780`, `homingBackoffSteps = 3800`, `maxSpeed = 4000.0f`, `accel = 6000.0f`, `homingSpeed = 500.0f`, `minPos = 0`). These fields become zero-initialised and are then set from `gaugeConfigs[i]` in `setup()`, eliminating the risk of the struct defaults and config table silently diverging. + +--- + +## Section 2: Uniform `sscanf` parsing + +Three `parse*` functions currently use manual `indexOf`/`substring`. Convert them to `sscanf` to match the rest of the parser. + +### `parseSpeed` + +```cpp +bool parseSpeed(const String& line) { + int id; float speed; + if (sscanf(line.c_str(), "SPEED %d %f", &id, &speed) == 2) { + if (id < 0 || id >= GAUGE_COUNT) { sendReply("ERR BAD_ID"); return true; } + if (speed <= 0.0f) { sendReply("ERR BAD_SPEED"); return true; } + gauges[id].maxSpeed = speed; + sendReply("OK"); + return true; + } + return false; +} +``` + +### `parseAccel` + +```cpp +bool parseAccel(const String& line) { + int id; float accel; + if (sscanf(line.c_str(), "ACCEL %d %f", &id, &accel) == 2) { + if (id < 0 || id >= GAUGE_COUNT) { sendReply("ERR BAD_ID"); return true; } + if (accel <= 0.0f) { sendReply("ERR BAD_ACCEL"); return true; } + gauges[id].accel = accel; + sendReply("OK"); + return true; + } + return false; +} +``` + +### `parseSweep` + +```cpp +bool parseSweep(const String& line) { + int id; float accel, speed; + if (sscanf(line.c_str(), "SWEEP %d %f %f", &id, &accel, &speed) == 3) { + if (id < 0 || id >= GAUGE_COUNT) { sendReply("ERR BAD_ID"); return true; } + Gauge& g = gauges[id]; + if (accel <= 0.0f || speed <= 0.0f) { + g.sweepEnabled = false; + g.velocity = 0.0f; + stopTimerStepping(id); + sendReply("OK"); + return true; + } + g.accel = accel; + g.maxSpeed = speed; + g.sweepEnabled = true; + g.sweepTowardMax = true; + atomicWriteLong(g.targetPos, g.maxPos); + sendReply("OK"); + return true; + } + return false; +} +``` + +No change to accepted syntax, error codes, or response format. + +--- + +## File inventory + +| File | Change | +|---|---| +| `Gaugecontroller/gauge_config.h` | New — all pin + motion defaults | +| `Gaugecontroller/Gaugecontroller.ino` | Remove `GaugePins`, add include, update `setup()`, rewrite 3 parsers | + +## Success criteria + +- Sketch compiles cleanly with `arduino-cli compile --fqbn arduino:avr:mega Gaugecontroller` +- `GAUGE_COUNT` need not be edited when adding a gauge — only `gaugeConfigs[]` changes +- No `indexOf`/`substring` remain in any `parse*` function +- All existing protocol commands behave identically to v1