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