xref: /btstack/port/arduino/examples/LECentral/LECentral.ino (revision ec820d7756c9d2f5a09a7becda8464bd60ffa072)
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