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.
This commit is contained in:
162
docs/superpowers/specs/2026-05-21-gaugecontroller-v2-design.md
Normal file
162
docs/superpowers/specs/2026-05-21-gaugecontroller-v2-design.md
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user