4673 Commits
Author SHA1 Message Date
3urobeat 02726edcdc feat: Mask PIN input on lockscreen 2026-09-16 18:44:24 +02:00
Scott Powell 3caf033de2 * refactor: removed UITask dependency from MyMesh. New MyMesh::Listener interface
* UITask's are now a MyMesh task Listener
2026-09-16 21:03:44 +10:00
JakubandClaude Sonnet 5 95e3cf2618 feat(radio): honest TX power for GAT562 30S external PA
gat562_30s_mesh_kit has an always-on external PA rated at +30dBm, but the
firmware wrote tx_power_dbm straight to the SX1262 register (capped at 22),
so the app/CLI/UI never matched the real radiated power and couldn't ask
for more than 22 even though the hardware supports it.

Add a generic, macro-gated PA gain-curve resolution in
RadioLibWrapper::setTxPower() (the single choke point every TX-power path
already funnels through), reusable by any future board with a similar PA.
For this board, reuse the vendor-measured 869MHz curve from the open
upstream Meshtastic PR that added it for the same physical module
(meshtastic/firmware#11212): pick the lowest SX1262 setting whose measured
output reaches the requested dBm, clamped at the PA's saturation knee.
Bump LORA_TX_POWER/MAX_LORA_TX_POWER to 30 for this variant only -- the
other three GAT562 boards use the SX1262's own PA_BOOST (22dBm, no
external PA) and are unaffected.

Since a request below the PA's floor gain (14dBm) still radiates at that
floor, also make CommonCLICallbacks::setTxPower() return what was actually
applied, and have every setter (BLE CMD_SET_RADIO_TX_POWER, CLI "tx",
simple_repeater/room_server/sensor) persist and report that value instead
of the raw request -- so tx_power_dbm never silently disagrees with the
physical output in either direction.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-16 12:54:48 +02:00
ripplebizandGitHub 0ec5ed5fe7 Merge pull request #3393 from jbrazio/fix/static-pool-hardening
Improve null checks and index validation in StaticPoolPacketManager
2026-09-16 12:26:29 +10:00
ripplebizandGitHub a2d43e8df8 Merge pull request #3248 from meshcore-dev/wio-tracker-L1-1W
WIO Tracker L1 1W variant
2026-09-16 11:50:09 +10:00
Liam CottleandGitHub ec28f6c605 Merge pull request #3415 from und3rX1337/fix/config-key-digits
fix(config): allow digits in config keys
2026-09-16 11:07:31 +12:00
Und3r_1337 9d6d53a71a fix(config): allow digits in config keys so keys like gr_1hop round-trip 2026-09-16 01:01:14 +02:00
Frieder SchrempfandGitHub 9d0e2f2fac Merge branch 'dev' into dev 2026-09-15 22:59:09 +02:00
Scott Powell 3a3c13a143 * refactor to support new Board::handleCommand(), and "get/set radio.fem.*" commands 2026-09-15 18:41:11 +10:00
Scott Powell 53b6476409 Merge branch 'dev' into wio-tracker-L1-1W 2026-09-15 18:24:52 +10:00
Scott Powell 3819a0bd89 * subscribe response now returns expiry_secs (not timestamp) 2026-09-15 17:17:48 +10:00
AvamanderandGitHub 61fcc49c8a Fix Heltec Mesh Node T1 GNSS pins 2026-09-14 17:53:50 +03:00
Huw DuddyandGitHub e6dd8f08dd Merge pull request #3344 from robekl/docs/fix-stale-defaults
docs: correct stale defaults and stale references across docs 🤖🤖
2026-09-14 23:28:22 +10:00
Huw DuddyandGitHub 6b2c35449c Merge pull request #3390 from jbrazio/feature/is-gps-detected
Add isGPSDetected() capability probe to SensorManager
2026-09-14 22:41:37 +10:00
Huw DuddyandGitHub 5f5f4958bf Merge pull request #3002 from entr0p1/feat/nrf52-pwrmgt-v2_3-prep1
nRF52840 Power Management Stage 1 (Boot Lock) - v2.3 preparation work
2026-09-14 21:26:16 +10:00
Scott Powell bc8ccefafe * fix for telemHasChanged() 2026-09-14 20:23:10 +10:00
TadyTheFish a88de69486 Enable INA226 support for ProMicro nRF52840 2026-09-13 08:27:48 +02:00
ripplebizandGitHub 34404309ae Merge pull request #3350 from Quency-D/fix-heltec_v4r8-lna
fix-heltec_v4r8-lna
2026-09-13 12:05:35 +10:00
ripplebizandGitHub 000a1b6cda Merge pull request #3371 from agessaman/feat/tbeam-1w-thermal-fan
Add thermal fan control for T-Beam 1W
2026-09-13 12:01:37 +10:00
Scott Powell c6b051d063 * request payload doco 2026-09-13 11:40:25 +10:00
Scott Powell df82dc2339 * min_deltas now mandatory 2026-09-13 11:05:38 +10:00
JakubandClaude Opus 5 1d5dbe2f9a feat(companion): contact expiry + prune, and scope-list follow-up fixes
Settings > Contacts gains "Expire" (Off/7d/30d/90d) and a "Prune now" action
that counts first and asks before removing anything. A contact with no
advert/update within the threshold is eligible; favourites are always kept.
Thresholds and their labels live in one NodePrefs table so the age shown and
the age enforced can't drift. SCHEMA_SENTINEL -> 0xC0DE002C (sizeof unchanged
at 2824, confirmed on native and a real WioTrackerL1 build).

Also fixes four bugs in the scope list from 89c02eea:

- removeScope() saved /scopes1 but never savePrefs(), so the ch_scope_idx[]
  and repeat_extra_scope_mask fix-ups it makes were lost on reboot, leaving
  shifted entries against unshifted indices.
- CMD_SET_DEFAULT_FLOOD_SCOPE wrote the legacy fields directly instead of
  going through setPrimaryScope(), so the app's default-scope setting had
  nothing reading it once sends resolved through the list.
- The reverse direction was stale too: an on-device "Set as default" never
  refreshed default_scope_name/key, so CMD_GET_DEFAULT_FLOOD_SCOPE reported
  a scope the device had stopped using. New syncLegacyDefaultScope().
- Upgrading from the old single Scope field set the default (so DMs kept it)
  but left every channel unscoped, since "*" means unscoped, not "inherit".
  loadScopeList() now reports when it migrated and begin() seeds the channels
  that already exist, leaving empty slots alone.

"Set default" -> "Set as default", and setting it now says what it governs
("Default: DMs + relay") rather than leaving the [default] marker to imply
more than it does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 21:55:46 +02:00
21cdd7b319 Add Muzi Base board support (Duo + Uno + SuperIO)
One nRF52840 variant shared across both radios, picked per env: Duo runs
the LR1121, Uno the SX1262. Same PCB, only the radio and its DIO1 pin
change.

SuperIO adds the SH1107 OLED, GPS, buzzer and joystick. GPS is driven by
the 3-position mode switch (Mode 2 = on), polled live the way thinknode_m1
does it.

Five quick user-button presses power the unit off; a press wakes it back
on (arms the button as an nRF52 GPIO SENSE source before SYSTEMOFF).

Consolidates #2054 and andyshinn's shared-base/uno work, rebased on dev.

Co-authored-by: lbibass <ewdries02@gmail.com>
Co-authored-by: Andy Shinn <andys@andyshinn.as>
2026-09-12 05:41:26 -07:00
Scott Powell d69ae98b5a * telemHasChanged() mod, so that only _specified_ min_delta values are considered. 2026-09-12 16:53:45 +10:00
JakubandClaude Sonnet 5 89c02eea27 feat(companion): shared named-scope list, per-channel/repeater popup pickers
Replace the single device-wide Scope text field with a small named list
(wildcard "*" fixed first, plus a movable default): each channel picks one
scope of its own (matching the app's per-channel region picker, surfaced in
the channel-history title), and the repeater's Extra scopes multi-selects
from the same list instead of comma-typed names. Both pickers are popups
(PopupMenu gains addCheckItem()/a fillable-square checkbox glyph) over the
existing screen rather than a full-screen takeover.

Also fixes a stray-bits bug where a pre-existing prefs file's own sentinel
tail could read into the new repeat_extra_scope_mask/ch_scope_idx fields and
silently reappear as a real pick later once the scope list grew into that
range.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 00:05:40 +02:00
ripplebizandGitHub b599bc5117 Merge pull request #3389 from jbrazio/fix/set-af-validation
Validate set af input to prevent out-of-range airtime factors
2026-09-11 21:25:31 +10:00
JakubandClaude Sonnet 5 7533c70ee2 fix(ui): message bubble color, received-message hop count, list wrap-around, age fallback; add sim WASM build workflow
- Unselected message bubbles no longer carry a stray light-filled header
  strip behind the sender name -- the one place with a filled patch on an
  otherwise plain row. Selected fill is unchanged.
- Received DMs and channel posts now show their hop count as the same tiny
  digit icon a sent message already uses for its repeater/echo count, using
  the hop path the mesh already records for incoming messages.
- DM/channel history list: UP at the oldest message now wraps straight to
  the newest (not the compose row), closing the loop the same way every
  other list in the UI does at its ends.
- A received message's age no longer goes blank when its timestamp reads
  slightly ahead of the device's own clock (sender/receiver skew, or an
  unsynced receiver) or is genuinely unknown -- both now show the receipt
  time instead of leaving the row with just a name and hop count.
- release-notes.md: add the v1.28 section covering the above plus this
  cycle's already-committed dev fixes (GPS altitude field, units
  compliance, noise floor, Msg wake location, bot relay tracking, PR #34).
- Add .github/workflows/build-solo-sim.yml: builds the companion+repeater
  WASM sim via Emscripten and attaches it as a fixed-name asset to the tag's
  draft release, independent of the hardware firmware build job.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 12:30:42 +02:00
ripplebizandGitHub 0984111d62 Merge pull request #3395 from jbrazio/fix/serial-command-clamp
[HIGH] Ensure command buffers stay NUL-terminated to prevent overflow
2026-09-11 14:09:24 +10:00
ripplebizandGitHub 5c63e06a00 Merge pull request #3386 from tekk/fix/channel-data-payload-bound
fix(companion): gate CMD_SEND_CHANNEL_DATA payload on MAX_GROUP_DATA_LENGTH
2026-09-11 13:57:00 +10:00
JakubandClaude Sonnet 5 64b79d00eb fix(ui): wrap-around at both ends of the DM/channel history list
Every other list in the UI (contacts, channels, message-pick, the
mode tabs) wraps at its ends; the DM and channel history lists were the
one exception -- UP at the oldest message (top of the list) just
stopped instead of wrapping to the compose row (the list's own
bottom-most stop), and DOWN from the compose row didn't wrap back to
the oldest either. Both directions now close the loop, same as
everywhere else.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 21:31:31 +02:00
JakubandClaude Sonnet 5 aa73400414 fix(ui): community v1.27 feedback — GPS altitude field, noise floor, Msg wake location, units
- Add a separate "Altitude (GPS)" dashboard field (label AltG) alongside
  the existing barometric one (now labelled "Altitude (Baro)") -- the
  original single Altitude field only ever read a barometric sensor's
  CayenneLPP telemetry, showing "--" on any board without one even with
  a perfectly good GPS fix.
- Every altitude reading (baro + GPS, Clock and Lock screen) and the GPS
  home page's "alt" row now go through a shared fmtAlt() helper and
  respect Settings > System > Units, same as every other distance in the
  UI -- none of them checked units_imperial before. Same fix for
  Locator's Radius row, which always showed metric regardless.
- Radio home page's noise floor showed "n/a" whenever Pwr save (RX
  duty-cycle) was on, on the assumption duty-cycle RX can't sample the
  floor. That's stale: RadioLibWrapper already periodically recalibrates
  it (noiseFloorCalibCheck(), every 60s) even under duty-cycle, which is
  exactly why Diagnostics' unconditional read of the same value was
  already showing something real. Radio home page now matches.
- Moved "Msg wake" from Settings > Sound to Settings > Display -- more
  logical home for a display-wake toggle than notifications. Menu-order
  only; NodePrefs storage is unaffected by this enum's ordering.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-10 20:12:29 +02:00
João Brázio f722794e50 Validate set af input to prevent out-of-range airtime factors 2026-09-10 09:17:37 +01:00
tekk 0a82fcd20d fix(companion): gate CMD_SEND_CHANNEL_DATA payload on MAX_GROUP_DATA_LENGTH
Payloads of 166 or 167 bytes previously passed the frame-sized bound
(MAX_CHANNEL_DATA_LENGTH = 167) but were rejected by sendGroupData
against MAX_GROUP_DATA_LENGTH (165), causing ERR_CODE_TABLE_FULL (retry
later) to be returned instead of ERR_CODE_ILLEGAL_ARG.

Fixes #3345
2026-09-09 22:10:41 +02:00
JakubandClaude Sonnet 5 6e0e981048 fix(companion): arm the "Relayed by" tracker for bot-originated channel posts
Remote Bot's channel trigger reply, channel command reply, and !gps fix
result all mirrored their send into the on-device history with
addOwnChannelMsg() but never called armChannelRelay() afterward, unlike
the app's CMD_SEND_CHANNEL_TXT_MSG send which already did both -- so a
bot's own channel posts never showed which repeaters echoed them back,
while everything else did.

Factor the always-together pair (mirror into history, then arm relay
tracking from the send sendGroupMessage's sendFloodScoped already
tracked) into MyMesh::mirrorOwnChannelMsg(), and route all four call
sites through it instead of pairing the two calls by hand -- the bot
bug was exactly a dropped second call, so a new call site can no longer
add the first half without the second.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-09 16:22:04 +02:00
Jakub 42afd180f9 Merge pull request #34 from 3urobeat/fix/message-channel-create-refresh
fix: Fix message channel list not updating instantly

Creating a channel didn't rebuild the Messages screen's cached channel
list until you backed all the way out and reopened it. Tracks whether
the Add/Edit channel form was active on the previous render and rebuilds
the list once it closes.
2026-09-09 14:19:32 +02:00
Liam CottleandGitHub 1751783931 Merge pull request #3357 from skandragon/feat/picow-wifi-ble
Add WiFi to RPI PicoW (and perhaps others)
2026-09-09 21:55:22 +12:00
Liam CottleandGitHub 017fd09633 Merge pull request #2971 from YoshiWalsh/fix/projectconfigurationgit
Fix Git error during project configuration
2026-09-09 20:08:37 +12:00
Yoshi Walsh 82ea992d86 Fix Git error during project configuration
Previously project configuration would fail with "fatal: couldn't find remote ref d541301" (git v2.55.0)

Per https://git-scm.com/docs/git-fetch#Documentation/git-fetch.txt-refspec, the <src> needs to be either a ref or a "**fully spelled** hex object name"

This requirement is also mentioned in this SO answer: https://stackoverflow.com/a/30701724

After this change, project configuration now succeeds.
219812
2026-09-09 17:42:35 +10:00
Jakub 48cfdff1f3 Merge main into dev (README live-demo link) 2026-09-08 23:50:48 +02:00
JakubandClaude Sonnet 5 de2fceca7c fix(companion): stop the FIRMWARE_VERSION fallback claiming a fixed version
Same staleness bug as the build-date fallback: bypassing build.sh (a
direct `pio run`, an IDE Build button, the sim) fell back to a hardcoded
"v1.27-dev" literal that would silently keep claiming to BE v1.27 forever,
even once development has moved well past it. build.sh/CI's real
git-tag-derived FIRMWARE_VERSION is untouched -- only the bypass fallback
changes, to a version-agnostic "dev-<build date>".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-08 23:48:27 +02:00
JakubandClaude Sonnet 5 d72fed66c1 docs(readme): link the live browser demo at the top
Fills in the "Try it out live" placeholder that's sat empty since the
website went up.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-08 23:48:20 +02:00
3urobeat 6f8703842c feat: Add PIN input keyboard and use it on lockscreen 2026-09-08 22:25:39 +02:00
3urobeat a16c9bf2da chore: Make sure lockscreen always wins 2026-09-08 21:13:52 +02:00
3urobeat 39f2d7edc9 fix: Fix creating message channels not rebuilding channel list immediately 2026-09-08 19:18:51 +02:00
JakubandClaude Sonnet 5 6426ef9319 docs(readme): add 3urobeat to contributors
PR #32 (lock screen title bar), merged into v1.27.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v1.27
2026-09-08 16:04:54 +02:00
JakubandClaude Sonnet 5 e9f590ad40 docs(release-notes): add contacts-flush fix, Sats field, popup marquee fix
Three v1.27 changes this cycle weren't reflected yet: the low-battery/
reboot contacts-persistence fix (matching the settings one already
documented), the Clock page's new Sats field, and the Path/Relayed by
popup's marquee glitch + slowness fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-08 16:03:37 +02:00
JakubandClaude Sonnet 5 e3279646f9 refactor(companion): unify every reboot/power-off through UITask::shutdown()
Settings > Reboot, the phone app's CMD_REBOOT, and the serial CLI's
"reboot" command each hand-rolled their own flush-then-board.reboot()
sequence, so each could drift independently (Settings > Reboot and the
CLI command were both missing the contacts flush added in the previous
commit). Add a pure-virtual AbstractUITask::shutdown(bool restart) --
every UI variant (ui-new/ui-tiny/ui-orig) already implements a method
with this exact signature -- and route all three call sites through it
instead. CMD_FACTORY_RESET intentionally keeps calling board.reboot()
directly: flushing stale in-RAM prefs back out would defeat the format
it just did.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-08 16:03:37 +02:00
JakubandClaude Sonnet 5 a868a60935 fix(companion): flush pending lazy contacts write on shutdown/power-off
Contact updates (advert path cache, new/removed contacts, favourites) are
lazily debounced (dirty_contacts_expiry, 5s) to avoid wearing flash on
every mesh packet. CMD_REBOOT already flushed this before rebooting, but
UITask::shutdown() -- the low-battery auto-shutdown and long-press
power-off path -- didn't, so under continuous mesh traffic (which keeps
re-arming the debounce timer) a pending write could sit unflushed for a
whole session and be lost when one of those paths fired. Add
MyMesh::flushDirtyContacts() and call it from shutdown() alongside the
existing savePrefs()/saveRTCTime() flush.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-08 13:27:45 +02:00
JakubandClaude Sonnet 5 23ccc06d1d fix(ui): stop the Path/Relayed by popup fighting its own row for marquee state
Two distinct bugs behind "Relayed by" scrolling looking broken:

1. The DM/channel history row underneath a context menu (Path/Relayed by,
   Options, etc.) keeps rendering every frame with sel=true regardless of
   the popup on top of it, and both it and the popup's own selected item
   share DisplayDriver's single marquee slot. Whichever drew last each
   frame kept is_new-resetting the other's animation -- looked like the
   scroll "remembered" a stale, wrong position. Now suppressed via
   `sel && !_ctx_menu.active` at all four call sites that had it (DM/
   channel history bodies, contact/channel pick names).

2. PopupMenu::render() returns 50 (it wants a fast, smooth redraw), but
   all 7 `_ctx_menu.render(display)` call sites discarded that return
   value, so the enclosing phase's own return statement (500-2000ms)
   governed the actual redraw cadence instead -- the popup's marquee only
   ever advanced whenever the much slower underlying screen happened to
   redraw. Now folded into the existing mq_delay accumulator, same as
   every other marquee source in this file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-08 13:04:36 +02:00
liamcottle cee43752c7 add new ENABLE_WIFI_INTERFACE build flag for companions without providing ssid and pwd 2026-09-08 22:54:50 +12:00