Skip to content

Commit 34c6417

Browse files
committed
Add examples
1 parent f672d04 commit 34c6417

35 files changed

+1355
-0
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# The following lines of boilerplate have to be in your project's
2+
# CMakeLists in this exact order for cmake to work correctly
3+
cmake_minimum_required(VERSION 3.5)
4+
5+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
6+
set(SUPPORTED_TARGETS esp32)
7+
project(NimBLE_Client)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
PROJECT_NAME := NimBLE_Client
2+
3+
include $(IDF_PATH)/make/project.mk
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
set(COMPONENT_SRCS "main.cpp")
2+
set(COMPONENT_ADD_INCLUDEDIRS ".")
3+
4+
register_component()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#
2+
# "main" pseudo-component makefile.
3+
#
4+
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
Lines changed: 372 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,372 @@
1+
2+
/** NimBLE_Server Demo:
3+
*
4+
* Demonstrates many of the available features of the NimBLE client library.
5+
*
6+
* Created: on March 24 2020
7+
* Author: H2zero
8+
*
9+
*/
10+
#include <NimBLEDevice.h>
11+
12+
extern "C" {void app_main(void);}
13+
14+
void scanEndedCB(NimBLEScanResults results);
15+
16+
static NimBLEAdvertisedDevice* advDevice;
17+
18+
static bool doConnect = false;
19+
static uint32_t scanTime = 0; /** 0 = scan forever */
20+
21+
22+
/** None of these are required as they will be handled by the library with defaults. **
23+
** Remove as you see fit for your needs */
24+
class ClientCallbacks : public NimBLEClientCallbacks {
25+
void onConnect(NimBLEClient* pClient) {
26+
printf("Connected\n");
27+
/** After connection we should change the parameters if we don't need fast response times.
28+
* These settings are 150ms interval, 0 latency, 450ms timout.
29+
* Timeout should be a multiple of the interval, minimum is 100ms.
30+
* I find a multiple of 3-5 * the interval works best for quick response/reconnect.
31+
* Min interval: 120 * 1.25ms = 150, Max interval: 120 * 1.25ms = 150, 0 latency, 45 * 10ms = 450ms timeout
32+
*/
33+
pClient->updateConnParams(120,120,0,45);
34+
};
35+
36+
void onDisconnect(NimBLEClient* pClient) {
37+
printf("%s Disconnected - Starting scan\n", pClient->getPeerAddress().toString().c_str());
38+
NimBLEDevice::getScan()->start(scanTime, scanEndedCB);
39+
};
40+
41+
/********************* Security handled here **********************
42+
****** Note: these are the same return values as defaults ********/
43+
uint32_t onPassKeyRequest(){
44+
printf("Client Passkey Request\n");
45+
/** return the passkey to send to the server */
46+
return 123456;
47+
};
48+
49+
bool onConfirmPIN(uint32_t pass_key){
50+
printf("The passkey YES/NO number: %d\n", pass_key);
51+
/** Return false if passkeys don't match. */
52+
return true;
53+
};
54+
55+
/** Pairing process complete, we can check the results in ble_gap_conn_desc */
56+
void onAuthenticationComplete(ble_gap_conn_desc* desc){
57+
if(!desc->sec_state.encrypted) {
58+
printf("Encrypt connection failed - disconnecting\n");
59+
/** Find the client with the connection handle provided in desc */
60+
NimBLEDevice::getClientByID(desc->conn_handle)->disconnect();
61+
return;
62+
}
63+
};
64+
};
65+
66+
67+
/** Define a class to handle the callbacks when advertisments are received */
68+
class AdvertisedDeviceCallbacks: public NimBLEAdvertisedDeviceCallbacks {
69+
70+
void onResult(NimBLEAdvertisedDevice* advertisedDevice) {
71+
printf("Advertised Device found: %s\n", advertisedDevice->toString().c_str());
72+
if(advertisedDevice->isAdvertisingService(NimBLEUUID("DEAD")))
73+
{
74+
printf("Found Our Service\n");
75+
/** stop scan before connecting */
76+
NimBLEDevice::getScan()->stop();
77+
/** Save the device reference in a global for the client to use*/
78+
advDevice = advertisedDevice;
79+
/** Ready to connect now */
80+
doConnect = true;
81+
}
82+
};
83+
};
84+
85+
86+
/** Notification / Indication receiving handler callback */
87+
void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify){
88+
std::string str = (isNotify == true) ? "Notification" : "Indication";
89+
str += " from ";
90+
str += pRemoteCharacteristic->getRemoteService()->getClient()->getPeerAddress().toString();
91+
str += ": Service = " + pRemoteCharacteristic->getRemoteService()->getUUID().toString();
92+
str += ", Characteristic = " + pRemoteCharacteristic->getUUID().toString();
93+
str += ", Value = " + std::string((char*)pData, length);
94+
printf("%s\n", str.c_str());
95+
}
96+
97+
/** Callback to process the results of the last scan or restart it */
98+
void scanEndedCB(NimBLEScanResults results){
99+
printf("Scan Ended\n");
100+
}
101+
102+
103+
/** Create a single global instance of the callback class to be used by all clients */
104+
static ClientCallbacks clientCB;
105+
106+
107+
/** Handles the provisioning of clients and connects / interfaces with the server */
108+
bool connectToServer() {
109+
NimBLEClient* pClient = nullptr;
110+
111+
/** Check if we have a client we should reuse first **/
112+
if(NimBLEDevice::getClientListSize()) {
113+
/** Special case when we already know this device, we send false as the
114+
* second argument in connect() to prevent refreshing the service database.
115+
* This saves considerable time and power.
116+
*/
117+
pClient = NimBLEDevice::getClientByPeerAddress(advDevice->getAddress());
118+
if(pClient){
119+
if(!pClient->connect(advDevice, false)) {
120+
printf("Reconnect failed\n");
121+
return false;
122+
}
123+
printf("Reconnected client\n");
124+
}
125+
/** We don't already have a client that knows this device,
126+
* we will check for a client that is disconnected that we can use.
127+
*/
128+
else {
129+
pClient = NimBLEDevice::getDisconnectedClient();
130+
}
131+
}
132+
133+
/** No client to reuse? Create a new one. */
134+
if(!pClient) {
135+
if(NimBLEDevice::getClientListSize() >= NIMBLE_MAX_CONNECTIONS) {
136+
printf("Max clients reached - no more connections available\n");
137+
return false;
138+
}
139+
140+
pClient = NimBLEDevice::createClient();
141+
142+
printf("New client created\n");
143+
144+
pClient->setClientCallbacks(&clientCB, false);
145+
/** Set initial connection parameters: These settings are 15ms interval, 0 latency, 120ms timout.
146+
* These settings are safe for 3 clients to connect reliably, can go faster if you have less
147+
* connections. Timeout should be a multiple of the interval, minimum is 100ms.
148+
* Min interval: 12 * 1.25ms = 15, Max interval: 12 * 1.25ms = 15, 0 latency, 12 * 10ms = 120ms timeout
149+
*/
150+
pClient->setConnectionParams(6,6,0,15);
151+
/** Set how long we are willing to wait for the connection to complete (seconds), default is 30. */
152+
pClient->setConnectTimeout(5);
153+
154+
155+
if (!pClient->connect(advDevice)) {
156+
/** Created a client but failed to connect, don't need to keep it as it has no data */
157+
NimBLEDevice::deleteClient(pClient);
158+
printf("Failed to connect, deleted client\n");
159+
return false;
160+
}
161+
}
162+
163+
if(!pClient->isConnected()) {
164+
if (!pClient->connect(advDevice)) {
165+
printf("Failed to connect\n");
166+
return false;
167+
}
168+
}
169+
170+
printf("Connected to: %s RSSI: %d\n",
171+
pClient->getPeerAddress().toString().c_str(),
172+
pClient->getRssi());
173+
174+
/** Now we can read/write/subscribe the charateristics of the services we are interested in */
175+
NimBLERemoteService* pSvc = nullptr;
176+
NimBLERemoteCharacteristic* pChr = nullptr;
177+
NimBLERemoteDescriptor* pDsc = nullptr;
178+
179+
pSvc = pClient->getService("DEAD");
180+
if(pSvc) { /** make sure it's not null */
181+
pChr = pSvc->getCharacteristic("BEEF");
182+
}
183+
184+
if(pChr) { /** make sure it's not null */
185+
if(pChr->canRead()) {
186+
printf("%s Value: %s\n",
187+
pChr->getUUID().toString().c_str(),
188+
pChr->readValue().c_str());
189+
}
190+
191+
if(pChr->canWrite()) {
192+
if(pChr->writeValue("Tasty")) {
193+
printf("Wrote new value to: %s\n", pChr->getUUID().toString().c_str());
194+
}
195+
else {
196+
/** Disconnect if write failed */
197+
pClient->disconnect();
198+
return false;
199+
}
200+
201+
if(pChr->canRead()) {
202+
printf("The value of: %s is now: %s\n",
203+
pChr->getUUID().toString().c_str(),
204+
pChr->readValue().c_str());
205+
}
206+
}
207+
208+
/** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe().
209+
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false.
210+
* Unsubscribe parameter defaults are: response=false.
211+
*/
212+
if(pChr->canNotify()) {
213+
//if(!pChr->registerForNotify(notifyCB)) {
214+
if(!pChr->subscribe(true, notifyCB)) {
215+
/** Disconnect if subscribe failed */
216+
pClient->disconnect();
217+
return false;
218+
}
219+
}
220+
else if(pChr->canIndicate()) {
221+
/** Send false as first argument to subscribe to indications instead of notifications */
222+
//if(!pChr->registerForNotify(notifyCB, false)) {
223+
if(!pChr->subscribe(false, notifyCB)) {
224+
/** Disconnect if subscribe failed */
225+
pClient->disconnect();
226+
return false;
227+
}
228+
}
229+
}
230+
231+
else{
232+
printf("DEAD service not found.\n");
233+
}
234+
235+
pSvc = pClient->getService("BAAD");
236+
if(pSvc) { /** make sure it's not null */
237+
pChr = pSvc->getCharacteristic("F00D");
238+
}
239+
240+
if(pChr) { /** make sure it's not null */
241+
if(pChr->canRead()) {
242+
printf("%s Value: %s\n",
243+
pChr->getUUID().toString().c_str(),
244+
pChr->readValue().c_str());
245+
}
246+
247+
pDsc = pChr->getDescriptor(NimBLEUUID("C01D"));
248+
if(pDsc) { /** make sure it's not null */
249+
printf("Descriptor: %s Value: %s\n",
250+
pDsc->getUUID().toString().c_str(),
251+
pDsc->readValue().c_str());
252+
}
253+
254+
if(pChr->canWrite()) {
255+
if(pChr->writeValue("No tip!")) {
256+
printf("Wrote new value to: %s\n", pChr->getUUID().toString().c_str());
257+
}
258+
else {
259+
/** Disconnect if write failed */
260+
pClient->disconnect();
261+
return false;
262+
}
263+
264+
if(pChr->canRead()) {
265+
printf("The value of: %s is now: %s\n",
266+
pChr->getUUID().toString().c_str(),
267+
pChr->readValue().c_str());
268+
}
269+
}
270+
271+
/** registerForNotify() has been deprecated and replaced with subscribe() / unsubscribe().
272+
* Subscribe parameter defaults are: notifications=true, notifyCallback=nullptr, response=false.
273+
* Unsubscribe parameter defaults are: response=false.
274+
*/
275+
if(pChr->canNotify()) {
276+
//if(!pChr->registerForNotify(notifyCB)) {
277+
if(!pChr->subscribe(true, notifyCB)) {
278+
/** Disconnect if subscribe failed */
279+
pClient->disconnect();
280+
return false;
281+
}
282+
}
283+
else if(pChr->canIndicate()) {
284+
/** Send false as first argument to subscribe to indications instead of notifications */
285+
//if(!pChr->registerForNotify(notifyCB, false)) {
286+
if(!pChr->subscribe(false, notifyCB)) {
287+
/** Disconnect if subscribe failed */
288+
pClient->disconnect();
289+
return false;
290+
}
291+
}
292+
}
293+
294+
else{
295+
printf("BAAD service not found.\n");
296+
}
297+
298+
printf("Done with this device!\n");
299+
return true;
300+
}
301+
302+
void connectTask (void * parameter){
303+
/** Loop here until we find a device we want to connect to */
304+
for(;;) {
305+
if(doConnect) {
306+
doConnect = false;
307+
/** Found a device we want to connect to, do it now */
308+
if(connectToServer()) {
309+
printf("Success! we should now be getting notifications, scanning for more!\n");
310+
} else {
311+
printf("Failed to connect, starting scan\n");
312+
}
313+
314+
NimBLEDevice::getScan()->start(scanTime,scanEndedCB);
315+
}
316+
vTaskDelay(10/portTICK_PERIOD_MS);
317+
}
318+
319+
vTaskDelete(NULL);
320+
}
321+
322+
void app_main (void){
323+
printf("Starting NimBLE Client\n");
324+
/** Initialize NimBLE, no device name spcified as we are not advertising */
325+
NimBLEDevice::init("");
326+
327+
/** Set the IO capabilities of the device, each option will trigger a different pairing method.
328+
* BLE_HS_IO_KEYBOARD_ONLY - Passkey pairing
329+
* BLE_HS_IO_DISPLAY_YESNO - Numeric comparison pairing
330+
* BLE_HS_IO_NO_INPUT_OUTPUT - DEFAULT setting - just works pairing
331+
*/
332+
//NimBLEDevice::setSecurityIOCap(BLE_HS_IO_KEYBOARD_ONLY); // use passkey
333+
//NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_YESNO); //use numeric comparison
334+
335+
/** 2 different ways to set security - both calls achieve the same result.
336+
* no bonding, no man in the middle protection, secure connections.
337+
*
338+
* These are the default values, only shown here for demonstration.
339+
*/
340+
//NimBLEDevice::setSecurityAuth(false, false, true);
341+
NimBLEDevice::setSecurityAuth(/*BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM |*/ BLE_SM_PAIR_AUTHREQ_SC);
342+
343+
/** Optional: set the transmit power, default is -3db */
344+
NimBLEDevice::setPower(ESP_PWR_LVL_P9); /** 12db */
345+
346+
/** Optional: set any devices you don't want to get advertisments from */
347+
// NimBLEDevice::addIgnored(NimBLEAddress ("aa:bb:cc:dd:ee:ff"));
348+
349+
/** create new scan */
350+
NimBLEScan* pScan = NimBLEDevice::getScan();
351+
352+
/** create a callback that gets called when advertisers are found */
353+
pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());
354+
355+
/** Set scan interval (how often) and window (how long) in milliseconds */
356+
pScan->setInterval(400);
357+
pScan->setWindow(100);
358+
359+
/** Active scan will gather scan response data from advertisers
360+
* but will use more energy from both devices
361+
*/
362+
pScan->setActiveScan(true);
363+
/** Start scanning for advertisers for the scan time specified (in seconds) 0 = forever
364+
* Optional callback for when scanning stops.
365+
*/
366+
pScan->start(scanTime, scanEndedCB);
367+
368+
printf("Scanning for peripherals\n");
369+
370+
xTaskCreate(connectTask, "connectTask", 5000, NULL, 1, NULL);
371+
}
372+

0 commit comments

Comments
 (0)