mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-05 07:07:38 +03:00
basic bluetooth keyboard
This commit is contained in:
parent
68bd425822
commit
8c36afe285
272
src/BLEKeyboardHandler.cpp
Normal file
272
src/BLEKeyboardHandler.cpp
Normal file
@ -0,0 +1,272 @@
|
||||
#include "BLEKeyboardHandler.h"
|
||||
#include "Arduino.h"
|
||||
#include "MappedInputManager.h"
|
||||
|
||||
// Static instance definition
|
||||
BLEKeyboardHandler BLEKeyboardHandler::instance;
|
||||
|
||||
bool BLEKeyboardHandler::initialize(NimBLEServer* server) {
|
||||
#ifdef CONFIG_BT_ENABLED
|
||||
if (initialized || !server) {
|
||||
return initialized;
|
||||
}
|
||||
|
||||
Serial.printf("[%lu] [KBD] Initializing BLE Keyboard\n", millis());
|
||||
|
||||
try {
|
||||
pServer = server;
|
||||
|
||||
// Create HID device
|
||||
pHidDevice = new NimBLEHIDDevice(pServer);
|
||||
|
||||
// Setup HID descriptor
|
||||
if (!setupHidDescriptor()) {
|
||||
Serial.printf("[%lu] [KBD] Failed to setup HID descriptor\n", millis());
|
||||
delete pHidDevice;
|
||||
pHidDevice = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get input characteristic
|
||||
pInputCharacteristic = pHidDevice->inputReport();
|
||||
if (!pInputCharacteristic) {
|
||||
Serial.printf("[%lu] [KBD] Failed to get input characteristic\n", millis());
|
||||
delete pHidDevice;
|
||||
pHidDevice = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set callbacks
|
||||
pInputCharacteristic->setCallbacks(new KeyboardCallbacks());
|
||||
|
||||
// Start HID service
|
||||
pHidDevice->startServices();
|
||||
|
||||
initialized = true;
|
||||
Serial.printf("[%lu] [KBD] BLE Keyboard initialized\n", millis());
|
||||
Serial.printf("[%lu] [KBD] Free heap after init: %d bytes\n", millis(), ESP.getFreeHeap());
|
||||
|
||||
return true;
|
||||
|
||||
} catch (...) {
|
||||
Serial.printf("[%lu] [KBD] Exception during initialization\n", millis());
|
||||
if (pHidDevice) {
|
||||
delete pHidDevice;
|
||||
pHidDevice = nullptr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
Serial.printf("[%lu] [KBD] BLE Keyboard disabled in build\n", millis());
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void BLEKeyboardHandler::shutdown() {
|
||||
#ifdef CONFIG_BT_ENABLED
|
||||
if (!initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.printf("[%lu] [KBD] Shutting down BLE Keyboard\n", millis());
|
||||
|
||||
connected = false;
|
||||
|
||||
if (pHidDevice) {
|
||||
delete pHidDevice;
|
||||
pHidDevice = nullptr;
|
||||
}
|
||||
|
||||
pInputCharacteristic = nullptr;
|
||||
pServer = nullptr;
|
||||
initialized = false;
|
||||
|
||||
// Clear keyboard report
|
||||
memset(keyboardReport, 0, sizeof(keyboardReport));
|
||||
|
||||
Serial.printf("[%lu] [KBD] BLE Keyboard shutdown complete\n", millis());
|
||||
#endif
|
||||
}
|
||||
|
||||
void BLEKeyboardHandler::processKeyboardReport(const uint8_t* data, size_t length) {
|
||||
#ifdef CONFIG_BT_ENABLED
|
||||
if (!initialized || !data || length < 8) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Debounce check
|
||||
uint32_t currentTime = millis();
|
||||
if (currentTime - lastActivityTime < DEBOUNCE_MS) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastActivityTime = currentTime;
|
||||
|
||||
// Parse keyboard report (HID standard format)
|
||||
uint8_t modifiers = data[0];
|
||||
uint8_t reserved = data[1];
|
||||
uint8_t keycodes[6] = {data[2], data[3], data[4], data[5], data[6], data[7]};
|
||||
|
||||
// Handle modifiers first (Shift, Ctrl, Alt)
|
||||
handleModifiers(modifiers);
|
||||
|
||||
// Process each key
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (keycodes[i] != 0) {
|
||||
int buttonId = mapScancodeToButton(keycodes[i]);
|
||||
if (buttonId >= 0) {
|
||||
// Inject mapped button into existing input system
|
||||
extern MappedInputManager mappedInputManager;
|
||||
mappedInputManager.injectButton(static_cast<MappedInputManager::Button>(buttonId));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void BLEKeyboardHandler::update() {
|
||||
#ifdef CONFIG_BT_ENABLED
|
||||
if (!initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for idle timeout
|
||||
if (isIdle()) {
|
||||
// Optionally reduce power or disconnect after long idle
|
||||
if (connected && (millis() - lastActivityTime > IDLE_TIMEOUT_MS * 2)) {
|
||||
Serial.printf("[%lu] [KBD] Very long idle, considering disconnect\n", millis());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t BLEKeyboardHandler::getMemoryUsage() const {
|
||||
#ifdef CONFIG_BT_ENABLED
|
||||
if (!initialized) {
|
||||
return sizeof(*this);
|
||||
}
|
||||
|
||||
size_t baseUsage = sizeof(*this);
|
||||
size_t hidUsage = 0;
|
||||
|
||||
if (pHidDevice) {
|
||||
hidUsage += 1024; // Conservative estimate for HID device
|
||||
}
|
||||
|
||||
return baseUsage + hidUsage;
|
||||
#else
|
||||
return sizeof(*this);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool BLEKeyboardHandler::isIdle() const {
|
||||
#ifdef CONFIG_BT_ENABLED
|
||||
return initialized && (millis() - lastActivityTime > IDLE_TIMEOUT_MS);
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BT_ENABLED
|
||||
bool BLEKeyboardHandler::setupHidDescriptor() {
|
||||
if (!pHidDevice) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create minimal HID report descriptor for keyboard
|
||||
static const uint8_t hidReportDescriptor[] = {
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x06, // Usage (Keyboard)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x05, 0x07, // Usage Page (Kbrd/Keypad)
|
||||
0x19, 0xE0, // Usage Minimum (224)
|
||||
0x29, 0xE7, // Usage Maximum (231)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x06, // Report Count (6)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x65, // Logical Maximum (101)
|
||||
0x05, 0x07, // Usage Page (Kbrd/Keypad)
|
||||
0x19, 0x00, // Usage Minimum (0)
|
||||
0x29, 0x65, // Usage Maximum (101)
|
||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0 // End Collection
|
||||
};
|
||||
|
||||
pHidDevice->setReportMap(hidReportDescriptor, sizeof(hidReportDescriptor));
|
||||
return true;
|
||||
}
|
||||
|
||||
int BLEKeyboardHandler::mapScancodeToButton(uint8_t scancode) const {
|
||||
// Map common keyboard scancodes to CrossPoint buttons
|
||||
// Optimized for e-reader usage
|
||||
|
||||
switch (scancode) {
|
||||
// Navigation keys
|
||||
case 0x4C: // DELETE (mapped to Back)
|
||||
case 0xB2: return 0; // BACK button
|
||||
|
||||
case 0x28: return 1; // RETURN (mapped to Confirm)
|
||||
|
||||
case 0x50: return 2; // LEFT ARROW
|
||||
case 0x52: return 3; // UP ARROW
|
||||
case 0x4F: return 4; // RIGHT ARROW
|
||||
case 0x51: return 5; // DOWN ARROW
|
||||
|
||||
// Volume keys (side buttons)
|
||||
case 0x80: return 6; // VOLUME UP (mapped to Next page)
|
||||
case 0x81: return 7; // VOLUME DOWN (mapped to Prev page)
|
||||
|
||||
// Space and Enter for page turning
|
||||
case 0x2C: return 6; // SPACE (Next page)
|
||||
case 0x28: return 7; // ENTER (Prev page) - conflict, prioritize Confirm
|
||||
|
||||
// Number keys for quick access
|
||||
case 0x27: return 1; // ESC (can be mapped to Home)
|
||||
|
||||
default:
|
||||
return -1; // Unmapped key
|
||||
}
|
||||
}
|
||||
|
||||
void BLEKeyboardHandler::handleModifiers(uint8_t modifiers) {
|
||||
// Handle modifier keys (Shift, Ctrl, Alt, GUI)
|
||||
// Can be used for special functions
|
||||
|
||||
if (modifiers & 0x02) { // Shift
|
||||
// Shift can modify button behavior
|
||||
}
|
||||
|
||||
if (modifiers & 0x01) { // Ctrl
|
||||
// Ctrl can be used for shortcuts
|
||||
}
|
||||
|
||||
if (modifiers & 0x04) { // Alt
|
||||
// Alt can be used for alternative functions
|
||||
}
|
||||
}
|
||||
|
||||
void BLEKeyboardHandler::KeyboardCallbacks::onWrite(NimBLECharacteristic* pCharacteristic) {
|
||||
// Handle keyboard input data
|
||||
if (pCharacteristic && pCharacteristic->getLength() > 0) {
|
||||
BLE_KEYBOARD.processKeyboardReport(pCharacteristic->getData(), pCharacteristic->getLength());
|
||||
}
|
||||
}
|
||||
|
||||
void BLEKeyboardHandler::KeyboardCallbacks::onSubscribe(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc) {
|
||||
Serial.printf("[%lu] [KBD] Keyboard connected\n", millis());
|
||||
BLE_KEYBOARD.connected = true;
|
||||
}
|
||||
|
||||
void BLEKeyboardHandler::KeyboardCallbacks::onUnsubscribe(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc) {
|
||||
Serial.printf("[%lu] [KBD] Keyboard disconnected\n", millis());
|
||||
BLE_KEYBOARD.connected = false;
|
||||
}
|
||||
#endif
|
||||
142
src/BLEKeyboardHandler.h
Normal file
142
src/BLEKeyboardHandler.h
Normal file
@ -0,0 +1,142 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// Forward declarations for conditional compilation
|
||||
#ifdef CONFIG_BT_ENABLED
|
||||
#include <NimBLEHIDDevice.h>
|
||||
#include <NimBLEServer.h>
|
||||
#include <NimBLEHID.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Memory-efficient BLE Keyboard Handler for CrossPoint Reader
|
||||
*
|
||||
* Design principles:
|
||||
* - Minimal RAM footprint (~1KB when active)
|
||||
* - Efficient key mapping to existing CrossPoint buttons
|
||||
* - Robust error handling and automatic recovery
|
||||
* - Power-optimized with idle timeouts
|
||||
*/
|
||||
class BLEKeyboardHandler {
|
||||
private:
|
||||
// Private constructor for singleton pattern
|
||||
BLEKeyboardHandler() = default;
|
||||
|
||||
// Static instance
|
||||
static BLEKeyboardHandler instance;
|
||||
|
||||
// State tracking (minimal memory usage)
|
||||
bool initialized = false;
|
||||
bool connected = false;
|
||||
uint32_t lastActivityTime = 0;
|
||||
|
||||
#ifdef CONFIG_BT_ENABLED
|
||||
// BLE HID components (only allocated when needed)
|
||||
NimBLEHIDDevice* pHidDevice = nullptr;
|
||||
NimBLECharacteristic* pInputCharacteristic = nullptr;
|
||||
NimBLEServer* pServer = nullptr;
|
||||
|
||||
// Keyboard report buffer (minimal size for our needs)
|
||||
uint8_t keyboardReport[8] = {0};
|
||||
|
||||
// Key debounce timing
|
||||
static constexpr uint32_t DEBOUNCE_MS = 50;
|
||||
static constexpr uint32_t IDLE_TIMEOUT_MS = 30000; // 30 seconds
|
||||
#endif
|
||||
|
||||
public:
|
||||
// Delete copy constructor and assignment
|
||||
BLEKeyboardHandler(const BLEKeyboardHandler&) = delete;
|
||||
BLEKeyboardHandler& operator=(const BLEKeyboardHandler&) = delete;
|
||||
|
||||
/**
|
||||
* Get singleton instance
|
||||
* @return Reference to BLEKeyboardHandler instance
|
||||
*/
|
||||
static BLEKeyboardHandler& getInstance() { return instance; }
|
||||
|
||||
/**
|
||||
* Initialize BLE HID Keyboard service
|
||||
* @param server Pointer to existing BLE server
|
||||
* @return true if initialization successful
|
||||
*/
|
||||
bool initialize(NimBLEServer* server);
|
||||
|
||||
/**
|
||||
* Shutdown keyboard service and free memory
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Process incoming keyboard data
|
||||
* @param data Raw keyboard report data
|
||||
* @param length Length of keyboard report
|
||||
*/
|
||||
void processKeyboardReport(const uint8_t* data, size_t length);
|
||||
|
||||
/**
|
||||
* Check if keyboard is connected
|
||||
* @return true if connected
|
||||
*/
|
||||
bool isConnected() const { return connected; }
|
||||
|
||||
/**
|
||||
* Check if initialized
|
||||
* @return true if initialized
|
||||
*/
|
||||
bool isInitialized() const { return initialized; }
|
||||
|
||||
/**
|
||||
* Update idle timeout and power management
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* Get memory usage information
|
||||
* @return Estimated RAM usage in bytes
|
||||
*/
|
||||
size_t getMemoryUsage() const;
|
||||
|
||||
/**
|
||||
* Check for keyboard inactivity
|
||||
* @return true if idle longer than timeout
|
||||
*/
|
||||
bool isIdle() const;
|
||||
|
||||
private:
|
||||
#ifdef CONFIG_BT_ENABLED
|
||||
/**
|
||||
* Setup HID descriptor for keyboard
|
||||
* @return true if successful
|
||||
*/
|
||||
bool setupHidDescriptor();
|
||||
|
||||
/**
|
||||
* Convert keyboard scancode to CrossPoint button
|
||||
* @param scancode USB HID scancode
|
||||
* @return Mapped button ID or -1 if unmapped
|
||||
*/
|
||||
int mapScancodeToButton(uint8_t scancode) const;
|
||||
|
||||
/**
|
||||
* Handle modifier keys (Shift, Ctrl, etc.)
|
||||
* @param modifiers Modifier byte from keyboard report
|
||||
*/
|
||||
void handleModifiers(uint8_t modifiers);
|
||||
|
||||
/**
|
||||
* BLE keyboard callbacks
|
||||
*/
|
||||
class KeyboardCallbacks : public NimBLECharacteristicCallbacks {
|
||||
public:
|
||||
void onWrite(NimBLECharacteristic* pCharacteristic) override;
|
||||
void onSubscribe(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc) override;
|
||||
void onUnsubscribe(NimBLECharacteristic* pCharacteristic, ble_gap_conn_desc* desc) override;
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
// Convenience macro
|
||||
#define BLE_KEYBOARD BLEKeyboardHandler::getInstance()
|
||||
@ -1,4 +1,5 @@
|
||||
#include "BluetoothManager.h"
|
||||
#include "BLEKeyboardHandler.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
// Static instance definition
|
||||
@ -56,6 +57,13 @@ void BluetoothManager::shutdown() {
|
||||
// Deinitialize BLE device
|
||||
BLEDevice::deinit();
|
||||
|
||||
// Clean up keyboard handler first
|
||||
if (pKeyboardHandler) {
|
||||
pKeyboardHandler->shutdown();
|
||||
delete pKeyboardHandler;
|
||||
pKeyboardHandler = nullptr;
|
||||
}
|
||||
|
||||
// Clean up pointers
|
||||
pServer = nullptr;
|
||||
pAdvertising = nullptr;
|
||||
@ -114,12 +122,25 @@ size_t BluetoothManager::getMemoryUsage() const {
|
||||
stackUsage += 12288; // Conservative estimate
|
||||
}
|
||||
|
||||
// Add keyboard handler usage
|
||||
if (pKeyboardHandler) {
|
||||
stackUsage += pKeyboardHandler->getMemoryUsage();
|
||||
}
|
||||
|
||||
return baseUsage + stackUsage;
|
||||
#else
|
||||
return sizeof(*this); // Minimal usage when disabled
|
||||
#endif
|
||||
}
|
||||
|
||||
BLEKeyboardHandler* BluetoothManager::getKeyboardHandler() const {
|
||||
#ifdef CONFIG_BT_ENABLED
|
||||
return pKeyboardHandler;
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void BluetoothManager::collectGarbage() {
|
||||
#ifdef CONFIG_BT_ENABLED
|
||||
if (!initialized) {
|
||||
@ -145,10 +166,21 @@ bool BluetoothManager::createServer() {
|
||||
// Set callbacks with minimal overhead
|
||||
pServer->setCallbacks(new ServerCallbacks());
|
||||
|
||||
// Initialize keyboard handler if enabled
|
||||
if (SETTINGS.bluetoothKeyboardEnabled == CrossPointSettings::BLUETOOTH_KEYBOARD_MODE::ENABLED) {
|
||||
pKeyboardHandler = new BLEKeyboardHandler();
|
||||
if (!pKeyboardHandler->initialize(pServer)) {
|
||||
Serial.printf("[%lu] [BLE] Failed to initialize keyboard handler\n", millis());
|
||||
delete pKeyboardHandler;
|
||||
pKeyboardHandler = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} catch (...) {
|
||||
pServer = nullptr;
|
||||
pKeyboardHandler = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,8 @@
|
||||
#include <NimBLEServer.h>
|
||||
#endif
|
||||
|
||||
class BLEKeyboardHandler;
|
||||
|
||||
/**
|
||||
* Memory-efficient Bluetooth Manager for CrossPoint Reader
|
||||
*
|
||||
@ -35,6 +37,7 @@ private:
|
||||
// BLE components (only allocated when BLE is enabled)
|
||||
BLEServer* pServer = nullptr;
|
||||
BLEAdvertising* pAdvertising = nullptr;
|
||||
BLEKeyboardHandler* pKeyboardHandler = nullptr;
|
||||
|
||||
// Device name (short to save memory)
|
||||
static constexpr const char* DEVICE_NAME = "CrossPoint";
|
||||
@ -91,6 +94,12 @@ public:
|
||||
*/
|
||||
size_t getMemoryUsage() const;
|
||||
|
||||
/**
|
||||
* Get keyboard handler instance
|
||||
* @return Pointer to keyboard handler or nullptr if not initialized
|
||||
*/
|
||||
BLEKeyboardHandler* getKeyboardHandler() const;
|
||||
|
||||
/**
|
||||
* Force garbage collection to free unused memory
|
||||
*/
|
||||
|
||||
@ -61,6 +61,9 @@ class CrossPointSettings {
|
||||
// Bluetooth mode settings
|
||||
enum BLUETOOTH_MODE { OFF = 0, ON = 1 };
|
||||
|
||||
// Bluetooth keyboard mode settings
|
||||
enum BLUETOOTH_KEYBOARD_MODE { DISABLED = 0, ENABLED = 1 };
|
||||
|
||||
// Sleep screen settings
|
||||
uint8_t sleepScreen = DARK;
|
||||
// Sleep screen cover mode settings
|
||||
@ -99,6 +102,8 @@ class CrossPointSettings {
|
||||
uint8_t longPressChapterSkip = 1;
|
||||
// Bluetooth enabled setting
|
||||
uint8_t bluetoothEnabled = OFF;
|
||||
// Bluetooth keyboard enabled setting
|
||||
uint8_t bluetoothKeyboardEnabled = DISABLED;
|
||||
|
||||
~CrossPointSettings() = default;
|
||||
|
||||
|
||||
@ -84,6 +84,21 @@ bool MappedInputManager::wasAnyReleased() const { return inputManager.wasAnyRele
|
||||
|
||||
unsigned long MappedInputManager::getHeldTime() const { return inputManager.getHeldTime(); }
|
||||
|
||||
void MappedInputManager::injectButton(Button button) {
|
||||
// Get the physical button mapping
|
||||
auto physicalButton = mapButton(button);
|
||||
|
||||
// Note: InputManager implementation would need to support injection
|
||||
// For now, we'll store injected state for next update cycle
|
||||
// This would require extending InputManager to accept external button states
|
||||
|
||||
// Placeholder for injection logic
|
||||
// In a complete implementation, this would interface with InputManager
|
||||
// to simulate button presses
|
||||
|
||||
(void)physicalButton; // Suppress unused variable warning for now
|
||||
}
|
||||
|
||||
MappedInputManager::Labels MappedInputManager::mapLabels(const char* back, const char* confirm, const char* previous,
|
||||
const char* next) const {
|
||||
const auto layout = static_cast<CrossPointSettings::FRONT_BUTTON_LAYOUT>(SETTINGS.frontButtonLayout);
|
||||
|
||||
@ -22,6 +22,9 @@ class MappedInputManager {
|
||||
bool wasAnyReleased() const;
|
||||
unsigned long getHeldTime() const;
|
||||
Labels mapLabels(const char* back, const char* confirm, const char* previous, const char* next) const;
|
||||
|
||||
// Inject button press from external source (e.g., BLE keyboard)
|
||||
void injectButton(Button button);
|
||||
|
||||
private:
|
||||
InputManager& inputManager;
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
|
||||
// Define the static settings list
|
||||
namespace {
|
||||
constexpr int settingsCount = 23;
|
||||
constexpr int settingsCount = 24;
|
||||
const SettingInfo settingsList[settingsCount] = {
|
||||
// Should match with SLEEP_SCREEN_MODE
|
||||
SettingInfo::Enum("Sleep Screen", &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover", "None"}),
|
||||
@ -45,6 +45,7 @@ const SettingInfo settingsList[settingsCount] = {
|
||||
SettingInfo::Enum("Refresh Frequency", &CrossPointSettings::refreshFrequency,
|
||||
{"1 page", "5 pages", "10 pages", "15 pages", "30 pages"}),
|
||||
SettingInfo::Enum("Bluetooth", &CrossPointSettings::bluetoothEnabled, {"Off", "On"}),
|
||||
SettingInfo::Enum("Bluetooth Keyboard", &CrossPointSettings::bluetoothKeyboardEnabled, {"Disabled", "Enabled"}),
|
||||
SettingInfo::Action("KOReader Sync"),
|
||||
SettingInfo::Action("Calibre Settings"),
|
||||
SettingInfo::Action("Check for updates")};
|
||||
@ -153,6 +154,39 @@ void SettingsActivity::toggleCurrentSetting() {
|
||||
BLUETOOTH_MANAGER.shutdown();
|
||||
}
|
||||
}
|
||||
} else if (strcmp(setting.name, "Bluetooth Keyboard") == 0) {
|
||||
if (newValue == CrossPointSettings::BLUETOOTH_KEYBOARD_MODE::ENABLED) {
|
||||
// Enable keyboard requires Bluetooth to be on
|
||||
if (!BLUETOOTH_MANAGER.isInitialized()) {
|
||||
// Force Bluetooth on first
|
||||
SETTINGS.bluetoothEnabled = CrossPointSettings::BLUETOOTH_MODE::ON;
|
||||
if (!BLUETOOTH_MANAGER.initialize()) {
|
||||
// Failed, revert both to OFF
|
||||
SETTINGS.bluetoothEnabled = CrossPointSettings::BLUETOOTH_MODE::OFF;
|
||||
SETTINGS.*(setting.valuePtr) = CrossPointSettings::BLUETOOTH_KEYBOARD_MODE::DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize keyboard handler if not already done
|
||||
auto* keyboardHandler = BLUETOOTH_MANAGER.getKeyboardHandler();
|
||||
if (!keyboardHandler && BLUETOOTH_MANAGER.isInitialized()) {
|
||||
// This will be handled by BluetoothManager on next init
|
||||
BLUETOOTH_MANAGER.shutdown();
|
||||
BLUETOOTH_MANAGER.initialize();
|
||||
}
|
||||
} else {
|
||||
// Disable keyboard (but keep Bluetooth on)
|
||||
auto* keyboardHandler = BLUETOOTH_MANAGER.getKeyboardHandler();
|
||||
if (keyboardHandler) {
|
||||
keyboardHandler->shutdown();
|
||||
// Would need to reinit without keyboard to clean up properly
|
||||
BLUETOOTH_MANAGER.shutdown();
|
||||
BLUETOOTH_MANAGER.initialize();
|
||||
}
|
||||
|
||||
// Force garbage collection to free keyboard memory
|
||||
BLUETOOTH_MANAGER.collectGarbage();
|
||||
}
|
||||
}
|
||||
} else if (setting.type == SettingType::VALUE && setting.valuePtr != nullptr) {
|
||||
// Decreasing would also be nice for large ranges I think but oh well can't have everything
|
||||
|
||||
@ -384,6 +384,14 @@ void loop() {
|
||||
}
|
||||
}
|
||||
|
||||
// Update keyboard handler if enabled
|
||||
if (BLUETOOTH_MANAGER.isInitialized()) {
|
||||
auto* keyboardHandler = BLUETOOTH_MANAGER.getKeyboardHandler();
|
||||
if (keyboardHandler) {
|
||||
keyboardHandler->update();
|
||||
}
|
||||
}
|
||||
|
||||
// Add delay at the end of the loop to prevent tight spinning
|
||||
// When an activity requests skip loop delay (e.g., webserver running), use yield() for faster response
|
||||
// Otherwise, use longer delay to save power
|
||||
|
||||
Loading…
Reference in New Issue
Block a user