0.10.0
Pre-release
Pre-release
CANShift v0.10.0
A UX-focused release: the device finally reads at a glance. Per-sensor colours, real units everywhere, smaller decimals, a diag drawer that actually closes, and a top bar that hides what it doesn't know yet. The Studio canvas now mirrors the firmware pixel-by-pixel for everything we ship as defaults.
Big internal cleanups too: warningLevel and dangerLevel are now a single threshold, the demo dashboard ships with the sensor palette wired up, and a Rust spike landed for ota_hmac (phase 3 — staticlib linked into PlatformIO without growing flash).
Highlights
- Semantic per-sensor palette. Each metric draws in its native colour (coolant blue, oil pressure green, boost violet, AFR magenta, RPM cyan, throttle orange, IAT light blue, …) below the threshold, warning red above. Two zones only — the old three-zone tinting is gone.
- Units everywhere, by default. Every gauge / bar / numeric widget shows its unit (
°C,km/h,bar,kPa,V,AFR,%) without per-widget configuration. Pulled from the bound signal'sunitfield insignals.json;cfg.suffixstill wins when set manually. - Smaller fractional digits on AFR / voltage / lambda / pressure readouts — the integer part stays headline-size, the
.X/.XXrenders at ~70 %. Arc gauges + numeric widgets, firmware and Studio match. - Diag drawer rebuild. Full-screen panel, swipe-up to open from anywhere, big visible X to close (no longer shares coordinates with the top bar's day/night toggle), scrollable content, ECU flag dots now visibly circular instead of squarish.
- Top bar de-clutter. Signal slots with no live reading hide themselves instead of showing a misleading
--.-placeholder. Mode-flag badges (ALS, LC, FS, TC, MAP) gained a hidden-on-startup state so they only appear once the ECU asserts them. - Theme toggle survives page rebuilds. The day / night icon used to vanish into a fragmented heap after a few toggles; LVGL image cache doubled to 24 entries, theme icons get re-warmed before every rebuild, the post-rebuild FS-open guard relaxed (with graceful fallback if the pool is still starved).
- Error bar — visible X + no-reboot dismiss. Hit target widened, glyph swapped to uppercase, dismiss path guarded against the low-heap LVGL allocations that previously triggered the assert handler on press.
Firmware
- Demo dashboard migrated: 4 pages (overview / engine / fluids / controls), every gauge opts into the sensor palette via
iconName, controls page slimmed to 4 buttons (MAP1, MAP2, Launch, Anti-lag) with strong on/off contrast. - Numeric widget value re-centered (was anchored at the bottom of the cell post flex-row refactor).
- Engine page bottom row split: TPS numeric (160×56) + Battery numeric (160×56) instead of a single full-width horizontal bar.
- Overview ↔ engine: Gear moved to engine (xl), IAT moved to overview (l).
--placeholder replaces em-dash (no moreU+2014glyph spam in the serial log).- Lambda unit renamed from
λtoAFR(Orbitron doesn't carry the Greek small letter lambda). - Per-row dismiss on the error drawer with stable newest-first row→index mapping.
- Optimistic SignalStore write on toggle-button click — the diag-drawer flag turns red on tap even without a CAN echo (then reconciles when the ECU answers).
- Top bar mode-flag badges (ALS / LC / FS / TC / MAP slot) — visible only while the bound signal is high; adjacent separators auto-hide so the bar stays tight.
- Orbitron 28 / 48 px fonts restored.
- BLE write size capped before JSON parse (prevents the UI thread from chewing oversize payloads).
- USB JSON snprintf paths detect truncation and surface a warn line.
- BLE GATT writes detect serializeJson truncation.
- PUT_FILE path validation flipped from blacklist to strict allowlist.
- Concurrent lazy page-build requests now coalesce instead of double-allocating.
- TWAI driver tick guarded when uninstalled; init retries on heap recovery.
- Boot-time log line
DIAG_DRAWER init done — z-reaffirm + scroll + 12px badges + frac digits buildso you can confirm at a glance which firmware is on the chip.
Studio
- Canvas preview keeps per-widget colours instead of collapsing them to the page palette — finally matches the rail thumbnails.
- Default unit overlay on numeric widgets (small grey, right of the value, baseline-aligned) and on arc gauges (centred below). Falls back to the built-in MaxxECU catalog when the local signal store is empty, so units appear on first launch without picking a profile.
- Smaller fractional digits in the Canvas preview, matching the firmware.
- Signal binding in the property panel is now a Radix Select dropdown listing every loaded signal as
signal_name — unit. Picking a signal auto-applies suffix / min / max / dangerLevel from the catalog. - Horizontal bar gauge orientation removed from the bar-gauge picker (the layout didn't read well; only vertical remains).
defaultSimConfig.tsmirrors the new firmware demo (palette + controls page + Gear/IAT swap + TPS+Battery split).- Config validation on File → Open and session restore paths.
- CAN-frame flush interval cleared on shutdown.
- IPC: every USB connection transition now published to the renderer (no more stale connected indicators).
- Detached CLI window registers
setWindowOpenHandlerso external links don't open silently. mainWindowref nulled out in theclosedhandler — fixes a latent dangling-pointer warning.
Core (@tmbk/canshift-core)
SensorPaletteschema + per-sensor OK / warning colour table (shared by firmware + Studio + mobile).- Threshold collapse:
warningLeveldropped,dangerLevelis now the sole numeric threshold across gauge / bar configs. Migration runner upgrades existing dashboards. - New 1.15 → 1.16 → 1.17 migration steps;
CURRENT_SCHEMA_VERSIONbumped accordingly. - Schema fixture test confirms the bundled
dashboard.jsonvalidates clean against the catalog. - Signal protocol id renamed
maxxecu_v1.2→ genericcustom_v1.0so the open-source repo stops mentioning a specific brand by default. - Export the
ButtonActiondiscriminant tuple + type guards for downstream consumers. LIGHT_TOKENSmade internal until the light-theme consumer lands.
Mobile
DashTopBarsim / BLE mode now reactive via the Zustand store (no more stale toggle on app resume).
Rust spike (firmware)
ota_hmacported to Rust in three phases: host parity tests → C ABI bridge → staticlib linked into PlatformIO.- Δ flash −16 B once weak
memcpy/memmove/memset/memcmp/bcmpsymbols in the Rust library are localized so ESP-IDF's strong IRAM versions win at link time (fixes a boot-timeEXCCAUSE 7we hit while validating). - Behind the
USE_RUST_OTA_HMACbuild flag — the C++ implementation stays the default until the on-device 1 h soak passes.
CI / chores
- Sanitizers, clang-tidy, and the firmware boot-smoke (QEMU) checks gating every PR.
--depth=1dropped from the base-ref refetch so merge bases stay reachable on long-lived branches.- Documentation pass to make the repo ECU-agnostic — MaxxECU / VR6 specifics moved into examples, FIRST_FLASH translated to English.
Known limitations
- Heap fragmentation after several rapid day/night toggles can still trip the LVGL FS-open guard on icon decodes. Mitigations are in place (preload before and after rebuild, cache size doubled, heap floor relaxed) but the deeper SPIFFS-driver fix is tracked in #895.
- A USB task watchdog reboot path was observed once after touch calibration on a heap-starved boot. Tracked in #976.
- Disconnecting the device in Studio immediately auto-reconnects within 2 s — there's no manual-disconnect flag yet. Tracked in #977.
- The Studio Canvas day / night toggle preview drifts from the firmware render in a few places. Tracked in #957.
Upgrade notes
- Flash both binary AND SPIFFS for the new dashboard / signals to take effect:
pio run -e crowpanel_28 -t upload pio run -e crowpanel_28 -t uploadfs - After flash, the serial log should show
DIAG_DRAWER init done — z-reaffirm + scroll + 12px badges + frac digits buildat boot. Grep for it to confirm the new firmware is on the device. - Existing dashboards are auto-migrated to the 1.17 schema on first load;
warningLevelis dropped anddangerLevelbecomes the single threshold. Re-save in Studio to persist.