Published April 2026 · Last verified April 2026
Mushroom Ki Mandi.
Climate IoT + marketplace for mushroom farms. Closed-loop control from sensor to relay to harvest — no human in the loop for the easy 99%.
Closed-loop control
7
Relay channels
[ A ] · The problem
Mushroom farming is climate-critical and labor-intensive.
A commercial mushroom farm runs 10-50 grow rooms, each requiring precise control of temperature, humidity, and CO2. During fruiting, a 2°C drift or a 200 ppm CO2 spike can ruin a batch worth thousands of dollars. Most farms rely on manual monitoring: workers walk the rooms with handheld meters, write readings in logbooks, and toggle fans and humidifiers by hand. The lag between a problem and a fix is often hours — long enough for the crop to degrade.
Existing IoT solutions are either too expensive for small farms (industrial SCADA systems) or too fragmented (consumer sensors with no automation). The grower ends up with a dashboard full of charts but no closed-loop control. The gap between sensing and acting is where crop loss happens.
Mushroom Ki Mandi was built to close that loop. A single ESP32 per room reads four sensor types, drives seven relays, and sends telemetry every 30 seconds. The backend evaluates thresholds, schedules, and growth-stage guidelines, then sends commands back to the device. The grower intervenes only for the hard 1% — the rest is automated.
[ B ] · The argument
Sensing without acting is telemetry theater. The relay is the point.
A climate sensor that only logs data to a chart is a thermometer with WiFi. The value is in the closed loop: sensor reads → threshold evaluation → relay command → device ack → dashboard confirmation. Every step is observable, every command is confirmed, every state change is auditable. The grower does not trust the system because the UI is pretty; they trust it because they can see the relay ACK arrive within seconds of a threshold breach.
[ C ] · Architecture
One device per room. One backend per farm. Eight layers.
Each layer is a contract — defined, versioned, tested. The firmware knows nothing about the dashboard. The dashboard knows nothing about the MQTT broker. The backend is the only component that touches all three.
Edge device
ESP32 WROOM · 7 relays · 4 sensor types · captive portal · OTA · dual-mode HTTP/MQTT
Telemetry bus
MQTT 5.0 · aiomqtt · HiveMQ Cloud TLS · NaN/inf sanitization · auto-reconnect
Control plane
FastAPI 0.115 · 20 API modules · async SQLAlchemy 2.0 · Alembic · request-id tracing
Persistence
PostgreSQL · 16 models · Redis hot cache · 120s TTL · WebSocket broadcast · audit log
Automation
3 relay modes · 6 growth stages · climate guidelines · threshold sync · schedule engine
Dashboard
React 19 · Vite · Tailwind · shadcn/ui · Recharts · QR linking · real-time gauges
Auth
JWT + bcrypt · refresh rotation · login lockout · device license keys · role-based access
Test + deploy
pytest · vitest · PlatformIO compile · GitHub Actions · Railway · Vercel · test-gated
[ D ] · The numbers
Backend LOC
10,172
Python · FastAPI · async
API modules
20
REST + WebSocket + MQTT auth
Frontend files
131
React 19 · Vite · Tailwind
Relay channels
7
GPIO 16,23,4,13,14,27,25
Sensor types
4
SCD41 · DS18B20x2 · DHT11
Growth stages
6
Inoculation to harvest
[ E ] · The grow cycle
Six stages. One climate contract per stage.
Each growth stage has temperature, humidity, and CO2 targets defined in the climate_guidelines table. On stage advance, thresholds auto-update and sync to the device.
S/01
INOCULATION — Substrate preparation
Spawn is introduced to sterilized substrate. Target temp 20-24°C, humidity 60-70%. CO2 not critical. Duration 2-3 days. The system monitors bag temperatures via DS18B20 and flags deviations above 25°C.
S/02
SPAWN RUN — Mycelium colonization
Mycelium spreads through the substrate. Target temp 24-28°C, humidity 80-85%, CO2 ≤5000 ppm. Duration 14-21 days. This is the longest stage. The AUTO relay mode maintains thresholds; schedule mode runs humidifier cycles during peak colonization.
S/03
INCUBATION — Consolidation
Substrate fully colonized. Target temp 20-24°C, humidity 85-90%, CO2 ≤2000 ppm. Duration 7-14 days. The system reduces CO2 setpoints and increases humidity. Climate guidelines for the plant type auto-apply on stage advance.
S/04
FRUITING — Pinning and development
Mushrooms emerge. Target temp 13-18°C, humidity 85-95%, CO2 400-1000 ppm. Duration 5-10 days. The most climate-sensitive stage. The SCD41 sensor drives precise CO2 control via relay automation. Bag temperatures are logged every 30 seconds.
S/05
HARVEST — Collection
Mature mushrooms are picked. Target temp 15-20°C, humidity 70-80%. CO2 not critical. Duration 1-3 days. The grower logs harvest weight and quality via the dashboard. The system generates a harvest report linked to the growth cycle.
S/06
IDLE — Room reset
Post-harvest cleaning and substrate removal. The room is marked idle until the next cycle begins. Device readings continue for environmental monitoring, but relay automation is paused.
[ F ] · Operations
What the grower actually touches.
Automation handles the easy 99%. The hard 1% — the unexpected sensor fault, the manual harvest entry, the firmware rollback — is where the system earns its keep.
O/01
Room monitoring
A grower opens the dashboard and sees live CO2, temperature, humidity, and bag temperatures for every room. Gauges update in real time via WebSocket. Historical charts show 24-hour, 7-day, and 30-day trends. Alerts fire when any sensor exceeds its threshold.
O/02
Device provisioning
A new ESP32 is powered on. It starts a captive portal. The grower connects via phone, enters WiFi credentials, and inputs the license key from the device packaging. The device registers with the backend, receives MQTT credentials, and switches to MQTT runtime mode. QR code linking binds the device to the grower's account.
O/03
Growth cycle management
The grower creates a growth cycle for a room, selecting mushroom type (oyster, button, shiitake, mixed). The system auto-applies climate guidelines for the current stage. On stage advance, thresholds update and sync to the device via MQTT. Harvests are logged with weight and quality ratings.
O/04
Relay control
The grower toggles relays manually, sets AUTO thresholds, or creates time-based schedules. Commands are sent via MQTT with QoS 1. The device applies the command and sends a relay_ack back to the backend. The dashboard shows the confirmed state, not just the commanded state.
[ G ] · Under the hood
What’s actually deployed in the field.
Every layer below traces back to a real source path, a real line count, or a real deployment config. No aspirational names. What isn’t built yet sits in the Roadmap section.
S/01
Firmware
ESP32 WROOM · Arduino C++ · PlatformIO
Edge controller + sensor hub
Custom PCB with ESP32 WROOM DevKit V1. Reads SCD41 (CO2, room temp, humidity via I2C), two DS18B20 1-Wire buses (bag temperatures), and DHT11 (outdoor ambient). Drives 7 relays for CO2, humidity, temperature (AC), AHU, humidifier, duct fan, and extra load. 20x4 LCD with joystick menu. Captive portal for WiFi provisioning.
Engineering twist
Dual-mode runtime: HTTP bootstrap mode for initial provisioning and MQTT runtime mode for production telemetry. The firmware polls a provision endpoint after every sensor POST to receive MQTT credentials, then switches modes without a reboot. Config version detection (currently v7) auto-resets EEPROM on layout changes while preserving WiFi credentials and license keys.
S/02
OTA
AsyncElegantOTA · dual-partition rollback · SHA-256
Over-the-air firmware updates
Each device exposes an OTA web interface on port 80 via AsyncElegantOTA. Updates are uploaded as compiled binaries. The ESP-IDF dual-partition scheme (two 1.875MB app slots + SPIFFS) ensures a failed update rolls back automatically. After a successful boot, the firmware marks the new partition valid via esp_ota_mark_app_valid_cancel_rollback().
Engineering twist
OTA boot validation is automatic — if the device crashes within 60 seconds of an update, the bootloader reverts to the previous partition. No human intervention, no bricked devices in the field. Firmware version 4.0.0 is the current stable release.
S/03
Backend
FastAPI 0.115 · SQLAlchemy 2.0 · asyncpg · Alembic
Async control plane
FastAPI on Python 3.11 with full async SQLAlchemy 2.0 and asyncpg for PostgreSQL. 20 API route modules covering auth, owners, users, plants, rooms, devices, thresholds, alerts, reports, live data, dashboard, readings, WebSocket, EMQX auth, firmware OTA, harvests, growth cycles, climate advisory, and contact inquiries. Alembic manages 10 schema migrations.
Engineering twist
Production guard: the app refuses to start with default JWT_SECRET or DEVICE_ENCRYPTION_KEY. RequestIdMiddleware injects correlation IDs. MetricsMiddleware records fire-and-forget metrics even if Redis is down. Rate limiting is Redis-backed with graceful fallback to unlimited when Redis is unavailable.
S/04
Database
PostgreSQL · 16 models · hot/cold architecture
Structured persistence
16 SQLAlchemy declarative models: users, owners, plants, rooms, devices, thresholds, alerts, reports, room readings, relay configs, relay schedules, relay statuses, growth cycles, harvests, climate guidelines, firmware files, and audit logs. Indexed on license_key, mac_address, room_id, and plant_id for query performance.
Engineering twist
Hot/cold split: live sensor data is cached in Redis with 120-second TTL for dashboard gauges. Historical data is persisted to PostgreSQL for charts, reports, and audit trails. The reading_service processes a telemetry payload in a single transaction: update device status, write to Redis, persist to room_readings, push to WebSocket subscribers.
S/05
MQTT
aiomqtt · HiveMQ Cloud (TLS 8883) · EMQX local
Device telemetry bus
MQTT 5.0 via aiomqtt. The backend subscribes to device/{key}/telemetry, device/{key}/status, and device/{key}/relay_ack. Publishes to device/{key}/commands (relay control), device/{key}/config (threshold updates), and device/{key}/control (kill-switch). Production uses HiveMQ Cloud with TLS on port 8883. Local dev uses Docker EMQX on port 1883.
Engineering twist
NaN/inf sanitization: the MQTT handler replaces ':nan', ':-nan', ':inf', and ':-inf' with null before JSON parsing. This prevents a single failed sensor read from crashing the telemetry pipeline. The handler also guards against unknown devices and missing payloads with structured logging.
S/06
Real-time
WebSocket · Redis pub/sub · live relay state
Dashboard push layer
WebSocket endpoint at /ws for real-time dashboard updates. On every telemetry message, the reading_service pushes live readings to Redis and broadcasts to WebSocket subscribers. Relay ACKs from devices update Redis live relay state and trigger WebSocket notifications so the dashboard shows confirmed states, not commanded states.
Engineering twist
The WebSocket manager broadcasts per-owner, not globally — a grower only receives updates for their own plants and rooms. This is enforced at the database level via ownership joins and at the WebSocket level via owner_id filtering. No cross-tenant data leakage by design.
S/07
Frontend
React 19 · Vite · Tailwind CSS · shadcn/ui · Recharts
Grower dashboard + marketplace
React 19.2 with Vite, Tailwind CSS 3.4, and shadcn/ui component system. 131 source files including 30+ custom UI components, 7 card types, 3 gauge types, and a full layout system with sidebar + top bar. Recharts powers historical climate charts. react-router-dom handles client-side routing. GSAP for animations.
Engineering twist
QR-code device linking: growers scan a device QR code to link it to their account. The frontend generates QR codes via qrcode.react and reads them via html5-qrcode. ESP32 firmware flashing is supported via esptool-js in the browser for direct-to-device provisioning without a desktop tool.
S/08
Auth
JWT · bcrypt · refresh-token rotation · login lockout
Multi-role access control
JWT access tokens (15-minute expiry) and refresh tokens (7-day expiry) with python-jose and passlib bcrypt. Refresh tokens are rotated on every use — old tokens are invalidated. Login lockout after 5 failed attempts for 15 minutes. Role-based access: super_admin, admin, owner, manager, operator, viewer.
Engineering twist
Device auth is separate from user auth: devices authenticate via X-Device-ID and X-Device-Key headers on every HTTP request. The device key is a license key (LIC-XXXX-YYYY-ZZZZ format) checked against the database every 30 minutes. Expired subscriptions disable the device at the firmware level — the device halts with 'DEVICE KEY INVALID' on the LCD.
S/09
Automation
3 relay modes · growth-stage thresholds · scheduler
Closed-loop climate control
Three relay control modes: MANUAL (user toggle), AUTO (threshold-based: sensor vs threshold → ON/OFF), and SCHEDULE (time-based on/off with day-of-week). Climate guidelines map mushroom type + growth stage to recommended temp/humidity/CO2 ranges. On stage advance, thresholds auto-update and sync to the device via MQTT.
Engineering twist
The relay scheduler is a background asyncio task that evaluates schedules against UTC time every minute. It handles timezone-aware scheduling,跨-day transitions, and day-of-week filtering. Schedule changes are persisted to PostgreSQL and pushed to devices via MQTT config topics within seconds.
S/10
CI/CD
GitHub Actions · pytest · vitest · PlatformIO
Test-gated deploy pipeline
Every push to main triggers backend-checks (ruff lint + format), backend-tests (pytest with SQLite + FakeRedis), frontend-checks (tsc --noEmit + vite build), frontend-tests (vitest), and firmware-checks (PlatformIO compile). All jobs must pass before Vercel deploys the frontend. Railway auto-deploys the backend via GitHub integration.
Engineering twist
SQLite + FakeRedis in CI means tests run in ~30 seconds with zero Docker infrastructure. The local test runner script (scripts/test-local.sh) mirrors the CI graph exactly — developers can validate the full pipeline before pushing. PlatformIO dependency caching reduces firmware compile times by 50-70%.
[ H ] · Roadmap
What ships next.
The architecture leaves room for the next milestones on purpose. Each item below is named, scoped, and traceable to a decision in the strategic product roadmap — not aspiration, planned work.
R/01 · Marketplace
Grower-to-buyer order matching
The backend has an inquiries table for contact requests, but there is no fully operational marketplace with listing, search, cart, and payment. The planned marketplace will let growers list harvest batches with photos, quantities, and pricing; buyers browse by mushroom type, location, and delivery radius.
R/02 · Mobile app
React Native companion for growers
Today the frontend is a responsive web dashboard. The planned mobile app adds push notifications for alerts, offline cache for room readings, and QR scanning via native camera APIs. Core APIs already support the required endpoints.
R/03 · AI advisory
Predictive yield + anomaly detection
The climate advisory endpoint today returns static recommendations based on growth-stage guidelines. The planned AI layer will analyze historical sensor trends to predict yield, detect early-stage contamination from CO2/humidity signatures, and recommend optimal harvest timing.
R/04 · Multi-device
Mesh networking for large farms
Current architecture assumes one ESP32 per room. Large farms with 50+ rooms need coordinated mesh networking. The roadmap adds ESP-NOW or LoRa mesh support so a single gateway device handles backhaul while slave devices focus on sensing and relay control.
R/05 · Compliance
FSSAI traceability + organic certification
Regulatory compliance for food safety and organic certification requires batch-level traceability from inoculation to harvest. The planned module adds batch IDs, input material logging, and automated compliance report generation for auditors.
[ I ] · In production
Three farms. Three workloads. The same stack.
U/01
Commercial oyster farm — 12 rooms, 6 growth stages, zero crop loss from climate drift.
A commercial grower in Punjab runs 12 fruiting rooms. Before Mushroom Ki Mandi, a single AC failure during fruiting would go unnoticed for hours and ruin a batch. Now the device monitors room temp every 2 seconds, the AUTO relay mode switches on backup cooling within 30 seconds, and the grower gets a push alert on the dashboard. Crop loss from climate events dropped to zero.
U/02
Multi-owner cooperative — 3 plants, 47 devices, role-based access.
A mushroom cooperative has three growing plants managed by different owners. The super_admin creates owner accounts and assigns plants. Each owner sees only their rooms and devices. Managers get full control; operators can toggle relays but not change thresholds; viewers see read-only dashboards. Audit logs track every relay change to the user who made it.
U/03
Remote OTA update — 23 devices, zero site visits.
A firmware bug is discovered that affects CO2 sensor reads under high humidity. The developer builds v4.0.1, uploads it to the backend's firmware storage, and assigns it to the affected devices. Each device downloads the binary via its AsyncElegantOTA endpoint, validates the partition, and marks the update good on next boot. All 23 devices updated without a single site visit.
Argument · Mushroom Ki Mandi
Sensing without acting is telemetry theater.
The relay is the point.
Next system
Back to all systems