sequenceDiagram
participant RTC as DS3231 RTC
participant ESP as ESP32
participant SD as SD Card
RTC->>ESP: Alarm interrupt (every N minutes)
ESP->>ESP: Boot & evaluate FSM state
ESP->>RTC: Read timestamp
ESP->>ESP: Read all sensors
ESP->>SD: Append CSV row
ESP->>RTC: Set next alarm (now + interval)
ESP->>ESP: Enter deep sleep
System Architecture
Status: 🟢 Stable
Two clearly separated subsystems
The system is split into two independent parts that communicate asynchronously:
- The weather station — a low-power field device. Measures, stores, sleeps.
- The backend — a home server that ingests, stores, and visualizes data, and runs predictive models.
These two parts are never directly connected. The phone acts as a physical data mule — an asynchronous gateway.
Data flow overview
flowchart TB
subgraph Station["🌦️ Weather Station (ESP32)"]
S1[Sensors] --> MCU[ESP32]
MCU --> SD[SD Card]
MCU --> AP[Wi-Fi AP<br/>SERVER mode]
end
subgraph Phone["📱 Phone Gateway"]
DL[Download logs<br/>via browser / script]
UL[Upload to server<br/>over home Wi-Fi]
DL --> UL
end
subgraph Backend["🖥️ SBC Server"]
API[REST API<br/>FastAPI]
DB[Database<br/>TinyDB / InfluxDB]
DASH[Dashboard<br/>TBD]
ML[ML Models<br/>RF + LSTM]
API --> DB --> DASH
DB --> ML
end
AP -->|"User walks up with phone"| DL
UL -->|"Home Wi-Fi"| API
There is no always-on internet connection at the station. The station operates fully autonomously. Data is collected on the SD card and pulled manually (or on a schedule) by the phone.
Station — operating principle
The station follows a wake-do-sleep cycle. The ESP32 spends the vast majority of its life in deep sleep, consuming microamps.
Key principle: no code runs during deep sleep. Every wake-up is a fresh boot.
Station — finite state machine (FSM)
At every boot, the firmware evaluates the system state and executes exactly one role:
stateDiagram-v2
[*] --> BOOT
BOOT --> CHECK_WAKEUP : esp_sleep_get_wakeup_cause()
CHECK_WAKEUP --> INIT_RTC : Power-on (UNDEFINED) AND RTC invalid
CHECK_WAKEUP --> READ_SWITCH : Power-on (UNDEFINED) AND RTC valid
CHECK_WAKEUP --> DATA_LOGGER : EXT0 — RTC alarm interrupt
CHECK_WAKEUP --> SERVER : EXT1 — switch set to SERVER
READ_SWITCH --> SERVER : Mode switch = SERVER
READ_SWITCH --> DATA_LOGGER : Mode switch = MEASURE
SERVER --> INIT_RTC : Long button press (NTP sync)
INIT_RTC --> SERVER : NTP sync successful
DATA_LOGGER --> DEEP_SLEEP : Measurements written
SERVER --> DEEP_SLEEP : Mode switch → MEASURE
DEEP_SLEEP --> [*]
At every boot, esp_sleep_get_wakeup_cause() is called first. Three wakeup causes are handled:
UNDEFINED(power-on): RTC validity is checked — if never set, go toINIT_RTC; if already valid, sample the mode switch (READ_SWITCH).EXT0(RTC alarm interrupt): go directly toDATA_LOGGER.EXT1(mode switch GPIO): fires only when the switch is moved to the SERVER position — device was already initialised, go directly toSERVER. The return to MEASURE is handled in software whileSERVERis active (no wakeup needed).
This requires the mode switch to be wired to an EXT1-capable GPIO and registered as a wakeup source, which is what disambiguates a switch-flip from a fresh power-on. States are mutually exclusive. The FSM is deterministic and fully observable via the status LED — see the Firmware — Status LED section for the full LED behaviour table.
Phone gateway — operating principle
The phone acts as a store-and-forward relay:
- User walks to the station with their phone
- Phone connects to the station’s Wi-Fi AP (SERVER mode)
- User downloads the latest CSV log file(s) via the browser
- When back on home Wi-Fi, the user opens the server web UI and uploads the file directly from a browser form
This approach requires zero infrastructure at the station location and no app or script on the phone — only a browser. The station can be in a garden, a field, or a rooftop — anywhere.
Backend — server role
The SBC is the home server. It is always on (or scheduled) and provides:
- Data ingestion — REST endpoint that accepts uploaded CSV batches
- Storage — time-series database (TinyDB prototype → InfluxDB production)
- Visualization — web dashboard with historical data and derived metrics (Grafana, Plotly Dash, or similar — TBD)
- Inference — runs trained ML models to produce watering recommendations and short-term weather forecasts
The backend is fully independent of the station. It can operate, process, and display data even if the station has been offline for days.
Key design invariants
These do not change across phases:
| Invariant | Value |
|---|---|
| Time authority | DS3231 RTC |
| Primary storage | SD card (append-only CSV) |
| Station-server link | Asynchronous (phone relay) |
| Data format | YYYY-MM-DD HH:MM:SS,fields... |
| Firmware model | Wake-do-sleep FSM |
| Internet at station | Never required |
Future consideration: direct upload from the station (Wi-Fi or cellular modem) could bypass the phone relay entirely, eliminating the manual data-mule step. Worth keeping in mind once the project reaches sufficient maturity.