Unverified Commit 8e8fea4b authored by Earle F. Philhower, III's avatar Earle F. Philhower, III Committed by GitHub

Add (unsupported) BTstack Arduino library (#1305)

The BTstack driver includes a basic Arduino library as part of the ports
directory.
parent ebe5bfbc
This is the BTstack ports/arduino library, with compile errors fixed only.
#include <BTstack.h>
#include <stdio.h>
#include "ble/att_server.h"
#include "ble/gatt_client.h"
#include "ble/gatt-service/ancs_client.h"
#include "ble/sm.h"
#include "btstack_event.h"
#include <SPI.h>
/*
EXAMPLE_START(ANCS): ANCS Client
*/
/*
@section Advertisement
@text An ANCS Client needs to include the ANCS UUID in its advertisement to
get recognized by iOS
*/
/* LISTING_START(ANCSAdvertisement): ANCS Advertisement */
const uint8_t adv_data[] = {
// Flags general discoverable
0x02, 0x01, 0x02,
// Name
0x05, 0x09, 'A', 'N', 'C', 'S',
// Service Solicitation, 128-bit UUIDs - ANCS (little endian)
0x11, 0x15, 0xD0, 0x00, 0x2D, 0x12, 0x1E, 0x4B, 0x0F, 0xA4, 0x99, 0x4E, 0xCE, 0xB5, 0x31, 0xF4, 0x05, 0x79
};
/* LISTING_END(ANCSAdvertisement): ANCS Advertisement */
/*
@section Setup
@text In the setup, the LE Security Manager is configured to accept pairing requests.
Then, the ANCS Client library is initialized and and ancs_callback registered.
Finally, the Advertisement data is set and Advertisements are started.
*/
/* LISTING_START(ANCSSetup): ANCS Setup */
void setup(void) {
Serial.begin(9600);
Serial.println("BTstack ANCS Client starting up...");
// startup BTstack and configure log_info/log_error
BTstack.setup();
sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_ONLY);
sm_set_authentication_requirements(SM_AUTHREQ_BONDING);
// setup ANCS Client
ancs_client_init();
ancs_client_register_callback(&ancs_callback);
// enable advertisements
BTstack.setAdvData(sizeof(adv_data), adv_data);
BTstack.startAdvertising();
}
/* LISTING_END(ANCSSetup): ANCS Setup */
void loop(void) {
BTstack.loop();
}
/*
@section ANCS Callback
@text In the ANCS Callback, connect and disconnect events are received.
For actual notifications, ancs_client_attribute_name_for_id allows to
look up the name. To get the notification body, e.g., the actual message,
the GATT Client needs to be used directly.
*/
/* LISTING_START(ANCSCallback): ANCS Callback */
void ancs_callback(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
(void) packet_type;
(void) channel;
(void) size;
const char * attribute_name;
if (hci_event_packet_get_type(packet) != HCI_EVENT_ANCS_META) {
return;
}
switch (hci_event_ancs_meta_get_subevent_code(packet)) {
case ANCS_SUBEVENT_CLIENT_CONNECTED:
Serial.println("ANCS Client: Connected");
break;
case ANCS_SUBEVENT_CLIENT_DISCONNECTED:
Serial.println("ANCS Client: Disconnected");
break;
case ANCS_SUBEVENT_CLIENT_NOTIFICATION:
attribute_name = ancs_client_attribute_name_for_id(ancs_subevent_client_notification_get_attribute_id(packet));
if (!attribute_name) {
break;
}
Serial.print("Notification: ");
Serial.print(attribute_name);
Serial.print(" - ");
Serial.println(ancs_subevent_client_notification_get_text(packet));
break;
default:
break;
}
}
/* LISTING_END(ANCSCallback): ANCS Callback */
#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 define the list of necessary 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", false, BLECharacteristic() },
{ UUID("bf45e40a-de2a-4bc8-bba0-e5d6065f1b4b"), "TX", false, BLECharacteristic() },
{ UUID("2fbc0f31-726a-4014-b9fe-c8be0652e982"), "Baudrate", false, BLECharacteristic() },
{ UUID("65c228da-bad1-4f41-b55f-3d177f4e2196"), "BD ADDR", false, BLECharacteristic() }
};
/* 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) {
(void) 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 subscribe 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 necessary 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) {
(void) status;
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) {
(void) status;
(void) 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) {
(void) status;
(void) device;
sendCounter = true;
}
/* LISTING_END(LECentralWrittenCallback): Written Callback */
/*
@section Notification Callback
@text Notifications 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) {
(void) device;
(void) value_handle;
(void) length;
Serial.print("Notification: ");
Serial.println((const char *)value);
}
/* LISTING_END(LECentralNotificationCallback): Notification Callback */
// LE Peripheral Example - not working yet
#include <BTstack.h>
#include <SPI.h>
/*
EXAMPLE_START(LEPeripheral): LE Peripheral
@text BTstack allows to setup a GATT Services and Characteristics directly
from the setup function without using other tools outside of the Arduino IDE.
@section Setup
@text First, a number of callbacks are set. Then, a Service with a Read-only
Characteristic and a dynamic Characteristic is added to the GATT database.
In BTstack, a dynamic Characteristic is a Characteristic where reads and writes
are forwarded to the Sketch. In this example, the dynamic Characteristic is
provided by the single byte variable characteristic_data.
*/
/* LISTING_START(LEPeripheralSetup): Setup */
static char characteristic_data = 'H';
void setup(void) {
Serial.begin(9600);
// set callbacks
BTstack.setBLEDeviceConnectedCallback(deviceConnectedCallback);
BTstack.setBLEDeviceDisconnectedCallback(deviceDisconnectedCallback);
BTstack.setGATTCharacteristicRead(gattReadCallback);
BTstack.setGATTCharacteristicWrite(gattWriteCallback);
// setup GATT Database
BTstack.addGATTService(new UUID("B8E06067-62AD-41BA-9231-206AE80AB551"));
BTstack.addGATTCharacteristic(new UUID("f897177b-aee8-4767-8ecc-cc694fd5fcef"), ATT_PROPERTY_READ, "This is a String!");
BTstack.addGATTCharacteristicDynamic(new UUID("f897177b-aee8-4767-8ecc-cc694fd5fce0"), ATT_PROPERTY_READ | ATT_PROPERTY_WRITE | ATT_PROPERTY_NOTIFY, 0);
// startup Bluetooth and activate advertisements
BTstack.setup();
BTstack.startAdvertising();
}
/* LISTING_END(LEPeripheralSetup): Setup */
void loop(void) {
BTstack.loop();
}
/*
@section Device Connected Callback
@text When a remove device connects, device connected callback is callec.
*/
/* LISTING_START(LEPeripheralDeviceConnectedCallback): Device Connected Callback */
void deviceConnectedCallback(BLEStatus status, BLEDevice *device) {
(void) device;
switch (status) {
case BLE_STATUS_OK:
Serial.println("Device connected!");
break;
default:
break;
}
}
/* LISTING_END(LEPeripheralDeviceConnectedCallback): Device Connected Callback */
/*
@section Device Disconnected Callback
@text If the connection to a device breaks, the device disconnected callback
is called.
*/
/* LISTING_START(LEPeripheralDeviceDisconnectedCallback): Device Disconnected Callback */
void deviceDisconnectedCallback(BLEDevice * device) {
(void) device;
Serial.println("Disconnected.");
}
/* LISTING_END(LEPeripheralDeviceDisconnectedCallback): Device Disconnected Callback */
/*
@section Read Callback
@text In BTstack, the Read Callback is first called to query the size of the
Characteristic Value, before it is called to provide the data.
Both times, the size has to be returned. The data is only stored in the provided
buffer, if the buffer argeument is not NULL.
If more than one dynamic Characteristics is used, the value handle is used
to distinguish them.
*/
/* LISTING_START(LEPeripheralReadCallback): Read Callback */
uint16_t gattReadCallback(uint16_t value_handle, uint8_t * buffer, uint16_t buffer_size) {
(void) value_handle;
(void) buffer_size;
if (buffer) {
Serial.print("gattReadCallback, value: ");
Serial.println(characteristic_data, HEX);
buffer[0] = characteristic_data;
}
return 1;
}
/* LISTING_END(LEPeripheralDeviceDisconnectedCallback): Read Callback */
/*
@section Write Callback
@text When the remove device writes a Characteristic Value, the Write callback
is called. The buffer arguments points to the data of size size/
If more than one dynamic Characteristics is used, the value handle is used
to distinguish them.
*/
/* LISTING_START(LEPeripheralWriteCallback): Write Callback */
int gattWriteCallback(uint16_t value_handle, uint8_t *buffer, uint16_t size) {
(void) value_handle;
(void) size;
characteristic_data = buffer[0];
Serial.print("gattWriteCallback , value ");
Serial.println(characteristic_data, HEX);
return 0;
}
/* LISTING_END(LEPeripheralWriteCallback): Write Callback */
#include <BTstack.h>
#include <stdio.h>
#include <SPI.h>
/* EXAMPLE_START(iBeacon): iBeacon Simulator
@section Setup
@text After BTstack.setup(), iBeaconConfigure() configures BTstack
to send out iBeacons Advertisements with the provided Major ID,
Minor ID and UUID.
*/
/* LISTING_START(iBeaconSetup): iBeacon Setup */
UUID uuid("E2C56DB5-DFFB-48D2-B060-D0F5A71096E0");
void setup(void) {
Serial.begin(9600);
BTstack.setup();
BTstack.iBeaconConfigure(&uuid, 4711, 2);
BTstack.startAdvertising();
}
/* LISTING_END(iBeaconSetup) */
void loop(void) {
BTstack.loop();
}
#include <BTstack.h>
#include <SPI.h>
/* EXAMPLE_START(iBeaconScanner): iBeacon Scanner
@section Setup
@text After BTstack.setup(), BTstack is configured to call
advertisementCallback whenever an Advertisement was received.
Then, a device discovery is started
*/
/* LISTING_START(iBeaconSetup): iBeacon Scanner Setup */
void setup(void) {
Serial.begin(9600);
BTstack.setup();
BTstack.setBLEAdvertisementCallback(advertisementCallback);
BTstack.bleStartScanning();
}
/* LISTING_END(iBeaconSetup): iBeacon Scanner Setup */
void loop(void) {
BTstack.loop();
}
/*
@section Advertisement Callback
@text Whenever an Advertisement is received, isIBeacon() checks if
it contains an iBeacon. If yes, the Major ID, Minor ID, and UUID
is printed.
If it's not an iBeacon, only the BD_ADDR and the received signal strength
(RSSI) is shown.
*/
/* LISTING_START(iBeaconCallback): iBeacon Scanner Callback */
void advertisementCallback(BLEAdvertisement *adv) {
if (adv->isIBeacon()) {
Serial.print("iBeacon found ");
Serial.print(adv->getBdAddr()->getAddressString());
Serial.print(", RSSI ");
Serial.print(adv->getRssi());
Serial.print(", UUID ");
Serial.print(adv->getIBeaconUUID()->getUuidString());
Serial.print(", MajorID ");
Serial.print(adv->getIBeaconMajorID());
Serial.print(", MinorID ");
Serial.print(adv->getIBecaonMinorID());
Serial.print(", Measured Power ");
Serial.println(adv->getiBeaconMeasuredPower());
} else {
Serial.print("Device discovered: ");
Serial.print(adv->getBdAddr()->getAddressString());
Serial.print(", RSSI ");
Serial.println(adv->getRssi());
}
}
/* LISTING_END(iBeaconCallback): iBeacon Scanner Callback */
#######################################
# Syntax Coloring Map
#######################################
#######################################
# Library (KEYWORD3)
#######################################
BTstack KEYWORD3 RESERVED_WORD
#######################################
# Datatypes (KEYWORD1)
#######################################
BLEStatus KEYWORD2
UUID KEYWORD2
BD_ADDR_TYPE KEYWORD2
BD_ADDR KEYWORD2
BLEAdvertisement KEYWORD2
BLECharacteristic KEYWORD2
BLEService KEYWORD2
BLEDevice KEYWORD2
BTstackManager KEYWORD2
#######################################
# Methods and Functions (KEYWORD2)
#######################################
getUuidString KEYWORD2
getUuid128String KEYWORD2
getUuid KEYWORD2
matches KEYWORD2
getAddress KEYWORD2
getAddressString KEYWORD2
getAddressType KEYWORD2
getBdAddr KEYWORD2
getBdAddrType KEYWORD2
getRssi KEYWORD2
containsService KEYWORD2
nameHasPrefix KEYWORD2
getAdvData KEYWORD2
isIBeacon KEYWORD2
getIBeaconUUID KEYWORD2
getIBeaconMajorID KEYWORD2
getIBecaonMinorID KEYWORD2
getiBeaconMeasuredPower KEYWORD2
isValueHandle KEYWORD2
getCharacteristic KEYWORD2
getUUID KEYWORD2
getService KEYWORD2
getHandle KEYWORD2
discoverGATTServices KEYWORD2
discoverCharacteristicsForService KEYWORD2
readCharacteristic KEYWORD2
writeCharacteristic KEYWORD2
writeCharacteristicWithoutResponse KEYWORD2
subscribeForNotifications KEYWORD2
unsubscribeFromNotifications KEYWORD2
subscribeForIndications KEYWORD2
unsubscribeFromIndications KEYWORD2
setPublicBdAddr KEYWORD2
enablePacketLogger KEYWORD2
enableDebugLogger KEYWORD2
setAdvData KEYWORD2
iBeaconConfigure KEYWORD2
startAdvertising KEYWORD2
stopAdvertising KEYWORD2
bleStartScanning KEYWORD2
bleStopScanning KEYWORD2
bleConnect KEYWORD2
bleDisconnect KEYWORD2
setBLEAdvertisementCallback KEYWORD2
setBLEDeviceConnectedCallback KEYWORD2
setBLEDeviceDisconnectedCallback KEYWORD2
setGATTServiceDiscoveredCallback KEYWORD2
setGATTCharacteristicDiscoveredCallback KEYWORD2
setGATTCharacteristicReadCallback KEYWORD2
setGATTCharacteristicNotificationCallback KEYWORD2
setGATTCharacteristicIndicationCallback KEYWORD2
setGATTDoneCallback KEYWORD2
setGATTCharacteristicWrittenCallback KEYWORD2
setGATTCharacteristicSubscribedCallback KEYWORD2
setGATTCharacteristicUnsubscribedCallback KEYWORD2
setGATTCharacteristicRead KEYWORD2
setGATTCharacteristicWrite KEYWORD2
addGATTService KEYWORD2
addGATTCharacteristic KEYWORD2
addGATTCharacteristicDynamic KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
BLE_STATUS_OK LITERAL1
BLE_STATUS_DONE LITERAL1
BLE_STATUS_CONNECTION_TIMEOUT LITERAL1
BLE_STATUS_CONNECTION_ERROR LITERAL1
BLE_STATUS_OTHER_ERROR LITERAL1
PUBLIC_ADDRESS LITERAL1
PRIVAT_ADDRESS LITERAL1
name=BTstack
version=1.0
author=bluekitchen-gmbh
maintainer=Earle Philhower
sentence=BTstack Arduino library distributed with BTstack
paragraph=
category=Data Processing
url=
architectures=rp2040
dot_a_linkage=true
/**
Arduino Wrapper for BTstack
*/
#include <Arduino.h>
#include <SPI.h>
#ifdef __AVR__
#include <avr/wdt.h>
#endif
#if __arm__
//#include <Reset.h> // also provides NVIC_SystemReset
#endif
#include "BTstack.h"
#include "btstack_memory.h"
#include "hal_tick.h"
#include "hal_cpu.h"
#include "hci_cmd.h"
#include "btstack_util.h"
#include "btstack_run_loop.h"
#include "btstack_event.h"
#include "btstack_run_loop_embedded.h"
#include "hci_transport.h"
#include "hci_transport_h4.h"
#include "ad_parser.h"
//#include "btstack_chipset_em9301.h"
#include "btstack_debug.h"
#include "gap.h"
#include "hci.h"
#include "hci_dump.h"
#include "hci_dump_embedded_stdout.h"
#include "l2cap.h"
#include "ble/att_db.h"
#include "ble/att_server.h"
#include "ble/att_db_util.h"
#include "ble/le_device_db.h"
#include "ble/sm.h"
// Pin 13 has an LED connected on most Arduino boards.
//#define PIN_LED 13
// prototypes
extern "C" void hal_uart_dma_process(void);
enum {
SET_ADVERTISEMENT_PARAMS = 1 << 0,
SET_ADVERTISEMENT_DATA = 1 << 1,
SET_ADVERTISEMENT_ENABLED = 1 << 2,
};
typedef enum gattAction {
gattActionWrite,
gattActionSubscribe,
gattActionUnsubscribe,
gattActionServiceQuery,
gattActionCharacteristicQuery,
gattActionRead,
} gattAction_t;
static gattAction_t gattAction;
// btstack state
static int btstack_state;
static const uint8_t iBeaconAdvertisement01[] = { 0x02, 0x01 };
static const uint8_t iBeaconAdvertisement38[] = { 0x1a, 0xff, 0x4c, 0x00, 0x02, 0x15 };
static uint8_t adv_data[31];
static uint16_t adv_data_len = 0;
//static int gatt_is_characteristics_query;
static uint16_t le_peripheral_todos = 0;
static bool have_custom_addr;
static bd_addr_t public_bd_addr;
static btstack_timer_source_t connection_timer;
static void (*bleAdvertismentCallback)(BLEAdvertisement * bleAdvertisement) = NULL;
static void (*bleDeviceConnectedCallback)(BLEStatus status, BLEDevice * device) = NULL;
static void (*bleDeviceDisconnectedCallback)(BLEDevice * device) = NULL;
static void (*gattServiceDiscoveredCallback)(BLEStatus status, BLEDevice * device, BLEService * bleService) = NULL;
static void (*gattCharacteristicDiscoveredCallback)(BLEStatus status, BLEDevice * device, BLECharacteristic * characteristic) = NULL;
static void (*gattCharacteristicNotificationCallback)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length) = NULL;
static void (*gattCharacteristicIndicationCallback)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length) = NULL;
static void (*gattCharacteristicReadCallback)(BLEStatus status, BLEDevice * device, uint8_t * value, uint16_t length) = NULL;
static void (*gattCharacteristicWrittenCallback)(BLEStatus status, BLEDevice * device) = NULL;
static void (*gattCharacteristicSubscribedCallback)(BLEStatus status, BLEDevice * device) = NULL;
static void (*gattCharacteristicUnsubscribedCallback)(BLEStatus status, BLEDevice * device) = NULL;
// retarget printf to Serial
#ifdef ENERGIA
extern "C" int putchar(int c) {
Serial.write((uint8_t)c);
return c;
}
#else
#ifdef __AVR__
static FILE uartout = {0} ;
static int uart_putchar(char c, FILE *stream) {
Serial.write(c);
return 0;
}
#endif
// added for Arduino Zero. Arduino Due already has tis own _write(..) implementation
// in /Users/mringwal/Library/Arduino15/packages/arduino/hardware/sam/1.6.4/cores/arduino/syscalls_sam3.c
#if defined(__SAMD21G18A__)
// #ifdef __arm__
extern "C" int _write(int file, char *ptr, int len) {
int i;
for (i = 0; i < len; i++) {
if (ptr[i] == '\n') {
Serial.write((uint8_t)'\r');
}
Serial.write((uint8_t)ptr[i]);
}
return i;
}
#endif
#endif
// HAL CPU Implementation
extern "C" void hal_cpu_disable_irqs(void) { }
extern "C" void hal_cpu_enable_irqs(void) { }
extern "C" void hal_cpu_enable_irqs_and_sleep(void) { }
//
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
(void) channel;
(void) size;
hci_con_handle_t con_handle;
switch (packet_type) {
case HCI_EVENT_PACKET:
switch (packet[0]) {
case BTSTACK_EVENT_STATE:
btstack_state = packet[2];
// bt stack activated, get started
if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) {
le_peripheral_todos |= SET_ADVERTISEMENT_PARAMS
| SET_ADVERTISEMENT_DATA
| SET_ADVERTISEMENT_ENABLED;
bd_addr_t addr;
gap_local_bd_addr(addr);
printf("BTstack up and running at %s\n", bd_addr_to_str(addr));
}
break;
case HCI_EVENT_DISCONNECTION_COMPLETE:
if (bleDeviceDisconnectedCallback) {
con_handle = little_endian_read_16(packet, 3);
BLEDevice device(con_handle);
(*bleDeviceDisconnectedCallback)(&device);
}
le_peripheral_todos |= SET_ADVERTISEMENT_ENABLED;
break;
case GAP_EVENT_ADVERTISING_REPORT: {
if (bleAdvertismentCallback) {
BLEAdvertisement advertisement(packet);
(*bleAdvertismentCallback)(&advertisement);
}
break;
}
case HCI_EVENT_LE_META:
switch (packet[2]) {
case HCI_SUBEVENT_LE_CONNECTION_COMPLETE:
con_handle = little_endian_read_16(packet, 4);
printf("Connection complete, con_handle 0x%04x\n", con_handle);
btstack_run_loop_remove_timer(&connection_timer);
if (!bleDeviceConnectedCallback) {
break;
}
if (packet[3]) {
(*bleDeviceConnectedCallback)(BLE_STATUS_CONNECTION_ERROR, NULL);
} else {
BLEDevice device(con_handle);
(*bleDeviceConnectedCallback)(BLE_STATUS_OK, &device);
}
break;
default:
break;
}
break;
}
}
}
static void extract_service(gatt_client_service_t * service, uint8_t * packet) {
service->start_group_handle = little_endian_read_16(packet, 4);
service->end_group_handle = little_endian_read_16(packet, 6);
service->uuid16 = 0;
reverse_128(&packet[8], service->uuid128);
if (uuid_has_bluetooth_prefix(service->uuid128)) {
service->uuid16 = big_endian_read_32(service->uuid128, 0);
}
}
static void extract_characteristic(gatt_client_characteristic_t * characteristic, uint8_t * packet) {
characteristic->start_handle = little_endian_read_16(packet, 4);
characteristic->value_handle = little_endian_read_16(packet, 6);
characteristic->end_handle = little_endian_read_16(packet, 8);
characteristic->properties = little_endian_read_16(packet, 10);
characteristic->uuid16 = 0;
reverse_128(&packet[12], characteristic->uuid128);
if (uuid_has_bluetooth_prefix(characteristic->uuid128)) {
characteristic->uuid16 = big_endian_read_32(characteristic->uuid128, 0);
}
}
static void gatt_client_callback(uint8_t packet_type, uint16_t channel, uint8_t * packet, uint16_t size) {
(void) channel;
(void) packet_type;
(void) size;
// if (hci) event is not 4-byte aligned, event->handle causes crash
// workaround: check event type, assuming GATT event types are contagious
if (packet[0] < GATT_EVENT_QUERY_COMPLETE) {
return;
}
if (packet[0] > GATT_EVENT_MTU) {
return;
}
hci_con_handle_t con_handle = little_endian_read_16(packet, 2);
uint8_t status;
uint8_t * value;
uint16_t value_handle;
uint16_t value_length;
BLEDevice device(con_handle);
switch (hci_event_packet_get_type(packet)) {
case GATT_EVENT_SERVICE_QUERY_RESULT:
if (gattServiceDiscoveredCallback) {
gatt_client_service_t service;
extract_service(&service, packet);
BLEService bleService(service);
(*gattServiceDiscoveredCallback)(BLE_STATUS_OK, &device, &bleService);
}
break;
case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
if (gattCharacteristicDiscoveredCallback) {
gatt_client_characteristic_t characteristic;
extract_characteristic(&characteristic, packet);
BLECharacteristic bleCharacteristic(characteristic);
(*gattCharacteristicDiscoveredCallback)(BLE_STATUS_OK, &device, &bleCharacteristic);
}
break;
case GATT_EVENT_QUERY_COMPLETE:
status = little_endian_read_16(packet, 4);
switch (gattAction) {
case gattActionWrite:
if (gattCharacteristicWrittenCallback) {
gattCharacteristicWrittenCallback(status ? BLE_STATUS_OTHER_ERROR : BLE_STATUS_OK, &device);
}
break;
case gattActionSubscribe:
if (gattCharacteristicSubscribedCallback) {
gattCharacteristicSubscribedCallback(status ? BLE_STATUS_OTHER_ERROR : BLE_STATUS_OK, &device);
}
break;
case gattActionUnsubscribe:
if (gattCharacteristicUnsubscribedCallback) {
gattCharacteristicUnsubscribedCallback(status ? BLE_STATUS_OTHER_ERROR : BLE_STATUS_OK, &device);
}
break;
case gattActionServiceQuery:
if (gattServiceDiscoveredCallback) {
gattServiceDiscoveredCallback(BLE_STATUS_DONE, &device, NULL);
}
break;
case gattActionCharacteristicQuery:
if (gattCharacteristicDiscoveredCallback) {
gattCharacteristicDiscoveredCallback(BLE_STATUS_DONE, &device, NULL);
}
break;
default:
break;
};
break;
case GATT_EVENT_NOTIFICATION:
if (gattCharacteristicNotificationCallback) {
value_handle = little_endian_read_16(packet, 4);
value_length = little_endian_read_16(packet, 6);
value = &packet[8];
(*gattCharacteristicNotificationCallback)(&device, value_handle, value, value_length);
}
break;
case GATT_EVENT_INDICATION:
if (gattCharacteristicIndicationCallback) {
value_handle = little_endian_read_16(packet, 4);
value_length = little_endian_read_16(packet, 6);
value = &packet[8];
(*gattCharacteristicIndicationCallback)(&device, value_handle, value, value_length);
}
break;
case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:
if (gattCharacteristicReadCallback) {
value_handle = little_endian_read_16(packet, 4);
value_length = little_endian_read_16(packet, 6);
value = &packet[8];
(*gattCharacteristicReadCallback)(BLE_STATUS_OK, &device, value, value_length);
}
break;
default:
break;
}
}
static void connection_timeout_handler(btstack_timer_source_t * timer) {
(void) timer;
// log_info("Cancel outgoing connection");
gap_connect_cancel();
if (!bleDeviceConnectedCallback) {
return;
}
(*bleDeviceConnectedCallback)(BLE_STATUS_CONNECTION_TIMEOUT, NULL); // page timeout 0x04
}
//
/// UUID class
UUID::UUID(void) {
memset(uuid, 0, 16);
}
UUID::UUID(const uint8_t uuid[16]) {
memcpy(this->uuid, uuid, 16);
}
UUID::UUID(const char * uuidStr) {
memset(uuid, 0, 16);
int len = strlen(uuidStr);
if (len <= 4) {
// Handle 4 Bytes HEX
unsigned int uuid16;
int result = sscanf((char *) uuidStr, "%x", &uuid16);
if (result == 1) {
uuid_add_bluetooth_prefix(uuid, uuid16);
}
return;
}
// quick UUID parser, ignoring dashes
int i = 0;
int data = 0;
int have_nibble = 0;
while (*uuidStr && i < 16) {
const char c = *uuidStr++;
if (c == '-') {
continue;
}
data = data << 4 | nibble_for_char(c);
if (!have_nibble) {
have_nibble = 1;
continue;
}
uuid[i++] = data;
data = 0;
have_nibble = 0;
}
}
const uint8_t * UUID::getUuid(void) const {
return uuid;
}
static char uuid16_buffer[5];
const char * UUID::getUuidString() const {
// TODO: fix uuid_has_bluetooth_prefix call to use const
if (uuid_has_bluetooth_prefix((uint8_t*)uuid)) {
sprintf(uuid16_buffer, "%04x", (uint16_t) big_endian_read_32(uuid, 0));
return uuid16_buffer;
} else {
// TODO: fix uuid128_to_str
return uuid128_to_str((uint8_t*)uuid);
}
}
const char * UUID::getUuid128String() const {
return uuid128_to_str((uint8_t*)uuid);
}
bool UUID::matches(UUID *other) const {
return memcmp(this->uuid, other->uuid, 16) == 0;
}
// BD_ADDR class
BD_ADDR::BD_ADDR(void) {
}
BD_ADDR::BD_ADDR(const char * address_string, BD_ADDR_TYPE address_type) : address_type(address_type) {
(void) address_string;
// TODO: implement
// log_error("BD_ADDR::BD_ADDR(const char *, BD_ADDR_TYPE) not implemented yet!");
}
BD_ADDR::BD_ADDR(const uint8_t address[6], BD_ADDR_TYPE address_type) : address_type(address_type) {
memcpy(this->address, address, 6);
}
const uint8_t * BD_ADDR::getAddress(void) {
return address;
}
const char * BD_ADDR::getAddressString(void) {
return bd_addr_to_str(address);
}
BD_ADDR_TYPE BD_ADDR::getAddressType(void) {
return address_type;
}
BLEAdvertisement::BLEAdvertisement(uint8_t * event_packet) :
advertising_event_type(event_packet[2]),
rssi(event_packet[10]),
data_length(event_packet[11]),
iBeacon_UUID(NULL) {
bd_addr_t addr;
reverse_bd_addr(&event_packet[4], addr);
bd_addr = BD_ADDR(addr, (BD_ADDR_TYPE)event_packet[3]);
memcpy(data, &event_packet[12], LE_ADVERTISING_DATA_SIZE);
}
BLEAdvertisement::~BLEAdvertisement() {
if (iBeacon_UUID) {
delete (iBeacon_UUID);
}
}
const uint8_t * BLEAdvertisement::getAdvData(void) {
return data;
}
BD_ADDR * BLEAdvertisement::getBdAddr(void) {
return &bd_addr;
}
int BLEAdvertisement::getRssi(void) {
return rssi > 127 ? rssi - 256 : rssi;
}
bool BLEAdvertisement::containsService(UUID * service) {
return ad_data_contains_uuid128(data_length, data, (uint8_t*) service->getUuid());
}
bool BLEAdvertisement::nameHasPrefix(const char * name_prefix) {
ad_context_t context;
int name_prefix_len = strlen(name_prefix);
for (ad_iterator_init(&context, data_length, data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)) {
uint8_t data_type = ad_iterator_get_data_type(&context);
uint8_t data_len = ad_iterator_get_data_len(&context);
const uint8_t * data = ad_iterator_get_data(&context);
int compare_len = name_prefix_len;
switch (data_type) {
case 8: // shortented local name
case 9: // complete local name
if (compare_len > data_len) {
compare_len = data_len;
}
if (strncmp(name_prefix, (const char*) data, compare_len) == 0) {
return true;
}
break;
default:
break;
}
}
return false;
};
bool BLEAdvertisement::isIBeacon(void) {
return ((memcmp(iBeaconAdvertisement01, data, sizeof(iBeaconAdvertisement01)) == 0)
&& (memcmp(iBeaconAdvertisement38, &data[3], sizeof(iBeaconAdvertisement38)) == 0));
}
const UUID * BLEAdvertisement::getIBeaconUUID(void) {
if (!iBeacon_UUID) {
iBeacon_UUID = new UUID(&data[9]);
}
return iBeacon_UUID;
};
uint16_t BLEAdvertisement::getIBeaconMajorID(void) {
return big_endian_read_16(data, 25);
};
uint16_t BLEAdvertisement::getIBecaonMinorID(void) {
return big_endian_read_16(data, 27);
};
uint8_t BLEAdvertisement::getiBeaconMeasuredPower(void) {
return data[29];
}
BLECharacteristic::BLECharacteristic(void) {
}
BLECharacteristic::BLECharacteristic(gatt_client_characteristic_t characteristic)
: characteristic(characteristic), uuid(characteristic.uuid128) {
}
const UUID * BLECharacteristic::getUUID(void) {
return &uuid;
}
bool BLECharacteristic::matches(UUID * uuid) {
return this->uuid.matches(uuid);
}
bool BLECharacteristic::isValueHandle(uint16_t value_handle) {
return characteristic.value_handle == value_handle;
}
const gatt_client_characteristic_t * BLECharacteristic::getCharacteristic(void) {
return &characteristic;
}
BLEService::BLEService(void) {
}
BLEService::BLEService(gatt_client_service_t service)
: service(service), uuid(service.uuid128) {
}
const UUID * BLEService::getUUID(void) {
return &uuid;
}
bool BLEService::matches(UUID * uuid) {
return this->uuid.matches(uuid);
}
const gatt_client_service_t * BLEService::getService(void) {
return &service;
}
// discovery of services and characteristics
BLEDevice::BLEDevice(void) {
}
BLEDevice::BLEDevice(hci_con_handle_t handle)
: handle(handle) {
}
uint16_t BLEDevice::getHandle(void) {
return handle;
}
int BLEDevice::discoverGATTServices(void) {
return BTstack.discoverGATTServices(this);
}
int BLEDevice::discoverCharacteristicsForService(BLEService * service) {
return BTstack.discoverCharacteristicsForService(this, service);
}
int BLEDevice::readCharacteristic(BLECharacteristic * characteristic) {
return BTstack.readCharacteristic(this, characteristic);
}
int BLEDevice::writeCharacteristic(BLECharacteristic * characteristic, uint8_t * data, uint16_t size) {
return BTstack.writeCharacteristic(this, characteristic, data, size);
}
int BLEDevice::writeCharacteristicWithoutResponse(BLECharacteristic * characteristic, uint8_t * data, uint16_t size) {
return BTstack.writeCharacteristicWithoutResponse(this, characteristic, data, size);
}
int BLEDevice::subscribeForNotifications(BLECharacteristic * characteristic) {
return BTstack.subscribeForNotifications(this, characteristic);
}
int BLEDevice::unsubscribeFromNotifications(BLECharacteristic * characteristic) {
return BTstack.unsubscribeFromNotifications(this, characteristic);
}
int BLEDevice::subscribeForIndications(BLECharacteristic * characteristic) {
return BTstack.subscribeForIndications(this, characteristic);
}
int BLEDevice::unsubscribeFromIndications(BLECharacteristic * characteristic) {
return BTstack.unsubscribeFromIndications(this, characteristic);
}
static uint16_t (*gattReadCallback)(uint16_t characteristic_id, uint8_t * buffer, uint16_t buffer_size);
static int (*gattWriteCallback)(uint16_t characteristic_id, uint8_t *buffer, uint16_t buffer_size);
// ATT Client Read Callback for Dynamic Data
// - if buffer == NULL, don't copy data, just return size of value
// - if buffer != NULL, copy data and return number bytes copied
// @param offset defines start of attribute value
static uint16_t att_read_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size) {
(void) con_handle;
(void) offset;
if (gattReadCallback) {
return gattReadCallback(att_handle, buffer, buffer_size);
}
return 0;
}
/* LISTING_END */
/*
@section ATT Write
@text The only valid ATT write in this example is to the Client Characteristic Configuration, which configures notification
and indication. If the ATT handle matches the client configuration handle, the new configuration value is stored and used
in the heartbeat handler to decide if a new value should be sent. See Listing attWrite.
*/
/* LISTING_START(attWrite): ATT Write */
static int att_write_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) {
(void) con_handle;
(void) transaction_mode;
(void) offset;
if (gattWriteCallback) {
gattWriteCallback(att_handle, buffer, buffer_size);
}
return 0;
}
BTstackManager::BTstackManager(void) {
// client_packet_handler = NULL;
have_custom_addr = false;
// reset handler
bleAdvertismentCallback = NULL;
bleDeviceConnectedCallback = NULL;
bleDeviceDisconnectedCallback = NULL;
gattServiceDiscoveredCallback = NULL;
gattCharacteristicDiscoveredCallback = NULL;
gattCharacteristicNotificationCallback = NULL;
att_db_util_init();
// disable LOG_INFO messages
hci_dump_enable_log_level(HCI_DUMP_LOG_LEVEL_INFO, 0);
#ifdef __AVR__
// configure stdout to go via Serial
fdev_setup_stream(&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
stdout = &uartout;
#endif
}
void BTstackManager::setBLEAdvertisementCallback(void (*callback)(BLEAdvertisement * bleAdvertisement)) {
bleAdvertismentCallback = callback;
}
void BTstackManager::setBLEDeviceConnectedCallback(void (*callback)(BLEStatus status, BLEDevice * device)) {
bleDeviceConnectedCallback = callback;
}
void BTstackManager::setBLEDeviceDisconnectedCallback(void (*callback)(BLEDevice * device)) {
bleDeviceDisconnectedCallback = callback;
}
void BTstackManager::setGATTServiceDiscoveredCallback(void (*callback)(BLEStatus status, BLEDevice * device, BLEService * bleService)) {
gattServiceDiscoveredCallback = callback;
}
void BTstackManager::setGATTCharacteristicDiscoveredCallback(void (*callback)(BLEStatus status, BLEDevice * device, BLECharacteristic * characteristic)) {
gattCharacteristicDiscoveredCallback = callback;
}
void BTstackManager::setGATTCharacteristicNotificationCallback(void (*callback)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length)) {
gattCharacteristicNotificationCallback = callback;
}
void BTstackManager::setGATTCharacteristicIndicationCallback(void (*callback)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length)) {
gattCharacteristicIndicationCallback = callback;
}
void BTstackManager::setGATTCharacteristicReadCallback(void (*callback)(BLEStatus status, BLEDevice * device, uint8_t * value, uint16_t length)) {
gattCharacteristicReadCallback = callback;
}
void BTstackManager::setGATTCharacteristicWrittenCallback(void (*callback)(BLEStatus status, BLEDevice * device)) {
gattCharacteristicWrittenCallback = callback;
}
void BTstackManager::setGATTCharacteristicSubscribedCallback(void (*callback)(BLEStatus status, BLEDevice * device)) {
gattCharacteristicSubscribedCallback = callback;
}
void BTstackManager::setGATTCharacteristicUnsubscribedCallback(void (*callback)(BLEStatus status, BLEDevice * device)) {
gattCharacteristicUnsubscribedCallback = callback;
}
int BTstackManager::discoverGATTServices(BLEDevice * device) {
gattAction = gattActionServiceQuery;
return gatt_client_discover_primary_services(gatt_client_callback, device->getHandle());
}
int BTstackManager::discoverCharacteristicsForService(BLEDevice * device, BLEService * service) {
gattAction = gattActionCharacteristicQuery;
return gatt_client_discover_characteristics_for_service(gatt_client_callback, device->getHandle(), (gatt_client_service_t*) service->getService());
}
int BTstackManager::readCharacteristic(BLEDevice * device, BLECharacteristic * characteristic) {
return gatt_client_read_value_of_characteristic(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic());
}
int BTstackManager::writeCharacteristic(BLEDevice * device, BLECharacteristic * characteristic, uint8_t * data, uint16_t size) {
gattAction = gattActionWrite;
return gatt_client_write_value_of_characteristic(gatt_client_callback, device->getHandle(), characteristic->getCharacteristic()->value_handle,
size, data);
}
int BTstackManager::writeCharacteristicWithoutResponse(BLEDevice * device, BLECharacteristic * characteristic, uint8_t * data, uint16_t size) {
return gatt_client_write_value_of_characteristic_without_response(device->getHandle(), characteristic->getCharacteristic()->value_handle,
size, data);
}
int BTstackManager::subscribeForNotifications(BLEDevice * device, BLECharacteristic * characteristic) {
gattAction = gattActionSubscribe;
return gatt_client_write_client_characteristic_configuration(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic(),
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
}
int BTstackManager::subscribeForIndications(BLEDevice * device, BLECharacteristic * characteristic) {
gattAction = gattActionSubscribe;
return gatt_client_write_client_characteristic_configuration(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic(),
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION);
}
int BTstackManager::unsubscribeFromNotifications(BLEDevice * device, BLECharacteristic * characteristic) {
gattAction = gattActionUnsubscribe;
return gatt_client_write_client_characteristic_configuration(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic(),
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE);
}
int BTstackManager::unsubscribeFromIndications(BLEDevice * device, BLECharacteristic * characteristic) {
gattAction = gattActionUnsubscribe;
return gatt_client_write_client_characteristic_configuration(gatt_client_callback, device->getHandle(), (gatt_client_characteristic_t*) characteristic->getCharacteristic(),
GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NONE);
}
void BTstackManager::bleConnect(BLEAdvertisement * advertisement, int timeout_ms) {
bleConnect(advertisement->getBdAddr(), timeout_ms);
}
void BTstackManager::bleConnect(BD_ADDR * address, int timeout_ms) {
bleConnect(address->getAddressType(), address->getAddress(), timeout_ms);
}
void BTstackManager::bleConnect(BD_ADDR_TYPE address_type, const char * address, int timeout_ms) {
(void) address_type;
(void) address;
(void) timeout_ms;
// TODO: implement
// log_error("BTstackManager::bleConnect(BD_ADDR_TYPE address_type, const char * address, int timeout_ms) not implemented");
}
void BTstackManager::bleConnect(BD_ADDR_TYPE address_type, const uint8_t address[6], int timeout_ms) {
gap_connect((uint8_t*)address, (bd_addr_type_t) address_type);
if (!timeout_ms) {
return;
}
btstack_run_loop_set_timer(&connection_timer, timeout_ms);
btstack_run_loop_set_timer_handler(&connection_timer, connection_timeout_handler);
btstack_run_loop_add_timer(&connection_timer);
}
void BTstackManager::bleDisconnect(BLEDevice * device) {
btstack_run_loop_remove_timer(&connection_timer);
gap_disconnect(device->getHandle());
}
void BTstackManager::setPublicBdAddr(bd_addr_t addr) {
have_custom_addr = true;
memcpy(public_bd_addr, addr, 6);
}
void bluetooth_hardware_error(uint8_t error) {
printf("Bluetooth Hardware Error event 0x%02x. Restarting...\n\n\n", error);
#ifdef __AVR__
wdt_enable(WDTO_15MS);
// wait for watchdog to trigger
#endif
#ifdef __arm__
// NVIC_SystemReset();
#endif
while (1);
}
#if 0
static hci_transport_config_uart_t config = {
HCI_TRANSPORT_CONFIG_UART,
115200,
0, // main baudrate
1, // flow control
NULL,
};
#endif
static btstack_packet_callback_registration_t hci_event_callback_registration;
void BTstackManager::setup(void) {
//#ifdef PIN_LED
// pinMode(PIN_LED, OUTPUT);
//#endif
// printf("BTstackManager::setup()\n");
#if 0
btstack_memory_init();
btstack_run_loop_init(btstack_run_loop_embedded_get_instance());
const hci_transport_t * transport = hci_transport_h4_instance(btstack_uart_block_embedded_instance());
hci_init(transport, (void*) &config);
hci_set_chipset(btstack_chipset_em9301_instance());
if (have_custom_addr) {
hci_set_bd_addr(public_bd_addr);
}
hci_set_hardware_error_callback(&bluetooth_hardware_error);
#endif
// inform about BTstack state
hci_event_callback_registration.callback = &packet_handler;
hci_add_event_handler(&hci_event_callback_registration);
l2cap_init();
// setup central device db
le_device_db_init();
sm_init();
att_server_init(att_db_util_get_address(), att_read_callback, att_write_callback);
gatt_client_init();
// setup advertisements params
uint16_t adv_int_min = 0x0030;
uint16_t adv_int_max = 0x0030;
uint8_t adv_type = 0;
bd_addr_t null_addr;
memset(null_addr, 0, 6);
gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00);
// setup advertisements data
int pos = 0;
const uint8_t flags[] = { 0x02, 0x01, 0x02 };
memcpy(&adv_data[pos], flags, sizeof(flags));
pos += sizeof(flags);
const char * name = "BTstack LE Shield";
adv_data[pos++] = strlen(name) + 1;
adv_data[pos++] = 0x09;
memcpy(&adv_data[pos], name, strlen(name));
pos += strlen(name);
adv_data_len = pos;
gap_advertisements_set_data(adv_data_len, adv_data);
// turn on!
btstack_state = 0;
hci_power_control(HCI_POWER_ON);
}
void BTstackManager::enablePacketLogger(void) {
hci_dump_init(hci_dump_embedded_stdout_get_instance());
}
void BTstackManager::enableDebugLogger() {
// enable LOG_INFO messages
hci_dump_enable_log_level(HCI_DUMP_LOG_LEVEL_INFO, 1);
}
void BTstackManager::loop(void) {
// process data from/to Bluetooth module
// hal_uart_dma_process();
// BTstack Run Loop
// btstack_run_loop_embedded_execute_once();
}
void BTstackManager::bleStartScanning(void) {
// printf("Start scanning\n");
gap_start_scan();
}
void BTstackManager::bleStopScanning(void) {
gap_stop_scan();
}
void BTstackManager::setGATTCharacteristicRead(uint16_t (*cb)(uint16_t characteristic_id, uint8_t * buffer, uint16_t buffer_size)) {
gattReadCallback = cb;
}
void BTstackManager::setGATTCharacteristicWrite(int (*cb)(uint16_t characteristic_id, uint8_t *buffer, uint16_t buffer_size)) {
gattWriteCallback = cb;
}
void BTstackManager::addGATTService(UUID * uuid) {
att_db_util_add_service_uuid128((uint8_t*)uuid->getUuid());
}
uint16_t BTstackManager::addGATTCharacteristic(UUID * uuid, uint16_t flags, const char * text) {
return att_db_util_add_characteristic_uuid128((uint8_t*)uuid->getUuid(), flags, ATT_SECURITY_NONE, ATT_SECURITY_NONE, (uint8_t*)text, strlen(text));
}
uint16_t BTstackManager::addGATTCharacteristic(UUID * uuid, uint16_t flags, uint8_t * data, uint16_t data_len) {
return att_db_util_add_characteristic_uuid128((uint8_t*)uuid->getUuid(), flags, ATT_SECURITY_NONE, ATT_SECURITY_NONE, data, data_len);
}
uint16_t BTstackManager::addGATTCharacteristicDynamic(UUID * uuid, uint16_t flags, uint16_t characteristic_id) {
(void) characteristic_id;
return att_db_util_add_characteristic_uuid128((uint8_t*)uuid->getUuid(), flags | ATT_PROPERTY_DYNAMIC, ATT_SECURITY_NONE, ATT_SECURITY_NONE, NULL, 0);
}
void BTstackManager::setAdvData(uint16_t adv_data_len, const uint8_t * adv_data) {
gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data);
}
void BTstackManager::startAdvertising() {
gap_advertisements_enable(1);
}
void BTstackManager::stopAdvertising() {
gap_advertisements_enable(0);
}
void BTstackManager::iBeaconConfigure(UUID * uuid, uint16_t major_id, uint16_t minor_id, uint8_t measured_power) {
memcpy(adv_data, iBeaconAdvertisement01, sizeof(iBeaconAdvertisement01));
adv_data[2] = 0x06;
memcpy(&adv_data[3], iBeaconAdvertisement38, sizeof(iBeaconAdvertisement38));
memcpy(&adv_data[9], uuid->getUuid(), 16);
big_endian_store_16(adv_data, 25, major_id);
big_endian_store_16(adv_data, 27, minor_id);
adv_data[29] = measured_power;
adv_data_len = 30;
gap_advertisements_set_data(adv_data_len, adv_data);
}
// 02 01 06 1A FF 4C 00 02 15 -- F8 97 17 7B AE E8 47 67 8E CC CC 69 4F D5 FC EE -- 12 67 00 02 00 00
// 02 01 06 1a ff 4c 00 02 15 -- FB 0B 57 A2 82 28 44 CD 91 3A 94 A1 22 BA 12 06 -- 00 01 00 02 D1 00
BTstackManager BTstack;
/**
Arduino Wrapper for BTstack
*/
#ifndef __ARDUINO_BTSTACK_H
#define __ARDUINO_BTSTACK_H
#if defined __cplusplus
extern "C" {
#endif
#include "ble/att_db.h"
#include "btstack_util.h"
#include "ble/gatt_client.h"
#include "hci.h"
#include <stdint.h>
typedef enum BLEStatus {
BLE_STATUS_OK,
BLE_STATUS_DONE, // e.g. for service or characteristic discovery done
BLE_STATUS_CONNECTION_TIMEOUT,
BLE_STATUS_CONNECTION_ERROR,
BLE_STATUS_OTHER_ERROR
} BLEStatus;
typedef void (*btstack_packet_handler_t)(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
class UUID {
private:
uint8_t uuid[16];
public:
UUID();
UUID(const uint8_t uuid[16]);
UUID(const char * uuidStr);
const char * getUuidString() const;
const char * getUuid128String() const;
const uint8_t * getUuid(void) const;
bool matches(UUID *uuid) const;
};
typedef enum BD_ADDR_TYPE {
PUBLIC_ADDRESS = 0,
PRIVAT_ADDRESS
} BD_ADDR_TYPE;
class BD_ADDR {
private:
uint8_t address[6];
BD_ADDR_TYPE address_type;
public:
BD_ADDR();
BD_ADDR(const char * address_string, BD_ADDR_TYPE address_type = PUBLIC_ADDRESS);
BD_ADDR(const uint8_t address[6], BD_ADDR_TYPE address_type = PUBLIC_ADDRESS);
const uint8_t * getAddress();
const char * getAddressString();
BD_ADDR_TYPE getAddressType();
};
class BLEAdvertisement {
private:
uint8_t advertising_event_type;
uint8_t rssi;
uint8_t data_length;
uint8_t data[10 + LE_ADVERTISING_DATA_SIZE];
BD_ADDR bd_addr;
UUID * iBeacon_UUID;
public:
BLEAdvertisement(uint8_t * event_packet);
~BLEAdvertisement();
BD_ADDR * getBdAddr();
BD_ADDR_TYPE getBdAddrType();
int getRssi();
bool containsService(UUID * service);
bool nameHasPrefix(const char * namePrefix);
const uint8_t * getAdvData();
bool isIBeacon();
const UUID * getIBeaconUUID();
uint16_t getIBeaconMajorID();
uint16_t getIBecaonMinorID();
uint8_t getiBeaconMeasuredPower();
};
class BLECharacteristic {
private:
gatt_client_characteristic_t characteristic;
UUID uuid;
public:
BLECharacteristic();
BLECharacteristic(gatt_client_characteristic_t characteristic);
const UUID * getUUID();
bool matches(UUID * uuid);
bool isValueHandle(uint16_t value_handle);
const gatt_client_characteristic_t * getCharacteristic();
};
class BLEService {
private:
gatt_client_service_t service;
UUID uuid;
public:
BLEService();
BLEService(gatt_client_service_t service);
const UUID * getUUID();
bool matches(UUID * uuid);
const gatt_client_service_t * getService();
};
class BLEDevice {
private:
hci_con_handle_t handle;
public:
BLEDevice();
BLEDevice(hci_con_handle_t handle);
hci_con_handle_t getHandle();
// discovery of services and characteristics
int discoverGATTServices();
int discoverCharacteristicsForService(BLEService * service);
// read/write
int readCharacteristic(BLECharacteristic * characteristic);
int writeCharacteristic(BLECharacteristic * characteristic, uint8_t * data, uint16_t size);
int writeCharacteristicWithoutResponse(BLECharacteristic * characteristic, uint8_t * data, uint16_t size);
// subscribe/unsubscribe
int subscribeForNotifications(BLECharacteristic * characteristic);
int unsubscribeFromNotifications(BLECharacteristic * characteristic);
int subscribeForIndications(BLECharacteristic * characteristic);
int unsubscribeFromIndications(BLECharacteristic * characteristic);
};
class BTstackManager {
public:
BTstackManager(void);
void setup(void);
void loop(void);
void setPublicBdAddr(bd_addr_t addr);
void enablePacketLogger();
void enableDebugLogger();
void setAdvData(uint16_t size, const uint8_t * data);
void iBeaconConfigure(UUID * uuid, uint16_t major_id, uint16_t minor_id, uint8_t measured_power = 0xc6);
void startAdvertising();
void stopAdvertising();
void bleStartScanning();
void bleStopScanning();
// connection management
void bleConnect(BD_ADDR_TYPE address_type, const uint8_t address[6], int timeout_ms);
void bleConnect(BD_ADDR_TYPE address_type, const char * address, int timeout_ms);
void bleConnect(BD_ADDR * address, int timeout_ms);
void bleConnect(BLEAdvertisement * advertisement, int timeout_ms);
void bleDisconnect(BLEDevice * device);
// discovery of services and characteristics
int discoverGATTServices(BLEDevice * device);
int discoverCharacteristicsForService(BLEDevice * peripheral, BLEService * service);
// read/write
int readCharacteristic(BLEDevice * device, BLECharacteristic * characteristic);
int writeCharacteristic(BLEDevice * device, BLECharacteristic * characteristic, uint8_t * data, uint16_t size);
int writeCharacteristicWithoutResponse(BLEDevice * device, BLECharacteristic * characteristic, uint8_t * data, uint16_t size);
// subscribe/unsubscribe notification and indications
int subscribeForNotifications(BLEDevice * device, BLECharacteristic * characteristic);
int unsubscribeFromNotifications(BLEDevice * device, BLECharacteristic * characteristic);
int subscribeForIndications(BLEDevice * device, BLECharacteristic * characteristic);
int unsubscribeFromIndications(BLEDevice * device, BLECharacteristic * characteristic);
// Callbacks
void setBLEAdvertisementCallback(void (*)(BLEAdvertisement * bleAdvertisement));
void setBLEDeviceConnectedCallback(void (*)(BLEStatus status, BLEDevice * device));
void setBLEDeviceDisconnectedCallback(void (*)(BLEDevice * device));
void setGATTServiceDiscoveredCallback(void (*)(BLEStatus status, BLEDevice * device, BLEService * bleService));
void setGATTCharacteristicDiscoveredCallback(void (*)(BLEStatus status, BLEDevice * device, BLECharacteristic * characteristic));
void setGATTCharacteristicReadCallback(void (*)(BLEStatus status, BLEDevice * device, uint8_t * value, uint16_t length));
void setGATTCharacteristicNotificationCallback(void (*)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length));
void setGATTCharacteristicIndicationCallback(void (*)(BLEDevice * device, uint16_t value_handle, uint8_t* value, uint16_t length));
void setGATTDoneCallback(void (*)(BLEStatus status, BLEDevice * device));
void setGATTCharacteristicWrittenCallback(void (*)(BLEStatus status, BLEDevice * device));
void setGATTCharacteristicSubscribedCallback(void (*)(BLEStatus status, BLEDevice * device));
void setGATTCharacteristicUnsubscribedCallback(void (*)(BLEStatus status, BLEDevice * device));
void setGATTCharacteristicRead(uint16_t (*)(uint16_t characteristic_id, uint8_t * buffer, uint16_t buffer_size));
void setGATTCharacteristicWrite(int (*)(uint16_t characteristic_id, uint8_t *buffer, uint16_t buffer_size));
void addGATTService(UUID * uuid);
uint16_t addGATTCharacteristic(UUID * uuid, uint16_t flags, const char * text);
uint16_t addGATTCharacteristic(UUID * uuid, uint16_t flags, uint8_t * data, uint16_t data_len);
uint16_t addGATTCharacteristicDynamic(UUID * uuid, uint16_t flags, uint16_t characteristic_id);
};
extern BTstackManager BTstack;
#if defined __cplusplus
}
#endif
#endif // __ARDUINO_BTSTACK_H
\ No newline at end of file
......@@ -10,7 +10,7 @@ for dir in ./cores/rp2040 ./libraries/EEPROM ./libraries/I2S ./libraries/SingleF
./libraries/Updater ./libraries/HTTPClient ./libraries/HTTPUpdate \
./libraries/WebServer ./libraries/HTTPUpdateServer ./libraries/DNSServer \
./libraries/Joystick ./libraries/Keyboard ./libraries/Mouse \
./libraries/JoystickBT ./libraries/KeyboardBT ./variants \
./libraries/JoystickBT ./libraries/KeyboardBT ./variants ./libraries/BTstack \
./libraries/MouseBT ./libraries/SerialBT ./libraries/HID_Bluetooth \
./libraries/JoystickBLE ./libraries/KeyboardBLE ./libraries/MouseBLE ; do
find $dir -type f \( -name "*.c" -o -name "*.h" -o -name "*.cpp" \) -a \! -path '*api*' -exec astyle --suffix=none --options=./tests/astyle_core.conf \{\} \;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment