- BLE (ESP32): fast advertising (20-30 ms) for 30 s, then 152.5 ms, as the
nRF52 interface; the library default kept 30-60 ms forever
- Live map tiles: the WiFi drops after a minute with nothing missing
- ES8311 to standby while the I2S clocks are off, set up again before the
amp comes on
- Buzzer: no channel (DMA out of memory) waits out the samples instead of
spinning above the UI loop
- OTA: the image header check waits for its 16 bytes
- Comments: P10 is just the LED; the LCD SPI clock is 40 MHz on the wire
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The speaker's I2S channel was only disabled when idle, which leaves MCLK
running. Its fractional divider (160 MHz / 39 1/16 = 4.096 MHz) makes a
comb of lines every 256 kHz: one at 869.632 MHz, in the EU narrow mesh
channel, lifted the LoRa noise floor from ~-102 to ~-85 dBm, and others
land near GPS L1 and BeiDou B1. The channel is now deleted and its pins
held low after each sound, and made again for the next one. Measured:
noise floor -102 dBm, outdoor first fix 87 s (was 620 s and more).
Also: TCXO at 3.0 V (the L2's part, as Meshtastic has it); GLONASS
enabled on the L76K after each power-up; begin() on a running receiver
no longer resets it (a reset threw away the acquisition in progress).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Home > GPS and Settings > System > GPS details: satellites on a sky plot
(filled = used in the fix, colour = C/N0), a bar per satellite with its
dB-Hz, fix state with time to first fix, DOPs, position, altitude, speed,
UTC and per-constellation counts; a Turn on button while GPS is off
- helpers/sensors/GpsSky.h parses GSV / GSA / GGA / RMC from any talker,
fed from MicroNMEALocationProvider behind -D GPS_SKYVIEW (L2 and the
sim only; L1 unchanged); the sim feeds it made-up NMEA
- Roadmap: GPS indoors diagnosed with it - on the USB cable the L76K
tracks satellites but never decodes navigation data; on battery it
gets UTC within seconds and a fix
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Message history archived per conversation on the SD card (HIST_ARCHIVE):
restored on boot, Older/Newer paging in threads, kept count selectable
- Settings > System > Storage: card and flash usage, breakdown by maps,
messages, trails and other, kept-per-chat setting, delete history;
reads the mounted volume and warns when the FAT partition is much
smaller than the card
- Saved trails on the card: every Save is a new file, Load opens a list
with load (framed on the map), GPX export and delete
- Diagnostics: GPS data rows, last start reason, last crash summary
- Messages restored before the clock is set show their date instead of 0s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- New env Wio_Tracker_L2_companion_solo_lvgl_v3 on pioarduino 55.03.312-1;
the 2.0.17 env stays until the new one passes the hardware checklist
- SerialBLEInterface: NimBLE variant (PIN pairing through encrypted,
authenticated characteristic properties; setAccessPermissions is a no-op)
- buzzer: i2s_std driver on IDF 5 (legacy driver kept for 2.0.17)
- ESPNOWRadio: IDF 5 callback signatures
- ui-lvgl: psramBuf() for trail drawing, tile cache, map marks, list rows,
keyboard maps and message metadata; internal heap back to 2.0.17 level
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- genericBuzzer I2S path (BUZZER_I2S): an audio task synthesises the notes
with eased edges and keeps the DMA queue fed with silence between sounds;
ES8311 setup; amp powered through the board only around sounds
- ui-core/SoundControl.h (mode, volume, built-in sounds, melody model),
SoundNotifier moved to the Core, chanctl::melody, schema Sound page
- L2: notifications, alarm / timer ring, locator sounds, Settings > Sound,
melody editor with the playing note lit, per-chat sound, mute icon
- noteIndex() on every buzzer backend; scrolling popups past screen height
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- FEAT_RX_POWERSAVE lived in MyMesh.h, but SettingsScreen.h only pulls
Features.h -- it saw the flag only via include order, and an undefined
macro in `#if` silently reads as 0, which would split the build the day
it's flipped to 1. Now defined in Features.h (the home of FEAT_* flags),
which MyMesh.h includes; comment/doc pointers updated.
- Changing Live Share's "Stop after" restarted the whole session, forcing
an immediate re-announce [LOC] although the position hadn't changed. It
now restarts only the duration clock (restartLocShareClock()).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GxEPD2 polls the panel's BUSY pin for the whole refresh (up to ~1-2s),
stalling the main loop. A TX finishing in that window left the SX126x in
standby -- deaf -- until the refresh ended; packets received meanwhile could
be lost (only the latest is readable, and a later CRC failure rejects a good
one still pending). Hook GxEPD2's busy callback to finish TX / drain RX into
a small staging queue and re-arm right away, without touching dispatch/UI.
Wired only where the display driver supports it (GxEPD2: the two e-ink Solo
boards); OLED and sim builds are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DM and channel history each live in a fixed-size ring (32 DM / 48
channel entries). A conversation that outpaces reading can fill its
ring, evicting the oldest entries — including unread ones that were
never opened — so the unread badge silently caps at ring capacity
instead of reflecting how many messages actually arrived.
MessageHistory now tracks a sticky per-channel overflow flag, set at
the exact point an unread channel entry gets evicted (mirroring the
existing unread-decrement logic) and cleared once the count reads back
down to 0. DMUnreadEntry gains the same bit for DM contacts, detected
by comparing the raw per-contact counter against actual ring occupancy
on each new message (the DM ring, unlike the channel ring, doesn't
proactively decrement on eviction).
DisplayDriver::drawUnreadBadge/unreadBadgeWidth/fmtBadgeCount take an
optional overflow flag (default false, existing call sites unchanged)
that appends "+" to the badge. Wired into every place an unread count
is shown: DM/favourites and channel lists, the DM/Channels/Rooms
mode-select screen, favourite tiles, and the aggregate "Msgs" field on
the clock and lock dashboards plus the Messages home-carousel page.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Field report: a user on the stock SF8 "EU/UK (Narrow)" preset saw reception
drop from ~1-5 msg/min to ~1/3h with Pwr save on, unaffected by a better
antenna. Root cause, confirmed against the SX1262 datasheet and RadioLib's
own maintainers (jgromes/RadioLib#1597, closed as inherent chip behaviour):
the SX126x's duty-cycle preamble detection needs the sender's actual
preamble to closely match what we've configured our receiver to expect.
A mismatch — e.g. a repeater still on pre-v1.16 firmware sending 16 symbols
against our 32 — doesn't cost a little sensitivity, it silently drops every
packet from that sender regardless of signal strength. There's no local
parameter fix: shortening minSymbols to tolerate shorter preambles directly
shortens the wake-window's correlator dwell time, trading the preamble
mismatch failure mode for a marginal-signal one instead. Checked whether
IoTThinks' MeshCore fork solved this differently — it doesn't hit the
problem at all, because its "power saving" only light-sleeps the MCU on the
radio's own GPIO interrupt and never touches RX duty-cycle.
examples/companion_radio/MyMesh.h now defines FEAT_RX_POWERSAVE 0, gating
out the Settings row, the Diagnostics RXPS watchdog row, and every call site
that would apply rx_powersave to the radio or CAD auto-enable — including
forcing setPowerSaving(false) unconditionally so a stale rx_powersave=1 byte
from before this change can't do anything either. The real duty-cycle
implementation in RadioLibWrapper/CustomSX1262Wrapper is left in place,
unneutered, for if a network-wide compatibility mechanism (e.g. the
still-unused ADV_FEAT1/FEAT2 advert bits) is ever built to make it safe.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
95e3cf26 raised LORA_TX_POWER to 30 for the GAT562 30S, but CustomSX1262 passed
it straight to RadioLib's begin(), whose setOutputPower() rejects anything over
22 dBm (ERR_INVALID_OUTPUT_POWER) -- so radio init would fail on that board.
With NUM_PA_POINTS/TX_GAIN_LORA defined, LORA_TX_POWER is a radiated power, not
a chip register value: init at register 0 and let RadioLibWrapper::setTxPower()
apply the real power through the curve right after (companion applyApc(),
repeater setTxPower at begin). Other boards are unchanged.
Verified by build (GAT562 30S companion + repeater, Heltec V3 repeater) and by
reading RadioLib's SX1262::checkOutputPower(); not tested on hardware.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The major rev from 19.x to 20.x breaks Radiolab, so pin it for now. This should fix the failing wio-e5-mini_repeater builds.
ltoa is removed upstream, so it was replaced with snprintf. The p[] buffer is always 16 bytes. Copy/paste for why claude thinks this is fine below, but also if this is broken it wouldn't be new.
```
exp2 in [23, 30] (int_part = mantissa << (exp2 - 23)): this is where int_part can reach its true max, up to 2,147,483,520 — 10 digits. That's what the snprintf bound of 11 (10 digits + NUL) is sized for. But in this branch frac_part is never assigned anything other than its initial 0, so only *p++ = '0' runs for the fraction — 1 char, not 7.
exp2 in [0, 22] (int_part = mantissa >> (23 - exp2)): this is the only branch where frac_part can also be nonzero and enter the 7-digit loop. Here int_part < 2^23 by construction, so it's capped at 7 digits, never 10.
```
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>
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>
User report: a repeater path popup's short "?A1B2C3D4"-style hop tags
(often just a couple pixels over the box width) felt stuck rather than
scrolling, while longer names (Nodes list) already felt right after the
recent marqueeHoldMs() speedup.
Root cause: the swing marquee's hold applies uniformly regardless of how
much text is actually hidden. When only 1-2 codepoints overflow, the full
cycle is hold + a single, barely-there step + hold + step back -- nearly
all hold, for almost no new information revealed each side. New
marqueeHoldForSkip(skip_cp) scales the hold down (to 1/3 or 1/2) when the
computed reveal distance is small; longer overflows keep the full hold,
since there's real text worth pausing to read.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
release-notes.md was missing three shipped changes: the Msg-wake toggle +
all-home-pages default, the repeater advert-interval default (2min->6min),
and three Fixes (settings-persist-on-reboot, RTC bootstrap floor-not-
rollback, ESP32 diagnostics heap/stack).
marqueeHoldMs() reverted 1500/2500 -> 700/1200 (OLED/e-ink) -- the longer
hold made the swing-marquee's full reveal cycle too slow for long message
previews.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Currently, time is only set to ~2024 when it powers on cleanly. When
time is lost, this results in time dropping to 1970. Save time as backup
to RTC slow memory. Should fix time staying accurate during brownouts
etc.
'custom:{}' (a DynamicConfigSerializer with nothing set) hits EXPECT_KEY
with a '}' and returns TOK_ERROR, so loadSerial stops there and silently
drops every property after it. Nothing follows 'custom' in NodePrefs
today, so it goes unnoticed until you add one.
Also include stdlib.h, which Arduino.h was providing on-device but not
in the native test build.
SerialWifiInterface has no ESP32-specific code, so move it to
helpers/wifi and reuse it on RP2040. Guard the ESP32-only WiFi
event/auto-reconnect calls and poll link state on RP2040 instead.