#+TITLE: FAPG DAQ system #+AUTHOR: Marius PETER #+DATE: <2026-06-27 Sat> * Overview The goal of this project is to deploy a DAQ network at the /Ferme Aquaponique du Pays de Gex/ (FAPG). DAQ sensor nodes periodically send readings to a central DAQ hub. An external VPS obtains these readings, and presents them on a dashboard accessible via the web. * Architecture #+begin_src plantuml :file docs/architecture.png @startuml title FAPG DAQ - from reading to web dashboard actor "Farm user/browser" as Browser participant "EZO probe\npH / EC / DO / ORP" as Probe participant "Pi Zero 2W\nfapg-daq service" as Zero participant "Pi 5\nMosquitto MQTT broker" as Broker participant "VPS\nMQTT subscriber service" as VPSSubscriber database "Readings database\nSQLite / PostgreSQL" as DB participant "VPS\nLive dashboard app" as Dashboard == DAQ acquisition == Zero -> Probe : Request UART reading Probe --> Zero : Return raw sensor value Zero -> Zero : Normalize reading == MQTT publication == Zero -> Broker : MQTT PUBLISH\nfapg/daq///reading\nvalid reading JSON payload Zero -> Broker : MQTT PUBLISH\nfapg/daq///status\nnode/probe status JSON payload Broker -> Broker : MQTT PUBLISH\nfapg/daq/hub//status\nhub status JSON payload note right of Zero Example reading payload: { "schema": "fapg.daq.reading.v1", "node": "fapg-zero-ph-01", "probe": "ph", "value": 7.12, "unit": "pH", "timestamp": "2026-06-27T13:45:00Z" } Example status payload: { "schema": "fapg.daq.status.v1", "node": "fapg-zero-ph-01", "probe": "ph", "status": "probe_error", "timestamp": "2026-06-27T13:45:05Z", "error": "timeout waiting for EZO response" } end note VPSSubscriber -> Broker : MQTT SUBSCRIBE\nfapg/daq/# Broker --> VPSSubscriber : MQTT MESSAGE\nreading/status topic + JSON payload VPSSubscriber -> VPSSubscriber : Validate topic and payload\nparse probe, node, value/status, timestamp == Database storage == VPSSubscriber -> DB : INSERT reading VPSSubscriber -> DB : INSERT node status DB --> VPSSubscriber : OK == Live dashboard presentation == Browser -> Dashboard : Open HTTPS dashboard Dashboard -> DB : SELECT recent readings DB --> Dashboard : Return recent readings Dashboard --> Browser : Initial HTML + Chart.js data loop live updates VPSSubscriber -> Dashboard : Notify new reading\nWebSocket / SSE / app event Dashboard --> Browser : Push new reading\nWebSocket or Server-Sent Events Browser -> Browser : Append point to Chart.js chart end @enduml #+end_src #+RESULTS: [[file:docs/architecture.png]] ** Hosts *** Raspberry Pi Zero 2W These hosts collect DAQ readings, and publish them via MQTT on the LAN. | Pi Zero | Probe | |---------+-------| | 01 | PH | | 02 | EC | | 03 | DO | | 04 | ORP | *** Raspberry Pi 5 This host brokers MQTT messages sent on the LAN. *** VPS This host establishes a VPN connection with the Pi 5 host, subscribes to all MQTT topics, saves messages to a database, and exposes a realtime web dashboard available over the internet. ** MQTT Topics Valid probe measurements are published only on: #+begin_src text fapg/daq///reading #+end_src Node/probe health is published separately on: #+begin_src text fapg/daq///status #+end_src The DAQ hub publishes its own heartbeat status on: #+begin_src text fapg/daq/hub//status #+end_src Status payloads use schema =fapg.daq.status.v1=. A recent =ok= status means the node is reachable; for probe nodes it also means the node is publishing valid readings. A recent =probe_error= status means the node is reachable on the farm network, but the probe or USB serial path is not returning a valid reading. If the dashboard has no recent status for a node or the DAQ hub, it treats that system as unreachable.