Add Bluetooth HID remote control support

Integrate BLE keyboard host functionality for page-turner remotes. Adds device pairing, button mapping, preset configurations for Free2/Free3 remotes, and persistent storage of mappings. Migrates from open-x4-sdk to freeink-sdk submodule which includes the BleKeyboardHost library. Implements CPU frequency locking during BLE operations to prevent watchdog timeouts.
This commit is contained in:
Justin Mitchell
2026-06-24 15:36:29 -04:00
parent 6e8dbd7f23
commit 9ab0b0bfb7
20 changed files with 984 additions and 15 deletions
+28
View File
@@ -11,6 +11,7 @@
#include "BookmarkEntry.h"
#include "CrossPointSettings.h"
#include "CrossPointState.h"
#include "MappedInputManager.h"
#include "OpdsServerStore.h"
#include "RecentBooksStore.h"
#include "SettingsList.h"
@@ -142,6 +143,16 @@ bool JsonSettingsIO::saveSettings(const CrossPointSettings& s, const char* path)
doc["frontButtonConfirm"] = s.frontButtonConfirm;
doc["frontButtonLeft"] = s.frontButtonLeft;
doc["frontButtonRight"] = s.frontButtonRight;
// Bluetooth — managed by BluetoothSettingsActivity, not in SettingsList.
doc["bluetoothEnabled"] = s.bluetoothEnabled;
JsonArray bleMap = doc["bleKeyMap"].to<JsonArray>();
for (const auto& e : s.bleKeyMap) {
if (e.keyKind == 0xFF || e.button == 0xFF) continue; // skip empty/unassigned slots
JsonObject o = bleMap.add<JsonObject>();
o["k"] = e.keyKind;
o["v"] = e.keyValue;
o["b"] = e.button;
}
// Font family — uses dynamic getter/setter in SettingsList so the generic loop skips it.
doc["fontFamily"] = s.fontFamily;
// SD card font family name — not in SettingsList, save manually
@@ -243,6 +254,23 @@ bool JsonSettingsIO::loadSettings(CrossPointSettings& s, const char* json, bool*
clamp(doc["frontButtonRight"] | (uint8_t)S::FRONT_HW_RIGHT, S::FRONT_BUTTON_HARDWARE_COUNT, S::FRONT_HW_RIGHT);
CrossPointSettings::validateFrontButtonMapping(s);
// Bluetooth — managed by BluetoothSettingsActivity, not in SettingsList.
s.bluetoothEnabled = clamp(doc["bluetoothEnabled"] | (uint8_t)0, 2, 0);
for (auto& e : s.bleKeyMap) e = CrossPointSettings::BleKeyMapEntry{}; // reset to empty
JsonArrayConst bleMap = doc["bleKeyMap"];
if (!bleMap.isNull()) {
uint8_t slot = 0;
for (JsonObjectConst o : bleMap) {
if (slot >= CrossPointSettings::BLE_MAP_CAPACITY) break;
const uint8_t button = o["b"] | (uint8_t)0xFF;
if (button >= MappedInputManager::kButtonCount) continue; // drop invalid mappings
s.bleKeyMap[slot].keyKind = o["k"] | (uint8_t)0xFF;
s.bleKeyMap[slot].keyValue = o["v"] | (uint8_t)0;
s.bleKeyMap[slot].button = button;
slot++;
}
}
// Font family — uses dynamic getter/setter in SettingsList so the generic loop skips it.
const uint8_t storedFontFamily = doc["fontFamily"] | (uint8_t)0;
s.fontFamily = clamp(storedFontFamily, CrossPointSettings::BUILTIN_FONT_COUNT, 0);