Commit Graph
263 Commits
Author SHA1 Message Date
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 e36d9b33a3 companion: scope joystick-only state to joystick builds
- #ifdef-gate _pending_joystick_{ping,admin}_tag fields + setters
  under CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK (saves 8 bytes per
  CompanionMesh instance on button-UI builds)
- gate logTx ui_notify_packet_sent() to joystick builds only;
  was firing on every TX for any UI variant (dead code on button UI)
- drop redundant _pending_login manual set in CMD_SEND_LOGIN;
  BaseChatMesh::sendLogin's onLoginSent hook owns it now, just
  clear the other pending fields explicitly
2026-05-21 09:06:55 +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
liquidraver 781b4f5a33 fixups for battery reading 2026-05-20 22:27:32 +02:00
liquidraver 597daee6c3 Merge Calvario/ZephCore ui_joystick into joysticktest
# Conflicts:
#	zephcore/helpers/ui-button/ui_task.c
2026-05-20 22:14:35 +02:00
liquidraver 3a35fec8b3 ui: make housekeeping refresh path on-demand
Stop doing UI work nobody asked for. The 5 s housekeeping tick was
reading env sensors (I2C, 10-50 ms), the battery ADC (regulator
toggle + 8 samples, every 60 s), and re-rendering the display
unconditionally — all while the display might be off and nothing
on-air had requested any of it.

Now:
- render_sensors() reads env sensors only when the user is on
  that page (event-driven, never fires during idle)
- battery refresh is lazy on ui_pages_render() with a 30 s
  freshness guard; explicit ui_set_battery() calls also count
- the unconditional OLED rerender from housekeeping is gone;
  real state changes (messages, BLE, button press) still fire
  schedule_render() directly

Telemetry / stats paths read fresh ADC + sensors on demand and
were never using the UI cache, so over-the-air consumers are
unaffected.
2026-05-20 22:10:14 +02:00
Steve Calvário 56b10f70a1 Init 2026-05-20 20:40:58 +01:00
liquidraver c115803786 expand cmakelists change 2026-05-20 21:12:24 +02:00
liquidraver 3fafe08e16 Merge branch 'dev' of https://github.com/liquidraver/ZephCore into dev 2026-05-20 21:08:27 +02:00
liquidraver 51983b0e66 expand CMakeLists.txt change 2026-05-20 21:08:10 +02:00
liquidraver 0a22481da0 Merge pull request #22 from Calvario/t114_fix_long_press
Fix Heltec T114 screen long press
2026-05-20 21:03:33 +02:00
Steve Calvário 0eedb72d73 Improve CMakeLists handling for T114 2026-05-20 20:02:27 +01:00
Steve Calvário f7f1603536 Fix T114 long press 2026-05-20 17:16:12 +01:00
liquidraver 65311f0ea9 fix(ble): resolve two Phase 3 audit findings
1. CONFIG_BT_DEVICE_NAME_GATT_WRITABLE=y removed. The default GAP
   Device Name write permission is plain BT_GATT_PERM_WRITE — no
   bonding required (Zephyr gap_svc.c:158). Any connected peer
   (bonded or not) could rename the device. Worse, a GAP write
   updates bt_get_name() but NOT prefs.node_name, so the advertised
   name wouldn't track the renamed value. Rename now flows
   exclusively through CMD_SET_ADVERT_NAME, which is NUS-protected
   (AUTHEN required) and properly propagates via
   zephcore_ble_update_name() to prefs + GATT + adv data.

2. CONFIG_BT_DIS_FW_REV_STR synced from "1.13.0" to "v1.15.1-zephyr"
   to match CompanionMesh.cpp CMD_DEVICE_QUERY's version string.
   Comment added requiring the two to stay in sync.
2026-05-20 16:00:17 +02:00
liquidraver 0ba2721b40 refactor(companion): tighten Phase 2F polish — error codes, length checks, build assert
Three small correctness/polish improvements from BLE audit Phase 2F:

1. Five handlers (CMD_APP_START, CMD_GET_CHANNEL, CMD_SET_CHANNEL,
   CMD_DEVICE_QUERY, CMD_SEND_CHANNEL_TXT_MSG) previously responded
   with ERR_UNSUPPORTED on short-frame validation failure (because
   they fell through to the dispatcher's default break, which the
   caller converts to "unknown command"). They now explicitly
   sendPacketError(ERR_ILLEGAL_ARG) — the semantically correct code
   for "known cmd, bad frame".

2. CMD_SET_TUNING_PARAMS previously returned PACKET_OK on short
   frames without applying any change. Now sends ERR_ILLEGAL_ARG so
   the phone learns the change didn't take.

3. Added static_assert that CONFIG_ZEPHCORE_BOARD_NAME fits in 40
   bytes including its null terminator, so a future too-long board
   name fails at build time instead of producing an unterminated
   wire-format response.
2026-05-20 15:54:20 +02:00
liquidraver b39483add3 fix(companion): null-terminate contact name in CMD_ADD_UPDATE_CONTACT
The wire format reserves a 32-byte name field; if the phone sends 32
non-null bytes, ContactInfo::name has no terminator. Subsequent
LOG_INF/LOG_DBG sites using %s with contact.name then read past the
field into adjacent struct bytes (type, flags, out_path_len, ...)
until the first null. No memory corruption — serializeContact uses
StrHelper::strzcpy which is length-bounded — but log output gets
garbage and a paired peer could probe a few bytes of the struct
through log capture.

Sibling handler CMD_SET_CHANNEL at :1593-1594 already does this
defensively. Match the pattern.
2026-05-20 15:40:18 +02:00
liquidraver 988b438ec3 refactor(companion): harden telemetry buffer sizing and custom-vars snprintf
Two polish items from BLE audit Phase 2B:

1. CMD_SEND_TELEMETRY_REQ self-response buffer was uint8_t rsp[96]
   with a comment claiming 70 B worst case. Actual worst case at
   POWER_MAX_CHANNELS=4 is 82 B; if the channel cap ever grew the
   buffer would silently overflow. Replaced with a sizeof-style
   expression that tracks POWER_MAX_CHANNELS, plus an 8-byte safety
   pad. No size change today (90 vs. 96) but the upper bound auto-
   tracks any future bump.

2. CMD_GET_CUSTOM_VARS used `dp += snprintf(dp, 20, ...)` which
   advances by the would-be-written length, not bytes actually
   written. Currently safe only because gps_interval is capped
   ≤86400, but if either cap drifted or a new key was added the
   length passed to writeFrame would include uninitialized stack
   bytes between the truncation point and the (over-advanced) dp.
   Now tracks rsp_end, computes remaining per snprintf, and only
   advances dp on real progress.

Both are correctness polish, not exploitable today.
2026-05-20 11:57:37 +02:00
liquidraver bd1e022e88 fix(security): close OOB read in path-decoding callers (BLE + LoRa-anon)
Both mesh::Packet::writePath and ::copyPath did a raw memcpy of the
decoded hash_count*hash_size bytes from src to dest with no bound on
src. Two call sites used phone-supplied or LoRa-anon-supplied buffers
where the path_len byte was attacker-controlled:

  - CompanionMesh CMD_SEND_CHANNEL_DATA accepted len>=4 and called
    writePath with no src bound; a paired phone could leak up to ~65
    bytes of syswq stack into the outgoing LoRa channel-data frame.

  - RepeaterMesh handleAnonRegionsReq / handleAnonOwnerReq /
    handleAnonClockReq read reply_path_len from an unauthenticated
    LoRa anon-request payload and called copyPath without any src
    bound. Any LoRa neighbor could leak repeater stack into the
    reply path.

Hardened the API: both functions now require an explicit src_len
and reject (return 0) when the decoded byte count exceeds it.
Updated all 14 call sites across Packet/Mesh/Dispatcher/BaseChatMesh/
CompanionMesh/RepeaterMesh. Trusted callers (internal MAX_PATH_SIZE
buffers) pass MAX_PATH_SIZE; untrusted callers pass real remaining
length. Added len-5 plumbing through the anon-handler signatures.

CMD_SEND_CHANNEL_DATA also gained a local len>=5 + path_bytes
sanity check for early rejection.
2026-05-20 11:52:39 +02:00
liquidraver d7e420bf2f fix(ble,usb): three bugs from BLE audit
1. USB takeover opcode mismatch
   ZephyrCompanionUSB.cpp checked payload[0] == 0x03 with a comment
   claiming CMD_APP_START, but CMD_APP_START is 0x01 (0x03 is
   CMD_SEND_CHANNEL_TXT_MSG). The USB handshake silently dropped the
   companion app's first frame on every connection; the app appeared
   broken over USB until the user happened to send a channel message.

2. CMD_SET_ADVERT_NAME didn't propagate to BLE adv data
   Name changes were persisted to prefs but the advertising payload
   and GATT device name kept the old value until reboot. Added
   zephcore_ble_update_name() and called it from the handler.

3. No advertising-health watchdog
   If bt_le_adv_start() ever failed transiently (HCI timeout,
   controller pacing), the device would silently stop advertising
   and stay undiscoverable until reboot. Added an adv_running flag
   and a 5s watchdog in the companion housekeeping handler that
   nudges adv back on if it stops outside a connection. Tracks
   Arduino nrf52's equivalent 10s watchdog.
2026-05-20 11:09:45 +02:00
liquidraver 78f0c1c840 fix(ble): don't clobber overflow frame on congestion 2026-05-20 10:49:55 +02:00
liquidraver 3ecc42dd4d add t114 noscreen builds 2026-05-20 10:30:21 +02:00
liquidraver f0c29a612f ble cleanup 2026-05-20 10:27:42 +02:00
liquidraver a9f3b8ef3b cleanup t114, add build 2026-05-19 07:00:03 +02:00
liquidraver 924130b408 Merge pull request #20 from Calvario/t114
Add T114 variants (screen and screenless)
2026-05-19 06:54:16 +02:00
liquidraver b271179cc9 activate PSRAM in capable devices to fit OTA 2026-05-18 22:31:04 +02:00
liquidraver df30c447e5 west update (following 4.4 branch backports, main is still too noisy for us) 2026-05-18 21:52:15 +02:00
Steve Calvário d65703d82b Add T114 screen and screenless 2026-05-16 22:43:36 +01:00
Steve Calvário f2cdab84c4 Merge branch 'liquidraver:master' into t114 2026-05-15 19:54:47 +01:00
Steve Calvário 6dc86cc443 Init T114 without screen 2026-05-15 19:04:14 +01:00
liquidraver 3441caf8b0 new rx busy latch v20260514.205836 2026-05-14 22:24:48 +02:00
liquidraver a6d095bc16 edit default prefs 2026-05-12 22:07:02 +02:00
liquidraver a3bfb1e4a3 fix pubic channel anomaly v2 v20260511.125830 2026-05-11 14:42:47 +02:00
liquidraver 1bb04dd968 refactor atomicwrites, add atomic contacts save to QSPI capable devices v20260511.112139 2026-05-11 13:02:55 +02:00
liquidraver e78e197dee sx1262 fixes
- calibrate_image: revert to datasheet band table.  Narrow ±2 MHz
  window had truncation bug placing 433/869 MHz operating freq
  outside their own calibration windows.  DS §9.2.1 confirms cal
  is range-validity, not point-precision
- reset_agc: K_FOREVER mutex → K_MSEC(50).  Was holding dispatcher
  thread up to ~3 s if a long TX/RX held the SPI lock.  Now bails
  with WARN log; caller retries next maintenance interval.
2026-05-11 11:26:33 +02:00
liquidraver 2ee0fffa69 sync with vanilla 2026-05-08 10:13:42 +02:00
liquidraver 6919c3511c rx_boost state is undefined at boot (minor fix)
every LBT-retried flood packet loses its priority (fixed)
witching between LBT and non-LBT mode (or any cad.mode change) could silently skip full reconfiguration and leave the radio in the wrong mode (fixed)
2026-05-08 09:50:52 +02:00
liquidraver 66f7b8508f add/fix promicro_sx1262 build 2026-05-07 22:35:30 +02:00
liquidraver 147ac462b3 fix deleting public channel could wipe all other channels on reboot 2026-05-07 21:43:48 +02:00
liquidraver 2a8cc88262 fix fast path LBT CAD mode v20260507.103840 2026-05-07 11:37:09 +02:00
liquidraver 98fcddb502 add formatter tools 2026-05-06 21:56:23 +02:00
liquidraver 65f31ced7d no permanent gps disable on repeaters v20260506.102120 2026-05-06 10:11:09 +02:00
liquidraver 43a31e190d fix CAD IRQ status clear 2026-05-06 10:07:44 +02:00
liquidraver 6bc3ba7405 disable default duty cycling + SX driver fixes 2026-05-05 21:56:00 +02:00