Commit Graph

3012 Commits

Author SHA1 Message Date
Jakub
8d3b955da1 feat: transliterate accented/diacritic characters to ASCII in translateUTF8ToBlocks
Replaces the block-character fallback with proper transliteration for
Polish (ą→a, ć→c, ę→e, ł→l, ń→n, ó→o, ś→s, ź→z, ż→z), German (ä ö ü ß),
and common French/Spanish/Portuguese accents. Unknown codepoints fall back
to '?' instead of █.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 14:46:53 +02:00
Jakub
2978b0ffc4 Merge remote-tracking branch 'upstream/main' into wio-tracker-l1-improvements 2026-05-16 10:51:42 +02:00
ripplebiz
c940ea5f30 Merge pull request #2567 from recrof/patch-4
Change MeshCore intro video link to The Comms Channel's MC intro playlist
2026-05-16 13:03:00 +10:00
Jakub
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>
wio-tracker-v1.15-plus.1.4
2026-05-16 00:33:13 +02:00
Jakub
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
e0e79b725d Merge branch 'meshcore-dev:main' into wio-tracker-l1-improvements 2026-05-16 00:02:22 +02:00
Rastislav Vysoky
d4c99dec65 Change MeshCore intro video link to The Comms Channel's MC intro playlist 2026-05-15 16:42:29 +02:00
Jakub
1d04930c1e quick fix 2026-05-15 16:33:19 +02:00
Jakub
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
Jakub
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
Jakub
a819b05ed0 fix: correct UF2 artifact name for wio-tracker tags with hyphenated versions
##*- strips to the last hyphen, so wio-tracker-v1.15-plus.1.3 yielded
"plus.1.3" instead of "v1.15-plus.1.3". Switch to #*-v (shortest prefix
ending with -v) which preserves hyphens inside the version string.

Also remove the -Plus suffix appended in build_wio_tracker_l1_firmwares()
since the version scheme already encodes "plus" in the tag/version string,
making the suffix redundant and producing doubled output like "plus.1.3-Plus".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 15:24:54 +02:00
Jakub
bf05a13549 fix: implement nRF52 RTTTL player using direct NRF_PWM2 control for working volume
tone() dynamically computes seq_refresh so the DMA repeats 50% duty for
~13-30ms per note before re-reading its buffer — volume cannot take effect
until that delay passes, producing a staircase artifact.

Replace tone()/NonBlockingRtttl with a custom RTTTL parser that drives
NRF_PWM2 directly using REFRESH=0. DMA now re-reads _duty_buf every
PWM period so:
- Duty is set before SEQSTART fires → no initial full-volume burst
- applyVolume() writes to _duty_buf and takes effect within one period (<2.3ms at A4)
- setVolume() during playback adjusts loudness immediately and smoothly

Non-nRF52 platforms continue to use NonBlockingRtttl + analogWrite.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 13:09:38 +02:00
Jakub
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
Jakub
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
Jakub
39731bb7fa docs: update README for v1.15-plus.1.3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 09:21:32 +02:00
ripplebiz
181a54e718 Merge pull request #2532 from swaits/fix/trace-offset-widening
fix(mesh): widen TRACE offset to uint16 to avoid narrowing
2026-05-15 13:17:03 +10:00
Jakub
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>
wio-tracker-v1.15-plus.1.3
2026-05-14 20:21:13 +02:00
Jakub
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
Jakub
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
Jakub
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
Jakub
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
Jakub
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
Jakub
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
Jakub
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
Jakub
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
Jakub
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
Jakub
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
Jakub
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
Jakub
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
Jakub
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
Jakub
d59687647f fix: wire NearbyScreen ping through expected_ack_table so RTT is measurable
Direct sendMessage() calls bypass the BT serial handler that populates
expected_ack_table; add registerExpectedAck() to MyMesh and call it from
NearbyScreen so isAckPending() can actually track the outstanding ping.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 15:57:00 +02:00
Jakub
11cb0ae25e fix: NearbyScreen shows all contacts, not only those with GPS
- Remove gps_lat/lon == 0 filter: screen now shows all contacts
  reachable by radio; distance column shows ?GPS when coords unavailable
- Guard haversine: only compute distance when both own AND remote GPS
  are valid (prevents bogus distance to 0 N 0 E)
- Fix sort: nodes without GPS (-1) sort to end instead of front
- Rename Chat filter label to Comp, type display to Companion
- Update empty list message to "No contacts found"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 15:49:23 +02:00
Jakub
862714982e fix: reset _ping_ack_seen on detail view enter and exit
Stale _ping_ack_seen=true from a previous ping attempt could cause an
immediate PING_OK (with wrong RTT) on the next ping if the new ACK entry
hadn't appeared in the table yet. Reset the flag everywhere _ping_state
is set to PING_IDLE.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 13:55:35 +02:00
Jakub
10307d3a03 fix: ping RTT 0ms, degree symbol, clock page dots overlap
- Ping: add _ping_ack_seen flag; RTT is measured only after ACK entry
  was first confirmed present in the table, preventing false 0ms when
  isAckPending() returns false before the entry is registered
- NearbyScreen: replace unsupported degree symbol \xb0 with 'd'
  in bearing format ("Dist:1.5km 45d")
- Clock page: hide page indicator dots (already hides node name/battery);
  eliminates overlap with size-2 clock text drawn at y=0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 13:28:52 +02:00
Jakub
85de0b02be fix: guard savePrefs with dirty flag in BotScreen and DashboardConfigScreen
BotScreen and DashboardConfigScreen were calling savePrefs() on every
exit regardless of whether any setting changed. Add _dirty flag set only
on actual value changes (toggle, channel select, keyboard confirm,
field cycle), reset on enter().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:36:11 +02:00
Jakub
9e5c0e79e7 fix: move EXPECTED_ACK_TABLE_SIZE define before class for isAckPending
The #define was inside the private section, after isAckPending() which
references it — preprocessor hadn't seen the symbol yet at that point.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:30:43 +02:00
Jakub
24474f67fa feat: NearbyScreen, configurable clock dashboard, memory optimizations
- Add NearbyScreen: GPS contact list with haversine distance sort,
  type filter (ALL/Chat/Rpt/Room/Snsr), detail view with ping (RTT via
  ACK poll), node scan via advert(), context menu
- Add DashboardConfigScreen: configure up to 3 data fields on the clock
  page (Battery, Temp, Humidity, Pressure, GPS, Altitude, Lux, CO2,
  Contacts); entered via long-press ENTER on clock page
- Rework HP_CLOCK layout: full-screen clock (size 2 at y=0), date at
  y=19, separator at y=28, three data field rows at y=31/41/51;
  hide node name/battery indicator on clock page
- Persist dashboard_fields[3] in NodePrefs and DataStore
- Add isAckPending() to MyMesh for NearbyScreen ping detection
- Wire NearbyScreen into ToolsScreen and UITask
- Reduce NearbyScreen entry buffer from MAX_CONTACTS(100) to 32,
  saving ~3.3 KB RAM
- Switch haversine/bearing math from double to float (sinf/cosf/atan2f),
  eliminating double libm and saving ~5-10 KB flash

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 11:28:36 +02:00
Jakub
7b63af1068 refactor: use PopupMenu in keyboard and context menus, fix bugs
- KeyboardWidget: replace inline placeholder overlay with PopupMenu
- UITask QuickMsgScreen: replace inline CONTACT_PICK/CHANNEL_PICK context
  menus with PopupMenu; notif label stored in _ctx_notif_item at open time
- BotScreen: clear placeholder list for trigger field (expansion tokens
  would never match incoming literal text)
- MsgExpand: fix APPEND macro off-by-one (oi+_l < out_len-1 → < out_len)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 08:06:26 +02:00
Jakub
2f32683494 refactor: extract popup menu into reusable PopupMenu struct with wrap-around navigation
- Add PopupMenu.h: generic scrollable overlay with wrap-around nav (up from
  first wraps to last, down from last wraps to first), closes on ENTER or
  CANCEL/CONTEXT_MENU. Default constructor ensures active=false on creation.
- KeyboardWidget: replace inline placeholder overlay (~50 lines) with PopupMenu
- UITask QuickMsgScreen: replace inline context menus in CONTACT_PICK and
  CHANNEL_PICK with PopupMenu; notif label stored in _ctx_notif_item at open time
- BotScreen: clear placeholder list for trigger field (placeholders are
  expansion tokens that would never match incoming literal text)
- MsgExpand: fix APPEND macro off-by-one (oi+_l < out_len-1 → < out_len)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 08:06:07 +02:00
Jakub
42b51935b1 feat: sensor placeholders in messages and keyboard picker
expandMsg now expands {temp}, {hum}, {pres}, {batt}, {alt}, {lux}, {dist},
{co2} from live sensor readings. The [{}] picker in the on-screen keyboard
shows only sensors currently detected by SensorManager and scrolls when
more than 3 items fit the overlay.

New SensorPlaceholders.h helper keeps sensor-to-keyboard wiring out of
UITask.cpp. MsgExpand.h handles all expansion logic centrally; MyMeshBot
and QuickMsgScreen pass &sensors and batt_volts to each call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 00:12:07 +02:00
Jakub
26d3d7ab00 refactor: extract fullscreen message view into reusable FullscreenMsgView module
Shared struct handles rendering and input for both DM and channel fullscreen
views, replacing ~80 lines of duplicated inline code. Fixes navigation arrow
indicators being swapped in channel fullscreen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 23:54:26 +02:00
Jakub
c7887a940d feat: DM history fullscreen message view on ENTER
Pressing ENTER on a selected DM message now opens a fullscreen view
(matching channel history behavior) instead of going directly to compose.
Fullscreen supports UP/DOWN scrolling for long messages, LEFT/RIGHT to
navigate between messages, and ENTER/CANCEL to return to the list.
Compose is still accessible via [+ send] (ENTER when nothing selected).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 23:40:15 +02:00
Jakub
4063c2d296 style: shorten Del label to [Dl] to fit 25px special row cell
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
wio-tracker-v1.15-plus.1.2
2026-05-13 21:09:47 +02:00
Jakub
464f5cd7a2 fix: remove label prefix from keyboard preview for visual consistency
All three keyboard instances (Settings, QuickMsg, Bot) now show plain
text + cursor without any field-name prefix. Removes the label field
from KeyboardWidget entirely — the screen title already indicates context.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
5fecafd4f9 fix: KeyboardWidget preview buffer size and KEY_CONTEXT_MENU handling
- preview[] increased from 32 to 40 bytes to safely fit max label (15 chars)
  + 20 chars text + cursor + null without snprintf truncation
- KEY_CONTEXT_MENU (long-press ENTER) now treated as CANCEL in keyboard
  input handler, restoring BotScreen behavior lost in the refactor

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
07bd5f1167 refactor: extract on-screen keyboard into reusable KeyboardWidget module
All three keyboard implementations (SettingsScreen, QuickMsgScreen, BotScreen)
now share a single KeyboardWidget struct defined in KeyboardWidget.h, eliminating
~480 lines of duplicated render/input logic. Also fixes the SettingsScreen bug
where navigation keys were checked before the placeholder overlay, causing cursor
movement when the overlay was open.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
60867a4c9e fix: check getContactByIdx return value in DM context menu handler
Prevents using uninitialized ContactInfo if the lookup fails.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
eb0f436924 feat: DM per-contact notification settings and mark-as-read
Long-press ENTER in the DM contact list opens a context menu with:
- "Mark as read": clears unread counter for that contact
- "Notif: default/OFF/ON": cycles notification state (same as channels)

Notification state is persisted in NodePrefs (dm_notif[16] table,
keyed by 4-byte pub_key prefix). When muted, the buzzer is silenced
for that contact's messages; when force-on, buzzer plays even in
quiet mode. Default follows the global buzzer setting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
b50f21060c fix: clear DM and channel unread counters on app sync completion
When companion app finishes syncing all messages (msgRead(0)),
clear both _dm_unread_table and _ch_unread so indicators match
the app's read state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
4a4ff76f10 feat: DM chat history, per-contact unread badges, bot history, contact index fix
- Add DM_HIST phase in QuickMsgScreen: 32-entry ring buffer tracks incoming
  and outgoing DMs per contact; selecting a contact now opens history view
  before compose
- Per-contact DM unread badge in contact list (replaces hop count display)
- Bot replies (DM and channel) stored in history so they appear on-screen
- Fix contact index truncation: _sorted was uint8_t (max 255), causing
  contacts at indices 256-349 to map to wrong entries; changed to uint16_t
- Add addDMMsg() virtual to AbstractUITask; wire through MyMesh.cpp
- Incoming chat DMs call addDMMsg(); outgoing after successful send too
- msgRead(0) from companion app clears per-contact unread table

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00