Mycelium framework
What it is
Mycelium is an open-source framework for building IoT systems without the usual integration tax. Plug in an ESP32, flash, power it on — it announces itself on the network, the hub picks it up, and you can talk to it. Out loud, in plain English. No glue code, no firmware-side state machine, no per-device UI work.
It's the framework I always wanted when wiring up sensors at home: one device file declares what the hardware can do, and the rest — WiFi handshake, MQTT lifecycle, command acks, dashboard panels, voice intent, charts — comes for free.
Architecture
┌──── ESP32 (MicroPython) ────┐ ┌──── Mycelium hub ────────┐
│ registry.json declares │ │ FastAPI + asyncio │
│ sensors + actuators │ ──MQTT─▶│ capability registry │
│ HomeHubClient handles │ │ intent JSON ─┐ │
│ WiFi, sessions, telemetry │ ◀──cmd──│ command bus │ │
└─────────────────────────────┘ │ │ │
│ Ollama │ React │
│ STT (Vosk) │ dash │
│ TTS (Kokoro) │ │
└───────────────┴──────────┘
Notable choices
- The device registry drives the UI. When a device comes online, its birth message advertises its sensors and actuators. The React dashboard reads that birth message and dynamically builds the panel — toggles for
SWITCH, momentary buttons forPULSE, live gauges, historical charts. No frontend code changes to support new hardware. - Capability declaration as the single source of truth. The same
registry.jsonon the device drives the dashboard, the voice surface, and the command-bus contract. Adding a new actuator type is one entry, not three. - Voice + chat in the same pipeline. Vosk for STT, Kokoro for TTS, Ollama for intent extraction. Voice and chat resolve into the same structured intent JSON that the command bus already understands — "turn the fan on for ten minutes" and a manual button press hit the device by exactly the same path.
- Hot / cold data split. Redis holds 48 hours of high-resolution telemetry; SQLite aggregates the cold path. The dashboard reads both transparently.
- Devices don't care where the command came from. Voice, chat, dashboard button, or an external controller speaking the same MQTT contract — the ESP32 only sees a validated intent JSON.
What works well
Mycelium has been running my home shop and outdoor sensor mesh long enough to teach me what IoT tutorials leave out: WiFi flakiness, broker reconnect storms, devices that lie about their state. The capability-declaration model has held up — adding new hardware is genuinely one file. The voice loop has held up too: STT → Ollama intent → command bus → device → spoken confirmation is fast enough on a Pi 5 that it feels like talking to the house, not querying a server.
The framework intentionally stops short of the control layer. Closed-loop optimisation (MPC, state estimation, planning over a horizon) is its own problem, and a much harder one. When I needed that for my grow chamber I built it as a separate project.
Links
- github.com/curtisblanchette/mycelium
- Pairs with Cortex MPC for closed-loop control — Cortex speaks the same MQTT contract and shows up to Mycelium as just another commanding client.