mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-01-03 23:47:08 +00:00
320 lines
12 KiB
C++
320 lines
12 KiB
C++
#include <BTstack.h>
|
|
#include <SPI.h>
|
|
|
|
/*
|
|
* EXAMPLE_START(LECentral): LE Central
|
|
*
|
|
* @text Compared with the other examples, the LE Central is
|
|
* a bit more complex. This is because it performs multiple
|
|
* steps in sequence as it is common with GATT Client APIs.
|
|
*
|
|
* It shows how to first scan for other
|
|
* devices and then connect to one. When connected, a series of
|
|
* GATT Client operations are performed: first the list of
|
|
* GATT Services is queried. If a particular service is found,
|
|
* the list of its GATT Characteristics is retrieved and a set
|
|
* of known Characteristics are cached for later access.
|
|
*/
|
|
|
|
/*
|
|
* @section Characteristic Summary
|
|
* @text As multiple Characteristics need to be found, a custom
|
|
* struct is used to collect all information about it. This allows
|
|
* to defined the list of neccessary characteristics in the
|
|
* characteristics[] array
|
|
*/
|
|
/* LISTING_START(LECentralSummary): Characteristic Summary */
|
|
|
|
// BLE Shield Service V2 incl. used Characteristics
|
|
UUID bleShieldServiceV2UUID("B8E06067-62AD-41BA-9231-206AE80AB550");
|
|
|
|
typedef struct characteristic_summary {
|
|
UUID uuid;
|
|
const char * name;
|
|
bool found;
|
|
BLECharacteristic characteristic;
|
|
} characteristic_summary_t;
|
|
|
|
typedef enum characteristicIDs {
|
|
charRX = 0,
|
|
charTX,
|
|
charBaud,
|
|
charBdAddr,
|
|
numCharacteristics /* last one */
|
|
} characteristicIDs_t;
|
|
|
|
characteristic_summary characteristics[] = {
|
|
{ UUID("f897177b-aee8-4767-8ecc-cc694fd5fcee"), "RX" },
|
|
{ UUID("bf45e40a-de2a-4bc8-bba0-e5d6065f1b4b"), "TX" },
|
|
{ UUID("2fbc0f31-726a-4014-b9fe-c8be0652e982"), "Baudrate" },
|
|
{ UUID("65c228da-bad1-4f41-b55f-3d177f4e2196"), "BD ADDR" }
|
|
};
|
|
|
|
/* LISTING_END(LECentralSummary): Characteristic Summary */
|
|
|
|
// Application state
|
|
BLEDevice myBLEDevice;
|
|
BLEService myBLEService;
|
|
bool serviceFound;
|
|
bool sendCounter = false;
|
|
|
|
int counter = 0;
|
|
char counterString[20];
|
|
|
|
static btstack_timer_source_t heartbeat;
|
|
|
|
/*
|
|
* @section Setup
|
|
* @text In the setup, various callbacks are registered. After that
|
|
* we start scanning for other devices
|
|
*/
|
|
/* LISTING_START(LECentralSetup): LE Central Setup */
|
|
void setup(void){
|
|
Serial.begin(9600);
|
|
BTstack.setBLEAdvertisementCallback(advertisementCallback);
|
|
BTstack.setBLEDeviceConnectedCallback(deviceConnectedCallback);
|
|
BTstack.setBLEDeviceDisconnectedCallback(deviceDisconnectedCallback);
|
|
BTstack.setGATTServiceDiscoveredCallback(gattServiceDiscovered);
|
|
BTstack.setGATTCharacteristicDiscoveredCallback(gattCharacteristicDiscovered);
|
|
BTstack.setGATTCharacteristicNotificationCallback(gattCharacteristicNotification);
|
|
BTstack.setGATTCharacteristicReadCallback(gattReadCallback);
|
|
BTstack.setGATTCharacteristicWrittenCallback(gattWrittenCallback);
|
|
BTstack.setGATTCharacteristicSubscribedCallback(gattSubscribedCallback);
|
|
BTstack.setup();
|
|
BTstack.bleStartScanning();
|
|
}
|
|
/* LISTING_END(LECentralSetup): LE Central Setup */
|
|
|
|
/*
|
|
* @section Loop
|
|
*
|
|
* @text In the standard Arduino loop() function, BTstack's loop() is called first
|
|
* If we're connected, we send the string "BTstack" plus a counter as fast as possible.
|
|
* As the Bluetooth module might be busy, it's important to check the result of the
|
|
* writeCharacteristicWithoutResponse() call. If it's not ok, we just try again in the
|
|
* next loop iteration.
|
|
*/
|
|
/* LISTING_START(LECentralLoop): Loop */
|
|
void loop(void){
|
|
BTstack.loop();
|
|
|
|
// send counter as fast as possible
|
|
if (sendCounter){
|
|
sprintf(counterString, "BTstack %u\n", counter);
|
|
int result = myBLEDevice.writeCharacteristicWithoutResponse(&characteristics[charTX].characteristic, (uint8_t*) counterString, strlen(counterString) );
|
|
if (result == 0){
|
|
Serial.print("Wrote without response: ");
|
|
Serial.println(counterString);
|
|
counter++;
|
|
}
|
|
}
|
|
}
|
|
/* LISTING_END(LECentralLoop): Loop */
|
|
|
|
/*
|
|
* @section Advertisement Callback
|
|
*
|
|
* @text When an Advertisement is received, we check if it contains
|
|
* the UUID of the service we're interested in. Only a single service
|
|
* with a 128-bit UUID can be contained in and Advertisement and not
|
|
* all BLE devices provides this. Other options are to match on the
|
|
* reported device name or the BD ADDR prefix.
|
|
*
|
|
* If we found an interesting device, we try to connect to it.
|
|
*/
|
|
/* LISTING_START(LECentralAdvertisementCallback): Advertisement Callback */
|
|
void advertisementCallback(BLEAdvertisement *bleAdvertisement) {
|
|
Serial.print("Device discovered: ");
|
|
Serial.print(bleAdvertisement->getBdAddr()->getAddressString());
|
|
Serial.print(", RSSI: ");
|
|
Serial.println(bleAdvertisement->getRssi());
|
|
if (bleAdvertisement->containsService(&bleShieldServiceV2UUID)) {
|
|
Serial.println("\nBLE ShieldService V2 found!\n");
|
|
BTstack.bleStopScanning();
|
|
BTstack.bleConnect(bleAdvertisement, 10000); // 10 s
|
|
}
|
|
}
|
|
/* LISTING_END(LECentralAdvertisementCallback): Advertisement Callback */
|
|
|
|
/*
|
|
* @section Device Connected Callback
|
|
*
|
|
* @text At the end of bleConnect(), the device connected callback is callec.
|
|
* The status argument tells if the connection timed out, or if the connection
|
|
* was established successfully.
|
|
*
|
|
* On a successful connection, a GATT Service Discovery is started.
|
|
*/
|
|
/* LISTING_START(LECentralDeviceConnectedCallback): Device Connected Callback */
|
|
void deviceConnectedCallback(BLEStatus status, BLEDevice *device) {
|
|
switch (status){
|
|
case BLE_STATUS_OK:
|
|
Serial.println("Device connected!");
|
|
myBLEDevice = *device;
|
|
counter = 0;
|
|
myBLEDevice.discoverGATTServices();
|
|
break;
|
|
case BLE_STATUS_CONNECTION_TIMEOUT:
|
|
Serial.println("Error while Connecting the Peripheral");
|
|
BTstack.bleStartScanning();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
/* LISTING_END(LECentralDeviceConnectedCallback): Device Connected Callback */
|
|
|
|
/*
|
|
* @section Device Disconnected Callback
|
|
*
|
|
* @text If the connection to a device breaks, the device disconnected callback
|
|
* is called. Here, we start scanning for new devices again.
|
|
*/
|
|
/* LISTING_START(LECentralDeviceDisconnectedCallback): Device Disconnected Callback */
|
|
void deviceDisconnectedCallback(BLEDevice * device){
|
|
Serial.println("Disconnected, starting over..");
|
|
sendCounter = false;
|
|
BTstack.bleStartScanning();
|
|
}
|
|
/* LISTING_END(LECentralDeviceDisconnectedCallback): Device Disconnected Callback */
|
|
|
|
/*
|
|
* @section Service Discovered Callback
|
|
*
|
|
* @text The service discovered callback is called for each service and after the
|
|
* service discovery is complete. The status argument is provided for this.
|
|
*
|
|
* The main information about a discovered Service is its UUID.
|
|
* If we find our service, we store the reference to this service.
|
|
* This allows to discover the Characteristics for our service after
|
|
* the service discovery is complete.
|
|
*/
|
|
/* LISTING_START(LECentralServiceDiscoveredCallback): Service Discovered Callback */
|
|
void gattServiceDiscovered(BLEStatus status, BLEDevice *device, BLEService *bleService) {
|
|
switch(status){
|
|
case BLE_STATUS_OK:
|
|
Serial.print("Service Discovered: :");
|
|
Serial.println(bleService->getUUID()->getUuidString());
|
|
if (bleService->matches(&bleShieldServiceV2UUID)) {
|
|
serviceFound = true;
|
|
Serial.println("Our service located!");
|
|
myBLEService = *bleService;
|
|
}
|
|
break;
|
|
case BLE_STATUS_DONE:
|
|
Serial.println("Service discovery finished");
|
|
if (serviceFound) {
|
|
device->discoverCharacteristicsForService(&myBLEService);
|
|
}
|
|
break;
|
|
default:
|
|
Serial.println("Service discovery error");
|
|
break;
|
|
}
|
|
}
|
|
/* LISTING_END(LECentralServiceDiscoveredCallback): Service Discovered Callback */
|
|
|
|
/*
|
|
* @section Characteristic Discovered Callback
|
|
*
|
|
* @text Similar to the Service Discovered callback, the Characteristic Discovered
|
|
* callback is called for each Characteristic found and after the discovery is complete.
|
|
*
|
|
* The main information is again its UUID. If we find a Characteristic that we're
|
|
* interested in, it's name is printed and a reference stored for later.
|
|
*
|
|
* On discovery complete, we subscribe to a particular Characteristic to receive
|
|
* Characteristic Value updates in the Notificaation Callback.
|
|
*/
|
|
/* LISTING_START(LECentralCharacteristicDiscoveredCallback): Characteristic Discovered Callback */
|
|
void gattCharacteristicDiscovered(BLEStatus status, BLEDevice *device, BLECharacteristic *characteristic) {
|
|
switch(status){
|
|
case BLE_STATUS_OK:
|
|
Serial.print("Characteristic Discovered: ");
|
|
Serial.print(characteristic->getUUID()->getUuidString());
|
|
Serial.print(", handle 0x");
|
|
Serial.println(characteristic->getCharacteristic()->value_handle, HEX);
|
|
int i;
|
|
for (i=0;i<numCharacteristics;i++){
|
|
if (characteristic->matches(&characteristics[i].uuid)){
|
|
Serial.print("Characteristic found: ");
|
|
Serial.println(characteristics[i].name);
|
|
characteristics[i].found = 1;
|
|
characteristics[i].characteristic = *characteristic;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case BLE_STATUS_DONE:
|
|
Serial.print("Characteristic discovery finished, status ");
|
|
Serial.println(status, HEX);
|
|
if (characteristics[charRX].found) {
|
|
device->subscribeForNotifications(&characteristics[charRX].characteristic);
|
|
}
|
|
break;
|
|
default:
|
|
Serial.println("Characteristics discovery error");
|
|
break;
|
|
}
|
|
}
|
|
/* LISTING_END(LECentralCharacteristicDiscoveredCallback): Characteristic Discovered Callback */
|
|
|
|
/*
|
|
* @section Subscribed Callback
|
|
*
|
|
* @text After the subcribe operation is complete, we get notified if it was
|
|
* successful. In this example, we read the Characteristic that contains the
|
|
* BD ADDR of the other device. This isn't strictly neccessary as we already
|
|
* know the device address from the Advertisement, but it's a common pattern
|
|
* with iOS as the device address is hidden from applications.
|
|
*/
|
|
/* LISTING_START(LECentralSubscribedCallback): Subscribed Callback */
|
|
void gattSubscribedCallback(BLEStatus status, BLEDevice * device){
|
|
device->readCharacteristic(&characteristics[charBdAddr].characteristic);
|
|
}
|
|
/* LISTING_END(LECentralSubscribedCallback): Subscribed Callback */
|
|
|
|
/*
|
|
* @section Read Callback
|
|
*
|
|
* @text The Read callback is called with the result from a read operation.
|
|
* Here, we write to the TX Characteristic next.
|
|
*/
|
|
/* LISTING_START(LECentralReadCallback): Read Callback */
|
|
void gattReadCallback(BLEStatus status, BLEDevice *device, uint8_t *value, uint16_t length) {
|
|
Serial.print("Read callback: ");
|
|
Serial.println((const char *)value);
|
|
device->writeCharacteristic(&characteristics[charTX].characteristic, (uint8_t*) "Hello!", 6);
|
|
}
|
|
/* LISTING_END(LECentralReadCallback): Read Callback */
|
|
|
|
/*
|
|
* @section Written Callback
|
|
*
|
|
* @text After the write operation is complete, the Written Callback is callbed with
|
|
* the result in the status argument. As we're done with the initial setup of the remote
|
|
* device, we set the flag to write the test string as fast as possible.
|
|
*/
|
|
/* LISTING_START(LECentralWrittenCallback): Written Callback */
|
|
void gattWrittenCallback(BLEStatus status, BLEDevice *device){
|
|
sendCounter = true;
|
|
}
|
|
/* LISTING_END(LECentralWrittenCallback): Written Callback */
|
|
|
|
/*
|
|
* @section Notification Callback
|
|
*
|
|
* @text Notifictions for Characteristic Value Updates are delivered via the
|
|
* Notification Callback. When more than one Characteristic is subscribed,
|
|
* the value handle can be used to distinguish between them. The
|
|
* BLECharacteristic.isValueHandle(int handle) allows to test if a value handle
|
|
* belongs to a particular Characteristic.
|
|
*/
|
|
/* LISTING_START(LECentralNotificationCallback): Notification Callback */
|
|
void gattCharacteristicNotification(BLEDevice *device, uint16_t value_handle, uint8_t *value, uint16_t length) {
|
|
Serial.print("Notification: ");
|
|
Serial.println((const char *)value);
|
|
}
|
|
/* LISTING_END(LECentralNotificationCallback): Notification Callback */
|
|
|