--- /dev/null
+#include "BLESerial.h"
+
+uint32_t bt_passkey_callback();
+void bt_passkey_notify_callback(uint32_t passkey);
+bool bt_security_request_callback();
+void bt_authentication_complete_callback(esp_ble_auth_cmpl_t auth_result);
+bool bt_confirm_pin_callback(uint32_t pin);
+void bt_connect_callback(uint16_t conn_handle);
+void bt_disconnect_callback(uint16_t conn_handle, uint8_t reason);
+
+uint32_t BLESerial::onPassKeyRequest() { return bt_passkey_callback(); }
+void BLESerial::onPassKeyNotify(uint32_t passkey) { bt_passkey_notify_callback(passkey); }
+bool BLESerial::onSecurityRequest() { return bt_security_request_callback(); }
+void BLESerial::onAuthenticationComplete(esp_ble_auth_cmpl_t auth_result) { bt_authentication_complete_callback(auth_result); }
+void BLESerial::onConnect(BLEServer *server) { bt_connect_callback(0); }
+void BLESerial::onDisconnect(BLEServer *server) { bt_disconnect_callback(0, 0); ble_server->startAdvertising(); }
+bool BLESerial::onConfirmPIN(uint32_t pin) { return false; };
+bool BLESerial::connected() { return ble_server->getConnectedCount() > 0; }
+
+int BLESerial::read() {
+ int result = this->rx_buffer.pop();
+ if (result == '\n') { this->numAvailableLines--; }
+ return result;
+}
+
+size_t BLESerial::readBytes(uint8_t *buffer, size_t bufferSize) {
+ int i = 0;
+ while (i < bufferSize && available()) { buffer[i] = (uint8_t)this->rx_buffer.pop(); i++; }
+ return i;
+}
+
+int BLESerial::peek() {
+ if (this->rx_buffer.getLength() == 0) return -1;
+ return this->rx_buffer.get(0);
+}
+
+int BLESerial::available() { return this->rx_buffer.getLength(); }
+
+size_t BLESerial::print(const char *str) {
+ if (ble_server->getConnectedCount() <= 0) return 0;
+ size_t written = 0; for (size_t i = 0; str[i] != '\0'; i++) { written += this->write(str[i]); }
+ flush();
+
+ return written;
+}
+
+size_t BLESerial::write(const uint8_t *buffer, size_t bufferSize) {
+ if (ble_server->getConnectedCount() <= 0) { return 0; } else {
+ size_t written = 0; for (int i = 0; i < bufferSize; i++) { written += this->write(buffer[i]); }
+ flush();
+
+ return written;
+ }
+}
+
+size_t BLESerial::write(uint8_t byte) {
+ if (ble_server->getConnectedCount() <= 0) { return 0; } else {
+ this->transmitBuffer[this->transmitBufferLength] = byte;
+ this->transmitBufferLength++;
+ if (this->transmitBufferLength == maxTransferSize) { flush(); }
+ return 1;
+ }
+}
+
+void BLESerial::flush() {
+ if (this->transmitBufferLength > 0) {
+ TxCharacteristic->setValue(this->transmitBuffer, this->transmitBufferLength);
+ this->transmitBufferLength = 0;
+ this->lastFlushTime = millis();
+ TxCharacteristic->notify(true);
+ }
+}
+
+void BLESerial::begin(const char *name) {
+ ConnectedDeviceCount = 0;
+ BLEDevice::init(name);
+
+ esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, ESP_PWR_LVL_P9);
+ esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_P9);
+ esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_SCAN ,ESP_PWR_LVL_P9);
+
+ ble_server = BLEDevice::createServer();
+ ble_server->setCallbacks(this);
+ BLEDevice::setSecurityCallbacks(this);
+
+ SetupSerialService();
+
+ ble_adv = BLEDevice::getAdvertising();
+ ble_adv->addServiceUUID(BLE_SERIAL_SERVICE_UUID);
+ ble_adv->setMinPreferred(0x20);
+ ble_adv->setMaxPreferred(0x40);
+ ble_adv->setScanResponse(true);
+ ble_adv->start();
+}
+
+void BLESerial::end() { BLEDevice::deinit(); }
+
+void BLESerial::onWrite(BLECharacteristic *characteristic) {
+ if (characteristic->getUUID().toString() == BLE_RX_UUID) {
+ auto value = characteristic->getValue();
+ for (int i = 0; i < value.length(); i++) { rx_buffer.push(value[i]); }
+ }
+}
+
+void BLESerial::SetupSerialService() {
+ SerialService = ble_server->createService(BLE_SERIAL_SERVICE_UUID);
+
+ RxCharacteristic = SerialService->createCharacteristic(BLE_RX_UUID, BLECharacteristic::PROPERTY_WRITE);
+ RxCharacteristic->setAccessPermissions(ESP_GATT_PERM_WRITE_ENC_MITM);
+ RxCharacteristic->addDescriptor(new BLE2902());
+ RxCharacteristic->setWriteProperty(true);
+ RxCharacteristic->setCallbacks(this);
+
+ TxCharacteristic = SerialService->createCharacteristic(BLE_TX_UUID, BLECharacteristic::PROPERTY_NOTIFY);
+ TxCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM);
+ TxCharacteristic->addDescriptor(new BLE2902());
+ TxCharacteristic->setReadProperty(true);
+
+ SerialService->start();
+}
+
+BLESerial::BLESerial() { }
--- /dev/null
+#pragma once
+#include <Arduino.h>
+
+#include <BLEDevice.h>
+#include <BLEUtils.h>
+#include <BLEServer.h>
+#include <BLE2902.h>
+
+template <size_t n>
+class BLEFIFO {
+private:
+ uint8_t buffer[n];
+ int head = 0;
+ int tail = 0;
+
+public:
+ void push(uint8_t value) {
+ buffer[head] = value;
+ head = (head + 1) % n;
+ if (head == tail) { tail = (tail + 1) % n; }
+ }
+
+ int pop() {
+ if (head == tail) {
+ return -1;
+ } else {
+ uint8_t value = buffer[tail];
+ tail = (tail + 1) % n;
+ return value;
+ }
+ }
+
+ void clear() { head = 0; tail = 0; }
+
+ int get(size_t index) {
+ if (index >= this->getLength()) {
+ return -1;
+ } else {
+ return buffer[(tail + index) % n];
+ }
+ }
+
+ size_t getLength() {
+ if (head >= tail) {
+ return head - tail;
+ } else {
+ return n - tail + head;
+ }
+ }
+};
+
+#define RX_BUFFER_SIZE 6144
+#define BLE_BUFFER_SIZE 512 // Must fit in max GATT attribute length
+#define MIN_MTU 50
+
+class BLESerial : public BLECharacteristicCallbacks, public BLEServerCallbacks, public BLESecurityCallbacks, public Stream {
+public:
+ BLESerial();
+
+ void begin(const char *name);
+ void end();
+ void onWrite(BLECharacteristic *characteristic);
+ int available();
+ int peek();
+ int read();
+ size_t readBytes(uint8_t *buffer, size_t bufferSize);
+ size_t write(uint8_t byte);
+ size_t write(const uint8_t *buffer, size_t bufferSize);
+ size_t print(const char *value);
+ void flush();
+ void onConnect(BLEServer *server);
+ void onDisconnect(BLEServer *server);
+
+ uint32_t onPassKeyRequest();
+ void onPassKeyNotify(uint32_t passkey);
+ bool onSecurityRequest();
+ void onAuthenticationComplete(esp_ble_auth_cmpl_t);
+ bool onConfirmPIN(uint32_t pin);
+
+ bool connected();
+
+ BLEServer *ble_server;
+ BLEAdvertising *ble_adv;
+ BLEService *SerialService;
+ BLECharacteristic *TxCharacteristic;
+ BLECharacteristic *RxCharacteristic;
+ size_t transmitBufferLength;
+ unsigned long long lastFlushTime;
+
+private:
+ BLESerial(BLESerial const &other) = delete;
+ void operator=(BLESerial const &other) = delete;
+
+ BLEFIFO<RX_BUFFER_SIZE> rx_buffer;
+ size_t numAvailableLines;
+ uint8_t transmitBuffer[BLE_BUFFER_SIZE];
+
+ int ConnectedDeviceCount;
+ void SetupSerialService();
+
+ uint16_t peerMTU;
+ uint16_t maxTransferSize = BLE_BUFFER_SIZE;
+
+ bool checkMTU();
+
+ const char *BLE_SERIAL_SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
+ const char *BLE_RX_UUID = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";
+ const char *BLE_TX_UUID = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
+
+ bool started = false;
+};