EnvOS — Custom Environmental Monitoring OS
Production-grade IoT environmental monitor on a custom Raspberry Pi OS image
A production-grade IoT system built from bare metal up — a Go service, a custom-branded Raspberry Pi OS image, and a real-time web dashboard — running unattended on a Raspberry Pi Zero 2 W.
EnvOS is a fully self-hosted environmental monitoring system — live dashboard, historical trends, Home Assistant integration, MQTT alerts — running on hardware I fully controlled, from the OS image up.
The Problem
Off-the-shelf home sensor kits are either cloud-locked, closed-source, or too limited to extend. I wanted something I could own end-to-end instead.
What I Built
- A Go service that reads 5 I2C sensors (temperature, humidity, pressure, VOC air quality, ambient light, UV index, 9-axis IMU) through a single contention-safe I2C goroutine, fanning out readings via an internal event bus to independent consumers: a REST/SSE API, an MQTT publisher, a CSV logger, and an e-paper display renderer.
- A live web dashboard — server-sent events for real-time sensor cards, Chart.js historical graphs reading straight from rotating CSV logs, mobile-responsive layout.
- A custom Raspberry Pi OS image — my own build pipeline (macOS-hosted, APFS copy-on-write base cloning, cross-compilation, boot partition config, rootfs verification, xz compression) that produces a branded, pre-configured image: boot splash, MOTD, systemd services, WiFi-from-SD-card provisioning, USB-OTG fallback networking, and auto-installing MQTT broker — all baked in, ready to flash and boot headless.
- MQTT + Home Assistant integration — auto-discovery on startup, threshold-based alerting (VOC, over-temp).
- A physical status display — an SSD1680 e-paper panel rendering a live summary, driven by a custom driver (partial-refresh, custom bitmap font, flicker-free redraws).

Highlights / Engineering Decisions
- Single I2C goroutine + event bus fan-out — avoids bus contention while keeping API, MQTT, logging, and display fully decoupled from the sensor read loop.
- Dual network paths — WiFi (auto-reconnect + watchdog) with a USB-OTG gadget fallback, so the device is always reachable even with no WiFi configured, useful for a headless single-board computer.
- Built and debugged the image pipeline myself — including chasing down a Bookworm/NetworkManager quirk that left the USB gadget interface “unmanaged” after boot, and a watchdog bug that was intermittently forcing the network into sleep state.
- Design for reflash safety — sensor logs are backed up to the boot partition and restored automatically on first boot, so re-imaging the SD card doesn’t lose historical data.
Stack
Go · periph.io (I2C) · paho MQTT · Home Assistant · Chart.js · Server-Sent Events · systemd · NetworkManager · Raspberry Pi OS / Bookworm · e-paper (SSD1680) · bash (image build tooling)
Status
Running continuously in production on a Raspberry Pi Zero 2 W: live sensor readings, dashboard, MQTT/Home Assistant integration, CSV history, and e-paper display are all complete and stable. Next: OTA update delivery.