Skip to content

Commit 2634f77

Browse files
authored
Introduce L2CAP infrastructure. (#954)
1 parent b6b91b2 commit 2634f77

9 files changed

+800
-2
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
//
2+
// (C) Dr. Michael 'Mickey' Lauer <mickey@vanille-media.de>
3+
//
4+
5+
#include <Arduino.h>
6+
#include <NimBLEDevice.h>
7+
8+
#if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM <= 0
9+
# error "CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM must be set to 1 or greater"
10+
#endif
11+
12+
// See the following for generating UUIDs:
13+
// https://www.uuidgenerator.net/
14+
15+
// The remote service we wish to connect to.
16+
static NimBLEUUID serviceUUID("dcbc7255-1e9e-49a0-a360-b0430b6c6905");
17+
// The characteristic of the remote service we are interested in.
18+
static NimBLEUUID charUUID("371a55c8-f251-4ad2-90b3-c7c195b049be");
19+
20+
#define L2CAP_CHANNEL 150
21+
#define L2CAP_MTU 5000
22+
23+
const NimBLEAdvertisedDevice* theDevice = NULL;
24+
NimBLEClient* theClient = NULL;
25+
NimBLEL2CAPChannel* theChannel = NULL;
26+
27+
size_t bytesSent = 0;
28+
size_t bytesReceived = 0;
29+
size_t numberOfSeconds = 0;
30+
31+
class L2CAPChannelCallbacks : public NimBLEL2CAPChannelCallbacks {
32+
public:
33+
void onConnect(NimBLEL2CAPChannel* channel) { Serial.println("L2CAP connection established"); }
34+
35+
void onMTUChange(NimBLEL2CAPChannel* channel, uint16_t mtu) { Serial.printf("L2CAP MTU changed to %d\n", mtu); }
36+
37+
void onRead(NimBLEL2CAPChannel* channel, std::vector<uint8_t>& data) {
38+
Serial.printf("L2CAP read %d bytes\n", data.size());
39+
}
40+
void onDisconnect(NimBLEL2CAPChannel* channel) { Serial.println("L2CAP disconnected"); }
41+
} L2Callbacks;
42+
43+
class ClientCallbacks : public NimBLEClientCallbacks {
44+
void onConnect(NimBLEClient* pClient) {
45+
Serial.println("GAP connected");
46+
pClient->setDataLen(251);
47+
48+
theChannel = NimBLEL2CAPChannel::connect(pClient, L2CAP_CHANNEL, L2CAP_MTU, &L2Callbacks);
49+
}
50+
51+
void onDisconnect(NimBLEClient* pClient, int reason) {
52+
printf("GAP disconnected (reason: %d)\n", reason);
53+
theDevice = NULL;
54+
theChannel = NULL;
55+
NimBLEDevice::getScan()->start(5 * 1000, true);
56+
}
57+
} clientCallbacks;
58+
59+
class ScanCallbacks : public NimBLEScanCallbacks {
60+
void onResult(const NimBLEAdvertisedDevice* advertisedDevice) {
61+
if (theDevice) {
62+
return;
63+
}
64+
Serial.printf("BLE Advertised Device found: %s\n", advertisedDevice->toString().c_str());
65+
66+
if (!advertisedDevice->haveServiceUUID()) {
67+
return;
68+
}
69+
if (!advertisedDevice->isAdvertisingService(serviceUUID)) {
70+
return;
71+
}
72+
73+
Serial.println("Found the device we're interested in!");
74+
NimBLEDevice::getScan()->stop();
75+
76+
// Hand over the device to the other task
77+
theDevice = advertisedDevice;
78+
}
79+
} scanCallbacks;
80+
81+
void setup() {
82+
Serial.begin(115200);
83+
Serial.println("Starting L2CAP client example");
84+
85+
NimBLEDevice::init("L2CAP-Client");
86+
NimBLEDevice::setMTU(BLE_ATT_MTU_MAX);
87+
88+
auto scan = NimBLEDevice::getScan();
89+
scan->setScanCallbacks(&scanCallbacks);
90+
scan->setInterval(1349);
91+
scan->setWindow(449);
92+
scan->setActiveScan(true);
93+
scan->start(25 * 1000, false);
94+
}
95+
96+
void loop() {
97+
static uint8_t sequenceNumber = 0;
98+
static unsigned long firstBytesTime = 0;
99+
auto now = millis();
100+
101+
if (!theDevice) {
102+
delay(1000);
103+
return;
104+
}
105+
106+
if (!theClient) {
107+
theClient = NimBLEDevice::createClient();
108+
theClient->setConnectionParams(6, 6, 0, 42);
109+
theClient->setClientCallbacks(&clientCallbacks);
110+
if (!theClient->connect(theDevice)) {
111+
Serial.println("Error: Could not connect to device");
112+
return;
113+
}
114+
delay(2000);
115+
}
116+
117+
if (!theChannel) {
118+
Serial.println("l2cap channel not initialized");
119+
delay(2000);
120+
return;
121+
}
122+
123+
if (!theChannel->isConnected()) {
124+
Serial.println("l2cap channel not connected\n");
125+
delay(2000);
126+
return;
127+
}
128+
129+
std::vector<uint8_t> data(5000, sequenceNumber++);
130+
if (theChannel->write(data)) {
131+
if (bytesSent == 0) {
132+
firstBytesTime = now;
133+
}
134+
bytesSent += data.size();
135+
if (now - firstBytesTime > 1000) {
136+
int bytesSentPerSeconds = bytesSent / ((now - firstBytesTime) / 1000);
137+
printf("Bandwidth: %d b/sec = %d KB/sec\n", bytesSentPerSeconds, bytesSentPerSeconds / 1024);
138+
}
139+
} else {
140+
Serial.println("failed to send!");
141+
abort();
142+
}
143+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//
2+
// (C) Dr. Michael 'Mickey' Lauer <mickey@vanille-media.de>
3+
//
4+
5+
#include <Arduino.h>
6+
#include <NimBLEDevice.h>
7+
8+
// See the following for generating UUIDs:
9+
// https://www.uuidgenerator.net/
10+
11+
#define SERVICE_UUID "dcbc7255-1e9e-49a0-a360-b0430b6c6905"
12+
#define CHARACTERISTIC_UUID "371a55c8-f251-4ad2-90b3-c7c195b049be"
13+
#define L2CAP_CHANNEL 150
14+
#define L2CAP_MTU 5000
15+
16+
class GATTCallbacks : public NimBLEServerCallbacks {
17+
public:
18+
void onConnect(NimBLEServer* pServer, NimBLEConnInfo& info) {
19+
/// Booster #1
20+
pServer->setDataLen(info.getConnHandle(), 251);
21+
/// Booster #2 (especially for Apple devices)
22+
NimBLEDevice::getServer()->updateConnParams(info.getConnHandle(), 12, 12, 0, 200);
23+
}
24+
} gattCallbacks;
25+
26+
class L2CAPChannelCallbacks : public NimBLEL2CAPChannelCallbacks {
27+
public:
28+
bool connected = false;
29+
size_t numberOfReceivedBytes;
30+
uint8_t nextSequenceNumber;
31+
int numberOfSeconds;
32+
33+
public:
34+
void onConnect(NimBLEL2CAPChannel* channel) {
35+
Serial.println("L2CAP connection established");
36+
connected = true;
37+
numberOfSeconds = numberOfReceivedBytes = nextSequenceNumber = 0;
38+
}
39+
40+
void onRead(NimBLEL2CAPChannel* channel, std::vector<uint8_t>& data) {
41+
numberOfReceivedBytes += data.size();
42+
size_t sequenceNumber = data[0];
43+
Serial.printf("L2CAP read %d bytes w/ sequence number %d", data.size(), sequenceNumber);
44+
if (sequenceNumber != nextSequenceNumber) {
45+
Serial.printf("(wrong sequence number %d, expected %d)\n", sequenceNumber, nextSequenceNumber);
46+
} else {
47+
nextSequenceNumber++;
48+
}
49+
}
50+
51+
void onDisconnect(NimBLEL2CAPChannel* channel) {
52+
Serial.println("L2CAP disconnected");
53+
connected = false;
54+
}
55+
} l2capCallbacks;
56+
57+
void setup() {
58+
Serial.begin(115200);
59+
Serial.println("Starting L2CAP server example");
60+
61+
NimBLEDevice::init("L2CAP-Server");
62+
NimBLEDevice::setMTU(BLE_ATT_MTU_MAX);
63+
64+
auto cocServer = NimBLEDevice::createL2CAPServer();
65+
auto channel = cocServer->createService(L2CAP_CHANNEL, L2CAP_MTU, &l2capCallbacks);
66+
67+
auto server = NimBLEDevice::createServer();
68+
server->setCallbacks(&gattCallbacks);
69+
70+
auto service = server->createService(SERVICE_UUID);
71+
auto characteristic = service->createCharacteristic(CHARACTERISTIC_UUID, NIMBLE_PROPERTY::READ);
72+
characteristic->setValue(L2CAP_CHANNEL);
73+
service->start();
74+
75+
auto advertising = BLEDevice::getAdvertising();
76+
advertising->addServiceUUID(SERVICE_UUID);
77+
advertising->enableScanResponse(true);
78+
79+
NimBLEDevice::startAdvertising();
80+
Serial.println("Server waiting for connection requests");
81+
}
82+
83+
void loop() {
84+
// Wait until transfer actually starts...
85+
if (!l2capCallbacks.numberOfReceivedBytes) {
86+
delay(10);
87+
return;
88+
}
89+
90+
delay(1000);
91+
if (!l2capCallbacks.connected) {
92+
return;
93+
}
94+
95+
int bps = l2capCallbacks.numberOfReceivedBytes / ++l2capCallbacks.numberOfSeconds;
96+
Serial.printf("Bandwidth: %d b/sec = %d KB/sec\n", bps, bps / 1024);
97+
}

src/NimBLEDevice.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@
6565

6666
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
6767
# include "NimBLEServer.h"
68+
# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM > 0
69+
# include "NimBLEL2CAPServer.h"
70+
# endif
6871
# endif
6972

7073
# include "NimBLELog.h"
@@ -85,6 +88,9 @@ NimBLEScan* NimBLEDevice::m_pScan = nullptr;
8588

8689
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
8790
NimBLEServer* NimBLEDevice::m_pServer = nullptr;
91+
# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM > 0
92+
NimBLEL2CAPServer* NimBLEDevice::m_pL2CAPServer = nullptr;
93+
# endif
8894
# endif
8995

9096
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
@@ -140,6 +146,27 @@ NimBLEServer* NimBLEDevice::createServer() {
140146
NimBLEServer* NimBLEDevice::getServer() {
141147
return m_pServer;
142148
} // getServer
149+
150+
# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM > 0
151+
/**
152+
* @brief Create an instance of a L2CAP server.
153+
* @return A pointer to the instance of the L2CAP server.
154+
*/
155+
NimBLEL2CAPServer* NimBLEDevice::createL2CAPServer() {
156+
if (NimBLEDevice::m_pL2CAPServer == nullptr) {
157+
NimBLEDevice::m_pL2CAPServer = new NimBLEL2CAPServer();
158+
}
159+
return m_pL2CAPServer;
160+
} // createL2CAPServer
161+
162+
/**
163+
* @brief Get the instance of the L2CAP server.
164+
* @return A pointer to the L2CAP server instance or nullptr if none have been created.
165+
*/
166+
NimBLEL2CAPServer* NimBLEDevice::getL2CAPServer() {
167+
return m_pL2CAPServer;
168+
} // getL2CAPServer
169+
# endif
143170
# endif // #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
144171

145172
/* -------------------------------------------------------------------------- */
@@ -965,6 +992,12 @@ bool NimBLEDevice::deinit(bool clearAll) {
965992
delete NimBLEDevice::m_pServer;
966993
NimBLEDevice::m_pServer = nullptr;
967994
}
995+
# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM > 0
996+
if (NimBLEDevice::m_pL2CAPServer != nullptr) {
997+
delete NimBLEDevice::m_pL2CAPServer;
998+
NimBLEDevice::m_pL2CAPServer = nullptr;
999+
}
1000+
# endif
9681001
# endif
9691002

9701003
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)

src/NimBLEDevice.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ class NimBLEAdvertising;
5959

6060
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
6161
class NimBLEServer;
62+
# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM > 0
63+
class NimBLEL2CAPServer;
64+
# endif
6265
# endif
6366

6467
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) || defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
@@ -95,6 +98,13 @@ class NimBLEDeviceCallbacks;
9598
# define BLEEddystoneTLM NimBLEEddystoneTLM
9699
# define BLEEddystoneURL NimBLEEddystoneURL
97100
# define BLEConnInfo NimBLEConnInfo
101+
# define BLEL2CAPServer NimBLEL2CAPServer
102+
# define BLEL2CAPService NimBLEL2CAPService
103+
# define BLEL2CAPServiceCallbacks NimBLEL2CAPServiceCallbacks
104+
# define BLEL2CAPClient NimBLEL2CAPClient
105+
# define BLEL2CAPClientCallbacks NimBLEL2CAPClientCallbacks
106+
# define BLEL2CAPChannel NimBLEL2CAPChannel
107+
# define BLEL2CAPChannelCallbacks NimBLEL2CAPChannelCallbacks
98108

99109
# ifdef CONFIG_BT_NIMBLE_MAX_CONNECTIONS
100110
# define NIMBLE_MAX_CONNECTIONS CONFIG_BT_NIMBLE_MAX_CONNECTIONS
@@ -160,6 +170,10 @@ class NimBLEDevice {
160170
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
161171
static NimBLEServer* createServer();
162172
static NimBLEServer* getServer();
173+
# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM > 0
174+
static NimBLEL2CAPServer* createL2CAPServer();
175+
static NimBLEL2CAPServer* getL2CAPServer();
176+
# endif
163177
# endif
164178

165179
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) || defined(CONFIG_BT_NIMBLE_ROLE_CENTRAL)
@@ -216,6 +230,9 @@ class NimBLEDevice {
216230

217231
# if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL)
218232
static NimBLEServer* m_pServer;
233+
# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM > 0
234+
static NimBLEL2CAPServer* m_pL2CAPServer;
235+
# endif
219236
# endif
220237

221238
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)
@@ -275,6 +292,10 @@ class NimBLEDevice {
275292
# include "NimBLEService.h"
276293
# include "NimBLECharacteristic.h"
277294
# include "NimBLEDescriptor.h"
295+
# if CONFIG_BT_NIMBLE_L2CAP_COC_MAX_NUM > 0
296+
# include "NimBLEL2CAPServer.h"
297+
# include "NimBLEL2CAPChannel.h"
298+
# endif
278299
# endif
279300

280301
# if defined(CONFIG_BT_NIMBLE_ROLE_BROADCASTER)

0 commit comments

Comments
 (0)