librescoot-scootui (scootui-qt)¶
The original Flutter/Dart scootui is deprecated and replaced by this Qt/QML version.
Description¶
ScootUI is the primary user interface for Librescoot. It runs on the DBC (Dashboard Computer) and communicates with all Librescoot services via Redis.
- Qt 6 / QML — UI framework
- QMapLibre — Vector map rendering
- Valhalla — Routing engine
- hiredis — Redis client (async pub/sub + worker thread for polling)
- CMake — Build system
Features¶
Real-time Telemetry Display¶
- Speed (raw or wheel-corrected), power output, battery levels
- Odometer, trip meter, GPS status, connectivity indicators
- System warnings and fault codes
View Modes¶
- Cluster — Speedometer and vehicle status
- Map — 3D-tilted navigation with turn-by-turn directions
- Destination — Saved locations and destination input
- Navigation Setup — Route preview and confirmation
- OTA Update — System update progress and control
Navigation¶
- Offline vector map support (MBTiles via QMapLibre)
- Online map tiles (VersaTiles OSM) as alternative
- Valhalla routing (on-device or remote)
- Speed limit indicators from vector tiles
- Auto-rotating map with heading tracking
Other¶
- Alarm status display
- Hop-on / hop-off combo lock screen
- Light and dark themes with auto-switching
- Configurable blinker overlay styles
- Multi-language support
Build¶
Desktop (simulator mode)¶
Or manually:
cmake -B build -DDESKTOP_MODE=ON -DCMAKE_BUILD_TYPE=Debug
cmake --build build -j$(nproc)
SCOOTUI_REDIS_HOST=none ./build/bin/scootui
SCOOTUI_REDIS_HOST=none disables Redis and activates the built-in simulator.
Target (cross-compile for i.MX6 armhf)¶
Produces deploy-armhf/ with binary, Qt plugins, and launcher. Deploy:
Make shortcuts¶
Environment Variables¶
| Variable | Default | Description |
|---|---|---|
SCOOTUI_REDIS_HOST |
192.168.7.1 |
Redis host. none = simulator mode. Supports host:port. |
SCOOTUI_RESOLUTION |
480x480 |
Display resolution (WIDTHxHEIGHT) |
SCOOTUI_SETTINGS_PATH |
(none) | Path to persistent settings file |
Redis Operations¶
Subscriptions (pub/sub + periodic HGETALL)¶
| Channel | Store | Poll interval |
|---|---|---|
engine-ecu |
EngineStore | 200 ms |
vehicle |
VehicleStore, AutoStandbyStore | 500–1000 ms |
buttons |
VehicleStore, ShortcutMenuStore | event-driven |
battery:0, battery:1 |
BatteryStore | 30 s |
gps:tpv (pub/sub) + gps hash |
GpsStore | push + 5 s safety poll |
motion:heading (5 Hz) + motion:sensors (10 Hz) pub/sub + motion hash |
MotionStore | push + 5 s safety poll |
ble |
BluetoothStore | 5 s |
internet |
InternetStore | 5 s |
modem |
ModemStore | 5 s |
navigation |
NavigationStore | 5 s |
settings |
SettingsStore | 5 s |
ota |
OtaStore | 5 s |
usb |
UsbStore | 5 s |
speed-limit |
SpeedLimitStore | 5 s |
cb-battery |
CbBatteryStore | 30 s |
aux-battery |
AuxBatteryStore | 30 s |
dashboard |
DashboardStore | 500 ms |
Additionally polled (no subscription): system, version:mdb, version:dbc (30 s).
HSET Writes¶
| Hash | Field | Value | When |
|---|---|---|---|
dashboard |
ready |
true |
Startup and every Redis reconnect |
dashboard |
serial-number |
Hardware serial | Startup (if readable) |
dashboard |
backlight-enabled |
true/false |
On backlight control |
navigation |
latitude, longitude, address, timestamp, destination |
lat/lon (6 dp), address string, ISO-8601 UTC, lat,lon |
When the rider picks a destination in the UI (then publishes navigation = updated) |
settings |
dashboard.* |
user values | On settings changes via menu |
settings |
updates.{mdb,dbc}.channel, .method, .check-interval |
user values | Settings > System > Updates; channel only after confirmation |
usb |
mode |
normal/ums-by-dbc |
On USB mode change |
Note: scootui is not consume-only on the navigation hash. When the rider sets a destination in the UI, NavigationService writes the destination fields above and publishes navigation = updated; clearing the destination blanks the same fields and publishes navigation = cleared.
LPUSH Commands¶
| List | Commands | Written by |
|---|---|---|
scooter:blinker |
left, right, both, off |
Blinker/hazard controls |
scooter:hop-on |
engage, engage-learning, release |
HopOnStore |
scooter:update:{mdb,dbc} |
check-now, preview-channel:<channel> |
Settings > System > Updates |
HDEL¶
navigation destination,latitude,longitude,address,timestamp— on destination clear
LRANGE¶
usb:log(indices 0–19) — UMS transfer log, polled whileusb statusisprocessing
Settings¶
Settings are stored in the settings Redis hash. Managed by settings-service.
| Key | Values | Default | Description |
|---|---|---|---|
dashboard.show-raw-speed |
true/false |
false |
Raw ECU speed vs wheel-corrected |
dashboard.show-gps |
always/active-or-error/error/never |
error |
GPS icon visibility |
dashboard.show-bluetooth |
always/active-or-error/error/never |
active-or-error |
Bluetooth icon |
dashboard.show-cloud |
always/active-or-error/error/never |
active-or-error |
Cloud connection icon. Only shown when internet[unu-cloud] is present (a cloud client is running); hidden on de-clouded scooters |
dashboard.show-internet |
always/active-or-error/error/never |
active-or-error |
Cellular icon. active-or-error shows when internet[connectivity] is connected/disconnected/failed, hides on disabled/no-sim/denied |
dashboard.show-clock |
always/date-time/alternate/never |
always |
Clock format and visibility |
dashboard.theme |
light/dark/auto |
auto |
UI theme |
dashboard.blinker-style |
icon/overlay |
icon |
Blinker indicator style |
dashboard.language |
en, de, … |
en |
UI language |
dashboard.battery-display-mode |
percentage/range/icon |
percentage |
Battery display |
dashboard.power-display-mode |
kw/amps |
kw |
Power unit |
dashboard.mode |
speedometer/navigation/debug |
speedometer |
Default screen |
dashboard.map.type |
online/offline |
offline |
Map source |
dashboard.map.render-mode |
vector/raster |
vector |
Offline map rendering |
dashboard.map.traffic-overlay |
true/false |
false |
Traffic overlay (online only) |
dashboard.valhalla-url |
URL | http://127.0.0.1:8002/ |
Valhalla routing endpoint |
dashboard.maps.check-for-updates |
true/false |
false |
Auto-check for map updates |
dashboard.maps.auto-download |
true/false |
false |
Auto-download map updates |
dashboard.milestone-celebrations |
true/false |
false |
Confetti + banner when passing a 500 km odometer milestone or an easter-egg number. Off suppresses all milestone output (including easter eggs) |
dashboard.hop-on-combo |
pipe-delimited tokens | (empty) | Custom hop-on unlock combo |
Settings > System > Updates also writes the update-service keys. Each entry writes both components at once, so the two boards never end up on different channels:
| Key | Values | Description |
|---|---|---|
updates.{mdb,dbc}.channel |
stable/testing/nightly |
Release channel. Written only after the rider confirms on the channel-switch screen |
updates.{mdb,dbc}.method |
delta/full |
Update type |
updates.{mdb,dbc}.check-interval |
duration, 0 to disable |
How often to look for updates |
Neither of the two entries that write these applies on the tap that lands on it: Change Update Type and Switch Release Channel both open a list of checkable rows, because a single stray brake tap on a cycle would have turned the next update into a full-image download with no confirmation. Both rows carry their current value beside the chevron.
The Switch Release Channel entry does not apply a selection directly either. It first pushes
preview-channel:<channel> to both scooter:update:mdb and scooter:update:dbc, sums
the two ota[preview-size:*] answers, and shows the total on a confirm screen; only
confirming writes the setting and pushes check-now to both components. With no
connectivity there is nothing to preview and nothing that could download, so the same
screen instead explains that the target channel's .mender can be installed over
Update Mode. See update-service for the preview protocol.
When dashboard.theme is auto, AutoThemeService drives light/dark switching from the dashboard hash field brightness (lux). It listens on the dashboard pub/sub channel and also polls every 1 s, and switches with hysteresis (dark below 8 lux, light above 20 lux) after the reading has held past the threshold for 2.5 s, then locks out the reverse flip for 10 s.
Hardware Interfaces¶
Serial Number¶
Primary: /sys/devices/soc0/serial_number
Fallback (OTP fuses): /sys/fsl_otp/HW_OCOTP_CFG0 + HW_OCOTP_CFG1
Boot Animation¶
On Linux startup:
- Fades in framebuffer overlay:
imx-overlay-alpha fade 0 255 1000(skipped on kernel 6.6 imx-drm) - Stops boot animation:
systemctl stop boot-animation.service
Shutdown¶
On SIGTERM or vehicle state → shutting-down: ShutdownStore::forceBlackout() blacks out the display.
Map Tiles¶
Offline tiles expected at /data/maps/map.mbtiles on the DBC. The app watches /data/maps/ via inotify and reloads map services when the file appears or changes.
What is installed is recorded in /data/maps/metadata.json (region slug, per-artifact SHA-256, upstream publication time, last update check) and mirrored into the maps Redis hash on the MDB. scootui-qt republishes the hash on every Redis reconnect, because the MDB's valkey keeps nothing across a reboot. The same values back the System > Info > Maps screen, so the dashboard and an MDB-side consumer cannot report different things.
Routing tiles live separately at /data/valhalla/tiles.tar, which Valhalla mmaps as its tile_extract and which therefore has to stay an uncompressed seekable tar.
MapDownloadService fetches both from the manifest at downloads.librescoot.org/releases/tiles.json, keyed by region slug. Each region's valhalla entry may carry an optional nested compressed object (codec, url, size, sha256); when it is present and the codec is zstd, the service downloads that instead, verifies its SHA256, and decompresses it into place at install. The parent url, size and sha256 continue to describe the uncompressed tar, so a client that predates the compressed asset keeps working unchanged. Downloads resume via HTTP Range against the artifact actually being fetched; the compressed variant uses its own .part file.
The .mbtiles is not compressed for transport. Its vector tiles are already gzipped per tile, so the archive only compresses by about 1.14x.
Project Structure¶
src/
core/ App initialization, environment config
models/ Data models and enums
repositories/ Redis (hiredis-based) and in-memory MDB implementations
stores/ QML-exposed state stores
services/ System services (navigation, settings, map, input, ...)
routing/ Valhalla client and route models
simulator/ Simulator service (desktop mode only)
l10n/ Translations
qml/
screens/ Main UI screens
widgets/ Reusable UI components
overlays/ Modal overlays (menu, toast, ...)
simulator/ Simulator panel (desktop mode only)
Dependencies¶
- Redis server
- Qt 6.4+ (Quick, Qml, Svg, Network, Sql, Concurrent)
- QMapLibre (required on target; optional in desktop mode)
- hiredis
- Valhalla routing engine
Related Documentation¶
- dashboard/README.md — Complete dashboard documentation
- dashboard/REDIS.md — Redis operations reference
- Redis Operations — Dashboard hash fields
- States — Vehicle state display