Retry EPUB section build after freeing BLE stack

When building an EPUB section fails with Bluetooth enabled, temporarily stop the BLE stack to free up memory (~16KB), retry the build, then restart BLE. This works around memory fragmentation caused by the NimBLE stack that prevents allocating the large contiguous buffer needed for inflate/deflate operations. The recovery only runs once per uncached chapter since chapters are cached afterwards.
This commit is contained in:
Justin Mitchell
2026-06-24 16:13:43 -04:00
parent 9ab0b0bfb7
commit 6305777b22
9 changed files with 63 additions and 94 deletions
@@ -21,14 +21,18 @@ const BleButtonMapActivity::Fn BleButtonMapActivity::kFunctions[] = {
{MappedInputManager::Button::Left, StrId::STR_DIR_LEFT},
{MappedInputManager::Button::Right, StrId::STR_DIR_RIGHT},
};
const uint8_t BleButtonMapActivity::kFunctionCount =
static_cast<uint8_t>(sizeof(kFunctions) / sizeof(kFunctions[0]));
const uint8_t BleButtonMapActivity::kFunctionCount = static_cast<uint8_t>(sizeof(kFunctions) / sizeof(kFunctions[0]));
void BleButtonMapActivity::onEnter() {
Activity::onEnter();
step = Step::WaitForKey;
capturedKind = 0xFF;
functionIndex = 0;
// Start every mapping session from a clean slate: the user re-maps each remote
// button once, so a button can't be left bound to a stale action and there's no
// separate "clear mappings" step to remember.
for (auto& e : SETTINGS.bleKeyMap) e = CrossPointSettings::BleKeyMapEntry{};
SETTINGS.saveToFile();
mappedInput.setBleCaptureMode(true);
requestUpdate();
}
@@ -40,6 +44,13 @@ void BleButtonMapActivity::onExit() {
bool BleButtonMapActivity::assignCapturedKey(MappedInputManager::Button button) {
const uint8_t btn = static_cast<uint8_t>(button);
// One key per action: drop any other key currently bound to this action so the same
// action can't be triggered by two different remote buttons.
for (auto& e : SETTINGS.bleKeyMap) {
if (e.button == btn && !(e.keyKind == capturedKind && e.keyValue == capturedValue)) {
e = CrossPointSettings::BleKeyMapEntry{};
}
}
// Update an existing binding for this key, if present.
for (auto& e : SETTINGS.bleKeyMap) {
if (e.button != 0xFF && e.keyKind == capturedKind && e.keyValue == capturedValue) {
@@ -92,11 +103,8 @@ void BleButtonMapActivity::loop() {
});
if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) {
if (!assignCapturedKey(kFunctions[functionIndex].button)) {
// Table full: surface it instead of silently dropping the binding.
errorUntil = millis() + 2500;
}
// Back to capturing so the user can map the next remote button.
assignCapturedKey(kFunctions[functionIndex].button);
// Back to capturing so the user can map (or re-map) the next remote button.
step = Step::WaitForKey;
capturedKind = 0xFF;
requestUpdate();
@@ -146,10 +154,6 @@ void BleButtonMapActivity::render(RenderLock&&) {
[this](int i) { return std::string(I18N.get(kFunctions[i].label)); }, nullptr, nullptr, nullptr, false);
}
if (errorUntil > millis()) {
GUI.drawHelpText(renderer, Rect{0, pageHeight - metrics.buttonHintsHeight - 22, pageWidth, 20}, tr(STR_BT_MAP_FULL));
}
const char* confirm = step == Step::WaitForKey ? "" : tr(STR_SELECT);
const auto labels = mappedInput.mapLabels(tr(STR_BACK), confirm, tr(STR_DIR_UP), tr(STR_DIR_DOWN));
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);