# Leak Detection Sensor Module — Raspberry Pi 4 Model B Only A monitoring module that reads the e-con **5C-08 resistive leak detection sensor** via the GPIO of a Raspberry Pi 4 Model B. On a leak it immediately reports to the PHP API server (+ triggers an SMS), and **captures an on-site photo with the Pi Camera and uploads it**. > This folder is a port of a module originally written for the **Raspberry Pi Zero 2 W**, > adapted to be **Pi 4 Model B only**. The GPIO (BCM) numbering scheme and the > `rpicam`/`libcamera` camera stack are identical on both boards, so the sensor/capture > logic is unchanged; the differences are only in the **wiring/power/Ethernet/camera > connector** and the install environment. --- ## 1. Files | File | Role | |---|---| | `leak_sensor.py` | Main — GPIO interrupt leak detection, periodic reporting, capture+upload on leak | | `config.py` | Non-secret default settings (sensor/threshold/interval/camera). Loads secrets from `config_local` | | `config_local.example.py` | Operational/secret value template → copy to `config_local.py` before use (not committed) | | `requirements.txt` | Python dependencies (pinned) | | `install.sh` | Pi 4 install script (air-gapped offline + online fallback) | | `vendor.sh` | Pre-download dependencies for air-gapped use (run on build machine) | | `test_connection.py` | Server connection/signature check | | `systemd/leak-sensor.service` | Auto-start-on-boot service unit | --- ## 2. Hardware (based on Pi 4 Model B) - **Raspberry Pi 4 Model B** (BCM2711) + Raspberry Pi OS **Bookworm 64-bit** - e-con **5C-08** leak detection sensor (resistive, high-impedance ~1.5MΩ) - **External 10MΩ pull-down resistor** (required — internal pull resistor is off) - (Optional) **Pi Camera** — Pi 4 = **15-pin (1.0mm pitch) standard CSI**, Zero 2 W = **22-pin (0.5mm pitch) CSI** → Ribbon/camera boards are not compatible (a Pi 4 15-pin ribbon or a 15↔22 adapter is required) - Power: **USB-C 5V/3A** (a power margin is recommended when using the camera) - Network: Pi 4 **Gigabit Ethernet** (wired) or WiFi ### 2.1 Pinout (default single sensor) | Signal | BCM | 40-pin header physical pin | |---|---|---| | Sensor input | GPIO17 | 11 | | 3.3V | — | 1 | | GND | — | 6 (or 9/14/20/25/30/34/39) | > To add a sensor, add an entry by BCM number to `SENSORS` in `config.py`. > (e.g. GPIO27=physical pin 13, GPIO22=physical pin 15) ### 2.2 Electrical design (important) - `ACTIVE_LOW = False`: when dry GPIO=LOW(0), on leak GPIO=HIGH(1) - `USE_INTERNAL_PULL = False` (**keep PUD_OFF**): if you turn on the internal 50kΩ pull-down, the 1.5MΩ sensor cannot pull GPIO up to HIGH, causing **detection failure**. Use only the external 10MΩ pull-down. --- ## 3. Server compatibility (must verify) This module reports to a **server that receives** the `leak_alert` / `leak_recovery` events and `photo_upload.php`. > ✅ **The `php/` server in this repo is exactly that server (single-server integration complete).** > Both module 1 (leak + camera) and module 2 (SHT30 temperature/humidity) report to the > **same cafe24 `php/` server** with the raw-body signature > `X-Signature = lowercase_hex(HMAC-SHA256(key=API_KEY, msg=request body bytes))`, > and leak status, photos, and temperature/humidity are shown together on a single > dashboard (`dashboard.php`). > > - **New install:** run only `sql/schema_sht30.sql` — the leak elements > (`leak_sensor_log.is_leak` / `leak_sensor_status.is_leak` columns, `leak_incident`, `leak_photo`, > `leak_ingest_nonce` tables) are already included. > > Set `API_URL` / `PHOTO_UPLOAD_URL` in `config_local.py` to this integrated server, and make > `API_KEY` **byte-for-byte identical** to the `API_KEY` in the server's `config.local.php`. --- ## 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 / PHOTO_UPLOAD_URL / API_KEY / DEVICE_ID ``` If `config_local.py` is missing, execution **stops immediately with a clear error** (fail closed). **Never commit** `config_local.py` or `API_KEY` (they are handled by `.gitignore`). ### 4.2 Online install (environment with internet) ```bash chmod +x install.sh ./install.sh ``` ### 4.3 Air-gapped offline install On a machine with internet of the **same architecture (aarch64, ideally the same Pi 4 + Bookworm 64-bit)**, first fetch the dependencies to build a bundle, then move it to the target Pi 4 to install. ```bash # (build machine, internet YES) create 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, installs offline automatically ``` > If `debs/` exists, `install.sh` installs offline with `dpkg`; if `wheelhouse/` exists, it > installs offline with `pip --no-index`. It falls back to internet apt/pip only when neither > is present. wheel/deb are architecture-dependent, so ones fetched on an x86_64 PC will not > work on the Pi 4. --- ## 5. Running ```bash source venv/bin/activate python3 leak_sensor.py ``` In an environment without `RPi.GPIO` (a development PC, etc.), it automatically runs in **simulation mode**. ### Connection check ```bash source venv/bin/activate python3 test_connection.py ``` --- ## 6. Auto-start on boot (systemd) ```bash # Assumes the folder is placed at /home/pi/raspberry_pi4 (if different, edit the path in .service) sudo cp systemd/leak-sensor.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable leak-sensor sudo systemctl start leak-sensor sudo systemctl status leak-sensor journalctl -u leak-sensor -f ``` `SupplementaryGroups=video` is required for camera access. --- ## 7. Security notes - Secret values (`API_KEY`), the 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 with a **raw-body HMAC signature**: `X-Signature = lowercase_hex(HMAC-SHA256(key=API_KEY, msg=request body bytes))` (`leak_sensor.sign_raw` ↔ server `config.php::verify_signature_raw`, same scheme as module 2). - The canonical string for the photo upload signature is `device_id|sensor_id|log_id|timestamp` (no spaces) (see `config_local.example.py`). - The old `sha256(API_KEY + sorted JSON)` body-field signature is **deprecated**, and the server default rejects it (`ALLOW_LEGACY_BODY_SIGNATURE=false`, `php/config.php`). - If `config_local.py` is exposed, immediately rotate `API_KEY` (change it on the server at the same time).