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
|
# Gateway
|
||||||
|
|
||||||
This component runs on a Raspberry Pi 4 and acts as the communication bridge
|
BLE-to-MQTT gateway running on a Raspberry Pi 4. Discovers Nordic Thingy:52
|
||||||
between the Nordic Thingy:52 sensor nodes and the rest of the system.
|
sensor nodes, reads environmental data over BLE and publishes it to a local
|
||||||
It reads environmental data from the nodes over BLE and publishes it to a
|
MQTT broker on each notification received.
|
||||||
local MQTT broker (Mosquitto) in a structured JSON format.
|
|
||||||
|
## Architecture
|
||||||
## Role in the architecture
|
Thingy:52 nodes Raspberry Pi MQTT broker
|
||||||
```
|
[Thingy #1] --+
|
||||||
Thingy:52 nodes --> (BLE) --> Raspberry Pi --> (MQTT) --> Database / ML / Notifications
|
|
|
||||||
```
|
[Thingy #2] --+--> (BLE) --> [gateway.py] --> (MQTT) --> [Mosquitto]
|
||||||
|
|
|
||||||
## Dependencies
|
[Thingy #n] --+
|
||||||
|
|
||||||
Install the required Python libraries:
|
The gateway discovers nodes automatically by filtering BLE advertising packets
|
||||||
```bash
|
on the Nordic Configuration service UUID (`ef680100`). Once connected, it
|
||||||
pip3 install bleak paho-mqtt --break-system-packages
|
subscribes to GATT notifications for temperature, humidity and CO2. Each
|
||||||
```
|
received value triggers an immediate MQTT publication.
|
||||||
|
|
||||||
Install and start the Mosquitto MQTT broker:
|
## MQTT interface
|
||||||
```bash
|
|
||||||
sudo apt install -y mosquitto mosquitto-clients
|
**Topic:** `{gateway_id}/{thingy_mac}/update`
|
||||||
sudo systemctl enable mosquitto
|
|
||||||
sudo systemctl start mosquitto
|
Example: `gateway_lausanne_01/C4:64:02:60:D9:16/update`
|
||||||
```
|
|
||||||
|
**Payload:**
|
||||||
## Usage
|
```json
|
||||||
```bash
|
{
|
||||||
python3 gateway.py
|
"timestamp": "2026-04-08T07:53:28Z",
|
||||||
```
|
"temp": 25.37,
|
||||||
|
"humidity": 44,
|
||||||
At startup, the script asks for the room ID (e.g. C1, A2, B5).
|
"co2_ppm": 400
|
||||||
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).
|
Fields `temp`, `humidity` and `co2_ppm` are included only once the
|
||||||
|
corresponding value has been received from the node. The CO2 sensor
|
||||||
To run the gateway in the background and keep it running after closing SSH:
|
requires a warm-up period of approximately 60 seconds before returning
|
||||||
```bash
|
valid readings.
|
||||||
nohup python3 gateway.py > log.txt 2>&1 &
|
|
||||||
```
|
## Configuration
|
||||||
|
|
||||||
## MQTT topic structure
|
All environment-specific parameters are defined in `config.json`:
|
||||||
```
|
|
||||||
classroom/{room_id}/{node_id}
|
```json
|
||||||
```
|
{
|
||||||
|
"gateway_id": "gateway_lausanne_01",
|
||||||
Example: `classroom/C1/C1_thingy1`
|
"mqtt": {
|
||||||
|
"broker": "localhost",
|
||||||
## Message format
|
"port": 1883
|
||||||
|
},
|
||||||
Each message is published as a JSON object with the following structure:
|
"ble": {
|
||||||
```json
|
"service_uuid": "ef680100-9b35-4933-9b10-52ffa9740042",
|
||||||
{
|
"characteristics": {
|
||||||
"timestamp": "2026-03-26T13:24:05.176072+00:00",
|
"temperature": "ef680201-9b35-4933-9b10-52ffa9740042",
|
||||||
"room_id": "C1",
|
"co2": "ef680204-9b35-4933-9b10-52ffa9740042",
|
||||||
"node_id": "C1_thingy1",
|
"humidity": "ef680203-9b35-4933-9b10-52ffa9740042"
|
||||||
"sensors": {
|
}
|
||||||
"co2_ppm": 400,
|
}
|
||||||
"temperature_c": 25.37,
|
}
|
||||||
"humidity_pct": 44
|
```
|
||||||
}
|
|
||||||
}
|
Each deployed gateway has its own `config.json`. The source code remains
|
||||||
```
|
identical across all deployments.
|
||||||
|
|
||||||
| Field | Type | Description |
|
## Installation
|
||||||
|---|---|---|
|
|
||||||
| timestamp | string (ISO 8601 UTC) | Time of measurement, added by the gateway |
|
```bash
|
||||||
| room_id | string | Identifier of the monitored room |
|
sudo apt install -y mosquitto mosquitto-clients
|
||||||
| node_id | string | Identifier of the Thingy:52 node |
|
sudo systemctl enable mosquitto
|
||||||
| co2_ppm | integer | eCO2 concentration in parts per million |
|
sudo systemctl start mosquitto
|
||||||
| temperature_c | float | Indoor air temperature in degrees Celsius |
|
|
||||||
| humidity_pct | integer | Relative humidity in percent |
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
## Output files
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
For each session, two local files are created in the gateway directory:
|
|
||||||
|
## Usage
|
||||||
- `data_{room_id}.csv` — comma-separated file for local analysis
|
|
||||||
- `data_{room_id}.json` — JSON file following the database team format
|
```bash
|
||||||
|
source venv/bin/activate
|
||||||
## Publishing interval
|
python gateway.py
|
||||||
|
```
|
||||||
The default publishing interval is 5 minutes (300 seconds).
|
|
||||||
This can be adjusted by modifying the `INTERVAL` variable in `gateway.py`.
|
## Notes on the CO2 sensor
|
||||||
|
|
||||||
## Utility scripts
|
The Thingy:52 embeds a CCS811 sensor which measures eCO2 — an estimated
|
||||||
|
CO2 value derived from volatile organic compound (VOC) levels rather than
|
||||||
- `scan.py` — scans for nearby BLE devices and prints their name and MAC address
|
a direct CO2 measurement. Values should be interpreted as indicative trends.
|
||||||
- `check_uuid.py` — connects to Thingy:52 nodes and prints their advertised service UUIDs
|
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.
|
||||||
## 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.
|
|
||||||
Reference in New Issue
Block a user