Two ways the error counters lied about sensor health.
The first measurement failed on every boot, leaving a permanent
i2c_errors=1 that a master polling READ_STATUS could not tell apart from
a real fault. The sensor NACKs a command arriving while it is still busy
with the previous one, and the bring-up sequence issued a measurement
immediately after clear_status with no quiet time. Commands are now
spaced by sht3x::COMMAND_SETTLE_MS. Bring-up failures stay uncounted on
purpose: the counters describe operational health, and an unreadable
serial already surfaces as a zero in READ_INFO.
read_status() failures were discarded via `if let Ok`, which made a
persistently unreadable status register indistinguishable from a
genuinely clean one — both leave sensor_status at zero. They are now
counted, but kept out of the consecutive-failure tally so only the
measurement path drives the fault and soft-reset logic.
Confirmed on hardware: a fresh boot now holds err[i2c=0 sht_crc=0
frame=0 bus_crc=0] indefinitely, where it previously showed i2c=1 from
the first poll onwards. After a full bus test run the counters read
exactly frame=2 bus_crc=2, accounting for the four faults the suite
injects deliberately and nothing else.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
RTIC 2 firmware for an RP2040 acting as an RS485 slave, reading a
Sensirion SHT31 over I2C and answering a custom binary protocol.
Split into two crates so the protocol is testable without hardware:
- wiredsensor-core: framing, CRC-16/MODBUS, command dispatch and SHT3x
data-sheet math. Pure computation, no I/O, no peripherals. Covered by
42 host tests including the CRC-16/MODBUS catalogue vector, the SHT3x
data-sheet CRC-8 example, and an exhaustive single-bit-corruption
sweep over every byte of a frame.
- wiredsensor-fw: the RTIC application, PL011 register driver and SHT31
I2C driver.
Requests are answered entirely from a cached reading. An SHT31
high-repeatability conversion takes up to 15 ms, well past the
turnaround a master expects, so the sensor is polled at the lowest
priority and the bus path never touches I2C. RTIC's priority ceilings
make "a conversion cannot delay a reply" a checked property rather than
an argument.
Two PL011 details drive the low-level approach, since rp2040-hal exposes
neither: the receive-timeout interrupt for prompt frame delimiting, and
the BUSY flag, which is the only way to know the final stop bit has left
the shift register before releasing DE. The HAL performs the fiddly
baud-divisor setup once, then the raw peripheral is reclaimed via free().
Note that RTIM cannot be the sole frame delimiter: it only fires while
the RX FIFO is non-empty, so a frame landing exactly on the FIFO
watermark is drained by the watermark interrupt and never times out. The
authoritative delimiter is a monotonic timer measured from the last
received byte.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>