Files

73 lines
3.0 KiB
Markdown

---
phase: "07"
plan: "03"
subsystem: firmware
tags: [firmware, micropython, frame-mode, drgb, drgbw, dnrgb, udp]
dependency_graph:
requires: [07-01, 07-02]
provides: [firmware/protocol.py#FrameAssembler, firmware/main.py#frame-mode]
affects: [lightsync/protocol/drgb.py]
tech_stack:
added: []
patterns: [DNRGB multi-packet reassembly, frame timeout with animation fallback]
key_files:
created: [tests/test_firmware_frames.py]
modified: [firmware/protocol.py, firmware/main.py]
decisions:
- "Frame timeout (2s) reverts to last animation, not idle — matches WLED behavior"
- "frame_is_rgbw flag routes DRGB/DNRGB to write_rgb, DRGBW to write_rgbw"
- "FrameAssembler keyed by absolute pixel index (start_index + i) — tolerates out-of-order chunks"
metrics:
duration: "2 minutes"
completed: "2026-04-07"
tasks_completed: 3
files_changed: 3
---
# Phase 7 Plan 3: Frame Mode Renderer Summary
**One-liner:** DRGB/DRGBW/DNRGB raw frame handling with FrameAssembler multi-packet reassembly and 2s animation fallback timeout.
## What Was Built
Added raw frame mode to the firmware. The device now receives pixel data from the server and writes it directly to the LED strip, supporting three packet types:
- **DRGB** (0x02): 3 bytes/pixel RGB, up to 490 pixels per packet — routed to `strip.write_rgb()`
- **DRGBW** (0x03): 4 bytes/pixel RGBW, up to 367 pixels per packet — routed to `strip.write_rgbw()`
- **DNRGB** (0x04): chunked RGB with start_index for large strips — reassembled by `FrameAssembler`, then routed to `strip.write_rgb()`
Frame mode takes priority over animation mode. When no frame packet arrives for 2 seconds, the firmware reverts to the last animation command.
## Tasks Completed
| Task | Description | Commit | Files |
|------|-------------|--------|-------|
| 1 | Add FrameAssembler to firmware/protocol.py | d14fa3c | firmware/protocol.py |
| 2 | Replace firmware/main.py with frame mode version | 2a22eef | firmware/main.py |
| 3 | Create tests/test_firmware_frames.py (19 tests) | ec97600 | tests/test_firmware_frames.py |
## Verification
- [x] `uv run pytest tests/test_firmware_frames.py -v` — 19 passed in 0.11s
- [x] `firmware/protocol.py` contains `FrameAssembler` with `feed()`, `reset()`, 200ms timeout
- [x] `firmware/main.py` handles DRGB (write_rgb), DRGBW (write_rgbw), DNRGB (via FrameAssembler)
- [x] Frame timeout (2s) reverts to last animation command mode
- [x] DNRGB 600-LED multi-packet frame: 2 packets (489 + 111 LEDs) round-trip passes
- [x] 300-LED DRGB = 902 bytes, DRGBW = 1202 bytes — both fit in single UDP packet
- [x] FrameAssembler clears stale partial frames after 200ms timeout
## Deviations from Plan
None — plan executed exactly as written.
## Known Stubs
None — all frame handling is fully wired.
## Self-Check: PASSED
- firmware/protocol.py exists with FrameAssembler: FOUND
- firmware/main.py exists with frame mode: FOUND
- tests/test_firmware_frames.py exists: FOUND
- Commits d14fa3c, 2a22eef, ec97600: FOUND