4.9 KiB
AGENTS.md
PlatformIO / Arduino-framework firmware for a NUCLEO-F042K6 reading two MS5611
barometers (one I2C, one SPI) and emitting validated CSV over the ST-LINK VCP.
See README.md for wiring tables, CSV format and status codes.
Commands
pio run # build (the only real verification gate)
pio run -t upload # flash over ST-LINK
pio device monitor # 115200 baud
pio run -t clean
There is one env, nucleo_f042k6. No lint, format, or typecheck step exists.
Always read the size report at the end of pio run. It is the acceptance
criterion for any change (see below). Current: flash 88.8% (29108 / 32768),
RAM 32.6% (2004 / 6144).
Flash budget is the dominant constraint
3.6 KB of flash headroom. This shapes almost every design decision here, and it is the single easiest thing to break without noticing.
-flto and -fsingle-precision-constant in platformio.ini are load-bearing.
Verified: building without them overflows FLASH by 7252 bytes and fails to
link. Both MS5611 driver libraries write their compensation maths with unsuffixed
double literals, which otherwise links the double soft-float helpers.
Consequences for new code:
- Never introduce
double, unsuffixed floating literals, or<math.h>calls on doubles. Usefloatandf-suffixed constants. - Never call
Serial.print(someFloat, digits)— it resolves toPrint::print(double, int)and pulls in soft-float.main.cpp::printFixed2()exists solely to format floats via scaled integers; use it. - Wrap string literals in
F()so they stay in flash. - No
String, nonew/delete.~BaroChannel()is deliberately non-virtual andprotectedto keep a destructor slot out of the vtable and avoid linkingoperator delete. Do not "fix" this into a virtual destructor. lib_depspin exact driver tags (MS5611#0.5.2,MS5611_SPI#0.4.3). Bumping them can blow the budget; rebuild and check size if you do.
Architecture
include/config.h— every pin, address, rate, threshold and validation limit. Change hardware behaviour here, not in the.cppfiles.include/baro_channel.h/src/baro_channel.cpp—BaroChannel, the shared state machine: init + PROM/CRC handshake, per-poll validation, error streaks, re-init, and the 1 s error LED pulse. Five pure-virtualdriver*()hooks are the only per-bus surface.src/baro_i2c.cpp/src/baro_spi.cpp— one concrete subclass each, plus a file-static instance exposed throughbaroI2cChannel()/baroSpiChannel().src/ms5611_crc.cpp— standalone PROM CRC-4 (AN520).src/main.cpp—setup()/loop(), CSV formatting, 10 Hz report scheduling.
The two-file split is mandatory, not stylistic. MS5611.h and
MS5611_SPI.h each define their own enum osr_t and MS5611_READ_OK, so they
cannot be included in the same translation unit. baro_channel.h therefore
includes neither driver header, and the channels are handed out via accessor
functions. Do not merge these TUs or hoist a driver include into the header.
Conventions that differ from defaults
- millis() rollover safety: deadline comparisons use a signed difference —
if ((int32_t)(now - deadline) >= 0). Never writenow >= deadline. poll()blocks for ~2 ADC conversions (~5 ms/sensor at OSR 1024).main.cppre-readsmillis()after polling before evaluating LED and report deadlines. Preserve that if you touch the loop.- Validation belongs in
BaroChannel, not the drivers. Neither library checks the PROM CRC, andMS5611::read()returnsMS5611_READ_OKfor an ADC read taken before the conversion completed (which yields 0). Do not simplify the range / stale / all-zero-PROM checks away by trusting driver return codes. - Style is Allman braces, 2-space indent,
_camelCaseprivate members,//(two spaces) block comments. There is no.clang-formatin the repo — runningclang-formatwould reformat everything into LLVM style. Don't. SPIandI2Cappear inlib_deps, but the code usesWiredirectly and nothing includes the third-partyI2Clibrary.
Testing
There is no automated test suite. test/ holds only the stock PlatformIO
README, and pio test collects 0 cases. Do not claim tests pass.
ms5611_crc.{h,cpp} is intentionally free of Arduino headers so it can be
exercised on the host:
clang++ -std=c++17 -Wall -Wextra -c src/ms5611_crc.cpp -Iinclude -o /tmp/crc.o
The differential check against the NuttX reference described in README.md was
a host-side exercise and is not committed.
Anything beyond the CRC needs real hardware: build, flash, and watch the CSV
stream. Unplugging a sensor is the quick way to exercise the error paths
(ERR_INIT / ERR_CRC, LED pulse, auto re-init after 10 consecutive failures).
Notes
.pio/, .omo/, .junie/ and .idea/ are gitignored tooling state, not
project sources.