Commit Graph
893 Commits
Author SHA1 Message Date
AustinandClaude Opus 5 f481585de4 Make GxEPD2_Multi non-copyable to address cppcheck warnings (#11326)
cppcheck reports noCopyConstructor and noOperatorEq against GxEPD2_Multi
on every e-ink environment (over 80 duplicate pairs on a single
heltec-wireless-paper run, one per template instantiation point):

  src/graphics/GxEPD2Multi.h:123: [medium:warning] Class 'GxEPD2_Multi <
  GxEPD2_213_FC1 , GxEPD2_213_E0213A367 >' does not have a copy
  constructor which is recommended since it has dynamic memory/resource
  allocation(s). [noCopyConstructor]

The warning is correct. The constructor news one of two GxEPD2_BW drivers
into a raw pointer member and caches &driver->epd2 in epd2.m_epd2, so the
compiler-generated copy operations would alias that driver: two objects
would drive the same panel, and the second to be destroyed would free a
driver the first still points at.

Nothing copies it - EInkDisplay2 heap-allocates a single instance and
holds a pointer - so declare that intent by deleting the copy operations
rather than adding a suppression.

Also null the unselected driver pointer. Only one of driver0/driver1 is
allocated and the other was left indeterminate; every method branches on
`which` before dereferencing, so this is latent rather than a live bug,
but an indeterminate owning pointer is one refactor away from a wild
dereference.

Behaviour is unchanged: no caller could have copied this type, and the
two added stores only initialize a pointer that is never read.

Verified on heltec-wireless-paper: `./bin/check-all.sh
heltec-wireless-paper` now reports "No defects found" (exit 0), and
`pio run -e heltec-wireless-paper` builds clean. The build matters
separately here because syntaxError is suppressed in suppressions.txt,
so cppcheck alone would stay green on a malformed declaration.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 18:13:20 +00:00
4b4e82bd72 SenseCAP Indicator: RP2040 peripherals for the main firmware (#6220)
* indicator: RP2040 peripherals for the main firmware

The SenseCAP Indicator RP2040 co-processor serves as a generic
peripheral bridge over a serial protobuf link (interdevice.proto):

- FakeI2C implements TwoWire and tunnels write and read transactions,
  so the standard sensor drivers and the I2C scan work unmodified on
  the bridged second bus (WIRE1)
- FakeUART forwards GPS NMEA to the regular GPS driver
- SD card access with chunked file transfers, paged directory
  listings and card statistics; device-ui loads map tiles and map
  styles from the card behind the RP2040
- link at 2M baud with 4KB chunks, message structs kept off task
  stacks

Log messages carrying their own bracket tag render it like a thread
name. Replaces the earlier IndicatorSensor/COBS approach.

* indicator: address review

Correlate responses with request ids, serialize the shared TX buffer,
reject oversized frames, fix RX buffer overflow and NMEA truncation,
full-length file paths.

* indicator: assign the GPS FakeUART at runtime

Static initialization order across translation units is undefined,
so createGps() assigns and null-checks the bridged serial instead.
Bound the NMEA length defensively.

* indicator: bump device-ui pin to 27e6c0c

* indicator: ping/pong link probe, non-blocking runOnce, FakeI2C locking

The RP2040 sends nothing unsolicited without a GPS module attached, so
wait_ready now probes with the new ping message instead of listening
passively. runOnce skips its pump while a requester holds link_lock,
keeping the main loop from blocking for a full request timeout. FakeI2C
serializes transactions between the UI task and the main loop with an
owner-tracked lock held from beginTransmission to transaction end.

* indicator: link resync, config-honoring GPS, bridged-bus routing, stats validity

Frame resync scans to the next magic instead of flushing the RX buffer,
and the pump handles all buffered frames per pass. The RX drain reads in
bulk and the protobuf encoder gets the correct buffer bound. GPS honors
the gps_mode setting on the Indicator instead of always running. RTC,
I2C keyboard and motion sensor drivers resolve WIRE1 through
ScanI2CTwoWire::fetchI2CBus so bridged buses reach the right transport.
FakeUART implements flush/availableForWrite/const-write from the Stream
contract and fences its cross-core ring buffer. SdCardInfo.stats_valid
is passed through to device-ui, and the remote FS backend gains the
remove operation used for cleanup of failed tile saves.

* indicator: retry lost link round trips, I2CResult UNSPECIFIED

Remote FS operations retry once on a transport timeout. Correlation ids
drop late responses of the first attempt; a retried append whose first
attempt landed is recognized by the offset conflict carrying the
resulting file size. Definitive failures are not retried, missing-tile
probes stay a single round trip. Regenerated bindings add the
I2CResult.Status UNSPECIFIED zero value so an empty result cannot
decode as success.

* indicator: nack responses, rename bridge classes to I2CProxy/UARTProxy

A request the co-processor cannot decode or handle is nacked, so the
requester fails fast instead of burning its timeout. All requests stage
the shared tx_message under link_lock. FakeI2C and FakeUART are renamed
to I2CProxy and UARTProxy after the pattern they implement, with their
instances following suit. Drops dead code (unused NO_NEWS_PAUSE,
unreachable not-running branches, doubled include guards) and the GPS
pin log line that is meaningless on the tunneled port.

* indicator: refuse a co-processor that speaks another protocol version

The ping/pong handshake now carries InterdeviceVersion. A pong reporting
a version other than ours means the RP2040 runs firmware that does not
match this build, so the bridge stays shut down for the session and the
mismatch is logged with both versions. Requests fail fast instead of
being misinterpreted by the other side.

* indicator: regen protos, interdevice protocol version 2

* indicator: per-task I2C contexts, gated handshake, retryable link failures

The bridged I2C bus is shared between the main loop and the UI task, and
TwoWire has no transaction bracket a lock can span: drivers drain the read
buffer with available()/read() long after requestFrom() returned. Each
calling task therefore gets its own staging and read buffers instead of a
lock that could be left held (or that could not protect the read buffer
anyway). The transaction is staged inside the link, under its lock.

No request is sent before the co-processor has completed the version
handshake, and runOnce keeps probing until it does, so a co-processor that
boots slowly or reboots on its watchdog no longer leaves the bridge dead
for the session. Requests in flight are counted, not flagged: two threads
can be in a request and the first one out must not clear the other's state.

File operations are retried on a lost frame and on a co-processor busy with
card maintenance, but not on a refusal (nack) or a definitive failure, and
they release the SPI lock while they wait so a slow link does not starve
the radio.

* indicator: fail safe on a peer mismatch, wait out card maintenance

FileStatus moved to a fresh tag: reusing the tag of the removed success flag
made every failure status decode as success on a peer that predates it.

A card being mounted (busy) is retried rather than reported as an empty
slot, and a co-processor busy with card maintenance is waited out: mounting
takes seconds and the free space scan of a large card walks its whole FAT,
which is not a reason to report a missing tile. The bridged I2C bus releases
the SPI lock as well, so the keyboard scan on the UI task cannot starve the
radio either. Slot claims in the I2C proxy are atomic, NMEA is not sent to a
peer we refuse to talk to, and the handshake is completed by the unsolicited
ping the co-processor sends when it has booted, which also reports a
reboot.

* indicator: regen protos, FileStatus back on the original tags

* indicator: regen protos, ping/pong carry the InterdeviceVersion enum

* indicator: point the protobufs submodule at the merged interdevice protos

* indicator: pin device-ui to the branch with the remote SD support

* indicator: honor the txOnly flag of flush, report dropped GPS writes

flush() through a Stream pointer discarded the receive buffer: the flag is
txOnly, and HardwareSerial::flush() keeps what has been received. write()
reported bytes as written even when the link refused to send them. The link
probe uses Throttle for its rate limit.

* indicator: decide the log tag on the formatted message, hex request ids

The thread tag was suppressed based on the printf template, which disagrees
with the rendered message it is compared against: a format starting with a
conversion could produce two tags, and one without a trailing bracket-space
lost the tag entirely. vprintf now receives the thread name and picks. Also
shifts only the bytes actually buffered after a frame, throttles with
Throttle and logs request ids as hex.

* indicator: SD mount, eject and format commands over the link

* indicator: bound how long a busy card state blocks the UI task

* indicator: a busy co-processor must not block the UI task for ever

The busy retry re-armed its own budget on every busy answer, so a
co-processor that stayed busy kept the caller in the loop with no way out.
Transport retries and the wait for a busy card are now separate budgets that
only count down.

* indicator: start each request from an aligned receive buffer

A byte run lost mid-response (a UART overflow during a 4KB tile chunk, when
the display starves the RX interrupt) misaligns the assembly buffer. The
buffer was never reset, so the poison outlived the request and cascaded into
the following chunks of the same tile: one glitch dropped a whole multi-chunk
tile, while single-chunk tiles resynced in the idle gap and survived. Each
request now flushes the buffer first, bounding a glitch to the one chunk it
hit. Adds resync/decode/timeout counters, logged rarely, to see the rate.

* indicator: enlarge the LVGL heap for low-zoom map tiles

The heap was 3MB and the image cache reserves 1.5MB of it, so a low-zoom map
tile could not find a large enough contiguous block to decode and rendered
white. 5MB of the 8MB PSRAM fixes it with room to spare.

* indicator: advance the device-ui and protobufs pins to the merged commits

Point the protobufs submodule at the merged SD command protos (protobufs
#986) so it matches the checked in interdevice sources, and bump the
device-ui archive to the current indicator branch tip that carries the SD
button and format UI.

* Update device-ui library dependency URL

* remove cutom sdkconfig

* remove duplicated synchronisation (after PR11278 is in place)

* set commit reference to updated RemoteSDService class

* Add board_level configuration for release

* fix cppcheck errors

---------

Co-authored-by: Manuel <71137295+mverch67@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
Co-authored-by: mverch67 <manuel.verch@gmx.de>
2026-08-01 16:23:30 +00:00
Jason P 1d2fb23a0f Consolidate drawGps related code (#11304)
* Update UIRenderer.cpp

* Update GPS code path
2026-08-01 12:30:50 +00:00
Jason P 3d8ff48fab BaseUI: Clean up dead code (#11306)
* Clean up dead code

* Fix up the WiFi macro to be more directly called
2026-07-31 17:29:21 +00:00
Ben MeadorsandManuel e2460b546e MUI: stop holding the SPI bus across the whole UI cycle (#11278)
tft_task_handler held spiLock for the entire LVGL cycle. Most of that
cycle is timer work and rendering into the draw buffer, which issues no
SPI at all - but on boards where the TFT, SD card and LoRa radio share
one bus (T-Deck), every radio operation on the main loop still waited it
out. That is tens to hundreds of milliseconds whenever the UI animates,
felt as mesh RX/TX latency.

device-ui now takes the lock around its own transfers instead
(meshtastic/device-ui#356), so the coarse hold here can go and the bus is
contended only during real traffic.

Lend it spiLock through a reentrant adapter. device-ui nests its guards -
SdFsCard::usedBytes() calls cardSize() and freeBytes(), each of which
takes the lock - while spiLock is a plain binary semaphore that would
self-deadlock on the second take, so track the owning task and only touch
the underlying lock on the outermost acquire.

Requires the device-ui pin bump included here.

Tested on T-Deck: flush, touch, panel init, SD detect, powersave sleep
and wake all exercised; LoRa RX decoding under a live UI, no deadlocks,
no watchdog resets. Also builds seeed-sensecap-indicator-tft.

Co-authored-by: Manuel <71137295+mverch67@users.noreply.github.com>
2026-07-31 10:49:39 +00:00
Jason PandAustin 575788ae7b Implement Fixes for Meshnology W12's Two Color Display (#11288)
* Round 1 Fixes

* Update GPS icon

* Update calculations for revised GPS icon after regression testing

* Add BICOLOR_OLED_DISPLAY and actually use it

---------

Co-authored-by: Austin <vidplace7@gmail.com>
2026-07-31 04:54:54 +00:00
597f6767b5 Add Elecrow ThinkNode M8 board support (thinknode_m8) (#11226)
* Add Elecrow ThinkNode M8 variant scaffold (thinknode_m8)

nRF52840 + SX1262 + 2.4" e-paper + ATGM336H-5NR32 GPS.
All pins resolved from ThinkNode_M8_V0.3.sch; cross-checked
against meshtastic/firmware#9181 (Elecrow V0.1 reference).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add Elecrow ThinkNode M8 board support (nRF52840/SX1262, 1.54in e-ink, ATGM336H GNSS, SC7A20, EC04 encoder)

* Address review: keep the stored backlight level out of blanking, match only the SC7A20 WHO_AM_I byte, and transfer detents atomically

* Use std::atomic for the press-and-turn detent counter so native builds compile

* Drop the ThinkNode M8 LED_BUILTIN redefinition that warned on every translation unit

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-30 15:59:35 +00:00
Thomas Göttgens 047c4e9feb add LR 2021 to portduino, and allow Framebuffer devices to rotate the screen from config (#11252)
* add LR 2021 to portduino, and allow Framebuffer devices to rotate the screen from config. Requires https://github.com/meshtastic/device-ui/pull/355 and supersedes https://github.com/meshtastic/firmware/pull/10567 and https://github.com/meshtastic/firmware/pull/11138

Many thanks to the original authors https://github.com/a-li3n and https://github.com/jessm33

* Build LR2021Interface.cpp in the wasm env

initLoRa() constructs LR2021Interface for Lora.Module: lr2021, so excluding
the file from the native-wasm source filter left the constructor undefined at
link time. LR20x0Interface.cpp stays excluded; it is template-only and comes in
via the InterfacesTemplates.cpp amalgamation.

Also replace the non-UTF-8 degree signs in the framebuffer rotation comment and
correct the rfswitch alias cleanup comment.

* Trim comments

* Report setenv failure for the framebuffer rotation
2026-07-30 10:44:52 +00:00
a640d44b5c Add T-Beam BPF - 144-148 Mhz LoRa @ ~37 dBm (5 Watts) (#10558)
* Add T-Beam BPF (144-148 Mhz LoRa)

* minor correction to fix compiler warnings

* Add T-Beam BPF (144-148 Mhz LoRa)

* minor correction to fix compiler warnings

* Add ITU regions for this device and make GPS work.

* Switch pin after defining it as output

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Lora CS is indeed 1, SD Card CS is 10

* Include the back option.

* Fix compilation with pioarduino (USB_MODE)

* Default ham to narrow_fast

* Default PROFILE_HAM to slot 17

This is an appropriate default in the USA but not the EU.

The slot override really should follow the region itself, not the regionprofile.

* Fix for ITU 2/3 split

* Add ITU region options to MenuAction enum

* Add HAS_HAM_2M definition to variant headers for 2M support

* Re-add PROFILE_HAM regionprofile

Accidentally removed in last merge

* Trunk fmt

* Initial default slots

* Switch to TinyFast

Still need to flesh out the default channels

* Adjust slotOverrides for TinyFast

* RadioLib doesn't accept 15.625 kHz

Use 15.6 instead

* Set RF95 pins for T-Beam Supreme

May cause regressions!!

* Remove other-variant changes (BPF-only)

These have been moved to other PRs

* Remove mismatch guarding (we need a more comprehensive approach)

* Add comment back

* Add template for PA curve

* This is a 5-6W radio!! Add TX_GAIN_LORA

* Trunk fmt because NomDeTom hates emdashes

---------

Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-27 13:54:53 +00:00
1e982fa78c Sign plaintext packets in licensed mode (#10969)
* feat: sign licensed plaintext packets

Preserve and publish identity keys in licensed mode so existing XEdDSA signatures can authenticate plaintext traffic. Sign licensed broadcasts and direct messages when they fit, while keeping PKI encryption disabled and normal routing behavior unchanged.

* fix(security): persist licensed channel sanitation

* fix(security): close licensed migration lifecycle gaps

* fix(security): address licensed signing review feedback

* test: restore signing globals from Unity teardown

* fix(baseui): allow confirmed ham region selection

* fix(baseui): guard region picker validation

* style: trunk fmt

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Austin <vidplace7@gmail.com>
2026-07-27 02:57:15 +00:00
Jorropo 9e529da460 replace screens with unique_ptr (#11163) 2026-07-25 11:51:13 +00:00
Thomas Göttgens 11c510ff1b WIP: Big Display Node (#10926) 2026-07-22 09:35:36 +02:00
Jason P 65abfaa0c8 Improve user experience with a new GPS icon for Home and Position frames (#11103) 2026-07-20 20:30:57 -05:00
Thomas Göttgens b8fb42c065 Fix bell-append off-by-one write past payload buffer (#11099)
The send_bell append writes bytes[size] and bytes[size+1] while the guard only checked size < DATA_PAYLOAD_LEN, overrunning the 233-byte payload array by one when size == DATA_PAYLOAD_LEN - 1. Guard on size + 1.
2026-07-20 18:38:50 -05:00
Thomas Göttgens fb75410e53 Null-check packet allocations in allocForSending and its callers (#11086)
* Null-check packet allocations in allocForSending and its callers

Sibling of 0ae44d701.

* Null-check allocDataProtobuf, allocAckNak and allocErrorResponse callers

Second tier of the same nullable contract.

* Keep telemetry sleep scheduling on allocation failure

Allocation failure now marks the telemetry invalid instead of returning
early, so power-saving SENSOR nodes still arm deep sleep.
2026-07-20 18:37:13 -05:00
HarukiToreda 09de848fbe Ignore notification (#11076) 2026-07-20 18:36:36 -05:00
Ben Meadors 829ff80a09 fix(t5s3-epaper): 40MHz octal PSRAM; guard EInk init; esp32s3 sw-atomic shim (#11062)
- t5s3-epaper: the board's 3.3V octal (AP_3v3) PSRAM is unreliable at the qio_opi
  default of 80MHz (Total PSRAM reads 0, display dead / boot-loop); clock it at 40MHz.
- EInkParallelDisplay: skip EInk init and no-op display ops when the PSRAM framebuffer
  is unavailable, so the node boots headless instead of hard-faulting on a NULL buffer.
- src/platform/esp32: weak software __atomic_*_{1,2,4} so esp32s3 links on toolchains
  (e.g. macOS) that lack the sized libcalls GCC emits under -mdisable-hardware-atomics.
2026-07-20 12:13:50 +02:00
Thomas Göttgens 023351a979 Remove dead code (#11082)
Removed:
- MessageStore::addFromPacket and addFromString, superseded by
  tryAddFromPacket
- GeoCoord rangeRadiansToMeters, distanceTo, bearingTo
- Router::rawSend, declared virtual with no override and no caller
- ContentHandler handleHotspot, handleFs, handleAdminSettings,
  handleAdminSettingsApply, handleDeleteFsContent and their commented
  route registrations, plus the now unreachable htmlDeleteDir and the
  handleUpdateFs declaration that had no definition
- ContentHelper replaceAll
- OnScreenKeyboardModule popup chain: showPopup, clearPopup, drawPopup,
  drawPopupOverlay and their state, unreachable since the frame based UI
  was replaced by baseUI
- DebugRenderer drawDebugInfoTrampoline, drawDebugInfoSettingsTrampoline
  and the orphaned drawFrameSettings
- NodeListRenderer calculateMaxScroll, drawColumns and a stale extern
  haveGlyphs declaration with no definition
- UIRenderer::haveGlyphs, Screen::blink,
  NotificationRenderer::showKeyboardMessagePopupWithTitle,
  VirtualKeyboard::getInputText
- InkHUD touchNavLeft, touchNavRight, Applet::getActiveNodeCount,
  ThreadedMessageApplet::saveMessagesToFlash
- TwoButton::setHandlerUp, TwoButtonExtended setHandlerUp,
  setJoystickDownHandlers, setJoystickUpHandlers
- CannedMessageModule LaunchRepeatDestination, isCharInputAllowed,
  hasMessages
- TrafficManagementModule resetStats, recordRouterHopPreserved,
  saturatingIncrement
- UnitConversions::MetersPerSecondToMilesPerHour
- EncryptedStorage getSessionRemainingSeconds
- BMI270Sensor::writeRegisters, GPS::hasFlow, FSCommon copyFile,
  SerialConsole consolePrintf, buzz playLongPressLeadUp,
  memGet displayPercentHeapFree
2026-07-20 11:55:17 +02:00
HarukiToreda 8c6cc5dbde Don't show messages from Ignored nodes (#11068)
* Honor Node Ignore messages

* Screenless node fix
2026-07-19 06:32:34 -05:00
Andrew YongandBen Meadors b71c1adb26 stm32wl: add hardware RTC support (rak3172) (#10961)
* stm32wl: add hardware RTC support infrastructure

Wires the STM32WL chip's internal RTC (running off the LSE 32.768kHz
crystal) into meshtastic's existing time-of-day framework
(perhapsSetRTC()/readFromRTC()), following the same pattern already
used for I2C RTC chips (RV3028, PCF8563/85063, RX8130CE).

LSE is started and polled manually before ever calling into the
STM32RTC library, with our own bounded timeout - the library's own
internal LSE startup path has no bounded fallback and hangs forever
via Error_Handler() if the crystal never locks, so this is required
for a board with a missing/faulty crystal to boot normally rather
than hang.

Gated behind a new HAS_LSE variant flag (currently unset everywhere,
so this is inert until a variant opts in - see follow-up commit).

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

* gps: qualify RTC.h includes to avoid case-insensitive filesystem collision with STM32RTC

The stm32duino STM32RTC library (added to lib_deps in a follow-up
commit) ships its own src/rtc.h. On case-insensitive filesystems
(the macOS default), an unqualified #include "RTC.h"/<RTC.h> from
any file outside src/gps/ resolves to the library's rtc.h instead of
src/gps/RTC.h, since PlatformIO's LDF puts lib_deps include paths
ahead of the project's own -Isrc/gps.

Qualify every include as gps/RTC.h so it can't collide with any
same-named header a future dependency might ship, regardless of
filesystem case sensitivity. Purely mechanical, no behavior change.

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

* stm32wl(rak3172): enable hardware RTC support

Opts rak3172 into the HAS_LSE infrastructure added previously: sets
STM32WL_LSE_DRIVE to a conservative default and pulls in the
STM32RTC library. rak3172 has ~63KB flash headroom going in;
build-verified at 76.7% flash usage after this change (up from a
73.8% baseline), well within budget.

wio-e5 is not opted in here despite sharing the same STM32WLE5 chip
- it's already at 96.8% flash usage today (GPS + I2C sensor support
compiled in, unlike rak3172), leaving too little headroom to safely
add STM32RTC without first trimming something else.

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

* stm32wl: add docstrings for LSE/RTC setup functions

Addresses CodeRabbit's docstring coverage check on PR #10961.

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

* stm32wl: address CodeRabbit nitpicks on PR #10961

- Brace the single-statement HAS_LSE branch in perhapsSetRTC() to
  match the sibling readFromRTC() branch's style.
- Quote the RTC.h include in PhoneAPI.cpp for consistency with every
  other qualified include site.

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

---------

Signed-off-by: Andrew Yong <me@ndoo.sg>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2026-07-16 19:00:45 -05:00
8d1fbbf55f Snake! (#10936)
* Snake!

* Add spiLock to snake score saving

* Check fixes

* More careful locking

* WIP: Big Display Node

* Update src/graphics/HUB75Display.cpp

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Add HUB75 Native

* Add Tetris game module with core logic and UI integration

- Implement TetrisGame class for game logic, including piece movement, rotation, and line clearing.
- Create TetrisModule class to manage game state, input handling, and rendering on OLED display.
- Introduce high score tracking with persistence and optional mesh broadcasting.
- Define UI states for title, playing, paused, game over, and high scores.
- Implement input handling for game controls and state transitions.
- Add rendering functions for the game board, high scores, and title screen.

* feat(snake): add snake graphics and update display logic in SnakeModule

* Prompt for Initials for Tetris, too

* refactor games module

* Games refactor

* hub75 native double buffer

* Games tuning

* Make joystick repeat events on held button

* Add clouds and colors

* Fix breakout and more color

* difficulty tuning

* trunk

* refactor game announcements, etc

* Scale chirpy gravity as game speed increases

* Portduino, check for hub75 display before reading in hub75 options

* Final(?) games tuning

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Properly ignore input when games screen not shown

* Fail gracefully when HUB75 is selected but not supported.

---------

Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Ixitxachitl <kramerfm@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-16 18:35:33 -05:00
Ben Meadors 5cf85d523a fix: heap leaks on display wake and BLE re-enable; remove dead PacketCache (#11019)
* fix(display): don't leak a TFT driver instance on every screen wake

On variants whose Screen::handleSetOn() re-runs ui->init() on each display
wake (Heltec Tracker V1.x/V2, VTFT_LEDA and ST7796 boards, MUZI/Cardputer
via dispdev->init()), OLEDDisplay::init() unconditionally re-invokes
connect(), and TFTDisplay::connect() allocated a fresh driver instance
each time without freeing the old one. Each OFF->ON transition orphaned a
full LGFX device (measured: exactly 1,260 bytes per wake on a Heltec
Wireless Tracker V2). Since PowerFSM wakes the screen on every received
text message, a device on a busy channel leaked tens of KB per day -
matching field reports of heap climbing from 78% to 92% within a day.

Null-guard the driver allocations (matching the existing linePixelBuffer/
repaintChunkBuffer guards below them) so re-entry re-runs tft->init() on
the existing instance. Bench-validated on a Tracker V2: free heap now flat
across 13 consecutive wake cycles, with the panel still re-initializing
and rendering on every wake.

* fix(ble): don't leak BluetoothPhoneAPI and callbacks on BLE re-enable

NimbleBluetooth::setup()/setupService() re-run when Bluetooth is
re-enabled after deinit(), but BLEDevice::deinit() only frees the GATT
objects - the caller-owned allocations were re-created unguarded on every
cycle:

- bluetoothPhoneAPI: ~3.5KB PhoneAPI object per cycle, and the orphaned
  instance is an OSThread that stays registered with the scheduler, so a
  duplicate thread kept servicing the same static queues
- toRadioCallbacks / fromRadioCallbacks / security + server callbacks:
  one small object each per cycle
- the BLESecurity shim was heap-allocated and never freed even on first
  boot; it only forwards to static setters, so use a stack instance

Reuse the existing objects on re-setup; the setCallbacks() calls still
run every time since the characteristics themselves are new.

* chore: remove unused PacketCache

PacketCache landed in #8341 but no consumer was ever wired up - repo-wide,
the only references to PacketCache/packetCache are in its own two files.
As designed it also malloc()s per cached packet with no eviction or size
cap, so it should be re-reviewed for bounds before any future use. Remove
the dead code; git history preserves it if a bounded revival is wanted.

* fix(ble): reset stale session state when reusing BluetoothPhoneAPI

Review follow-up: reusing bluetoothPhoneAPI across BLE enable cycles could
hand the next session the previous session's dirty state. The only full
cleanup path (onDisconnect) is skipped when deinit()'s bounded disconnect
wait expires before the event lands (its 2s cap matches the connection
supervision timeout, so a phone that walked out of range makes this a
coin flip): the reused object then enters the next session mid-config,
with stale queue contents served to the new phone and a stale connection
handle that defeats the checkConnectionTimeout self-heal.

Factor onDisconnect's cleanup into resetBleSessionState() and run it from
setupService() when reusing the instance, restoring the old fresh-object
invariant. Also switch the four stateless callback objects to function-
local statics (the resolved framework BLE wrapper stores plain pointers
and never frees them, so static instances are safe and avoid the guarded
heap allocations), correct the comment that claimed deinit() frees the
GATT objects (it deletes only the BLEServer itself; the services and
characteristics it created remain a small library-side leak per cycle),
and fix the inverted connection check in getRssi() that made BLE RSSI
always read 0 on ESP32-S3/C6.

* refactor(display): construct-once guard around the whole driver ladder

Review follow-up: one if (!tft) around the #if/#elif/#else construction
block instead of a guard per branch, so a future display family can't
reintroduce the per-wake leak by copying an unguarded branch. Make the
HACKADAY bus pointer local to the construction (it was a write-only
global), and point the comment at the Screen::handleSetOn gates instead
of hand-listing boards that would go stale.

* fix(ble): use-after-free notifying a freed characteristic after deinit()

deinit() nulled bleServer and BatteryCharacteristic but left
fromNumCharacteristic dangling after BLEDevice::deinit(true) freed the
GATT graph, and it never detached the PhoneAPI fromNumChanged observer.
When deinit()'s bounded 2s disconnect wait expires before onDisconnect
runs (the same stale-bond / host-reset race resetBleSessionState was
built for), close() is skipped, so the observer stays attached with
state == STATE_SEND_PACKETS. After BLE is off, the next mesh packet
drives MeshService fromNumChanged -> PhoneAPI::onNotify -> onNowHasData
-> fromNumCharacteristic->notify() on freed memory (the framework BLE
wrapper's notify() dereferences the freed server via getConnectedCount).
Unlike sendLog(), onNowHasData() had no isConnected guard.

deinit() now calls resetBleSessionState() to detach the observer and
reset session state unconditionally (also forcing the conn handle to
NONE so checkConnectionTimeout can't be fooled by the stale handle),
nulls fromNumCharacteristic/logRadioCharacteristic like the other freed
pointers, and onNowHasData() bails on a null characteristic.

Reachable on all ESP32/S3/C6 NimBLE boards via admin disable-bluetooth
or a sleep transition while a phone is connected. Found by a follow-up
lifecycle audit of the re-enable changes in this PR.
2026-07-16 15:13:26 -05:00
Oleksandr KravchukandTom 36bfb86937 Remove UA_868 as obsolete (#10994)
Decree of the Cabinet of Ministers of Ukraine dated February 25, 2026,*
No. 262, on amendments to Section 2 of the Plan for the Allocation and
Use of the Radio Frequency Spectrum in Ukraine, has harmonised
Ukrainian regulations on the 868 MHz spectrum with the EU's, thereby
making UA_868 legally obsolete.

* https://zakon.rada.gov.ua/laws/show/262-2026-п

Co-authored-by: Tom <116762865+NomDeTom@users.noreply.github.com>
2026-07-16 08:52:50 -05:00
952c825167 Allow key verification to work for unknown nodes. (#10669)
* Allow key verification to work for unknown nodes.

* trunk

* More reliable admin key decryption

* Add admin key fallback tests

* Actually check haveRemoteKey

* Logging cleanup

* Address review feedback

- Persist the committed key + manually-verified flag in
  commitVerifiedRemoteNode via saveToDisk(SEGMENT_NODEDATABASE),
  replacing the "todo: initiate save"
- Guard the CryptoEngine pending-key slot with a dedicated internal
  lock; the Router reads it while already holding the non-recursive
  cryptLock, so the accessors cannot reuse that lock
- Draw the security number from the hardware RNG (CryptRNG fallback)
  under cryptLock instead of random(); on nRF52 the entropy fill
  toggles the same CC310 the BLE task's packet crypto uses
- Return true after fully handling the hash2 response (restores
  develop behavior; consistent with the hash1 branch)
- Take uint32_t in the number-picker callbacks so 8-digit hex
  nodenums can't truncate through int
- Trim over-long comments flagged by review

* feat(tests): add deterministic tests for admin session-key behavior

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2026-07-14 21:38:41 -05:00
Jonathan Bennett eb4e24b2f6 Avoid setting HeaderBackground for transparent backgrounds (#11002) 2026-07-14 09:22:57 +02:00
Jonathan Bennett 77292c8415 Fid double free in virtual keyboard (#10999)
Found via portduino. As there are two separate references to this pointer, they both could call free
2026-07-12 16:00:24 -05:00
Ben Meadors c5355641d3 Add MEDIUM_TURBO modem preset (#10988)
* Protobufs

* Wire up MEDIUM_TURBO modem preset

MEDIUM_TURBO (500 kHz, SF9, CR 4/5) already existed in the protobuf enum but
was never wired into firmware, so selecting it silently fell through to the
LONG_FAST default and rendered an "Invalid" display name.

Add its bw/sf/cr mapping (modemPresetToParams), display name (MediumTurbo/MedT),
PRESETS_STD membership (standard regions only — 500 kHz does not fit EU868's
250 kHz band, so it stays out of PRESETS_EU_868 and is rejected/clamped there),
and the MEDIUM SNR-grading bucket. Includes positive coverage in test_radio,
EU868-reject + US-accept coverage in test_admin_radio and test_mesh_beacon,
the STD preset count 9->10, an extended fuzz range, and the client-spec doc.

* Address review feedback on MEDIUM_TURBO tests

- test_mesh_beacon: assert has_mesh_beacon before checking the invalid preset was
  cleared, so the EU868-cleared test can't pass on a dropped message (matches the
  existing SHORT_TURBO test).
- test_fuzz_packets: draw modem presets from _ModemPreset_ARRAYSIZE instead of a
  hard-coded 17 so the fuzz range tracks future enum additions automatically.
2026-07-11 08:24:35 -05:00
HarukiToreda f4efbd9149 tlora-t3s3-epaper: e-ink startup and build cleanup (#10973)
* T3s3 Eink

* Update SerialConsole.cpp

* Update

* Update SerialConsole.h

* Update EInkDisplay2.cpp
2026-07-10 05:50:30 -05:00
Ixitxachitl faf92f6720 Add new emojis: faces, gestures, weather, objects, holidays, and misc (#10930) 2026-07-09 05:03:57 -05:00
Andrew YongandBen Meadors d16ae2b098 Rename power.h to Power.h for casing consistency with Power.cpp (#10919)
Every other .cpp/.h pair in src/ (350 of 351) uses identical
capitalization between the two files. Power.cpp/power.h was the sole
outlier; this aligns it with the rest of the codebase. No functional
change — all includes already resolved this file the same way on
case-sensitive filesystems.

Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

Signed-off-by: Andrew Yong <me@ndoo.sg>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2026-07-07 10:15:01 -05:00
TomandClaude Fable 5 d846780a9b More fuzz tests and small fixes for the findings (#10864)
* first pass tests

* more tests

* Fix two crafted-admin-packet crashes found by the E5 fuzzer

Both are reachable from an authorized admin (local from==0, admin channel,
or PKC) - remote DoS:

1. SIGFPE in LoRa config validation. A set_config LoRaConfig with
   use_preset=false and bandwidth=0 makes freqSlotWidth 0, so numFreqSlots
   is 0 and `hash(name) % numFreqSlots` (RadioInterface.cpp) divides by
   zero. Guard the modulo; the existing channel_num check then rejects/
   clamps the config.

2. Stack overflow in Channels::getKey. A SECONDARY channel at the primary
   slot with an empty PSK recursed into getKey(primaryIndex) forever. Skip
   the primary-key borrow when chIndex == primaryIndex.

Re-enable the E5 admin fuzzer to hit both triggers again (use_preset both
ways incl. bandwidth 0, plus the set_channel tag) as regression guards.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Correct fuzz-test invariants after the crash fixes

- E5 admin fuzz: node eviction under a filling NodeDB is legitimate, so
  assert only the bounded-count invariant, not that a specific seed node
  survives 6000 mutating ops.
- TMM blitz: scope off the nodeinfo direct-response send path (it needs a
  fully-wired MeshService/phone queue the fixture doesn't provide; the
  deterministic directResponse tests cover it). The crafted-nodenum
  rate/unknown/position cache stress is unchanged.

clod helped too

* realistic tests

* test: dedup fuzz RNG into shared test/support/DeterministicRng.h

The four in-tree fuzz suites (test_fuzz_decode, test_fuzz_packets,
test_hop_scaling, test_traffic_management) each carried a byte-identical
copy of the seeded 64-bit LCG (rngSeed/rngNext/rngByte/rngRange). Hoist
it into one shared header so there is a single generator to reason about
and no risk of the copies drifting. static inline keeps per-suite state
per translation unit and avoids -Wunused-function for suites that don't
use every helper. Also corrects a stale comment in test_traffic_management
(the blitz's nodeinfo direct-response path is intentionally left off).

No behavioral change: same constants, same per-suite seeds.

clod helped too

* test: fuzz uncovered ProtobufModule handlers and the MQTT downlink ingress

Extend the in-tree fuzz coverage to packet sources that previously had
none:

- test_fuzz_packets E8/E9/E10: drive PositionModule, DeviceTelemetryModule
  and NeighborInfoModule at handleReceivedProtobuf directly (via using-shims,
  bypassing the ProtobufModule reply/send path so no router is needed). The
  fixture already stands up nodeDB/service/channels, and nodeStatus/powerStatus
  are auto-initialized in main.cpp, so no new globals are required. Adds a
  shared fuzzRxHeader() helper for crafting adversarial RX packet headers.
- test_fuzz_decode: add meshtastic_KeyVerification to the decode table. The
  KeyVerification and StoreForward handler paths are documented as decode-level
  only, with the concrete reason each is intrinsic (private-state gating /
  PSRAM + self-pointer wiring), not a fixture gap.
- test_mqtt: test_receiveFuzzServiceEnvelope blitzes the non-RF broker-push
  ingress (onReceiveProto) two ways - raw garbage bytes that must fail envelope
  decode cleanly, and a well-formed ServiceEnvelope wrapping a crafted inner
  MeshPacket over crafted channel_id/gateway_id - exercising the channel match,
  isFromUs, XEdDSA receive policy and perhapsDecode chain. Adds a deliverRaw()
  passthrough to MQTTUnitTest.

All under the coverage env (ASan/LSan). No firmware/src changes. Full sweep
GREEN 27/27, 544 cases.

clod helped too

* Harden LoRa/channel config against crafted admin messages; consolidate test helpers

Production (review findings on the hot-fuzz crash fixes):
- Clamp bandwidth at the source (clampBandwidthKHz) in checkOrClampConfigLora
  and applyModemConfig so numFreqSlots can never be 0 for any consumer; a
  bandwidth-0 set_config previously passed validation and re-armed the SIGFPE
  on the next applyModemConfig.
- Guard applyModemConfig's hash % numFreqSlots (the validator's sibling modulo
  was fixed earlier but this one was still unguarded).
- Enforce the primary-channel invariant in Channels::onConfigChanged: a config
  demoting every slot now re-promotes the stale SECONDARY slot (keeping its
  key) or restores the default channel if the slot is DISABLED, instead of
  leaving every getPrimaryIndex() reader on a non-primary slot. The getKey
  recursion guard stays as defense-in-depth.

Tests:
- New test/support/MockMeshService.h and AdminModuleTestShim.h replace four
  byte-identical mocks and three divergent admin shims (test_mqtt's capturing
  mock is genuinely different and stays).
- DeterministicRng.h: add rngFill() (replaces 14 hand-rolled fill loops) and
  rngEdgeNodeNum() (unifies the three NodeNum boundary pools).
- Extract fuzzChannelSettings() shared by the set_channel case and fuzzBeacon.
- fuzzBeacon: the un-terminated branch now fills the whole buffer with non-NUL
  bytes so the strnlen bound is actually stressed (~50% of iterations, not ~4%).
- E6 beacon fuzz: replace the TEST_ASSERT_TRUE(true) tautology with real
  invariants (handler never consumes; offers land in lastReceivedOffer keyed
  to the sender).
- Trim the seven over-long comment blocks flagged against the 1-2 line rule;
  the FINDINGS trailer moves to this commit message (see production notes).

Full native suite GREEN 27/27 under the coverage (ASan/LSan) env.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Clamp UTF-8 char length in the emote walkers; add test_fuzz_emotes

A TEXT_MESSAGE payload is opaque protobuf bytes, so PB_VALIDATE_UTF8 never
screens it - invalid UTF-8 and truncated multi-byte lead bytes reach the
emote/width render path verbatim. EmoteRenderer's walkers advanced by
utf8CharLen(lead) without clamping to the bytes actually remaining, so a
truncated lead (e.g. a lone 0xF0, which claims 4 bytes) near the end of the
buffer made getUtf8ChunkWidth's memcpy read past the string. ASan confirms a
heap-buffer-overflow READ from measureStringWithEmotes.

Add utf8CharLenClamped() and use it at every walk site (width measure,
truncation cut-loop, and the draw-path text-run/chunk builders); the one
already-guarded site (matchAtIgnoringModifiers) is unchanged.

New test/test_fuzz_emotes drives measureStringWithEmotes and truncateToWidth
over adversarial byte strings (biased to embed/end in truncated multi-byte
leads) in exact-sized heap buffers so any over-read is a hard ASan fault. Its
headless display uses a synthetic font (firstChar 0, fontData centered in a
large buffer) so the stock OLEDDisplay::getStringWidth - which indexes the
font jump table with a signed char and over-reads for any byte >= 0x80 - does
not mask the finding. native-suite-count bumped 27 -> 28.

Full native suite GREEN 28/28 under the coverage (ASan/LSan) env.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Keep emote width measurement in-bounds for non-ASCII bytes

OLEDDisplay::getStringWidth (the utf8=false path EmoteRenderer uses on default
builds) indexes the font jump table by (c - firstChar) with a signed char and
no bounds check, so any byte outside printable ASCII - high bytes from UTF-8
text, but also a stray control byte like 0x0A - reads outside the font array.
On-device this reads adjacent flash and returns a garbage width; under ASan the
test_fuzz_emotes fuzzer flags it as a global-buffer-overflow, and it made the
non-ASCII width measurement meaningless either way.

The OLED driver is a pinned upstream dependency, so guard it firmware-side in
EmoteRenderer's getStringWidth helper: measure a sanitized copy where any byte
outside [0x20, 0x7E] counts as a '?' placeholder. Printable ASCII is unchanged
and the UA/RU lookup path is untouched.

test_fuzz_emotes now drives a real ArialMT font instead of the synthetic
in-bounds font it needed before this fix, so the suite exercises the true
production width path (utf8CharLen clamp + this sanitizer) end to end. The same
fuzzer tripped the global-buffer-overflow before this change.

Full native suite GREEN 28/28 under the coverage (ASan/LSan) env.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:19:01 -05:00
Ben Meadors 6c7ee8afc7 Add MemAudit: per-subsystem heap accounting in the boot log (#10900)
* Add MemAudit: per-subsystem heap accounting in the boot log

The 2.8.0 nRF52840 heap-exhaustion field reports had to be diagnosed by
hand, reconstructing each subsystem's heap footprint from source and
build flags one report at a time. This makes every future report
self-diagnosing from the serial log: a tiny fixed-size registry
(src/memory/MemAudit.*) that big long-lived allocations report into,
printed as one line at the end of setup() and alongside the periodic
"Heap free:" log:

  MemAudit[boot]: tmm=2500 warm=4000 pkthist=5824 nodedb=13440
  msgstore=2200 pktpool(live)=3270 total=31234

Instrumented: NodeDB hot vector (nodedb) + satellite maps (satmaps,
rb-tree overhead estimated), WarmNodeStore (warm), PacketHistory records
and hash index (pkthist), TrafficManagement caches (tmm/tmm_ni),
MessageStore text pool (msgstore), TFT line/repaint buffers (display),
and live in-flight packets (pktpool(live)) via an optional audit tag on
the packet pool allocator - the one hot path, counted with a relaxed
32-bit atomic add (single instructions on Cortex-M, no locks).

Cost: 128 B RAM for the 16-tag table, well under 1 KB flash on rak4631.
MESHTASTIC_MEM_AUDIT=0 compiles it out to inline no-op stubs (call
sites need no ifdefs); STM32WL, the tightest flash target, defaults off.

New native suite test_mem_audit covers add/set/snapshot arithmetic, tag
reuse (pointer and cross-TU strcmp fallback), null/unknown tags, and
table-full behavior; test/native-suite-count bumped to 28.

* native-wasm: add src/memory/ to the curated source filter

The wasm env denies all sources and adds an explicit file list; MemAudit
callers (main, MeshService, NodeDB, PacketHistory) are in that list but
src/memory/MemAudit.cpp was not, so wasm-ld failed on undefined
memaudit:: symbols.
2026-07-06 13:18:49 -05:00
HarukiToreda b50da75070 MapTile Compression Improvement (#10882)
* compression improvement

* Update MapTile.h

* t-echo-inkhud  fix
2026-07-05 05:39:14 -05:00
Benjamin Faershtein 6085a4f003 fix(nrf52): keep BLE active during reset cleanup (#10873) 2026-07-03 18:57:15 -05:00
Austin 397ff8523a Add 70cm amateur radio region support (#10627) 2026-07-03 10:00:14 +02:00
Jason P f273d3f875 XEdDSA packet signing UI (BaseUI) (#10841)
* XEdDSA packet signing UI (BaseUI)

* Move Pixels around for TFT
2026-07-02 18:26:54 -05:00
HarukiToreda 43440dbb9b InkHUD: GPS improvements (#10846)
* GPS improvements

* Update

* Update MenuApplet.cpp

* Remove fixed position

* Update MenuApplet.cpp
2026-07-02 17:09:50 -05:00
Ben MeadorsandClaude Opus 4.8 510e9796f9 Extract mcp-server to its own repo (meshtastic/meshtastic-mcp) (#10861)
The Python MCP server + hardware test harness that lived under mcp-server/
now has its own home at https://github.com/meshtastic/meshtastic-mcp
(published, versioned independently). Remove the in-tree copy and wire the
firmware repo to the standalone server externally.

- Delete mcp-server/ (96 files) and the 8 harness-coupled AI workflow files
  under .claude/commands/ and .github/prompts/ that drove ./mcp-server/
  run-tests.sh — those workflows now ship with meshtastic-mcp as skills.
- .mcp.json: register the server via
  `uvx --from git+https://github.com/meshtastic/meshtastic-mcp meshtastic-mcp`
  instead of a local ./mcp-server/.venv, keeping MESHTASTIC_FIRMWARE_ROOT="."
  so the MCP tools still work from this checkout with no local build.
- Repoint the remaining references (AGENTS.md, CLAUDE.md,
  .github/copilot-instructions.md, bin/regen-*.sh, docs, Screen.h,
  userPrefs.jsonc, test/fixtures/nodedb/README.md, .trunk/configs/.bandit)
  at the standalone repo. The MCP tool surface is unchanged — only the
  pytest harness moves out; run it from a meshtastic-mcp checkout with
  MESHTASTIC_FIRMWARE_ROOT pointed here.

No build/CI/platformio coupling existed, so nothing in the firmware build
changes.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 14:33:04 -05:00
oscgonferandBen Meadors 4673ed22fc Reclock I2C for sensors is aware of screen I2C frequency (#9898)
* Add define for screen I2C frequency

* Add functions to get I2C screen frequency and port

* Make reclock function aware of screen-set I2C speeds

* Refurbish ReClockI2C API

* Make ReClockI2C API class
* Use new API in AirQualityTelemetry module
* Minor changes on some logs

* Update esp8266-oled library

* Fix esp8266 library

* Minor logging changes

* Improve setClock and restoreClock cases

* Make getter functions const, fix capitalisaiton of new functions

* Minor typo, remove pragma once

* Low prio debug fixes

* Mark getter functions as const (properly)

* Fix LOG based on coderabbit feedback

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2026-07-02 14:32:48 -05:00
Tom 3becaf2d95 emdashes begone (#10847) 2026-07-01 19:01:27 -05:00
Jonathan Bennett 2f97112987 Enhance TFTDisplay with board-specific driver support (#10827)
Added conditional inclusion for board-specific framebuffer backends and updated comments for clarity.
2026-06-30 16:47:32 -05:00
Tom fd62322876 Log tidy (#10798)
* clarify channel number formatting and fix the fixed-width padded nodeIDs

* use the explicit 0x%08x for packets and nodeIDs throughout

* LLM instructions
2026-06-30 12:44:25 -05:00
Jonathan Bennett 87bb149afb adds VARIANT_TOUCHSCREEN and ENABLE_TOUCH_INT (#10815)
* addsVARIANT_TOUCHSCREEN and ENABLE_TOUCH_INT

* Remove a duplicated _getTouch function pointer
2026-06-30 00:28:24 -05:00
Jonathan Bennett 21dbe21745 Faster TFTColor processing (#10814) 2026-06-29 21:50:09 -05:00
Jonathan Bennett fd8ba4fa4e Chop down tft-related loooong ifdefs chains (#10803)
* Chop down tft-related loooong ifdefs chains

* Fix accidental sweep-up of portduino into screen #ifdef refactor
2026-06-29 21:23:25 -05:00
webgeek1234andJonathan Bennett 9cef69a9d3 feat(gps): Do not start gps if lora region is unset (#10386)
* Disable gps thread on startup if lora region is unset

There is little reason to waste battery on the gps if the data cannot
yet be used.

* fix goobered merge

Refactor GPS enabling logic and remove duplicate code.

* trunk

---------

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2026-06-28 18:22:09 -05:00
29b3cfaeca Fix L1 E-Ink InkHUD issues (#10804)
* Fixes

* Update KeyboardApplet.cpp

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-28 15:27:01 -05:00
HarukiToreda 25c71cb9f1 InkHUD: add offline map tile backgrounds and zoom controls (#10785)
* InkHUD: map tile background, zoom controls, and GPS live tracking

- Map background tiles are now rendered on the display when available, compressed with LZ4 to keep flash usage low
- If no map tiles are loaded, the map applets behave exactly as before
- Zoom in, zoom out, and reset zoom (back to auto-fit) are accessible from the menu, and only appear when the menu is opened from a map screen
- The map updates automatically whenever GPS gets a new position or your phone shares a location
- The Positions and Favorites map applets now also refresh when mesh position packets arrive for your own node
- The Favorites map now shows your position on the map even if you have no favorites yet

* README Update

* Update MapApplet.cpp

* Zoom fixed

* Zoom with no tiles fix

* CI fix
2026-06-27 14:35:03 -05:00
79a7dcc46c Pr1 nodedb warmstore (#10705)
* NodeDB: 3-tier node store with persistent warm tier (long-tail identity retention)

Introduces a tiered NodeDB so the device retains identity (public key,
last_heard) for far more nodes than fit in the full-record hot store,
without growing heap or the persisted nodes.proto unboundedly.

- Hot store: full NodeInfoLite, MAX_NUM_NODES (120 on nRF52).
- Satellite maps: position/telemetry/environment/status capped at
  MAX_SATELLITE_NODES (40 freshest); eviction via enforceSatelliteCaps /
  evictSatelliteOverCap.
- Warm tier (WarmNodeStore): 40 B {num,last_heard,public_key} records for
  evicted nodes so DMs to/from long-tail nodes keep encrypting/decrypting.
  Persisted to /prefs/warm.dat, or on nRF52840 a dedicated 12 KB raw-flash
  record-ring below LittleFS (3x4 KB pages; see linker scripts + the
  nrf52_warm_region.py post-link guard).

NodeDB::getOrCreateMeshNode now demotes evicted nodes into the warm tier and
re-admits them (restoring key/last_heard). Router PKI decrypt/encode resolve
the peer key via NodeDB::copyPublicKey (hot store, then warm tier).

NodeInfoLite gains snr_q4 (sint32, Q4-encoded dB); the float snr is zeroed on
disk. NodeInfoLite grows 105 -> 112 B; backup 2432 -> 2468 B.

Note: the snr_q4 .proto change still needs to land in the protobufs submodule
(generated header is updated here; submodule pointer left at upstream).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* NodeDB: robust receive + retention for blocked (ignored) nodes

Hardens how ignored/favourite nodes are received over admin and retained,
closing paths where a block could be lost or accidentally cleared.

- Blocking keeps the node's public key (admin set_ignored_node and
  addFromContact no longer zero it / drop the warm-tier key), so a blocked
  peer stays a verifiable identity.
- set_ignored_node creates the node if absent, so a block by node ID sticks
  even for a node we've never heard from (e.g. pushed by a remote admin) with
  no NodeInfo or key.
- Eviction protection (favourite/ignored/manually-verified) now also applies to
  the load-time hot-store migration and is never undone by cleanupMeshDB, which
  previously purged ignored nodes that lacked user info.
- The hot-store migration leaves our own node (index 0) in place and prefers to
  demote non-protected nodes, like the runtime eviction scan.

Caps the protected set (favourite + ignored + verified) at MAX_NUM_NODES-2 via
NodeDB::setProtectedFlag(), so at least two evictable slots always remain and
getOrCreateMeshNode can always make room — replacing the previous unconditional
append that could run off the end of the node vector when every node was
protected. A locally-set favourite/ignore that hits the cap reports back to the
phone via a ClientNotification.

Adds test_nodedb_blocked covering the migration, favourite/ignored eviction
protection, ignored-survives-cleanup, and the protected-node cap. The
maintenance methods stay private in production; the test reaches them through a
PIO_UNIT_TESTING-guarded friend shim.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

# Conflicts:
#	src/mesh/NodeDB.h

* fix copilot comments

* once again

* WarmNodeStore: fix cppcheck warnings (uninitvar, constVariablePointer)

Zero-initialise `stranded[]` and `seqs[]/order[]` VLAs so cppcheck can
verify there are no unguarded reads of uninitialised memory (the guards
exist but are not visible to static analysis). Mark two local pointers
`const` where the pointed-to entry is never mutated after assignment.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* self-care added to assist 2.7 and 2.8 nodedb migration

* Tidy warm-store/self-care: comments, guards, log + flash cleanup

Style/cleanup pass over the branch (no behavior change except the noted
preprocessor simplifications, which are semantically identical):

- Comments: move function descriptions to the headers, cap in-function
  comments at ~3-4 lines, drop leading-number step markers, label stacked
  #endif blocks, de-decorate banner comments.
- dumpToLog: fully gate decl + definition + AdminModule call site behind
  MESHTASTIC_NODEDB_MIGRATION_VERBOSE so it compiles out when disabled
  (~1.2 KB when off).
- mesh-pb-constants: drop the dead nRF52832 WARM_NODE_COUNT branch and trim
  the macro docs.
- WarmNodeStore: simplify the redundant `ARCH_NRF52 && NRF52840_XXAA` guards
  to `NRF52840_XXAA`, add a kNoPage sentinel for the ring page state.
- Shorten the always-on LOG_WARN strings (~120 B flash).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* more tidying up, aligning with docs and undoing other-arch regressions

* Update protobufs (#19)

Co-authored-by: NomDeTom <116762865+NomDeTom@users.noreply.github.com>

* made the migration pathway cleareer

* address copilot review

* fixed a copilot review on a downstream PR.

* Address Copilot review comments for PR #10705 (warmstore/nodedb)

- WarmNodeStore.h: default MIGRATION_VERBOSE to 0 (suppress info-level
  chatter on production builds; opt in with =1)
- WarmNodeStore.cpp load(): move memset to top of function so all
  failure paths (header-read fail, invalid header) leave entries clear
- WarmNodeStore.cpp save(): replace manual spiLock lock/unlock around
  mkdir with LockGuard covering the full SafeFile sequence, matching
  the lock discipline in load()
- Router.cpp: memcpy(&p->public_key.bytes, ...) -> memcpy(p->public_key.bytes,
  ...) — pass decayed uint8_t* rather than pointer-to-array
- AdminModule.cpp: check setProtectedFlag return for PKC auto-favorite;
  log cap-refusal warning instead of unconditional "auto-favoriting"
- nrf52_warm_region.py: error message references both v6.ld and v7.ld

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* NodeDB: formatting cleanup (blank lines after preprocessor blocks)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Lukewarm store

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2026-06-18 08:41:34 +01:00
HarukiToreda 358e4e2fcd InkHUD: Wipe all messages option (#10721)
* Wipe messages

* trunk
2026-06-17 13:34:05 -05:00