#pragma once #include #include #include namespace fgc { // Sensirion SHT4x (SHT41) I2C protocol helpers: the command bytes, the CRC-8 // the sensor appends to each 16-bit word, and the decode from a raw 6-byte // measurement response to physical units. Kept pure (no I/O) so it is // unit-testable and lives in fgc_core — mirrors MtiProtocol / TelemetryParser. // The I/O half (bus, poll thread, staleness) stays in Sht41EnvSensor. // // Measurement response layout, big-endian: // [0..1] temperature raw [2] CRC of bytes 0..1 // [3..4] humidity raw [5] CRC of bytes 3..4 // Soft reset; the datasheet asks for ~1 ms to settle afterwards. inline constexpr uint8_t kSht41CmdSoftReset = 0x94; // Measure temperature + RH, high precision; ~10 ms conversion time. inline constexpr uint8_t kSht41CmdMeasureHighPrec = 0xFD; // Bytes in a measurement response. inline constexpr size_t kSht41ResponseBytes = 6; // SHT4x CRC-8: polynomial 0x31, init 0xFF, no reflection, no final XOR. // Datasheet test vector: {0xBE, 0xEF} -> 0x92. uint8_t sht41Crc8(const uint8_t* data, size_t len); // One decoded measurement in physical units. struct Sht41Reading { float temp_c = 0.0f; float humidity_pct = 0.0f; }; // Decode a measurement response. Returns nullopt if the response is not // kSht41ResponseBytes long or either word fails its CRC — a corrupt frame must // be discarded rather than reported as a reading, since a garbled word is // indistinguishable from a plausible temperature once converted. // // Conversions (datasheet): T[°C] = -45 + 175 * raw/65535, // RH[%] = -6 + 125 * raw/65535, with RH clamped to 0..100 (the raw formula // slightly overshoots both ends by design). std::optional decodeSht41Measurement(const std::vector& rx); } // namespace fgc