feat(screen): stream framebuffer to the client as FromRadio display_frame chunks

ScreenMirror snapshots the 1bpp OLEDDisplay buffer after each committed
frame (updateUiFrame), diffing against the last sent snapshot so only
changed frames stream. Armed via the new AdminMessage set_display_mirror
(continuous) / get_display_frame_request (one-shot); chunks drain through
PhoneAPI's STATE_SEND_PACKETS chain like XModem, so BLE, serial and TCP
clients all receive them. The lockdown path mirrors the LOCKED frame,
preserving display redaction. Excludable with MESHTASTIC_EXCLUDE_SCREEN_MIRROR.

Bumps the protobufs submodule to the DisplayFrame PoC commit and
regenerates nanopb sources (FromRadio_size stays 510 <= 512).
This commit is contained in:
James Rich committed 2026-11-03 07:39:17 -06:00
1 parent 11f8de309c
commit 74bf36e3d1
5 files changed
+210 -1

No files matched your search

+15 -1
View File
@@ -10,6 +10,7 @@
#include "SPILock.h"
#include "UptimeClock.h"
#include "gps/RTC.h"
#include "graphics/ScreenMirror.h"
#include "input/InputBroker.h"
#include "meshUtils.h"
#include <ErriezCRC32.h>
@@ -695,6 +696,18 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
handleSendInputEvent(r->send_input_event);
break;
}
#if HAS_SCREEN && !defined(MESHTASTIC_EXCLUDE_SCREEN_MIRROR)
case meshtastic_AdminMessage_get_display_frame_request_tag: {
LOG_INFO("Client requests display frame");
graphics::screenMirror.requestFrame();
break;
}
case meshtastic_AdminMessage_set_display_mirror_tag: {
LOG_INFO("Client sets display mirror: %d", r->set_display_mirror);
graphics::screenMirror.setMirror(r->set_display_mirror);
break;
}
#endif
#ifdef ARCH_PORTDUINO
case meshtastic_AdminMessage_exit_simulator_tag:
LOG_INFO("Exiting simulator");
@@ -2134,7 +2147,8 @@ bool AdminModule::messageIsRequest(const meshtastic_AdminMessage *r)
r->which_payload_variant == meshtastic_AdminMessage_get_ringtone_request_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_device_connection_status_request_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_node_remote_hardware_pins_request_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_ui_config_request_tag)
r->which_payload_variant == meshtastic_AdminMessage_get_ui_config_request_tag ||
r->which_payload_variant == meshtastic_AdminMessage_get_display_frame_request_tag)
return true;
else
return false;