-
Notifications
You must be signed in to change notification settings - Fork 87
Add BLE stream classes. #384
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,4 @@ | ||
| docs/doxydocs | ||
| dist | ||
| dist | ||
| .development | ||
| _codeql_detected_source_root |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| set(COMPONENT_SRCS "main.cpp") | ||
| set(COMPONENT_ADD_INCLUDEDIRS ".") | ||
|
|
||
| register_component() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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)); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| set(COMPONENT_SRCS "main.cpp") | ||
| set(COMPONENT_ADD_INCLUDEDIRS ".") | ||
|
|
||
| register_component() |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.