docs(gateway): add README with architecture and MQTT interface
Assisted-by: Claude:claude-sonnet-4-6 — ASCII architecture diagram
This commit is contained in:
@@ -1,118 +1,91 @@
|
||||
# Gateway — BLE to MQTT
|
||||
|
||||
This component runs on a Raspberry Pi 4 and acts as the communication bridge
|
||||
between the Nordic Thingy:52 sensor nodes and the rest of the system.
|
||||
It reads environmental data from the nodes over BLE and publishes it to a
|
||||
local MQTT broker (Mosquitto) in a structured JSON format.
|
||||
|
||||
## Role in the architecture
|
||||
```
|
||||
Thingy:52 nodes --> (BLE) --> Raspberry Pi --> (MQTT) --> Database / ML / Notifications
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
Install the required Python libraries:
|
||||
```bash
|
||||
pip3 install bleak paho-mqtt --break-system-packages
|
||||
```
|
||||
|
||||
Install and start the Mosquitto MQTT broker:
|
||||
```bash
|
||||
sudo apt install -y mosquitto mosquitto-clients
|
||||
sudo systemctl enable mosquitto
|
||||
sudo systemctl start mosquitto
|
||||
```
|
||||
|
||||
## Usage
|
||||
```bash
|
||||
python3 gateway.py
|
||||
```
|
||||
|
||||
At startup, the script asks for the room ID (e.g. C1, A2, B5).
|
||||
It then automatically discovers all Thingy:52 nodes in range by filtering
|
||||
BLE advertising packets on the Nordic Configuration service UUID (ef680100).
|
||||
Each detected node is assigned a name based on the room ID and a counter
|
||||
(e.g. C1_thingy1, C1_thingy2).
|
||||
|
||||
To run the gateway in the background and keep it running after closing SSH:
|
||||
```bash
|
||||
nohup python3 gateway.py > log.txt 2>&1 &
|
||||
```
|
||||
|
||||
## MQTT topic structure
|
||||
```
|
||||
classroom/{room_id}/{node_id}
|
||||
```
|
||||
|
||||
Example: `classroom/C1/C1_thingy1`
|
||||
|
||||
## Message format
|
||||
|
||||
Each message is published as a JSON object with the following structure:
|
||||
```json
|
||||
{
|
||||
"timestamp": "2026-03-26T13:24:05.176072+00:00",
|
||||
"room_id": "C1",
|
||||
"node_id": "C1_thingy1",
|
||||
"sensors": {
|
||||
"co2_ppm": 400,
|
||||
"temperature_c": 25.37,
|
||||
"humidity_pct": 44
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|---|---|---|
|
||||
| timestamp | string (ISO 8601 UTC) | Time of measurement, added by the gateway |
|
||||
| room_id | string | Identifier of the monitored room |
|
||||
| node_id | string | Identifier of the Thingy:52 node |
|
||||
| co2_ppm | integer | eCO2 concentration in parts per million |
|
||||
| temperature_c | float | Indoor air temperature in degrees Celsius |
|
||||
| humidity_pct | integer | Relative humidity in percent |
|
||||
|
||||
## Output files
|
||||
|
||||
For each session, two local files are created in the gateway directory:
|
||||
|
||||
- `data_{room_id}.csv` — comma-separated file for local analysis
|
||||
- `data_{room_id}.json` — JSON file following the database team format
|
||||
|
||||
## Publishing interval
|
||||
|
||||
The default publishing interval is 5 minutes (300 seconds).
|
||||
This can be adjusted by modifying the `INTERVAL` variable in `gateway.py`.
|
||||
|
||||
## Utility scripts
|
||||
|
||||
- `scan.py` — scans for nearby BLE devices and prints their name and MAC address
|
||||
- `check_uuid.py` — connects to Thingy:52 nodes and prints their advertised service UUIDs
|
||||
|
||||
## Notes on the CO2 sensor
|
||||
|
||||
The Thingy:52 uses a CCS811 sensor which measures eCO2 (equivalent CO2),
|
||||
estimated from volatile organic compound (VOC) levels rather than directly
|
||||
measuring CO2 concentration. Values should be interpreted as indicative trends
|
||||
rather than precise measurements, particularly during the first 24 to 48 hours
|
||||
of operation while the sensor calibrates.
|
||||
|
||||
## Overnight test results
|
||||
|
||||
An overnight test was conducted with 2 Thingy:52 nodes placed in two separate
|
||||
rooms (windows closed, 7h session, 5 minute interval).
|
||||
|
||||
- Room 1: 4 occupants (2 adults, 2 children)
|
||||
- Room 2: unoccupied
|
||||
|
||||

|
||||
|
||||
CO2 rose progressively from 400 ppm to a peak of 1071 ppm in the occupied room,
|
||||
consistent with human respiration in a confined space. The unoccupied room
|
||||
remained stable between 400 and 465 ppm, confirming that variations in Room 1
|
||||
are directly linked to human presence.
|
||||
|
||||
Note: the CCS811 sensor measures eCO2 estimated from VOC levels, not direct CO2.
|
||||
Occasional spikes (e.g. 877 ppm at 02:45, 1071 ppm at 03:05) may be caused by
|
||||
factors such as perspiration or movement near the sensor and should be interpreted
|
||||
with caution.
|
||||
# Gateway
|
||||
|
||||
BLE-to-MQTT gateway running on a Raspberry Pi 4. Discovers Nordic Thingy:52
|
||||
sensor nodes, reads environmental data over BLE and publishes it to a local
|
||||
MQTT broker on each notification received.
|
||||
|
||||
## Architecture
|
||||
Thingy:52 nodes Raspberry Pi MQTT broker
|
||||
[Thingy #1] --+
|
||||
|
|
||||
[Thingy #2] --+--> (BLE) --> [gateway.py] --> (MQTT) --> [Mosquitto]
|
||||
|
|
||||
[Thingy #n] --+
|
||||
|
||||
The gateway discovers nodes automatically by filtering BLE advertising packets
|
||||
on the Nordic Configuration service UUID (`ef680100`). Once connected, it
|
||||
subscribes to GATT notifications for temperature, humidity and CO2. Each
|
||||
received value triggers an immediate MQTT publication.
|
||||
|
||||
## MQTT interface
|
||||
|
||||
**Topic:** `{gateway_id}/{thingy_mac}/update`
|
||||
|
||||
Example: `gateway_lausanne_01/C4:64:02:60:D9:16/update`
|
||||
|
||||
**Payload:**
|
||||
```json
|
||||
{
|
||||
"timestamp": "2026-04-08T07:53:28Z",
|
||||
"temp": 25.37,
|
||||
"humidity": 44,
|
||||
"co2_ppm": 400
|
||||
}
|
||||
```
|
||||
|
||||
Fields `temp`, `humidity` and `co2_ppm` are included only once the
|
||||
corresponding value has been received from the node. The CO2 sensor
|
||||
requires a warm-up period of approximately 60 seconds before returning
|
||||
valid readings.
|
||||
|
||||
## Configuration
|
||||
|
||||
All environment-specific parameters are defined in `config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"gateway_id": "gateway_lausanne_01",
|
||||
"mqtt": {
|
||||
"broker": "localhost",
|
||||
"port": 1883
|
||||
},
|
||||
"ble": {
|
||||
"service_uuid": "ef680100-9b35-4933-9b10-52ffa9740042",
|
||||
"characteristics": {
|
||||
"temperature": "ef680201-9b35-4933-9b10-52ffa9740042",
|
||||
"co2": "ef680204-9b35-4933-9b10-52ffa9740042",
|
||||
"humidity": "ef680203-9b35-4933-9b10-52ffa9740042"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Each deployed gateway has its own `config.json`. The source code remains
|
||||
identical across all deployments.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
sudo apt install -y mosquitto mosquitto-clients
|
||||
sudo systemctl enable mosquitto
|
||||
sudo systemctl start mosquitto
|
||||
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
source venv/bin/activate
|
||||
python gateway.py
|
||||
```
|
||||
|
||||
## Notes on the CO2 sensor
|
||||
|
||||
The Thingy:52 embeds a CCS811 sensor which measures eCO2 — an estimated
|
||||
CO2 value derived from volatile organic compound (VOC) levels rather than
|
||||
a direct CO2 measurement. Values should be interpreted as indicative trends.
|
||||
The sensor requires a burn-in period of 48 hours on first use, and a warm-up
|
||||
of approximately 20 minutes on each startup before readings stabilize.
|
||||
Reference in New Issue
Block a user