3.4 KiB
Audit Hardening — 2026-08-17
This pass addresses the findings from the independent prototype-v0 audit. It does not add calibration, BLE, persistent storage, filtering beyond the selected L3G4200D hardware filter, or sensor fusion.
Git checkpoints
f1ac20f: exact pre-hardening firmware and axis-validation baseline- The verified hardening changes are committed separately after this report
No remote repository is configured; these are local history checkpoints.
Changes
- A clean build now selects
esp32c3fromsdkconfig.defaults. - ADXL345
INT_SOURCEand L3G4200DSTATUS_REGare read immediately before their corresponding six-byte XYZ reads. - Raw status bytes and a cumulative loop-overrun count are appended to CSV.
- A delayed acquisition loop resynchronizes rather than issuing catch-up bursts.
- ADXL345 FIFO, interrupt enables, and hardware offset registers are explicitly reset during initialization so warm MCU resets are deterministic.
- L3G4200D LPF2 is routed to the output with
CTRL_REG5=0x02, making the configured 25 Hz cutoff effective. - Both sensor drivers remove their I2C device handles after an initialization failure and expose deinitializers.
- Candidate-address loops use an element count rather than byte size.
- The capture utility auto-detects a single USB modem port, writes numeric CSV only, and reports sequence gaps, resets, timestamp anomalies, malformed records, and ignored non-record lines.
- Documentation now distinguishes the MCU poll timestamp and nominal scale from sensor sample timing and measured per-axis scale.
Build validation
- Normal incremental build: pass
- Clean temporary copy with no
sdkconfigorbuild/: pass - Clean configuration selected
CONFIG_IDF_TARGET="esp32c3"fromsdkconfig.defaults - Hardened binary size:
0x242d0bytes; 86% of the application partition free
Hardware validation
The hardened firmware was flashed to the assembled XIAO ESP32-C3 prototype. Both devices initialized successfully after a USB-triggered warm reset, including all new register writes and the L3G4200D CTRL5 readback.
The capture captures/hardening_freshness.csv contains:
- 3,796 consecutive records
- Sequence gaps: 0
- Sequence resets: 0
- Timestamp anomalies: 0
- Timestamp interval: 10,000 us for all 3,795 intervals
- Rejected records: 0
- Acquisition loop overruns: 0
- ADXL345 DATA_READY clear at status time: 192 records (5.058%)
- ADXL345 overrun set: 0 records
- L3G4200D ZYXDA clear: 0 records
- L3G4200D ZYXOR set: 112 records (2.950%)
The ADXL345 clear events have a coherent 27/28-poll pattern, including adjacent clear events when a device update likely occurs between the separate status and data transactions. A clear bit is therefore conservatively treated as stale/untrusted; a set bit proves unread data existed before the read. The gyro overrun pattern directly confirms that its internal output clock is faster than the MCU poll on this unit.
These results validate the audit finding that exact 100 Hz MCU polling does not mean either sensor is synchronized to that clock. Status labeling is the scoped prototype fix. Interrupt/FIFO-driven acquisition remains a later buffering task.
A final post-build/post-flash smoke capture added another 1,634 consecutive records with zero gaps, timestamp anomalies, rejected records, ADXL345 overruns, or acquisition-loop overruns. It again exposed ADXL345 DATA_READY clear on 81 polls and L3G4200D overrun on 48 polls.