01
Pi #1 — data hub install
Install sequence
| 1. OS | Raspberry Pi OS Lite 64-bit (bookworm) · headless |
| 2. Mosquitto | apt install mosquitto mosquitto-clients · enable + start |
| 3. InfluxDB 2 | influxdata.com ARM64 repo · systemctl enable influxdb |
| 4. Grafana | influxdata Grafana OSS repo · connect InfluxDB datasource |
| 5. Reticulum | pip3 install rns · rnsd as systemd service |
| 6. rtl_433 | apt install rtl-433 · configure -F mqtt output |
| 7. Direwolf | apt install direwolf · 144.390 MHz decode |
| 8. dump1090 | apt install dump1090-fa · optional ADS-B |
| 9. Node-RED | Official install script · npm global |
| 10. Meshtastic GW | pip3 install meshtastic · USB bridge to Mosquitto |
Validation checks
| Test 1 | Dummy Python MQTT publisher → confirm data lands in InfluxDB panel |
| Test 2 | rtl_433 running → publish to radio/433 topic → confirm in MQTT explorer |
| Test 3 | Meshtastic gateway node powered → Python bridge running → sensor message appears in nodes/A/env |
| Test 4 | InfluxDB query: from(bucket:"mesh") |> range(start:-1h) returns data |
| Test 5 | Grafana dashboard renders time-series from InfluxDB — confirm axes, tags, units |
| Do not proceed | Deploy nodes outdoors only after all 5 pass |
02
MQTT topic schema
| Topic | Publisher | Payload fields |
|---|---|---|
nodes/A/env | Meshtastic GW bridge | temp_c · humidity_pct · pressure_hpa · lux · uv_a · uv_b |
nodes/B/env | Meshtastic GW bridge | temp_c · humidity_pct · pressure_hpa · lux · uv_a · uv_b · co2_ppm |
nodes/A/soil | Node A script | vwc_pct · soil_temp_c |
nodes/B/soil | Node B script | vwc_pct · soil_temp_c |
nodes/A/wx | Node A script | wind_speed_ms · wind_dir_deg · rain_mm_tip · rain_mm_total |
radio/433 | rtl_433 | Auto-decoded JSON per protocol (model · id · fields) |
radio/aprs | Direwolf | APRS packet JSON · call · lat · lon · wx fields |
radio/noaa | rtl_fm decoder | WX bulletin text |
external/openmeteo | Python API ingest | Regional hourly actuals + 48 hr forecast |
external/noaa_ncei | Python API ingest | 30-year climate normals for location |
drone/telem | DJI GCS → Wi-Fi bridge | lat · lon · alt_m · battery_pct · speed_ms · mode |
03
InfluxDB 2 measurement schema
| Measurement | Tags | Fields |
|---|---|---|
| environment | node · location · source | temp_c · humidity_pct · pressure_hpa · lux · uv_a · uv_b · co2_ppm |
| soil | node · depth_cm | vwc_pct · temp_c |
| weather_local | node=A | wind_speed_ms · wind_dir_deg · rain_mm |
| regional | source · station_id | temp_c · humidity_pct · pressure_hpa · precip_mm · wind_ms |
| drone | flight_id · session | lat · lon · alt_m · battery_pct · speed_ms |
| radio_decode | source · model · device_id | All auto-decoded rtl_433 fields |
04
Pi #2 — spatial processing install
| 1. Docker | Official Docker Engine for Raspberry Pi OS |
| 2. WebODM | git clone https://github.com/OpenDroneMap/WebODM && ./webodm.sh start |
| 3. Potree Converter | Build from source or prebuilt ARM binary — converts LAZ/LAS to Potree format |
| 4. Potree viewer | Static files served via nginx or Node.js |
| 5. QGIS | apt install qgis |
| 6. Python venv | openmeteo-requests · requests-cache · influxdb-client · shapely |
| 7. ODM note | 200 photo survey ≈ 4–12 hrs on Pi 4. If an x86 laptop is available, run ODM there and copy outputs to Pi #2 for serving. |