#include "fgc/sensors/Sht41EnvSensor.h" #include "fgc/Logger.h" #include "fgc/sensors/I2cBus.h" #include "fgc/sensors/Sht41Protocol.h" #include #include #include #include namespace fgc { namespace { // The command bytes and the frame decode (CRC + raw->physical) live in // Sht41Protocol, which is pure and unit-tested; this file owns only the I/O: // bus access, poll timing, and the staleness rule. constexpr auto kConversionDelay = std::chrono::milliseconds(10); constexpr auto kSoftResetSettle = std::chrono::milliseconds(1); } // namespace struct Sht41EnvSensor::Impl { Impl(std::string dev, uint8_t a, std::chrono::milliseconds p) : bus(std::move(dev), a), period(p) {} using clock = std::chrono::steady_clock; I2cBus bus; std::chrono::milliseconds period; std::thread worker; std::atomic running{false}; std::mutex mutex; EnvSample latest; clock::time_point last_ok{}; void readOnce() { if (!bus.write({kSht41CmdMeasureHighPrec})) return; std::this_thread::sleep_for(kConversionDelay); std::vector rx(kSht41ResponseBytes); if (!bus.writeRead({}, rx)) return; auto reading = decodeSht41Measurement(rx); if (!reading) { LOG_WARN << "SHT41: CRC mismatch, discarding sample"; return; } std::lock_guard lock(mutex); latest.valid = true; latest.temp_c = reading->temp_c; latest.humidity_pct = reading->humidity_pct; latest.timestamp_ms = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) .count(); last_ok = clock::now(); } void run() { while (running.load()) { readOnce(); std::this_thread::sleep_for(period); } } }; Sht41EnvSensor::Sht41EnvSensor(std::string i2c_device, uint8_t addr, std::chrono::milliseconds period) : impl_(std::make_unique(std::move(i2c_device), addr, period)) {} Sht41EnvSensor::~Sht41EnvSensor() { stop(); } void Sht41EnvSensor::start() { if (!impl_->bus.open()) { LOG_ERROR << "SHT41: failed to open I2C bus; sensor disabled"; return; } impl_->bus.write({kSht41CmdSoftReset}); std::this_thread::sleep_for(kSoftResetSettle); impl_->running = true; impl_->worker = std::thread([this] { impl_->run(); }); LOG_INFO << "SHT41 env sensor started"; } void Sht41EnvSensor::stop() { if (!impl_) return; impl_->running = false; if (impl_->worker.joinable()) impl_->worker.join(); impl_->bus.close(); } bool Sht41EnvSensor::connected() const { std::lock_guard lock(impl_->mutex); return impl_->latest.valid && (Impl::clock::now() - impl_->last_ok) < 2 * impl_->period; } std::optional Sht41EnvSensor::sample() { std::lock_guard lock(impl_->mutex); if (!impl_->latest.valid || (Impl::clock::now() - impl_->last_ok) >= 2 * impl_->period) return std::nullopt; return impl_->latest; } } // namespace fgc