mirror of
https://github.com/h2zero/esp-nimble-cpp.git
synced 2026-07-08 09:21:04 +02:00
Add BLE stream classes.
Co-authored-by: doudar <17362216+doudar@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(NimBLE_Stream_Client)
|
||||
@@ -0,0 +1,53 @@
|
||||
# NimBLE Stream Client Example
|
||||
|
||||
This example demonstrates how to use the `NimBLEStreamClient` class to connect to a BLE GATT server and communicate using the familiar Arduino Stream interface.
|
||||
|
||||
## Features
|
||||
|
||||
- Uses Arduino Stream interface (print, println, read, available, etc.)
|
||||
- Automatic server discovery and connection
|
||||
- Bidirectional communication
|
||||
- Buffered TX/RX using ring buffers
|
||||
- Automatic reconnection on disconnect
|
||||
- Similar usage to Serial communication
|
||||
|
||||
## How it Works
|
||||
|
||||
1. Scans for BLE devices advertising the target service UUID
|
||||
2. Connects to the server and discovers the stream characteristic
|
||||
3. Initializes `NimBLEStreamClient` with the remote characteristic
|
||||
4. Subscribes to notifications to receive data in the RX buffer
|
||||
5. Uses familiar Stream methods like `print()`, `println()`, `read()`, and `available()`
|
||||
|
||||
## Usage
|
||||
|
||||
1. Build and flash the NimBLE_Stream_Server example to one ESP32 using ESP-IDF (`idf.py build flash monitor`)
|
||||
2. Build and flash this client example to another ESP32 using ESP-IDF
|
||||
3. The client will automatically:
|
||||
- Scan for the server
|
||||
- Connect when found
|
||||
- Set up the stream interface
|
||||
- Begin bidirectional communication
|
||||
4. Open `idf.py monitor` on each board to observe stream traffic
|
||||
|
||||
## Service UUIDs
|
||||
|
||||
Must match the server:
|
||||
- Service: `6E400001-B5A3-F393-E0A9-E50E24DCCA9E`
|
||||
- Characteristic: `6E400002-B5A3-F393-E0A9-E50E24DCCA9E`
|
||||
|
||||
## Monitor Output
|
||||
|
||||
The example displays:
|
||||
- Server discovery progress
|
||||
- Connection status
|
||||
- All data received from the server
|
||||
- Confirmation of data sent to the server
|
||||
|
||||
## Testing
|
||||
|
||||
Run with NimBLE_Stream_Server to see bidirectional communication:
|
||||
- Server sends periodic status messages
|
||||
- Client sends periodic uptime messages
|
||||
- Both echo data received from each other
|
||||
- You can send data from either `idf.py monitor` session
|
||||
@@ -0,0 +1,4 @@
|
||||
set(COMPONENT_SRCS "main.cpp")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
|
||||
register_component()
|
||||
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* NimBLE_Stream_Client Example:
|
||||
*
|
||||
* Demonstrates using NimBLEStreamClient to connect to a BLE GATT server
|
||||
* and communicate using the Stream-like interface.
|
||||
*
|
||||
* This example connects to the NimBLE_Stream_Server example.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include <NimBLEDevice.h>
|
||||
|
||||
// Service and Characteristic UUIDs (must match the server)
|
||||
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
#define CHARACTERISTIC_UUID "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
|
||||
// Create the stream client instance
|
||||
NimBLEStreamClient bleStream;
|
||||
|
||||
struct RxOverflowStats {
|
||||
uint32_t droppedOld{0};
|
||||
uint32_t droppedNew{0};
|
||||
};
|
||||
|
||||
RxOverflowStats g_rxOverflowStats;
|
||||
uint32_t scanTime = 5000; // Scan duration in milliseconds
|
||||
|
||||
NimBLEStream::RxOverflowAction onRxOverflow(const uint8_t* data, size_t len, void* userArg) {
|
||||
auto* stats = static_cast<RxOverflowStats*>(userArg);
|
||||
if (stats) {
|
||||
stats->droppedOld++;
|
||||
}
|
||||
|
||||
// For status/telemetry streams, prioritize newest packets.
|
||||
(void)data;
|
||||
(void)len;
|
||||
return NimBLEStream::DROP_OLDER_DATA;
|
||||
}
|
||||
|
||||
static uint64_t millis() {
|
||||
return esp_timer_get_time() / 1000ULL;
|
||||
}
|
||||
|
||||
// Connection state variables
|
||||
static bool doConnect = false;
|
||||
static bool connected = false;
|
||||
static const NimBLEAdvertisedDevice* pServerDevice = nullptr;
|
||||
static NimBLEClient* pClient = nullptr;
|
||||
|
||||
/** Scan callbacks to find the server */
|
||||
class ScanCallbacks : public NimBLEScanCallbacks {
|
||||
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) override {
|
||||
printf("Advertised Device: %s\n", advertisedDevice->toString().c_str());
|
||||
|
||||
// Check if this device advertises our service.
|
||||
if (advertisedDevice->isAdvertisingService(NimBLEUUID(SERVICE_UUID))) {
|
||||
printf("Found our stream server!\n");
|
||||
pServerDevice = advertisedDevice;
|
||||
NimBLEDevice::getScan()->stop();
|
||||
doConnect = true;
|
||||
}
|
||||
}
|
||||
|
||||
void onScanEnd(const NimBLEScanResults& results, int reason) override {
|
||||
(void)results;
|
||||
(void)reason;
|
||||
printf("Scan ended\n");
|
||||
if (!doConnect && !connected) {
|
||||
printf("Server not found, restarting scan...\n");
|
||||
NimBLEDevice::getScan()->start(scanTime, false, true);
|
||||
}
|
||||
}
|
||||
} scanCallbacks;
|
||||
|
||||
/** Client callbacks for connection/disconnection events */
|
||||
class ClientCallbacks : public NimBLEClientCallbacks {
|
||||
void onConnect(NimBLEClient* pClient) override {
|
||||
printf("Connected to server\n");
|
||||
// Update connection parameters for better throughput.
|
||||
pClient->updateConnParams(12, 24, 0, 200);
|
||||
}
|
||||
|
||||
void onDisconnect(NimBLEClient* pClient, int reason) override {
|
||||
(void)pClient;
|
||||
printf("Disconnected from server, reason: %d\n", reason);
|
||||
connected = false;
|
||||
bleStream.end();
|
||||
|
||||
// Restart scanning.
|
||||
printf("Restarting scan...\n");
|
||||
NimBLEDevice::getScan()->start(scanTime, false, true);
|
||||
}
|
||||
} clientCallbacks;
|
||||
|
||||
/** Connect to the BLE Server and set up the stream */
|
||||
bool connectToServer() {
|
||||
printf("Connecting to: %s\n", pServerDevice->getAddress().toString().c_str());
|
||||
|
||||
// Create or reuse a client.
|
||||
pClient = NimBLEDevice::getClientByPeerAddress(pServerDevice->getAddress());
|
||||
if (!pClient) {
|
||||
pClient = NimBLEDevice::createClient();
|
||||
if (!pClient) {
|
||||
printf("Failed to create client\n");
|
||||
return false;
|
||||
}
|
||||
pClient->setClientCallbacks(&clientCallbacks, false);
|
||||
pClient->setConnectionParams(12, 24, 0, 200);
|
||||
pClient->setConnectTimeout(5000);
|
||||
}
|
||||
|
||||
// Connect to the remote BLE Server.
|
||||
if (!pClient->connect(pServerDevice)) {
|
||||
printf("Failed to connect to server\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Connected! Discovering services...\n");
|
||||
|
||||
// Get the service and characteristic.
|
||||
NimBLERemoteService* pRemoteService = pClient->getService(SERVICE_UUID);
|
||||
if (!pRemoteService) {
|
||||
printf("Failed to find our service UUID\n");
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
printf("Found the stream service\n");
|
||||
|
||||
NimBLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(CHARACTERISTIC_UUID);
|
||||
if (!pRemoteCharacteristic) {
|
||||
printf("Failed to find our characteristic UUID\n");
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
printf("Found the stream characteristic\n");
|
||||
|
||||
// subscribeNotify=true means notifications are stored in the RX buffer.
|
||||
if (!bleStream.begin(pRemoteCharacteristic, true)) {
|
||||
printf("Failed to initialize BLE stream!\n");
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
bleStream.setRxOverflowCallback(onRxOverflow, &g_rxOverflowStats);
|
||||
|
||||
printf("BLE Stream initialized successfully!\n");
|
||||
connected = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" void app_main(void) {
|
||||
printf("Starting NimBLE Stream Client\n");
|
||||
|
||||
/** Initialize NimBLE */
|
||||
NimBLEDevice::init("NimBLE-StreamClient");
|
||||
|
||||
// Create the BLE scan instance and set callbacks.
|
||||
NimBLEScan* pScan = NimBLEDevice::getScan();
|
||||
pScan->setScanCallbacks(&scanCallbacks, false);
|
||||
pScan->setActiveScan(true);
|
||||
|
||||
// Start scanning for the server.
|
||||
printf("Scanning for BLE Stream Server...\n");
|
||||
pScan->start(scanTime, false, true);
|
||||
|
||||
uint32_t lastDroppedOld = 0;
|
||||
uint32_t lastDroppedNew = 0;
|
||||
uint64_t lastSend = 0;
|
||||
|
||||
for (;;) {
|
||||
if (g_rxOverflowStats.droppedOld != lastDroppedOld || g_rxOverflowStats.droppedNew != lastDroppedNew) {
|
||||
lastDroppedOld = g_rxOverflowStats.droppedOld;
|
||||
lastDroppedNew = g_rxOverflowStats.droppedNew;
|
||||
printf("RX overflow handled (drop-old=%" PRIu32 ", drop-new=%" PRIu32 ")\n", lastDroppedOld, lastDroppedNew);
|
||||
}
|
||||
|
||||
// If we found a server, try to connect.
|
||||
if (doConnect) {
|
||||
doConnect = false;
|
||||
if (connectToServer()) {
|
||||
printf("Stream ready for communication!\n");
|
||||
} else {
|
||||
printf("Failed to connect to server, restarting scan...\n");
|
||||
pServerDevice = nullptr;
|
||||
NimBLEDevice::getScan()->start(scanTime, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
// If connected, demonstrate stream communication.
|
||||
if (connected && bleStream) {
|
||||
if (bleStream.available()) {
|
||||
printf("Received from server: ");
|
||||
while (bleStream.available()) {
|
||||
char c = bleStream.read();
|
||||
putchar(c);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
uint64_t now = millis();
|
||||
if (now - lastSend > 5000) {
|
||||
lastSend = now;
|
||||
bleStream.printf("Hello from client! Uptime: %" PRIu64 " seconds\n", now / 1000);
|
||||
printf("Sent data to server via BLE stream\n");
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
# Override some defaults so BT stack is enabled
|
||||
# in this example
|
||||
|
||||
#
|
||||
# BT config
|
||||
#
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
|
||||
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
|
||||
CONFIG_BTDM_CTRL_MODE_BTDM=n
|
||||
CONFIG_BT_BLUEDROID_ENABLED=n
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
@@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(NimBLE_Stream_Echo)
|
||||
@@ -0,0 +1,39 @@
|
||||
# NimBLE Stream Echo Example
|
||||
|
||||
This is the simplest example demonstrating `NimBLEStreamServer`. It echoes back any data received from BLE clients.
|
||||
|
||||
## Features
|
||||
|
||||
- Minimal code showing essential NimBLE Stream usage
|
||||
- Echoes all received data back to the client
|
||||
- Uses default service and characteristic UUIDs
|
||||
- Perfect starting point for learning the Stream interface
|
||||
|
||||
## How it Works
|
||||
|
||||
1. Initializes BLE with minimal configuration
|
||||
2. Creates a stream server with default UUIDs
|
||||
3. Waits for client connection and data
|
||||
4. Echoes received data back to the client
|
||||
5. Displays received data in the ESP-IDF monitor output
|
||||
|
||||
## Default UUIDs
|
||||
|
||||
- Service: `0xc0de`
|
||||
- Characteristic: `0xfeed`
|
||||
|
||||
## Usage
|
||||
|
||||
1. Build and flash this example to your ESP32 using ESP-IDF (`idf.py build flash monitor`)
|
||||
2. Connect with a BLE client app (nRF Connect, Serial Bluetooth Terminal, etc.)
|
||||
3. Find the service `0xc0de` and characteristic `0xfeed`
|
||||
4. Subscribe to notifications
|
||||
5. Write data to the characteristic
|
||||
6. The data will be echoed back and displayed in `idf.py monitor`
|
||||
|
||||
## Good For
|
||||
|
||||
- Learning the basic NimBLE Stream API
|
||||
- Testing BLE connectivity
|
||||
- Starting point for custom applications
|
||||
- Understanding Stream read/write operations
|
||||
@@ -0,0 +1,4 @@
|
||||
set(COMPONENT_SRCS "main.cpp")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
|
||||
register_component()
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* NimBLE_Stream_Echo Example:
|
||||
*
|
||||
* A minimal example demonstrating NimBLEStreamServer.
|
||||
* Echoes back any data received from BLE clients.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include <NimBLEDevice.h>
|
||||
|
||||
NimBLEStreamServer bleStream;
|
||||
|
||||
struct RxOverflowStats {
|
||||
uint32_t droppedOld{0};
|
||||
uint32_t droppedNew{0};
|
||||
};
|
||||
|
||||
RxOverflowStats g_rxOverflowStats;
|
||||
|
||||
NimBLEStream::RxOverflowAction onRxOverflow(const uint8_t* data, size_t len, void* userArg) {
|
||||
auto* stats = static_cast<RxOverflowStats*>(userArg);
|
||||
if (stats) {
|
||||
stats->droppedOld++;
|
||||
}
|
||||
|
||||
// Echo mode prefers the latest incoming bytes.
|
||||
(void)data;
|
||||
(void)len;
|
||||
return NimBLEStream::DROP_OLDER_DATA;
|
||||
}
|
||||
|
||||
extern "C" void app_main(void) {
|
||||
printf("NimBLE Stream Echo Server\n");
|
||||
|
||||
// Initialize BLE.
|
||||
NimBLEDevice::init("BLE-Echo");
|
||||
auto pServer = NimBLEDevice::createServer();
|
||||
pServer->advertiseOnDisconnect(true); // Keep advertising after disconnects.
|
||||
|
||||
if (!bleStream.begin(NimBLEUUID(uint16_t(0xc0de)),
|
||||
NimBLEUUID(uint16_t(0xfeed)),
|
||||
1024,
|
||||
1024,
|
||||
false)) {
|
||||
printf("Failed to initialize BLE stream\n");
|
||||
return;
|
||||
}
|
||||
|
||||
bleStream.setRxOverflowCallback(onRxOverflow, &g_rxOverflowStats);
|
||||
|
||||
// Start advertising.
|
||||
NimBLEDevice::getAdvertising()->start();
|
||||
printf("Ready! Connect with a BLE client and send data.\n");
|
||||
|
||||
uint32_t lastDroppedOld = 0;
|
||||
uint32_t lastDroppedNew = 0;
|
||||
|
||||
for (;;) {
|
||||
if (g_rxOverflowStats.droppedOld != lastDroppedOld || g_rxOverflowStats.droppedNew != lastDroppedNew) {
|
||||
lastDroppedOld = g_rxOverflowStats.droppedOld;
|
||||
lastDroppedNew = g_rxOverflowStats.droppedNew;
|
||||
printf("RX overflow handled (drop-old=%" PRIu32 ", drop-new=%" PRIu32 ")\n", lastDroppedOld, lastDroppedNew);
|
||||
}
|
||||
|
||||
// Echo any received data back to the client.
|
||||
if (bleStream.ready() && bleStream.available()) {
|
||||
printf("Echo: ");
|
||||
while (bleStream.available()) {
|
||||
char c = bleStream.read();
|
||||
putchar(c);
|
||||
bleStream.write(c);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
# Override some defaults so BT stack is enabled
|
||||
# in this example
|
||||
|
||||
#
|
||||
# BT config
|
||||
#
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
|
||||
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
|
||||
CONFIG_BTDM_CTRL_MODE_BTDM=n
|
||||
CONFIG_BT_BLUEDROID_ENABLED=n
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
@@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(NimBLE_Stream_Server)
|
||||
@@ -0,0 +1,42 @@
|
||||
# NimBLE Stream Server Example
|
||||
|
||||
This example demonstrates how to use the `NimBLEStreamServer` class to create a BLE GATT server that behaves like a serial port using the familiar Arduino Stream interface.
|
||||
|
||||
## Features
|
||||
|
||||
- Uses Arduino Stream interface (print, println, read, available, etc.)
|
||||
- Automatic connection management
|
||||
- Bidirectional communication
|
||||
- Buffered TX/RX using ring buffers
|
||||
- Similar usage to Serial communication
|
||||
|
||||
## How it Works
|
||||
|
||||
1. Creates a BLE GATT server with a custom service and characteristic
|
||||
2. Initializes `NimBLEStreamServer` with the characteristic configured for notifications and writes
|
||||
3. Uses familiar Stream methods like `print()`, `println()`, `read()`, and `available()`
|
||||
4. Automatically handles connection state and MTU negotiation
|
||||
|
||||
## Usage
|
||||
|
||||
1. Build and flash this example to your ESP32 using ESP-IDF (`idf.py build flash monitor`)
|
||||
2. The device will advertise as "NimBLE-Stream"
|
||||
3. Connect with a BLE client (such as the NimBLE_Stream_Client example or a mobile app)
|
||||
4. Once connected, the server will:
|
||||
- Send periodic messages to the client
|
||||
- Echo back any data received from the client
|
||||
- Display all communication in `idf.py monitor`
|
||||
|
||||
## Service UUIDs
|
||||
|
||||
- Service: `6E400001-B5A3-F393-E0A9-E50E24DCCA9E`
|
||||
- Characteristic: `6E400002-B5A3-F393-E0A9-E50E24DCCA9E`
|
||||
|
||||
These are based on the Nordic UART Service (NUS) UUIDs for compatibility with many BLE terminal apps.
|
||||
|
||||
## Compatible With
|
||||
|
||||
- NimBLE_Stream_Client example
|
||||
- nRF Connect mobile app
|
||||
- Serial Bluetooth Terminal apps
|
||||
- Any BLE client that supports characteristic notifications and writes
|
||||
@@ -0,0 +1,4 @@
|
||||
set(COMPONENT_SRCS "main.cpp")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
|
||||
register_component()
|
||||
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* NimBLE_Stream_Server Example:
|
||||
*
|
||||
* Demonstrates using NimBLEStreamServer to create a BLE GATT server
|
||||
* that behaves like a serial port using the Stream-like interface.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#include <NimBLEDevice.h>
|
||||
|
||||
// Create the stream server instance
|
||||
NimBLEStreamServer bleStream;
|
||||
|
||||
struct RxOverflowStats {
|
||||
uint32_t droppedOld{0};
|
||||
uint32_t droppedNew{0};
|
||||
};
|
||||
|
||||
RxOverflowStats g_rxOverflowStats;
|
||||
|
||||
NimBLEStream::RxOverflowAction onRxOverflow(const uint8_t* data, size_t len, void* userArg) {
|
||||
auto* stats = static_cast<RxOverflowStats*>(userArg);
|
||||
if (stats) {
|
||||
stats->droppedOld++;
|
||||
}
|
||||
|
||||
// Keep the newest bytes for command/stream style traffic.
|
||||
(void)data;
|
||||
(void)len;
|
||||
return NimBLEStream::DROP_OLDER_DATA;
|
||||
}
|
||||
|
||||
static uint64_t millis() {
|
||||
return esp_timer_get_time() / 1000ULL;
|
||||
}
|
||||
|
||||
// Service and Characteristic UUIDs for the stream.
|
||||
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
#define CHARACTERISTIC_UUID "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
|
||||
/** Server callbacks to handle connection/disconnection events */
|
||||
class ServerCallbacks : public NimBLEServerCallbacks {
|
||||
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo) override {
|
||||
printf("Client connected: %s\n", connInfo.getAddress().toString().c_str());
|
||||
// Optionally update connection parameters for better throughput.
|
||||
pServer->updateConnParams(connInfo.getConnHandle(), 12, 24, 0, 200);
|
||||
}
|
||||
|
||||
void onDisconnect(NimBLEServer* pServer, NimBLEConnInfo& connInfo, int reason) override {
|
||||
(void)pServer;
|
||||
(void)connInfo;
|
||||
printf("Client disconnected - reason: %d, restarting advertising\n", reason);
|
||||
NimBLEDevice::startAdvertising();
|
||||
}
|
||||
|
||||
void onMTUChange(uint16_t MTU, NimBLEConnInfo& connInfo) override {
|
||||
printf("MTU updated: %u for connection ID: %u\n", MTU, connInfo.getConnHandle());
|
||||
}
|
||||
} serverCallbacks;
|
||||
|
||||
extern "C" void app_main(void) {
|
||||
printf("Starting NimBLE Stream Server\n");
|
||||
|
||||
/** Initialize NimBLE and set the device name */
|
||||
NimBLEDevice::init("NimBLE-Stream");
|
||||
|
||||
/**
|
||||
* Create the BLE server and set callbacks.
|
||||
* Note: The stream will create its own service and characteristic.
|
||||
*/
|
||||
NimBLEServer* pServer = NimBLEDevice::createServer();
|
||||
pServer->setCallbacks(&serverCallbacks);
|
||||
|
||||
/**
|
||||
* Initialize the stream server with:
|
||||
* - Service UUID
|
||||
* - Characteristic UUID
|
||||
* - txBufSize: 1024 bytes for outgoing data (notifications)
|
||||
* - rxBufSize: 1024 bytes for incoming data (writes)
|
||||
* - secure: false (no encryption required - set true for secure connections)
|
||||
*/
|
||||
if (!bleStream.begin(NimBLEUUID(SERVICE_UUID),
|
||||
NimBLEUUID(CHARACTERISTIC_UUID),
|
||||
1024,
|
||||
1024,
|
||||
false)) {
|
||||
printf("Failed to initialize BLE stream!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
bleStream.setRxOverflowCallback(onRxOverflow, &g_rxOverflowStats);
|
||||
|
||||
// Make the stream service discoverable.
|
||||
NimBLEAdvertising* pAdvertising = NimBLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setName("NimBLE-Stream");
|
||||
pAdvertising->enableScanResponse(true);
|
||||
pAdvertising->start();
|
||||
|
||||
printf("BLE Stream Server ready!\n");
|
||||
printf("Waiting for client connection...\n");
|
||||
|
||||
uint32_t lastDroppedOld = 0;
|
||||
uint32_t lastDroppedNew = 0;
|
||||
uint64_t lastSend = 0;
|
||||
|
||||
for (;;) {
|
||||
if (g_rxOverflowStats.droppedOld != lastDroppedOld || g_rxOverflowStats.droppedNew != lastDroppedNew) {
|
||||
lastDroppedOld = g_rxOverflowStats.droppedOld;
|
||||
lastDroppedNew = g_rxOverflowStats.droppedNew;
|
||||
printf("RX overflow handled (drop-old=%" PRIu32 ", drop-new=%" PRIu32 ")\n", lastDroppedOld, lastDroppedNew);
|
||||
}
|
||||
|
||||
if (bleStream.ready()) {
|
||||
uint64_t now = millis();
|
||||
if (now - lastSend > 2000) {
|
||||
lastSend = now;
|
||||
bleStream.printf("Hello from server! Uptime: %" PRIu64 " seconds\n", now / 1000);
|
||||
bleStream.printf("Free heap: %" PRIu32 " bytes\n", esp_get_free_heap_size());
|
||||
printf("Sent data to client via BLE stream\n");
|
||||
}
|
||||
|
||||
if (bleStream.available()) {
|
||||
printf("Received from client: ");
|
||||
while (bleStream.available()) {
|
||||
char c = bleStream.read();
|
||||
putchar(c);
|
||||
bleStream.write(c); // Echo back to BLE client.
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
} else {
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
# Override some defaults so BT stack is enabled
|
||||
# in this example
|
||||
|
||||
#
|
||||
# BT config
|
||||
#
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
|
||||
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
|
||||
CONFIG_BTDM_CTRL_MODE_BTDM=n
|
||||
CONFIG_BT_BLUEDROID_ENABLED=n
|
||||
CONFIG_BT_NIMBLE_ENABLED=y
|
||||
Reference in New Issue
Block a user