Merge PR #7 with reviewed ExpressLRS power and memory fixes

Integrate ExpressLRS TX module support and its stacked ESP32 heap changes.
Use the approved Linkflow calibration (17-30 dBm), preserve the PA drive
and output path after radio recovery, and keep LoRa OTA enabled.

Preserve stored ACLs and filters on allocation failure, release owned
client/filter buffers, service heap OTA contexts on Companions, release
self-serving workspaces after TempRadio, and reset staged-resume state
when a context is released. Keep manual staging and active operations
alive. Account for all moved allocations in the runtime RAM gate.

Validation: 1,393 native cases; radio-power, heap-context, ACL persistence,
shared-queue transfer, display/inbox, radio receive, and memory regressions.
Firmware builds passed for Linkflow, Heltec V2 Companion, T-Beam MQTT
repeater, Heltec V4 R8 MQTT repeater, RAK4631 repeater, and Indicator Full.
Physical verification awaits access to the currently offline lab Pi.
This commit is contained in:
mikecarper committed 2026-09-10 22:06:16 -07:00
commit fa010196b1
26 files changed
+1072 -74

No files matched your search

+5 -3
View File
@@ -640,7 +640,7 @@ void ClientACL::load(FILESYSTEM* fs, const mesh::LocalIdentity& self_id) {
c.last_timestamp = UINT32_MAX;
}
self_id.calcSharedSecret(c.shared_secret, pub_key); // recalculate shared secrets in case our private key changed
if (num_clients < MAX_CLIENTS) {
if (num_clients < capacity) {
clients[num_clients++] = c;
} else {
full = true;
@@ -691,6 +691,8 @@ bool ClientACL::authorizeLoginTimestamp(
}
bool ClientACL::save(FILESYSTEM* fs, bool (*filter)(ClientInfo*)) {
// A failed allocation is not an empty ACL. Preserve the stored managers.
if (capacity == 0 || fs == NULL) return false;
_fs = fs;
#if defined(NRF52_PLATFORM)
mesh::AtomicFileWriter file(_fs, "/s_contacts");
@@ -803,7 +805,7 @@ bool ClientACL::clear() {
const bool files_cleared = !_fs->exists("/s_contacts")
&& !_fs->exists("/s_contacts.tmp")
&& !_fs->exists("/s_contacts.bak");
memset(clients, 0, sizeof(clients));
if (clients) memset(clients, 0, sizeof(ClientInfo) * (size_t)capacity);
num_clients = 0;
return files_cleared;
}
@@ -828,7 +830,7 @@ ClientInfo* ClientACL::putClient(const mesh::Identity& id, uint8_t init_perms) {
}
ClientInfo* c;
if (num_clients < MAX_CLIENTS) {
if (num_clients < capacity) {
c = &clients[num_clients++];
} else {
if (oldest == NULL) return NULL; // every entry is protected