# Temperature/Humidity (SHT30) Sensor Module — Raspberry Pi 4 Model B only (Module 2) A module that reads a **Sensirion SHT30** (Hanjin Data P4422-3, I2C) temperature/humidity sensor over the Raspberry Pi 4 Model B I2C bus and periodically reports to the cafe24 server (`php/api/sensor_data.php`). The server stores the measurements and performs **high/low temperature and high/low humidity threshold evaluation, SMS, and dashboard display**. > This folder is a **port of the STM32F407 firmware (`sht30_fw`) behavior to Raspberry Pi 4 + Python**, > the "Module 2". The wire format (body/signature), CRC, and conversion formulas are **byte-for-byte identical** to the firmware. > "Module 1" (leak + camera) is in the `../raspberry_pi4/` folder. For the full setup, see > [`SYSTEM_OVERVIEW.md`](../SYSTEM_OVERVIEW.md) in the root. --- ## 1. Files | File | Role | |---|---| | `sht30_sensor.py` | Main — periodic measurement → body build/sign → POST (with retry), startup→periodic | | `sht30_driver.py` | SHT30 I2C measurement + CRC-8 verification + physical-value conversion (smbus2, simulation fallback) | | `wire.py` | Body JSON + raw-body HMAC signature (byte-identical to firmware `jsonbody.c`/`sig.c`) | | `config.py` | Non-secret default settings (sensor id/interval/range/I2C). Loads secrets from `config_local` | | `config_local.example.py` | Operational/secret-value template → copy to `config_local.py` for use (not committed) | | `requirements.txt` | Python dependencies (`requests`, `smbus2`, pinned) | | `install.sh` | Pi 4 install (air-gapped offline + online fallback, includes enabling I2C) | | `vendor.sh` | Pre-download dependencies for air-gapped use (run on the build machine) | | `test_connection.py` | Server connectivity/signed-POST check | | `test_sensor.py` | CRC/format self-check + one measurement (format check passes even without hardware) | | `systemd/sht30-sensor.service` | Service unit for auto-start on boot | --- ## 2. Hardware (Pi 4 Model B baseline) - **Raspberry Pi 4 Model B** (BCM2711) + Raspberry Pi OS **Bookworm 64-bit** - **SHT30** temperature/humidity sensor module (I2C, Hanjin Data P4422-3) - I2C pull-up resistors **4.7kΩ × 2** (omit if built into the module) - Power: **USB-C 5V/3A**, network: Pi 4 **Gigabit Ethernet** (wired) or WiFi ### 2.1 Pinout (I2C bus 1) | Signal | Pi 4 pin | Note | |---|---|---| | SHT30 VCC | 3.3V (physical 1) | **No 5V** | | SHT30 GND | GND (physical 6/9/14/…) | | | SHT30 SDA | GPIO2 / SDA1 (physical 3) | + 4.7kΩ pull-up → 3.3V | | SHT30 SCL | GPIO3 / SCL1 (physical 5) | + 4.7kΩ pull-up → 3.3V | | SHT30 ADDR | GND → address **0x44** | 0x45 if tied to VDD (`config.py` `I2C_ADDR`) | --- ## 3. Server compatibility (must verify) This module reports to **the current repo's `php/` server (= the cafe24 SHT30 dashboard server)**. The server requires a **raw-body HMAC signature**. ``` X-Signature = lowercase_hex( HMAC-SHA256( key=API_KEY, msg=request body bytes ) ) ``` - `API_KEY` in `config_local.py` must be **exactly identical** to `API_KEY` in the server's `config.local.php`. - The server blocks replays via `timestamp` freshness (±600 seconds), so the Pi clock must be **NTP-synchronized** (enabled by default on Raspberry Pi OS). - It reports with `sensor_id=2`, and the server dashboard (`dashboard.php`) shows sensor 2 as the default chart. - Threshold alerts (high/low temperature, high/low humidity), SMS, and recovery evaluation are **all performed by the server**. This module only reports measurements. > For the server-side schema/deployment, see [`../sql/schema_sht30.sql`](../sql/schema_sht30.sql), > [`../docs/INSTALL_PI_SERVER.md`](../docs/INSTALL_PI_SERVER.md), > [`../docs/SHT30_SENSOR2_SETUP.md`](../docs/SHT30_SENSOR2_SETUP.md). --- ## 4. Installation ### 4.1 Prepare configuration values ```bash cp config_local.example.py config_local.py # Open config_local.py and fill in API_URL / API_KEY / DEVICE_ID / DEVICE_LOCATION ``` If `config_local.py` is missing, execution **stops immediately with a clear error** (fail closed). **Never commit** `config_local.py` or `API_KEY` (handled by `.gitignore`). ### 4.2 Online install (internet-capable environment) ```bash chmod +x install.sh ./install.sh sudo reboot # reboot once if I2C was newly enabled ``` ### 4.3 Air-gapped offline install On an internet-connected machine of the **same architecture (aarch64, ideally the same Pi 4 + Bookworm 64-bit)**, download the dependencies first to build a bundle, then move it to the target Pi 4 and install. ```bash # (build machine, internet YES) generate wheelhouse/ + debs/ ./vendor.sh # after copying wheelhouse/ and debs/ into this folder on the target Pi 4: # (target Pi 4, internet NO) ./install.sh # if debs/ · wheelhouse/ exist, it installs offline automatically ``` > `smbus2` is pure Python (arch-independent), so it can be downloaded on an x86 build machine. > Other wheels/debs such as `requests` must be the **aarch64** builds (running `vendor.sh` on the target arch is recommended). --- ## 5. Run / check ```bash # is the sensor visible (0x44) i2cdetect -y 1 # format/CRC self-check + one measurement (simulation if no hardware) source venv/bin/activate python3 test_sensor.py # server connectivity/signature check python3 test_connection.py # actual run python3 sht30_sensor.py ``` In environments without `smbus2` installed or with I2C disabled (dev PC, etc.), it automatically runs in **simulation mode**. --- ## 6. Auto-start on boot (systemd) ```bash # assumes the folder is placed at /home/pi/raspberry_pi4_sht30 (otherwise edit the path in the .service) sudo cp systemd/sht30-sensor.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable sht30-sensor sudo systemctl start sht30-sensor sudo systemctl status sht30-sensor journalctl -u sht30-sensor -f ``` `SupplementaryGroups=i2c` is required for `/dev/i2c-1` access. --- ## 7. Security notes - Secrets (`API_KEY`), server URL, and device info live only in `config_local.py` (not committed). Committed files contain only placeholders. - The report body is protected against tampering by an `HMAC-SHA256(API_KEY, raw_body)` signature (`X-Signature` header) (immune to length-extension attacks). The legacy body-field sha256 fallback is disabled by default on the server. - The signed bytes and the transmitted bytes are **always identical** (`wire.py` builds the body directly, signs those bytes, then raw POSTs). This matches firmware `sig.c` / `reference.py` / server `verify_signature_raw` byte-for-byte. - If `config_local.py` is exposed, immediately rotate `API_KEY` (change it on the server at the same time).