Commit Graph
76 Commits
Author SHA1 Message Date
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 c7a00b9533 update version 2026-05-22 15:39:07 +02:00
liquidraver 6b624a69e5 sync with vnailla dev 2026-05-22 14:42:12 +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 4f388c0b98 companion: fix "62 hops" garbage in local-sent BLE mirror frames
The phone app interprets path_len with an unconditional `& 63`, so
the OUT_PATH_SENT (0xFE) sentinel we were writing into the
offline-queue frame became "62 hops" + 4 bogus path bytes in the
app's UI. There are 0 LoRa hops between the sender (us) and the
phone viewing the message — path_len = 0 is both correct and
renders cleanly as "direct / 0 hops". The "(>>✓) " / "(>>✗) " body
prefix still distinguishes wio-originated messages from incoming.

OUT_PATH_SENT remains the marker for the joystick UI's own local
entries (UnreadScreen, _ch_previews) — formatHopCount handles it
explicitly so it's safe there.
2026-05-22 09:11:12 +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 ec2ae31b67 joystick UI: prefix sender to mirrored channel sends for BLE app
BaseChatMesh::sendGroupMessage wraps the body as "<sender_name>: <body>"
before transmitting (the receiver's onChannelMessageRecv sees the full
"USER: MSG" string, which CompanionMesh queues as-is for the phone).
queueLocalSentChannelMessage was queuing just the raw body, so the
phone parsed an empty sender and lost the body. Prepend
"<prefs.node_name>: " to mirror the wire format.

joystick UI: prefix DM mirrors to phone with sent-marker

DM offline-queue frames identify the sender by pubkey field, not by
in-text prefix. queueLocalSentContactMessage was queuing with the
contact's pubkey + raw body, so the phone app rendered wio-originated
DMs identically to incoming ones from that contact. Prepend "(>>) "
to the body so sent messages are visually distinguishable; matches
the joystick UI's own sent indicator (UnreadScreen::addPreview).
2026-05-21 13:41:13 +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 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
Steve Calvário 56b10f70a1 Init 2026-05-20 20:40:58 +01: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 a6d095bc16 edit default prefs 2026-05-12 22:07:02 +02:00
liquidraver 2ee0fffa69 sync with vanilla 2026-05-08 10:13:42 +02:00
liquidraver 147ac462b3 fix deleting public channel could wipe all other channels on reboot 2026-05-07 21:43:48 +02:00
liquidraver 6bc3ba7405 disable default duty cycling + SX driver fixes 2026-05-05 21:56:00 +02:00
liquidraverandClaude Opus 4.7 5e7adfb130 normalize source-file line endings to LF
Add .gitattributes rules so .c/.h/.cpp/.hpp are always stored as LF
(prevents EOL drift from editors with autocrlf-true defaults), and
renormalize the 30 source files that had drifted to CRLF in the index.

Pure mechanical change — `git diff --ignore-cr-at-eol` is empty.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 14:56:44 +02:00
liquidraver 28e12c7eef initial companion jitter lowering 2026-05-05 14:52:12 +02:00
liquidraver 62232bf609 small ble fixes 2026-04-30 09:23:56 +02:00
liquidraver 5dc07d78ba force unscoped send on magic scope word: "none" even if there is a default scope 2026-04-29 13:17:04 +02:00
liquidraver b84830b3b3 (greatly) improve our BLE 2026-04-29 12:09:21 +02:00
liquidraver 1092c49a7d port vanilla duty cycling logic 2026-04-28 14:43:12 +02:00
liquidraver b57df41753 micro-tweak: skip APC math when APC is off 2026-04-28 14:18:10 +02:00
liquidraver e18d017b2c refactor repeater prefs changes 2026-04-28 13:49:08 +02:00
liquidraver bf9ad0ef40 tune delay knobs 2026-04-28 13:49:08 +02:00
liquidraver 7ecf647af8 runtime guard agc reset if rx duty cycle is enabled 2026-04-28 13:49:08 +02:00
liquidraver e7219893c6 fix repeater prefs saving 2026-04-27 10:15:37 +02:00
liquidraver d5f260c1c9 Fix ESP OTA functions 2026-04-25 14:52:45 +02:00
liquidraver 57aab5f522 GPS start fix when requesting telemetry
APC disabled by default, but compiled in
2026-04-24 22:32:05 +02:00
liquidraver 37eb87efd6 repeater housekeeping bugfix 2026-04-24 14:30:07 +02:00
liquidraver a3244e2dc2 Three driver fixes for RX duty cycle, all applied to the existing patch
stolen from Zephyr main:

  1. Issue StopTimerOnPreamble=1 before SetRxDutyCycle so the chip's
     timer is not reset on every preamble detect (per §13.1 of the
     datasheet). Without this, duty cycle effectively never sleeps in
     noisy RF and current draw spikes.
  2. On IRQ_RX_TX_TIMEOUT during duty-cycle RX, re-arm via
     sx126x_restart_rx() instead of falling through to set_sleep().
     The old path silently killed duty cycle after the first preamble
     false-positive.
  3. On recv_duty_cycle(NULL) cancel, wake the radio before issuing
     SetStandby — BUSY stays asserted during the sleep phase and the
     standby command was being dropped.

Also adds a dc_timeout_restarts atomic counter incremented on the Fix 2
path, exposed end-to-end: sx126x_ext.h accessors → LoRaRadioBase vtable
→ SX126xRadio override → CommonCLICallbacks → RepeaterMesh. Query via
`get dc.restarts` on the repeater CLI; cleared by `clear stats`. High
values indicate a noisy environment or a too-loose preamble threshold.

(+increase ESP BT stack because future zephyr pin advance will trip that mine)
2026-04-20 13:36:45 +02:00
liquidraver 78561036d0 bump version to match vanilla 2026-04-19 10:16:59 +02:00
liquidraver 7876ae79c3 make CAD retry jittery, sync prefs load to companion-style 2026-04-18 22:21:57 +02:00
liquidraver d2cec84100 sync with vanilla dev 2026-04-17 13:21:30 +02:00
liquidraver ba4b86752d acw: airtime-scale jitter caps, companion surroundings awareness
- flood retransmit jitter now capped at min(2000ms, 6·airtime) instead of
  fixed 2000ms — spreads tighter at SF7, unchanged at SF8
- reactive per-dupe backoff cap now min(2000ms, 12·airtime), keeps
  semantic of "push past ~12 relay slots"
- contention ring 16 → 24 for 50-neighbor hilltops
- companions passively track heard floods (warms EMA without forwarding)
  and spread their own TX by up to min(1000ms, 3·airtime), hopefully
  fixing repeaters missing companion's first transmission

config cleanup:
- move BLE TX buffer bumps (ACL_TX=12 etc.) from zephcore_common.conf to
  esp32_common.conf — the Espressif blob needs them, nRF doesn't, and
  the bumps were overflowing nRF52840 RAM
- remove CONFIG_ZEPHCORE_MAX_CONTACTS=510 overrides from 5 nRF52840
  companion boards; Kconfig default of 350 fits with comfortable margin
  (wio prod: 91% → 79% RAM)
2026-04-17 10:24:12 +02:00
liquidraver e1c04113ca update default prefs 2026-04-14 12:07:52 +02:00
liquidraver 5c3df69621 atomic write (temp + sync + rename) for identity, prefs, and channels 2026-04-13 21:52:40 +02:00
liquidraver a716d2478f port seeed solar p1 + gps command fixes 2026-04-13 11:48:34 +02:00
liquidraver 4f6b0cca27 fix repeater-observer hybrid freezing 2026-04-09 12:23:27 +02:00
liquidraver 0898699c33 patch file CLRF fixup and repeater-observer hybrid first implementation 2026-04-09 11:02:58 +02:00
Rastislav Vysoky da22b127c3 sx1276 2026-04-07 19:05:32 +02:00
liquidraver 7998b5eae3 observer self-advert on mqtt when lat/lon and custom name is set 2026-04-05 17:21:37 +02:00
liquidraver 875e6554d4 Implement MQTT Observer role 2026-04-05 14:34:06 +02:00
liquidraver e115f24b31 buzzer and led queued offline message indicator
- buzzer was bugged when message received
- green led: hearthbeat, blue led: message received indicator (when not connected to app)
- if only one led blinks: hearthbeat 20msec, message received 200msec
2026-04-02 21:06:28 +02:00