Files
EggDuino/src/BLE_Interface.cpp
André Fiedler 83a55fcc47 Add print log functionality and device name handling
- Introduced functions to capture and manage incoming print logs.
- Added a download link for the incoming print log in the web interface.
- Updated device name prefix for BLE and added device name building logic.
- Enhanced SerialCommand class with line handler support.
- Implemented WiFi reconnect logic and status tracking.
2026-02-28 22:36:12 +01:00

232 lines
6.0 KiB
C++

#include "EggDuino.h"
#ifdef ESP32
#include <NimBLEDevice.h>
#include <string.h>
namespace
{
constexpr char kBleDeviceNamePrefix[] = "EggBot_";
constexpr char kBleServiceUuid[] = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
constexpr char kBleRxCharUuid[] = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";
constexpr char kBleTxCharUuid[] = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
constexpr size_t kBleRxQueueSize = 512;
constexpr size_t kBleNotifyChunkSize = 20;
NimBLEServer *g_pBleServer = NULL;
NimBLECharacteristic *g_pBleTxCharacteristic = NULL;
uint8_t g_bleRxQueue[kBleRxQueueSize];
size_t g_bleRxHead = 0;
size_t g_bleRxTail = 0;
bool g_bleRxQueueOverflow = false;
bool g_bleClientConnected = false;
portMUX_TYPE g_bleQueueMux = portMUX_INITIALIZER_UNLOCKED;
void logBleDiag(const String &message)
{
Log(message);
}
bool queueBleByte(uint8_t value)
{
bool queued = false;
portENTER_CRITICAL(&g_bleQueueMux);
const size_t nextHead = (g_bleRxHead + 1) % kBleRxQueueSize;
if (nextHead == g_bleRxTail)
{
g_bleRxQueueOverflow = true;
}
else
{
g_bleRxQueue[g_bleRxHead] = value;
g_bleRxHead = nextHead;
queued = true;
}
portEXIT_CRITICAL(&g_bleQueueMux);
return queued;
}
bool dequeueBleByte(uint8_t *value)
{
bool hasData = false;
portENTER_CRITICAL(&g_bleQueueMux);
if (g_bleRxHead != g_bleRxTail)
{
*value = g_bleRxQueue[g_bleRxTail];
g_bleRxTail = (g_bleRxTail + 1) % kBleRxQueueSize;
hasData = true;
}
portEXIT_CRITICAL(&g_bleQueueMux);
return hasData;
}
class EggDuinoBleServerCallbacks : public NimBLEServerCallbacks
{
void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo)
{
(void)pServer;
(void)connInfo;
g_bleClientConnected = true;
Log("BLE client connected");
}
void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason)
{
(void)connInfo;
(void)reason;
g_bleClientConnected = false;
const bool restartedAdvertising = pServer->startAdvertising();
logBleDiag(String("BLE client disconnected; advertising restart: ") + (restartedAdvertising ? "ok" : "failed"));
}
};
class EggDuinoBleRxCallbacks : public NimBLECharacteristicCallbacks
{
void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo)
{
(void)connInfo;
const std::string value = pCharacteristic->getValue();
for (size_t i = 0; i < value.size(); ++i)
{
queueBleByte(static_cast<uint8_t>(value[i]));
}
}
};
} // namespace
void startBleInterface()
{
char bleDeviceName[32] = {0};
buildDeviceName(bleDeviceName, sizeof(bleDeviceName));
if (bleDeviceName[0] == '\0')
{
snprintf(bleDeviceName, sizeof(bleDeviceName), "%sUNKNOWN", kBleDeviceNamePrefix);
}
logBleDiag("BLE init begin");
logBleDiag(String("BLE device name: ") + bleDeviceName);
logBleDiag(String("BLE service UUID: ") + kBleServiceUuid);
logBleDiag(String("BLE RX UUID: ") + kBleRxCharUuid);
logBleDiag(String("BLE TX UUID: ") + kBleTxCharUuid);
NimBLEDevice::init(bleDeviceName);
const bool blePowerSet = NimBLEDevice::setPower(ESP_PWR_LVL_P6);
const std::string bleAddress = NimBLEDevice::getAddress().toString();
logBleDiag(String("BLE radio address: ") + bleAddress.c_str());
logBleDiag(String("BLE TX power set: ") + (blePowerSet ? "ok" : "failed"));
g_pBleServer = NimBLEDevice::createServer();
if (g_pBleServer == NULL)
{
logBleDiag("BLE init failed: createServer returned null");
return;
}
g_pBleServer->setCallbacks(new EggDuinoBleServerCallbacks());
logBleDiag("BLE server created");
NimBLEService *pService = g_pBleServer->createService(kBleServiceUuid);
if (pService == NULL)
{
logBleDiag("BLE init failed: createService returned null");
return;
}
g_pBleTxCharacteristic = pService->createCharacteristic(
kBleTxCharUuid,
NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ);
if (g_pBleTxCharacteristic == NULL)
{
logBleDiag("BLE init failed: TX characteristic creation failed");
return;
}
NimBLECharacteristic *pRxCharacteristic = pService->createCharacteristic(
kBleRxCharUuid,
NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_NR);
if (pRxCharacteristic == NULL)
{
logBleDiag("BLE init failed: RX characteristic creation failed");
return;
}
pRxCharacteristic->setCallbacks(new EggDuinoBleRxCallbacks());
logBleDiag("BLE characteristics created");
const bool serviceStarted = pService->start();
logBleDiag(String("BLE service start: ") + (serviceStarted ? "ok" : "failed"));
if (!serviceStarted)
{
return;
}
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
if (pAdvertising == NULL)
{
logBleDiag("BLE init failed: getAdvertising returned null");
return;
}
pAdvertising->enableScanResponse(true);
const bool localNameSet = pAdvertising->setName(bleDeviceName);
const bool serviceUuidAdded = pAdvertising->addServiceUUID(kBleServiceUuid);
const bool advertisingStarted = pAdvertising->start();
logBleDiag(String("BLE advertising set local name: ") + (localNameSet ? "ok" : "failed"));
logBleDiag(String("BLE advertising add service UUID: ") + (serviceUuidAdded ? "ok" : "failed"));
logBleDiag(String("BLE advertising start: ") + (advertisingStarted ? "ok" : "failed"));
if (serviceUuidAdded && advertisingStarted)
{
logBleDiag("BLE service started");
}
}
void handleBleInterface()
{
if (g_bleRxQueueOverflow)
{
g_bleRxQueueOverflow = false;
Log("BLE RX queue overflow");
}
uint8_t value = 0;
while (dequeueBleByte(&value))
{
setActiveProtocolContext(&g_BLECmd, PROTOCOL_TRANSPORT_BLE);
g_BLECmd.readChar(static_cast<char>(value));
}
}
bool bleProtocolWrite(const char *message)
{
if ((message == NULL) || !g_bleClientConnected || (g_pBleTxCharacteristic == NULL))
{
return false;
}
const uint8_t *payload = reinterpret_cast<const uint8_t *>(message);
size_t remaining = strlen(message);
if (remaining == 0)
{
return true;
}
while (remaining > 0)
{
size_t chunkLen = remaining;
if (chunkLen > kBleNotifyChunkSize)
{
chunkLen = kBleNotifyChunkSize;
}
g_pBleTxCharacteristic->setValue(payload, chunkLen);
g_pBleTxCharacteristic->notify();
payload += chunkLen;
remaining -= chunkLen;
delay(3);
}
return true;
}
#endif