feat: user path to resume BT from low-memory pause via reader menu toggle
Field session: after a recovery teardown the heap settles at ~72 KB -- below the 100 KB start floor -- and never recovers on its own (the defrag silent-restart only fires when a build FAILS, and builds succeed now). BT stayed paused forever, the menu toggle claimed ON, and toggling off/on changed nothing. - Menu label tells the truth: ON / PAUSED (enabled but stack down) / OFF (new STR_STATE_PAUSED string) - Toggling BT on below the heap floor silent-restarts into the current book: the fresh boot's ~118 KB passes the gate and BT auto-starts on resume. Explicit user intent is the right trigger for the defrag. - Hoist the floor to bleinput::kStartMinFreeHeap, shared by the lifecycle gate and the toggle
This commit is contained in:
@@ -299,6 +299,8 @@ STR_BT_NO_PAIRED: "No paired devices"
|
||||
STR_BT_MAP_BUTTONS: "Map Remote Buttons"
|
||||
STR_BT_PRESS_REMOTE: "Press a button on your remote"
|
||||
STR_BT_CONNECTING_POPUP: "BT Connecting..."
|
||||
STR_BT_PAUSED_LOW_MEM_POPUP: "BT paused (low memory)"
|
||||
STR_STATE_PAUSED: "PAUSED"
|
||||
STR_BT_PAGE_FORWARD: "Page Forward"
|
||||
STR_BT_PAGE_BACK: "Page Back"
|
||||
STR_BT_FORGET_PROMPT: "Hold Confirm to forget"
|
||||
|
||||
@@ -25,6 +25,12 @@ namespace bleinput {
|
||||
// Advertised central name shown to peripherals during pairing.
|
||||
inline constexpr const char* kHostName = "CrossPoint";
|
||||
|
||||
// Heap floor for starting the NimBLE stack (~57 KB) while leaving the reader's
|
||||
// section-build pre-flight (40 KB) satisfiable afterwards. Shared by the main-loop
|
||||
// lifecycle gate and the reader menu's toggle (which offers a defrag restart when a
|
||||
// user turns BT on below the floor).
|
||||
inline constexpr size_t kStartMinFreeHeap = 100 * 1024;
|
||||
|
||||
// Start the BLE HID host (idempotent). Returns false if BLE is compiled out or
|
||||
// NimBLE init failed. Safe to call repeatedly.
|
||||
bool ensureStarted();
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
|
||||
#include <GfxRenderer.h>
|
||||
#include <I18n.h>
|
||||
#include <Logging.h>
|
||||
|
||||
#include "BleInput.h"
|
||||
#include "CrossPointSettings.h"
|
||||
#include "MappedInputManager.h"
|
||||
#include "SilentRestart.h"
|
||||
#include "components/UITheme.h"
|
||||
#include "fontIds.h"
|
||||
|
||||
@@ -84,6 +87,15 @@ void EpubReaderMenuActivity::loop() {
|
||||
// so start/stop has a single owner.
|
||||
SETTINGS.bluetoothEnabled = SETTINGS.bluetoothEnabled ? 0 : 1;
|
||||
SETTINGS.saveToFile();
|
||||
// Turning BT on below the lifecycle's heap floor would otherwise sit in PAUSED
|
||||
// until the heap happens to recover -- which a long session's fragmentation never
|
||||
// gives back. The user asked for BT *now*: silent-restart into this book to
|
||||
// defrag (fresh boot is ~118 KB free, comfortably above the floor), and BT
|
||||
// auto-starts on the way back in.
|
||||
if (SETTINGS.bluetoothEnabled && !BleHid.isRunning() && ESP.getFreeHeap() < bleinput::kStartMinFreeHeap) {
|
||||
LOG_INF("ERM", "BT enabled below heap floor (%u); silent restart to defrag", ESP.getFreeHeap());
|
||||
silentRestartToReader();
|
||||
}
|
||||
requestUpdate();
|
||||
return;
|
||||
}
|
||||
@@ -138,8 +150,12 @@ void EpubReaderMenuActivity::render(RenderLock&&) {
|
||||
// Render current page turn value on the right edge of the content area.
|
||||
return pageTurnLabels[selectedPageTurnOption];
|
||||
} else if (value == MenuAction::TOGGLE_BLUETOOTH) {
|
||||
// Render current Bluetooth on/off state on the right edge.
|
||||
return SETTINGS.bluetoothEnabled ? tr(STR_STATE_ON) : tr(STR_STATE_OFF);
|
||||
// Render current Bluetooth state on the right edge. "Enabled but the stack
|
||||
// isn't running" is the low-memory deferral -- show PAUSED, not a lie.
|
||||
if (SETTINGS.bluetoothEnabled) {
|
||||
return BleHid.isRunning() ? tr(STR_STATE_ON) : tr(STR_STATE_PAUSED);
|
||||
}
|
||||
return tr(STR_STATE_OFF);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
||||
+5
-5
@@ -514,18 +514,18 @@ void updateBluetoothLifecycle() {
|
||||
static constexpr uint32_t BLE_RESTART_COOLDOWN_MS = 30 * 1000;
|
||||
const bool inCooldown = recoveryTeardownMs != 0 && millis() - recoveryTeardownMs < BLE_RESTART_COOLDOWN_MS;
|
||||
// Heap gate: NimBLE needs ~57 KB, and the section-build pre-flight needs 40 KB free
|
||||
// AFTER the stack is up -- so anything below ~100 KB just re-enters build recovery
|
||||
// AFTER the stack is up -- so anything below the floor just re-enters build recovery
|
||||
// and tears BLE straight back down. (A first cut used 70 KB for restarts; 70-57=13 KB
|
||||
// left for builds, guaranteeing the oscillation above.) Defer and retry next loop;
|
||||
// the build path's silent-restart defrag yields ~118 KB and passes this gate.
|
||||
static constexpr size_t BLE_START_MIN_FREE_HEAP = 100 * 1024;
|
||||
// the reader menu's toggle offers a defrag restart, and the build path's silent
|
||||
// restart also yields ~118 KB and passes this gate.
|
||||
static bool deferralAnnounced = false;
|
||||
if (wanted && !BleHid.isRunning() && (inCooldown || ESP.getFreeHeap() < BLE_START_MIN_FREE_HEAP)) {
|
||||
if (wanted && !BleHid.isRunning() && (inCooldown || ESP.getFreeHeap() < bleinput::kStartMinFreeHeap)) {
|
||||
static uint32_t lastGateLogMs = 0;
|
||||
if (millis() - lastGateLogMs > 10000) {
|
||||
lastGateLogMs = millis();
|
||||
LOG_INF("BLELC", "start deferred: heap %u floor %u cooldown=%d", ESP.getFreeHeap(),
|
||||
(unsigned)BLE_START_MIN_FREE_HEAP, inCooldown ? 1 : 0);
|
||||
(unsigned)bleinput::kStartMinFreeHeap, inCooldown ? 1 : 0);
|
||||
}
|
||||
// Tell the reader once per deferral episode that the remote is paused -- otherwise
|
||||
// the only symptom is a remote that silently stopped working. Draws into the
|
||||
|
||||
Reference in New Issue
Block a user