1*8caefee3SMatthias Ringwald#include <BTstack.h> 2*8caefee3SMatthias Ringwald#include <SPI.h> 3*8caefee3SMatthias Ringwald 4*8caefee3SMatthias Ringwald/* 5*8caefee3SMatthias Ringwald * EXAMPLE_START(LECentral): LE Central 6*8caefee3SMatthias Ringwald * 7*8caefee3SMatthias Ringwald * @text Compared with the other examples, the LE Central is 8*8caefee3SMatthias Ringwald * a bit more complex. This is because it performs multiple 9*8caefee3SMatthias Ringwald * steps in sequence as it is common with GATT Client APIs. 10*8caefee3SMatthias Ringwald * 11*8caefee3SMatthias Ringwald * It shows how to first scan for other 12*8caefee3SMatthias Ringwald * devices and then connect to one. When connected, a series of 13*8caefee3SMatthias Ringwald * GATT Client operations are performed: first the list of 14*8caefee3SMatthias Ringwald * GATT Services is queried. If a particular service is found, 15*8caefee3SMatthias Ringwald * the list of its GATT Characteristics is retrieved and a set 16*8caefee3SMatthias Ringwald * of known Characteristics are cached for later access. 17*8caefee3SMatthias Ringwald */ 18*8caefee3SMatthias Ringwald 19*8caefee3SMatthias Ringwald/* 20*8caefee3SMatthias Ringwald * @section Characteristic Summary 21*8caefee3SMatthias Ringwald * @text As multiple Characteristics need to be found, a custom 22*8caefee3SMatthias Ringwald * struct is used to collect all information about it. This allows 23*8caefee3SMatthias Ringwald * to defined the list of neccessary characteristics in the 24*8caefee3SMatthias Ringwald * characteristics[] array 25*8caefee3SMatthias Ringwald */ 26*8caefee3SMatthias Ringwald/* LISTING_START(LECentralSummary): Characteristic Summary */ 27*8caefee3SMatthias Ringwald 28*8caefee3SMatthias Ringwald// BLE Shield Service V2 incl. used Characteristics 29*8caefee3SMatthias RingwaldUUID bleShieldServiceV2UUID("B8E06067-62AD-41BA-9231-206AE80AB550"); 30*8caefee3SMatthias Ringwald 31*8caefee3SMatthias Ringwaldtypedef struct characteristic_summary { 32*8caefee3SMatthias Ringwald UUID uuid; 33*8caefee3SMatthias Ringwald const char * name; 34*8caefee3SMatthias Ringwald bool found; 35*8caefee3SMatthias Ringwald BLECharacteristic characteristic; 36*8caefee3SMatthias Ringwald} characteristic_summary_t; 37*8caefee3SMatthias Ringwald 38*8caefee3SMatthias Ringwaldtypedef enum characteristicIDs { 39*8caefee3SMatthias Ringwald charRX = 0, 40*8caefee3SMatthias Ringwald charTX, 41*8caefee3SMatthias Ringwald charBaud, 42*8caefee3SMatthias Ringwald charBdAddr, 43*8caefee3SMatthias Ringwald numCharacteristics /* last one */ 44*8caefee3SMatthias Ringwald} characteristicIDs_t; 45*8caefee3SMatthias Ringwald 46*8caefee3SMatthias Ringwaldcharacteristic_summary characteristics[] = { 47*8caefee3SMatthias Ringwald { UUID("f897177b-aee8-4767-8ecc-cc694fd5fcee"), "RX" }, 48*8caefee3SMatthias Ringwald { UUID("bf45e40a-de2a-4bc8-bba0-e5d6065f1b4b"), "TX" }, 49*8caefee3SMatthias Ringwald { UUID("2fbc0f31-726a-4014-b9fe-c8be0652e982"), "Baudrate" }, 50*8caefee3SMatthias Ringwald { UUID("65c228da-bad1-4f41-b55f-3d177f4e2196"), "BD ADDR" } 51*8caefee3SMatthias Ringwald}; 52*8caefee3SMatthias Ringwald 53*8caefee3SMatthias Ringwald/* LISTING_END(LECentralSummary): Characteristic Summary */ 54*8caefee3SMatthias Ringwald 55*8caefee3SMatthias Ringwald// Application state 56*8caefee3SMatthias RingwaldBLEDevice myBLEDevice; 57*8caefee3SMatthias RingwaldBLEService myBLEService; 58*8caefee3SMatthias Ringwaldbool serviceFound; 59*8caefee3SMatthias Ringwaldbool sendCounter = false; 60*8caefee3SMatthias Ringwald 61*8caefee3SMatthias Ringwaldint counter = 0; 62*8caefee3SMatthias Ringwaldchar counterString[20]; 63*8caefee3SMatthias Ringwald 64*8caefee3SMatthias Ringwaldstatic timer_source_t heartbeat; 65*8caefee3SMatthias Ringwald 66*8caefee3SMatthias Ringwald/* 67*8caefee3SMatthias Ringwald * @section Setup 68*8caefee3SMatthias Ringwald * @text In the setup, various callbacks are registered. After that 69*8caefee3SMatthias Ringwald * we start scanning for other devices 70*8caefee3SMatthias Ringwald */ 71*8caefee3SMatthias Ringwald/* LISTING_START(LECentralSetup): LE Central Setup */ 72*8caefee3SMatthias Ringwald void setup(void){ 73*8caefee3SMatthias Ringwald Serial.begin(9600); 74*8caefee3SMatthias Ringwald BTstack.setBLEAdvertisementCallback(advertisementCallback); 75*8caefee3SMatthias Ringwald BTstack.setBLEDeviceConnectedCallback(deviceConnectedCallback); 76*8caefee3SMatthias Ringwald BTstack.setBLEDeviceDisconnectedCallback(deviceDisconnectedCallback); 77*8caefee3SMatthias Ringwald BTstack.setGATTServiceDiscoveredCallback(gattServiceDiscovered); 78*8caefee3SMatthias Ringwald BTstack.setGATTCharacteristicDiscoveredCallback(gattCharacteristicDiscovered); 79*8caefee3SMatthias Ringwald BTstack.setGATTCharacteristicNotificationCallback(gattCharacteristicNotification); 80*8caefee3SMatthias Ringwald BTstack.setGATTCharacteristicReadCallback(gattReadCallback); 81*8caefee3SMatthias Ringwald BTstack.setGATTCharacteristicWrittenCallback(gattWrittenCallback); 82*8caefee3SMatthias Ringwald BTstack.setGATTCharacteristicSubscribedCallback(gattSubscribedCallback); 83*8caefee3SMatthias Ringwald BTstack.setup(); 84*8caefee3SMatthias Ringwald BTstack.bleStartScanning(); 85*8caefee3SMatthias Ringwald} 86*8caefee3SMatthias Ringwald/* LISTING_END(LECentralSetup): LE Central Setup */ 87*8caefee3SMatthias Ringwald 88*8caefee3SMatthias Ringwald/* 89*8caefee3SMatthias Ringwald * @section Loop 90*8caefee3SMatthias Ringwald * 91*8caefee3SMatthias Ringwald * @text In the standard Arduino loop() function, BTstack's loop() is called first 92*8caefee3SMatthias Ringwald * If we're connected, we send the string "BTstack" plus a counter as fast as possible. 93*8caefee3SMatthias Ringwald * As the Bluetooth module might be busy, it's important to check the result of the 94*8caefee3SMatthias Ringwald * writeCharacteristicWithoutResponse() call. If it's not ok, we just try again in the 95*8caefee3SMatthias Ringwald * next loop iteration. 96*8caefee3SMatthias Ringwald */ 97*8caefee3SMatthias Ringwald/* LISTING_START(LECentralLoop): Loop */ 98*8caefee3SMatthias Ringwaldvoid loop(void){ 99*8caefee3SMatthias Ringwald BTstack.loop(); 100*8caefee3SMatthias Ringwald 101*8caefee3SMatthias Ringwald // send counter as fast as possible 102*8caefee3SMatthias Ringwald if (sendCounter){ 103*8caefee3SMatthias Ringwald sprintf(counterString, "BTstack %u\n", counter); 104*8caefee3SMatthias Ringwald int result = myBLEDevice.writeCharacteristicWithoutResponse(&characteristics[charTX].characteristic, (uint8_t*) counterString, strlen(counterString) ); 105*8caefee3SMatthias Ringwald if (result == BLE_PERIPHERAL_OK){ 106*8caefee3SMatthias Ringwald Serial.print("Wrote without response: "); 107*8caefee3SMatthias Ringwald Serial.println(counterString); 108*8caefee3SMatthias Ringwald counter++; 109*8caefee3SMatthias Ringwald } 110*8caefee3SMatthias Ringwald } 111*8caefee3SMatthias Ringwald} 112*8caefee3SMatthias Ringwald/* LISTING_END(LECentralLoop): Loop */ 113*8caefee3SMatthias Ringwald 114*8caefee3SMatthias Ringwald/* 115*8caefee3SMatthias Ringwald * @section Advertisement Callback 116*8caefee3SMatthias Ringwald * 117*8caefee3SMatthias Ringwald * @text When an Advertisement is received, we check if it contains 118*8caefee3SMatthias Ringwald * the UUID of the service we're interested in. Only a single service 119*8caefee3SMatthias Ringwald * with a 128-bit UUID can be contained in and Advertisement and not 120*8caefee3SMatthias Ringwald * all BLE devices provides this. Other options are to match on the 121*8caefee3SMatthias Ringwald * reported device name or the BD ADDR prefix. 122*8caefee3SMatthias Ringwald * 123*8caefee3SMatthias Ringwald * If we found an interesting device, we try to connect to it. 124*8caefee3SMatthias Ringwald */ 125*8caefee3SMatthias Ringwald/* LISTING_START(LECentralAdvertisementCallback): Advertisement Callback */ 126*8caefee3SMatthias Ringwaldvoid advertisementCallback(BLEAdvertisement *bleAdvertisement) { 127*8caefee3SMatthias Ringwald Serial.print("Device discovered: "); 128*8caefee3SMatthias Ringwald Serial.print(bleAdvertisement->getBdAddr()->getAddressString()); 129*8caefee3SMatthias Ringwald Serial.print(", RSSI: "); 130*8caefee3SMatthias Ringwald Serial.println(bleAdvertisement->getRssi()); 131*8caefee3SMatthias Ringwald if (bleAdvertisement->containsService(&bleShieldServiceV2UUID)) { 132*8caefee3SMatthias Ringwald Serial.println("\nBLE ShieldService V2 found!\n"); 133*8caefee3SMatthias Ringwald BTstack.bleStopScanning(); 134*8caefee3SMatthias Ringwald BTstack.bleConnect(bleAdvertisement, 10000); // 10 s 135*8caefee3SMatthias Ringwald } 136*8caefee3SMatthias Ringwald} 137*8caefee3SMatthias Ringwald/* LISTING_END(LECentralAdvertisementCallback): Advertisement Callback */ 138*8caefee3SMatthias Ringwald 139*8caefee3SMatthias Ringwald/* 140*8caefee3SMatthias Ringwald * @section Device Connected Callback 141*8caefee3SMatthias Ringwald * 142*8caefee3SMatthias Ringwald * @text At the end of bleConnect(), the device connected callback is callec. 143*8caefee3SMatthias Ringwald * The status argument tells if the connection timed out, or if the connection 144*8caefee3SMatthias Ringwald * was established successfully. 145*8caefee3SMatthias Ringwald * 146*8caefee3SMatthias Ringwald * On a successful connection, a GATT Service Discovery is started. 147*8caefee3SMatthias Ringwald */ 148*8caefee3SMatthias Ringwald/* LISTING_START(LECentralDeviceConnectedCallback): Device Connected Callback */ 149*8caefee3SMatthias Ringwaldvoid deviceConnectedCallback(BLEStatus status, BLEDevice *device) { 150*8caefee3SMatthias Ringwald switch (status){ 151*8caefee3SMatthias Ringwald case BLE_STATUS_OK: 152*8caefee3SMatthias Ringwald Serial.println("Device connected!"); 153*8caefee3SMatthias Ringwald myBLEDevice = *device; 154*8caefee3SMatthias Ringwald counter = 0; 155*8caefee3SMatthias Ringwald myBLEDevice.discoverGATTServices(); 156*8caefee3SMatthias Ringwald break; 157*8caefee3SMatthias Ringwald case BLE_STATUS_CONNECTION_TIMEOUT: 158*8caefee3SMatthias Ringwald Serial.println("Error while Connecting the Peripheral"); 159*8caefee3SMatthias Ringwald BTstack.bleStartScanning(); 160*8caefee3SMatthias Ringwald break; 161*8caefee3SMatthias Ringwald default: 162*8caefee3SMatthias Ringwald break; 163*8caefee3SMatthias Ringwald } 164*8caefee3SMatthias Ringwald} 165*8caefee3SMatthias Ringwald/* LISTING_END(LECentralDeviceConnectedCallback): Device Connected Callback */ 166*8caefee3SMatthias Ringwald 167*8caefee3SMatthias Ringwald/* 168*8caefee3SMatthias Ringwald * @section Device Disconnected Callback 169*8caefee3SMatthias Ringwald * 170*8caefee3SMatthias Ringwald * @text If the connection to a device breaks, the device disconnected callback 171*8caefee3SMatthias Ringwald * is called. Here, we start scanning for new devices again. 172*8caefee3SMatthias Ringwald */ 173*8caefee3SMatthias Ringwald/* LISTING_START(LECentralDeviceDisconnectedCallback): Device Disconnected Callback */ 174*8caefee3SMatthias Ringwaldvoid deviceDisconnectedCallback(BLEDevice * device){ 175*8caefee3SMatthias Ringwald Serial.println("Disconnected, starting over.."); 176*8caefee3SMatthias Ringwald sendCounter = false; 177*8caefee3SMatthias Ringwald BTstack.bleStartScanning(); 178*8caefee3SMatthias Ringwald} 179*8caefee3SMatthias Ringwald/* LISTING_END(LECentralDeviceDisconnectedCallback): Device Disconnected Callback */ 180*8caefee3SMatthias Ringwald 181*8caefee3SMatthias Ringwald/* 182*8caefee3SMatthias Ringwald * @section Service Discovered Callback 183*8caefee3SMatthias Ringwald * 184*8caefee3SMatthias Ringwald * @text The service discovered callback is called for each service and after the 185*8caefee3SMatthias Ringwald * service discovery is complete. The status argument is provided for this. 186*8caefee3SMatthias Ringwald * 187*8caefee3SMatthias Ringwald * The main information about a discovered Service is its UUID. 188*8caefee3SMatthias Ringwald * If we find our service, we store the reference to this service. 189*8caefee3SMatthias Ringwald * This allows to discover the Characteristics for our service after 190*8caefee3SMatthias Ringwald * the service discovery is complete. 191*8caefee3SMatthias Ringwald */ 192*8caefee3SMatthias Ringwald/* LISTING_START(LECentralServiceDiscoveredCallback): Service Discovered Callback */ 193*8caefee3SMatthias Ringwaldvoid gattServiceDiscovered(BLEStatus status, BLEDevice *device, BLEService *bleService) { 194*8caefee3SMatthias Ringwald switch(status){ 195*8caefee3SMatthias Ringwald case BLE_STATUS_OK: 196*8caefee3SMatthias Ringwald Serial.print("Service Discovered: :"); 197*8caefee3SMatthias Ringwald Serial.println(bleService->getUUID()->getUuidString()); 198*8caefee3SMatthias Ringwald if (bleService->matches(&bleShieldServiceV2UUID)) { 199*8caefee3SMatthias Ringwald serviceFound = true; 200*8caefee3SMatthias Ringwald Serial.println("Our service located!"); 201*8caefee3SMatthias Ringwald myBLEService = *bleService; 202*8caefee3SMatthias Ringwald } 203*8caefee3SMatthias Ringwald break; 204*8caefee3SMatthias Ringwald case BLE_STATUS_DONE: 205*8caefee3SMatthias Ringwald Serial.println("Service discovery finished"); 206*8caefee3SMatthias Ringwald if (serviceFound) { 207*8caefee3SMatthias Ringwald device->discoverCharacteristicsForService(&myBLEService); 208*8caefee3SMatthias Ringwald } 209*8caefee3SMatthias Ringwald break; 210*8caefee3SMatthias Ringwald default: 211*8caefee3SMatthias Ringwald Serial.println("Service discovery error"); 212*8caefee3SMatthias Ringwald break; 213*8caefee3SMatthias Ringwald } 214*8caefee3SMatthias Ringwald} 215*8caefee3SMatthias Ringwald/* LISTING_END(LECentralServiceDiscoveredCallback): Service Discovered Callback */ 216*8caefee3SMatthias Ringwald 217*8caefee3SMatthias Ringwald/* 218*8caefee3SMatthias Ringwald * @section Characteristic Discovered Callback 219*8caefee3SMatthias Ringwald * 220*8caefee3SMatthias Ringwald * @text Similar to the Service Discovered callback, the Characteristic Discovered 221*8caefee3SMatthias Ringwald * callback is called for each Characteristic found and after the discovery is complete. 222*8caefee3SMatthias Ringwald * 223*8caefee3SMatthias Ringwald * The main information is again its UUID. If we find a Characteristic that we're 224*8caefee3SMatthias Ringwald * interested in, it's name is printed and a reference stored for later. 225*8caefee3SMatthias Ringwald * 226*8caefee3SMatthias Ringwald * On discovery complete, we subscribe to a particular Characteristic to receive 227*8caefee3SMatthias Ringwald * Characteristic Value updates in the Notificaation Callback. 228*8caefee3SMatthias Ringwald */ 229*8caefee3SMatthias Ringwald/* LISTING_START(LECentralCharacteristicDiscoveredCallback): Characteristic Discovered Callback */ 230*8caefee3SMatthias Ringwaldvoid gattCharacteristicDiscovered(BLEStatus status, BLEDevice *device, BLECharacteristic *characteristic) { 231*8caefee3SMatthias Ringwald switch(status){ 232*8caefee3SMatthias Ringwald case BLE_STATUS_OK: 233*8caefee3SMatthias Ringwald Serial.print("Characteristic Discovered: "); 234*8caefee3SMatthias Ringwald Serial.print(characteristic->getUUID()->getUuidString()); 235*8caefee3SMatthias Ringwald Serial.print(", handle 0x"); 236*8caefee3SMatthias Ringwald Serial.println(characteristic->getCharacteristic()->value_handle, HEX); 237*8caefee3SMatthias Ringwald int i; 238*8caefee3SMatthias Ringwald for (i=0;i<numCharacteristics;i++){ 239*8caefee3SMatthias Ringwald if (characteristic->matches(&characteristics[i].uuid)){ 240*8caefee3SMatthias Ringwald Serial.print("Characteristic found: "); 241*8caefee3SMatthias Ringwald Serial.println(characteristics[i].name); 242*8caefee3SMatthias Ringwald characteristics[i].found = 1; 243*8caefee3SMatthias Ringwald characteristics[i].characteristic = *characteristic; 244*8caefee3SMatthias Ringwald break; 245*8caefee3SMatthias Ringwald } 246*8caefee3SMatthias Ringwald } 247*8caefee3SMatthias Ringwald break; 248*8caefee3SMatthias Ringwald case BLE_STATUS_DONE: 249*8caefee3SMatthias Ringwald Serial.print("Characteristic discovery finished, status "); 250*8caefee3SMatthias Ringwald Serial.println(status, HEX); 251*8caefee3SMatthias Ringwald if (characteristics[charRX].found) { 252*8caefee3SMatthias Ringwald device->subscribeForNotifications(&characteristics[charRX].characteristic); 253*8caefee3SMatthias Ringwald } 254*8caefee3SMatthias Ringwald break; 255*8caefee3SMatthias Ringwald default: 256*8caefee3SMatthias Ringwald Serial.println("Characteristics discovery error"); 257*8caefee3SMatthias Ringwald break; 258*8caefee3SMatthias Ringwald } 259*8caefee3SMatthias Ringwald} 260*8caefee3SMatthias Ringwald/* LISTING_END(LECentralCharacteristicDiscoveredCallback): Characteristic Discovered Callback */ 261*8caefee3SMatthias Ringwald 262*8caefee3SMatthias Ringwald/* 263*8caefee3SMatthias Ringwald * @section Subscribed Callback 264*8caefee3SMatthias Ringwald * 265*8caefee3SMatthias Ringwald * @text After the subcribe operation is complete, we get notified if it was 266*8caefee3SMatthias Ringwald * successful. In this example, we read the Characteristic that contains the 267*8caefee3SMatthias Ringwald * BD ADDR of the other device. This isn't strictly neccessary as we already 268*8caefee3SMatthias Ringwald * know the device address from the Advertisement, but it's a common pattern 269*8caefee3SMatthias Ringwald * with iOS as the device address is hidden from applications. 270*8caefee3SMatthias Ringwald */ 271*8caefee3SMatthias Ringwald/* LISTING_START(LECentralSubscribedCallback): Subscribed Callback */ 272*8caefee3SMatthias Ringwaldvoid gattSubscribedCallback(BLEStatus status, BLEDevice * device){ 273*8caefee3SMatthias Ringwald device->readCharacteristic(&characteristics[charBdAddr].characteristic); 274*8caefee3SMatthias Ringwald} 275*8caefee3SMatthias Ringwald/* LISTING_END(LECentralSubscribedCallback): Subscribed Callback */ 276*8caefee3SMatthias Ringwald 277*8caefee3SMatthias Ringwald/* 278*8caefee3SMatthias Ringwald * @section Read Callback 279*8caefee3SMatthias Ringwald * 280*8caefee3SMatthias Ringwald * @text The Read callback is called with the result from a read operation. 281*8caefee3SMatthias Ringwald * Here, we write to the TX Characteristic next. 282*8caefee3SMatthias Ringwald */ 283*8caefee3SMatthias Ringwald/* LISTING_START(LECentralReadCallback): Read Callback */ 284*8caefee3SMatthias Ringwaldvoid gattReadCallback(BLEStatus status, BLEDevice *device, uint8_t *value, uint16_t length) { 285*8caefee3SMatthias Ringwald Serial.print("Read callback: "); 286*8caefee3SMatthias Ringwald Serial.println((const char *)value); 287*8caefee3SMatthias Ringwald device->writeCharacteristic(&characteristics[charTX].characteristic, (uint8_t*) "Hello!", 6); 288*8caefee3SMatthias Ringwald} 289*8caefee3SMatthias Ringwald/* LISTING_END(LECentralReadCallback): Read Callback */ 290*8caefee3SMatthias Ringwald 291*8caefee3SMatthias Ringwald/* 292*8caefee3SMatthias Ringwald * @section Written Callback 293*8caefee3SMatthias Ringwald * 294*8caefee3SMatthias Ringwald * @text After the write operation is complete, the Written Callback is callbed with 295*8caefee3SMatthias Ringwald * the result in the status argument. As we're done with the initial setup of the remote 296*8caefee3SMatthias Ringwald * device, we set the flag to write the test string as fast as possible. 297*8caefee3SMatthias Ringwald */ 298*8caefee3SMatthias Ringwald/* LISTING_START(LECentralWrittenCallback): Written Callback */ 299*8caefee3SMatthias Ringwaldvoid gattWrittenCallback(BLEStatus status, BLEDevice *device){ 300*8caefee3SMatthias Ringwald sendCounter = true; 301*8caefee3SMatthias Ringwald} 302*8caefee3SMatthias Ringwald/* LISTING_END(LECentralWrittenCallback): Written Callback */ 303*8caefee3SMatthias Ringwald 304*8caefee3SMatthias Ringwald/* 305*8caefee3SMatthias Ringwald * @section Notification Callback 306*8caefee3SMatthias Ringwald * 307*8caefee3SMatthias Ringwald * @text Notifictions for Characteristic Value Updates are delivered via the 308*8caefee3SMatthias Ringwald * Notification Callback. When more than one Characteristic is subscribed, 309*8caefee3SMatthias Ringwald * the value handle can be used to distinguish between them. The 310*8caefee3SMatthias Ringwald * BLECharacteristic.isValueHandle(int handle) allows to test if a value handle 311*8caefee3SMatthias Ringwald * belongs to a particular Characteristic. 312*8caefee3SMatthias Ringwald */ 313*8caefee3SMatthias Ringwald/* LISTING_START(LECentralNotificationCallback): Notification Callback */ 314*8caefee3SMatthias Ringwaldvoid gattCharacteristicNotification(BLEDevice *device, uint16_t value_handle, uint8_t *value, uint16_t length) { 315*8caefee3SMatthias Ringwald Serial.print("Notification: "); 316*8caefee3SMatthias Ringwald Serial.println((const char *)value); 317*8caefee3SMatthias Ringwald} 318*8caefee3SMatthias Ringwald/* LISTING_END(LECentralNotificationCallback): Notification Callback */ 319*8caefee3SMatthias Ringwald 320