Drogheda/03-Architecture-Hardware/firmware-review.md

121 lines
6.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Firmware Review — `blackpillrtkcode.ino` (v1 → v2)
Review of the as-deployed watchdog firmware, with the fixes shipped in
`04-Source-Code/firmware/blackpillrtkcode_v2/blackpillrtkcode_v2.ino`. Severity: 🔴 likely field-impacting ·
🟠 robustness · 🟡 cleanup.
UBX command checksums and ADC divider math were **verified programmatically**
(see end of this doc) — those parts of v1 are correct.
---
## Issues found
### 🔴 1. Loss-of-corrections recovery targets the wrong subsystem
**v1:** after `SOFT_RESET_DELAY` (5 min) of `fixQuality < 4`, it sends
`UBX_SOFT_RESET` (a GNSS hot-reset) to the ZED-F9P.
**Problem:** corrections arrive over the **4G NTRIP modem**, not from the GNSS.
Hot-resetting the receiver does nothing for a hung modem — the dominant field
failure per the client. Five minutes are spent on a no-op before the only
effective action (a full power-cycle at 10 min).
**v2:** drops the GNSS hot-reset; recovery = **power-cycle the whole stack**
(which restarts the 4G module), gated by a grace window and backoff.
### 🔴 2. Hard-reset has no backoff → reboot-loop / Wialon flapping
**v1:** after `HARD_RESET_DELAY` (10 min) it power-cycles, returns to BROWNOUT,
immediately powers back on, and — if the fault persists — does it **again every
~10 min indefinitely**. Each cycle is a multi-minute disconnect.
**Problem:** a genuinely dead 4G card, or a unit parked without sky view, makes
the unit **flap off Wialon forever**. This is a prime suspect for "desconexiones
recurrentes."
**v2:** **exponential backoff** (1→2→4→8→16 min, capped 20 min) then a **latched
LIMP mode** that keeps the unit **reporting standalone GPS** and only retries
recovery every 30 min. The unit never silently vanishes.
### 🔴 3. No detection of a fully hung stack (NMEA silence)
**v1:** only reacts via `fixQuality`. If the GNSS/4G stack hangs and stops
emitting NMEA entirely, `fixQuality` simply freezes at its last value — the
watchdog may not act at all, or acts on stale data.
**v2:** tracks `lastNmeaMs`; **no NMEA for 60 s while powered ⇒ power-cycle now**.
Catches hung hardware far faster than the fix-quality ladder.
### 🟠 4. No hysteresis / debounce on vehicle-power & ignition
**v1:** `pa4_on = (v5vF > 4.0)` is a single hard threshold; ignition is a raw
`digitalRead`. Around the threshold (alternator noise, ignition flicker), the
rate mode (1 Hz↔30 s) and state can chatter.
**v2:** hysteresis band (ON >4.2 V, OFF <3.8 V) + 750 ms debounce on both
vehicle-power and ignition.
### 🟠 5. Brownout exit has no debounce (oscillation risk)
**v1:** entry to BROWNOUT is debounced 3 s, but the **exit** powers on the instant
`pa4_on || pa5_on || vBat>3.7`. A brief blip near the threshold can start a
power-on it can't sustain → drain → repeat.
**v2:** wake is driven by the same debounced/hysteretic signals; brownout entry
uses a 5 s confirmation.
### 🟠 6. No thermal awareness (the actual field failure mode)
**v1:** nothing measures temperature, despite "died from heat."
**v2:** reads the STM32 internal temp sensor (F411 factory cal), logs session
**min/max**, **derates to 30 s** above 75 °C to cut self-heat/power, and shows it
on the dashboard. Firmware can't cool the box, but a pulled unit's `tMax` now
**proves or kills the thermal hypothesis**. (Disable with `ENABLE_MCU_THERMAL 0`
if your core lacks `ATEMP`/`AVREF`.)
### 🟡 7. `String` heap churn on an ARM MCU
**v1:** comments claim `const char*` avoids fragmentation, but `nmeaBuffer` is a
`String`, and `drawDashboard` builds a `String paddedMsg` every refresh.
**v2:** fixed `char nmeaBuf[110]` parser (no heap) and `snprintf` padding.
### 🟡 8. Doc/firmware mismatch: boot delay
The LED table says "waiting 25 s"; v1 `BOOT_STABILIZE_TIME` is **15 s**.
**v2:** set to **25 s** to match the documented SOP (gives the 4G modem more time
to attach before the supervisor starts judging the link). Tune on the bench.
### 🟡 9. Minor: redundant `pinMode` before `GNSS.begin()`
Harmless (the serial peripheral reconfigures the pins); left as explicit intent
in v2.
---
## What v1 got *right* (kept in v2)
- **Float is not treated as failure.** `fixQuality >= 4` correctly counts both
RTK **fixed (4)** and **float (5)** as healthy. (Earlier verbal claim that
FLOAT triggered resets was wrong — corrected here.)
- Floating the TX pin on power-off to avoid back-powering the rail through a GPIO.
- Low-pass filtering the ADC readings.
- Hysteresis between `BAT_CRITICAL` (3.4 V) and `BAT_RECOVERY` (3.7 V).
- The UBX rate commands and their checksums (verified below).
---
## v2 recovery state machine (summary)
```
SLEEP ──(vehicle pwr | ignition | Vbat>3.7)──► BOOTING ──(25 s)──► RUNNING
▲ │
│ Vbat<3.4 & no pwr & no ign (5 s) │
└───────────────────────────────────────────────────────── RUNNING supervises:
• no NMEA 60 s ........................► POWER_CYCLE (gated by backoff)
• RTK lost > 120 s grace .............► POWER_CYCLE (gated by backoff)
• RTK healthy 5 min .................► clear backoff + LIMP latch
POWER_CYCLE (6 s rail off) ──► BOOTING ; after 5 fails ──► LIMP (keep reporting)
```
---
## Verification performed (2026-06-19)
- **UBX checksums** recomputed (8-bit Fletcher over class…payload): `UBX_1HZ`
→ `01 39` ✓, `UBX_30S` → `1E AD` ✓, `UBX_SOFT_RESET` → `0F 66` ✓.
- **CFG-RATE decode:** 1 Hz = measRate 1000 ms × navRate 1; 30 s = 1000 ms × 30 ✓.
- **ADC dividers (12-bit, 3.3 V, ÷2):** LiPo 3.4–4.2 V → 1.70–2.10 V at pin
(2110–2606 counts); 5 V rail → 2.50 V (3102 counts). No ADC clipping ✓.
- **Backoff ladder:** 1, 2, 4, 8, 16 min → LIMP @ 30 min, as designed ✓.
- **Compile:** v2 builds clean under `g++ -std=c++14 -Wall -Wextra` against a
hardware stub (logic/types/flow only).
> ⚠️ The stub compile validates **logic**, not the STM32 target. **Bench-flash and
> test v2 on real hardware** (Arduino IDE / PlatformIO, STM32duino core) before
> any field deployment — especially the thermal read and the power-cycle timing.