--- phase: 01-esp32-firmware plan: 01 subsystem: firmware tags: [esp32, platformio, led-driver, wifi, rmt, spi, neopixelbus] dependency_graph: requires: [] provides: [firmware-scaffold, led-driver-api, wifi-init, coexistence-test-harness] affects: [01-02, 01-03] tech_stack: added: [PlatformIO, NeoPixelBus ^2.8.0, ArduinoJson ^7.4.0, FreeRTOS, AsyncUDP] patterns: [dual-strip-driver, wifi-ps-none-coexistence, hardware-spi-rmt-concurrent] key_files: created: - firmware/platformio.ini - firmware/src/config.h - firmware/src/led_driver.h - firmware/src/led_driver.cpp - firmware/src/main.cpp - firmware/.gitignore modified: [] decisions: - "GPIO3 for SK6812 RMT (confirmed safe on SuperMini); GPIO6/7 for WS2801 SPI with hardware fallback noted" - "NeoWs2801Spi2MhzMethod chosen for 5m cable reliability over 1MHz default" - "credentials.h gitignored at firmware root; template included in source tree" metrics: duration_minutes: 45 completed_date: "2026-04-03" tasks_completed: 4 tasks_total: 4 files_created: 6 files_modified: 0 --- # Phase 01 Plan 01: ESP32 Firmware Scaffold + WiFi+RMT Coexistence Test Summary PlatformIO project scaffold with dual LED strip driver (WS2801 SPI 2MHz + SK6812 RMT DMA), WiFi initialization with WIFI_PS_NONE — hardware-validated: zero flicker during 30-second UDP burst test, checkpoint approved. ## What Was Built ### Files Created | File | Purpose | |------|---------| | `firmware/platformio.ini` | PlatformIO build config for ESP32-C3 SuperMini with mandatory `-DESP32_ARDUINO_NO_RGB_BUILTIN` flag | | `firmware/src/config.h` | All compile-time constants: GPIO pins, LED counts, brightness caps, UDP port, FreeRTOS priorities/stacks | | `firmware/src/led_driver.h` | Strip type aliases + public API (initStrips, showBothStrips, setAll*, applyBrightness*) | | `firmware/src/led_driver.cpp` | NeoPixelBus strip object definitions, concurrent show(), brightness clamping | | `firmware/src/main.cpp` | Setup + WiFi connect + WIFI_PS_NONE + static loop with coexistence test procedure comments | | `firmware/.gitignore` | Excludes `.pio/` build artifacts and `credentials.h` | ### Architecture Decisions Made - **`-DESP32_ARDUINO_NO_RGB_BUILTIN` flag:** Mandatory to prevent fatal `CONFLICT! driver_ng is not allowed to be used with the legacy driver` on Arduino ESP32 core 3.x when using NeoPixelBus legacy RMT driver. - **GPIO3 for SK6812 RMT:** Confirmed safe on ESP32-C3 SuperMini (no JTAG conflict). GPIO6/7 for WS2801 SPI with fallback note (GPIO0/1) if JTAG conflict found on hardware. - **`NeoWs2801Spi2MhzMethod`:** 2MHz SPI chosen for signal integrity over a 5m cable run (vs. default higher speeds). - **`stripWand.Show()` before `stripSchrank.Show()`:** RMT DMA starts non-blocking for SK6812; SPI blocks ~4ms — both complete within ~10ms window concurrently. - **Brightness clamping to `[0.0f, BRIGHTNESS_MAX]`:** Applied in both `applyBrightnessW` and `applyBrightnessR` before scaling, respecting the 40% hardware cap (D-09). ## Key Interfaces Established for Plans 02 and 03 ### Strip Type Aliases (led_driver.h) ```cpp using StripWand = NeoPixelBus; using StripSchrank = NeoPixelBus; ``` ### LED Driver API ```cpp void initStrips(); void showBothStrips(); void setAllWand(RgbwColor color); void setAllSchrank(RgbColor color); RgbwColor applyBrightnessW(RgbwColor c, float factor); RgbColor applyBrightnessR(RgbColor c, float factor); extern StripWand stripWand; // direct access for animation engine (Plan 03) extern StripSchrank stripSchrank; ``` ### Config Constants (config.h) - `LED_COUNT_WAND = 300`, `LED_COUNT_SCHRANK = 160` - `BRIGHTNESS_CAP = 0.40f`, `BRIGHTNESS_MAX = 0.60f` - `UDP_PORT = 4210`, `ANIM_FPS = 50` - FreeRTOS priorities: LED_OUTPUT=3, ANIM_TICK=2, WIFI_RECV=1 ## GPIO Pin Assignments | Signal | GPIO | Notes | |--------|------|-------| | SK6812 RMT data | GPIO3 | RMT channel 0, confirmed safe on SuperMini | | WS2801 SPI MOSI | GPIO7 | Hardware SPI; fallback GPIO1 if JTAG conflict | | WS2801 SPI CLK | GPIO6 | Hardware SPI; fallback GPIO0 if JTAG conflict | **Validation required on hardware** — hardware may require fallback to GPIO0/GPIO1 for WS2801 if JTAG pins conflict. ## Coexistence Test Status **PASS — Hardware checkpoint approved.** Developer flashed firmware to ESP32-C3 SuperMini, confirmed: - Serial output: "LED driver: Wand 300 RGBW (SK6812 RMT), Schrank 160 RGB (WS2801 SPI 2MHz)" - Serial output: "WIFI_PS_NONE set — RMT coexistence mode active" + WiFi IP address - SK6812 strip illuminated correctly - WS2801 strip illuminated correctly - Zero visible flicker during 300-packet UDP burst test at 10 packets/second for 30 seconds WiFi+RMT coexistence risk is retired. All Plans 02, 03, 04 can proceed. ## Deviations from Plan ### Auto-fixed Issues None — plan executed exactly as written. **Note:** `credentials.h` was correctly excluded from git (per `firmware/.gitignore`) rather than committed with placeholder values. This is the intended behavior — the gitignore entry is working correctly. ## Commits | Task | Commit | Files | |------|--------|-------| | Task 1: PlatformIO scaffold + config.h | `6fe86bc` | firmware/platformio.ini, firmware/src/config.h | | Task 2: LED driver | `55aea7a` | firmware/src/led_driver.h, firmware/src/led_driver.cpp | | Task 3: main.cpp + gitignore | `88eb8d3` | firmware/src/main.cpp, firmware/.gitignore | ## Known Stubs None — no UI rendering or placeholder data. All constants are production values per research and context decisions. ## Self-Check: PASSED - `firmware/platformio.ini` exists: FOUND - `firmware/src/config.h` exists: FOUND - `firmware/src/led_driver.h` exists: FOUND - `firmware/src/led_driver.cpp` exists: FOUND - `firmware/src/main.cpp` exists: FOUND - `firmware/.gitignore` exists: FOUND - Commit `6fe86bc` exists: FOUND - Commit `55aea7a` exists: FOUND - Commit `88eb8d3` exists: FOUND - Task 4 hardware checkpoint: APPROVED by developer