Commit Graph
162 Commits
Author SHA1 Message Date
JakubandClaude Sonnet 4.6 2da37b3af5 feat: 2-line boxed discover entries with RSSI, SNR, remote SNR
Each discovered node now renders as a boxed 2-line card (same style as
message history):
  ┌──────────────────────────────┐
  │ NodeName (or [Sensor])  Type │  ← inverted header
  │ RSSI:-87 SNR:7 Rem:5         │  ← signal stats
  └──────────────────────────────┘

Add snr_x4 and remote_snr_x4 to DiscoverResult:
  snr_x4       = _radio->getLastSNR()*4  (how well we heard the response)
  remote_snr_x4 = payload[1]             (how well responder heard our request)

2 items visible at a time; UP/DOWN scroll through results.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 08:56:54 +02:00
JakubandClaude Sonnet 4.6 2a9874d05d feat: show RSSI in discover results list
Add rssi field to DiscoverResult, captured from _radio->getLastRSSI()
when the discover response is received.

Display format in the right column:
  known node  → "Rpt-87" / "Snsr-92" (type + dBm)
  new node    → "*-87" (asterisk + dBm; type already visible in "[Rpt]" label)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 08:51:43 +02:00
JakubandClaude Sonnet 4.6 6249e63ec0 fix: remove isBLEConnected guard from discover response handling
The !isBLEConnected() check was too restrictive — if BLE happened to be
connected (app in background) the standalone discover would silently drop
all responses. Tag matching already provides correct isolation: responses
matching _pending_node_discover_tag are handled by the standalone UI and
not forwarded to the BLE app; responses with a different tag fall through
to the normal forward path.

Also show short type names (Rpt/Snsr/Room) in the discover results list
instead of generic "known"/"NEW", with a '*' prefix for nodes not yet
in contacts — matching the repeater/sensor distinction the app shows.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 08:37:17 +02:00
JakubandClaude Sonnet 4.6 64098bc4dc fix: change [M]=Discover hint to [Enter]=Discover in NearbyScreen
[M] key doesn't exist on the device; context menu is long-press Enter.
Also handle KEY_ENTER directly when the list is empty so the hint
is accurate — pressing Enter with no contacts enters discover mode
without going through the context menu.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 08:13:13 +02:00
JakubandClaude Sonnet 4.6 d851043059 feat: add dedicated discover sub-screen to NearbyScreen
Replaces the inline advert scan in the nearby list with a proper
CTL_TYPE_NODE_DISCOVER_RESP-based sub-screen accessible via the context menu.

- DiscoveredEntry → DiscoverResult: adds name[32] and is_known flag so
  both known contacts and unknown nodes are shown with useful labels
- getDiscoveredNodes → getDiscoverResults, capacity 8 → 16
- onControlDataRecv populates name from contacts for known nodes and
  records all respondents (not just unknowns)
- NearbyScreen gains _discover_mode sub-screen: 8-second scan window,
  live result polling each render cycle, UP/DOWN scroll, CANCEL to
  return, ENTER/M to re-scan; known nodes show actual name + "known",
  unknown nodes show "[Type]" + "NEW"
- List view is clean: no discovered nodes mixed in, no scanning banner

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 07:57:21 +02:00
JakubandClaude Sonnet 4.6 9cabcd5ac7 feat: replace advert scan with active node discovery in NearbyScreen
NearbyScreen now sends CTL_TYPE_NODE_DISCOVER_REQ (same protocol as the
companion app) instead of a plain self-advert. Nearby repeaters, sensors
and room servers respond with their pub_key and type. Known contacts get
their lastmod refreshed; unknown nodes appear as temporary entries (e.g.
"Repeater") in the list until their advert is received.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 00:16:53 +02:00
JakubandClaude Sonnet 4.6 6399df1f4a fix: improve buzzer volume control on nRF52
- Use inverted PWM polarity (0x8000) with PCT {2,5,12,25,50} giving
  ~6-8 dB perceptual steps (-24/-16/-9/-3/0 dB) vs the original
  uneven steps where levels 4 and 5 were only 1 dB apart
- Guard against cmp=0 truncation in both _nrfStartPwm and applyVolume:
  at level 1 with high notes (freq > ~2.5 kHz) integer math gives
  cmp=0 which with inverted polarity means 100% HIGH → complete silence
- Change volume preview note from C5 (523 Hz) to C6 (1047 Hz) to better
  match the piezo resonant frequency range and actual notification sounds

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 09:06:00 +02:00
JakubandClaude Sonnet 4.6 fa194016d7 feat: show message age (3m, 2h, >1d) in channel and DM history list
Stores RTC timestamp on each history entry and renders it right-aligned
on the sender row using a compact format (Xs/Xm/Xh/>1d).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 20:21:32 +02:00
JakubandClaude Sonnet 4.6 eee496b60b fix: add missing space after Type: and Seen: labels in NearbyScreen detail view
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 17:44:49 +02:00
JakubandClaude Sonnet 4.6 0f5740f6b6 fix: remove redundant slen clamp and update stale comment
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 18:20:42 +02:00
JakubandClaude Sonnet 4.6 34cfd27b8a fix: use @[nick] format for replies and fix fullscreen message display
- Reply prefix changed to "@[nick] " format — bracket delimiter handles
  nicks with spaces unambiguously
- FullscreenMsgView: parse "@[nick] body" to show "To: nick" header;
  fall back gracefully if format not matched
- Reduce FS_CHARS 21→20 to prevent scroll arrows overlapping last char
- Transliterate nick before displaying in "To:" header and "RE:" title
  so non-ASCII characters render correctly on the OLED font

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 18:18:45 +02:00
JakubandClaude Sonnet 4.6 b21bcb5ca4 fix: render reply popup in CHANNEL_HIST and DM_HIST phases
_ctx_menu.render() was only called in CONTACT_PICK and CHANNEL_PICK,
so the reply popup was active but invisible in history views. Added
render calls for list view and fullscreen view in both DM_HIST and
CHANNEL_HIST phases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 16:11:25 +02:00
JakubandClaude Sonnet 4.6 6f7de3ed20 feat: add reply action to channel and DM message history
Long-press Enter (KEY_CONTEXT_MENU) on a selected message shows an
'Options / Reply' popup in both list and fullscreen views. Confirming
opens MSG_PICK with 'RE: nick' title — the reply can be sent via
keyboard or quick message templates, both automatically prefixed with
'@nick '.

- Channel: reply prefix extracted from 'sender: message' format
- DM: reply available only on incoming messages, uses contact name
- FullscreenMsgView: REPLY added to Result enum, KEY_CONTEXT_MENU handled
- MSG_PICK: _reply_mode flag routes prefix through keyboard and templates
- Guard: prevent sending empty reply (prefix-only keyboard input ignored)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 15:12:11 +02:00
JakubandClaude Sonnet 4.6 8f566732ed feat: show @recipient as 'To: nick' bar in fullscreen message view
When a message starts with @nick, the fullscreen view expands the header
to show a second row 'To: nick' below the sender name, and displays the
message body without the @nick prefix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 15:00:01 +02:00
JakubandClaude Sonnet 4.6 9b61bc3028 fix: increase quick-message expansion buffer from 80 to 140 bytes
custom_msgs templates are up to 140 chars; the 80-byte msg[] buffer
silently truncated any message longer than 79 characters before sending.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 09:53:36 +02:00
JakubandClaude Sonnet 4.6 44122565f6 perf: query sensors once for both lock screen dashboard fields
Previously each LPP sensor field called querySensors() separately.
Now a single querySensors() call fills a shared buffer used by both fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 18:52:40 +02:00
JakubandClaude Sonnet 4.6 360d5d10f7 feat: add 30s minimum interval option to auto-advert screen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 14:33:58 +02:00
JakubandClaude Sonnet 4.6 a2856b7a48 fix: lock screen wake window no longer extends on repeated key presses
Previously every key press while locked reset _lock_wake_until to now+5s,
so multiple presses (or spurious button events) kept extending the window.
Now the 5s timer is set only when the display first wakes from dark.
The unlock sequence still extends the window on each Back+Enter step.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 14:30:53 +02:00
JakubandClaude Sonnet 4.6 3fb0a55aef fix: auto-reset _lock_seq_used after 5s in case Back release event is missed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:17:02 +02:00
JakubandClaude Sonnet 4.6 20e88443b4 fix: polish lock screen sequence — hints update instantly, screen stays on, triple-Back blocked
- Turn display on at first Back+Enter press so hints are visible even when screen was dark
- Extend _lock_wake_until on every sequence step to prevent screen blanking mid-press
- Set _next_refresh=0 on every Enter press (not just on completion) for immediate hint redraw
- Add _lock_seq_used flag to suppress the Back CLICK that fires on release after unlock
- Block triple-Back (buzzer toggle) when screen is locked

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 11:17:59 +02:00
JakubandClaude Sonnet 4.6 43f3621fdc fix: replace long-press lock trigger with back_btn.isPressed() check
Long press detection on back_btn was unreliable — the release generated
a spurious CLICK that cancelled the sequence. Now the Enter click handler
checks back_btn.isPressed() directly: holding Back and clicking Enter 3×
within 3 seconds locks/unlocks the screen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 10:09:54 +02:00
JakubandClaude Sonnet 4.6 ef0241b5f3 feat: show two dashboard sensor values on lock screen
Reuses dashboard_fields[0] and [1] from NodePrefs — left-aligned and
right-aligned in one row under the clock, no labels to save space.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 10:04:28 +02:00
JakubandClaude Sonnet 4.6 9ff42a9296 feat: screen lock — hold Back + 3×Enter to lock/unlock
- Long press Back starts lock sequence; 3 Enter presses within 3s toggle lock
- Locked screen wakes for 5 seconds on any key, showing clock and unlock hint
- Incoming messages do not wake the display while locked
- Auto Lock setting in Display section: locks automatically when screen turns off
- Lock state persisted in UITask; NodePrefs gains auto_lock flag

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 09:55:51 +02:00
JakubandClaude Sonnet 4.6 b068318bc5 refactor: extract SettingsScreen and QuickMsgScreen to separate header files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 00:12:40 +02:00
JakubandClaude Sonnet 4.6 a60ec376d7 perf: increase render interval on static screens from 300ms to 2000ms
Static screens (SettingsScreen, BotScreen, FullscreenMsgView, message
list, contact picker) were re-rendering every 300ms with no visible
benefit. New messages still trigger immediate refresh via newMsg() →
_next_refresh=100, so responsiveness is unchanged. Reduces unnecessary
SPI transfers and CPU wakeups ~6x on idle static screens.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 23:42:52 +02:00
JakubandClaude Sonnet 4.6 c4b152fd6a fix: clear arrow background in FullscreenMsgView to prevent text overlap
Text lines fill the full display width, causing the last chars to bleed
into the scroll arrow area. Draw a DARK fillRect behind each arrow before
rendering it, so the arrow appears on a clean black background without
truncating the text width.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 23:28:18 +02:00
JakubandClaude Sonnet 4.6 44b2519df4 fix: with_sender buffer too small and FS_LINE_H mismatch
- with_sender[160] could hold only ~160 chars but node_name(32) +
  expanded(200) needs up to 234; snprintf would silently truncate
  the UI display entry. Increased to 240.
- FS_LINE_H was 9 but Adafruit 5x7 font line height is 8, wasting
  1px per line and showing 5 lines instead of 6.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 23:23:20 +02:00
JakubandClaude Sonnet 4.6 b91afa05b9 fix: reduce low battery shutdown text size to fit 128px OLED
Size 2 rendered text wider than display width. Switched to size 1
and adjusted y-positions to keep both lines visible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 17:47:22 +02:00
JakubandClaude Sonnet 4.6 6a7f8b7ca7 fix: transliterate message text in FullscreenMsgView before wrapping
Raw UTF-8 message body was passed directly to display.print(), rendering
Polish and other accented chars as garbage on the Adafruit font.
Transliterating before wrapLines also fixes byte-count wrapping for
multi-byte UTF-8 characters.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 15:24:36 +02:00
JakubandClaude Sonnet 4.6 fbe636a05a feat: show plus version number on splash screen
Splash screen bar changes from "Plus for Wio" to "Plus 1.4 for Wio"
by extracting the suffix after "plus." from FIRMWARE_VERSION.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 00:33:13 +02:00
JakubandClaude Sonnet 4.6 9c40a253e2 update README and fix quick message / melody naming collision
- Expand README: ringtone editor now documents two melody slots, full
  note spec, and melody assignment to DM/channel notifications; settings
  section lists DM Melody and Channel Melody options; context menu
  description updated with melody override detail
- Rename quick reply template labels from M1-M10 to Q1-Q10 to avoid
  ambiguity with melody slot identifiers M1/M2
- Increase DM message history buffer from 32 to 64 entries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 00:09:57 +02:00
Jakub 1d04930c1e quick fix 2026-05-15 16:33:19 +02:00
JakubandClaude Sonnet 4.6 ec601ef118 fix: renderBar always shows slot outlines regardless of value
Previously empty slots used drawRect and filled slots used fillRect,
so at max value (e.g. volume=4) no drawRect was called and no slot
borders were visible when selected (inverted colors). Brightness at
default (2/5) always had visible outlines, making the two bars look
inconsistent.

Now drawRect is called for every slot, with fillRect(+1,+1,w-2,h-2)
on top for filled ones. All 5 slots are always visually distinct.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 15:48:05 +02:00
JakubandClaude Sonnet 4.6 484f0891b1 fix: three bugs found in code review
- DataStore: validate ringtone2_len <= 32 after loading from flash
  (same guard as ringtone_len on line 252; we introduced this field
  but forgot the validation)
- UITask: fix shutdown buzzer timeout to conventional unsigned form
  (millis() - start) < timeout to avoid unsigned underflow if called
  within first 2.5s of boot
- UITask: guard _last_notif_ch_idx < 64 before 1ULL shift in notify()
  (defensive; MAX_GROUP_CHANNELS=40 so currently unreachable, but
  prevents UB if the value is ever unexpectedly out of range)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 15:34:10 +02:00
JakubandClaude Sonnet 4.6 d02c1cf098 feat: play preview tone when buzzer volume changes in settings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 11:46:48 +02:00
JakubandClaude Sonnet 4.6 b3c6416874 feat: melody assignment per channel/DM, dual ringtone slots, volume cleanup
Add second ringtone slot (ringtone2_*) to NodePrefs and DataStore.
Add per-channel and per-DM melody assignment (built-in / melody 1 / 2)
stored in NodePrefs bitmasks and DmMelodyEntry table.
Settings screen exposes DM/CH sound items; RingtoneEditorScreen supports
switching between slots. notify() uses buildMelodyFromPrefs() to select
the correct RTTTL string per contact.

Remove broken nRF52 volume control from buzzer.cpp: tone() uses
NRF_PWM2 with DECODER.MODE=Auto so TASKS_NEXTSTEP has no effect and
duty cannot be changed before the first DMA read without patching the
Arduino core. applyVolume() is now a no-op on nRF52.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 11:43:38 +02:00
JakubandClaude Sonnet 4.6 c86e6c803e fix: reverse fullscreen message nav order and remove channel # prefix
- Swap left/right navigation in fullscreen view: left=newer, right=older
- Update arrow indicators to match new direction
- Remove # prefix from channel name in message and compose view titles

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 20:21:13 +02:00
JakubandClaude Sonnet 4.6 1fcad7776a feat: add unread message count field to clock dashboard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 18:20:49 +02:00
JakubandClaude Sonnet 4.6 9426c86e79 feat: polish status bar and home screen layout
- Move "A" (auto-advert) indicator left of BT "B" in status bar
- Ellipsize node name to fit available width instead of overflowing
- Shrink A and B background rectangles by 1px right, 2px bottom
- Change A blink to 2s on / 2s off at 1000ms refresh rate
- Auto-advert sends GPS position directly via createSelfAdvert/sendZeroHop
- Align Settings and Tools home page titles to y=22, same as Messages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 18:13:18 +02:00
JakubandClaude Sonnet 4.6 b36c970a2d fix: auto-advert indicator - 300ms refresh, background shifted 1px left
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 17:45:26 +02:00
JakubandClaude Sonnet 4.6 e205f3712d fix: auto-advert indicator - inverted A, 50% duty 1s blink, 1s refresh
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 17:41:33 +02:00
JakubandClaude Sonnet 4.6 20151cc609 fix: align status bar icons to y=0, reduce battery icon to 8px height
BT indicator and mute icon were at y=1 (1px below text baseline).
Battery icon height reduced 10→8px to match text height.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 17:34:57 +02:00
JakubandClaude Sonnet 4.6 8efff38a4f fix: auto-advert indicator - capital A with space, slow blink, proper refresh
- Changed ~ to " A" (space + capital A, no inverted background)
- Blink: on 500ms / off 1500ms (once per 2s)
- Home screen refresh rate drops to 500ms when auto-advert is active
  so the blink is actually visible

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 17:25:27 +02:00
JakubandClaude Sonnet 4.6 e38dc87c25 feat: blinking ~ indicator in status bar when auto-advert is active
Blinks at 4Hz (250ms toggle) with inverted background so it's clearly
visible without being distracting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 16:51:57 +02:00
JakubandClaude Sonnet 4.6 e43047f561 fix: show keyboard placeholders only for sensors with actual live data
Replace getAvailableLPPTypes() (hardware-detected) with a live querySensors()
call so only placeholders for sensors currently returning values appear in
the picker.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 16:41:04 +02:00
JakubandClaude Sonnet 4.6 9c4dc0039a fix: clarify advert button labels in NearbyScreen
Scan does not discover others — it announces our own presence.
Updated labels: "Scan for nodes"→"Send my advert", "SCANNING..."→"ADVERTISING...", "[M]=Scan"→"[M]=Advert".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 16:36:35 +02:00
JakubandClaude Sonnet 4.6 f434852706 feat: show last-heard time in detail view, fix disappearing contact edge case
- Detail view now shows "Seen: Xs/Xm/Xh ago" using ContactInfo.lastmod
- Periodic detail refresh exits back to list view if the contact is no
  longer in the filtered list (e.g. removed from favourites)
- Rearranged detail layout to fit 6 rows within 64px display

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 16:29:04 +02:00
JakubandClaude Sonnet 4.6 404cb6956d feat: add Fav filter and clearer bearing display in NearbyScreen
- Add "Fav" as first filter category (contacts with flags & 1)
- Show distance and bearing on separate lines in detail view
- Add cardinal direction to bearing: "Az: 245d (SW)"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 16:21:11 +02:00
JakubandClaude Sonnet 4.6 083ddac85c refactor: remove ping from NearbyScreen, rely on auto-advert for presence
Ping sent empty messages appearing in target's chat history, and RTT
measurement never worked for direct sendMessage() calls. Removed all
ping state machinery and registerExpectedAck(); detail view now shows
node info only (lat/lon/dist/bearing/type).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 16:17:52 +02:00
JakubandClaude Sonnet 4.6 09ac455806 feat: add periodic auto-advert with GPS position (Auto-Advert tool)
Adds configurable periodic 0-hop self-advert that includes GPS coordinates,
making the device's position visible in other nodes' Nearby Nodes screen.
Options: off / 1min / 2min / 5min / 10min / 30min / 1h.

Accessible via Tools → Auto-Advert. Timer runs in MyMesh::loop().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 16:08:33 +02:00