Commit Graph

833 Commits

Author SHA1 Message Date
Jakub
2fc9e094e7 Settings: add Home Pages visibility control
New "Home Pages" section in Settings lets the user toggle which pages
appear in the home screen carousel. Settings and Messages are always
shown; Clock, Recent, Radio, Bluetooth, Advert, GPS, Sensors, Tools
and Shutdown can each be hidden individually.

- NodePrefs: home_pages_mask uint16_t (bit per page, 0=all visible)
- HomeScreen: isPageVisible/navPage skip hidden pages; dots indicator
  counts only visible pages; enforces visible page on re-entry
- SettingsScreen: SECTION_HOME_PAGES with per-page ON/OFF toggles
- DataStore: persisted at offset 1598
- Default: all pages visible (0x01FF)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
95805da4b2 RingtoneEditor: play note preview on pitch/octave change
When UP/DOWN changes pitch or ENTER cycles octave, a short 1/16-note
preview (BPM 240) is played immediately so the user hears the current
note while composing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
4891a36c72 Add ringtone editor, buzzer volume, unread fixes and buzzer notification fixes
- RingtoneEditorScreen: 32-note step sequencer accessible via new Tools page on home screen. U/D=pitch, ENTER=octave cycle, L/R=cursor, MENU=options (play, duration, BPM, insert/delete, save). Notes stored packed in NodePrefs and persisted via DataStore.
- ToolsScreen: new "Tools" home page entry launching the editor
- Buzzer volume: 0-4 bar control in Settings > Sound, persisted to prefs
- Buzzer notifications: fixed notify() firing inside wrong serial-guard branch and before addChannelMsg set the channel index
- Unread counter: fixed newest-first index direction; watermark formula now correct
- OLED brightness: wider range via pre-charge register 0xD9 combined with contrast

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
ffa568938f Settings: add buzzer volume control (5 levels, like brightness)
- NodePrefs: add buzzer_volume field (0=min..4=max, default 4)
- DataStore: persist buzzer_volume at end of prefs file (backward-compat)
- buzzer: setVolume()/getVolume(); applyVolume() sets PWM duty cycle via
  analogWrite() after each rtttl::play() call to control output amplitude
- Settings UI: new BzrVol bar item in Sound section (left/right to adjust)
- UITask: apply saved volume on startup alongside brightness

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
861a65d4b1 Fix buzzer notifications, unread tracking, OLED dimming; remove dead code
- MyMesh: notify() now always fires for channel/contact messages regardless
  of serial connection; called after addChannelMsg so channel index is set
- ChannelHist: fix unread watermark direction (histEntry is newest-first,
  index 0=newest); counter now decrements correctly as user reads messages
- ChannelHist: reset unread to 0 after sending (user is active in channel)
- SH1106Display: add pre-charge register (0xD9) to setBrightness for wider
  dimming range; re-apply contrast+precharge in turnOn(); tune curve
- UITask: remove MsgPreviewScreen dead code (~180 lines, never shown)
- UITask: fix strcpy→snprintf in showAlert()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
40f470ebf2 Sensors page: show placeholders for all connected sensors; support all LPP types
- SensorManager::getAvailableLPPTypes() reports which LPP types will be
  present based on initialized sensor flags (temp, humidity, pressure, etc.)
- Sensors page renders one row per available type; shows "--" for any sensor
  that hasn't produced a reading yet instead of hiding the row entirely
- LPPReader: add readLuminosity, readPercentage, readConcentration, readDistance
- All known LPP types now have proper names and units in the sensor display

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
9d92fb62df Buzzer: add playForced(); fix per-channel force-on notification ignoring global mute
buzzer.play() was returning early when _is_quiet=true, so channels with
explicit notification=ON were silent even though the logic correctly set
play=true. playForced() bypasses the quiet check for these overrides.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
1281ec5c35 ChannelHist: lazy unread counter + fullscreen left/right message navigation
- Unread badge now clears gradually as the user navigates through messages
  instead of instantly on channel entry; counter tracks the highest-seen
  message watermark so navigating back doesn't re-add unread count
- Fullscreen view: LEFT/RIGHT switch between messages in the thread (resets
  scroll); < > arrows shown at bottom corners when adjacent messages exist
- Navigating in list (UP/DOWN) or entering fullscreen also marks messages read

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
f603d82b0c MsgPreview: fix nav-to-home from chat screen; add fullscreen left/right message switching
- msgRead(0) now only navigates to home when curr == msg_preview, preventing
  unexpected redirects while the user is in the chat selection screen
- Fullscreen message view: LEFT/RIGHT navigate between unread messages (resets
  scroll to top); < > hints shown at bottom corners when more messages available

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
eea9254212 Fix several bugs found in code review
- DataStore: persist custom_msgs, ch_notif_override/muted, dm_show_all,
  room_fav_only — none were being saved/loaded, all settings lost on reboot.
  Load uses file.available() guard for backward compat with old saves.
- QuickMsgScreen reset(): clear _ctx_open/_ctx_dirty/_ctx_sel so context
  menu state is clean when re-entering the messaging screen.
- Remove unused _ctx_ch_idx field (code always uses _channel_indices[_channel_sel]).
- _room_unread: cap at _msgcount before incrementing to prevent badge
  going negative (getMsgCount() - getRoomUnreadCount()).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
4b06dff4e7 Channel context menu: defer flash write until CANCEL closes menu
savePrefs() is now called once when the user closes the menu with
Back/Cancel, only if the notification state was actually changed
(_ctx_dirty flag). Toggling multiple times before closing still
results in a single write.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
158bbf74c8 Unread badges per category + context menu stays open on action
- newMsg() now takes contact_type; room server messages (ADV_TYPE_ROOM)
  tracked separately in _room_unread; DM badge = msgCount - roomUnread
- MODE_SELECT shows unread badges for all 3 rows: DM, Channels, Rooms
- Room unread cleared when user opens Room Servers list or msgRead(0)
- Channel context menu: ENTER performs action but stays open; only
  CANCEL (Back button) closes it so user can see the updated state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
1a1cda4a1a Settings: contact filter toggles for DM and Room Servers
Adds SECTION_CONTACTS with two items under Settings:
- DM: "fav" (default) / "all" — controlled by dm_show_all in NodePrefs
- Rooms: "all" (default) / "fav" — controlled by room_fav_only in NodePrefs

Both fields default to 0, preserving current behaviour on firmware upgrade.
buildContactList() reads the prefs to filter accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
9cbbaa8852 Messages: add Room Servers as third option alongside DM and Channels
Adds ADV_TYPE_ROOM contacts as a selectable category in the MODE_SELECT
screen. Reuses the existing CONTACT_PICK → MSG_PICK → send flow via a
_room_mode flag; sendMessage() works identically for room servers.
All room server contacts are listed (no favourites filter).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
1ba59d2236 Home Messages page: show total unread count (DM + all channels)
Sums getMsgCount() (DM unreads) and getTotalChannelUnread() (sum of
_ch_unread[] across all channels) and displays "N unread" under the
"Messages" heading when non-zero.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
bd07ff6099 Direct message: show only favourited companions
The contact picker now lists only contacts of type ADV_TYPE_CHAT
that have the favourite flag set. Shows "No favourites" when none
are starred. Asterisk prefix removed since all entries are favourites.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
a26bde6ce5 Per-channel notification settings via long-press context menu
Long-pressing ENTER on a channel in the channel picker opens a
2-item context menu: "Mark all read" and "Notif: default/OFF/ON".
Muted channels still increment the unread counter silently; force-ON
overrides global buzzer mute. Settings persist to flash via NodePrefs
(ch_notif_override / ch_notif_muted bitmasks).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
aa99b77de4 WioTrackerL1 UI v1.15.1: bugfixes and UX polish
- Fix placeholder picker colors: dark box + white border, light fill
  for selected item (consistent with rest of UI)
- Fix placeholders not expanded when sending from keyboard (expandMsg
  was only called in MSG_PICK path, not KEYBOARD path)
- Fix sent messages incrementing unread counter: set _phase=CHANNEL_HIST
  before addChannelMsg so viewing=true
- Fix keyboard column inverse mapping on UP from special row
- Remove old_row dead code from QuickMsgScreen keyboard handler
- Alert banner shown only on home screen, suppressed in settings/messages
- Bump FIRMWARE_VERSION to v1.15.1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
56788e4b58 WioTrackerL1 UI: predefined messages, caps lock, placeholder picker, 139-char limit
- Settings → Messages section: 10 user-editable message slots (M1–M10)
  with on-screen keyboard editor; slot M1 defaults to "OK" on first boot
- Keyboard: add Caps Lock [^] button — stays highlighted when active,
  all letters display and insert as uppercase until toggled off
- Keyboard: add [{}] placeholder picker overlay — inserts {loc} or {time}
  into the message at cursor position; available in both compose and
  settings slot editor
- Max message length raised to 139 chars to match the companion app;
  ChHistEntry text buffer extended to 140 chars
- NodePrefs: custom_msgs slot size extended to 140 chars per slot
- KB constants (KB_MAX_LEN, KB_CHARS, etc.) moved before SettingsScreen
  so both SettingsScreen and QuickMsgScreen can reference them

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
6779b3489b WioTrackerL1: redesign companion UI (channels, keyboard, msg preview)
- QuickMsg → Message screen: channel history, on-screen keyboard, custom message first
- Channel history: RAM ring buffer (32 entries, 80 chars), fullscreen view with word-wrap scroll
- After channel send: stay in history, own message shown as "Me: ..."
- MsgPreview: one message per screen with up/down nav, ENTER opens fullscreen scroll
- Monochromatic display: all color usage corrected to DARK/LIGHT only
- Top bar: battery icon, BT muted indicator, splash "Plus for Wio"
- KB_MAX_LEN: 64 → 100 chars; channel text buffer: 52 → 80 chars

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
81c69f89c6 Splash screen: show unofficial build marker for Plus builds
Add FIRMWARE_PLUS_BUILD compile flag to both companion sections in
platformio.ini. When set, SplashScreen shows "+ unofficial build" in
yellow below the build date. Avoids runtime string parsing — the
existing version stripping logic (cuts at first dash) is unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
60de6630fa SettingsScreen: skip flash write if no settings changed
Track dirty state per-field in handleInput. savePrefs() on exit only
if at least one value was actually modified. Buzzer is excluded — it
already saves immediately via toggleBuzzer(). markClean() resets the
flag each time the settings screen is opened.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
4e83e99316 Fix null pointer dereferences in SettingsScreen index helpers
lowBatIndex(), autoOffIndex(), gpsIntervalIndex() each called
getNodePrefs() independently without null check, while renderItem()
had its own guarded copy. Added null checks consistent with the rest
of the class. Also initialize _node_prefs to NULL in UITask constructor.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
35b2b008c8 Battery %: use LiPo discharge curve with variable shutdown threshold
Replace linear voltage-to-percent formula with a LiPo discharge curve
lookup table and linear interpolation. The curve is then rescaled so
that low_batt_mv (configurable shutdown threshold) maps to 0% and
4200mV maps to 100%, preserving the non-linear shape regardless of
the chosen threshold.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
03085f0715 QuickMsgScreen: show only companion (chat) contacts
Filter contact list to ADV_TYPE_CHAT only — excludes repeaters, rooms,
and sensors which cannot receive direct chat messages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
20824c2db4 Fix: use out_path_len instead of path_len in QuickMsgScreen
ContactInfo has out_path_len, not path_len.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
18c13002db QuickMsgScreen: sort favourites first, mark with asterisk
Favourite contacts (flags & 0x01 set by companion app) appear at the
top of the contact picker, marked with '*'. Non-favourites appear
below. The favourite bit is already synced by the existing protocol
via CMD_ADD_UPDATE_CONTACT — no protocol changes needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
af2aca151f Add hardware watchdog and QuickMsgScreen
Watchdog: nRF52840 hardware WDT with 30s timeout, runs during sleep.
Resets device if main loop stalls for >30s. Pets in every loop()
iteration so normal operation never triggers it.

QuickMsgScreen: new UI screen accessible from home screen pages.
Two-phase UI: contact picker (shows all contacts with hop count),
then predefined message picker (location, I'm OK, need help, on my
way, ETA 10min, ETA 30min). Location message uses live GPS fix if
available. Sends via the_mesh.sendMessage() and shows sent/error alert.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
1fe5922c79 Add RTC time persistence to flash
Save last known unix timestamp to /rtc_save on shutdown and restore it
on startup, so the clock shows correct time immediately after restart
without waiting for GPS fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:47 +02:00
Jakub
4d46abb2c1 feat(wio-tracker-l1): energy optimizations
- CPU sleep: board.sleep(0) in main loop puts NRF52 into WFE/sd_app_evt_wait
  between iterations; wakes on any interrupt (radio, BLE, timer). Largest
  power saving of the three changes.

- VBAT_ENABLE gating: voltage divider now powered only during ADC read
  (HIGH for 10ms, then LOW). Previously stayed HIGH continuously.
  Fixed in both variant.cpp (init) and WioTrackerL1Board::getBattMilliVolts().

- LED off with display: PIN_LED is cleared when auto-off triggers and
  restored (handed back to userLedHandler) when display wakes. Avoids
  idle LED current draw during screen-off periods.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:46 +02:00
Jakub
c3846ba9f5 feat(wio-tracker-l1): add settings screen and companion radio improvements
Settings screen (UI_HAS_JOYSTICK_UPDOWN build variant):
- Display brightness (5 levels, contrast curve tuned for SH1106 non-linearity)
- Buzzer on/off (respected at startup, no more forced unmute)
- TX power (2–22 dBm)
- Auto-off delay (5s / 15s / 30s / 60s / never)
- GPS update interval (off / 30s / 1min / 5min / 15min / 30min)
- Timezone offset (UTC-12..+14) for clock screen
- Low battery auto-shutdown threshold (off / 3.0–3.5V, default 3.4V)
- Battery display mode (icon / % / voltage)

Battery:
- EMA filter (alpha=0.2) on ADC readings to smooth voltage spikes from uneven load
- Battery % calculated using low_batt_mv as 0% anchor
- Mute icon repositions dynamically based on battery display width

Clock screen on HomeScreen showing time/date with timezone support.

Build:
- _settings variants added for USB and BLE companion radio builds
- Automatic UF2 generation on every build via create-uf2.py post-script
- UI_HAS_JOYSTICK_UPDOWN flag guards joystick_up/down to avoid breaking other boards

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:09:46 +02:00
Scott Powell
3751785400 * version 1.15.0 2026-04-19 11:27:55 +10:00
Scott Powell
d7a3d41843 Merge branch 'default-scope' into dev 2026-04-17 16:30:19 +10:00
Scott Powell
77d02e844f * bug fix 2026-04-17 14:38:03 +10:00
Scott Powell
df1e12de3e * Repeater, room server: rule change for sendFloodReply() 2026-04-16 13:22:39 +10:00
Scott Powell
d2fdd6fad4 * companion: FIRMWARE_VER_CODE now bumped to 11 2026-04-15 20:47:17 +10:00
Scott Powell
4131a455a2 * repeater: refactored 'region' CLI commands -> CommonCLI
* room server: added RegionMap, and new CommonCLI wiring, default_scope handling
* sensor: only minimal RegionMap wiring. Still needs work to handle default-scope
2026-04-15 13:32:49 +10:00
Scott Powell
569cfe177a * repeater: default-scope support 2026-04-14 17:51:34 +10:00
Scott Powell
efdd2b6a6c * companion: simplified the CMD_GET / CMD_SET _DEFAULT_FLOOD_SCOPE 2026-04-13 23:11:21 +10:00
Scott Powell
d131e8ae35 * companion: RegionMap now used in Datastore
* companion: new CMD_SET_DEFAULT_FLOOD_SCOPE
* support for regional builds with DEFAULT_REGION_SCOPE
2026-04-13 21:06:53 +10:00
Scott Powell
3b32f35288 * Companion: default scope 2026-04-10 17:01:41 +10:00
Wessel Nieboer
0a13ac7fc7 Default button polarity to active-LOW across all firmware types
Nearly all LoRa boards use a boot button that pulls to ground when
pressed.
2026-04-04 13:19:13 +02:00
terminalvelocity23
08de3fb01f Make sendNodeDiscoverReq() public for post-boot discovery 2026-03-30 22:35:05 +03:00
Scott Powell
2325973fec * Companion: applyGPSPrefs() now just in one place (moved out of UITask) 2026-03-25 16:26:51 +11:00
Alejandro Ramirez
f8dbdce6bb fix: apply persisted GPS enabled setting on boot for companion radio
The companion_radio example was not restoring the GPS enabled/disabled
preference from flash after reboot. The preference was being saved
correctly when toggled via the mobile app, but on boot,
sensors.begin() -> initBasicGPS() unconditionally sets gps_active=false
and nothing subsequently restored the persisted state.

Added applyGpsPrefs() (matching the pattern in simple_repeater,
simple_sensor, and simple_room_server) and call it from main.cpp
after sensors.begin() to ensure the GPS hardware is initialized
before the saved preference is applied.
2026-03-24 09:10:09 -05:00
liamcottle
1d61df72c3 add define for reserved group data type 2026-03-23 23:09:35 +13:00
liamcottle
c78f7133c9 reorder command args 2026-03-23 23:02:24 +13:00
liamcottle
ed326255d5 add support for direct paths when sending group data 2026-03-23 21:46:21 +13:00
Liam Cottle
91aed048e9 Merge pull request #1928 from dz0ny/feat/grp-data-upstream
feat: Add support for PAYLOAD_TYPE_GRP_DATA
2026-03-23 21:41:51 +13:00
Rastislav Vysoky
285fc685c5 allow to set lower LoRa frequency 2026-03-22 13:54:42 +01:00