Skip to content

Commit cb589db

Browse files
Copilotdoudar
andcommitted
Add stream-class examples: NimBLE_Stream_Server, NimBLE_Stream_Client, and NimBLE_Stream_Echo
Co-authored-by: doudar <[email protected]>
1 parent da75f08 commit cb589db

File tree

6 files changed

+531
-0
lines changed

6 files changed

+531
-0
lines changed
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
/**
2+
* NimBLE_Stream_Client Example:
3+
*
4+
* Demonstrates using NimBLEStreamClient to connect to a BLE GATT server
5+
* and communicate using the Arduino Stream interface.
6+
*
7+
* This allows you to use familiar methods like print(), println(),
8+
* read(), and available() over BLE, similar to how you would use Serial.
9+
*
10+
* This example connects to the NimBLE_Stream_Server example.
11+
*
12+
* Created: November 2025
13+
* Author: NimBLE-Arduino Contributors
14+
*/
15+
16+
#include <Arduino.h>
17+
#include <NimBLEDevice.h>
18+
#include <NimBLEStream.h>
19+
20+
// Service and Characteristic UUIDs (must match the server)
21+
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
22+
#define CHARACTERISTIC_UUID "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
23+
24+
// Create the stream client instance
25+
NimBLEStreamClient bleStream;
26+
27+
// Connection state variables
28+
static bool doConnect = false;
29+
static bool connected = false;
30+
static const NimBLEAdvertisedDevice* pServerDevice = nullptr;
31+
static NimBLEClient* pClient = nullptr;
32+
33+
/** Scan callbacks to find the server */
34+
class ScanCallbacks : public NimBLEScanCallbacks {
35+
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) override {
36+
Serial.printf("Advertised Device: %s\n", advertisedDevice->toString().c_str());
37+
38+
// Check if this device advertises our service
39+
if (advertisedDevice->isAdvertisingService(NimBLEUUID(SERVICE_UUID))) {
40+
Serial.println("Found our stream server!");
41+
pServerDevice = advertisedDevice;
42+
NimBLEDevice::getScan()->stop();
43+
doConnect = true;
44+
}
45+
}
46+
47+
void onScanEnd(const NimBLEScanResults& results, int reason) override {
48+
Serial.println("Scan ended");
49+
if (!doConnect && !connected) {
50+
Serial.println("Server not found, restarting scan...");
51+
NimBLEDevice::getScan()->start(5, false, true);
52+
}
53+
}
54+
} scanCallbacks;
55+
56+
/** Client callbacks for connection/disconnection events */
57+
class ClientCallbacks : public NimBLEClientCallbacks {
58+
void onConnect(NimBLEClient* pClient) override {
59+
Serial.println("Connected to server");
60+
// Update connection parameters for better throughput
61+
pClient->updateConnParams(12, 24, 0, 200);
62+
}
63+
64+
void onDisconnect(NimBLEClient* pClient, int reason) override {
65+
Serial.printf("Disconnected from server, reason: %d\n", reason);
66+
connected = false;
67+
bleStream.deinit();
68+
69+
// Restart scanning
70+
Serial.println("Restarting scan...");
71+
NimBLEDevice::getScan()->start(5, false, true);
72+
}
73+
} clientCallbacks;
74+
75+
/** Connect to the BLE Server and set up the stream */
76+
bool connectToServer() {
77+
Serial.printf("Connecting to: %s\n", pServerDevice->getAddress().toString().c_str());
78+
79+
// Create or reuse a client
80+
pClient = NimBLEDevice::getClientByPeerAddress(pServerDevice->getAddress());
81+
if (!pClient) {
82+
pClient = NimBLEDevice::createClient();
83+
if (!pClient) {
84+
Serial.println("Failed to create client");
85+
return false;
86+
}
87+
pClient->setClientCallbacks(&clientCallbacks, false);
88+
pClient->setConnectionParams(12, 24, 0, 200);
89+
pClient->setConnectTimeout(5000);
90+
}
91+
92+
// Connect to the remote BLE Server
93+
if (!pClient->connect(pServerDevice)) {
94+
Serial.println("Failed to connect to server");
95+
return false;
96+
}
97+
98+
Serial.println("Connected! Discovering services...");
99+
100+
// Get the service and characteristic
101+
NimBLERemoteService* pRemoteService = pClient->getService(SERVICE_UUID);
102+
if (!pRemoteService) {
103+
Serial.println("Failed to find our service UUID");
104+
pClient->disconnect();
105+
return false;
106+
}
107+
Serial.println("Found the stream service");
108+
109+
NimBLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(CHARACTERISTIC_UUID);
110+
if (!pRemoteCharacteristic) {
111+
Serial.println("Failed to find our characteristic UUID");
112+
pClient->disconnect();
113+
return false;
114+
}
115+
Serial.println("Found the stream characteristic");
116+
117+
/**
118+
* Initialize the stream client with the remote characteristic
119+
* subscribeNotify=true means we'll receive notifications in the RX buffer
120+
*/
121+
if (!bleStream.init(pRemoteCharacteristic, true)) {
122+
Serial.println("Failed to initialize BLE stream!");
123+
pClient->disconnect();
124+
return false;
125+
}
126+
127+
/** Start the stream (begins internal buffers and tasks) */
128+
if (!bleStream.begin()) {
129+
Serial.println("Failed to start BLE stream!");
130+
bleStream.deinit();
131+
pClient->disconnect();
132+
return false;
133+
}
134+
135+
Serial.println("BLE Stream initialized successfully!");
136+
connected = true;
137+
return true;
138+
}
139+
140+
void setup() {
141+
Serial.begin(115200);
142+
Serial.println("Starting NimBLE Stream Client");
143+
144+
/** Initialize NimBLE */
145+
NimBLEDevice::init("NimBLE-StreamClient");
146+
147+
/**
148+
* Create the BLE scan instance and set callbacks
149+
* Configure scan parameters
150+
*/
151+
NimBLEScan* pScan = NimBLEDevice::getScan();
152+
pScan->setScanCallbacks(&scanCallbacks, false);
153+
pScan->setInterval(100);
154+
pScan->setWindow(99);
155+
pScan->setActiveScan(true);
156+
157+
/** Start scanning for the server */
158+
Serial.println("Scanning for BLE Stream Server...");
159+
pScan->start(5, false, true);
160+
}
161+
162+
void loop() {
163+
// If we found a server, try to connect
164+
if (doConnect) {
165+
doConnect = false;
166+
if (connectToServer()) {
167+
Serial.println("Stream ready for communication!");
168+
} else {
169+
Serial.println("Failed to connect to server");
170+
// Scan will restart automatically
171+
}
172+
}
173+
174+
// If we're connected, demonstrate the stream interface
175+
if (connected && bleStream) {
176+
177+
// Check if we received any data from the server
178+
if (bleStream.available()) {
179+
Serial.print("Received from server: ");
180+
181+
// Read all available data (just like Serial.read())
182+
while (bleStream.available()) {
183+
char c = bleStream.read();
184+
Serial.write(c);
185+
}
186+
Serial.println();
187+
}
188+
189+
// Send a message every 5 seconds using Stream methods
190+
static unsigned long lastSend = 0;
191+
if (millis() - lastSend > 5000) {
192+
lastSend = millis();
193+
194+
// Using familiar Serial-like methods!
195+
bleStream.print("Hello from client! Uptime: ");
196+
bleStream.print(millis() / 1000);
197+
bleStream.println(" seconds");
198+
199+
Serial.println("Sent data to server via BLE stream");
200+
}
201+
202+
// You can also read from Serial and send over BLE
203+
if (Serial.available()) {
204+
Serial.println("Reading from Serial and sending via BLE:");
205+
while (Serial.available()) {
206+
char c = Serial.read();
207+
Serial.write(c); // Echo locally
208+
bleStream.write(c); // Send via BLE
209+
}
210+
Serial.println();
211+
}
212+
}
213+
214+
delay(10);
215+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# NimBLE Stream Client Example
2+
3+
This example demonstrates how to use the `NimBLEStreamClient` class to connect to a BLE GATT server and communicate using the familiar Arduino Stream interface.
4+
5+
## Features
6+
7+
- Uses Arduino Stream interface (print, println, read, available, etc.)
8+
- Automatic server discovery and connection
9+
- Bidirectional communication
10+
- Buffered TX/RX using FreeRTOS ring buffers
11+
- Automatic reconnection on disconnect
12+
- Similar usage to Serial communication
13+
14+
## How it Works
15+
16+
1. Scans for BLE devices advertising the target service UUID
17+
2. Connects to the server and discovers the stream characteristic
18+
3. Initializes `NimBLEStreamClient` with the remote characteristic
19+
4. Subscribes to notifications to receive data in the RX buffer
20+
5. Uses familiar Stream methods like `print()`, `println()`, `read()`, and `available()`
21+
22+
## Usage
23+
24+
1. First, upload the NimBLE_Stream_Server example to one ESP32
25+
2. Upload this client sketch to another ESP32
26+
3. The client will automatically:
27+
- Scan for the server
28+
- Connect when found
29+
- Set up the stream interface
30+
- Begin bidirectional communication
31+
4. You can also type in the Serial monitor to send data to the server
32+
33+
## Service UUIDs
34+
35+
Must match the server:
36+
- Service: `6E400001-B5A3-F393-E0A9-E50E24DCCA9E`
37+
- Characteristic: `6E400002-B5A3-F393-E0A9-E50E24DCCA9E`
38+
39+
## Serial Monitor Output
40+
41+
The example displays:
42+
- Server discovery progress
43+
- Connection status
44+
- All data received from the server
45+
- Confirmation of data sent to the server
46+
47+
## Testing
48+
49+
Run with NimBLE_Stream_Server to see bidirectional communication:
50+
- Server sends periodic status messages
51+
- Client sends periodic uptime messages
52+
- Both echo data received from each other
53+
- You can send data from either Serial monitor
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* NimBLE_Stream_Echo Example:
3+
*
4+
* A minimal example demonstrating NimBLEStreamServer.
5+
* Echoes back any data received from BLE clients.
6+
*
7+
* This is the simplest way to use the NimBLE Stream interface,
8+
* showing just the essential setup and read/write operations.
9+
*
10+
* Created: November 2025
11+
* Author: NimBLE-Arduino Contributors
12+
*/
13+
14+
#include <Arduino.h>
15+
#include <NimBLEDevice.h>
16+
#include <NimBLEStream.h>
17+
18+
NimBLEStreamServer bleStream;
19+
20+
void setup() {
21+
Serial.begin(115200);
22+
Serial.println("NimBLE Stream Echo Server");
23+
24+
// Initialize BLE
25+
NimBLEDevice::init("BLE-Echo");
26+
NimBLEDevice::createServer();
27+
28+
// Initialize stream with default UUIDs, allow writes
29+
bleStream.init(NimBLEUUID(uint16_t(0xc0de)), // Service UUID
30+
NimBLEUUID(uint16_t(0xfeed)), // Characteristic UUID
31+
true, // canWrite
32+
false); // secure
33+
bleStream.begin();
34+
35+
// Start advertising
36+
NimBLEDevice::getAdvertising()->start();
37+
Serial.println("Ready! Connect with a BLE client and send data.");
38+
}
39+
40+
void loop() {
41+
// Echo any received data back to the client
42+
if (bleStream.hasSubscriber() && bleStream.available()) {
43+
Serial.print("Echo: ");
44+
while (bleStream.available()) {
45+
char c = bleStream.read();
46+
Serial.write(c);
47+
bleStream.write(c); // Echo back
48+
}
49+
Serial.println();
50+
}
51+
delay(10);
52+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# NimBLE Stream Echo Example
2+
3+
This is the simplest example demonstrating `NimBLEStreamServer`. It echoes back any data received from BLE clients.
4+
5+
## Features
6+
7+
- Minimal code showing essential NimBLE Stream usage
8+
- Echoes all received data back to the client
9+
- Uses default service and characteristic UUIDs
10+
- Perfect starting point for learning the Stream interface
11+
12+
## How it Works
13+
14+
1. Initializes BLE with minimal configuration
15+
2. Creates a stream server with default UUIDs
16+
3. Waits for client connection and data
17+
4. Echoes received data back to the client
18+
5. Displays received data on Serial monitor
19+
20+
## Default UUIDs
21+
22+
- Service: `0xc0de`
23+
- Characteristic: `0xfeed`
24+
25+
## Usage
26+
27+
1. Upload this sketch to your ESP32
28+
2. Connect with a BLE client app (nRF Connect, Serial Bluetooth Terminal, etc.)
29+
3. Find the service `0xc0de` and characteristic `0xfeed`
30+
4. Subscribe to notifications
31+
5. Write data to the characteristic
32+
6. The data will be echoed back and displayed in Serial monitor
33+
34+
## Good For
35+
36+
- Learning the basic NimBLE Stream API
37+
- Testing BLE connectivity
38+
- Starting point for custom applications
39+
- Understanding Stream read/write operations

0 commit comments

Comments
 (0)