diff --git a/CMakeLists.txt b/CMakeLists.txt index 1384b1d..e813ad9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,6 +81,8 @@ set(FGC_SOURCES src/camera/ImagePipeline.cpp src/serial/SerialMotorController.cpp src/serial/MtiImuSource.cpp + src/sensors/I2cBus.cpp + src/sensors/Sht41EnvSensor.cpp ) if(WITH_MQTT) list(APPEND FGC_SOURCES src/mqtt/MqttControlChannel.cpp) diff --git a/config/config.example.ini b/config/config.example.ini index b38dad8..4b115ed 100644 --- a/config/config.example.ini +++ b/config/config.example.ini @@ -72,6 +72,15 @@ baud = 115200 device = baud = 115200 +[Env] +; Ambient temperature/humidity sensor (Adafruit SHT41), wired to the +; LattePanda's OWN native I2C bus - NOT the Arduino/motor serial link. Bus +; number varies by board; confirm with `i2cdetect -y N` (SHT41 shows up at +; 0x44). Enable with [Features] enable_env = true. +i2c_device = /dev/i2c-1 +i2c_addr = 68 +period_ms = 2000 + [Motor] ; Degrees<->encoder-counts calibration for each axis. The firmware speaks only ; in absolute encoder counts; these map them to the heading/elevation degrees @@ -137,6 +146,8 @@ enable_imu = false mock_camera = false mock_serial = false mock_imu = false +enable_env = false +mock_env = false [Test] ; Hardware self-test command (`test`). `profile` is the default profile used when diff --git a/docs/configuration.md b/docs/configuration.md index 08311d3..7b092ed 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -55,8 +55,13 @@ Parsed and validated by `ConfigLoader` ([src/core/Config.cpp](../src/core/Config | `Features` | `mock_camera` | bool | `false` | Use the simulated camera | | `Features` | `mock_serial` | bool | `false` | Use the simulated motor controller | | `Features` | `mock_imu` | bool | `false` | Use the simulated IMU instead of the MTi | +| `Features` | `enable_env` | bool | `false` | Use the ambient temp/humidity sensor (SHT41) | +| `Features` | `mock_env` | bool | `false` | Use the simulated env sensor instead of the SHT41 | | `IMU` | `device` | string | — | MTi serial device (see `[IMU]` note); required when `enable_imu` | | `IMU` | `baud` | int | `115200` | MTi serial baud rate | +| `Env` | `i2c_device` | string | `/dev/i2c-1` | SHT41 I2C bus device (see `[Env]` note); required when `enable_env` | +| `Env` | `i2c_addr` | int | `68` (`0x44`) | SHT41 I2C slave address | +| `Env` | `period_ms` | int | `2000` | Sample interval | | `Logging` | `level` | enum | `info` | Linear log level (`--log-level` overrides) | | `Logging` | `trace` | csv | — | Wire-trace categories, off by default (`--trace` overrides) | | `UI` | `enable_tui` | bool | `false` | Full-screen terminal dashboard (`--tui`/`--no-tui` override; needs `WITH_TUI=ON`) | @@ -140,6 +145,18 @@ The config is read **once at startup** and cached, so if you change the XKF prof stays stale until you **refresh** it: press `r` (or type `refresh`). That re-queries the device (briefly pausing the stream) and also requests a fresh firmware dump for the gimbal `g` view. +### `[Env]` — SHT41 ambient temperature/humidity sensor + +Enable with `[Features] enable_env = true` and point `[Env] i2c_device` at the LattePanda's **own +native I2C bus** — this is a separate physical link from the motor Leonardo's serial connection, not +the Arduino's D2 pin (the original DHT11 plan). Bus number varies by board; confirm with +`i2cdetect -y N` (the SHT41 answers at `0x44`/`68`). The backend (`Sht41EnvSensor`) polls at +`period_ms`, discarding any sample that fails the SHT4x CRC-8 check. Set `[Features] mock_env = true` +to use a synthetic reading on dev machines (no hardware) — this is also the checked-in +`config.example.ini` default. The sensor sits behind the generic `IEnvSensor` interface +([include/fgc/IEnvSensor.h](../include/fgc/IEnvSensor.h)), so swapping in a different ambient sensor +later means adding a new backend, not touching `Application`, the UI, or MQTT. + ### Secrets `mqtt_user` / `mqtt_pw` are read from the environment variables **`FGC_MQTT_USER` / `FGC_MQTT_PW`** first, @@ -220,8 +237,9 @@ operation is unchanged and remains the default — the same binary runs under sy logs on stdout. Dashboard panels: **Gimbal** (per-axis state, heading, encoder counts, flag badges, target), -**Sensors** (DHT11 still *pending*; the **Xsens MTi** shows live roll/pitch/yaw + temp once -`enable_imu`), **Camera** (count, capture state, rate, last capture), **Connectivity** (MQTT state, +**Sensors** (the **Xsens MTi** shows live roll/pitch/yaw + temp once `enable_imu`; the ambient +**SHT41** shows live temp/humidity once `enable_env` — see [known-issues.md](known-issues.md) for +its hardware-bring-up status), **Camera** (count, capture state, rate, last capture), **Connectivity** (MQTT state, broker, tower, control mode, target heading). Adding a panel later is a struct in `UiSnapshot.h` plus one node in [src/ui/TuiUi.cpp](../src/ui/TuiUi.cpp). diff --git a/docs/known-issues.md b/docs/known-issues.md index 8b56bb5..602619a 100644 --- a/docs/known-issues.md +++ b/docs/known-issues.md @@ -94,4 +94,8 @@ persistence for LPM + usbfs, and verify a full sweep captures white-balanced fra yet (not homed / no `gimbal dump`), nudge requests one and does nothing that press — it never falls back to the configured degree clamps (which, if `*_min_deg`/`*_max_deg` were unset, produced absurd ±100000° steps). -- DHT11 temperature/humidity is still a Sensors-panel placeholder (the IMU half is integrated). +- Ambient temperature/humidity (SHT41, `IEnvSensor`/`Sht41EnvSensor`/`MockEnvSensor`) is + integrated in software — config-gated (`[Features] enable_env`/`mock_env`, `[Env]`), Sensors + panel + MQTT `Env` topic wired — but defaults to `mock_env = true` in the checked-in example + config. Bus/address bring-up and validation against the physical sensor on the LattePanda's I2C + bus is still pending; flip `mock_env = false` on the deployed `config.ini` once confirmed. diff --git a/docs/mqtt-api.md b/docs/mqtt-api.md index 0b89a2e..5818d87 100644 --- a/docs/mqtt-api.md +++ b/docs/mqtt-api.md @@ -52,6 +52,7 @@ When a ControlCode message arrives, the program echoes the current code back on |-------|------|---------|--------------| | `GGS/FWT//StatusCode` | At startup (`"0"`); whenever a ControlCode message is received (echoes the code) | integer as string | 1 / retained | | `GGS/FWT//CamEvent` | After each image is saved | JSON object (below) | 1 / retained | +| `GGS/FWT//Env` | Once per fresh ambient sensor sample (see `[Env] period_ms`) | JSON object (below) | 1 / retained | ### CamEvent payload @@ -72,6 +73,22 @@ Built by `MqttControlChannel::publishCamEvent` from a `CamEvent`: > The `time` value is the same Unix-ms timestamp used as the image filename, so a consumer can locate the file > for a given event: `/