diff options
| author | Luke Hoersten <[email protected]> | 2026-06-13 22:48:48 -0500 |
|---|---|---|
| committer | Luke Hoersten <[email protected]> | 2026-06-13 22:48:48 -0500 |
| commit | 6cbdd4ed7466da28e524c9e0804722428a4b9698 (patch) | |
| tree | 90df296bf9df9ec4be0ad22267fcea51df92fc93 /TESTING.md | |
| parent | e7feac61e5ea275f303574fedd942ebed8fc8e73 (diff) | |
M5: POST /frame — hardware JPEG decode + orientation-aware paint
jpeg_decoder.{h,c} wraps esp_driver_jpeg (ESP32-P4 hardware decoder).
- One-time engine + DMA-aligned PSRAM scratch buffer setup (1 MB input,
768 KB output @ panel native 800x480 RGB565).
- try_lock + unlock so concurrent /frame POSTs get 503 instead of
queueing, per spec.
- jpeg_decoder_get_info() reports the dimensions; the http handler
validates them against the effective resolution before painting.
display.h adds display_present_rgb565(src, w, h):
- Landscape: src is 800x480, memcpy 1:1 into the panel framebuffer.
- Portrait: src is 480x800, software rotate 90° CW into the 800x480
panel framebuffer. (PPA / 2D-DMA hardware rotation is a later
optimization if portrait latency matters.)
http_api.c adds POST /frame:
- Content-Type must be image/jpeg → else 400.
- Empty body → 400. > 1 MB → 413. Display not initialized → 500.
- jpeg_decoder_try_lock(0) for concurrency: second post returns 503.
- Body streamed into the decoder's input buffer in chunks.
- Decode failure or dimension mismatch → 400 + decode_errors++.
- Paint failure → 500.
- Success → frames_received++, last_frame_us = esp_timer_get_time(),
204 No Content.
app_main initializes the JPEG decoder after display_init(). Both are
best-effort: failures log a warning and leave the rest of the firmware
running.
CMakeLists.txt: add esp_driver_jpeg to REQUIRES.
Known gap (M6 closes it): /frame currently paints regardless of
wake/sleep state. The 409-when-asleep rule lands with POST /state
in M6.
Build clean against ESP-IDF 5.4 (binary ~640 KB).
TESTING.md M5 expanded with portrait/landscape test commands,
ImageMagick test-image recipes, the full negative matrix (wrong
Content-Type, oversize, wrong dims, concurrent, garbage), and the
M6 dependency note.
Diffstat (limited to 'TESTING.md')
| -rw-r--r-- | TESTING.md | 59 |
1 files changed, 52 insertions, 7 deletions
@@ -201,25 +201,70 @@ Side-effects to confirm: ## M5 — JPEG Frame Push -**Acceptance**: `POST /frame` paints an 800x480 (or 480x800 portrait) JPEG. +**Acceptance**: `POST /frame` paints a 480x800 (portrait, default) or 800x480 (landscape) JPEG. Orientation determines the expected dimensions and the panel-side rotation. **How to verify** ```bash -# After /config sets state=wake (M6 dep — for now test post-/wake or hardcode awake): +# Default orientation (portrait) — send 480x800: curl -X POST -H "Content-Type: image/jpeg" \ --data-binary @test-480x800.jpg \ http://<device-ip>/frame + +# Then switch to landscape and re-test with an 800x480 JPEG: +curl -X POST -H "Content-Type: application/json" \ + -d '{"orientation":"landscape"}' http://<device-ip>/config + +curl -X POST -H "Content-Type: image/jpeg" \ + --data-binary @test-800x480.jpg \ + http://<device-ip>/frame ``` -Visual: matching pattern appears on the panel. Re-test in landscape orientation with `test-800x480.jpg`. +Visual: image fills the panel correctly oriented. After paint, `GET /state` shows `frames_received` incremented and `last_frame_ms_ago` populated. + +Generate test JPEGs with ImageMagick: + +```bash +# 8 vertical color bars at 480x800 portrait: +convert -size 60x800 gradient:white-black -duplicate 7 +append test-480x800.jpg +# or just any 480x800 JPEG: +convert -size 480x800 plasma: test-480x800.jpg +convert -size 800x480 plasma: test-800x480.jpg +``` Negative tests: -- Non-JPEG body → 400. -- 1.1 MB body → 413. -- 640x480 body (wrong size) → 400 or visible distortion (depends on decoder strictness). -**Status**: ⬜ pending. +```bash +# Wrong Content-Type — expect 400: +curl -i -X POST -H "Content-Type: image/png" --data-binary @anything \ + http://<device-ip>/frame + +# Oversize — expect 413: +dd if=/dev/urandom of=big.bin bs=1M count=2 +curl -i -X POST -H "Content-Type: image/jpeg" --data-binary @big.bin \ + http://<device-ip>/frame + +# Wrong dimensions — expect 400 with "expected WxH, got WxH": +convert -size 640x480 plasma: test-640x480.jpg +curl -i -X POST -H "Content-Type: image/jpeg" --data-binary @test-640x480.jpg \ + http://<device-ip>/frame + +# Concurrent posts — second gets 503: +curl -X POST -H "Content-Type: image/jpeg" --data-binary @test-480x800.jpg \ + http://<device-ip>/frame & +curl -i -X POST -H "Content-Type: image/jpeg" --data-binary @test-480x800.jpg \ + http://<device-ip>/frame # expect 503 if first is still in flight + +# Garbage bytes claiming to be JPEG — expect 400: +curl -i -X POST -H "Content-Type: image/jpeg" -d 'not a jpeg' \ + http://<device-ip>/frame +``` + +After every error: `decode_errors` in `GET /state` should increment. + +**Known gap (M6 closes this)**: M5 paints regardless of wake/sleep state. The `/frame` → `409` when asleep rule is added with `POST /state` in M6. Until then, a configured device boots ASLEEP per spec but `/frame` still paints because the state guard isn't wired yet. + +**Status**: 🟡 builds clean against ESP-IDF 5.4. Awaiting hardware verification (the test pattern from M3 confirms paint works; M5 adds the JPEG path). --- |
