Commit Graph
113 Commits
Author SHA1 Message Date
liquidraver 109ad4ef1e eink full redraw after 8 partial to avoid ghosting 2026-06-15 10:52:25 +02:00
liquidraver 5fc8fd1ead shared canonical path for UI adverts 2026-06-14 22:25:32 +02:00
liquidraver 061a65a7f0 cli commands fix 2026-06-12 21:07:37 +02:00
liquidraver d4a48ae5f3 joystick UI: proper accents, emojis stripped, new unlock mechanism 2026-06-11 20:55:56 +02:00
liquidraver cc8ade8298 bound AdvertDataParser name copy to _name size (helpers audit) 2026-06-11 14:18:15 +02:00
liquidraver 0ce847b9bb small fixes 2026-06-11 07:32:50 +02:00
liquidraver a7642cc3e2 3 consecutive battery reading before shutdown (3 hits / 90 s) 2026-06-09 14:50:11 +02:00
liquidraver 5564dcbde0 fix RTC and time display 2026-06-09 14:46:30 +02:00
liquidraver 2ad185bab4 license "refactor" :) 2026-06-07 22:47:17 +02:00
liquidraver 2cf4b9704e BT DFU fixes + power bump up 2026-06-07 22:33:50 +02:00
liquidraver 37edf303a2 fix MAX_ANON_CONTACTS 2026-06-07 12:51:31 +02:00
liquidraver 01fd56a573 wire up RTC to out-of-the-box capable nodes 2026-06-07 12:00:05 +02:00
liquidraver ffe9ea652d sync to vanilla 1.16 2026-06-06 16:42:39 +02:00
liquidraver f508c14dbb display improvements 2026-06-05 14:56:11 +02:00
liquidraver 28a8a6999d nrf companion auto-shutdown 2026-06-05 09:27:24 +02:00
liquidraver eda46166b4 companion CLI 2026-06-04 22:43:01 +02:00
liquidraver d3ebb2f172 adc tuning improvements 2026-06-04 13:30:51 +02:00
liquidraver d37b401033 battery: centralized LiPo OCV curve + ADC multiplier corrections
Add a board-aware battery SOC system replacing the two independent
linear approximations that existed in the UI helpers.

New helpers/battery_curve.{h,c}: a 21-point (5% step) OCV lookup table
with integer linear interpolation. The default generic LiPo curve is a
weak symbol — any board can override it by dropping a battery_curve.c
into its board directory. CMakeLists.txt selects the board-specific file
when present, falling back to the generic.

Board.h gains getBattPercent() (default 0); ZephyrBoard implements it
via battery_curve_lookup().

Board-specific curves added for boards with measured cell data:
t1000_e, rak_wismesh_tag, thinknode_m6, sensecap_solar, wio_tracker_l1.

ADC multiplier corrections applied across all nRF52840 boards:
- Boards using a correctly-derived 3600×ratio formula get +0.5% to
  compensate for nRF SAADC gain error (7200→7236, 6300→6332, etc.)
- rak_wismesh_tag, rak3401_1watt, gat562_30s had multipliers copied
  from Arduino's 3.0V AREF formula; corrected to 3600×1.73×1.005=6259
- xiao_nrf52840 (10911) left unchanged — empirically calibrated value
  above the theoretical, assumed already correct for that hardware

get adc.multiplier now reports current mV reading and the board's
curve 100% target, making field calibration self-guiding.
2026-06-03 13:21:06 +02:00
liquidraver 99e05e3b03 sync with vanilla dev 2026-06-02 14:50:18 +02:00
liquidraver ab89cdd6d4 fix(ui): resolve channel by name for the Unread-screen reply
Channel replies from the Unread screen passed index -1, which
sendComposedMessage can't route, so they silently failed to send. Look
up the real channel slot by name and bail if it can't be resolved.
2026-05-31 15:36:35 +02:00
liquidraver ff705b0d5d fix: unbreak the production (CONFIG_LOG=n) build
553b71c made production the default but broke configure+link:
- ZEPHCORE_COMPANION_USB sat inside the role choice (recursive dep)
- CONFIG_RESET_ON_FATAL_ERROR was an undefined, unimplemented symbol
- USB companion sources still gated on CONFIG_LOG, not ZEPHCORE_USB_STACK

Move the USB toggle out of the choice, add a real
ZEPHCORE_RESET_ON_FATAL_ERROR Kconfig + k_sys_fatal_error_handler that
cold-reboots, and match the CMake USB guard to ZEPHCORE_USB_STACK.
2026-05-31 15:36:10 +02:00
liquidraver b7e8ab580d simplify BaseChatMesh: extract shared send dispatch tail 2026-05-29 13:00:10 +02:00
liquidraver 94dcf61715 simplify app layer: dedup response/telemetry/JSON builders
- CompanionMesh: sendPacketSent() helper, collapse sendFloodScoped
  overloads, share self-telemetry LPP builder
- Mesh: shared computeAdaptive{Flood,Direct}Delay (was duplicated in
  Companion + Repeater)
- ObserverMesh/RepeaterMesh: shared helpers/MeshcoreJson.h builders;
  drop dead sign_input_len
2026-05-29 12:54:52 +02:00
liquidraver 515f3610e1 crypto: harden first-boot identity entropy + 3 RNG fixes
Adds a layered entropy mixer for first-boot identity Ed25519 keygen,
primarily to address ESP32 where the hardware TRNG (WDEV_RND_REG) is
only fed real entropy once the internal WiFi/BT radio is enabled —
but identity gen runs before that on companion and indefinitely
before that on a bare repeater. ESP-IDF's bootloader_random_enable()
is not compiled by Zephyr-Espressif HAL, ruling out that workaround.
Design reviewed with nextgens (author of upstream meshcore-dev/
MeshCore#2280 which fixes the same issue via BT/WiFi init/pull/deinit).

ZephyrRNG::random — retry sys_csrand_get up to 4x with k_msleep
backoff; cold-reboot on persistent failure. Previously fell back
silently to sys_rand_get (xoshiro PRNG), which would have produced
a weak Ed25519 seed on CSPRNG error. BUILD_ASSERT enforces
CONFIG_CSPRNG_ENABLED.

ZephyrRNG::mixIdentitySeed — layered entropy mixer for one-shot
identity keygen. Combines sys_csrand_get (early + late),
HWINFO unique device ID, caller-supplied ADC LSB noise, 200ms of
CPU cycle-counter jitter (NIST SP 800-90B class source), and
50ms more jitter in an independent timing window. Conditioned via
AES-256-CTR (NIST SP 800-108 KDF-in-Counter-Mode): SHA-256 of the
pool extracts a 32-byte AES key; AES-256-ECB on an incrementing
128-bit counter expands to the requested output length. Uses PSA
crypto already enabled in zephcore_common.conf. NIST-style
repetition-count + variance health check on jitter samples;
reboot on degenerate output. ~280ms one-time cost at first boot.
LoRa radio TRNG was considered as an additional source but rejected
on expert advice — radio sources are attacker-influenceable
(jamming/spoofing).

ui-joystick BLE passkey — switch from sys_rand32_get (non-crypto
xoshiro) to sys_csrand_get. The 6-digit passkey is the MITM
protection the rest of the BLE config enforces; predictable PINs
weaken it.

Identity reserved-prefix loop — replace the silent 10-attempt cap
(which committed whatever it had on fall-through) with a
bounded-retry-then-reboot pattern.

Also: fix a pre-existing scope bug at main_companion.cpp:357 in
the MESH_EVENT_PREFS_DIRTY handler — data_store was referenced
inside mesh_event_loop() but declared 50+ lines later. Moved the
call into a forward-declared helper defined after the statics.
Unrelated to crypto work but uncovered during build verification;
every companion build was broken.
2026-05-28 09:34:43 +02:00
liquidraver 1b47987057 extract shutdown to it's own helper 2026-05-27 12:31:58 +02:00
liquidraver 99279fd9fa sync with vanilla dev 2026-05-24 21:07:38 +02:00
liquidraver 051adef93e native linux initial commit 2026-05-24 20:06:49 +02:00
liquidraver bdb03145c3 joystick UI: drop dead do_render label
The wake-path goto was replaced with _pending_render + continue
during the loop refactor; the label became unreachable. gcc
flagged it with -Wunused-label.
2026-05-22 15:02:47 +02:00
liquidraver 6b624a69e5 sync with vnailla dev 2026-05-22 14:42:12 +02:00
liquidraver 895bbbc633 joystick UI:
Coalesce joystick scroll input and throttle OLED redraws so fast list navigation doesn't stall on one I2C blit per key.
2026-05-22 12:00:32 +02:00
liquidraver 094ea6e8a3 joystick UI:
- track real RTC sync source on the joystick Time screen
- gate joystick-only UI helpers behind stub headers so non-joystick builds skip the extra code without #ifdef at every call site.
2026-05-22 11:38:51 +02:00
liquidraver 322460fa78 joystick UI:
make UI show actual time source
2026-05-22 11:29:25 +02:00
liquidraver 1563658a68 joystick UI: add Path hash bytes setting (System → Device)
prefs.path_hash_mode already exists end-to-end (NodePrefs field,
ZephyrDataStore persists it, CompanionMesh::sendFlood reads it as
the path_hash_size for every outbound flood, and the phone protocol
exposes it). Add a joystick UI control so it can be set locally
without going through the phone app.

System → Device gets a new "Path hash: Nb" item; ENTER cycles
1 → 2 → 3 → 1 (path_hash_mode 0 → 1 → 2 → 0). Save goes through
the existing mesh_save_* deferred-write infrastructure
(UI_ACTION_PATH_HASH_MODE_SAVE, pending_path_hash_mode atomic),
handled in the mesh thread with savePrefs().
2026-05-22 09:24:38 +02:00
liquidraver 072b4edadb new level! :) 2026-05-22 09:11:32 +02:00
liquidraver 516536db1c joystick UI: "Reset path" now actually forces flood
The contact submenu's reset-path action set out_path_len = 0, which
means "direct, 0 hops" — formatHopCount rendered the contact as
"direct" and sendMessage would still try direct send. Use
OUT_PATH_UNKNOWN (0xFF) so the next DM floods and rediscovers the
path. Also schedule a contacts flush (markContactsDirtyPublic) so
the reset survives reboot — previously the change was in-memory only.
2026-05-21 22:34:10 +02:00
liquidraver e778326ad0 joystick UI: handle SENT_HEARD/UNHEARD in buildChannelReplyPrefix
After Phase J, the joystick UI rewrites a sent channel message's
path_len from OUT_PATH_SENT (0xFE) to OUT_PATH_SENT_HEARD (0xFD) or
OUT_PATH_SENT_UNHEARD (0xFC) once the feedback window resolves.
buildChannelReplyPrefix only excluded OUT_PATH_SENT, so a reply to
one of the user's own messages (post-feedback) would treat it as
incoming and build a garbled "@[<random body>]" prefix.

Also cleans up an awkward `class ContentionTracker&` qualifier in
Mesh.h.
2026-05-21 22:31:51 +02:00
liquidraver 03fcc60fa9 joystick UI: channel-send heard-repeat feedback
After broadcasting a group message, wait 5s to see if any neighbor
repeated the flood and use the outcome to mark the on-device entry
and the BLE-app mirror.

  - ContentionTracker gets extractDupeCount(hash): finds the tracked
    entry, captures dupe_count, finalizes (folds into EMA, marks
    inactive), returns the count or -1.
  - BaseChatMesh::sendGroupMessage gains an optional out_hash param;
    when set, the FNV-1a packet hash is also pre-registered with the
    contention tracker so heard retransmits get counted (originated
    floods weren't tracked before, only relays).
  - Mesh::getContentionTracker() promoted to public so the UI can
    query after the feedback window.

JoystickUITask grows a 4-slot pending-channel table with per-slot
k_timer (5s one-shot). startPendingChannel() broadcasts, adds the
local _ch_previews entry with path_len = OUT_PATH_SENT, and starts
the feedback timer. The timer ISR sets a feedback_due flag; the
loop's processPendingChannelFeedback() picks it up, calls
extractDupeCount(), and rewrites the preview's path_len to
OUT_PATH_SENT_HEARD (0xFD) or OUT_PATH_SENT_UNHEARD (0xFC) — which
formatHopCount renders as "sent+" / "sent?".

The deferred BLE-app mirror queues only on outcome with body prefix
"(>>✓) " (heard) or "(>>✗) " (not heard). queueLocalSentChannelMessage
gains a heard_repeat parameter for the selection.

Both channel send entry points (sendComposedMessage's channel branch
and sendChannelMessage) now route through startPendingChannel().
2026-05-21 22:24:26 +02:00
liquidraver 8e5e54a61e joystick UI: DM retry, force-flood fallback, BLE mirror on outcome
Outgoing DMs now go through a 4-slot pending-send table with per-slot
one-shot k_timer. On no-ACK the message retries (up to 5 attempts,
0-indexed); attempt 4 clears recipient.out_path_len + markContactsDirty
so the last try forces flood and future DMs re-discover the path.

ACK dispatch: CompanionMesh::processAck tries _ack_table first
(phone-initiated sends), then ui_joystick_try_match_ack() for joystick-
initiated sends, then falls through to connection-keepalive ACKs.

BLE-app mirror is now deferred until outcome is known. The body prefix
in the offline-queue frame is "(>>✓) " on delivery or "(>>✗) " on
failure, replacing the previous unconditional "(>>) ". UnreadScreen's
sent-entry origin prefix is updated in place via markSentEntryStatus()
to "(>>+) " / "(>>X) " (ASCII for the OLED font).

queueLocalSentContactMessage gains a 'delivered' parameter to pick the
prefix; markContactsDirtyPublic() exposes the lazy-write trigger so
the joystick's path-clear persists.
2026-05-21 22:09:56 +02:00
liquidraver 681ade41fa GPS UI screen changes
-open the screen with no fix → "Lat | Lon" placeholder;
  first fix arrives → lat/lon for 4 s → altitude for 4 s → repeat;
  lose fix → state resets, next fix starts fresh on lat/lon.
2026-05-21 22:00:27 +02:00
Steve Calvário 5caf53a496 Add GPS altitude support, add channel “reply to” targeting, fix snake wall collisions, fix unread navigation incorrectly returning to home, and fix BLE continuing to advertise after being disabled 2026-05-21 17:09:13 +01:00
liquidraver 0081e7af2b joystick UI: share the button-UI splash wordmark
Move the 128×13 zephcore_logo bitmap from ui-button/ui_pages.c into
the shared ui_common.c (linkage extern), with the dimensions and
declaration in display.h. Both UI splash renders now share the same
data — no duplicated array.

Joystick SplashScreen::render() now draws the wordmark at the top,
"MeshCore on Zephyr" beneath it, and the build date below — matching
the button-UI layout. Replaces the earlier text-only "MeshCore /
<version> / <date>" placeholder.
2026-05-21 13:29:57 +02:00
liquidraver fcf4e8d1cb joystick UI: drop dead members + refresh stale doc comments
JoystickUITask had two write-only fields after the recent refactors:
_msgcount (only reader getMsgCount() had no callers anywhere) and
_started_at (initialized, written in begin(), never read). Remove
both fields, their writes, and getMsgCount().

msgRead()'s auto-leave-Unread side-effect is preserved — it uses the
function parameter directly, not the field.

Also: update the loop() doc to drop the "calls poll()" reference and
the battery-cache comment to drop the "from housekeeping" wording —
both concepts gone since Phase C.
2026-05-21 13:25:02 +02:00
liquidraver 2630ce9096 joystick UI: pause periodic timers on display-off, reset lock on wake
UIScreen gains onDisplayOff()/onDisplayOn() hooks. JoystickUITask tracks
display state and dispatches them on transition — at top of loop()
(catches display.c's auto-off, which fires behind our back) and right
after _display.turnOn() in the wake path (immediate resume).

Override in SnakeScreen and GPSSettingsScreen: stop their periodic
k_timers while the screen is off, restart on wake. Game state and
GPS-fix state are preserved across sleep. Snake doesn't crash into a
wall five seconds after the screen sleeps anymore.

Other screen timers (Countdown alarm, Contacts/Admin response timeout,
the global lock timer) intentionally keep running — their job is to
fire while the user is idle.

Also: wake-from-off now reschedules the lock timer if not already
locked, so a keypress near the end of the lock window gives you a
fresh LOCK_AFTER_MS instead of being immediately re-locked.
2026-05-21 13:18:49 +02:00
liquidraver a38f789b29 joystick UI: refresh repeater discover on entry if >60s stale
s_scan_sent (one-shot per boot) becomes s_last_scan_ms; RepeatersScreen
onEnter() re-runs the discover if there has never been a scan or the
last one is older than REPEATER_RESCAN_AFTER_MS (60s). Walking away
and returning now gives a fresh list instead of stale results.

Also fixes the manual KEY_ENTER_LONG rescan: after Phase C removed
the poll() that re-checked the flag, just unsetting it no longer
triggered anything. Now it calls doScan() directly.
2026-05-21 13:07:35 +02:00
liquidraver 385c88592a joystick UI: kill heartbeat + type the screen members
Auto-off and auto-lock were the last polling-style deadline checks.
display.c already owns the auto-off (k_work_delayable rescheduled via
mc_display_reset_auto_off()), so its tracker in JoystickUITask was
fully redundant — remove _auto_off and the loop() check. Auto-lock
becomes a one-shot k_timer (_lock_timer) scheduled per activity; ISR
callback sets _locked + signals refresh.

The 2-second heartbeat timer in joystick_ui_hooks is gone, along with
the start/stopHeartbeatFns plumbing it served. The mesh thread now
wakes only on actual events: input, mesh, screen-owned timers, the
lock timer, and display.c's own auto-off work.

Also: screen member pointers in JoystickUITask are now their concrete
subclass types instead of UIScreen *; removes 14 static_cast<>s at
call sites. _curr stays UIScreen * (polymorphic).
2026-05-21 13:04:54 +02:00
liquidraver a01af82c2b joystick UI: replace per-screen poll() with onEnter/onExit + k_timers
UIScreen gains onEnter()/onExit() lifecycle hooks; poll() and the
_curr->poll() call in the main loop are removed. Each screen with
periodic or deadline-based work owns its own k_timer:

- one-shot timers: Splash dismiss, Countdown alarm, Contacts ping
  timeout, RepeaterAdmin cmd/login timeout, Unread preview expiry
- periodic timers: Snake tick, GPSSettings sample
- onEnter()-only: Repeaters discover, Doom start
- deleted: Home, Stopwatch (were empty)

Timer ISR callbacks only signal _task->notify() — never mutate
screen state. Main-thread render() handles transitions. Setting
_curr now fires onExit on the outgoing screen and onEnter on the
incoming one, so timers are scoped to screen lifetime and can't
fire stale events on the wrong screen.
2026-05-21 12:53:00 +02:00
liquidraver eb84b1a2b3 joystick UI: add Time Sync admin menu item
New 6th item in the repeater admin submenu (admin only). Sends
"clock sync" as a CLI command; the repeater reads sender_timestamp
from the packet metadata and sets its RTC to our companion epoch
if ours is ahead. Response lands in the existing admin history list.
2026-05-21 09:55:26 +02:00
liquidraver 3de179f9bc joystick UI: lock-screen info, BLE-aware unread, mirror local sends
- Lock overlay: show battery % + unread count between title and unlock
  sequence; ui_invalidate_battery_cache() on screen wake forces a fresh
  ADC sample so the user sees current data immediately
- UnreadScreen::addPreview gains initially_read; received msgs pass
  _ble_connected (don't count unread when phone is syncing); sent msgs
  pass true (you sent it, you know it)
- CompanionMesh::queueLocalSent{Contact,Channel}Message + PUSH_CODE_MSG_WAITING
  on wio-originated sends so a connected phone app sees them via the
  normal offline-queue flow (path_len = OUT_PATH_SENT marker)
- OUT_PATH_SENT moved back from joystick_defs.h to ContactInfo.h
  (now a wire-format value, not UI-only)
2026-05-21 09:37:24 +02:00
liquidraver 18a67e679d Add the four sensor fields to struct ui_state in ui-button/ui_pages.h — keeps Calvario's incomplete API but it stays unused (still no callers). 2026-05-21 09:14:10 +02:00
liquidraver 83f00ab200 refactor
- src/Mesh.cpp	Reverted #ifdef ZEPHCORE_COMPANION block → back to vanilla self_id.copyHashTo
- helpers/ContactInfo.h	Removed OUT_PATH_SENT
- helpers/ui-joystick/joystick_defs.h	Added OUT_PATH_SENT here (with comment clarifying it's UI-only)
- helpers/ui-joystick/joystick_ui_task.h	Removed dead _next_batt_refresh field
- helpers/ui-joystick/joystick_ui_task.cpp	Removed _next_batt_refresh(0) from ctor init list
- helpers/ui-joystick/joystick_screens.h	MsgEntry::origin[80]→[32]; MAX_UNREAD_MSGS 32→16
- Kconfig	DOOM help text now lists both UI activation paths
- ARCHITECTURE.md	Same correction in §8.5
2026-05-20 22:53:02 +02:00