Xteink-X4-crosspoint-reader/src/activities/bluetooth/BluetoothActivity.h
2026-01-21 21:32:42 -05:00

126 lines
3.4 KiB
C++

#pragma once
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <freertos/task.h>
#include <NimBLEDevice.h>
#include <NimBLEUtils.h>
#include <NimBLEServer.h>
#include <functional>
#include <SDCardManager.h>
#include "../Activity.h"
typedef struct __attribute__((packed)) {
uint32_t version;
uint32_t bodyLength;
uint32_t nameLength;
char name[];
} lfbt_msg_client_offer; // msg type 0
typedef struct __attribute__((packed)) {
uint32_t status;
} lfbt_msg_server_response; // msg type 1
typedef struct __attribute__((packed)) {
uint32_t offset;
char body[];
} lfbt_msg_client_chunk; // msg type 2
typedef union {
lfbt_msg_client_offer clientOffer;
lfbt_msg_server_response serverResponse;
lfbt_msg_client_chunk clientChunk;
} lfbt_message_body;
typedef struct __attribute__((packed)) {
uint32_t type;
uint32_t txnId;
lfbt_message_body body;
} lfbt_message;
/**
* BluetoothActivity receives files over a custom BLE protocol and stores them on the SD card.
*
* The onCancel callback is called if the user presses back.
* onFileReceived is called when a file is successfully received with the path to the file.
*/
class BluetoothActivity final : public Activity {
TaskHandle_t displayTaskHandle = nullptr;
SemaphoreHandle_t renderingMutex = nullptr;
bool updateRequired = false;
const std::function<void()> onCancel;
const std::function<void(const std::string&)> onFileReceived;
static void displayTaskTrampoline(void* param);
[[noreturn]] void displayTaskLoop();
void render() const;
static void reportTaskTrampoline(void* param);
void report();
void onConnected(bool isConnected);
void onRequest(lfbt_message *msg, size_t msg_len);
class ServerCallbacks : public NimBLEServerCallbacks {
friend class BluetoothActivity;
BluetoothActivity *activity;
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo);
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason);
protected:
explicit ServerCallbacks(BluetoothActivity *activity) : activity(activity) {}
};
ServerCallbacks serverCallbacks;
class RequestCallbacks : public NimBLECharacteristicCallbacks {
friend class BluetoothActivity;
BluetoothActivity *activity;
void onWrite(NimBLECharacteristic* pCharacteristic, NimBLEConnInfo& connInfo);
protected:
explicit RequestCallbacks(BluetoothActivity *activity) : activity(activity) {}
};
RequestCallbacks requestCallbacks;
NimBLECharacteristic *pResponseChar = nullptr;
void startAdvertising();
void stopAdvertising();
typedef enum {
STATE_INITIALIZING,
STATE_WAITING,
STATE_CONNECTED,
STATE_OFFERED,
STATE_RECEIVING,
STATE_DONE,
STATE_ERROR
} State;
State state = STATE_INITIALIZING;
std::string filename;
FsFile file;
size_t receivedBytes = 0;
size_t totalBytes = 0;
char errorMessage[256] = {};
uint32_t txnId = 0;
void intoState(State newState);
public:
explicit BluetoothActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
const std::function<void()>& onCancel,
const std::function<void(const std::string&)>& onFileReceived)
: Activity("Bluetooth", renderer, mappedInput), onCancel(onCancel), onFileReceived(onFileReceived),
serverCallbacks(this), requestCallbacks(this) {}
void onEnter() override;
void onExit() override;
void loop() override;
};