Code

Every component β€” firmware, data pipeline, database schema, AI planning prompts, and this website β€” is available for inspection, replication, and contribution.

Repositories

RepositoryWhatLanguage
verdify-firmwareESP32 ESPHome firmware β€” state machine, sensors, controlsYAML + C++ lambdas
verdify-platformIngestor, dispatcher, scripts, migrations, Docker stackPython + SQL
verdify-siteThis website β€” Quartz + Obsidian vaultMarkdown

Repositories will be published once sanitized for credentials.

ESP32 Firmware

The greenhouse controller runs ESPHome 2026.3.0 on a Kincony KC868-E16P board. Six YAML files define the entire system:

FileLinesWhat it does
greenhouse-v2.yaml380Top-level: WiFi, SNTP, MQTT, OTA, buttons, boot sequence
hardware.yaml292IΒ²C bus, UART/RS485, Modbus controllers (9 devices), PCF8574 I/O expanders, relay definitions
sensors.yaml1,250+6 air probes, 3 soil probes, COβ‚‚, light, water flow, hydro quality, derived metrics (VPD, enthalpy, DLI), solar position
globals.yaml584State variables, setpoint storage, counters, accumulators
controls.yaml1,100+42-state climate machine, irrigation scheduler, mister pulse-rotation, grow light DLI automation, occupancy-triggered lighting, economiser
tunables.yaml85157 number entities + 8 switch entities exposed to the AI planner

Key Design Decisions

The controller is autonomous. If the AI, database, and network all fail simultaneously, the ESP32 continues operating on its last-known setpoints. If those are lost, it falls back to hardcoded safe defaults. The AI makes it smarter β€” it doesn’t make it dependent.

Setpoints, not relay commands. The AI adjusts boundaries (temperature bands, VPD thresholds, mister timing). The firmware makes real-time equipment decisions at 5-second resolution within those boundaries. This separation means the AI can be slow (minutes) while the controller is fast (seconds).

Everything is tunable. 57 numeric parameters and 8 boolean switches are exposed as ESPHome number/switch entities. The AI can adjust any of them at any time via aioesphomeapi or HTTP pull.

Data Pipeline

ESP32 (aioesphomeapi) ──→ Ingestor (Python) ──→ TimescaleDB
                              β”‚
Tempest (HA API) ──────────────
Shelly EM50 (HTTP) ────────────
Open-Meteo (HTTP) ─────────────
Frigate (MQTT) β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Ingestor Architecture

One Python process, four async coroutines:

CoroutinePurpose
esp32_loop()Persistent aioesphomeapi connection. Subscribes to all entity state changes. Buffers readings. Shares the connection for setpoint pushes.
flush_loop()Every 2 minutes, batch-writes buffered sensor data to the climate table. Merges outdoor weather onto the same rows.
task_loop()10 periodic tasks on independent schedules: setpoint dispatcher (5min), alert monitor (5min), Shelly sync (5min), Tempest sync (5min), HA sensor sync (5min), forecast sync (1hr), reactive planner (5min), matview refresh (5min), grow light tracker (daily), water flow sync (5min).
mqtt_loop()Subscribes to Frigate occupancy topic. Writes to system_state table. Pushes occupancy to ESP32 for mister/fog inhibit.

Database

29 tables in TimescaleDB (PostgreSQL 16 + hypertable extensions):

  • Hypertables (time-series, compressed at 7 days): climate, equipment_state, energy, setpoint_changes, setpoint_plan, weather_forecast, diagnostics
  • Crop tables (event-driven): crops, crop_events, observations, treatments, harvests
  • Planning (AI-generated): plan_journal, planner_lessons
  • Config/reference: sensor_registry, equipment_assets, irrigation_schedule, nutrient_recipes, daily_summary

34 views and 14 functions provide derived analytics β€” from v_greenhouse_now (single-row snapshot of everything) to fn_glazing_transmission(azimuth) (physics-based light model).

Full schema: Data Model

AI Planning Prompts

The setpoint planner runs as an OpenClaw isolated agentTurn cron job. The prompt is ~4,000 tokens and instructs the agent to:

  1. Validate the previous plan (score 1–10, extract lessons)
  2. Gather 20+ data sections via gather-plan-context.sh
  3. Check active learned lessons
  4. Reason across temperature, VPD, misting, economiser, irrigation, lighting, safety
  5. Write time-keyed waypoints to setpoint_plan
  6. Record a journal entry with hypothesis and experiment
  7. Post summary to Slack

The prompt includes the full firmware state machine reference so the planner understands what each setpoint actually controls at the hardware level.

Full planning architecture: AI Planning Loop

This Website

Built with Quartz 4.5.2 β€” a static site generator designed for Obsidian vaults. The content lives in markdown files with YAML frontmatter, wikilinks, and embedded Grafana panels.

ComponentTechnology
Static site generatorQuartz 4.5.2
Content formatObsidian-compatible Markdown
Dashboard embedsGrafana solo panels via iframe
Hostingnginx (Alpine) container β†’ Traefik β†’ Cloudflare
Buildnpx quartz build β†’ docker recreate
Branding removalnginx sub_filter CSS injection

The entire site rebuilds in under 3 seconds. Content changes deploy with one command: /srv/verdify/scripts/rebuild-site.sh.

Replicate This

To build a similar system, you need:

ComponentCostPurpose
ESP32 board (Kincony KC868-E16P or similar)~$80Controller with relay outputs + RS485
Climate probes (RS485 Modbus)~$15-30 eachTemperature + humidity per zone
Soil probes (DFRobot SEN0600/SEN0601)~$20-30 eachSoil moisture, temp, EC
Linux VM or Raspberry Pi~$0-50Runs ingestor, DB, Grafana, agent
TimescaleDB (Docker)FreeTime-series database
Grafana OSS (Docker)FreeDashboards
ESPHomeFreeFirmware framework
OpenClawFreeAI agent runtime
Weather station (optional, Tempest)~$300Outdoor conditions + forecast validation

Total hardware cost: ~$200-500 depending on sensor count. All software is free and open source.

OpenClaw β†’ Β· ESPHome β†’ Β· TimescaleDB β†’ Β· Grafana β†’ Β· Quartz β†’