5cf85d523a205fb42d9ac1d34baf02c763bfacca
* 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.
Overview
This repository contains the official device firmware for Meshtastic, an open-source LoRa mesh networking project designed for long-range, low-power communication without relying on internet or cellular infrastructure. The firmware supports various hardware platforms, including ESP32, nRF52, RP2040/RP2350, and Linux-based devices.
Meshtastic enables text messaging, location sharing, and telemetry over a decentralized mesh network, making it ideal for outdoor adventures, emergency preparedness, and remote operations.
Get Started
- 🔧 Building Instructions - Learn how to compile the firmware from source.
- ⚡ Flashing Instructions - Install or update the firmware on your device.
Join our community and help improve Meshtastic! 🚀
Stats
Languages
C++
70.8%
C
25.8%
Python
2.2%
Shell
0.7%
Batchfile
0.2%
Other
0.1%
