sound-machine#
Two M5Stack Atom Echo–based nightstand white noise machines, replacing the Google Home speakers that used to live on our nightstands. Short press triggers a Home Assistant routine (lights off, white noise on, etc., depending on time of day), with offline fallback for travel use. Long-press cycles volume yo-yo through preset levels. Double-press is the late-night-lights gesture (raise outdoor + downstairs lights at low brightness for a 3 AM wake-up).
This repo holds everything for the project: firmware, design docs, hardware reference, and (eventually) 3D-printed enclosure models.
Layout#
sound-machine/
├── README.md # this file
├── Makefile # top-level entry: `make` builds firmware (and future model rendering)
├── .envrc # direnv: ESP toolchain env + libxml2 shim path
├── .envrc.private.example # template for host-only secrets (OTA dir, MQTT URL)
├── firmware/ # Rust firmware (esp-idf-svc, std). See firmware/README.md
└── reference/ # Design docs and hardware reference
├── mqtt-contract.md # wire protocol between device and HA
├── operating-modes.md # firmware state machine, LED scheme, NVS, OTA
├── signal-chain.md # audio path: ESP32 → MAX98357A → speaker
├── atom-echo/ # M5Stack Atom Echo pinmap, dimensions, schematic
├── speakers/ # Adafruit 1314 driver notes
└── datasheets/ # vendor PDFs for ESP32-PICO-D4, NS4168, SPM1423
Status#
- ✅ Hardware research and selection complete — see
reference/ - ✅ MQTT contract and operating modes designed —
reference/mqtt-contract.md,reference/operating-modes.md - ✅ Toolchain validated end-to-end —
firmware/builds, flashes, and runs on real hardware - ✅ v0.1.0 — offline-mode firmware — button, audio, NVS, LED
- ✅ v0.2.0 — online-mode firmware — WiFi + MQTT + HA Discovery
- ✅ v0.3.x — OTA-capable firmware — two-slot partition layout, HA
updateentity with progress bar,esp_ota_mark_app_validrollback - 🚧 Awaiting hardware — MAX98357A amps on order from DigiKey
- 🚧 Enclosure design — 3D-printable case TBD
Architecture in one paragraph#
Each device runs Rust firmware (esp-idf-svc, std mode) on an Atom Echo. WiFi connects to the home network, MQTT to a LAN-only broker (no TLS), HA Discovery announces entities. The button publishes events; HA decides what to do; HA sends back a "play white noise" command. Audio is generated locally on-device (no streaming dependency) and sent over I2S to an external MAX98357A amp driving a 3" 4Ω speaker. Onboard NS4168 amp is bypassed (no I2S data sent to its pins) — it's known not to be sized for sustained white noise. When WiFi or MQTT drops, the device falls into offline mode where the button toggles white noise locally; same code path as travel use.
Firmware updates are over-the-air via HA's MQTT update entity: make firmware-ota-publish builds the new binary, copies it to a static HTTP host on the LAN, and announces the version on a shared MQTT topic. HA shows an Install button on each device's card; clicking it streams the firmware in over plain HTTP, with a live progress bar driven by retained MQTT publishes. ESP-IDF's two-slot partition layout means a broken firmware automatically rolls back to the previous version on the next reset.
Both units run the same firmware binary — identity is derived at runtime from the chip's STA MAC and used directly as the topic-prefix segment (nightstand/<mac_hex>/...). HA users name each device in the HA UI; the MQTT contract guarantees stable unique_ids per MAC.
For the gory details: firmware/README.md, reference/mqtt-contract.md, reference/operating-modes.md.
Quick links#
- How to build firmware:
firmware/README.md - MQTT wire protocol:
reference/mqtt-contract.md - Firmware behavior spec:
reference/operating-modes.md - Audio signal chain:
reference/signal-chain.md - Atom Echo pinout:
reference/atom-echo/pinmap.md