mirror of
https://github.com/daveallie/crosspoint-reader.git
synced 2026-02-06 15:47:39 +03:00
Refactor image rendering and add Bluetooth setting
- Added logic to render images only in BW mode in Page.cpp. - Implemented getRenderMode() in GfxRenderer.h. - Increased SETTINGS_COUNT and added bluetoothEnabled field in CrossPointSettings. - Updated saveToFile and loadFromFile methods to handle the new Bluetooth setting. - Added Bluetooth toggle in SettingsActivity.
This commit is contained in:
parent
9db4ef6f4b
commit
1f2380be56
@ -29,8 +29,13 @@ std::unique_ptr<PageLine> PageLine::deserialize(FsFile& file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PageImage::render(GfxRenderer& renderer, const int fontId, const int xOffset, const int yOffset) {
|
void PageImage::render(GfxRenderer& renderer, const int fontId, const int xOffset, const int yOffset) {
|
||||||
|
// Only render images in BW mode, skip grayscale passes to keep images sharp
|
||||||
|
if (renderer.getRenderMode() != GfxRenderer::BW) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
FsFile imageFile;
|
FsFile imageFile;
|
||||||
if (!SdMan.openFileForRead("PGI", cachePath, imageFile)) {
|
if (!SdMan.openFileForRead("PGI", cachePath.c_str(), imageFile)) {
|
||||||
Serial.printf("[%lu] [PGI] Failed to open image: %s\n", millis(), cachePath.c_str());
|
Serial.printf("[%lu] [PGI] Failed to open image: %s\n", millis(), cachePath.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -83,6 +83,7 @@ class GfxRenderer {
|
|||||||
|
|
||||||
// Grayscale functions
|
// Grayscale functions
|
||||||
void setRenderMode(const RenderMode mode) { this->renderMode = mode; }
|
void setRenderMode(const RenderMode mode) { this->renderMode = mode; }
|
||||||
|
RenderMode getRenderMode() const { return renderMode; }
|
||||||
void copyGrayscaleLsbBuffers() const;
|
void copyGrayscaleLsbBuffers() const;
|
||||||
void copyGrayscaleMsbBuffers() const;
|
void copyGrayscaleMsbBuffers() const;
|
||||||
void displayGrayBuffer() const;
|
void displayGrayBuffer() const;
|
||||||
|
|||||||
@ -12,7 +12,7 @@ CrossPointSettings CrossPointSettings::instance;
|
|||||||
namespace {
|
namespace {
|
||||||
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
|
constexpr uint8_t SETTINGS_FILE_VERSION = 1;
|
||||||
// Increment this when adding new persisted settings fields
|
// Increment this when adding new persisted settings fields
|
||||||
constexpr uint8_t SETTINGS_COUNT = 10;
|
constexpr uint8_t SETTINGS_COUNT = 11;
|
||||||
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
|
constexpr char SETTINGS_FILE[] = "/.crosspoint/settings.bin";
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -37,6 +37,7 @@ bool CrossPointSettings::saveToFile() const {
|
|||||||
serialization::writePod(outputFile, fontFamily);
|
serialization::writePod(outputFile, fontFamily);
|
||||||
serialization::writePod(outputFile, fontSize);
|
serialization::writePod(outputFile, fontSize);
|
||||||
serialization::writePod(outputFile, lineSpacing);
|
serialization::writePod(outputFile, lineSpacing);
|
||||||
|
serialization::writePod(outputFile, bluetoothEnabled);
|
||||||
outputFile.close();
|
outputFile.close();
|
||||||
|
|
||||||
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
|
Serial.printf("[%lu] [CPS] Settings saved to file\n", millis());
|
||||||
@ -83,6 +84,8 @@ bool CrossPointSettings::loadFromFile() {
|
|||||||
if (++settingsRead >= fileSettingsCount) break;
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
serialization::readPod(inputFile, lineSpacing);
|
serialization::readPod(inputFile, lineSpacing);
|
||||||
if (++settingsRead >= fileSettingsCount) break;
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
|
serialization::readPod(inputFile, bluetoothEnabled);
|
||||||
|
if (++settingsRead >= fileSettingsCount) break;
|
||||||
} while (false);
|
} while (false);
|
||||||
|
|
||||||
inputFile.close();
|
inputFile.close();
|
||||||
|
|||||||
@ -62,6 +62,8 @@ class CrossPointSettings {
|
|||||||
uint8_t fontFamily = BOOKERLY;
|
uint8_t fontFamily = BOOKERLY;
|
||||||
uint8_t fontSize = MEDIUM;
|
uint8_t fontSize = MEDIUM;
|
||||||
uint8_t lineSpacing = NORMAL;
|
uint8_t lineSpacing = NORMAL;
|
||||||
|
// Bluetooth settings
|
||||||
|
uint8_t bluetoothEnabled = 0;
|
||||||
|
|
||||||
~CrossPointSettings() = default;
|
~CrossPointSettings() = default;
|
||||||
|
|
||||||
|
|||||||
@ -8,9 +8,11 @@
|
|||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "CrossPointSettings.h"
|
||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "NetworkModeSelectionActivity.h"
|
#include "NetworkModeSelectionActivity.h"
|
||||||
#include "WifiSelectionActivity.h"
|
#include "WifiSelectionActivity.h"
|
||||||
|
#include "activities/util/FullScreenMessageActivity.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -128,6 +130,15 @@ void CrossPointWebServerActivity::onNetworkModeSelected(const NetworkMode mode)
|
|||||||
Serial.printf("[%lu] [WEBACT] Network mode selected: %s\n", millis(),
|
Serial.printf("[%lu] [WEBACT] Network mode selected: %s\n", millis(),
|
||||||
mode == NetworkMode::JOIN_NETWORK ? "Join Network" : "Create Hotspot");
|
mode == NetworkMode::JOIN_NETWORK ? "Join Network" : "Create Hotspot");
|
||||||
|
|
||||||
|
// Check for WiFi/BLE mutual exclusion
|
||||||
|
if (SETTINGS.bluetoothEnabled) {
|
||||||
|
Serial.printf("[%lu] [WEBACT] ERROR: Cannot start WiFi while Bluetooth is enabled\n", millis());
|
||||||
|
exitActivity();
|
||||||
|
enterNewActivity(new FullScreenMessageActivity(
|
||||||
|
renderer, mappedInput, "Disable Bluetooth first\n\nGo to Settings > Bluetooth"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
networkMode = mode;
|
networkMode = mode;
|
||||||
isApMode = (mode == NetworkMode::CREATE_HOTSPOT);
|
isApMode = (mode == NetworkMode::CREATE_HOTSPOT);
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
// Define the static settings list
|
// Define the static settings list
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int settingsCount = 11;
|
constexpr int settingsCount = 12;
|
||||||
const SettingInfo settingsList[settingsCount] = {
|
const SettingInfo settingsList[settingsCount] = {
|
||||||
// Should match with SLEEP_SCREEN_MODE
|
// Should match with SLEEP_SCREEN_MODE
|
||||||
{"Sleep Screen", SettingType::ENUM, &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover"}},
|
{"Sleep Screen", SettingType::ENUM, &CrossPointSettings::sleepScreen, {"Dark", "Light", "Custom", "Cover"}},
|
||||||
@ -34,6 +34,7 @@ const SettingInfo settingsList[settingsCount] = {
|
|||||||
{"Bookerly", "Noto Sans", "Open Dyslexic"}},
|
{"Bookerly", "Noto Sans", "Open Dyslexic"}},
|
||||||
{"Reader Font Size", SettingType::ENUM, &CrossPointSettings::fontSize, {"Small", "Medium", "Large", "X Large"}},
|
{"Reader Font Size", SettingType::ENUM, &CrossPointSettings::fontSize, {"Small", "Medium", "Large", "X Large"}},
|
||||||
{"Reader Line Spacing", SettingType::ENUM, &CrossPointSettings::lineSpacing, {"Tight", "Normal", "Wide"}},
|
{"Reader Line Spacing", SettingType::ENUM, &CrossPointSettings::lineSpacing, {"Tight", "Normal", "Wide"}},
|
||||||
|
{"Bluetooth", SettingType::TOGGLE, &CrossPointSettings::bluetoothEnabled, {}},
|
||||||
{"Check for updates", SettingType::ACTION, nullptr, {}},
|
{"Check for updates", SettingType::ACTION, nullptr, {}},
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
188
src/bluetooth/BleFileTransfer.cpp
Normal file
188
src/bluetooth/BleFileTransfer.cpp
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
#include "BleFileTransfer.h"
|
||||||
|
|
||||||
|
#include <HardwareSerial.h>
|
||||||
|
#include <SDCardManager.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// BLE Service UUIDs (custom UUIDs for CrossPoint file transfer)
|
||||||
|
constexpr const char* SERVICE_UUID = "4fafc201-1fb5-459e-8fcc-c5c9c331914b";
|
||||||
|
constexpr const char* FILE_LIST_UUID = "beb5483e-36e1-4688-b7f5-ea07361b26a8";
|
||||||
|
constexpr const char* FILE_DATA_UUID = "1c95d5e3-d8f7-413a-bf3d-7a2e5d7be87e";
|
||||||
|
constexpr const char* CONTROL_UUID = "d7e72d4c-3f8e-4b4a-9c5d-8e3f7a2b1c9d";
|
||||||
|
|
||||||
|
constexpr int BLE_MTU = 512; // BLE Maximum Transmission Unit
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
BleFileTransfer::BleFileTransfer()
|
||||||
|
: running(false), pServer(nullptr), pFileService(nullptr), pFileListChar(nullptr), pFileDataChar(nullptr),
|
||||||
|
pControlChar(nullptr) {}
|
||||||
|
|
||||||
|
BleFileTransfer::~BleFileTransfer() {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BleFileTransfer::begin(const std::string& deviceName) {
|
||||||
|
if (running) {
|
||||||
|
Serial.printf("[%lu] [BLE] Already running\n", millis());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.printf("[%lu] [BLE] Starting BLE service...\n", millis());
|
||||||
|
Serial.printf("[%lu] [BLE] [MEM] Free heap before init: %d bytes\n", millis(), ESP.getFreeHeap());
|
||||||
|
|
||||||
|
// Initialize BLE
|
||||||
|
BLEDevice::init(deviceName);
|
||||||
|
|
||||||
|
// Set MTU size for larger transfers
|
||||||
|
BLEDevice::setMTU(BLE_MTU);
|
||||||
|
|
||||||
|
// Create BLE Server
|
||||||
|
pServer = BLEDevice::createServer();
|
||||||
|
if (!pServer) {
|
||||||
|
Serial.printf("[%lu] [BLE] ERROR: Failed to create server\n", millis());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
serverCallbacks.reset(new ServerCallbacks(this));
|
||||||
|
pServer->setCallbacks(serverCallbacks.get());
|
||||||
|
|
||||||
|
// Create File Transfer Service
|
||||||
|
pFileService = pServer->createService(SERVICE_UUID);
|
||||||
|
if (!pFileService) {
|
||||||
|
Serial.printf("[%lu] [BLE] ERROR: Failed to create service\n", millis());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create File List Characteristic (READ)
|
||||||
|
pFileListChar = pFileService->createCharacteristic(FILE_LIST_UUID, BLECharacteristic::PROPERTY_READ);
|
||||||
|
fileListCallbacks.reset(new FileListCallbacks(this));
|
||||||
|
pFileListChar->setCallbacks(fileListCallbacks.get());
|
||||||
|
|
||||||
|
// Create File Data Characteristic (READ | WRITE | NOTIFY)
|
||||||
|
pFileDataChar =
|
||||||
|
pFileService->createCharacteristic(FILE_DATA_UUID, BLECharacteristic::PROPERTY_READ |
|
||||||
|
BLECharacteristic::PROPERTY_WRITE |
|
||||||
|
BLECharacteristic::PROPERTY_NOTIFY);
|
||||||
|
pFileDataChar->addDescriptor(new BLE2902());
|
||||||
|
|
||||||
|
// Create Control Characteristic (WRITE)
|
||||||
|
pControlChar = pFileService->createCharacteristic(CONTROL_UUID, BLECharacteristic::PROPERTY_WRITE);
|
||||||
|
controlCallbacks.reset(new ControlCallbacks(this));
|
||||||
|
pControlChar->setCallbacks(controlCallbacks.get());
|
||||||
|
|
||||||
|
// Start the service
|
||||||
|
pFileService->start();
|
||||||
|
|
||||||
|
// Start advertising
|
||||||
|
BLEAdvertising* pAdvertising = BLEDevice::getAdvertising();
|
||||||
|
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||||
|
pAdvertising->setScanResponse(true);
|
||||||
|
pAdvertising->setMinPreferred(0x06); // helps with iPhone connections
|
||||||
|
pAdvertising->setMinPreferred(0x12);
|
||||||
|
BLEDevice::startAdvertising();
|
||||||
|
|
||||||
|
running = true;
|
||||||
|
Serial.printf("[%lu] [BLE] Service started successfully\n", millis());
|
||||||
|
Serial.printf("[%lu] [BLE] Device name: %s\n", millis(), deviceName.c_str());
|
||||||
|
Serial.printf("[%lu] [BLE] [MEM] Free heap after init: %d bytes\n", millis(), ESP.getFreeHeap());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BleFileTransfer::stop() {
|
||||||
|
if (!running) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.printf("[%lu] [BLE] Stopping BLE service...\n", millis());
|
||||||
|
|
||||||
|
running = false;
|
||||||
|
|
||||||
|
// Stop advertising
|
||||||
|
BLEDevice::getAdvertising()->stop();
|
||||||
|
|
||||||
|
// Clean up characteristics
|
||||||
|
pFileListChar = nullptr;
|
||||||
|
pFileDataChar = nullptr;
|
||||||
|
pControlChar = nullptr;
|
||||||
|
|
||||||
|
// Clean up service
|
||||||
|
pFileService = nullptr;
|
||||||
|
|
||||||
|
// Clean up server
|
||||||
|
pServer = nullptr;
|
||||||
|
|
||||||
|
// Clean up callbacks
|
||||||
|
serverCallbacks.reset();
|
||||||
|
controlCallbacks.reset();
|
||||||
|
fileListCallbacks.reset();
|
||||||
|
|
||||||
|
// Deinitialize BLE
|
||||||
|
BLEDevice::deinit(true);
|
||||||
|
|
||||||
|
Serial.printf("[%lu] [BLE] Service stopped\n", millis());
|
||||||
|
Serial.printf("[%lu] [BLE] [MEM] Free heap after cleanup: %d bytes\n", millis(), ESP.getFreeHeap());
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t BleFileTransfer::getConnectedCount() const {
|
||||||
|
if (pServer) {
|
||||||
|
return pServer->getConnectedCount();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BleFileTransfer::getFileList() {
|
||||||
|
// Return a simple JSON-like list of files in the root directory
|
||||||
|
// Format: "file1.epub,file2.epub,file3.epub"
|
||||||
|
// For a full implementation, this would traverse SD card directories
|
||||||
|
|
||||||
|
// Placeholder implementation - would need to integrate with SDCardManager
|
||||||
|
return "example1.epub,example2.epub,example3.epub";
|
||||||
|
}
|
||||||
|
|
||||||
|
void BleFileTransfer::handleControlCommand(const std::string& command) {
|
||||||
|
Serial.printf("[%lu] [BLE] Control command: %s\n", millis(), command.c_str());
|
||||||
|
|
||||||
|
// Parse and handle commands
|
||||||
|
// Commands could be: "LIST", "GET:filename", "PUT:filename", "DELETE:filename", etc.
|
||||||
|
// For a full implementation, this would handle file operations via SDCardManager
|
||||||
|
|
||||||
|
if (command == "LIST") {
|
||||||
|
// Refresh file list
|
||||||
|
Serial.printf("[%lu] [BLE] Refreshing file list\n", millis());
|
||||||
|
} else if (command.rfind("GET:", 0) == 0) {
|
||||||
|
std::string filename = command.substr(4);
|
||||||
|
Serial.printf("[%lu] [BLE] Request to download: %s\n", millis(), filename.c_str());
|
||||||
|
// Would implement file read and send via pFileDataChar notifications
|
||||||
|
} else if (command.rfind("PUT:", 0) == 0) {
|
||||||
|
std::string filename = command.substr(4);
|
||||||
|
Serial.printf("[%lu] [BLE] Request to upload: %s\n", millis(), filename.c_str());
|
||||||
|
// Would implement file write from pFileDataChar writes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server callbacks
|
||||||
|
void BleFileTransfer::ServerCallbacks::onConnect(BLEServer* pServer) {
|
||||||
|
Serial.printf("[%lu] [BLE] Client connected (total: %u)\n", millis(), pServer->getConnectedCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
void BleFileTransfer::ServerCallbacks::onDisconnect(BLEServer* pServer) {
|
||||||
|
Serial.printf("[%lu] [BLE] Client disconnected (total: %u)\n", millis(), pServer->getConnectedCount());
|
||||||
|
// Restart advertising to allow new connections
|
||||||
|
BLEDevice::startAdvertising();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Control callbacks
|
||||||
|
void BleFileTransfer::ControlCallbacks::onWrite(BLECharacteristic* pCharacteristic) {
|
||||||
|
std::string value = pCharacteristic->getValue();
|
||||||
|
if (value.length() > 0) {
|
||||||
|
parent->handleControlCommand(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// File list callbacks
|
||||||
|
void BleFileTransfer::FileListCallbacks::onRead(BLECharacteristic* pCharacteristic) {
|
||||||
|
std::string fileList = parent->getFileList();
|
||||||
|
pCharacteristic->setValue(fileList);
|
||||||
|
Serial.printf("[%lu] [BLE] File list requested (%zu bytes)\n", millis(), fileList.length());
|
||||||
|
}
|
||||||
77
src/bluetooth/BleFileTransfer.h
Normal file
77
src/bluetooth/BleFileTransfer.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <BLEDevice.h>
|
||||||
|
#include <BLEServer.h>
|
||||||
|
#include <BLEUtils.h>
|
||||||
|
#include <BLE2902.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// BLE File Transfer Service
|
||||||
|
// Provides file upload/download over Bluetooth Low Energy
|
||||||
|
// Designed for memory-constrained ESP32-C3 environment
|
||||||
|
class BleFileTransfer {
|
||||||
|
public:
|
||||||
|
BleFileTransfer();
|
||||||
|
~BleFileTransfer();
|
||||||
|
|
||||||
|
// Start the BLE service
|
||||||
|
bool begin(const std::string& deviceName = "CrossPoint-Reader");
|
||||||
|
|
||||||
|
// Stop the BLE service and free resources
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
// Check if service is running
|
||||||
|
bool isRunning() const { return running; }
|
||||||
|
|
||||||
|
// Get number of connected clients
|
||||||
|
uint32_t getConnectedCount() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool running;
|
||||||
|
BLEServer* pServer;
|
||||||
|
BLEService* pFileService;
|
||||||
|
BLECharacteristic* pFileListChar;
|
||||||
|
BLECharacteristic* pFileDataChar;
|
||||||
|
BLECharacteristic* pControlChar;
|
||||||
|
|
||||||
|
// Server callbacks
|
||||||
|
class ServerCallbacks : public BLEServerCallbacks {
|
||||||
|
public:
|
||||||
|
ServerCallbacks(BleFileTransfer* parent) : parent(parent) {}
|
||||||
|
void onConnect(BLEServer* pServer) override;
|
||||||
|
void onDisconnect(BLEServer* pServer) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
BleFileTransfer* parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Control characteristic callbacks
|
||||||
|
class ControlCallbacks : public BLECharacteristicCallbacks {
|
||||||
|
public:
|
||||||
|
ControlCallbacks(BleFileTransfer* parent) : parent(parent) {}
|
||||||
|
void onWrite(BLECharacteristic* pCharacteristic) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
BleFileTransfer* parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
// File list characteristic callbacks
|
||||||
|
class FileListCallbacks : public BLECharacteristicCallbacks {
|
||||||
|
public:
|
||||||
|
FileListCallbacks(BleFileTransfer* parent) : parent(parent) {}
|
||||||
|
void onRead(BLECharacteristic* pCharacteristic) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
BleFileTransfer* parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
void handleControlCommand(const std::string& command);
|
||||||
|
std::string getFileList();
|
||||||
|
|
||||||
|
std::unique_ptr<ServerCallbacks> serverCallbacks;
|
||||||
|
std::unique_ptr<ControlCallbacks> controlCallbacks;
|
||||||
|
std::unique_ptr<FileListCallbacks> fileListCallbacks;
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user