-
-
Notifications
You must be signed in to change notification settings - Fork 204
Add BLE stream classes. #1068
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
Open
h2zero
wants to merge
1
commit into
master
Choose a base branch
from
stream-class
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,710
−8
Open
Add BLE stream classes. #1068
Changes from all commits
Commits
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| docs/doxydocs | ||
| .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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,206 @@ | ||
| /** | ||
| * NimBLE_Stream_Client Example: | ||
| * | ||
| * Demonstrates using NimBLEStreamClient to connect to a BLE GATT server | ||
| * and communicate using the Arduino Stream interface. | ||
| * | ||
| * This allows you to use familiar methods like print(), println(), | ||
| * read(), and available() over BLE, similar to how you would use Serial. | ||
| * | ||
| * This example connects to the NimBLE_Stream_Server example. | ||
| * | ||
| * Created: November 2025 | ||
| * Author: NimBLE-Arduino Contributors | ||
| */ | ||
|
|
||
| #include <Arduino.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; | ||
|
|
||
| // 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 { | ||
| Serial.printf("Advertised Device: %s\n", advertisedDevice->toString().c_str()); | ||
|
|
||
| // Check if this device advertises our service | ||
| if (advertisedDevice->isAdvertisingService(NimBLEUUID(SERVICE_UUID))) { | ||
| Serial.println("Found our stream server!"); | ||
| pServerDevice = advertisedDevice; | ||
| NimBLEDevice::getScan()->stop(); | ||
| doConnect = true; | ||
| } | ||
| } | ||
|
|
||
| void onScanEnd(const NimBLEScanResults& results, int reason) override { | ||
| Serial.println("Scan ended"); | ||
| if (!doConnect && !connected) { | ||
| Serial.println("Server not found, restarting scan..."); | ||
| NimBLEDevice::getScan()->start(5, false, true); | ||
| } | ||
| } | ||
| } scanCallbacks; | ||
|
|
||
| /** Client callbacks for connection/disconnection events */ | ||
| class ClientCallbacks : public NimBLEClientCallbacks { | ||
| void onConnect(NimBLEClient* pClient) override { | ||
| Serial.println("Connected to server"); | ||
| // Update connection parameters for better throughput | ||
| pClient->updateConnParams(12, 24, 0, 200); | ||
| } | ||
|
|
||
| void onDisconnect(NimBLEClient* pClient, int reason) override { | ||
| Serial.printf("Disconnected from server, reason: %d\n", reason); | ||
| connected = false; | ||
| bleStream.end(); | ||
|
|
||
| // Restart scanning | ||
| Serial.println("Restarting scan..."); | ||
| NimBLEDevice::getScan()->start(5, false, true); | ||
| } | ||
| } clientCallbacks; | ||
|
|
||
| /** Connect to the BLE Server and set up the stream */ | ||
| bool connectToServer() { | ||
| Serial.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) { | ||
| Serial.println("Failed to create client"); | ||
| 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)) { | ||
| Serial.println("Failed to connect to server"); | ||
| return false; | ||
| } | ||
|
|
||
| Serial.println("Connected! Discovering services..."); | ||
|
|
||
| // Get the service and characteristic | ||
| NimBLERemoteService* pRemoteService = pClient->getService(SERVICE_UUID); | ||
| if (!pRemoteService) { | ||
| Serial.println("Failed to find our service UUID"); | ||
| pClient->disconnect(); | ||
| return false; | ||
| } | ||
| Serial.println("Found the stream service"); | ||
|
|
||
| NimBLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(CHARACTERISTIC_UUID); | ||
| if (!pRemoteCharacteristic) { | ||
| Serial.println("Failed to find our characteristic UUID"); | ||
| pClient->disconnect(); | ||
| return false; | ||
| } | ||
| Serial.println("Found the stream characteristic"); | ||
|
|
||
| /** | ||
| * Initialize the stream client with the remote characteristic | ||
| * subscribeNotify=true means we'll receive notifications in the RX buffer | ||
| */ | ||
| if (!bleStream.begin(pRemoteCharacteristic, true)) { | ||
| Serial.println("Failed to initialize BLE stream!"); | ||
| pClient->disconnect(); | ||
| return false; | ||
| } | ||
|
|
||
| Serial.println("BLE Stream initialized successfully!"); | ||
| connected = true; | ||
| return true; | ||
| } | ||
|
|
||
| void setup() { | ||
| Serial.begin(115200); | ||
| Serial.println("Starting NimBLE Stream Client"); | ||
|
|
||
| /** Initialize NimBLE */ | ||
| NimBLEDevice::init("NimBLE-StreamClient"); | ||
|
|
||
| /** | ||
| * Create the BLE scan instance and set callbacks | ||
| * Configure scan parameters | ||
| */ | ||
| NimBLEScan* pScan = NimBLEDevice::getScan(); | ||
| pScan->setScanCallbacks(&scanCallbacks, false); | ||
| pScan->setInterval(100); | ||
| pScan->setWindow(99); | ||
| pScan->setActiveScan(true); | ||
|
|
||
| /** Start scanning for the server */ | ||
| Serial.println("Scanning for BLE Stream Server..."); | ||
| pScan->start(5, false, true); | ||
| } | ||
|
|
||
| void loop() { | ||
| // If we found a server, try to connect | ||
| if (doConnect) { | ||
| doConnect = false; | ||
| if (connectToServer()) { | ||
| Serial.println("Stream ready for communication!"); | ||
| } else { | ||
| Serial.println("Failed to connect to server, restarting scan..."); | ||
| pServerDevice = nullptr; | ||
| NimBLEDevice::getScan()->start(5, false, true); | ||
| } | ||
h2zero marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| // If we're connected, demonstrate the stream interface | ||
| if (connected && bleStream) { | ||
| // Check if we received any data from the server | ||
| if (bleStream.available()) { | ||
| Serial.print("Received from server: "); | ||
|
|
||
| // Read all available data (just like Serial.read()) | ||
| while (bleStream.available()) { | ||
| char c = bleStream.read(); | ||
| Serial.write(c); | ||
| } | ||
| Serial.println(); | ||
| } | ||
|
|
||
| // Send a message every 5 seconds using Stream methods | ||
| static unsigned long lastSend = 0; | ||
| if (millis() - lastSend > 5000) { | ||
| lastSend = millis(); | ||
|
|
||
| // Using familiar Serial-like methods! | ||
| bleStream.print("Hello from client! Uptime: "); | ||
| bleStream.print(millis() / 1000); | ||
| bleStream.println(" seconds"); | ||
|
|
||
| Serial.println("Sent data to server via BLE stream"); | ||
| } | ||
|
|
||
| // You can also read from Serial and send over BLE | ||
| if (Serial.available()) { | ||
| Serial.println("Reading from Serial and sending via BLE:"); | ||
| while (Serial.available()) { | ||
| char c = Serial.read(); | ||
| Serial.write(c); // Echo locally | ||
| bleStream.write(c); // Send via BLE | ||
| } | ||
| Serial.println(); | ||
| } | ||
| } | ||
|
|
||
| delay(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,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. First, upload the NimBLE_Stream_Server example to one ESP32 | ||
| 2. Upload this client sketch to another ESP32 | ||
| 3. The client will automatically: | ||
| - Scan for the server | ||
| - Connect when found | ||
| - Set up the stream interface | ||
| - Begin bidirectional communication | ||
| 4. You can also type in the Serial monitor to send data to the server | ||
|
|
||
| ## Service UUIDs | ||
|
|
||
| Must match the server: | ||
| - Service: `6E400001-B5A3-F393-E0A9-E50E24DCCA9E` | ||
| - Characteristic: `6E400002-B5A3-F393-E0A9-E50E24DCCA9E` | ||
|
|
||
| ## Serial 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 Serial monitor |
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,62 @@ | ||
| /** | ||
| * NimBLE_Stream_Echo Example: | ||
| * | ||
| * A minimal example demonstrating NimBLEStreamServer. | ||
| * Echoes back any data received from BLE clients. | ||
| * | ||
| * This is the simplest way to use the NimBLE Stream interface, | ||
| * showing just the essential setup and read/write operations. | ||
| * | ||
| * Created: November 2025 | ||
| * Author: NimBLE-Arduino Contributors | ||
| */ | ||
|
|
||
| #include <Arduino.h> | ||
| #include <NimBLEDevice.h> | ||
|
|
||
| NimBLEStreamServer bleStream; | ||
|
|
||
| void setup() { | ||
| Serial.begin(115200); | ||
| Serial.println("NimBLE Stream Echo Server"); | ||
|
|
||
| // Initialize BLE | ||
| NimBLEDevice::init("BLE-Echo"); | ||
| auto pServer = NimBLEDevice::createServer(); | ||
| pServer->advertiseOnDisconnect(true); // Keep advertising after clients disconnect | ||
|
|
||
| /** | ||
| * 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 to true for secure connections) | ||
| */ | ||
| if (!bleStream.begin(NimBLEUUID(uint16_t(0xc0de)), // Service UUID | ||
| NimBLEUUID(uint16_t(0xfeed)), // Characteristic UUID | ||
| 1024, // txBufSize | ||
| 1024, // rxBufSize | ||
| false)) { // secure | ||
| Serial.println("Failed to initialize BLE stream"); | ||
| return; | ||
| } | ||
|
|
||
| // Start advertising | ||
| NimBLEDevice::getAdvertising()->start(); | ||
| Serial.println("Ready! Connect with a BLE client and send data."); | ||
| } | ||
|
|
||
| void loop() { | ||
| // Echo any received data back to the client | ||
| if (bleStream.ready() && bleStream.available()) { | ||
| Serial.print("Echo: "); | ||
| while (bleStream.available()) { | ||
| char c = bleStream.read(); | ||
| Serial.write(c); | ||
| bleStream.write(c); // Echo back | ||
| } | ||
| Serial.println(); | ||
| } | ||
| delay(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,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 on Serial monitor | ||
|
|
||
| ## Default UUIDs | ||
|
|
||
| - Service: `0xc0de` | ||
| - Characteristic: `0xfeed` | ||
|
|
||
| ## Usage | ||
|
|
||
| 1. Upload this sketch to your ESP32 | ||
| 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 Serial monitor | ||
|
|
||
| ## Good For | ||
|
|
||
| - Learning the basic NimBLE Stream API | ||
| - Testing BLE connectivity | ||
| - Starting point for custom applications | ||
| - Understanding Stream read/write operations |
Oops, something went wrong.
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.