MT-LB DVE · Periscope board · flex_kneron_ui — Porting & implementation plan
-Dlistener_ENABLE_PLATFORM_MQTT=ON and LOCAL_PROVIDER=MQTT.
Required CMake quirks: FETCHCONTENT_TRY_FIND_PACKAGE_MODE=NEVER, paho_mqtt_glue forced FetchContent.
Explicit -lpaho_mqtt_glue in LDFLAGS. UDP broadcast blocked on Periscope (no eth0).
MQTT on lo:1883 works via mosquitto with allow_anonymous true.LOG_D/LOG_I/LOG_E macros, ut::result<T>, type aliases (i8/u32/f32…),
VERIFY macros. Built for AArch64 with KL730 toolchain. No ARS45 dependency.S20network init script brings up lo (was down at boot on Periscope).
Mosquitto config: listener 1883 127.0.0.1, allow_anonymous true.
Priority ordering: S20network → S50mosquitto → S99bgs → S98battery → S99flex_kneron_ui.GLA_RenderContext (15 function pointers in render_api.h) — that the widget tree
calls to draw rectangles, bitmaps, and text, without knowing anything about the underlying
hardware. On ARS45 it is backed by the ARS45 HAL (not portable). For KL730 a completely new
backend was written from scratch inside universalguilibrary/lib/kl730_renderer/.GLA_RenderContext hooks for KL730.
Key path: end_frame() reads the RGBA working buffer (1920×1200) that the widget tree drew into,
converts it to YUV420 using NEON AArch64 intrinsics (BT.601 narrow-range),
rotates 90° CW (landscape→portrait 1200×1920 for the FT8201P panel),
composites each pixel against the live camera frame (alpha=0 → camera, alpha=255 → GUI),
then calls VMF_VDISP_ProcessOneFrame() to push the result to the display via VOC0.kl730_backend_init() allocates the RGBA working buffer via MemBroker_GetMemory()
(4096-byte aligned, physically contiguous DMA memory).
canvas.bind_rgba(kl730_backend_get_rgba_buf(), ...) makes the Canvas write directly into
that buffer — the widget tree draws into DMA memory, so end_frame() reads it
with no copy. On HOST_MOCK the Canvas uses a plain heap buffer instead._isp_0 pin never initialised — hardware path abandoned.
Rotation is done in end_frame() as a transposed scatter-write loop.
After that, the IMX678 sensor was delivering a landscape-flipped image (row-mirrored)
as a side-effect of the failed ISP_MODE_RT attempt.
Fix: camera row index = lr (landscape row, sequential) instead of
pc = GUI_H−1−lr (portrait column, reversed). Column unchanged.
Both Y and UV planes corrected. Verified working on device./dev/input.
BGS maps: F1→BUTTON_UP, F2→BUTTON_DOWN, F3→BUTTON_SET, F4→BUTTON_BACK.
Build flag: ATN_DEVICE_TYPE=KL730_PERISCOPE → ATN_DEVICE_IS_PERISCOPE=1.
No MENU button on hardware — F4/BACK opens menu when menu is closed.lstr::create_local_listener() subscribes to BUTTON_PRESSED / BUTTON_LONG_PRESSED.
Thread-safe queue: mutex + vector, swapped atomically each render tick.
INIT_READY_BROADCAST sent before render loop to unblock BGS.SimpleBatteryController (SARADC-based, absent on Periscope).
Bq27546 reads sysfs: /sys/class/power_supply/bq27546-0/ (I2C-0, slot 1),
/sys/class/power_supply/bq27546-1/ (I2C-2, slot 2). No raw I2C — kernel driver owns it.
Build flag: -DBATTERY_BQ27546=ON.capacity, voltage_now, current_now, present, time_to_empty_now.
Check MQTT publishes BATTERY_LEVEL {"bat_level1": X, "bat_level2": Y}.
Verify InfobarWidget battery icons update in flex_kneron_ui.EPS_STATUS on MQTT when charge state changes.
Guard all GPIO paths with #ifndef BATTERY_BQ27546 (GPIO 31/55 absent on Periscope).gui_vsrc_ifp_0.
DMA_2D copy to MemBroker double-buffer (4096-byte aligned). Atomic lock-free index flip.ZOOM_SET=<factor> to pipeline.
DayNightController: Auto/Day/Night modes, sends DAY_NIGHT_SET=<mode>.
Both fully wired through PipelineClient TCP 9001.EXPOSURE_SET=brightness=<v> etc. to pipeline daemon.
Pipeline-side ISP parameter mapping to define. Persistence to /mnt/flash/prefs.xml.
Menu carousel values need range confirmation with imaging team.date -s + hwclock -w, or time-picker widget.
LANGUAGE: hot-reload LanguageManager with new EN/RU/etc XML, redraw all widgets.
RESTORE_DEFAULTS: wipe /mnt/flash/ (non-flash-mapped files), reboot.
RANGE_UNITS: m/yd preference stored in prefs.xml.#ifndef BATTERY_BQ27546. Sleep = GPIO16 display-off or DPMS.
Wake on any button press. Optional IMU shake-wake if IMU available./mnt/flash/prefs.xml. Publishes brightness on MQTT for other services.
First step: confirm backlight control path in FT8201P/DW-MIPI-DSI driver.AttitudeController (SHOW_PITCH_ROLL tab) — not yet implemented.
Need to identify IMU chip on Periscope hardware (check schematics in documentation/).
Port position-service or read IMU sysfs directly (iio subsystem).
PitchRollWidget: horizon line + angle overlay on camera feed.
SET_LEVEL = calibrate IMU level reference, persist offset to prefs.xml.TrackProjectionController (VEHICLE_TRACKS tab) — not implemented.
Source of track data to clarify (CAN bus? external TCP service?).
Renders projected track lines as RGBA overlay on Canvas.
ALIGN_PROJECTIONS event: manual calibration procedure.
This is KL730-specific — no direct Artosyn equivalent.draw_text() passes raw bytes to FT_Load_Char — non-ASCII (Cyrillic, etc.) renders wrong.
Add UTF-8 decode loop (codepoint extraction) before FT_Load_Char.
Required for language switching beyond ASCII.is_dirty() flag to IWidget; skip render() call if not dirty.
Only composited output pushed to VDISP if canvas changed.
~30% CPU savings estimated for static infobar frames.CONFIG_ENV_OFFSET_REDUND=0x150000, CONFIG_ENV_RANGE=0x40000 to defconfigs.
Critical for shipping: loss of day_night_default / zoom_default degrades UX.SCHED_FIFO priority to prevent jitter
from other processes during compositing. Requires CAP_SYS_NICE or run as root.
Test: verify 30fps cadence stable under background system load.shutdown -h now.
Previously deferred — need to revisit priority./proc/<pid>/status VmRSS).
MQTT: BGS buttons all respond. Both battery slots read. Charging icon updates on power connect.
Menu: all tabs open, carousel items cycle, values persist across reboot.
Recording: start/stop, file appears on SD, REC blinks. Snapshot: file saved, feedback shown.