System Diagrams¶
Visual representations of the GreenThumb system architecture.
Hardware Setup¶
graph TB
subgraph "Raspberry Pi 5"
RPI[Raspberry Pi 5]
GPIO[GPIO/I2C]
USB[USB Port]
end
subgraph "Sensors"
AHT[AHT10<br/>Temp/Humidity]
BMP[BMP280<br/>Pressure/Temp]
TSL[TSL2561<br/>Light]
end
subgraph "Camera"
CAM[USB Camera]
end
subgraph "Actuators"
RELAY[8-channel relay bank]
LIGHT[Grow light]
FAN[Fan]
PUMP["Water / air pump"]
DOSE[Dosing pumps]
end
GPIO --> AHT
GPIO --> BMP
GPIO --> TSL
USB --> CAM
GPIO --> RELAY
RELAY --> LIGHT
RELAY --> FAN
RELAY --> PUMP
RELAY --> DOSE
Docker Services¶
graph LR
subgraph "Pi Docker Network"
DB[(PostgreSQL\n:5432)]
API[microcontroller-api\n:8080]
CTRL[controller]
DASH[local-dashboard\n:80]
WT[watchtower]
end
subgraph "External"
DH[Docker Hub]
BROWSER[User Browser]
end
CTRL -->|HTTP| API
DASH -->|HTTP proxy| API
API --> DB
BROWSER --> DASH
BROWSER --> API
DH --> WT
WT -->|auto-update| DASH
WT -.->|"notify only, manual promote"| API
WT -.->|"notify only, manual promote"| CTRL
Database Schema¶
The schema is described on the Database page; the source of truth is the database repository.
CI/CD Pipeline¶
graph LR
subgraph "Developer"
DEV[Push to prod]
end
subgraph "GitHub Actions"
BUILD[Build Docker Images]
PUSH[Push to Docker Hub]
end
subgraph "Docker Hub"
API_IMG["microcontroller-api:prod"]
CTRL_IMG["microcontroller-api-client:prod"]
DASH_IMG["local-dashboard:prod"]
end
subgraph "Raspberry Pi"
WT[Watchtower]
CONTAINERS[Running Containers]
end
DEV --> BUILD
BUILD --> PUSH
PUSH --> API_IMG
PUSH --> CTRL_IMG
PUSH --> DASH_IMG
API_IMG --> WT
CTRL_IMG --> WT
DASH_IMG --> WT
WT -->|"dashboard automatic · api/controller manual promote"| CONTAINERS
The cloud stack follows the same flow (its own workflow, push to prod).
Control Loop (Sense-Think-Act)¶
sequenceDiagram
participant CTRL as Controller
participant API as microcontroller-api
participant HW as Hardware
participant DB as PostgreSQL
Note over CTRL: Every 15 seconds
rect rgb(200, 230, 200)
Note over CTRL,API: SENSE
CTRL->>API: GET /state/
API->>HW: Read all sensors
HW-->>API: Sensor values
API->>API: Control rules from DeviceManager (in memory)
API->>DB: Variable labels
API-->>CTRL: SystemState
end
rect rgb(200, 200, 230)
Note over CTRL: THINK
Note over CTRL: Evaluate control rules (threshold, schedule, interval, after-actuator)
Note over CTRL: Determine actions
end
rect rgb(230, 200, 200)
Note over CTRL,HW: ACT
CTRL->>API: POST /actuator/{id}/command
API->>HW: Set actuator state
HW-->>API: OK
API-->>CTRL: OK
end
CTRL->>API: POST /state/heartbeat
Cloud Integration¶
Each Pi node syncs to the cloud API over the WireGuard VPN. The cloud runs a self-hosted PostgreSQL 17 + TimescaleDB instance for the fleet database, and Cloudflare R2 for photos.
graph TB
subgraph "Greenhouse 1"
RPI1[Raspberry Pi]
DB1[(Local PostgreSQL)]
end
subgraph "Greenhouse 2"
RPI2[Raspberry Pi]
DB2[(Local PostgreSQL)]
end
subgraph "Cloud"
GW[Gateway :80]
GAPI[greenthumb-api :8000]
AUTH[auth-service :8081]
ACC[account-service :8082]
SUPDB[(PostgreSQL 17 + TimescaleDB)]
SUPSTORAGE["Cloudflare R2 (photos)"]
LANDING[Landing page]
end
subgraph "Admin"
ADMINDASH["Admin Dashboard (nginx)"]
end
RPI1 -->|sync over WireGuard| GW
RPI2 -->|sync over WireGuard| GW
GW --> GAPI
GW --> AUTH
AUTH --> ACC
GAPI --> SUPDB
GAPI --> SUPSTORAGE
AUTH --> SUPDB
ACC --> SUPDB
ADMINDASH --> GW
The admin dashboard and API are not public yet; only the landing page is.
Sync is best-effort and offline-safe: the Pi queues unsynced rows locally and pushes them when connectivity is restored.