fwt_software/src/sensors/Sht41EnvSensor.cpp

108 lines
3.2 KiB
C++

#include "fgc/sensors/Sht41EnvSensor.h"
#include "fgc/Logger.h"
#include "fgc/sensors/I2cBus.h"
#include "fgc/sensors/Sht41Protocol.h"
#include <atomic>
#include <chrono>
#include <mutex>
#include <thread>
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<bool> 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<uint8_t> rx(kSht41ResponseBytes);
if (!bus.writeRead({}, rx)) return;
auto reading = decodeSht41Measurement(rx);
if (!reading) {
LOG_WARN << "SHT41: CRC mismatch, discarding sample";
return;
}
std::lock_guard<std::mutex> 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::milliseconds>(
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<Impl>(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<std::mutex> lock(impl_->mutex);
return impl_->latest.valid &&
(Impl::clock::now() - impl_->last_ok) < 2 * impl_->period;
}
std::optional<EnvSample> Sht41EnvSensor::sample() {
std::lock_guard<std::mutex> lock(impl_->mutex);
if (!impl_->latest.valid || (Impl::clock::now() - impl_->last_ok) >= 2 * impl_->period)
return std::nullopt;
return impl_->latest;
}
} // namespace fgc