Skip to content

Commit 4ea61fb

Browse files
h2zerodoudar
andcommitted
Add BLE stream classes.
Co-authored-by: doudar <17362216+doudar@users.noreply.github.com>
1 parent cc47671 commit 4ea61fb

12 files changed

Lines changed: 1710 additions & 8 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
docs/doxydocs
22
.development
3+
_codeql_detected_source_root
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
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+
19+
// Service and Characteristic UUIDs (must match the server)
20+
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
21+
#define CHARACTERISTIC_UUID "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
22+
23+
// Create the stream client instance
24+
NimBLEStreamClient bleStream;
25+
26+
// Connection state variables
27+
static bool doConnect = false;
28+
static bool connected = false;
29+
static const NimBLEAdvertisedDevice* pServerDevice = nullptr;
30+
static NimBLEClient* pClient = nullptr;
31+
32+
/** Scan callbacks to find the server */
33+
class ScanCallbacks : public NimBLEScanCallbacks {
34+
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) override {
35+
Serial.printf("Advertised Device: %s\n", advertisedDevice->toString().c_str());
36+
37+
// Check if this device advertises our service
38+
if (advertisedDevice->isAdvertisingService(NimBLEUUID(SERVICE_UUID))) {
39+
Serial.println("Found our stream server!");
40+
pServerDevice = advertisedDevice;
41+
NimBLEDevice::getScan()->stop();
42+
doConnect = true;
43+
}
44+
}
45+
46+
void onScanEnd(const NimBLEScanResults& results, int reason) override {
47+
Serial.println("Scan ended");
48+
if (!doConnect && !connected) {
49+
Serial.println("Server not found, restarting scan...");
50+
NimBLEDevice::getScan()->start(5, false, true);
51+
}
52+
}
53+
} scanCallbacks;
54+
55+
/** Client callbacks for connection/disconnection events */
56+
class ClientCallbacks : public NimBLEClientCallbacks {
57+
void onConnect(NimBLEClient* pClient) override {
58+
Serial.println("Connected to server");
59+
// Update connection parameters for better throughput
60+
pClient->updateConnParams(12, 24, 0, 200);
61+
}
62+
63+
void onDisconnect(NimBLEClient* pClient, int reason) override {
64+
Serial.printf("Disconnected from server, reason: %d\n", reason);
65+
connected = false;
66+
bleStream.end();
67+
68+
// Restart scanning
69+
Serial.println("Restarting scan...");
70+
NimBLEDevice::getScan()->start(5, false, true);
71+
}
72+
} clientCallbacks;
73+
74+
/** Connect to the BLE Server and set up the stream */
75+
bool connectToServer() {
76+
Serial.printf("Connecting to: %s\n", pServerDevice->getAddress().toString().c_str());
77+
78+
// Create or reuse a client
79+
pClient = NimBLEDevice::getClientByPeerAddress(pServerDevice->getAddress());
80+
if (!pClient) {
81+
pClient = NimBLEDevice::createClient();
82+
if (!pClient) {
83+
Serial.println("Failed to create client");
84+
return false;
85+
}
86+
pClient->setClientCallbacks(&clientCallbacks, false);
87+
pClient->setConnectionParams(12, 24, 0, 200);
88+
pClient->setConnectTimeout(5000);
89+
}
90+
91+
// Connect to the remote BLE Server
92+
if (!pClient->connect(pServerDevice)) {
93+
Serial.println("Failed to connect to server");
94+
return false;
95+
}
96+
97+
Serial.println("Connected! Discovering services...");
98+
99+
// Get the service and characteristic
100+
NimBLERemoteService* pRemoteService = pClient->getService(SERVICE_UUID);
101+
if (!pRemoteService) {
102+
Serial.println("Failed to find our service UUID");
103+
pClient->disconnect();
104+
return false;
105+
}
106+
Serial.println("Found the stream service");
107+
108+
NimBLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(CHARACTERISTIC_UUID);
109+
if (!pRemoteCharacteristic) {
110+
Serial.println("Failed to find our characteristic UUID");
111+
pClient->disconnect();
112+
return false;
113+
}
114+
Serial.println("Found the stream characteristic");
115+
116+
/**
117+
* Initialize the stream client with the remote characteristic
118+
* subscribeNotify=true means we'll receive notifications in the RX buffer
119+
*/
120+
if (!bleStream.begin(pRemoteCharacteristic, true)) {
121+
Serial.println("Failed to initialize BLE stream!");
122+
pClient->disconnect();
123+
return false;
124+
}
125+
126+
Serial.println("BLE Stream initialized successfully!");
127+
connected = true;
128+
return true;
129+
}
130+
131+
void setup() {
132+
Serial.begin(115200);
133+
Serial.println("Starting NimBLE Stream Client");
134+
135+
/** Initialize NimBLE */
136+
NimBLEDevice::init("NimBLE-StreamClient");
137+
138+
/**
139+
* Create the BLE scan instance and set callbacks
140+
* Configure scan parameters
141+
*/
142+
NimBLEScan* pScan = NimBLEDevice::getScan();
143+
pScan->setScanCallbacks(&scanCallbacks, false);
144+
pScan->setInterval(100);
145+
pScan->setWindow(99);
146+
pScan->setActiveScan(true);
147+
148+
/** Start scanning for the server */
149+
Serial.println("Scanning for BLE Stream Server...");
150+
pScan->start(5, false, true);
151+
}
152+
153+
void loop() {
154+
// If we found a server, try to connect
155+
if (doConnect) {
156+
doConnect = false;
157+
if (connectToServer()) {
158+
Serial.println("Stream ready for communication!");
159+
} else {
160+
Serial.println("Failed to connect to server, restarting scan...");
161+
pServerDevice = nullptr;
162+
NimBLEDevice::getScan()->start(5, false, true);
163+
}
164+
}
165+
166+
// If we're connected, demonstrate the stream interface
167+
if (connected && bleStream) {
168+
// Check if we received any data from the server
169+
if (bleStream.available()) {
170+
Serial.print("Received from server: ");
171+
172+
// Read all available data (just like Serial.read())
173+
while (bleStream.available()) {
174+
char c = bleStream.read();
175+
Serial.write(c);
176+
}
177+
Serial.println();
178+
}
179+
180+
// Send a message every 5 seconds using Stream methods
181+
static unsigned long lastSend = 0;
182+
if (millis() - lastSend > 5000) {
183+
lastSend = millis();
184+
185+
// Using familiar Serial-like methods!
186+
bleStream.print("Hello from client! Uptime: ");
187+
bleStream.print(millis() / 1000);
188+
bleStream.println(" seconds");
189+
190+
Serial.println("Sent data to server via BLE stream");
191+
}
192+
193+
// You can also read from Serial and send over BLE
194+
if (Serial.available()) {
195+
Serial.println("Reading from Serial and sending via BLE:");
196+
while (Serial.available()) {
197+
char c = Serial.read();
198+
Serial.write(c); // Echo locally
199+
bleStream.write(c); // Send via BLE
200+
}
201+
Serial.println();
202+
}
203+
}
204+
205+
delay(10);
206+
}
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 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: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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+
17+
NimBLEStreamServer bleStream;
18+
19+
void setup() {
20+
Serial.begin(115200);
21+
Serial.println("NimBLE Stream Echo Server");
22+
23+
// Initialize BLE
24+
NimBLEDevice::init("BLE-Echo");
25+
auto pServer = NimBLEDevice::createServer();
26+
pServer->advertiseOnDisconnect(true); // Keep advertising after clients disconnect
27+
28+
/**
29+
* Initialize the stream server with:
30+
* - Service UUID
31+
* - Characteristic UUID
32+
* - txBufSize: 1024 bytes for outgoing data (notifications)
33+
* - rxBufSize: 1024 bytes for incoming data (writes)
34+
* - secure: false (no encryption required - set to true for secure connections)
35+
*/
36+
if (!bleStream.begin(NimBLEUUID(uint16_t(0xc0de)), // Service UUID
37+
NimBLEUUID(uint16_t(0xfeed)), // Characteristic UUID
38+
1024, // txBufSize
39+
1024, // rxBufSize
40+
false)) { // secure
41+
Serial.println("Failed to initialize BLE stream");
42+
return;
43+
}
44+
45+
// Start advertising
46+
NimBLEDevice::getAdvertising()->start();
47+
Serial.println("Ready! Connect with a BLE client and send data.");
48+
}
49+
50+
void loop() {
51+
// Echo any received data back to the client
52+
if (bleStream.ready() && bleStream.available()) {
53+
Serial.print("Echo: ");
54+
while (bleStream.available()) {
55+
char c = bleStream.read();
56+
Serial.write(c);
57+
bleStream.write(c); // Echo back
58+
}
59+
Serial.println();
60+
}
61+
delay(10);
62+
}
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)