18caefee3SMatthias Ringwald#include <BTstack.h> 28caefee3SMatthias Ringwald#include <SPI.h> 38caefee3SMatthias Ringwald 48caefee3SMatthias Ringwald/* 58caefee3SMatthias Ringwald * EXAMPLE_START(LECentral): LE Central 68caefee3SMatthias Ringwald * 78caefee3SMatthias Ringwald * @text Compared with the other examples, the LE Central is 88caefee3SMatthias Ringwald * a bit more complex. This is because it performs multiple 98caefee3SMatthias Ringwald * steps in sequence as it is common with GATT Client APIs. 108caefee3SMatthias Ringwald * 118caefee3SMatthias Ringwald * It shows how to first scan for other 128caefee3SMatthias Ringwald * devices and then connect to one. When connected, a series of 138caefee3SMatthias Ringwald * GATT Client operations are performed: first the list of 148caefee3SMatthias Ringwald * GATT Services is queried. If a particular service is found, 158caefee3SMatthias Ringwald * the list of its GATT Characteristics is retrieved and a set 168caefee3SMatthias Ringwald * of known Characteristics are cached for later access. 178caefee3SMatthias Ringwald */ 188caefee3SMatthias Ringwald 198caefee3SMatthias Ringwald/* 208caefee3SMatthias Ringwald * @section Characteristic Summary 218caefee3SMatthias Ringwald * @text As multiple Characteristics need to be found, a custom 228caefee3SMatthias Ringwald * struct is used to collect all information about it. This allows 238caefee3SMatthias Ringwald * to defined the list of neccessary characteristics in the 248caefee3SMatthias Ringwald * characteristics[] array 258caefee3SMatthias Ringwald */ 268caefee3SMatthias Ringwald/* LISTING_START(LECentralSummary): Characteristic Summary */ 278caefee3SMatthias Ringwald 288caefee3SMatthias Ringwald// BLE Shield Service V2 incl. used Characteristics 298caefee3SMatthias RingwaldUUID bleShieldServiceV2UUID("B8E06067-62AD-41BA-9231-206AE80AB550"); 308caefee3SMatthias Ringwald 318caefee3SMatthias Ringwaldtypedef struct characteristic_summary { 328caefee3SMatthias Ringwald UUID uuid; 338caefee3SMatthias Ringwald const char * name; 348caefee3SMatthias Ringwald bool found; 358caefee3SMatthias Ringwald BLECharacteristic characteristic; 368caefee3SMatthias Ringwald} characteristic_summary_t; 378caefee3SMatthias Ringwald 388caefee3SMatthias Ringwaldtypedef enum characteristicIDs { 398caefee3SMatthias Ringwald charRX = 0, 408caefee3SMatthias Ringwald charTX, 418caefee3SMatthias Ringwald charBaud, 428caefee3SMatthias Ringwald charBdAddr, 438caefee3SMatthias Ringwald numCharacteristics /* last one */ 448caefee3SMatthias Ringwald} characteristicIDs_t; 458caefee3SMatthias Ringwald 468caefee3SMatthias Ringwaldcharacteristic_summary characteristics[] = { 478caefee3SMatthias Ringwald { UUID("f897177b-aee8-4767-8ecc-cc694fd5fcee"), "RX" }, 488caefee3SMatthias Ringwald { UUID("bf45e40a-de2a-4bc8-bba0-e5d6065f1b4b"), "TX" }, 498caefee3SMatthias Ringwald { UUID("2fbc0f31-726a-4014-b9fe-c8be0652e982"), "Baudrate" }, 508caefee3SMatthias Ringwald { UUID("65c228da-bad1-4f41-b55f-3d177f4e2196"), "BD ADDR" } 518caefee3SMatthias Ringwald}; 528caefee3SMatthias Ringwald 538caefee3SMatthias Ringwald/* LISTING_END(LECentralSummary): Characteristic Summary */ 548caefee3SMatthias Ringwald 558caefee3SMatthias Ringwald// Application state 568caefee3SMatthias RingwaldBLEDevice myBLEDevice; 578caefee3SMatthias RingwaldBLEService myBLEService; 588caefee3SMatthias Ringwaldbool serviceFound; 598caefee3SMatthias Ringwaldbool sendCounter = false; 608caefee3SMatthias Ringwald 618caefee3SMatthias Ringwaldint counter = 0; 628caefee3SMatthias Ringwaldchar counterString[20]; 638caefee3SMatthias Ringwald 64*ec820d77SMatthias Ringwaldstatic btstack_timer_source_t heartbeat; 658caefee3SMatthias Ringwald 668caefee3SMatthias Ringwald/* 678caefee3SMatthias Ringwald * @section Setup 688caefee3SMatthias Ringwald * @text In the setup, various callbacks are registered. After that 698caefee3SMatthias Ringwald * we start scanning for other devices 708caefee3SMatthias Ringwald */ 718caefee3SMatthias Ringwald/* LISTING_START(LECentralSetup): LE Central Setup */ 728caefee3SMatthias Ringwald void setup(void){ 738caefee3SMatthias Ringwald Serial.begin(9600); 748caefee3SMatthias Ringwald BTstack.setBLEAdvertisementCallback(advertisementCallback); 758caefee3SMatthias Ringwald BTstack.setBLEDeviceConnectedCallback(deviceConnectedCallback); 768caefee3SMatthias Ringwald BTstack.setBLEDeviceDisconnectedCallback(deviceDisconnectedCallback); 778caefee3SMatthias Ringwald BTstack.setGATTServiceDiscoveredCallback(gattServiceDiscovered); 788caefee3SMatthias Ringwald BTstack.setGATTCharacteristicDiscoveredCallback(gattCharacteristicDiscovered); 798caefee3SMatthias Ringwald BTstack.setGATTCharacteristicNotificationCallback(gattCharacteristicNotification); 808caefee3SMatthias Ringwald BTstack.setGATTCharacteristicReadCallback(gattReadCallback); 818caefee3SMatthias Ringwald BTstack.setGATTCharacteristicWrittenCallback(gattWrittenCallback); 828caefee3SMatthias Ringwald BTstack.setGATTCharacteristicSubscribedCallback(gattSubscribedCallback); 838caefee3SMatthias Ringwald BTstack.setup(); 848caefee3SMatthias Ringwald BTstack.bleStartScanning(); 858caefee3SMatthias Ringwald} 868caefee3SMatthias Ringwald/* LISTING_END(LECentralSetup): LE Central Setup */ 878caefee3SMatthias Ringwald 888caefee3SMatthias Ringwald/* 898caefee3SMatthias Ringwald * @section Loop 908caefee3SMatthias Ringwald * 918caefee3SMatthias Ringwald * @text In the standard Arduino loop() function, BTstack's loop() is called first 928caefee3SMatthias Ringwald * If we're connected, we send the string "BTstack" plus a counter as fast as possible. 938caefee3SMatthias Ringwald * As the Bluetooth module might be busy, it's important to check the result of the 948caefee3SMatthias Ringwald * writeCharacteristicWithoutResponse() call. If it's not ok, we just try again in the 958caefee3SMatthias Ringwald * next loop iteration. 968caefee3SMatthias Ringwald */ 978caefee3SMatthias Ringwald/* LISTING_START(LECentralLoop): Loop */ 988caefee3SMatthias Ringwaldvoid loop(void){ 998caefee3SMatthias Ringwald BTstack.loop(); 1008caefee3SMatthias Ringwald 1018caefee3SMatthias Ringwald // send counter as fast as possible 1028caefee3SMatthias Ringwald if (sendCounter){ 1038caefee3SMatthias Ringwald sprintf(counterString, "BTstack %u\n", counter); 1048caefee3SMatthias Ringwald int result = myBLEDevice.writeCharacteristicWithoutResponse(&characteristics[charTX].characteristic, (uint8_t*) counterString, strlen(counterString) ); 105616edd56SMatthias Ringwald if (result == 0){ 1068caefee3SMatthias Ringwald Serial.print("Wrote without response: "); 1078caefee3SMatthias Ringwald Serial.println(counterString); 1088caefee3SMatthias Ringwald counter++; 1098caefee3SMatthias Ringwald } 1108caefee3SMatthias Ringwald } 1118caefee3SMatthias Ringwald} 1128caefee3SMatthias Ringwald/* LISTING_END(LECentralLoop): Loop */ 1138caefee3SMatthias Ringwald 1148caefee3SMatthias Ringwald/* 1158caefee3SMatthias Ringwald * @section Advertisement Callback 1168caefee3SMatthias Ringwald * 1178caefee3SMatthias Ringwald * @text When an Advertisement is received, we check if it contains 1188caefee3SMatthias Ringwald * the UUID of the service we're interested in. Only a single service 1198caefee3SMatthias Ringwald * with a 128-bit UUID can be contained in and Advertisement and not 1208caefee3SMatthias Ringwald * all BLE devices provides this. Other options are to match on the 1218caefee3SMatthias Ringwald * reported device name or the BD ADDR prefix. 1228caefee3SMatthias Ringwald * 1238caefee3SMatthias Ringwald * If we found an interesting device, we try to connect to it. 1248caefee3SMatthias Ringwald */ 1258caefee3SMatthias Ringwald/* LISTING_START(LECentralAdvertisementCallback): Advertisement Callback */ 1268caefee3SMatthias Ringwaldvoid advertisementCallback(BLEAdvertisement *bleAdvertisement) { 1278caefee3SMatthias Ringwald Serial.print("Device discovered: "); 1288caefee3SMatthias Ringwald Serial.print(bleAdvertisement->getBdAddr()->getAddressString()); 1298caefee3SMatthias Ringwald Serial.print(", RSSI: "); 1308caefee3SMatthias Ringwald Serial.println(bleAdvertisement->getRssi()); 1318caefee3SMatthias Ringwald if (bleAdvertisement->containsService(&bleShieldServiceV2UUID)) { 1328caefee3SMatthias Ringwald Serial.println("\nBLE ShieldService V2 found!\n"); 1338caefee3SMatthias Ringwald BTstack.bleStopScanning(); 1348caefee3SMatthias Ringwald BTstack.bleConnect(bleAdvertisement, 10000); // 10 s 1358caefee3SMatthias Ringwald } 1368caefee3SMatthias Ringwald} 1378caefee3SMatthias Ringwald/* LISTING_END(LECentralAdvertisementCallback): Advertisement Callback */ 1388caefee3SMatthias Ringwald 1398caefee3SMatthias Ringwald/* 1408caefee3SMatthias Ringwald * @section Device Connected Callback 1418caefee3SMatthias Ringwald * 1428caefee3SMatthias Ringwald * @text At the end of bleConnect(), the device connected callback is callec. 1438caefee3SMatthias Ringwald * The status argument tells if the connection timed out, or if the connection 1448caefee3SMatthias Ringwald * was established successfully. 1458caefee3SMatthias Ringwald * 1468caefee3SMatthias Ringwald * On a successful connection, a GATT Service Discovery is started. 1478caefee3SMatthias Ringwald */ 1488caefee3SMatthias Ringwald/* LISTING_START(LECentralDeviceConnectedCallback): Device Connected Callback */ 1498caefee3SMatthias Ringwaldvoid deviceConnectedCallback(BLEStatus status, BLEDevice *device) { 1508caefee3SMatthias Ringwald switch (status){ 1518caefee3SMatthias Ringwald case BLE_STATUS_OK: 1528caefee3SMatthias Ringwald Serial.println("Device connected!"); 1538caefee3SMatthias Ringwald myBLEDevice = *device; 1548caefee3SMatthias Ringwald counter = 0; 1558caefee3SMatthias Ringwald myBLEDevice.discoverGATTServices(); 1568caefee3SMatthias Ringwald break; 1578caefee3SMatthias Ringwald case BLE_STATUS_CONNECTION_TIMEOUT: 1588caefee3SMatthias Ringwald Serial.println("Error while Connecting the Peripheral"); 1598caefee3SMatthias Ringwald BTstack.bleStartScanning(); 1608caefee3SMatthias Ringwald break; 1618caefee3SMatthias Ringwald default: 1628caefee3SMatthias Ringwald break; 1638caefee3SMatthias Ringwald } 1648caefee3SMatthias Ringwald} 1658caefee3SMatthias Ringwald/* LISTING_END(LECentralDeviceConnectedCallback): Device Connected Callback */ 1668caefee3SMatthias Ringwald 1678caefee3SMatthias Ringwald/* 1688caefee3SMatthias Ringwald * @section Device Disconnected Callback 1698caefee3SMatthias Ringwald * 1708caefee3SMatthias Ringwald * @text If the connection to a device breaks, the device disconnected callback 1718caefee3SMatthias Ringwald * is called. Here, we start scanning for new devices again. 1728caefee3SMatthias Ringwald */ 1738caefee3SMatthias Ringwald/* LISTING_START(LECentralDeviceDisconnectedCallback): Device Disconnected Callback */ 1748caefee3SMatthias Ringwaldvoid deviceDisconnectedCallback(BLEDevice * device){ 1758caefee3SMatthias Ringwald Serial.println("Disconnected, starting over.."); 1768caefee3SMatthias Ringwald sendCounter = false; 1778caefee3SMatthias Ringwald BTstack.bleStartScanning(); 1788caefee3SMatthias Ringwald} 1798caefee3SMatthias Ringwald/* LISTING_END(LECentralDeviceDisconnectedCallback): Device Disconnected Callback */ 1808caefee3SMatthias Ringwald 1818caefee3SMatthias Ringwald/* 1828caefee3SMatthias Ringwald * @section Service Discovered Callback 1838caefee3SMatthias Ringwald * 1848caefee3SMatthias Ringwald * @text The service discovered callback is called for each service and after the 1858caefee3SMatthias Ringwald * service discovery is complete. The status argument is provided for this. 1868caefee3SMatthias Ringwald * 1878caefee3SMatthias Ringwald * The main information about a discovered Service is its UUID. 1888caefee3SMatthias Ringwald * If we find our service, we store the reference to this service. 1898caefee3SMatthias Ringwald * This allows to discover the Characteristics for our service after 1908caefee3SMatthias Ringwald * the service discovery is complete. 1918caefee3SMatthias Ringwald */ 1928caefee3SMatthias Ringwald/* LISTING_START(LECentralServiceDiscoveredCallback): Service Discovered Callback */ 1938caefee3SMatthias Ringwaldvoid gattServiceDiscovered(BLEStatus status, BLEDevice *device, BLEService *bleService) { 1948caefee3SMatthias Ringwald switch(status){ 1958caefee3SMatthias Ringwald case BLE_STATUS_OK: 1968caefee3SMatthias Ringwald Serial.print("Service Discovered: :"); 1978caefee3SMatthias Ringwald Serial.println(bleService->getUUID()->getUuidString()); 1988caefee3SMatthias Ringwald if (bleService->matches(&bleShieldServiceV2UUID)) { 1998caefee3SMatthias Ringwald serviceFound = true; 2008caefee3SMatthias Ringwald Serial.println("Our service located!"); 2018caefee3SMatthias Ringwald myBLEService = *bleService; 2028caefee3SMatthias Ringwald } 2038caefee3SMatthias Ringwald break; 2048caefee3SMatthias Ringwald case BLE_STATUS_DONE: 2058caefee3SMatthias Ringwald Serial.println("Service discovery finished"); 2068caefee3SMatthias Ringwald if (serviceFound) { 2078caefee3SMatthias Ringwald device->discoverCharacteristicsForService(&myBLEService); 2088caefee3SMatthias Ringwald } 2098caefee3SMatthias Ringwald break; 2108caefee3SMatthias Ringwald default: 2118caefee3SMatthias Ringwald Serial.println("Service discovery error"); 2128caefee3SMatthias Ringwald break; 2138caefee3SMatthias Ringwald } 2148caefee3SMatthias Ringwald} 2158caefee3SMatthias Ringwald/* LISTING_END(LECentralServiceDiscoveredCallback): Service Discovered Callback */ 2168caefee3SMatthias Ringwald 2178caefee3SMatthias Ringwald/* 2188caefee3SMatthias Ringwald * @section Characteristic Discovered Callback 2198caefee3SMatthias Ringwald * 2208caefee3SMatthias Ringwald * @text Similar to the Service Discovered callback, the Characteristic Discovered 2218caefee3SMatthias Ringwald * callback is called for each Characteristic found and after the discovery is complete. 2228caefee3SMatthias Ringwald * 2238caefee3SMatthias Ringwald * The main information is again its UUID. If we find a Characteristic that we're 2248caefee3SMatthias Ringwald * interested in, it's name is printed and a reference stored for later. 2258caefee3SMatthias Ringwald * 2268caefee3SMatthias Ringwald * On discovery complete, we subscribe to a particular Characteristic to receive 2278caefee3SMatthias Ringwald * Characteristic Value updates in the Notificaation Callback. 2288caefee3SMatthias Ringwald */ 2298caefee3SMatthias Ringwald/* LISTING_START(LECentralCharacteristicDiscoveredCallback): Characteristic Discovered Callback */ 2308caefee3SMatthias Ringwaldvoid gattCharacteristicDiscovered(BLEStatus status, BLEDevice *device, BLECharacteristic *characteristic) { 2318caefee3SMatthias Ringwald switch(status){ 2328caefee3SMatthias Ringwald case BLE_STATUS_OK: 2338caefee3SMatthias Ringwald Serial.print("Characteristic Discovered: "); 2348caefee3SMatthias Ringwald Serial.print(characteristic->getUUID()->getUuidString()); 2358caefee3SMatthias Ringwald Serial.print(", handle 0x"); 2368caefee3SMatthias Ringwald Serial.println(characteristic->getCharacteristic()->value_handle, HEX); 2378caefee3SMatthias Ringwald int i; 2388caefee3SMatthias Ringwald for (i=0;i<numCharacteristics;i++){ 2398caefee3SMatthias Ringwald if (characteristic->matches(&characteristics[i].uuid)){ 2408caefee3SMatthias Ringwald Serial.print("Characteristic found: "); 2418caefee3SMatthias Ringwald Serial.println(characteristics[i].name); 2428caefee3SMatthias Ringwald characteristics[i].found = 1; 2438caefee3SMatthias Ringwald characteristics[i].characteristic = *characteristic; 2448caefee3SMatthias Ringwald break; 2458caefee3SMatthias Ringwald } 2468caefee3SMatthias Ringwald } 2478caefee3SMatthias Ringwald break; 2488caefee3SMatthias Ringwald case BLE_STATUS_DONE: 2498caefee3SMatthias Ringwald Serial.print("Characteristic discovery finished, status "); 2508caefee3SMatthias Ringwald Serial.println(status, HEX); 2518caefee3SMatthias Ringwald if (characteristics[charRX].found) { 2528caefee3SMatthias Ringwald device->subscribeForNotifications(&characteristics[charRX].characteristic); 2538caefee3SMatthias Ringwald } 2548caefee3SMatthias Ringwald break; 2558caefee3SMatthias Ringwald default: 2568caefee3SMatthias Ringwald Serial.println("Characteristics discovery error"); 2578caefee3SMatthias Ringwald break; 2588caefee3SMatthias Ringwald } 2598caefee3SMatthias Ringwald} 2608caefee3SMatthias Ringwald/* LISTING_END(LECentralCharacteristicDiscoveredCallback): Characteristic Discovered Callback */ 2618caefee3SMatthias Ringwald 2628caefee3SMatthias Ringwald/* 2638caefee3SMatthias Ringwald * @section Subscribed Callback 2648caefee3SMatthias Ringwald * 2658caefee3SMatthias Ringwald * @text After the subcribe operation is complete, we get notified if it was 2668caefee3SMatthias Ringwald * successful. In this example, we read the Characteristic that contains the 2678caefee3SMatthias Ringwald * BD ADDR of the other device. This isn't strictly neccessary as we already 2688caefee3SMatthias Ringwald * know the device address from the Advertisement, but it's a common pattern 2698caefee3SMatthias Ringwald * with iOS as the device address is hidden from applications. 2708caefee3SMatthias Ringwald */ 2718caefee3SMatthias Ringwald/* LISTING_START(LECentralSubscribedCallback): Subscribed Callback */ 2728caefee3SMatthias Ringwaldvoid gattSubscribedCallback(BLEStatus status, BLEDevice * device){ 2738caefee3SMatthias Ringwald device->readCharacteristic(&characteristics[charBdAddr].characteristic); 2748caefee3SMatthias Ringwald} 2758caefee3SMatthias Ringwald/* LISTING_END(LECentralSubscribedCallback): Subscribed Callback */ 2768caefee3SMatthias Ringwald 2778caefee3SMatthias Ringwald/* 2788caefee3SMatthias Ringwald * @section Read Callback 2798caefee3SMatthias Ringwald * 2808caefee3SMatthias Ringwald * @text The Read callback is called with the result from a read operation. 2818caefee3SMatthias Ringwald * Here, we write to the TX Characteristic next. 2828caefee3SMatthias Ringwald */ 2838caefee3SMatthias Ringwald/* LISTING_START(LECentralReadCallback): Read Callback */ 2848caefee3SMatthias Ringwaldvoid gattReadCallback(BLEStatus status, BLEDevice *device, uint8_t *value, uint16_t length) { 2858caefee3SMatthias Ringwald Serial.print("Read callback: "); 2868caefee3SMatthias Ringwald Serial.println((const char *)value); 2878caefee3SMatthias Ringwald device->writeCharacteristic(&characteristics[charTX].characteristic, (uint8_t*) "Hello!", 6); 2888caefee3SMatthias Ringwald} 2898caefee3SMatthias Ringwald/* LISTING_END(LECentralReadCallback): Read Callback */ 2908caefee3SMatthias Ringwald 2918caefee3SMatthias Ringwald/* 2928caefee3SMatthias Ringwald * @section Written Callback 2938caefee3SMatthias Ringwald * 2948caefee3SMatthias Ringwald * @text After the write operation is complete, the Written Callback is callbed with 2958caefee3SMatthias Ringwald * the result in the status argument. As we're done with the initial setup of the remote 2968caefee3SMatthias Ringwald * device, we set the flag to write the test string as fast as possible. 2978caefee3SMatthias Ringwald */ 2988caefee3SMatthias Ringwald/* LISTING_START(LECentralWrittenCallback): Written Callback */ 2998caefee3SMatthias Ringwaldvoid gattWrittenCallback(BLEStatus status, BLEDevice *device){ 3008caefee3SMatthias Ringwald sendCounter = true; 3018caefee3SMatthias Ringwald} 3028caefee3SMatthias Ringwald/* LISTING_END(LECentralWrittenCallback): Written Callback */ 3038caefee3SMatthias Ringwald 3048caefee3SMatthias Ringwald/* 3058caefee3SMatthias Ringwald * @section Notification Callback 3068caefee3SMatthias Ringwald * 3078caefee3SMatthias Ringwald * @text Notifictions for Characteristic Value Updates are delivered via the 3088caefee3SMatthias Ringwald * Notification Callback. When more than one Characteristic is subscribed, 3098caefee3SMatthias Ringwald * the value handle can be used to distinguish between them. The 3108caefee3SMatthias Ringwald * BLECharacteristic.isValueHandle(int handle) allows to test if a value handle 3118caefee3SMatthias Ringwald * belongs to a particular Characteristic. 3128caefee3SMatthias Ringwald */ 3138caefee3SMatthias Ringwald/* LISTING_START(LECentralNotificationCallback): Notification Callback */ 3148caefee3SMatthias Ringwaldvoid gattCharacteristicNotification(BLEDevice *device, uint16_t value_handle, uint8_t *value, uint16_t length) { 3158caefee3SMatthias Ringwald Serial.print("Notification: "); 3168caefee3SMatthias Ringwald Serial.println((const char *)value); 3178caefee3SMatthias Ringwald} 3188caefee3SMatthias Ringwald/* LISTING_END(LECentralNotificationCallback): Notification Callback */ 3198caefee3SMatthias Ringwald 320