Files
LvScreenshot/README.md
T
pablo2048andClaude Opus 4.8 80039764fa feat: standalone LvScreenshot library v1.0.0
Promoted from DIYStickyNotes/lib/LvScreenshot (byte-identical src) into a
reusable Gitea library. Adds LICENSE, .gitignore, library.properties,
keywords.txt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 14:56:14 +02:00

73 lines
3.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# LvScreenshot
Board-independent **LVGL 9 screen capture** as a reusable PlatformIO library: grab the current screen
into a tight **RGB565** buffer via `lv_snapshot_take()`, optionally serve it over HTTP, and save it as
a PNG on your PC. Because it re-renders the LVGL object tree (not the panel framebuffer), it works with
**any** display driver — partial-flush (LovyanGFX), full-frame (Arduino_GFX), or RGB panel.
## Requirements
- LVGL 9 with **`LV_USE_SNAPSHOT 1`** in your `lv_conf.h` (the core warns at compile time otherwise).
- **PSRAM must be enabled/available on ESP32** — see "Memory / PSRAM" below. This is essential.
- The optional HTTP helper needs `ESPAsyncWebServer`; it self-disables (no-op) when that is absent.
## Memory / PSRAM (important)
A full-frame snapshot is large — e.g. 320×480 RGB565 = **~300 KB**. LVGL's stock `lv_snapshot_take()`
allocates that from LVGL's own heap, which with **`LV_USE_STDLIB_MALLOC = LV_STDLIB_BUILTIN`** (the
default) is a small fixed pool (`LV_MEM_SIZE`). It cannot fit ~300 KB, so `lv_snapshot_take()` simply
**returns null and the capture fails** — the symptom that must be worked around whenever capture goes
through LVGL.
This library avoids that: instead of letting LVGL allocate, it allocates the draw buffer itself from
**PSRAM** (`heap_caps_malloc(MALLOC_CAP_SPIRAM)` on ESP32, plain `malloc` elsewhere) and renders into
it via `lv_snapshot_take_to_draw_buf()`. Consequences for integrators:
- **Enable PSRAM** in your build (e.g. `-DBOARD_HAS_PSRAM`, `board_build.arduino.memory_type` set to a
`*_opi`/`*_qspi` PSRAM variant). Without a working PSRAM heap the two ~300 KB allocations (draw buffer
+ tight-packed copy) will fail and capture returns `false`.
- You do **not** need to enlarge `LV_MEM_SIZE` or switch `LV_USE_STDLIB_MALLOC` — the library
deliberately bypasses LVGL's allocator for the snapshot buffer for exactly this reason.
- Peak transient usage is ~2× the frame (~600 KB) during a capture; both buffers are freed right after
(the HTTP helper keeps one until the next request so it can stream it).
## Core API (`LvScreenshot.hpp`, LVGL-only)
```cpp
LvScreenshot shot;
if (lvgl_port_lock(-1)) { // the caller must hold the LVGL lock
bool ok = lvScreenshotCaptureTop(&shot); // topmost overlay, else the active screen
lvgl_port_unlock();
if (ok) { /* shot.data = RGB565, shot.width/height/length */ lvScreenshotFree(&shot); }
}
```
`lvScreenshotCapture(obj, &shot)` captures a specific object. Buffers use PSRAM on ESP32.
## Optional HTTP endpoint (`LvScreenshotHttp.hpp`, needs ESPAsyncWebServer)
```cpp
#include <LvScreenshotHttp.hpp>
// in your web-server setup:
lvScreenshotAttachHttp(www, lvgl_port_lock, lvgl_port_unlock); // registers GET /screenshot
```
The route returns the raw RGB565 body plus `X-Width` / `X-Height` / `X-Format` headers.
## PC tool
```bash
uv run tools/screenshot.py --host diystickynotes.local --out shot.png
# --swap-rb if colours look inverted (BGR panel)
# --byteswap if the image is garbled (big-endian RGB565)
```
Converts the RGB565 response to a PNG (numpy + Pillow; deps auto-installed via uv / PEP 723).
## Gating it to dev builds (recommended)
Keep production lean: flip `LV_USE_SNAPSHOT` on only under a build flag, and only call
`lvScreenshotAttachHttp` under the same flag, so normal builds are byte-identical and expose no
endpoint. See the DIYStickyNotes `[env:screenshot_*]` envs for an example.
## License
MIT (code). Screenshots you capture are your own content.