mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-11-01 10:21:59 +00:00
feat(l2): screenshots over USB (RGB565); USB tools moved to the Solo site
- CMD_GET_SCREENSHOT on colour screens: display type 2 (RGB565 LE), 32-bit payload size; ui-lvgl captureFrame() shared with the SD BMP - ENABLE_SCREENSHOT in the L2 build; tools/screenshot.py decodes type 2 - docs and READMEs point to solo.marekzegarek.com/#pc-tools - USB popup: "Keep card" instead of "Charge only" (only the card is lent; charging and USB serial work either way) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+5
-3
@@ -13,9 +13,9 @@ Python dependencies are managed with [uv](https://docs.astral.sh/uv/) — run th
|
||||
scripts with `uv run tools/<script>.py` from the repository root.
|
||||
|
||||
> [!TIP]
|
||||
> The hosted [Solo Tools Web App](https://marekzegare4.github.io/Solo-tools/)
|
||||
> does both screenshots and GPX export with nothing to install, and is the
|
||||
> easiest option for most people. The scripts here are the offline equivalents.
|
||||
> The [USB tools on the Solo site](https://solo.marekzegarek.com/#pc-tools) do both screenshots and GPX
|
||||
> export with nothing to install (Chrome or Edge), and are the easiest option
|
||||
> for most people. The scripts here are the offline equivalents.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> USB serial is suspended while a BLE connection is active, so disconnect the
|
||||
@@ -57,6 +57,8 @@ PLATFORMIO_BUILD_FLAGS="-D ENABLE_SCREENSHOT" pio run -e <env> -t upload
|
||||
- The device replies with `RESP_CODE_SCREENSHOT` (29) carrying the framebuffer
|
||||
- The framebuffer arrives in chunks (a 128 × 64 display is 1024 bytes, split
|
||||
across several frames), which the tool reassembles into a PNG
|
||||
- A colour screen (Wio Tracker L2) sends a freshly rendered frame as RGB565
|
||||
(display type 2): 320 × 240 is 150 KB, about a second over USB
|
||||
|
||||
---
|
||||
|
||||
|
||||
+12
-2
@@ -30,6 +30,7 @@ RESP_CODE_ERR = 1
|
||||
# display_type values (byte [5] in response frame)
|
||||
DISPLAY_TYPE_OLED = 0 # page-based, column-major (SH1106/SSD1306)
|
||||
DISPLAY_TYPE_EINK = 1 # row-major, MSB-first, 1=white/0=black (GxEPD2)
|
||||
DISPLAY_TYPE_RGB565 = 2 # row-major, 16-bit little-endian (colour screens, e.g. Wio Tracker L2)
|
||||
|
||||
# No panel-specific constants needed — the firmware now sends GxEPD2's own reported
|
||||
# dimensions (which use WIDTH_VISIBLE, not the full physical WIDTH), so the header
|
||||
@@ -114,6 +115,8 @@ def _u16le(buf, off):
|
||||
|
||||
def _expected_buffer_size(width, height, display_type):
|
||||
"""Bytes the firmware should send for given dims+type."""
|
||||
if display_type == DISPLAY_TYPE_RGB565:
|
||||
return width * height * 2
|
||||
if display_type == DISPLAY_TYPE_EINK:
|
||||
# GxEPD2 buffer is in physical panel coordinates: row-major,
|
||||
# stride = ceil(WIDTH_VISIBLE / 8) bytes, HEIGHT rows.
|
||||
@@ -285,9 +288,16 @@ def eink_buffer_to_image(buffer, log_width, log_height, rotation):
|
||||
return image
|
||||
|
||||
|
||||
def rgb565_buffer_to_image(buffer, width, height):
|
||||
"""Colour screen: RGB565 little-endian, row-major."""
|
||||
return Image.frombuffer("RGB", (width, height), bytes(buffer), "raw", "BGR;16", 0, 1)
|
||||
|
||||
|
||||
def buffer_to_image(buffer, width, height, display_type, rotation=0, scale=1):
|
||||
"""Convert framebuffer to PIL Image, optionally upscaled."""
|
||||
if display_type == DISPLAY_TYPE_EINK:
|
||||
if display_type == DISPLAY_TYPE_RGB565:
|
||||
image_rgb = rgb565_buffer_to_image(buffer, width, height)
|
||||
elif display_type == DISPLAY_TYPE_EINK:
|
||||
image_rgb = eink_buffer_to_image(buffer, width, height, rotation)
|
||||
else:
|
||||
image_rgb = oled_buffer_to_image(buffer, width, height)
|
||||
@@ -357,7 +367,7 @@ def main():
|
||||
result = receive_screenshot(ser)
|
||||
if result:
|
||||
buffer_data, width, height, display_type, disp_rotation = result
|
||||
type_str = "e-ink" if display_type == DISPLAY_TYPE_EINK else "OLED"
|
||||
type_str = {DISPLAY_TYPE_EINK: "e-ink", DISPLAY_TYPE_RGB565: "colour"}.get(display_type, "OLED")
|
||||
print(
|
||||
f"Received framebuffer: {width}x{height} {type_str} rot={disp_rotation}, {len(buffer_data)} bytes"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user