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

+18 -7
View File
@@ -292,7 +292,10 @@ void Mesh::loop() {
bool Mesh::hasPendingOtaApply() const {
#if defined(ENABLE_OTA) && !defined(OTA_SEEDER_ONLY)
return ota::ota_ctx().apply_pending;
// A released dynamic context cannot hold a pending apply: the context is only
// handed back once apply_pending is clear.
const ota::OtaContext* oc = ota::ota_context_if_active();
return oc && oc->apply_pending;
#else
return false;
#endif
@@ -343,6 +346,20 @@ void __attribute__((noinline)) Mesh::serviceLoopMaintenance() {
}
}
#if defined(ENABLE_OTA)
#if OTA_DYNAMIC_CONTEXT
// Nothing below can run without storage, and a released context holds no
// pending apply or egress. Bail before any ota_ctx() dereference.
if (!ota::ota_context_if_active()) {
_ota_temp_was_active = false;
#if !defined(OTA_SEEDER_ONLY)
// A later workspace has a fresh manager. Resume its persistent staging
// store again, and let that manager evaluate automatic installation.
_ota_resumed = false;
_ota_autoinstall_tried = false;
#endif
return;
}
#endif
#if !defined(OTA_SEEDER_ONLY)
// Deferred apply-reboot: a verified `ota applydelta` approves the update but does NOT reboot inline,
// so its "verified; applying" reply can be delivered first (over LoRa that reply is the operator's
@@ -371,12 +388,6 @@ void __attribute__((noinline)) Mesh::serviceLoopMaintenance() {
}
}
}
#endif
#if defined(OTA_SHARED_COMPANION_QUEUE)
if (!ota::ota_context_if_active()) {
_ota_temp_was_active = false;
return;
}
#endif
const bool ota_active = isTempRadioActive();
if (!ota_active) {