From 1f6fb6def6ad9bd04130563ba147b8748bdf1aec Mon Sep 17 00:00:00 2001 From: lianggao Date: Mon, 17 Oct 2016 16:11:18 +0800 Subject: [PATCH 1/4] Arduino BLE new library init version Know issues: 1. Memory issue. Some times the memcmp not work correct. 2. Central not complete. But can do scan. --- libraries/BLE/examples/test/test.ino | 74 ++ libraries/BLE/src/ArduinoBLE.h | 42 + libraries/BLE/src/BLEAttribute.cpp | 64 ++ libraries/BLE/src/BLEAttribute.h | 73 ++ libraries/BLE/src/BLEAttributeWithValue.h | 63 ++ libraries/BLE/src/BLECallbacks.cpp | 190 ++++ libraries/BLE/src/BLECallbacks.h | 53 ++ libraries/BLE/src/BLECharacteristic.cpp | 365 ++++++++ libraries/BLE/src/BLECharacteristic.h | 506 ++++++++++ libraries/BLE/src/BLECharacteristicImp.cpp | 560 +++++++++++ libraries/BLE/src/BLECharacteristicImp.h | 313 +++++++ libraries/BLE/src/BLECommon.h | 139 +++ libraries/BLE/src/BLEDescriptor.cpp | 127 +++ libraries/BLE/src/BLEDescriptor.h | 98 ++ libraries/BLE/src/BLEDescriptorImp.cpp | 74 ++ libraries/BLE/src/BLEDescriptorImp.h | 69 ++ libraries/BLE/src/BLEDevice.cpp | 433 +++++++++ libraries/BLE/src/BLEDevice.h | 414 ++++++++ libraries/BLE/src/BLEDeviceManager.cpp | 803 ++++++++++++++++ libraries/BLE/src/BLEDeviceManager.h | 415 ++++++++ libraries/BLE/src/BLEProfileManager.cpp | 883 ++++++++++++++++++ libraries/BLE/src/BLEProfileManager.h | 188 ++++ libraries/BLE/src/BLEService.cpp | 194 ++++ libraries/BLE/src/BLEService.h | 137 +++ libraries/BLE/src/BLEServiceImp.cpp | 308 ++++++ libraries/BLE/src/BLEServiceImp.h | 96 ++ libraries/BLE/src/BLEUtils.cpp | 148 +++ libraries/BLE/src/BLEUtils.h | 13 + libraries/BLE/src/LinkList.h | 78 ++ libraries/BLE/src/internal/ble_client.c | 243 +++++ libraries/BLE/src/internal/ble_client.h | 119 +++ libraries/CurieBLE/src/BLECharacteristic.h | 2 +- libraries/CurieBLE/src/BLEPeripheral.h | 2 +- libraries/CurieBLE/src/BLEProfile.cpp | 2 +- system/libarc32_arduino101/bootcode/init.S | 5 +- .../drivers/ipc_uart_ns16550.c | 6 +- .../framework/src/cfw/service_api.c | 5 + .../framework/src/infra/port.c | 3 +- .../framework/src/os/panic.c | 16 + .../framework/src/services/ble/gatt.c | 3 +- .../src/services/ble_service/ble_service.c | 3 + variants/arduino_101/linker_scripts/flash.ld | 8 +- 42 files changed, 7323 insertions(+), 14 deletions(-) create mode 100644 libraries/BLE/examples/test/test.ino create mode 100644 libraries/BLE/src/ArduinoBLE.h create mode 100644 libraries/BLE/src/BLEAttribute.cpp create mode 100644 libraries/BLE/src/BLEAttribute.h create mode 100644 libraries/BLE/src/BLEAttributeWithValue.h create mode 100644 libraries/BLE/src/BLECallbacks.cpp create mode 100644 libraries/BLE/src/BLECallbacks.h create mode 100644 libraries/BLE/src/BLECharacteristic.cpp create mode 100644 libraries/BLE/src/BLECharacteristic.h create mode 100644 libraries/BLE/src/BLECharacteristicImp.cpp create mode 100644 libraries/BLE/src/BLECharacteristicImp.h create mode 100644 libraries/BLE/src/BLECommon.h create mode 100644 libraries/BLE/src/BLEDescriptor.cpp create mode 100644 libraries/BLE/src/BLEDescriptor.h create mode 100644 libraries/BLE/src/BLEDescriptorImp.cpp create mode 100644 libraries/BLE/src/BLEDescriptorImp.h create mode 100644 libraries/BLE/src/BLEDevice.cpp create mode 100644 libraries/BLE/src/BLEDevice.h create mode 100644 libraries/BLE/src/BLEDeviceManager.cpp create mode 100644 libraries/BLE/src/BLEDeviceManager.h create mode 100644 libraries/BLE/src/BLEProfileManager.cpp create mode 100644 libraries/BLE/src/BLEProfileManager.h create mode 100644 libraries/BLE/src/BLEService.cpp create mode 100644 libraries/BLE/src/BLEService.h create mode 100644 libraries/BLE/src/BLEServiceImp.cpp create mode 100644 libraries/BLE/src/BLEServiceImp.h create mode 100644 libraries/BLE/src/BLEUtils.cpp create mode 100644 libraries/BLE/src/BLEUtils.h create mode 100644 libraries/BLE/src/LinkList.h create mode 100644 libraries/BLE/src/internal/ble_client.c create mode 100644 libraries/BLE/src/internal/ble_client.h diff --git a/libraries/BLE/examples/test/test.ino b/libraries/BLE/examples/test/test.ino new file mode 100644 index 00000000..9a32ea8f --- /dev/null +++ b/libraries/BLE/examples/test/test.ino @@ -0,0 +1,74 @@ + +#include "ArduinoBLE.h" +#include "BLEAttribute.h" +#include "BLECharacteristicImp.h" +#include "BLEProfileManager.h" + +// LED pin +#define LED_PIN 13 + +// create service +BLEService ledService("19b10100e8f2537e4f6cd104768a1214"); + +BLECharacteristic switchCharacteristic("19b10101e8f2537e4f6cd104768a1214", BLERead | BLEWrite | BLENotify, 1); + +BLEDescriptor switchDescriptor("2901", "switch"); + +void setup() { + Serial1.begin(115200); + Serial1.println("test---"); + + // set LED pin to output mode + pinMode(LED_PIN, OUTPUT); + + // begin initialization + BLE.begin(); + Serial1.println(BLE.address()); + + // set advertised local name and service UUID + BLE.setLocalName("LED"); + BLE.setAdvertisedServiceUuid(ledService.uuid()); + + // add service and characteristic + BLE.addService(ledService); + ledService.addCharacteristic(switchCharacteristic); + switchCharacteristic.addDescriptor(switchDescriptor); + unsigned char test = 1; + switchCharacteristic.writeValue(&test,1); + BLE.startAdvertising(); +} + +void loop() { + static int i = 0; + BLEDevice central = BLE.central(); + bool temp = central; +i++; + if (temp) { + // central connected to peripheral + Serial1.print(i); + Serial1.print(F("Connected to central: ")); + Serial1.println(central.address()); + + Serial1.print(temp); + + while (central.connected()) { + // central still connected to peripheral + if (switchCharacteristic.written()) { + char ledValue = *switchCharacteristic.value(); + // central wrote new value to characteristic, update LED + if (ledValue) { + Serial1.println(F("LED on")); + digitalWrite(LED_PIN, HIGH); + } else { + Serial1.println(F("LED off")); + digitalWrite(LED_PIN, LOW); + } + } + } + + // central disconnected + Serial1.print(F("Disconnected from central: ")); + Serial1.println(central.address()); + } + //delay (1000); +} diff --git a/libraries/BLE/src/ArduinoBLE.h b/libraries/BLE/src/ArduinoBLE.h new file mode 100644 index 00000000..14fbddcc --- /dev/null +++ b/libraries/BLE/src/ArduinoBLE.h @@ -0,0 +1,42 @@ +/* + BLE API + Copyright (c) 2016 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ARDUINO_BLE_H +#define ARDUINO_BLE_H + +#define ARDUINO_BLE_API_VERSION 10000 // version 1.0.0 + +class BLEDevice; +class BLECharacteristic; +class BLEDescriptor; +class BLEService; +class BLECharacteristicImp; + +#include "BLECommon.h" + +#include "BLEDevice.h" + +#include "BLECharacteristic.h" +#include "BLEDescriptor.h" +#include "BLEService.h" + + +extern BLEDevice BLE; + +#endif diff --git a/libraries/BLE/src/BLEAttribute.cpp b/libraries/BLE/src/BLEAttribute.cpp new file mode 100644 index 00000000..01eef6fe --- /dev/null +++ b/libraries/BLE/src/BLEAttribute.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "ArduinoBLE.h" +#include "BLEAttribute.h" + +#include "BLEUtils.h" + + +BLEAttribute::BLEAttribute(const char* uuid, BLEAttributeType type) : + _type(type) +{ + memset(&_uuid, 0, sizeof (_uuid)); + BLEUtils::uuidString2BT(uuid, (bt_uuid_t*)&_uuid); +} + +BLEAttribute::BLEAttribute(const bt_uuid_t* uuid, BLEAttributeType type) : + _type(type) +{ + memcpy(&_uuid, uuid, sizeof (_uuid)); +} + +const bt_uuid_t *BLEAttribute::bt_uuid(void) +{ + return (bt_uuid_t *)&_uuid; +} + + +BLEAttributeType +BLEAttribute::type() const { + return this->_type; +} + +bool BLEAttribute::compareUuid(const bt_uuid_t* uuid) +{ + int cmpresult = 0; + cmpresult = bt_uuid_cmp(uuid, (const bt_uuid_t*)&_uuid); + return (cmpresult == 0); + +} + +bool BLEAttribute::compareUuid(const char* uuid) +{ + bt_uuid_128_t temp; + BLEUtils::uuidString2BT(uuid,(bt_uuid_t *)&temp); + return compareUuid((bt_uuid_t *)&temp); +} + diff --git a/libraries/BLE/src/BLEAttribute.h b/libraries/BLE/src/BLEAttribute.h new file mode 100644 index 00000000..db69cf32 --- /dev/null +++ b/libraries/BLE/src/BLEAttribute.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _BLE_ATTRIBUTE_H_INCLUDED +#define _BLE_ATTRIBUTE_H_INCLUDED + +#include "BLECommon.h" + +/// BLE attribute tyep enum +typedef enum { + BLETypeService = 0x2800, ///< the service type + BLETypeCharacteristic = 0x2803, ///< the characteristic type + BLETypeDescriptor = 0x2900 ///< the descriptor type +}BLEAttributeType; + + +class BLEAttribute { +public: + /** + * @brief Get the UUID raw data + * + * @param none + * + * @return bt_uuid_t* The pointer of UUID + * + * @note none + */ + const bt_uuid_t *bt_uuid(void); + + /** + * @brief Compare the UUID with the paramater data + * + * @param[in] data The pointer of data + * + * @param[in] uuidsize The max size of UUID + * + * @return bool true - UUID is the same with data + * false- UUID is not the same with data + * + * @note none + */ + bool compareUuid(const char* uuid); + bool compareUuid(const bt_uuid_t* uuid); + + BLEAttributeType type(void) const; + +protected: + BLEAttribute(const char* uuid, BLEAttributeType type); + BLEAttribute(const bt_uuid_t* uuid, BLEAttributeType type); +private: + bt_uuid_128_t _uuid; + + BLEAttributeType _type; + +}; + +#endif // _BLE_ATTRIBUTE_H_INCLUDED diff --git a/libraries/BLE/src/BLEAttributeWithValue.h b/libraries/BLE/src/BLEAttributeWithValue.h new file mode 100644 index 00000000..d1e2c152 --- /dev/null +++ b/libraries/BLE/src/BLEAttributeWithValue.h @@ -0,0 +1,63 @@ +/* + BLE Attribute with value API + Copyright (c) 2016 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ARDUINO_BLE_ATTRIBUTE_WITH_VALUE__H +#define ARDUINO_BLE_ATTRIBUTE_WITH_VALUE__H + +class BLEAttributeWithValue +{ + public: + BLEAttributeWithValue(); + + virtual int valueLength() const; // returns the length of the attribute value + virtual const byte* value() const; // returns the value of the attribute array + virtual byte operator[] (int offset) const; // access an attribute value at the specified offset + virtual bool writeValue(const byte value[], int length); + + // intepret the value of the attribute with the specified type + String stringValue() const; + char charValue() const; + unsigned char unsignedCharValue() const; + byte byteValue() const; + short shortValue() const; + unsigned short unsignedShortValue() const; + int intValue() const; + unsigned int unsignedIntValue() const; + long longValue() const; + unsigned long unsignedLongValue() const; + float floatValue() const; + double doubleValue() const; + + // write the value of the attribute with the specified type + bool writeString(const String& s); + bool writeString(const char* s); + bool writeChar(char c); + bool writeUnsignedChar(unsigned char c); + bool writeByte(byte b); + bool writeShort(short s); + bool writeUnsignedShort(unsigned short s); + bool writeInt(int i); + bool writeUnsignedInt(unsigned int i); + bool writeLong(long l); + bool writeUnsignedLong(unsigned int l); + bool writeFloat(float f); + bool writeDouble(double d); +}; + +#endif diff --git a/libraries/BLE/src/BLECallbacks.cpp b/libraries/BLE/src/BLECallbacks.cpp new file mode 100644 index 00000000..db298071 --- /dev/null +++ b/libraries/BLE/src/BLECallbacks.cpp @@ -0,0 +1,190 @@ + + +#include + +#include "ArduinoBLE.h" + +#include "BLEAttribute.h" +#include "BLECharacteristicImp.h" +#include "BLEDeviceManager.h" +#include "BLEProfileManager.h" + +// GATT Server Only +ssize_t profile_read_process(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + void *buf, uint16_t len, + uint16_t offset) +{ + const unsigned char *pvalue; + BLEAttribute *bleattr = (BLEAttribute *)attr->user_data; + BLEAttributeType type = bleattr->type(); + if (BLETypeCharacteristic == type) + { + BLECharacteristicImp* blecharacteritic = (BLECharacteristicImp*)bleattr; + pvalue = blecharacteritic->value(); + return bt_gatt_attr_read(conn, attr, buf, len, offset, pvalue, + blecharacteritic->valueLength()); + } + else if (BLETypeDescriptor == type) + { + BLEDescriptorImp* bledescriptor = (BLEDescriptorImp*)bleattr; + pvalue = bledescriptor->value(); + return bt_gatt_attr_read(conn, attr, buf, len, offset, pvalue, bledescriptor->valueLength()); + } + return 0; +} + +// GATT server only +ssize_t profile_write_process(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + const void *buf, uint16_t len, + uint16_t offset) +{ + pr_info(LOG_MODULE_BLE, "%s1", __FUNCTION__); + BLEAttribute *bleattr = (BLEAttribute *)attr->user_data; + BLECharacteristicImp* blecharacteritic; + BLEAttributeType type = bleattr->type(); + if ((BLETypeCharacteristic != type) || 0 != offset) + { + return 0; + } + + blecharacteritic = (BLECharacteristicImp*)bleattr; + blecharacteritic->setValue((const uint8_t *) buf, len); + return len; +} + +ssize_t profile_longwrite_process(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, + uint16_t offset) +{ + BLECharacteristicImp *blecharacteritic = (BLECharacteristicImp*)attr->user_data; + + blecharacteritic->setBuffer((const uint8_t *) buf, len, offset); + + return len; +} + +int profile_longflush_process(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + uint8_t flags) +{ + BLECharacteristicImp *blecharacteritic = (BLECharacteristicImp*)attr->user_data; + + switch (flags) + { + case BT_GATT_FLUSH_DISCARD: + /* Discard buffer reseting it back with data */ + blecharacteritic->discardBuffer(); + return 0; + case BT_GATT_FLUSH_SYNC: + /* Sync buffer to data */ + blecharacteritic->syncupBuffer2Value(); + return 0; + } + + return -EINVAL; +} + + +// GATT client only +uint8_t profile_notify_process (bt_conn_t *conn, + bt_gatt_subscribe_params_t *params, + const void *data, uint16_t length) +{ + //BLEPeripheralHelper* peripheral = BLECentralRole::instance()->peripheral(conn);// Find peripheral by bt_conn + //BLEAttribute* notifyatt = peripheral->attribute(params); // Find attribute by params + BLECharacteristicImp* chrc = NULL; + BLEDevice bleDevice(bt_conn_get_dst(conn)); + BLEProfileManager::instance()->characteristic(bleDevice, params->value_handle); + + //assert(notifyatt->type() == BLETypeCharacteristic); + pr_debug(LOG_MODULE_APP, "%s1", __FUNCTION__); + + chrc->setValue((const unsigned char *)data, length); + return BT_GATT_ITER_CONTINUE; +} + +// GATT client only +uint8_t profile_discover_process(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params) +{ + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + return BLEProfileManager::instance()->discoverResponseProc(conn, attr, params); +} + +// GATT Client only +uint8_t profile_read_rsp_process(bt_conn_t *conn, + int err, + bt_gatt_read_params_t *params, + const void *data, + uint16_t length) +{ + if (NULL == data) + { + return BT_GATT_ITER_STOP; + } + BLECharacteristicImp *chrc = NULL; + BLEDevice bleDevice(bt_conn_get_dst(conn)); + + // Get characteristic by handle params->single.handle + chrc = BLEProfileManager::instance()->characteristic(bleDevice, params->single.handle); + + chrc->setValue((const unsigned char *)data, length); + return BT_GATT_ITER_STOP; +} + + + +void bleConnectEventHandler(bt_conn_t *conn, + uint8_t err, + void *param) +{ + BLEDeviceManager* p = (BLEDeviceManager*)param; + + p->handleConnectEvent(conn, err); +} + + +void bleDisconnectEventHandler(bt_conn_t *conn, + uint8_t reason, + void *param) +{ + BLEDeviceManager* p = (BLEDeviceManager*)param; + + pr_info(LOG_MODULE_BLE, "Connect lost. Reason: %d", reason); + + p->handleDisconnectEvent(conn, reason); +} + +void bleParamUpdatedEventHandler(bt_conn_t *conn, + uint16_t interval, + uint16_t latency, + uint16_t timeout, + void *param) +{ + BLEDeviceManager* p = (BLEDeviceManager*)param; + + p->handleParamUpdated(conn, interval, latency, timeout); +} + + +void ble_central_device_found(const bt_addr_le_t *addr, + int8_t rssi, + uint8_t type, + const uint8_t *ad, + uint8_t len) +{ + char dev[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(addr, dev, sizeof(dev)); + //pr_debug(LOG_MODULE_BLE, "[DEVICE]: %s, AD evt type %u, AD data len %u, RSSI %i\n", + // dev, type, len, rssi); + + BLEDeviceManager::instance()->handleDeviceFound(addr, rssi, type, + ad, len); +} + + diff --git a/libraries/BLE/src/BLECallbacks.h b/libraries/BLE/src/BLECallbacks.h new file mode 100644 index 00000000..76144392 --- /dev/null +++ b/libraries/BLE/src/BLECallbacks.h @@ -0,0 +1,53 @@ + +#ifndef __BLECALLBACKS_H__ +#define __BLECALLBACKS_H__ + +uint8_t profile_notify_process (bt_conn_t *conn, + bt_gatt_subscribe_params_t *params, + const void *data, uint16_t length); +uint8_t profile_read_rsp_process(bt_conn_t *conn, int err, + bt_gatt_read_params_t *params, + const void *data, + uint16_t length); +int profile_longflush_process(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + uint8_t flags); +ssize_t profile_longwrite_process(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, + uint16_t offset); +ssize_t profile_write_process(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + const void *buf, uint16_t len, + uint16_t offset); +ssize_t profile_read_process(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + void *buf, uint16_t len, + uint16_t offset); + +uint8_t profile_discover_process(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params); + +void bleConnectEventHandler(bt_conn_t *conn, + uint8_t err, + void *param); + +void bleDisconnectEventHandler(bt_conn_t *conn, + uint8_t reason, + void *param); + +void bleParamUpdatedEventHandler(bt_conn_t *conn, + uint16_t interval, + uint16_t latency, + uint16_t timeout, + void *param); + +void ble_central_device_found(const bt_addr_le_t *addr, + int8_t rssi, + uint8_t type, + const uint8_t *ad, + uint8_t len); + +#endif + diff --git a/libraries/BLE/src/BLECharacteristic.cpp b/libraries/BLE/src/BLECharacteristic.cpp new file mode 100644 index 00000000..88033529 --- /dev/null +++ b/libraries/BLE/src/BLECharacteristic.cpp @@ -0,0 +1,365 @@ + +//#include "internal/ble_client.h" + +#include "BLECharacteristic.h" +#include "BLEProfileManager.h" +#include "BLEUtils.h" + +#include "BLECharacteristicImp.h" + +BLECharacteristic::BLECharacteristic(): + _bledev(), _internal(NULL), _properties(0), + _value_size(0), _value(NULL) +{ + memset(_uuid_cstr, 0, sizeof(_uuid_cstr)); +} + +BLECharacteristic::BLECharacteristic(const char* uuid, + unsigned char properties, + unsigned char valueSize): + _bledev(), _internal(NULL), _properties(properties), _value(NULL) +{ + bt_uuid_128 bt_uuid_tmp; + _value_size = valueSize > BLE_MAX_ATTR_LONGDATA_LEN ? BLE_MAX_ATTR_LONGDATA_LEN : valueSize; + BLEUtils::uuidString2BT(uuid, (bt_uuid_t *)&bt_uuid_tmp); + BLEUtils::uuidBT2String((const bt_uuid_t *)&bt_uuid_tmp, _uuid_cstr); + _bledev.setAddress(*BLEUtils::bleGetLoalAddress()); +} + +BLECharacteristic::BLECharacteristic(const char* uuid, + unsigned char properties, + const char* value): + BLECharacteristic(uuid, properties, strlen(value)) +{ + _setValue((const uint8_t*)value, strlen(value)); +} + +BLECharacteristic::BLECharacteristic(BLECharacteristicImp *characteristicImp, + const BLEDevice *bleDev): + _bledev(bleDev), _internal(characteristicImp), + _value(NULL) +{ + _properties = characteristicImp->properties(); + _value_size = characteristicImp->valueSize(); +} + +BLECharacteristic::~BLECharacteristic() +{ + if (_value) + { + bfree(_value); + _value = NULL; + } +} + +const char* BLECharacteristic::uuid() const +{ + return _uuid_cstr; +} + +unsigned char BLECharacteristic::properties() +{ + unsigned char property = 0; + BLECharacteristicImp *characteristicImp = getImplementation(); + if (NULL != characteristicImp) + { + property = characteristicImp->properties(); + } + return property; +} + +int BLECharacteristic::valueSize() //const +{ + int valuesize = 0; + BLECharacteristicImp *characteristicImp = getImplementation(); + if (NULL != characteristicImp) + { + valuesize = characteristicImp->valueSize(); + } + return valuesize; +} + +const byte* BLECharacteristic::value() //const +{ + const byte* value_temp = NULL; + BLECharacteristicImp *characteristicImp = getImplementation(); + if (NULL != characteristicImp) + { + value_temp = characteristicImp->value(); + } + return value_temp; +} + +int BLECharacteristic::valueLength() //const +{ + int valueLength = 0; + BLECharacteristicImp *characteristicImp = getImplementation(); + if (NULL != characteristicImp) + { + valueLength = characteristicImp->valueLength(); + } + return valueLength; +} + +BLECharacteristic::operator bool() const +{ + return (strlen(_uuid_cstr) > 3); +} + + +byte BLECharacteristic::operator[] (int offset) //const +{ + byte data = 0; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + data = (*characteristicImp)[offset]; + } + return data; +} + +bool BLECharacteristic::writeValue(const byte value[], int length) +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + characteristicImp->setValue((const unsigned char *)value, (uint16_t)length); + retVar = true; + } + return retVar; +} + +bool BLECharacteristic::writeValue(const byte value[], int length, int offset) +{ + // TODO: Not support it now. + // Will add this feature. + return false; +} + +bool BLECharacteristic::writeValue(const char* value) +{ + return writeValue((const byte*)value, strlen(value)); +} + +bool BLECharacteristic::broadcast() +{ + // TODO: Need more information + return false; +} + +bool BLECharacteristic::written() +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->written(); + } + return retVar; +} + +bool BLECharacteristic::subscribed() +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->subscribed(); + } + return retVar; +} + +bool BLECharacteristic::canNotify() +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->canNotify(); + } + return retVar; +} + +bool BLECharacteristic::canIndicate() +{ + // TODO: Need more confirmation + return false; +} + +bool BLECharacteristic::canRead() +{ + // TODO: Need more confirmation + return false; +} +bool BLECharacteristic::canWrite() +{ + // TODO: Need more confirmation + return false; +} +bool BLECharacteristic::canSubscribe() +{ + // TODO: Need more confirmation + return false; +} +bool BLECharacteristic::canUnsubscribe() +{ + // TODO: Need more confirmation + return false; +} + +bool BLECharacteristic::read() +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->read(); + } + return retVar; +} + +bool BLECharacteristic::write(const unsigned char* value, int length) +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->write(value, (uint16_t)length); + } + return retVar; +} + +bool BLECharacteristic::subscribe() +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + // TODO: Central feature. Peripheral first + //retVar = characteristicImp->s(); + } + return retVar; +} + +bool BLECharacteristic::unsubscribe() +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + // TODO: Central feature + //retVar = characteristicImp->canNotify(); + } + return retVar; +} + +bool BLECharacteristic::valueUpdated() +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->valueUpdated(); + } + return retVar; +} + +int BLECharacteristic::addDescriptor(BLEDescriptor& descriptor) +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->addDescriptor(descriptor); + } + return retVar; +} + +int BLECharacteristic::descriptorCount() //const +{ + int count = 0; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + count = characteristicImp->descriptorCount(); + } + return count; +} + +bool BLECharacteristic::hasDescriptor(const char* uuid) const +{ + // TODO: Not support now + return false; +} +bool BLECharacteristic::hasDescriptor(const char* uuid, int index) const +{ + // TODO: Not support now + return false; +} +BLEDescriptor BLECharacteristic::descriptor(int index) const +{ + // TODO: Not support now + return BLEDescriptor(); +} +BLEDescriptor BLECharacteristic::descriptor(const char * uuid) const +{ + // TODO: Not support now + return BLEDescriptor(); +} +BLEDescriptor BLECharacteristic::descriptor(const char * uuid, int index) const +{ + // TODO: Not support now + return BLEDescriptor(); +} +void BLECharacteristic::setEventHandler(BLECharacteristicEvent event, + BLECharacteristicEventHandler eventHandler) +{} + + +void +BLECharacteristic::_setValue(const uint8_t value[], uint16_t length) +{ + if (length > _value_size) { + length = _value_size; + } + + if (NULL == _value) + { + // Allocate the buffer for characteristic + _value = (unsigned char*)balloc(_value_size, NULL);//malloc(_value_size) + } + if (NULL == _value) + { + return; + } + memcpy(_value, value, length); +} + +BLECharacteristicImp* BLECharacteristic::getImplementation() +{ + if (NULL == _internal) + { + _internal = BLEProfileManager::instance()->characteristic(_bledev, (const char*)_uuid_cstr); + } + return _internal; +} + +void BLECharacteristic::setBLECharacteristicImp(BLECharacteristicImp *characteristicImp) +{ + _internal = characteristicImp; +} + + diff --git a/libraries/BLE/src/BLECharacteristic.h b/libraries/BLE/src/BLECharacteristic.h new file mode 100644 index 00000000..19a10266 --- /dev/null +++ b/libraries/BLE/src/BLECharacteristic.h @@ -0,0 +1,506 @@ +/* + BLE Characteristic API + Copyright (c) 2016 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ARDUINO_BLE_CHARACTERISTIC_H +#define ARDUINO_BLE_CHARACTERISTIC_H + +//#include "BLEAttributeWithValue.h" +#include "BLECommon.h" +//#include "BLEDescriptor.h" +#include "ArduinoBLE.h" + +#include "BLEDevice.h" +//#include "BLECharacteristicImp.h" +//class BLEDescriptorImp; + +enum BLECharacteristicEvent { + BLEWritten = 0, + BLESubscribed = 1, + BLEUnsubscribed = 2, + BLEValueUpdated = 3, + BLECharacteristicEventLast +}; + +enum BLEProperty { + BLEBroadcast = 0x01, + BLERead = 0x02, + BLEWriteWithoutResponse = 0x04, + BLEWrite = 0x08, + BLENotify = 0x10, + BLEIndicate = 0x20 +}; + +typedef void (*BLECharacteristicEventHandler)(BLEDevice& bledev, BLECharacteristic& characteristic); + +//#include "BLECharacteristicImp.h" + +class BLECharacteristic //: public BLEAttributeWithValue +{ +public: + BLECharacteristic(); + /** + * @brief Create a characteristic with specified value size + * + * @param uuid The UUID of the characteristic + * + * @param properties The properties of the characteristic + * + * @param valueSize The size of the characteristic data + * + * @return none + * + * @note none + */ + BLECharacteristic(const char* uuid, + unsigned char properties, + unsigned char valueSize); + + /** + * @brief Create a characteristic with string value + * + * @param uuid The UUID of the characteristic + * + * @param properties The properties of the characteristic + * + * @param value The string of the characteristic data + * + * @return none + * + * @note The data length is string's size. Can't set a string that is longger + * than the this input + */ + BLECharacteristic(const char* uuid, + unsigned char properties, + const char* value); + + virtual ~BLECharacteristic(); + + /** + * @brief Is the characteristic valid + * + * @param none + * + * @return bool true/false + * + * @note Invalid characteristic is NULL pointer or all zero with UUID + */ + virtual operator bool() const; // + + /** + * @brief Get the characteristic's UUID string + * + * @param none + * + * @return const char* The UUID string + * + * @note none + */ + const char* uuid() const; + + /** + * @brief Get the property mask of the characteristic + * + * @param none + * + * @return unsigned char The property mask of the characteristic + * + * @note none + */ + unsigned char properties(); + + /** + * @brief Get the maximum size of the value + * + * @param none + * + * @return int The maximum size of the value + * + * @note none + */ + int valueSize();//const + + /** + * @brief Get the value buffer + * + * @param none + * + * @return const byte* The value buffer + * + * @note none + */ + virtual const byte* value();//const + + /** + * @brief Get the current length of the value + * + * @param none + * + * @return int The current length of the value string + * + * @note TODO: How to handle if the data is RAW data? This API is danger + */ + virtual int valueLength();//const + + /** + * @brief Get a byte of the value at the specified offset + * + * @param none + * + * @return byte A byte of the value at the specified offset + * + * @note none + */ + virtual byte operator[] (int offset);//const + + /** + * @brief Write the value of the characteristic + * + * @param value The value buffer that want to write to characteristic + * + * @param length The value buffer's length + * + * @return bool true - Success, false - Failed + * + * @note none + */ + virtual bool writeValue(const byte value[], int length); + + /** + * @brief Write the value of the characteristic + * + * @param value The value buffer that want to write to characteristic + * + * @param length The value buffer's length + * + * @param offset The offset in the characteristic's data + * + * @return bool true - Success, false - Failed + * + * @note none + */ + bool writeValue(const byte value[], int length, int offset); + + /** + * @brief Write the value of the characteristic + * + * @param value The value string that want to write to characteristic + * + * @return bool true - Success, false - Failed + * + * @note none + */ + bool writeValue(const char* value); + + // peripheral mode + bool broadcast(); // broadcast the characteristic value in the advertisement data + + // GATT server + /** + * @brief Has the GATT client written a new value + * + * @param none + * + * @return bool true - Written, false - Not changed + * + * @note GATT server only. GATT client always return false. + */ + bool written(); + + /** + * @brief Is the GATT client subscribed + * + * @param none + * + * @return bool true - Subscribed, false - Not subscribed + * + * @note GATT server and client + */ + bool subscribed(); + + /** + * @brief Can a notification be sent to the GATT client + * + * @param none + * + * @return true - Yes, false - No + * + * @note GATT server only + */ + bool canNotify(); + + /** + * @brief Can a indication be sent to the GATT client + * + * @param none + * + * @return true - Yes, false - No + * + * @note GATT server only + */ + bool canIndicate(); + + // GATT + /** + * @brief Can the characteristic be read (based on properties) + * + * @param none + * + * @return true - readable, false - None + * + * @note none + */ + bool canRead(); + + /** + * @brief Can the characteristic be written (based on properties) + * + * @param none + * + * @return true - writable, false - None + * + * @note none + */ + bool canWrite(); + + /** + * @brief Can the characteristic be subscribed to (based on properties) + * + * @param none + * + * @return true - Can be subscribed, false - No + * + * @note What different with canUnsubscribe? + */ + bool canSubscribe(); + + /** + * @brief Can the characteristic be unsubscribed to (based on properties) + * + * @param none + * + * @return true - Can be unsubscribed, false - No + * + * @note none + */ + bool canUnsubscribe(); + + /** + * @brief Read the characteristic value + * + * @param none + * + * @return bool true - Success, false - Failed + * + * @note Only for GATT client. Schedule read request to the GATT server + */ + virtual bool read(); + + /** + * @brief Write the charcteristic value + * + * @param value The value buffer that want to write to characteristic + * + * @param length The value buffer's length + * + * @return bool true - Success, false - Failed + * + * @note Only for GATT client. Schedule write request to the GATT server + */ + virtual bool write(const unsigned char* value, int length); + + /** + * @brief Subscribe to the characteristic + * + * @param none + * + * @return bool true - Success, false - Failed + * + * @note Only for GATT client. Schedule CCCD to the GATT server + */ + bool subscribe(); + + /** + * @brief Unsubscribe to the characteristic + * + * @param none + * + * @return bool true - Success, false - Failed + * + * @note Only for GATT client. Schedule CCCD to the GATT server + */ + bool unsubscribe(); + + bool valueUpdated(); // Read response updated the characteristic + + /** + * @brief Add the characteristic's descriptor + * + * @param descriptor The descriptor for characteristic + * + * @return none + * + * @note none + */ + int addDescriptor(BLEDescriptor& descriptor); + + /** + * @brief Get the number of descriptors the characteristic has + * + * @param none + * + * @return int the number of descriptors the characteristic has + * + * @note none + */ + int descriptorCount();//const + + /** + * @brief Does the characteristic have a descriptor with the specified UUID + * + * @param uuid The descriptor's UUID + * + * @return bool true - Yes. false - No + * + * @note none + */ + bool hasDescriptor(const char* uuid) const; + + /** + * @brief Does the characteristic have an nth descriptor with the specified UUID + * + * @param uuid The descriptor's UUID + * + * @param index The index of descriptor + * + * @return bool true - Yes. false - No + * + * @note none + */ + bool hasDescriptor(const char* uuid, int index) const; + + /** + * @brief Get the nth descriptor of the characteristic + * + * @param index The index of descriptor + * + * @return BLEDescriptor The descriptor + * + * @note none + */ + BLEDescriptor descriptor(int index) const; + + /** + * @brief Get the descriptor with the specified UUID + * + * @param uuid The descriptor's UUID + * + * @return BLEDescriptor The descriptor + * + * @note none + */ + BLEDescriptor descriptor(const char * uuid) const; + + /** + * @brief Get the nth descriptor with the specified UUID + * + * @param uuid The descriptor's UUID + * + * @param index The index of descriptor + * + * @return BLEDescriptor The descriptor + * + * @note none + */ + BLEDescriptor descriptor(const char * uuid, int index) const; + + /** + * @brief Set an event handler (callback) + * + * @param event Characteristic event + * + * @param eventHandler The handler of characteristic + * + * @return none + * + * @note none + */ + void setEventHandler(BLECharacteristicEvent event, + BLECharacteristicEventHandler eventHandler); + +protected: + friend class BLEDevice; + friend class BLEService; + /** + * @brief Create a characteristic with specified value size + * + * @param characteristicImp The implementation of the characteristic + * + * @param bleDev The peer BLE device + * + * @return none + * + * @note none + */ + BLECharacteristic(BLECharacteristicImp *characteristicImp, + const BLEDevice *bleDev); + + /** + * @brief Create a characteristic with string value + * + * @param uuid The UUID of the characteristic + * + * @param properties The properties of the characteristic + * + * @param value The string of the characteristic data + * + * @param bleDev The peer BLE device + * + * @return none + * + * @note The data length is string's size. Can't set a string that is longger + * than the this input + */ + //BLECharacteristic(const char* uuid, + // unsigned char properties, + // const char* value, + // BLEDevice *bleDev); + + // For GATT + void setBLECharacteristicImp(BLECharacteristicImp *characteristicImp); + +private: + void _setValue(const uint8_t value[], uint16_t length); + BLECharacteristicImp *getImplementation(); + +private: + char _uuid_cstr[37]; // The characteristic UUID + BLEDevice _bledev; // The GATT server BLE object. Only for GATT client to read/write + // NULL - GATT server + // None-NULL - GATT client + BLECharacteristicImp *_internal; // The real implementation of characteristic. +protected: + friend class BLECharacteristicImp; + unsigned char _properties; // The characteristic property + + unsigned char _value_size; // The value size + unsigned char* _value; // The value. Will delete after create the _internal +}; + +#endif + diff --git a/libraries/BLE/src/BLECharacteristicImp.cpp b/libraries/BLE/src/BLECharacteristicImp.cpp new file mode 100644 index 00000000..34758015 --- /dev/null +++ b/libraries/BLE/src/BLECharacteristicImp.cpp @@ -0,0 +1,560 @@ +/* + * Copyright (c) 2015 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "BLEAttribute.h" +#include "BLECharacteristicImp.h" + +#include "BLECallbacks.h" +#include "BLEUtils.h" + +bt_uuid_16_t BLECharacteristicImp::_gatt_chrc_uuid = {BT_UUID_TYPE_16, BT_UUID_GATT_CHRC_VAL}; +bt_uuid_16_t BLECharacteristicImp::_gatt_ccc_uuid = {BT_UUID_TYPE_16, BT_UUID_GATT_CCC_VAL}; + +BLECharacteristicImp::BLECharacteristicImp(BLECharacteristic& characteristic, + const BLEDevice& bledevice): + BLEAttribute(characteristic.uuid(), BLETypeCharacteristic), + _value_length(0), + _value_buffer(NULL), + _value_updated(false), + _attr_chrc_declaration(NULL), + _attr_chrc_value(NULL), + _attr_cccd(NULL), + _ble_device() +{ + unsigned char properties = characteristic._properties; + _value_size = characteristic._value_size; + _value = (unsigned char*)balloc(_value_size, NULL); + if (_value_size > BLE_MAX_ATTR_DATA_LEN) + { + _value_buffer = (unsigned char*)balloc(_value_size, NULL); + } + + memset(&_ccc_cfg, 0, sizeof(_ccc_cfg)); + memset(&_ccc_value, 0, sizeof(_ccc_value)); + memset(&_gatt_chrc, 0, sizeof(_gatt_chrc)); + memset(&_sub_params, 0, sizeof(_sub_params)); + + _ccc_value.cfg = &_ccc_cfg; + _ccc_value.cfg_len = 1; + if (BLERead & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_READ; + } + if (BLEWrite & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_WRITE; + } + if (BLEWriteWithoutResponse & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_WRITE_WITHOUT_RESP; + } + if (BLENotify & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_NOTIFY; + _sub_params.value |= BT_GATT_CCC_NOTIFY; + } + if (BLEIndicate & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_INDICATE; + _sub_params.value |= BT_GATT_CCC_INDICATE; + } + _gatt_chrc.uuid = (bt_uuid_t*)this->bt_uuid();//&_characteristic_uuid;//this->uuid(); + memset(_event_handlers, 0, sizeof(_event_handlers)); + + _sub_params.notify = profile_notify_process; + + if (NULL != characteristic._value) + { + memcpy(_value, characteristic._value, _value_size); + } + + // Update BLE device object + _ble_device.setAddress(*bledevice.bt_le_address()); + + characteristic.setBLECharacteristicImp(this); + memset(&_descriptors_header, 0, sizeof(_descriptors_header)); +} + +BLECharacteristicImp::~BLECharacteristicImp() +{ + releaseDescriptors(); + if (_value) { + bfree(_value); + _value = NULL; + } + if (_value_buffer) + { + bfree(_value_buffer); + _value_buffer = NULL; + } +} + +unsigned char +BLECharacteristicImp::properties() const +{ + return _gatt_chrc.properties; +} + +bool BLECharacteristicImp::writeValue(const byte value[], int length) +{ + int status; + + _setValue(value, length); + + // Address same is GATT server. Send notification if CCCD enabled + // Different is GATT client. Send write request + if (true == BLEUtils::isLocalBLE(_ble_device) && + NULL != _attr_chrc_value) + { + // Notify for peripheral. + status = bt_gatt_notify(NULL, _attr_chrc_value, value, length, NULL); + if (0 != status) + { + return false; + } + } + + //Not schedule write request for central + // The write request may failed. + // If user want to get latest set value. Call read and get the real value + return true; +} + +bool +BLECharacteristicImp::setValue(const unsigned char value[], uint16_t length) +{ + _setValue(value, length); + // Read response/Notification/Indication for GATT client + // Write request for GATT server + if (_event_handlers[BLEWritten]) + { + BLECharacteristic chrcTmp(this, &_ble_device); + _event_handlers[BLEWritten](_ble_device, chrcTmp); + } + + return true; +} + +unsigned short +BLECharacteristicImp::valueSize() const +{ + return _value_size; +} + +const unsigned char* +BLECharacteristicImp::value() const +{ + return _value; +} + +unsigned short +BLECharacteristicImp::valueLength() const +{ + return _value_length; +} + +unsigned char +BLECharacteristicImp::operator[] (int offset) const +{ + return _value[offset]; +} + +bool +BLECharacteristicImp::written() +{ + bool written = false; + if (true == BLEUtils::isLocalBLE(_ble_device)) + { + // GATT server. The characteristic on local device + written = _value_updated; + _value_updated = false; + } + + return written; +} + +bool BLECharacteristicImp::valueUpdated() +{ + bool updated = false; + if (false == BLEUtils::isLocalBLE(_ble_device)) + { + // GATT client. The characteristic on remote device. + updated = _value_updated; + _value_updated = false; + } + return updated; +} + +bool +BLECharacteristicImp::subscribed() +{ + return (_gatt_chrc.properties & (BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE)); +} + +void +BLECharacteristicImp::setEventHandler(BLECharacteristicEvent event, BLECharacteristicEventHandler callback) +{ + noInterrupts(); + if (event < sizeof(_event_handlers)) { + _event_handlers[event] = callback; + } + interrupts(); +} + +uint16_t +BLECharacteristicImp::valueHandle() +{ + uint16_t handle = 0; + if (NULL != _attr_chrc_value) + { + handle = _attr_chrc_value->handle; + } + + return handle; +} + +uint16_t +BLECharacteristicImp::cccdHandle() +{ + uint16_t handle = 0; + if (NULL != _attr_cccd) + { + handle = _attr_cccd->handle; + } + return handle; +} + +void +BLECharacteristicImp::_setValue(const uint8_t value[], uint16_t length) +{ + if (length > _value_size) { + length = _value_size; + } + + _value_updated = true; + memcpy(_value, value, length); + _value_length = length; +} + +_bt_gatt_ccc_t* BLECharacteristicImp::getCccCfg(void) +{ + return &_ccc_value; +} + +bt_gatt_chrc_t* BLECharacteristicImp::getCharacteristicAttValue(void) +{ + return &_gatt_chrc; +} + +uint8_t BLECharacteristicImp::getPermission(void) +{ + uint8_t perm = 0; + if (_gatt_chrc.properties & BT_GATT_CHRC_READ) + { + perm |= BT_GATT_PERM_READ; + } + if (_gatt_chrc.properties & (BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP)) + { + perm |= BT_GATT_PERM_WRITE; + } + return perm; +} + +bt_uuid_t* BLECharacteristicImp::getCharacteristicAttributeUuid(void) +{ + return (bt_uuid_t*) &_gatt_chrc_uuid; +} + +bt_uuid_t* BLECharacteristicImp::getClientCharacteristicConfigUuid(void) +{ + return (bt_uuid_t*) &_gatt_ccc_uuid; +} + +#if 0 +void BLECharacteristicImp::discover(bt_gatt_discover_params_t *params) +{ + params->type = BT_GATT_DISCOVER_CHARACTERISTIC; + params->uuid = this->uuid(); + // Start discovering + _discoverying = true; + // Re-Init the read/write parameter + _reading = false; +} + + +void BLECharacteristicImp::discover(const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params) +{ + if (!attr) + { + // Discovery complete + _discoverying = false; + return; + } + + // Chracteristic Char + if (params->uuid == this->uuid()) + { + // Set Discover CCCD parameter + params->start_handle = attr->handle + 2; + if (subscribed()) + { + // Include CCCD + params->type = BT_GATT_DISCOVER_DESCRIPTOR; + params->uuid = this->getClientCharacteristicConfigUuid(); + } + else + { + // Complete the discover + _discoverying = false; + } + } + else if (params->uuid == this->getClientCharacteristicConfigUuid()) + { + params->start_handle = attr->handle + 1; + _discoverying = false; + } +} +#endif + +bt_gatt_subscribe_params_t *BLECharacteristicImp::getSubscribeParams() +{ + return &_sub_params; +} + +bool BLECharacteristicImp::read() +{ + int retval = 0; + bt_conn_t* conn = NULL; + + if (true == BLEUtils::isLocalBLE(_ble_device)) + { + // GATT server can't write + return false; + } + + if (_reading) + { + // Already in reading state + return false; + } + + _read_params.func = profile_read_rsp_process; + _read_params.handle_count = 1; + _read_params.single.handle = _value_handle; + _read_params.single.offset = 0; + + if (0 == _read_params.single.handle) + { + // Discover not complete + return false; + } + + conn = bt_conn_lookup_addr_le(_ble_device.bt_le_address()); + if (NULL == conn) + { + return false; + } + + // Send read request + retval = bt_gatt_read(conn, &_read_params); + bt_conn_unref(conn); + if (0 == retval) + { + _reading = true; + } + return _reading; +} + +bool BLECharacteristicImp::write(const unsigned char value[], + uint16_t length) +{ + int retval = 0; + bt_conn_t* conn = NULL; + + if (true == BLEUtils::isLocalBLE(_ble_device)) + { + // GATT server can't write + return false; + } + + conn = bt_conn_lookup_addr_le(_ble_device.bt_le_address()); + if (NULL == conn) + { + return false; + } + + // Send read request + retval = bt_gatt_write_without_response(conn, + _value_handle, + value, + length, + false); + bt_conn_unref(conn); + return (0 == retval); +} + +void BLECharacteristicImp::setBuffer(const uint8_t value[], + uint16_t length, + uint16_t offset) +{ + if (length + offset > _value_size) { + // Ignore the data + return; + } + + memcpy(_value_buffer + offset, value, length); +} + +void BLECharacteristicImp::syncupBuffer2Value() +{ + setValue(_value_buffer, _value_size); +} + +void BLECharacteristicImp::discardBuffer() +{ + memcpy(_value_buffer, _value, _value_size); +} + +bool BLECharacteristicImp::longCharacteristic() +{ + return (_value_size > BLE_MAX_ATTR_DATA_LEN); +} + +int BLECharacteristicImp::updateProfile(bt_gatt_attr_t *attr_start, int& index) +{ + bt_gatt_attr_t *start = attr_start; + int base_index = index; + int offset = 0; + int counter = 0; + + // Characteristic declare + memset(start, 0, sizeof(bt_gatt_attr_t)); + start->uuid = getCharacteristicAttributeUuid(); + start->perm = BT_GATT_PERM_READ; + start->read = bt_gatt_attr_read_chrc; + start->user_data = this->getCharacteristicAttValue(); + pr_info(LOG_MODULE_BLE, "chrc-%p, uuid type-%d", start, start->uuid->type); + + start++; + index++; + counter++; + + // Descriptor + memset(start, 0, sizeof(bt_gatt_attr_t)); + start->uuid = (bt_uuid_t *)bt_uuid(); + start->perm = this->getPermission(); + start->user_data = (void*)((BLEAttribute*)this); + start->read = profile_read_process; + + if (this->longCharacteristic() == false) + { + // Normal characteristic MAX. 20 + start->write = profile_write_process; + } + else + { + // Long characteristic. MAX. 512 + start->write = profile_longwrite_process; + start->flush = profile_longflush_process; + } + _attr_chrc_value = start; + pr_debug(LOG_MODULE_BLE, "chrcdescripor-%p, chimp-%p type-%d", start, this, this->type()); + + start++; + index++; + counter++; + + if (this->subscribed()) + { + // Descriptor + memset(start, 0, sizeof(bt_gatt_attr_t)); + start->uuid = this->getClientCharacteristicConfigUuid(); + start->perm = BT_GATT_PERM_READ | BT_GATT_PERM_WRITE; + start->read = bt_gatt_attr_read_ccc; + start->write = bt_gatt_attr_write_ccc; + start->user_data = this->getCccCfg(); + + pr_info(LOG_MODULE_BLE, "cccd-%p", start); + + start++; + index++; + counter++; + } + + BLEDescriptorNodePtr node = _descriptors_header.next; + while (NULL != node) + { + BLEDescriptorImp *descriptorImp = node->value; + start = attr_start + index - base_index; + offset = descriptorImp->updateProfile(start, index); + counter += offset; + node = node->next; + } + pr_debug(LOG_MODULE_BLE, "%s:type-%d", __FUNCTION__, this->type()); + return counter; +} + +int BLECharacteristicImp::addDescriptor(BLEDescriptor& descriptor) +{ + BLEDescriptorImp* descriptorImp = new BLEDescriptorImp(_ble_device, descriptor); + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + if (NULL == descriptorImp) + { + return BLE_STATUS_NO_MEMORY; + } + + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + BLEDescriptorNodePtr node = link_node_create(descriptorImp); + if (NULL == node) + { + delete[] descriptorImp; + return BLE_STATUS_NO_MEMORY; + } + link_node_insert_last(&_descriptors_header, node); + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + return BLE_STATUS_SUCCESS; +} + +void BLECharacteristicImp::releaseDescriptors() +{ + BLEDescriptorNodePtr node = link_node_get_first(&_descriptors_header); + + while (NULL != node) + { + BLEDescriptorImp* descriptorImp = node->value; + delete[] descriptorImp; + link_node_remove_first(&_descriptors_header); + node = link_node_get_first(&_descriptors_header); + } +} + +int BLECharacteristicImp::getAttributeCount() +{ + int counter = link_list_size(&_descriptors_header) + 2; // Declaration and descriptor + return counter; +} + +int BLECharacteristicImp::descriptorCount() const +{ + int counter = link_list_size(&_descriptors_header); + return counter; +} + + diff --git a/libraries/BLE/src/BLECharacteristicImp.h b/libraries/BLE/src/BLECharacteristicImp.h new file mode 100644 index 00000000..be95fe59 --- /dev/null +++ b/libraries/BLE/src/BLECharacteristicImp.h @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2015 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _BLE_CHARACTERISTICIMP_H_INCLUDED +#define _BLE_CHARACTERISTICIMP_H_INCLUDED + +//#include "BLECommon.h" + +//#include "BLEDevice.h" +//#include "BLEDescriptor.h" + +#include "ArduinoBLE.h" +#include "BLEDescriptorImp.h" + +#include "BLEDevice.h" + +#include "LinkList.h" +class BLEDescriptorImp; +/** + * BLE GATT Characteristic : public BLEAttribute + */ +class BLECharacteristicImp: public BLEAttribute{ +public: + + virtual ~BLECharacteristicImp(); + + + /** + * @brief Add the characteristic's descriptor + * + * @param descriptor The descriptor for characteristic + * + * @return none + * + * @note none + */ + int addDescriptor(BLEDescriptor& descriptor); + + void releaseDescriptors(); + + /** + * @brief Write the value of the characteristic + * + * @param value The value buffer that want to write to characteristic + * + * @param length The value buffer's length + * + * @param offset The offset in the characteristic's data + * + * @return bool true - Success, false - Failed + * + * @note none + */ + bool writeValue(const byte value[], int length); + + /** + * Set the current value of the Characteristic + * + * @param[in] value New value to set, as a byte array. Data is stored in internal copy. + * @param[in] length Length, in bytes, of valid data in the array to write. + * Must not exceed maxLength set for this characteristic. + * + * @return bool true set value success, false on error + */ + bool setValue(const unsigned char value[], unsigned short length); + + /** + * Get the property mask of the Characteristic + * + * @return unsigned char property mask of the Characteristic + */ + unsigned char properties(void) const; + + /** + * Get the (maximum) size of the Characteristic + * + * @return unsigned size of characateristic in bytes + */ + unsigned short valueSize(void) const; + + /** + * Get data pointer to the value of the Characteristic + * + * @return const unsigned char* pointer to the value of the Characteristic + */ + const unsigned char* value(void) const; + + /** + * Get the current length of the value of the Characteristic + * + * @return unsigned short size of characateristic value in bytes + */ + unsigned short valueLength() const; + + unsigned char operator[] (int offset) const; + + /** + * Has the value of the Characteristic been written by a central + * + * @return bool true is central has updated characteristic value, otherwise false + */ + bool written(void); + bool valueUpdated(); + + /** + * Is a central listening for notifications or indications of the Characteristic + * + * @return bool true is central is subscribed, otherwise false + */ + bool subscribed(void); + bool canNotify(); + + /** + * Provide a function to be called when events related to this Characteristic are raised + * + * @param[in] event Event type to set event handler for + * @param[in] callback Pointer to callback function to invoke when the event occurs. + */ + void setEventHandler(BLECharacteristicEvent event, BLECharacteristicEventHandler callback); + + /** + * @brief Schedule the read request to read the characteristic in peripheral + * + * @param[in] none + * + * @return bool Indicate the success or error + * + * @note Only for central device + */ + bool read(); + + /** + * @brief Schedule the write request to update the characteristic in peripheral + * + * @param[in] peripheral The peripheral device that want to be updated + * @param[in] value New value to set, as a byte array. Data is stored in internal copy. + * @param[in] length Length, in bytes, of valid data in the array to write. + * Must not exceed maxLength set for this characteristic. + * + * @return bool true set value success, false on error + * + * @note none + */ + bool write(const unsigned char value[], + uint16_t length); + + int descriptorCount() const; + +protected: + friend class BLEProfileManager; + friend class BLEServiceImp; + /** + * Constructor for BLE Characteristic + * + * @param[in] characteristic The characteristic + * @param[in] bledevice The device that has this characteristic + */ + BLECharacteristicImp(BLECharacteristic& characteristic, const BLEDevice& bledevice); + + friend int profile_longflush_process(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + uint8_t flags); + friend ssize_t profile_longwrite_process(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, + uint16_t offset); + + int updateProfile(bt_gatt_attr_t *attr_start, int& index); + + int getAttributeCount(); + + bool longCharacteristic(); + + void setBuffer(const uint8_t value[], + uint16_t length, + uint16_t offset); + void discardBuffer(); + void syncupBuffer2Value(); + + /** + * @brief Get the characteristic value handle + * + * @param none + * + * @return none + * + * @note Only for peripheral + */ + uint16_t valueHandle(void); + + /** + * @brief Get characteristic configuration descriptor value handle + * + * @param none + * + * @return uint16_t The value handle + * 0 is invalid handle + * + * @note Only for peripheral + */ + uint16_t cccdHandle(void); + + + void setUserDescription(BLEDescriptor *descriptor); + void setPresentationFormat(BLEDescriptor *descriptor); + + _bt_gatt_ccc_t* getCccCfg(void); + bt_gatt_chrc_t* getCharacteristicAttValue(void); + static bt_uuid_t* getCharacteristicAttributeUuid(void); + static bt_uuid_t* getClientCharacteristicConfigUuid(void); + + /** + * @brief Get the characteristic permission + * + * @param none + * + * @return uint8_t The characteristic permission + * + * @note none + */ + uint8_t getPermission(void); + + /** + * @brief For central to discover the peripherial profile + * + * @param[in] attr The discover response + * + * @param[in] params The discover parameter that need to fill + * + * @return none + * + * @note Only for central + */ + void discover(const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params); + + /** + * @brief For central to discover the peripherial profile + * + * @param[in] params The discover parameter that need to fill + * + * @return none + * + * @note Only for central + */ + void discover(bt_gatt_discover_params_t *params); + + /** + * @brief Get the subscribe parameter + * + * @param none + * + * @return bt_gatt_subscribe_params_t * the subscribe parameter + * + * @note Only for central + */ + bt_gatt_subscribe_params_t* getSubscribeParams(); + +private: + void _setValue(const uint8_t value[], uint16_t length); + +private: + // Those 2 UUIDs are used for define the characteristic. + static bt_uuid_16_t _gatt_chrc_uuid; // Characteristic UUID + static bt_uuid_16_t _gatt_ccc_uuid; // CCCD UUID + + unsigned short _value_size; + unsigned short _value_length; + unsigned char* _value; + unsigned char* _value_buffer; + bool _value_updated; + + uint16_t _value_handle; + bt_gatt_ccc_cfg_t _ccc_cfg; + _bt_gatt_ccc_t _ccc_value; + bt_gatt_chrc_t _gatt_chrc; + + bt_gatt_attr_t *_attr_chrc_declaration; + bt_gatt_attr_t *_attr_chrc_value; + bt_gatt_attr_t *_attr_cccd; + + // For GATT Client to subscribe the Notification/Indication + bt_gatt_subscribe_params_t _sub_params; + + bool _reading; + bt_gatt_read_params_t _read_params; // GATT read parameter + + typedef LinkNode BLEDescriptorLinkNodeHeader; + typedef LinkNode* BLEDescriptorNodePtr; + typedef LinkNode BLEDescriptorNode; + + BLECharacteristicEventHandler _event_handlers[BLECharacteristicEventLast]; + BLEDescriptorLinkNodeHeader _descriptors_header; + BLEDevice _ble_device; +}; + +#endif // _BLE_CHARACTERISTIC_H_INCLUDED diff --git a/libraries/BLE/src/BLECommon.h b/libraries/BLE/src/BLECommon.h new file mode 100644 index 00000000..ff315dad --- /dev/null +++ b/libraries/BLE/src/BLECommon.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2015 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _BLE_COMMON_H_INCLUDED +#define _BLE_COMMON_H_INCLUDED + +#include "Arduino.h" +//#include "ArduinoBLE.h" + +#include "../src/services/ble_service/ble_protocol.h" + + +#include "infra/log.h" + + +#include +#include +#include +#include +//#include + +#define BLE_ADDR_LEN 6 + +#define UUID_SIZE_128 16 +#define UUID_SIZE_16 2 +#define MAX_UUID_SIZE UUID_SIZE_128 + +/* Theoretically we should be able to support attribute lengths up to 512 bytes + * but this involves splitting it across multiple packets. For simplicity, + * we will just limit this to 20 bytes for now, which will fit in a single packet + */ +#define BLE_MAX_ATTR_DATA_LEN 20 +#define BLE_MAX_ATTR_LONGDATA_LEN 512 + +/* Default device name prefix, applied only if user does not provide a name + * If a factory-configured MAC address is defined, the last 2 bytes of the + * address will be appended to the device name */ +#define BLE_DEVICE_NAME_DEFAULT_PREFIX "Arduino101" + +/* Invalid BLE Address type */ +#define BLE_DEVICE_ADDR_INVALID 0xFF + +#if 0 +/** BLE response/event status codes. */ +enum BLE_STATUS { + BLE_STATUS_SUCCESS = 0, /**< General BLE Success code */ + BLE_STATUS_PENDING, /**< Request received and execution started, response pending */ + BLE_STATUS_TIMEOUT, /**< Request timed out */ + BLE_STATUS_NOT_SUPPORTED, /**< Request/feature/parameter not supported */ + BLE_STATUS_NOT_ALLOWED, /**< Request not allowed */ + BLE_STATUS_LINK_TIMEOUT, /**< Link timeout (link loss) */ + BLE_STATUS_NOT_ENABLED, /**< BLE not enabled, @ref ble_enable */ + BLE_STATUS_ERROR, /**< Generic Error */ + BLE_STATUS_ALREADY_REGISTERED, /**< BLE service already registered */ + BLE_STATUS_WRONG_STATE, /**< Wrong state for request */ + BLE_STATUS_ERROR_PARAMETER, /**< Parameter in request is wrong */ + BLE_STATUS_NO_MEMORY, /**< System doesn't have memory */ + BLE_STATUS_GAP_BASE = 0x100, /**< GAP specific error base */ + BLE_STATUS_GATT_BASE = 0x200, /**< GATT specific Error base */ +}; +#endif + +typedef enum +{ + BLE_STATUS_SUCCESS = 0, + BLE_STATUS_FORBIDDEN, /**< The operation is forbidden. Central mode call peripheral API and vice versa */ + BLE_STATUS_PENDING, /**< Request received and execution started, response pending */ + BLE_STATUS_TIMEOUT, /**< Request timed out */ + BLE_STATUS_NOT_SUPPORTED, /**< Request/feature/parameter not supported */ + BLE_STATUS_NOT_FOUND, + BLE_STATUS_NOT_ALLOWED, /**< Request not allowed */ + BLE_STATUS_LINK_TIMEOUT, /**< Link timeout (link loss) */ + BLE_STATUS_NOT_ENABLED, /**< BLE not enabled, @ref ble_enable */ + BLE_STATUS_ERROR, /**< Generic Error */ + BLE_STATUS_ALREADY_REGISTERED, /**< BLE service already registered */ + BLE_STATUS_WRONG_STATE, /**< Wrong state for request */ + BLE_STATUS_ERROR_PARAMETER, /**< Parameter in request is wrong */ + BLE_STATUS_NO_MEMORY, /**< System doesn't have memory */ + BLE_STATUS_NO_SERVICE, /**< System doesn't have service */ +}BLE_STATUS_T; + +typedef uint16_t ble_status_t; /**< Response and event BLE service status type @ref BLE_STATUS */ + +typedef ble_status_t BleStatus; + +#define BLE_MAX_CONN_CFG 2 + +typedef bool (*ble_advertise_handle_cb_t)(uint8_t type, const uint8_t *dataPtr, + uint8_t data_len, const bt_addr_le_t *addrPtr); + + +typedef struct ble_conn_param { + float interval_min; // millisecond 7.5 - 4000ms + float interval_max; // millisecond 7.5 - 4000ms + uint16_t latency; // 0x0000 - 0x01F4 + uint16_t timeout; // millisecond 100 - 32000ms +}ble_conn_param_t; +#ifdef __cplusplus +extern "C" { +#endif + +#include "os/os.h" + +/// Define the structure for app +typedef struct bt_uuid bt_uuid_t; +typedef struct bt_uuid_16 bt_uuid_16_t; +typedef struct bt_uuid_128 bt_uuid_128_t; +typedef struct bt_conn bt_conn_t; +typedef struct bt_gatt_attr bt_gatt_attr_t; +typedef struct bt_gatt_discover_params bt_gatt_discover_params_t; +typedef struct bt_le_scan_param bt_le_scan_param_t; +typedef struct bt_le_conn_param bt_le_conn_param_t; +typedef struct bt_gatt_subscribe_params bt_gatt_subscribe_params_t; +typedef struct bt_gatt_read_params bt_gatt_read_params_t; +typedef struct _bt_gatt_ccc _bt_gatt_ccc_t; +typedef struct bt_gatt_chrc bt_gatt_chrc_t; +typedef struct bt_gatt_ccc_cfg bt_gatt_ccc_cfg_t; +typedef struct bt_data bt_data_t; + +#ifdef __cplusplus +} +#endif +#endif // _BLE_COMMON_H_INCLUDED diff --git a/libraries/BLE/src/BLEDescriptor.cpp b/libraries/BLE/src/BLEDescriptor.cpp new file mode 100644 index 00000000..88aac259 --- /dev/null +++ b/libraries/BLE/src/BLEDescriptor.cpp @@ -0,0 +1,127 @@ +/* + BLE Descriptor API + Copyright (c) 2016 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "BLEDescriptor.h" +#include "BLEUtils.h" + +BLEDescriptor::BLEDescriptor() +{} + +BLEDescriptor::BLEDescriptor(const char* uuid, + const unsigned char value[], + unsigned char valueLength): + _bledev() +{ + bt_uuid_128_t uuid_tmp; + memset(_uuid_cstr, 0, sizeof (_uuid_cstr)); + BLEUtils::uuidString2BT(uuid, (bt_uuid_t *)&uuid_tmp); + BLEUtils::uuidBT2String((const bt_uuid_t *)&uuid_tmp, _uuid_cstr); + + _bledev.setAddress(*BLEUtils::bleGetLoalAddress()); + + _value_size = valueLength > BLE_MAX_ATTR_LONGDATA_LEN ? BLE_MAX_ATTR_LONGDATA_LEN : valueLength; + _value = (unsigned char*)balloc(_value_size, NULL); + memcpy(_value, value, _value_size); +} + +BLEDescriptor::BLEDescriptor(const char* uuid, + const char* value): + BLEDescriptor(uuid, (const unsigned char*)value, strlen(value)) +{} + +BLEDescriptor::~BLEDescriptor() +{ + if (_value) + { + bfree(_value); + _value = NULL; + } +} + +const char* BLEDescriptor::uuid() const +{ + return _uuid_cstr; +} + +const byte* BLEDescriptor::value() const +{ + // TODO: Not support now + return _value; +} + +int BLEDescriptor::valueLength() const +{ + // TODO: Not support now + return _value_size; +} + +byte BLEDescriptor::operator[] (int offset) const +{ + // TODO: Not support now + return 0; +} + +BLEDescriptor::operator bool() const +{ + // TODO: Not support now + return false; +} + +bool BLEDescriptor::writeValue(const byte value[], int length) +{ + // TODO: Not support now + return false; +} + +bool BLEDescriptor::writeValue(const byte value[], int length, int offset) +{ + // TODO: Not support now + return false; +} + +bool BLEDescriptor::writeValue(const char* value) +{ + // TODO: Not support now + return false; +} + +// GATT client Write the value of the descriptor +bool BLEDescriptor::write(const byte value[], int length) +{ + // TODO: Not support now + return false; +} + +bool BLEDescriptor::write(const byte value[], int length, int offset) +{ + // TODO: Not support now + return false; +} + +bool BLEDescriptor::write(const char* value) +{ + // TODO: Not support now + return false; +} + +bool BLEDescriptor::read() +{ + // TODO: Not support now + return false; +} + diff --git a/libraries/BLE/src/BLEDescriptor.h b/libraries/BLE/src/BLEDescriptor.h new file mode 100644 index 00000000..8c0391e3 --- /dev/null +++ b/libraries/BLE/src/BLEDescriptor.h @@ -0,0 +1,98 @@ +/* + BLE Descriptor API + Copyright (c) 2016 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ARDUINO_BLE_DESCRIPTOR_H +#define ARDUINO_BLE_DESCRIPTOR_H + +#include "ArduinoBLE.h" + +#include "BLEDevice.h" + +class BLEDescriptor +{ + public: + BLEDescriptor(); + BLEDescriptor(const char* uuid, const unsigned char value[], unsigned char valueLength); // create a descriptor the specified uuid and value + BLEDescriptor(const char* uuid, const char* value); // create a descriptor the specified uuid and string value + + virtual ~BLEDescriptor(); + + const char* uuid() const; + + virtual const byte* value() const; // returns the value buffer + virtual int valueLength() const; // returns the current length of the value + virtual byte operator[] (int offset) const; // returns a byte of the value at the specified offset + + virtual operator bool() const; // is the descriptor valid (discovered from peripheral) + + /** + * @brief Write the value of the descriptor + * + * @param value The value buffer that want to write to descriptor + * + * @param length The value buffer's length + * + * @return bool true - Success, false - Failed + * + * @note none + */ + virtual bool writeValue(const byte value[], int length); + + /** + * @brief Write the value of the descriptor + * + * @param value The value buffer that want to write to descriptor + * + * @param length The value buffer's length + * + * @param offset The offset in the descriptor's data + * + * @return bool true - Success, false - Failed + * + * @note none + */ + bool writeValue(const byte value[], int length, int offset); + + /** + * @brief Write the value of the descriptor + * + * @param value The value string that want to write to descriptor + * + * @return bool true - Success, false - Failed + * + * @note none + */ + bool writeValue(const char* value); + + // GATT client Write the value of the descriptor + virtual bool write(const byte value[], int length); + bool write(const byte value[], int length, int offset); + bool write(const char* value); + bool read(); +private: + char _uuid_cstr[37]; // The characteristic UUID + BLEDevice _bledev; + + unsigned char _properties; // The characteristic property + + unsigned char _value_size; // The value size + unsigned char* _value; // The value. Will delete after create the _internal +}; + +#endif diff --git a/libraries/BLE/src/BLEDescriptorImp.cpp b/libraries/BLE/src/BLEDescriptorImp.cpp new file mode 100644 index 00000000..9cdef95c --- /dev/null +++ b/libraries/BLE/src/BLEDescriptorImp.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2015 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "BLEAttribute.h" +#include "BLEDescriptorImp.h" + +#include "internal/ble_client.h" + +#include "BLECallbacks.h" + +BLEDescriptorImp::BLEDescriptorImp(BLEDevice& bledevice, BLEDescriptor &descriptor): + BLEAttribute(descriptor.uuid(), BLETypeDescriptor) +{ + _value_length = descriptor.valueLength(); + _value = (unsigned char*)balloc(_value_length, NULL); + + memcpy(_value, descriptor.value(), _value_length); +} + +BLEDescriptorImp::~BLEDescriptorImp() { + if (_value) { + bfree(_value); + _value = NULL; + } +} + +const unsigned char* +BLEDescriptorImp::value() const +{ + return _value; +} + +unsigned short +BLEDescriptorImp::valueLength() const +{ + return _value_length; +} + +unsigned char +BLEDescriptorImp::operator[] (int offset) const +{ + return _value[offset]; +} + +int BLEDescriptorImp::updateProfile(bt_gatt_attr_t *attr_start, int& index) +{ + bt_gatt_attr_t *start = attr_start; + start->uuid = (struct bt_uuid *)bt_uuid(); + start->perm = BT_GATT_PERM_READ; + start->read = profile_read_process; + start->user_data = (void*)((BLEAttribute*)this); + + pr_debug(LOG_MODULE_BLE, "Descriptor-%p", start); + index++; + return 1; +} + + diff --git a/libraries/BLE/src/BLEDescriptorImp.h b/libraries/BLE/src/BLEDescriptorImp.h new file mode 100644 index 00000000..5ea27caf --- /dev/null +++ b/libraries/BLE/src/BLEDescriptorImp.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _BLE_DESCRIPTORIMP_H_INCLUDED +#define _BLE_DESCRIPTORIMP_H_INCLUDED + +#include "ArduinoBLE.h" + +/** + * BLE GATT Descriptor class + */ +class BLEDescriptorImp: public BLEAttribute{ +public: + /** + * Constructor for BLE Descriptor + * + * @param[in] uuid 16-bit UUID (in string form) defined by BLE standard + * @param[in] value Value of descriptor, as a byte array. Data is stored in internal copy. + * @param[in] valueLength Data length required for descriptor value (<= BLE_MAX_ATTR_DATA_LEN) + */ + BLEDescriptorImp(BLEDevice& bledevice, BLEDescriptor &descriptor); + + virtual ~BLEDescriptorImp(); + + /** + * Get data pointer to the value of the Descriptor + * + * @return const unsigned char* pointer to the value of the Descriptor + */ + const unsigned char* value(void) const; + + /** + * Get the length of the value of the Descriptor + * + * @return unsigned short size of Descriptor value in bytes + */ + unsigned short valueLength(void) const; + + int updateProfile(bt_gatt_attr_t *attr_start, int& index); + + unsigned char operator[] (int offset) const; + +protected: + + +private: + unsigned short _value_length; + unsigned char* _value; + + bt_uuid_128 _descriptor_uuid; +}; + +#endif // _BLE_DESCRIPTOR_H_INCLUDED diff --git a/libraries/BLE/src/BLEDevice.cpp b/libraries/BLE/src/BLEDevice.cpp new file mode 100644 index 00000000..ece51365 --- /dev/null +++ b/libraries/BLE/src/BLEDevice.cpp @@ -0,0 +1,433 @@ +/* + BLE Device API + Copyright (c) 2016 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "ArduinoBLE.h" +#include "BLEDevice.h" + +#include "BLEUtils.h" + +#include "BLEProfileManager.h" +#include "BLEDeviceManager.h" +#include "BLECharacteristicImp.h" + +BLEDevice::BLEDevice() +{ + memset(&_bt_addr, 0, sizeof(_bt_addr)); + _conn_param.interval_max = BT_GAP_INIT_CONN_INT_MAX; + _conn_param.interval_min = BT_GAP_INIT_CONN_INT_MIN; + _conn_param.latency = 0; + _conn_param.timeout = 400; +} + +/* +BLEDevice::BLEDevice(String bleaddress) +{ + BLEUtils::macAddressString2BT(bleaddress.c_str(), _bt_addr); +} + +BLEDevice::BLEDevice(const char* bleaddress) +{ + BLEUtils::macAddressString2BT(bleaddress, _bt_addr); +} + +*/ + +BLEDevice::BLEDevice(const bt_addr_le_t* bleaddress): + BLEDevice() +{ + memcpy(&_bt_addr, bleaddress, sizeof(bt_addr_le_t)); +} + +BLEDevice::BLEDevice(const BLEDevice* bleaddress) +{ + memcpy(&_bt_addr, bleaddress->bt_le_address(), sizeof(bt_addr_le_t)); + memcpy(&_conn_param, bleaddress->bt_conn_param(), sizeof (ble_conn_param_t)); +} + +BLEDevice::~BLEDevice() +{ + //pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); +} + +bool BLEDevice::begin() +{ + return BLEDeviceManager::instance()->begin(this); +} + +void BLEDevice::poll() +{} + +void BLEDevice::end() +{} + +bool BLEDevice::connected() +{ + return BLEDeviceManager::instance()->connected(this); +} + +bool BLEDevice::disconnect() +{ + return BLEDeviceManager::instance()->disconnect(this); +} + +String BLEDevice::address() const +{ + return BLEUtils::macAddressBT2String(_bt_addr); +} + +void BLEDevice::setAddress(const bt_addr_le_t& addr) +{ + memcpy(&_bt_addr, &addr, sizeof(_bt_addr)); +} + +void BLEDevice::setAdvertisedServiceUuid(const char* advertisedServiceUuid) +{ + BLEDeviceManager::instance()->setAdvertisedServiceUuid(advertisedServiceUuid); +} + +void BLEDevice::setAdvertisedService(const BLEService& service) +{} + +void BLEDevice::setServiceSolicitationUuid(const char* serviceSolicitationUuid) +{} + +void BLEDevice::setManufacturerData(const unsigned char manufacturerData[], + unsigned char manufacturerDataLength) +{} + +void BLEDevice::setLocalName(const char *localName) +{ + BLEDeviceManager::instance()->setLocalName(localName); +} + +void BLEDevice::setAdvertisingInterval(float advertisingInterval) +{ + BLEDeviceManager::instance()->setAdvertisingInterval(advertisingInterval); +} + +void BLEDevice::setConnectionInterval(float minimumConnectionInterval, + float maximumConnectionInterval, + uint16_t latency, + uint16_t timeout) +{} + +void BLEDevice::setConnectionInterval(float minimumConnectionInterval, + float maximumConnectionInterval) +{} + +bool BLEDevice::setTxPower(int txPower) +{ + return BLEDeviceManager::instance()->setTxPower(txPower); +} + +void BLEDevice::setConnectable(bool connectable) +{ + BLEDeviceManager::instance()->setConnectable(connectable); +} + +void BLEDevice::setDeviceName(const char* deviceName) +{ + BLEDeviceManager::instance()->setDeviceName(deviceName); +} + +void BLEDevice::setAppearance(unsigned short appearance) +{ + BLEDeviceManager::instance()->setAppearance(appearance); +} + +BLE_STATUS_T BLEDevice::addService(BLEService& attribute) +{ + return BLEProfileManager::instance()->addService(*this, attribute); +} + +BLE_STATUS_T BLEDevice::startAdvertising() +{ + preCheckProfile(); + return BLEDeviceManager::instance()->startAdvertising(); +} + +void BLEDevice::stopAdvertising() +{ + BLEDeviceManager::instance()->stopAdvertising(); +} + +BLEDevice BLEDevice::central() +{ + return BLEDeviceManager::instance()->central(); +} + +BLEDevice BLEDevice::peripheral() +{ + // TODO + BLEDevice temp; + return temp; +} + +void BLEDevice::linkLost() +{} + +BLEDevice::operator bool() const +{ + return BLEUtils::macAddressValid(_bt_addr); +} + +BLEDevice& BLEDevice::operator=(const BLEDevice& device) +{ + if (*this != device) + { + memcpy(&(this->_bt_addr), &(device._bt_addr), sizeof (bt_addr_le_t)); + } + return *this; +} + +bool BLEDevice::operator==(const BLEDevice& device) const +{ + return (memcmp(this->_bt_addr.val, device._bt_addr.val, 6) == 0); +} + +bool BLEDevice::operator!=(const BLEDevice& device) const +{ + return (memcmp(this->_bt_addr.val, device._bt_addr.val, 6) != 0); +} + + +void BLEDevice::startScanning(String name) +{ + preCheckProfile(); + BLEDeviceManager::instance()->setAdvertiseCritical(name); + BLEDeviceManager::instance()->startScanning(); +} + +void BLEDevice::startScanningWithDuplicates() +{} + +void BLEDevice::stopScanning() +{ + BLEDeviceManager::instance()->stopScanning(); +} + +//void setAcceptAdvertiseLocalName(String name); +//void setAcceptAdvertiseLocalName(BLEService& service); +//void setAcceptAdvertiseCallback(String name); + +BLEDevice BLEDevice::available() +{ + return BLEDeviceManager::instance()->available(); +} + +bool BLEDevice::hasLocalName() const +{ + return BLEDeviceManager::instance()->hasLocalName(); +} + +bool BLEDevice::hasAdvertisedServiceUuid() const +{ + return BLEDeviceManager::instance()->hasAdvertisedServiceUuid(); +} + +bool BLEDevice::hasAdvertisedServiceUuid(int index) const +{ + return BLEDeviceManager::instance()->hasAdvertisedServiceUuid(index); +} + +int BLEDevice::advertisedServiceUuidCount() const +{ + return BLEDeviceManager::instance()->advertisedServiceUuidCount(); +} + +String BLEDevice::localName() const +{ + return BLEDeviceManager::instance()->localName(); +} + +String BLEDevice::advertisedServiceUuid() const +{ + return BLEDeviceManager::instance()->advertisedServiceUuid(); +} + +String BLEDevice::advertisedServiceUuid(int index) const +{ + return BLEDeviceManager::instance()->advertisedServiceUuid(index); +} + +int BLEDevice::rssi() const +{ + return BLEDeviceManager::instance()->rssi(); +} + +bool BLEDevice::connect() +{ + return BLEDeviceManager::instance()->connect(*this); +} + +bool BLEDevice::discoverAttributes() +{ + return BLEProfileManager::instance()->discoverAttributes(this); +} + +String BLEDevice::deviceName() +{ + return BLEDeviceManager::instance()->deviceName(); +} + +int BLEDevice::appearance() +{ + return BLEDeviceManager::instance()->appearance(); +} + +// For GATT +int BLEDevice::serviceCount() const +{ + return BLEProfileManager::instance()->serviceCount(*this); +} + +bool BLEDevice::hasService(const char* uuid) const +{ + BLEServiceImp* serviceImp = BLEProfileManager::instance()->service(*this, uuid); + return (NULL != serviceImp); +} + +bool BLEDevice::hasService(const char* uuid, int index) const +{ + BLEServiceImp* serviceImp = BLEProfileManager::instance()->service(*this, index); + return serviceImp->compareUuid(uuid); +} + +BLEService BLEDevice::service(int index) const +{ + BLEServiceImp* serviceImp = BLEProfileManager::instance()->service(*this, index); + if (serviceImp != NULL) + { + BLEService temp(serviceImp, this); + return temp; + } + BLEService temp; + return temp; +} + +BLEService BLEDevice::service(const char * uuid) const +{ + BLEServiceImp* serviceImp = BLEProfileManager::instance()->service(*this, uuid); + if (serviceImp != NULL) + { + BLEService temp(serviceImp, this); + return temp; + } + BLEService temp; + return temp; +} + +BLEService BLEDevice::service(const char * uuid, int index) const +{ + BLEServiceImp* serviceImp = BLEProfileManager::instance()->service(*this, index); + if (serviceImp != NULL && serviceImp->compareUuid(uuid)) + { + BLEService temp(serviceImp, this); + return temp; + } + BLEService temp; + return temp; +} + +int BLEDevice::characteristicCount() const +{ + return BLEProfileManager::instance()->characteristicCount(*this); +} + +bool BLEDevice::hasCharacteristic(const char* uuid) const +{ + BLECharacteristicImp* characteristicImp = BLEProfileManager::instance()->characteristic(*this, uuid); + return (NULL != characteristicImp); +} + +bool BLEDevice::hasCharacteristic(const char* uuid, int index) const +{ + BLECharacteristicImp* characteristicImp = BLEProfileManager::instance()->characteristic(*this, uuid, index); + return (NULL != characteristicImp); +} + +BLECharacteristic BLEDevice::characteristic(int index) const +{ + BLECharacteristicImp* characteristicImp = BLEProfileManager::instance()->characteristic(*this, index); + + if (NULL == characteristicImp) + { + BLECharacteristic temp; + return temp; + } + BLECharacteristic temp(characteristicImp, this); + return temp; +} + +BLECharacteristic BLEDevice::characteristic(const char * uuid) const +{ + BLECharacteristicImp* characteristicImp = BLEProfileManager::instance()->characteristic(*this, uuid); + + if (NULL == characteristicImp) + { + BLECharacteristic temp; + return temp; + } + BLECharacteristic temp(characteristicImp, this); + return temp; +} + +BLECharacteristic BLEDevice::characteristic(const char * uuid, int index) const +{ + BLECharacteristicImp* characteristicImp = BLEProfileManager::instance()->characteristic(*this, index); + if (false == characteristicImp->compareUuid(uuid)) + { + // UUID not matching + characteristicImp = NULL; + } + + if (NULL == characteristicImp) + { + BLECharacteristic temp; + return temp; + } + BLECharacteristic temp(characteristicImp, this); + return temp; +} + +// event handler +void BLEDevice::setEventHandler(BLEDeviceEvent event, + BLEDeviceEventHandler eventHandler) +{ + // TODO: +} + +const bt_addr_le_t* BLEDevice::bt_le_address() const +{ + return &_bt_addr; +} +const bt_le_conn_param* BLEDevice::bt_conn_param() const +{ + return &_conn_param; +} + +void BLEDevice::preCheckProfile() +{ + if (false == BLEProfileManager::instance()->hasRegisterProfile() && + BLEProfileManager::instance()->serviceCount(*this) > 0) + { + BLEProfileManager::instance()->registerProfile(*this); + delay(8); + } +} + diff --git a/libraries/BLE/src/BLEDevice.h b/libraries/BLE/src/BLEDevice.h new file mode 100644 index 00000000..8beebf64 --- /dev/null +++ b/libraries/BLE/src/BLEDevice.h @@ -0,0 +1,414 @@ +/* + BLE Device API + Copyright (c) 2016 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ARDUINO_BLE_DEVICE_H +#define ARDUINO_BLE_DEVICE_H + +#include + +//#include "BLEService.h" +//#include "BLECharacteristic.h" + +//class BLEDevice; + +enum BLEDeviceEvent { + BLEDiscovered = 0, // Discover profile completed + BLEConnected = 1, // BLE device connected + BLEDisconnected = 2, // BLE device disconnected + BLEConParamUpdate = 3, // Update the connection parameter + // Connection update request in central + // Connection parameter updated in peripheral +}; + +typedef void (*BLEDeviceEventHandler)(BLEDevice& device); + +class BLEDevice +{ + public: + /** + * @brief The BLE device constructure + * + * @param bleaddress BLE device address + * + * @return none + * + * @note none + */ + BLEDevice(); + //BLEDevice(String bleaddress); + //BLEDevice(const char* bleaddress); + BLEDevice(const bt_addr_le_t* bleaddress); + BLEDevice(const BLEDevice* bleaddress); + + virtual ~BLEDevice(); + + + /** + * @brief Initiliaze the BLE hardware + * + * @return bool indicating success or error + * + * @note This method are for real BLE device. + * Not for peer BLE device. + */ + bool begin(); + + /** + * @brief Poll for events + * + * @param none + * + * @return none + * + * @note This method are for real BLE device. + * Not for peer BLE device. + */ + void poll(); // Do we need add the return value or + // input parameter to get the events? + // Events may inlcue: + // GAP : Connected, Disconnected, Update connetion parameter + // GATT: Discovered + + /** + * @brief Deinitiliaze the BLE hardware + * + * @param none + * + * @return none + * + * @note This method are for real BLE device. + * Not for peer BLE device. + */ + void end(); + + /** + * @brief Is the device connected with another BLE device. + * + * @param none + * + * @return none + * + * @note none + */ + bool connected(); + + /** + * @brief Disconnect the connected device/s. + * + * @param none + * + * @return none + * + * @note The BLE may connected multiple devices. + * This call will disconnect all conected devices. + */ + bool disconnect(); + + + /** + * @brief Get the BLE address of the BLE in string format + * + * @return const char* address of the BLE in string format + */ + String address() const; + + /** + * @brief Set the service UUID that the BLE Peripheral Device advertises + * + * @param[in] advertisedServiceUuid 16-bit or 128-bit UUID to advertis + * (in string form) + * + * @note This method must be called before the begin method + * Only for peripheral mode. + */ + void setAdvertisedServiceUuid(const char* advertisedServiceUuid); + + /** + * @brief Set the service that the BLE Peripheral Device will advertise this UUID + * + * @param[in] service The service the will in advertise data. + * + * @note This method must be called before the begin method + * Only for peripheral mode. + */ + void setAdvertisedService(const BLEService& service); + + /** + * @brief Set the service UUID that is solicited in the BLE Peripheral + * Device advertises + * + * @param[in] advertisedServiceUuid 16-bit or 128-bit UUID to advertis + * (in string form) + * + * @note This method must be called before the begin method + * Only for peripheral mode. + */ + void setServiceSolicitationUuid(const char* serviceSolicitationUuid); + + /** + * @brief Set the manufacturer data in the BLE Peripheral Device advertises + * + * @param[in] manufacturerData The data about manufacturer will + * be set in advertisement + * @param[in] manufacturerDataLength The length of the manufacturer data + * + * @note This method must be called before the begin method + * Only for peripheral mode. + */ + void setManufacturerData(const unsigned char manufacturerData[], + unsigned char manufacturerDataLength); + + /** + * Set the local name that the BLE Peripheral Device advertises + * + * @param[in] localName local name to advertise + * + * @note This method must be called before the begin method + */ + void setLocalName(const char *localName); + + /** + * @brief Set advertising interval + * + * @param[in] advertisingInterval Advertising Interval in ms + * + * @return none + * + * @note none + */ + void setAdvertisingInterval(float advertisingInterval); + + /** + * @brief Set the connection parameters and send connection + * update request in both BLE peripheral and central + * + * @param[in] intervalmin Minimum Connection Interval (ms) + * + * @param[in] intervalmax Maximum Connection Interval (ms) + * + * @param[in] latency Connection Latency + * + * @param[in] timeout Supervision Timeout (ms) + * + * @return none + * + * @note none + */ + void setConnectionInterval(float minimumConnectionInterval, + float maximumConnectionInterval, + uint16_t latency, + uint16_t timeout); + + /** + * @brief Set the min and max connection interval and send connection + * update request in both BLE peripheral and central + * + * @param[in] intervalmin Minimum Connection Interval (ms) + * + * @param[in] intervalmax Maximum Connection Interval (ms) + * + * @return none + * + * @note none + */ + void setConnectionInterval(float minimumConnectionInterval, + float maximumConnectionInterval); + + /** + * @brief Set TX power of the radio in dBM + * + * @param[in] tx_power The antenna TX power + * + * @return boolean_t true if established connection, otherwise false + */ + bool setTxPower(int txPower); + + /** + * @brief Set advertising type as connectable/non-connectable + * + * @param[in] connectable true - The device connectable + * false - The device non-connectable + * + * @return none + * + * @note Only for peripheral mode. + * Default value is connectable + */ + void setConnectable(bool connectable); + + /** + * @brief Set the value of the device name characteristic + * + * @param[in] device User-defined name string for this device. Truncated if + * more than maximum allowed string length (20 bytes). + * + * @note This method must be called before the begin method + * If device name is not set, a default name will be used + */ + void setDeviceName(const char* deviceName); + + /** + * @brief Set the appearance type for the BLE Peripheral Device + * + * See https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml + * for available options. + * + * @param[in] appearance Appearance category identifier as defined by BLE Standard + * + * @return BleStatus indicating success or error + * + * @note This method must be called before the begin method + */ + void setAppearance(unsigned short appearance); + + /** + * @brief Add a Service to the BLE Peripheral Device + * + * @param[in] attribute The service that will add to Peripheral + * + * @return BLE_STATUS_T Indicating success or error type + * + * @note This method must be called before the begin method + */ + BLE_STATUS_T addService(BLEService& attribute); + + /** + * @brief Construct the ADV data and start send advertisement + * + * @param none + * + * @return BLE_STATUS_T 0 - Success. Others - error code + * + * @note none + */ + BLE_STATUS_T startAdvertising(); + + /** + * @brief Stop send advertisement + * + * @param none + * + * @return none + * + * @note none + */ + void stopAdvertising(); + + /** + * @brief Get currently connected central + * + * @return BLEDevice Connected central device + * + * @note Peripheral mode only + */ + BLEDevice central(); + + /** + * @brief Get currently connected peripheral + * + * @param none + * + * @return none + * + * @note Central mode only. How to distinguish the peripheral? + */ + BLEDevice peripheral(); + + /** + * @brief Release the resources when link lost + * + * @param none + * + * @return none + * + * @note Peer devices only. Do nothing if local BLE device called. + */ + void linkLost(); + + operator bool() const; + bool operator==(const BLEDevice& device) const; + bool operator!=(const BLEDevice& device) const; + BLEDevice& operator=(const BLEDevice& device); + // central mode + void startScanning(String name); // start scanning for peripherals + void startScanningWithDuplicates(); // start scanning for peripherals, and report all duplicates + void stopScanning(); // stop scanning for peripherals + + void setAcceptAdvertiseLocalName(String name); + void setAcceptAdvertiseLocalName(BLEService& service); + void setAcceptAdvertiseCallback(String name); + + BLEDevice available(); // retrieve a discovered peripheral + + bool hasLocalName() const; // does the peripheral advertise a local name + bool hasAdvertisedServiceUuid() const; // does the peripheral advertise a service + bool hasAdvertisedServiceUuid(int index) const; // does the peripheral advertise a service n + int advertisedServiceUuidCount() const; // number of services the peripheral is advertising + + String localName() const; // returns the advertised local name as a String + String advertisedServiceUuid() const; // returns the advertised service as a UUID String + String advertisedServiceUuid(int index) const; // returns the nth advertised service as a UUID String + + int rssi() const; // returns the RSSI of the peripheral at discovery + + bool connect(); // connect to the peripheral + bool discoverAttributes(); // discover the peripheral's attributes + + String deviceName(); // read the device name attribute of the peripheral, and return String value + int appearance(); // read the appearance attribute of the peripheral and return value as int + + // For GATT + int serviceCount() const; // returns the number of services the peripheral has + bool hasService(const char* uuid) const; // does the peripheral have a service with the specified UUID + bool hasService(const char* uuid, int index) const; // does the peripheral have an nth service with the specified UUID + BLEService service(int index) const; // return the nth service of the peripheral + BLEService service(const char * uuid) const; // return the service with the specified UUID + BLEService service(const char * uuid, int index) const; // return the nth service with the specified UUID + + int characteristicCount() const; // returns the number of characteristics the peripheral has + bool hasCharacteristic(const char* uuid) const; // does the peripheral have a characteristic with the specified UUID + bool hasCharacteristic(const char* uuid, int index) const; // does the peripheral have an nth characteristic with the specified UUID + BLECharacteristic characteristic(int index) const; // return the nth characteristic of the peripheral + BLECharacteristic characteristic(const char * uuid) const; // return the characteristic with the specified UUID + BLECharacteristic characteristic(const char * uuid, int index) const; // return the nth characteristic with the specified UUID + + // event handler + void setEventHandler(BLEDeviceEvent event, BLEDeviceEventHandler eventHandler); // set an event handler (callback) + +protected: + friend class BLECharacteristicImp; + friend class BLEServiceImp; + friend class BLEDeviceManager; + friend class BLEProfileManager; + friend class BLECharacteristic; + friend class BLEDescriptor; + friend class BLEService; + const bt_addr_le_t* bt_le_address() const; + const bt_le_conn_param* bt_conn_param() const; + void setAddress(const bt_addr_le_t& addr); +private: + void preCheckProfile(); + +private: + bt_addr_le_t _bt_addr; + + bt_le_conn_param _conn_param; +}; + +#endif diff --git a/libraries/BLE/src/BLEDeviceManager.cpp b/libraries/BLE/src/BLEDeviceManager.cpp new file mode 100644 index 00000000..72db8c45 --- /dev/null +++ b/libraries/BLE/src/BLEDeviceManager.cpp @@ -0,0 +1,803 @@ +/* + BLE Device API + Copyright (c) 2016 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "ArduinoBLE.h" +#include "BLEDeviceManager.h" +#include "BLEProfileManager.h" + +#include "internal/ble_client.h" + +#include +#include "../src/services/ble/conn_internal.h" + +#include "BLEUtils.h" +#include "BLECallbacks.h" + +BLEDeviceManager* BLEDeviceManager::_instance; + +BLEDeviceManager::BLEDeviceManager(): + _min_conn_interval(0), + _max_conn_interval(0), + _adv_critical_local_name(""), + _has_service_uuid(false), + _appearance(0), + _adv_type(0), + _adv_data_idx(0), + _local_name(""), + _state(BLE_PERIPH_STATE_NOT_READY), + _local_ble(NULL) +{ + memset(&_local_bda, 0, sizeof(_local_bda)); + + memset(&_service_uuid, 0, sizeof(_service_uuid)); + memset(&_service_solicit_uuid, 0, sizeof(_service_solicit_uuid)); + memset(_adv_data, 0, sizeof(_adv_data)); + + memset(&_peer_central, 0, sizeof (bt_addr_le_t)); + + ble_client_get_factory_config(&_local_bda, _device_name); + + _adv_param.type = BT_LE_ADV_IND; + _adv_param.addr_type = _local_bda.type; + _adv_param.interval_min = 0xA0; + _adv_param.interval_max = 0xF0; + + _scan_param.type = BT_HCI_LE_SCAN_ACTIVE; + _scan_param.filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_ENABLE; + _scan_param.interval = BT_GAP_SCAN_FAST_INTERVAL; + _scan_param.window = BT_GAP_SCAN_FAST_WINDOW; + + memset(_peer_adv_buffer, 0, sizeof(_peer_adv_buffer)); + memset(_peer_adv_mill, 0, sizeof(_peer_adv_mill)); + memset(&_adv_accept_critical, 0, sizeof(_adv_accept_critical)); + + memset(_peer_peripheral, 0, sizeof(_peer_peripheral)); +} + +BLEDeviceManager::~BLEDeviceManager() +{ + +} + +bool BLEDeviceManager::begin(BLEDevice *device) +{ + if (NULL == _local_ble && false == *device) + { + _local_ble = device; + _local_ble->setAddress(_local_bda); + + // Set device name + setDeviceName(); + _state = BLE_PERIPH_STATE_READY; + delay(4); + // TODO: Olny allow call one time + ble_client_init (bleConnectEventHandler, this, + bleDisconnectEventHandler, this, + bleParamUpdatedEventHandler, this); + return true; + } + else + { + return false; + } +} + +void BLEDeviceManager::poll() +{} + +void BLEDeviceManager::end() +{} + +bool BLEDeviceManager::connected(BLEDevice *device) +{ + bt_conn_t* conn = bt_conn_lookup_addr_le(device->bt_le_address()); + //pr_debug(LOG_MODULE_BLE, "%s-%d: add-%s", __FUNCTION__, __LINE__, device->address().c_str()); + if (NULL != conn) + { + //pr_debug(LOG_MODULE_BLE, "%s-%d: state-%d", __FUNCTION__, __LINE__,conn->state); + if (conn->state == BT_CONN_CONNECTED) + { + return true; + } + bt_conn_unref(conn); + } + return false; +} + +bool BLEDeviceManager::disconnect(BLEDevice *device) +{ + int err = 0; + bt_conn_t* conn = bt_conn_lookup_addr_le(device->bt_le_address()); + if (NULL == conn) + { + return false; + } + + err = bt_conn_disconnect (conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + bt_conn_unref(conn); + return (err == 0); +} + +void BLEDeviceManager::setAdvertisedServiceUuid(const char* advertisedServiceUuid) +{ + _has_service_uuid = true; + BLEUtils::uuidString2BT(advertisedServiceUuid, (bt_uuid_t *)&_service_uuid); +} + +void BLEDeviceManager::setAdvertisedService(const BLEService& service) +{ + //TODO: Different with setAdvertisedServiceUuid? +} + +void BLEDeviceManager::setServiceSolicitationUuid(const char* serviceSolicitationUuid) +{ + BLEUtils::uuidString2BT(serviceSolicitationUuid, (bt_uuid_t *)&_service_solicit_uuid); +} + +void BLEDeviceManager::setManufacturerData(const unsigned char manufacturerData[], + unsigned char manufacturerDataLength) +{ + // TODO: Add late +} + +void BLEDeviceManager::setLocalName(const char *localName) +{ + _local_name = localName; +} + +void BLEDeviceManager::setAdvertisingInterval(float advertisingInterval) +{ + uint16_t interval = (uint16_t) MSEC_TO_UNITS(advertisingInterval, UNIT_0_625_MS); + + _adv_param.interval_min = interval; + _adv_param.interval_max = interval; +} + +void BLEDeviceManager::setConnectionInterval(float minimumConnectionInterval, + float maximumConnectionInterval, + uint16_t latency, + uint16_t timeout) +{ +} + +void BLEDeviceManager::setConnectionInterval(float minimumConnectionInterval, + float maximumConnectionInterval) +{ + +} + +bool BLEDeviceManager::setTxPower(int txPower) +{ + ble_gap_set_tx_power(txPower); + return true; +} + +void BLEDeviceManager::setConnectable(bool connectable) +{ + uint8_t type = BT_LE_ADV_IND; + if (connectable == false) + { + type = BT_LE_ADV_NONCONN_IND; + } + _adv_param.type = type; +} + +void BLEDeviceManager::setDeviceName(const char* deviceName) +{ + memset(_device_name, 0, sizeof(_device_name)); + if (deviceName && deviceName[0]) + { + int len = strlen(deviceName); + if (len > BLE_MAX_DEVICE_NAME) + len = BLE_MAX_DEVICE_NAME; + memcpy(_device_name, deviceName, len); + setDeviceName(); + } +} + +void +BLEDeviceManager::setDeviceName() +{ + int len = strlen(_device_name); + bt_le_set_device_name(_device_name, len); +} + +void BLEDeviceManager::setAppearance(unsigned short appearance) +{ + _appearance = appearance; +} + +BLE_STATUS_T +BLEDeviceManager::_advDataInit(void) +{ + uint8_t lengthTotal = 2; // Flags data length + _adv_data_idx = 0; + + /* Add flags */ + _adv_type = (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR); + _adv_data[_adv_data_idx].type = BT_DATA_FLAGS; + _adv_data[_adv_data_idx].data = &_adv_type; + _adv_data[_adv_data_idx].data_len = 1; + _adv_data_idx++; + + if (_has_service_uuid) + { + uint8_t type; + uint8_t length; + uint8_t *data = NULL; + + pr_info(LOG_MODULE_BLE, "ADV Type-%d", _service_uuid.uuid.type); + if (BT_UUID_TYPE_16 == _service_uuid.uuid.type) + { + //UINT16_TO_LESTREAM(adv_tmp, uuid.uuid16); + data = (uint8_t *)&(((bt_uuid_16_t *)&_service_uuid)->val); + length = UUID_SIZE_16; + type = BT_DATA_UUID16_ALL; + } + else if (BT_UUID_TYPE_128 == _service_uuid.uuid.type) + { + data = _service_uuid.val; + length = UUID_SIZE_128; + type = BT_DATA_UUID128_ALL; + } + if (NULL != data) + { + _adv_data[_adv_data_idx].type = type; + _adv_data[_adv_data_idx].data = data; + _adv_data[_adv_data_idx].data_len = length; + _adv_data_idx++; + lengthTotal += length; + + pr_info(LOG_MODULE_BLE, "Service UUID Len -%d", length); + } + } + + + if (_local_name.length() > 0) + { + /* Add device name (truncated if too long) */ + _adv_data[_adv_data_idx].type = BT_DATA_NAME_COMPLETE; + _adv_data[_adv_data_idx].data = (const uint8_t*)_local_name.c_str(); + _adv_data[_adv_data_idx].data_len = _local_name.length(); + _adv_data_idx++; + + lengthTotal += _local_name.length(); + pr_info(LOG_MODULE_BLE, "Local Name -%s", _local_name.c_str()); + pr_info(LOG_MODULE_BLE, "Local Name Len -%d", _local_name.length()); + } + +#if 0 + + if (_service_data) + { + /* Add Service Data (if it will fit) */ + + /* A 128-bit Service Data UUID won't fit in an Advertising packet */ + if (BT_UUID_TYPE_16 != _service_data_uuid->type) + { + /* We support service data only for 16-bit service UUID */ + return BLE_STATUS_NOT_SUPPORTED; + } + + uint8_t block_len = sizeof(uint16_t) + _service_data_length; + if (1 + block_len > BLE_MAX_ADV_SIZE) + { + // Service data block is too large. + return BLE_STATUS_ERROR_PARAMETER; + } + + _adv_data[_adv_data_idx].type = BT_DATA_SVC_DATA16; + _adv_data[_adv_data_idx].data = _service_data_buf; + _adv_data[_adv_data_idx].data_len = block_len; + _adv_data_idx++; + + uint8_t *adv_tmp = _service_data_buf; + + UINT16_TO_LESTREAM(adv_tmp, (((bt_uuid_16_t *)_service_data_uuid)->val)); + memcpy(adv_tmp, _service_data, _service_data_length); + + lengthTotal += block_len; + pr_info(LOG_MODULE_BLE, "SVC Len -%d", block_len); + } +#endif + if (lengthTotal > BLE_MAX_ADV_SIZE) + { + pr_error(LOG_MODULE_BLE, "ADV Total length-%d", lengthTotal); + // Service data block is too large. + return BLE_STATUS_ERROR_PARAMETER; + } + return BLE_STATUS_SUCCESS; +} + +BLE_STATUS_T BLEDeviceManager::startAdvertising() +{ + int ret; + BLE_STATUS_T status; + status = _advDataInit(); + if (BLE_STATUS_SUCCESS != status) + { + return status; + } + + pr_info(LOG_MODULE_BLE, "%s-ad_len%d", __FUNCTION__, _adv_data_idx); + if (_state != BLE_PERIPH_STATE_READY) + return BLE_STATUS_WRONG_STATE; + + ret = bt_le_adv_start(&_adv_param, _adv_data, _adv_data_idx, NULL, 0); + if (0 != ret) + { + pr_error(LOG_MODULE_APP, "[ADV] Start failed. Error: %d", ret); + return BLE_STATUS_WRONG_STATE; + } + delay(10); + _state = BLE_PERIPH_STATE_ADVERTISING; + return BLE_STATUS_SUCCESS; +} + +BLE_STATUS_T BLEDeviceManager::stopAdvertising() +{ + int err_code = 0; + BLE_STATUS_T status = BLE_STATUS_WRONG_STATE; + + if (BLE_PERIPH_STATE_ADVERTISING == _state) + { + err_code = bt_le_adv_stop(); + status = errorno_to_ble_status(err_code); + } + + if (BLE_STATUS_SUCCESS != status) + return status; + + _state = BLE_PERIPH_STATE_READY; + return BLE_STATUS_SUCCESS; +} + +BLEDevice BLEDeviceManager::central() +{ + BLEDevice temp(&_peer_central); + return temp; +} + +BLEDevice BLEDeviceManager::peripheral() +{ + // TODO + BLEDevice temp; + return temp; +} + +void BLEDeviceManager::linkLost() +{} + +bool BLEDeviceManager::startScanning() +{ + int err = bt_le_scan_start(&_scan_param, ble_central_device_found); + if (err) + { + pr_info(LOG_MODULE_BLE, "Scanning failed to start (err %d)\n", err); + return false; + } + return true; +} + +bool BLEDeviceManager::startScanningWithDuplicates() +{ + // TODO: enable disable duplicate + return false; +} + +bool BLEDeviceManager::stopScanning() +{ + int err = bt_le_scan_stop(); + if (err) + { + pr_info(LOG_MODULE_BLE, "Stop LE scan failed (err %d)\n", err); + return false; + } + return true; +} + +void BLEDeviceManager::setAdvertiseCritical(String name) +{ + _adv_critical_local_name = name; + _adv_accept_critical.type = BT_DATA_NAME_COMPLETE; + _adv_accept_critical.data_len = name.length(); + _adv_accept_critical.data = (const uint8_t*)_adv_critical_local_name.c_str(); +} + +void BLEDeviceManager::setAdvertiseCritical(BLEService& service) +{ + BLEUtils::uuidString2BT(service.uuid(),(bt_uuid_t *)&_dv_critical_service_uuid); + uint8_t type = 0; + uint8_t length = 0; + uint8_t *data = NULL; + + pr_info(LOG_MODULE_BLE, "ADV Type-%d", _service_uuid.uuid.type); + if (BT_UUID_TYPE_16 == _service_uuid.uuid.type) + { + //UINT16_TO_LESTREAM(adv_tmp, uuid.uuid16); + data = (uint8_t *)&(((bt_uuid_16_t *)&_service_uuid)->val); + length = UUID_SIZE_16; + type = BT_DATA_UUID16_ALL; + } + else if (BT_UUID_TYPE_128 == _service_uuid.uuid.type) + { + data = _service_uuid.val; + length = UUID_SIZE_128; + type = BT_DATA_UUID128_ALL; + } + _adv_accept_critical.type = type; + _adv_accept_critical.data_len = length; + _adv_accept_critical.data = data; +} + + +//void setAcceptAdvertiseLocalName(String name); +//void setAcceptAdvertiseLocalName(BLEService& service); +//void setAcceptAdvertiseCallback(String name); + +bool BLEDeviceManager::hasLocalName() const +{ + return (_local_name.length() != 0); +} + +bool BLEDeviceManager::hasAdvertisedServiceUuid() const +{ + // TODO: + return false; +} + +bool BLEDeviceManager::hasAdvertisedServiceUuid(int index) const +{ + // TODO: + return false; +} + +int BLEDeviceManager::advertisedServiceUuidCount() const +{ + return 0; +} + +String BLEDeviceManager::localName() const +{ + return _local_name; +} + +String BLEDeviceManager::advertisedServiceUuid() const +{ + // TODO + return ""; +} + +String BLEDeviceManager::advertisedServiceUuid(int index) const +{ + // TODO + return ""; +} + +int BLEDeviceManager::rssi() const +{ + return 0; +} + +bool BLEDeviceManager::connect(BLEDevice &device) +{ + bt_addr_le_t* temp = NULL; + bt_addr_le_t* unused = NULL; + bool link_existed = false; + bool retval = false; + + pr_debug(LOG_MODULE_BLE, "%s-%d-1", __FUNCTION__, __LINE__); + // Find free peripheral Items + for (int i = 0; i < BLE_MAX_CONN_CFG; i++) + { + temp = &_peer_peripheral[i]; + if (true == BLEUtils::macAddressValid(*temp)) + { + if (bt_addr_le_cmp(temp, device.bt_le_address()) == 0) + { + // Connect request has scheduled but connection don't established. + // The central can see the ADV and no need to send connect request. + link_existed = true; + break; + } + } + else + { + if (NULL == unused) + { + unused = temp; + } + } + } + pr_debug(LOG_MODULE_BLE, "%s-%d:link_existed-%d unused-%p", __FUNCTION__, __LINE__, link_existed, unused); + + if (!link_existed) + { + pr_debug(LOG_MODULE_BLE, "%s-%d-Device:%s", __FUNCTION__, __LINE__, device.address().c_str()); + // Send connect request + bt_conn_t* conn = bt_conn_create_le(device.bt_le_address(), device.bt_conn_param()); + if (NULL != conn) + { + memcpy(unused, device.bt_le_address(), sizeof(bt_addr_le_t)); + retval = true; + bt_conn_unref(conn); + } + } + return retval; +} + +String BLEDeviceManager::deviceName() +{ + return _device_name; +} + +int BLEDeviceManager::appearance() +{ + return _appearance; +} + +BLEDeviceManager* BLEDeviceManager::instance() +{ + if (_instance == NULL) + { + _instance = new BLEDeviceManager(); + } + return _instance; +} + +void BLEDeviceManager::handleConnectEvent(bt_conn_t *conn, uint8_t err) +{ + struct bt_conn_info role_info; + bt_conn_get_info(conn, &role_info); + pr_info(LOG_MODULE_BLE, "%s-%d: role-%d", __FUNCTION__, __LINE__, role_info.role); + if (BT_CONN_ROLE_SLAVE == role_info.role) + { + // Central has established the connection with this peripheral device + memcpy(&_peer_central, bt_conn_get_dst(conn), sizeof (bt_addr_le_t)); + } + else + { + // Peripheral has established the connection with this Central device + BLEProfileManager::instance()->handleConnectedEvent(bt_conn_get_dst(conn)); + } +} + +void BLEDeviceManager::handleDisconnectEvent(bt_conn_t *conn, uint8_t reason) +{ + struct bt_conn_info role_info; + bt_conn_get_info(conn, &role_info); + pr_info(LOG_MODULE_BLE, "%s-%d: role-%d", __FUNCTION__, __LINE__, role_info.role); + if (BT_CONN_ROLE_SLAVE == role_info.role) + { + // Central has established the connection with this peripheral device + memset(&_peer_central, 0, sizeof (bt_addr_le_t)); + } + else + { + // Peripheral has established the connection with this Central device + } +} + +void BLEDeviceManager::handleParamUpdated (bt_conn_t *conn, + uint16_t interval, + uint16_t latency, + uint16_t timeout) +{ + +} + +bool BLEDeviceManager::advertiseDataProc(uint8_t type, + const uint8_t *dataPtr, + uint8_t data_len) +{ + //Serial1.print("[AD]:"); + //Serial1.print(type); + //Serial1.print(" data_len "); + //Serial1.println(data_len); + + //const bt_data_t zero = {0, 0,0}; + if (_adv_accept_critical.type == 0 && + _adv_accept_critical.data_len == 0 && + _adv_accept_critical.data == NULL) + { + // Not set the critical. Accept all. + return true; + } + if (type == _adv_accept_critical.type && + data_len == _adv_accept_critical.data_len && + 0 == memcmp(dataPtr, _adv_accept_critical.data, data_len)) + { + // Now Only support 1 critical. Change those code if want support multi-criticals + return true; + } + // Please see https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile + // To decode the data the central device cares. + // This example use UUID as identity. +#if 0 + switch (type) + { + case BT_DATA_UUID128_SOME: + case BT_DATA_UUID128_ALL: + { + if (data_len % UUID_SIZE_128 != 0) + { + Serial.println("AD malformed"); + return true; + } + for (i = 0; i < data_len; i += UUID_SIZE_128) + { + if (bleImuService.uuidCompare(dataPtr + i, UUID_SIZE_128) == false) + { + continue; + } + + // Accept the advertisement + if (!bleCentral.stopScan()) + { + Serial.println("Stop LE scan failed"); + continue; + } + Serial.println("Connecting"); + return false; + } + } + case BT_DATA_NAME_COMPLETE: + { + break; + } + } +#endif + + return false; +} + +BLEDevice BLEDeviceManager::available() +{ + BLEDevice tempdevice; + bt_addr_le_t* temp = NULL; + uint64_t timestamp = millis(); + uint8_t index = 3; + uint8_t i = 0; + uint64_t max_delta = 0; + + for (i = 0; i < 3; i++) + { + uint64_t timestamp_delta = timestamp - _peer_adv_mill[i]; + temp = &_peer_adv_buffer[i]; + if ((timestamp_delta <= 2000) && (max_delta < timestamp_delta)) + { + max_delta = timestamp_delta; + index = i; + } + } + //pr_debug(LOG_MODULE_BLE, "%s-%d:index %d, i-%d", __FUNCTION__, __LINE__, index, i); + + if (index < 3) + { + temp = &_peer_adv_buffer[index]; + if (true == BLEUtils::macAddressValid(*temp)) + { + tempdevice.setAddress(*temp); + //pr_debug(LOG_MODULE_BLE, "%s-%d:Con addr-%s", __FUNCTION__, __LINE__, BLEUtils::macAddressBT2String(*temp).c_str()); + _peer_adv_mill[index] -= 2000; // Set it as expired + } + } + return tempdevice; +} + +bool BLEDeviceManager::setAdvertiseBuffer(const bt_addr_le_t* bt_addr) +{ + bt_addr_le_t* temp = NULL; + uint64_t timestamp = millis(); + uint8_t index = 3; + uint8_t i = 0; + uint64_t max_delta = 0; + bool retval = false; + //pr_debug(LOG_MODULE_BLE, "%s-%d-1", __FUNCTION__, __LINE__); + for (i = 0; i < 3; i++) + { + uint64_t timestamp_delta = timestamp - _peer_adv_mill[i]; + temp = &_peer_adv_buffer[i]; + if (max_delta < timestamp_delta) + { + max_delta = timestamp_delta; + if (max_delta > 2000) // expired + index = i; + } + + if (bt_addr_le_cmp(temp, bt_addr) == 0) + //if (memcpy(temp->val, bt_addr->val, 6) == 0) + { + // The device alread in the buffer + index = i; + break; + } + } + //pr_debug(LOG_MODULE_BLE, "%s-%d:index %d, i-%d", __FUNCTION__, __LINE__, index, i); + + //pr_debug(LOG_MODULE_BLE, "%s-%d-2", __FUNCTION__, __LINE__); + if (index < 3) + { + temp = &_peer_adv_buffer[index]; + if (i >= 3) + { + memcpy(temp, bt_addr, sizeof (bt_addr_le_t)); + } + // Update the timestamp + _peer_adv_mill[index] = timestamp; + retval = true; + } + + return retval; +} + +void BLEDeviceManager::handleDeviceFound(const bt_addr_le_t *addr, + int8_t rssi, + uint8_t type, + const uint8_t *ad, + uint8_t data_len) +{ + const uint8_t *data = ad; + + /* We're only interested in connectable events */ + if (type == BT_LE_ADV_IND || type == BT_LE_ADV_DIRECT_IND) + { + //pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + while (data_len > 1) + { + uint8_t len = data[0]; + + /* Check for early termination */ + if (len == 0) + { + return; + } + + if ((len + 1 > data_len) || (data_len < 2)) { + pr_info(LOG_MODULE_BLE, "AD malformed\n"); + return; + } + + if (true == advertiseDataProc(data[1], &data[2], len - 1)) + { + // The critical is accepted + // Find the oldest and expired buffer + if(false == setAdvertiseBuffer(addr)) + { + pr_info(LOG_MODULE_BLE, "No buffer to store the ADV\n"); + } + else + { + BLEDevice testdev(addr); + stopScanning(); + connect(testdev); + } + pr_debug(LOG_MODULE_BLE, "%s-%d: Done", __FUNCTION__, __LINE__); + return; + } + + data_len -= len + 1; + data += len + 1; + } + //pr_debug(LOG_MODULE_BLE, "%s: done", __FUNCTION__); + } + +} + + + diff --git a/libraries/BLE/src/BLEDeviceManager.h b/libraries/BLE/src/BLEDeviceManager.h new file mode 100644 index 00000000..cbb597ed --- /dev/null +++ b/libraries/BLE/src/BLEDeviceManager.h @@ -0,0 +1,415 @@ +/* + BLE Device API + Copyright (c) 2016 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ARDUINO_BLE_DEVICE_MANAGER_H +#define ARDUINO_BLE_DEVICE_MANAGER_H + +#include + +class BLEDeviceManager +{ + public: + /** + * @brief The BLE device constructure + * + * @param bleaddress BLE device address + * + * @return none + * + * @note none + */ + BLEDeviceManager(); + + virtual ~BLEDeviceManager(); + + + /** + * @brief Initiliaze the BLE hardware + * + * @return bool indicating success or error + * + * @note This method are for real BLE device. + * Not for peer BLE device. + */ + bool begin(BLEDevice *device); + + /** + * @brief Poll for events + * + * @param none + * + * @return none + * + * @note This method are for real BLE device. + * Not for peer BLE device. + */ + void poll(); // Do we need add the return value or + // input parameter to get the events? + // Events may inlcue: + // GAP : Connected, Disconnected, Update connetion parameter + // GATT: Discovered + + /** + * @brief Deinitiliaze the BLE hardware + * + * @param none + * + * @return none + * + * @note This method are for real BLE device. + * Not for peer BLE device. + */ + void end(); + + /** + * @brief Is the device connected with another BLE device. + * + * @param none + * + * @return none + * + * @note none + */ + bool connected(BLEDevice *device); + + /** + * @brief Disconnect the connected device/s. + * + * @param none + * + * @return none + * + * @note The BLE may connected multiple devices. + * This call will disconnect all conected devices. + */ + bool disconnect(BLEDevice *device); + + /** + * @brief Set the service UUID that the BLE Peripheral Device advertises + * + * @param[in] advertisedServiceUuid 16-bit or 128-bit UUID to advertis + * (in string form) + * + * @note This method must be called before the begin method + * Only for peripheral mode. + */ + void setAdvertisedServiceUuid(const char* advertisedServiceUuid); + + /** + * @brief Set the service that the BLE Peripheral Device will advertise this UUID + * + * @param[in] service The service the will in advertise data. + * + * @note This method must be called before the begin method + * Only for peripheral mode. + */ + void setAdvertisedService(const BLEService& service); + + /** + * @brief Set the service UUID that is solicited in the BLE Peripheral + * Device advertises + * + * @param[in] advertisedServiceUuid 16-bit or 128-bit UUID to advertis + * (in string form) + * + * @note This method must be called before the begin method + * Only for peripheral mode. + */ + void setServiceSolicitationUuid(const char* serviceSolicitationUuid); + + /** + * @brief Set the manufacturer data in the BLE Peripheral Device advertises + * + * @param[in] manufacturerData The data about manufacturer will + * be set in advertisement + * @param[in] manufacturerDataLength The length of the manufacturer data + * + * @note This method must be called before the begin method + * Only for peripheral mode. + */ + void setManufacturerData(const unsigned char manufacturerData[], + unsigned char manufacturerDataLength); + + /** + * Set the local name that the BLE Peripheral Device advertises + * + * @param[in] localName local name to advertise + * + * @note This method must be called before the begin method + */ + void setLocalName(const char *localName); + + /** + * @brief Set advertising interval + * + * @param[in] advertisingInterval Advertising Interval in ms + * + * @return none + * + * @note none + */ + void setAdvertisingInterval(float advertisingInterval); + + /** + * @brief Set the connection parameters and send connection + * update request in both BLE peripheral and central + * + * @param[in] intervalmin Minimum Connection Interval (ms) + * + * @param[in] intervalmax Maximum Connection Interval (ms) + * + * @param[in] latency Connection Latency + * + * @param[in] timeout Supervision Timeout (ms) + * + * @return none + * + * @note none + */ + void setConnectionInterval(float minimumConnectionInterval, + float maximumConnectionInterval, + uint16_t latency, + uint16_t timeout); + + /** + * @brief Set the min and max connection interval and send connection + * update request in both BLE peripheral and central + * + * @param[in] intervalmin Minimum Connection Interval (ms) + * + * @param[in] intervalmax Maximum Connection Interval (ms) + * + * @return none + * + * @note none + */ + void setConnectionInterval(float minimumConnectionInterval, + float maximumConnectionInterval); + + /** + * @brief Set TX power of the radio in dBM + * + * @param[in] tx_power The antenna TX power + * + * @return boolean_t true if established connection, otherwise false + */ + bool setTxPower(int txPower); + + /** + * @brief Set advertising type as connectable/non-connectable + * + * @param[in] connectable true - The device connectable + * false - The device non-connectable + * + * @return none + * + * @note Only for peripheral mode. + * Default value is connectable + */ + void setConnectable(bool connectable); + + /** + * @brief Set the value of the device name characteristic + * + * @param[in] device User-defined name string for this device. Truncated if + * more than maximum allowed string length (20 bytes). + * + * @note This method must be called before the begin method + * If device name is not set, a default name will be used + */ + void setDeviceName(const char* deviceName); + void setDeviceName(); + /** + * @brief Set the appearance type for the BLE Peripheral Device + * + * See https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml + * for available options. + * + * @param[in] appearance Appearance category identifier as defined by BLE Standard + * + * @return BleStatus indicating success or error + * + * @note This method must be called before the begin method + */ + void setAppearance(unsigned short appearance); + + /** + * @brief Add a Service to the BLE Peripheral Device + * + * @param[in] attribute The service that will add to Peripheral + * + * @return BLE_STATUS_T Indicating success or error type + * + * @note This method must be called before the begin method + */ + BLE_STATUS_T addService(BLEService& attribute); + + /** + * @brief Construct the ADV data and start send advertisement + * + * @param none + * + * @return BLE_STATUS_T 0 - Success. Others - error code + * + * @note none + */ + BLE_STATUS_T startAdvertising(); + + /** + * @brief Stop send advertisement + * + * @param none + * + * @return none + * + * @note none + */ + BLE_STATUS_T stopAdvertising(); + + /** + * @brief Get currently connected central + * + * @return BLEDeviceManager Connected central device + * + * @note Peripheral mode only + */ + BLEDevice central(); + + /** + * @brief Get currently connected peripheral + * + * @param none + * + * @return none + * + * @note Central mode only. How to distinguish the peripheral? + */ + BLEDevice peripheral(); + + /** + * @brief Release the resources when link lost + * + * @param none + * + * @return none + * + * @note Peer devices only. Do nothing if local BLE device called. + */ + void linkLost(); + + operator bool() const; + + // central mode + void setAdvertiseCritical(String name); + void setAdvertiseCritical(BLEService& service); + bool startScanning(); // start scanning for peripherals + bool startScanningWithDuplicates(); // start scanning for peripherals, and report all duplicates + bool stopScanning(); // stop scanning for peripherals + + void setAcceptAdvertiseLocalName(String name); + void setAcceptAdvertiseLocalName(BLEService& service); + void setAcceptAdvertiseCallback(String name); + + BLEDevice available(); // retrieve a discovered peripheral + + bool hasLocalName() const; // does the peripheral advertise a local name + bool hasAdvertisedServiceUuid() const; // does the peripheral advertise a service + bool hasAdvertisedServiceUuid(int index) const; // does the peripheral advertise a service n + int advertisedServiceUuidCount() const; // number of services the peripheral is advertising + + String localName() const; // returns the advertised local name as a String + String advertisedServiceUuid() const; // returns the advertised service as a UUID String + String advertisedServiceUuid(int index) const; // returns the nth advertised service as a UUID String + + int rssi() const; // returns the RSSI of the peripheral at discovery + + bool connect(BLEDevice &device); // connect to the peripheral + + String deviceName(); // read the device name attribute of the peripheral, and return String value + int appearance(); // read the appearance attribute of the peripheral and return value as int + + static BLEDeviceManager* instance(); + + void handleConnectEvent(bt_conn_t *conn, uint8_t err); + void handleDisconnectEvent(bt_conn_t *conn, uint8_t reason); + void handleParamUpdated (bt_conn_t *conn, + uint16_t interval, + uint16_t latency, + uint16_t timeout); + void handleDeviceFound(const bt_addr_le_t *addr, + int8_t rssi, + uint8_t type, + const uint8_t *ad, + uint8_t data_len); + +protected: + +private: + BLE_STATUS_T _advDataInit(void); + bool advertiseDataProc(uint8_t type, + const uint8_t *dataPtr, + uint8_t data_len); + bool setAdvertiseBuffer(const bt_addr_le_t* bt_addr); + +private: + uint16_t _min_conn_interval; + uint16_t _max_conn_interval; + bt_addr_le_t _local_bda; + char _device_name[BLE_MAX_DEVICE_NAME+1]; + + // For Central + bt_le_scan_param_t _scan_param; // Scan parameter + bt_addr_le_t _peer_adv_buffer[3]; // Accepted peer device adress + uint64_t _peer_adv_mill[3]; // The ADV found time stamp + bt_data_t _adv_accept_critical; // The filters for central device + String _adv_critical_local_name; + bt_uuid_128_t _dv_critical_service_uuid; + + // For peripheral + struct bt_le_adv_param _adv_param; + bool _has_service_uuid; + bt_uuid_128_t _service_uuid; + bt_uuid_128_t _service_solicit_uuid; + uint16_t _appearance; + + // ADV data for peripheral + uint8_t _adv_type; + bt_data_t _adv_data[4]; + size_t _adv_data_idx; + + String _local_name; + // Peripheral states + enum BLEPeripheralState { + BLE_PERIPH_STATE_NOT_READY = 0, + BLE_PERIPH_STATE_READY, + BLE_PERIPH_STATE_ADVERTISING, + BLE_PERIPH_STATE_CONNECTED, + }; + + BLEPeripheralState _state; + + // Local + static BLEDeviceManager* _instance; + BLEDevice *_local_ble; + // Connected device object + bt_addr_le_t _peer_central; + bt_addr_le_t _peer_peripheral[BLE_MAX_CONN_CFG]; +}; + +#endif diff --git a/libraries/BLE/src/BLEProfileManager.cpp b/libraries/BLE/src/BLEProfileManager.cpp new file mode 100644 index 00000000..da9ebc96 --- /dev/null +++ b/libraries/BLE/src/BLEProfileManager.cpp @@ -0,0 +1,883 @@ +/* + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include "BLECommon.h" +#include "BLEProfileManager.h" +#include "BLECharacteristicImp.h" + +#include "BLECallbacks.h" +#include "BLEUtils.h" + +BLEDevice BLE; + +BLEProfileManager* BLEProfileManager::_instance = NULL; + +BLEProfileManager* BLEProfileManager::instance() +{ + if (NULL == _instance) + { + _instance = new BLEProfileManager(); + } + pr_debug(LOG_MODULE_BLE, "%s-%d: %p", __FUNCTION__, __LINE__, _instance); + return _instance; +} + +BLEProfileManager::BLEProfileManager (): + _cur_discover_service(NULL), + _attr_base(NULL), + _attr_index(0), + _sub_param(NULL), + _sub_param_idx(0), + _profile_registered(false) +{ + memset(_service_header_array, 0, sizeof(_service_header_array)); + memset(_discover_params, 0, sizeof(_discover_params)); + + memset(_addresses, 0, sizeof(_addresses)); + + bt_addr_le_copy(&_addresses[BLE_MAX_CONN_CFG], BLEUtils::bleGetLoalAddress()); + + pr_debug(LOG_MODULE_BLE, "%s-%d: Construct", __FUNCTION__, __LINE__); +} + +BLEProfileManager::~BLEProfileManager (void) +{ + if (this->_attr_base) + { + bfree(this->_attr_base); + } +} + +BLE_STATUS_T +BLEProfileManager::addService (BLEDevice &bledevice, BLEService& service) +{ + + BLEServiceLinkNodeHeader* serviceheader = getServiceHeader(bledevice); + if (NULL == serviceheader) + { + int index = getUnusedIndex(); + if (index >= BLE_MAX_CONN_CFG) + { + return BLE_STATUS_NO_MEMORY; + } + serviceheader = &_service_header_array[index]; + bt_addr_le_copy(&_addresses[index], bledevice.bt_le_address()); + } + + BLEServiceImp *serviceImp = new BLEServiceImp(service); + if (NULL == serviceImp) + { + return BLE_STATUS_NO_MEMORY; + } + BLEServiceNodePtr node = link_node_create(serviceImp); + if (NULL == node) + { + delete[] serviceImp; + return BLE_STATUS_NO_MEMORY; + } + link_node_insert_last(serviceheader, node); + return BLE_STATUS_SUCCESS; +} + +BLE_STATUS_T +BLEProfileManager::addService (BLEDevice &bledevice, const bt_uuid_t* uuid) +{ + BLEServiceLinkNodeHeader* serviceheader = getServiceHeader(bledevice); + if (NULL == serviceheader) + { + int index = getUnusedIndex(); + if (index >= BLE_MAX_CONN_CFG) + { + return BLE_STATUS_NO_MEMORY; + } + serviceheader = &_service_header_array[index]; + bt_addr_le_copy(&_addresses[index], bledevice.bt_le_address()); + } + + BLEServiceImp *serviceImp = new BLEServiceImp(uuid); + if (NULL == serviceImp) + { + return BLE_STATUS_NO_MEMORY; + } + BLEServiceNodePtr node = link_node_create(serviceImp); + if (NULL == node) + { + delete[] serviceImp; + return BLE_STATUS_NO_MEMORY; + } + link_node_insert_last(serviceheader, node); + return BLE_STATUS_SUCCESS; +} + +BLEProfileManager::BLEServiceLinkNodeHeader* BLEProfileManager::getServiceHeader(const BLEDevice &bledevice) +{ + String address = bledevice.address(); + int i; + for (i = 0; i <= BLE_MAX_CONN_CFG; i++) + { + if ((bt_addr_le_cmp(bledevice.bt_le_address(), &_addresses[i]) == 0)) + { + break; + } + } + if (i > BLE_MAX_CONN_CFG) + { + return NULL; + } + return &_service_header_array[i]; +} + +const BLEProfileManager::BLEServiceLinkNodeHeader* BLEProfileManager::getServiceHeader(const BLEDevice &bledevice) const +{ + String address = bledevice.address(); + int i; + for (i = 0; i <= BLE_MAX_CONN_CFG; i++) + { + if ((bt_addr_le_cmp(bledevice.bt_le_address(), &_addresses[i]) == 0)) + { + break; + } + } + if (i > BLE_MAX_CONN_CFG) + { + return NULL; + } + return &_service_header_array[i]; +} + +int BLEProfileManager::getUnusedIndex() +{ + int i; + for (i = 0; i < BLE_MAX_CONN_CFG; i++) + { + if (BLEUtils::macAddressValid(_addresses[i]) == false) + { + break; + } + } + + return i; +} + +int BLEProfileManager::getAttributeCount(BLEDevice &bledevice) +{ + BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + return 0; + } + + int attrCounter = 0; + + BLEServiceNodePtr node = serviceHeader->next; + while (node) + { + BLEServiceImp *service = node->value; + attrCounter += service->getAttributeCount(); + node = node->next; + } + return attrCounter; +} + +int BLEProfileManager::characteristicCount(const BLEDevice &bledevice) const +{ + const BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + return 0; + } + + int counter = 0; + + BLEServiceNodePtr node = serviceHeader->next; + while (node) + { + BLEServiceImp *service = node->value; + counter += service->getCharacteristicCount(); + node = node->next; + } + return counter; +} + +int BLEProfileManager::serviceCount(const BLEDevice &bledevice) const +{ + const BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + return 0; + } + return link_list_size(serviceHeader); +} + +int BLEProfileManager::registerProfile(BLEDevice &bledevice) +{ + int ret = 0; + + bt_gatt_attr_t *start; + BleStatus err_code = BLE_STATUS_SUCCESS; + + // The device is local BLE device. Register the service only allow local BLE device + BLEServiceLinkNodeHeader* serviceHeader = &_service_header_array[BLE_MAX_CONN_CFG]; + if ((bt_addr_le_cmp(bledevice.bt_le_address(), &_addresses[BLE_MAX_CONN_CFG]) != 0)) + { + return BLE_STATUS_FORBIDDEN; + } + + int attr_counter = getAttributeCount(bledevice); + if (0 == attr_counter) + { + return BLE_STATUS_NO_SERVICE; + } + + if (NULL == _attr_base) + { + _attr_base = (bt_gatt_attr_t *)balloc(attr_counter * sizeof(bt_gatt_attr_t), NULL); + memset(_attr_base, 0x00, (attr_counter * sizeof(bt_gatt_attr_t))); + pr_info(LOG_MODULE_BLE, "_attr_base_-%p, size-%d, attr_counter-%d", _attr_base, sizeof(_attr_base), attr_counter); + if (NULL == _attr_base) + { + err_code = BLE_STATUS_NO_MEMORY; + } + } + + if (BLE_STATUS_SUCCESS != err_code) + { + if (NULL != _attr_base) + { + bfree(_attr_base); + } + return err_code; + } + + pr_info(LOG_MODULE_BLE, "_attr_base_-%p", _attr_base); + + BLEServiceNodePtr node = serviceHeader->next; + while (node) + { + BLEServiceImp *service = node->value; + start = _attr_base + _attr_index; + service->updateProfile(start, _attr_index); + node = node->next; + } + +#if 0 + // Start debug + int i; + + for (i = 0; i < _attr_index; i++) { + { + pr_info(LOG_MODULE_APP, "gatt-: i %d, type %d, u16 0x%x", + i, + _attr_base[i].uuid->type, + BT_UUID_16(_attr_base[i].uuid)->val); + } + } + + delay(1000); + // End for debug +#endif + + ret = bt_gatt_register(_attr_base, + _attr_index); + pr_debug(LOG_MODULE_APP, "%s: ret, %d", __FUNCTION__, ret); + if (0 == ret) + { + _profile_registered = true; + } + return ret; +} + +void BLEProfileManager::clearProfile(BLEDevice &bledevice) +{ + BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + return; + } + + BLEServiceNodePtr node = link_node_get_first(serviceHeader); + + while (NULL != node) + { + BLEServiceImp *service = node->value; + delete[] service; + link_node_remove_first(serviceHeader); + node = link_node_get_first(serviceHeader); + } +} + +BLECharacteristicImp* BLEProfileManager::characteristic(const BLEDevice &bledevice, int index) +{ + BLECharacteristicImp* characteristicImp = NULL; + BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + // Doesn't find the service + return NULL; + } + int counter = 0; + BLEServiceNodePtr node = serviceHeader->next; + while (node != NULL) + { + BLEServiceImp *service = node->value; + int counterTmp = service->getCharacteristicCount(); + if (counter + counterTmp > index) + { + break; + } + counter += counterTmp; + node = node->next; + } + + if (NULL != node) + { + BLEServiceImp *service = node->value; + characteristicImp = service->characteristic(index - counter); + } + return characteristicImp; +} + +BLECharacteristicImp* BLEProfileManager::characteristic(const BLEDevice &bledevice, + const char* uuid, + int index) +{ + BLECharacteristicImp* characteristicImp = characteristic(bledevice, index); + if (NULL != characteristicImp) + { + if (false == characteristicImp->compareUuid(uuid)) + { + // UUID not align + characteristicImp = NULL; + } + } + return characteristicImp; +} + +BLECharacteristicImp* BLEProfileManager::characteristic(const BLEDevice &bledevice, + const char* uuid) +{ + BLECharacteristicImp* characteristicImp = NULL; + BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + // Doesn't find the service + return NULL; + } + BLEServiceNodePtr node = serviceHeader->next; + while (node != NULL) + { + BLEServiceImp *service = node->value; + characteristicImp = service->characteristic(uuid); + if (NULL != characteristicImp) + { + break; + } + node = node->next; + } + + return characteristicImp; +} + +BLEServiceImp* BLEProfileManager::service(const BLEDevice &bledevice, const char * uuid) const +{ + bt_uuid_128_t uuid_tmp; + + BLEUtils::uuidString2BT(uuid, (bt_uuid_t *)&uuid_tmp); + return service(bledevice, (const bt_uuid_t *)&uuid_tmp); +} + +BLEServiceImp* BLEProfileManager::service(const BLEDevice &bledevice, const bt_uuid_t* uuid) const +{ + BLEServiceImp* serviceImp = NULL; + const BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + // Doesn't find the service + return NULL; + } + BLEServiceNodePtr node = serviceHeader->next; + char uuid_tmp[37]; + BLEUtils::uuidBT2String(uuid, uuid_tmp); + pr_debug(LOG_MODULE_BLE, "%s-%d: %s", __FUNCTION__, __LINE__, uuid_tmp); + while (node != NULL) + { + serviceImp = node->value; + if (true == serviceImp->compareUuid(uuid)) + { + break; + } + node = node->next; + } + if (NULL == node) + { + serviceImp = NULL; + } + return serviceImp; +} + +BLEServiceImp* BLEProfileManager::service(const BLEDevice &bledevice, int index) const +{ + BLEServiceImp* serviceImp = NULL; + const BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + // Doesn't find the service + return NULL; + } + BLEServiceNodePtr node = serviceHeader->next; + + while (node != NULL) + { + if (0 == index) + { + break; + } + index--; + node = node->next; + } + if (NULL == node) + { + serviceImp = NULL; + } + else + { + serviceImp = node->value; + } + return serviceImp; +} + +void BLEProfileManager::handleConnectedEvent(const bt_addr_le_t* deviceAddr) +{ + int index = getUnusedIndex(); + if (index >= BLE_MAX_CONN_CFG) + { + //BLE_STATUS_NO_MEMORY + return; + } + bt_addr_le_copy(&_addresses[index], deviceAddr); +} + +bool BLEProfileManager::discoverAttributes(BLEDevice* device) +{ + int err; + bt_conn_t* conn; + int i = getDeviceIndex(device); + bool ret = false; + bt_gatt_discover_params_t* temp = NULL; + + pr_debug(LOG_MODULE_BLE, "%s-%d: index-%d,fun-%p", __FUNCTION__, __LINE__, i,profile_discover_process); + + if (i >= BLE_MAX_CONN_CFG) + { + // The device already in the buffer. + // This function only be called after connection established. + return ret; + } + + conn = bt_conn_lookup_addr_le(device->bt_le_address()); + if (NULL == conn) + { + // Link lost + pr_debug(LOG_MODULE_BLE, "Can't find connection\n"); + return ret; + } + temp = &_discover_params[i]; + temp->start_handle = 1; + temp->end_handle = 0xFFFF; + temp->uuid = NULL; + temp->type = BT_GATT_DISCOVER_PRIMARY; + temp->func = profile_discover_process; + + err = bt_gatt_discover(conn, temp); + bt_conn_unref(conn); + if (err) + { + pr_debug(LOG_MODULE_BLE, "Discover failed(err %d)\n", err); + return ret; + } + return true; +} + +int BLEProfileManager::getDeviceIndex(const bt_addr_le_t* macAddr) +{ + int i; + for (i = 0; i < BLE_MAX_CONN_CFG; i++) + { + if ((bt_addr_le_cmp(macAddr, &_addresses[i]) == 0)) + { + break; + } + } + return i; +} + +int BLEProfileManager::getDeviceIndex(const BLEDevice* device) +{ + return getDeviceIndex(device->bt_le_address()); +} + +uint8_t BLEProfileManager::discoverResponseProc(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params) +{ + const bt_addr_le_t* dst_addr = bt_conn_get_dst(conn); + int i = getDeviceIndex(dst_addr); + BLEDevice device(dst_addr); + uint8_t retVal = BT_GATT_ITER_STOP; + + pr_debug(LOG_MODULE_BLE, "%s-%d: index-%d", __FUNCTION__, __LINE__, i); + + if (i >= BLE_MAX_CONN_CFG) + { + return BT_GATT_ITER_STOP; + } + + // Process the service + switch (params->type) + { + case BT_GATT_DISCOVER_CHARACTERISTIC: + { + if (NULL != _cur_discover_service) + { + retVal = _cur_discover_service->discoverResponseProc(conn, + attr, + params); + } + break; + } + case BT_GATT_DISCOVER_DESCRIPTOR: + { + //descriptorDiscoverRsp(attr, attribute_tmp); + break; + } + case BT_GATT_DISCOVER_PRIMARY: + { + if (NULL != attr) + { + struct bt_gatt_service *svc_value = (struct bt_gatt_service *)attr->user_data; + int retval = (int)addService(device, svc_value->uuid); + BLEServiceImp* service_tmp = service(device, svc_value->uuid); + if (NULL != service_tmp) + { + pr_debug(LOG_MODULE_BLE, "%s-%d: handle-%d", + __FUNCTION__, __LINE__, attr->handle); + service_tmp->setHandle(attr->handle); + } + retVal = BT_GATT_ITER_CONTINUE; + } + else + { + // Service discover complete + serviceDiscoverComplete(device); + retVal = BT_GATT_ITER_STOP; + } + } + + default: + { + //attribute_tmp->discover(attr, &_discover_params); + break; + } + } + + if (retVal == BT_GATT_ITER_STOP) + { + const BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(device); + BLEServiceImp* serviceCurImp = NULL; + if (NULL == serviceHeader) + { + // Doesn't find the service + return BT_GATT_ITER_STOP; + } + BLEServiceNodePtr node = serviceHeader->next; + + // Discover next service + while (node != NULL) + { + serviceCurImp = node->value; + + if (NULL == _cur_discover_service) + { + bool result = serviceCurImp->discoverAttributes(&device); + if (result == true) + { + // Record the current discovering service + _cur_discover_service = serviceCurImp; + break; + } + } + else if (_cur_discover_service == serviceCurImp) + { + // Find next discoverable service + _cur_discover_service = NULL; + } + + node = node->next; + } + } + return retVal; +} + +void BLEProfileManager::serviceDiscoverComplete(const BLEDevice &bledevice) +{ + BLEServiceImp* serviceCurImp = NULL; + BLEServiceImp* servicePrevImp = NULL; + const BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + // Doesn't find the service + return ; + } + + BLEServiceNodePtr node = serviceHeader->next; + if (NULL != node) + { + servicePrevImp = node->value; + node = node->next; + } + + // Update the service handles + while (node != NULL) + { + serviceCurImp = node->value; + if (NULL != serviceCurImp) + { + servicePrevImp->setEndHandle(serviceCurImp->startHandle() - 1); + } + + pr_debug(LOG_MODULE_BLE, "Curr: start-%d, end-%d", servicePrevImp->startHandle(), servicePrevImp->endHandle()); + servicePrevImp = serviceCurImp; + pr_debug(LOG_MODULE_BLE, "Curr: start-%d, end-%d", servicePrevImp->startHandle(), servicePrevImp->endHandle()); + node = node->next; + } + return; +} + +#if 0 +void BLEProfileManager::characteristicDiscoverRsp(const bt_gatt_attr_t *attr, BLEAttribute* bleattr) +{ + bt_gatt_attr_t *attr_dec = declarationAttr(bleattr); + if ((NULL != attr) && (NULL != attr_dec)) + { + if (bt_uuid_cmp (attr_dec->uuid, attr->uuid) == 0) + { + attr_dec++; + attr_dec->handle = attr->handle + 1; + } + } + bleattr->discover(attr, &_discover_params); +} + +void BLEProfileManager::descriptorDiscoverRsp(const bt_gatt_attr_t *attr, BLEAttribute* bleattr) +{ + int err; + bt_gatt_attr_t *attr_dec = declarationAttr(bleattr); + if (BLETypeCharacteristic == bleattr->type()) + { + BLECharacteristic *chrc = (BLECharacteristic *)bleattr; + if (bt_uuid_cmp (chrc->getClientCharacteristicConfigUuid(), attr->uuid) == 0) + { + //CCCD + bt_gatt_attr_t *attr_chrc = attr_dec + 1; + bt_gatt_attr_t *attr_cccd = attr_dec + 2; + bt_gatt_subscribe_params_t *sub_param_tmp = chrc->getSubscribeParams(); + bt_gatt_subscribe_params_t *sub_param = _sub_param + _sub_param_idx; + bt_conn_t *conn = bt_conn_lookup_addr_le(_peripheral->bt_le_address()); + if (NULL == conn) + { + // Link lost + return; + } + + _sub_param_idx++; + attr_cccd->handle = attr->handle; + memcpy(sub_param, sub_param_tmp, sizeof(bt_gatt_subscribe_params_t)); + sub_param->ccc_handle = attr_cccd->handle; + sub_param->value_handle = attr_chrc->handle; + + // Enable CCCD to allow peripheral send Notification/Indication + err = bt_gatt_subscribe(conn, sub_param); + bt_conn_unref(conn); + if (err && err != -EALREADY) + { + pr_debug(LOG_MODULE_APP, "Subscribe failed (err %d)\n", err); + } + bleattr->discover(attr, &_discover_params); + } + else + { + // Not CCCD + // If want to support more descriptor, + // change the offset 3 as a loop to search the ATTR + bt_gatt_attr_t *attr_descriptor = attr_dec + 3; + if (attr_descriptor->uuid != NULL && + bt_uuid_cmp (attr_descriptor->uuid, attr->uuid) == 0) + { + attr_descriptor->handle = attr->handle; + } + } + } + else if (BLETypeDescriptor == bleattr->type()) + { + bt_gatt_attr_t *attr_descriptor = attr_dec++; // The descriptor is separate + if (bt_uuid_cmp (attr_dec->uuid, attr->uuid) == 0) + { + attr_descriptor->handle = attr->handle; + } + bleattr->discover(attr, &_discover_params); + } +} + +uint8_t BLEProfileManager::discover(const bt_gatt_attr_t *attr) +{ + BLEAttribute* attribute_tmp = NULL; + int i; + int err; + uint8_t ret = BT_GATT_ITER_STOP; + bool send_discover = false; + + for (i = 0; i < _num_attributes; i++) + { + // Find the discovering attribute + attribute_tmp = _attributes[i]; + if (attribute_tmp->discovering()) + { + if (NULL == attr) + { + attribute_tmp->discover(attr, &_discover_params); + break; + } + // Discover success + switch (_discover_params.type) + { + case BT_GATT_DISCOVER_CHARACTERISTIC: + { + characteristicDiscoverRsp(attr, attribute_tmp); + send_discover = true; + break; + } + case BT_GATT_DISCOVER_DESCRIPTOR: + { + descriptorDiscoverRsp(attr, attribute_tmp); + break; + } + case BT_GATT_DISCOVER_PRIMARY: + send_discover = true; + default: + { + attribute_tmp->discover(attr, &_discover_params); + break; + } + } + break; + } + } + + // Find next attribute to discover + if (attribute_tmp->discovering() == false) + { + // Current attribute complete discovery + i++; + while (i < _num_attributes) + { + attribute_tmp = _attributes[i]; + if (attribute_tmp->type() == BLETypeDescriptor) + { + // The descriptor may have been discovered by previous descriptor + bt_gatt_attr_t *attr_gatt = NULL; + for (int j = 0; j < _attr_index; j++) + { + attr_gatt = _attr_base + i; + if (attribute_tmp->uuid() == attr_gatt->uuid) + { + break; + } + } + + if (attr_gatt->handle != 0) + { + // Skip discovered descriptor + i++; + continue; + } + } + + attribute_tmp->discover(&_discover_params); + ret = BT_GATT_ITER_CONTINUE; + break; + } + } + else + { + ret = BT_GATT_ITER_CONTINUE; + } + + // Send the discover request if necessary + if (send_discover && attribute_tmp->discovering()) + { + bt_conn_t *conn = bt_conn_lookup_addr_le(_peripheral->bt_le_address()); + + ret = BT_GATT_ITER_STOP; + if (NULL == conn) + { + // Link lost + pr_debug(LOG_MODULE_APP, "Can't find connection\n"); + return ret; + } + err = bt_gatt_discover(conn, &_discover_params); + bt_conn_unref(conn); + if (err) + { + pr_debug(LOG_MODULE_APP, "Discover failed(err %d)\n", err); + return ret; + } + } + return ret; +} + + +void BLEProfileManager::discover() +{ + int err; + BLEService *serviceattr = (BLEService *)_attributes[0]; + bt_conn_t *conn = bt_conn_lookup_addr_le(_peripheral->bt_le_address()); + + if (NULL == conn) + { + // Link lost + pr_debug(LOG_MODULE_APP, "Can't find connection\n"); + return; + } + + // Reset start handle + _discover_params.start_handle = 0x0001; + serviceattr->discover(&_discover_params); + + err = bt_gatt_discover(conn, &_discover_params); + bt_conn_unref(conn); + if (err) + { + pr_debug(LOG_MODULE_APP, "Discover failed(err %d)\n", err); + return; + } +} +#endif + + diff --git a/libraries/BLE/src/BLEProfileManager.h b/libraries/BLE/src/BLEProfileManager.h new file mode 100644 index 00000000..70362c26 --- /dev/null +++ b/libraries/BLE/src/BLEProfileManager.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __BLE_PROFILE_MANAGER_H__ +#define __BLE_PROFILE_MANAGER_H__ + +#include "ArduinoBLE.h" + +#include "BLEServiceImp.h" + +//#include "BLECommon.h" +//#include "BLEDevice.h" +//#include "BLEService.h" + +class BLEProfileManager{ +public: + /** + * @brief Get the BLEProfile Manager instance + * + * @param none + * + * @return BLEProfileManager* BLE Profile manager + * + * @note none + */ + static BLEProfileManager* instance(); + + /** + * @brief Add an service to the BLE Device + * + * @param[in] bledevice The BLE device that owned the service + * + * @param[in] service The service to add to BLE device profile + * + * @return BleStatus indicating success or error + * + * @note This method must be called before the begin method in GATT server role + * Or be called in discover process. + */ + BLE_STATUS_T addService (BLEDevice &bledevice, BLEService& service); + + /** + * @brief Register the profile to Nordic BLE stack + * + * @param[in] bledevice The BLE Device + * + * @return int std C errno + * + * @note none + */ + int registerProfile(BLEDevice &bledevice); + + inline bool hasRegisterProfile(){return _profile_registered;} + + /** + * @brief Get the BLE's Characteristic implementation object by uuid and index + * + * @param[in] bledevice The BLE device + * + * @param[in] uuid The characteristic UUID + * + * @param[in] index The characteristic index in the profile + * + * @return BLECharacteristicImp* The BLE characteristic implementation object + * + * @note none + */ + BLECharacteristicImp* characteristic(const BLEDevice &bledevice, + const char* uuid, + int index); + BLECharacteristicImp* characteristic(const BLEDevice &bledevice, + const char* uuid); + BLECharacteristicImp* characteristic(const BLEDevice &bledevice, + int index); + BLEServiceImp* service(const BLEDevice &bledevice, const char * uuid) const; + BLEServiceImp* service(const BLEDevice &bledevice, int index) const; + BLEServiceImp* service(const BLEDevice &bledevice, const bt_uuid_t* uuid) const; + int serviceCount(const BLEDevice &bledevice) const; + int characteristicCount(const BLEDevice &bledevice) const; + + uint8_t discoverResponseProc(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params); + + bool discoverAttributes(BLEDevice* device); + void handleConnectedEvent(const bt_addr_le_t* deviceAddr); +protected: + friend ssize_t profile_write_process(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + const void *buf, uint16_t len, + uint16_t offset); +private: + typedef LinkNode BLEServiceLinkNodeHeader; + typedef LinkNode* BLEServiceNodePtr; + typedef LinkNode BLEServiceNode; + + BLEProfileManager(); + ~BLEProfileManager (void); + + BLE_STATUS_T addService (BLEDevice &bledevice, const bt_uuid_t* uuid); + void serviceDiscoverComplete(const BLEDevice &bledevice); + + int getDeviceIndex(const bt_addr_le_t* macAddr); + int getDeviceIndex(const BLEDevice* device); + /** + * @brief Get the unused service header index + * + * @param none + * + * @return int The unused BLE profile index + * + * @note This object has a buffer to manage all devices profile. + * The buffer is an array. The different profiles + * distinguished by BLE address. + */ + int getUnusedIndex(); + + /** + * @brief Get the Service header by BLE device + * + * @param[in] bledevice The BLE device + * + * @return none + * + * @note none + */ + BLEServiceLinkNodeHeader* getServiceHeader(const BLEDevice &bledevice); + const BLEServiceLinkNodeHeader* getServiceHeader(const BLEDevice &bledevice) const; + + /** + * @brief Get the BLE attribute counter based on services, characteristics + * and descriptors. + * + * @param none + * + * @return none + * + * @note none + */ + int getAttributeCount(BLEDevice &bledevice); + + /** + * @brief Discard the profile by BLE device + * + * @param[in] bledevice The BLE device + * + * @return none + * + * @note none + */ + void clearProfile(BLEDevice &bledevice); + +private: + // The last header is for local BLE + BLEServiceLinkNodeHeader _service_header_array[BLE_MAX_CONN_CFG + 1]; // The connected devices' service and self service + bt_addr_le_t _addresses[BLE_MAX_CONN_CFG + 1]; // The BLE devices' address + + bt_gatt_discover_params_t _discover_params[BLE_MAX_CONN_CFG]; + BLEServiceImp* _cur_discover_service; + + bt_gatt_attr_t *_attr_base; // Allocate the memory for BLE stack + int _attr_index; + + bt_gatt_subscribe_params_t *_sub_param; + int _sub_param_idx; + + static BLEProfileManager* _instance; // The profile manager instance + bool _profile_registered; +}; + +#endif + diff --git a/libraries/BLE/src/BLEService.cpp b/libraries/BLE/src/BLEService.cpp new file mode 100644 index 00000000..2c2e80c5 --- /dev/null +++ b/libraries/BLE/src/BLEService.cpp @@ -0,0 +1,194 @@ +/* + BLE Service API + Copyright (c) 2016 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include "BLEService.h" + +#include "BLEProfileManager.h" +#include "BLECharacteristicImp.h" + +#include "BLEUtils.h" + +BLEService::BLEService():_bledevice(),_service_imp(NULL) +{ + memset(_uuid_cstr, 0, sizeof (_uuid_cstr)); +} + +BLEService::BLEService(const char* uuid):_bledevice(),_service_imp(NULL) +{ + bt_uuid_128_t uuid_tmp; + memset(_uuid_cstr, 0, sizeof (_uuid_cstr)); + BLEUtils::uuidString2BT(uuid, (bt_uuid_t *)&uuid_tmp); + BLEUtils::uuidBT2String((const bt_uuid_t *)&uuid_tmp, _uuid_cstr); + + _bledevice.setAddress(*BLEUtils::bleGetLoalAddress()); +} + +BLEService::BLEService(BLEServiceImp* serviceImp, const BLEDevice* bledev): + _bledevice(bledev),_service_imp(serviceImp) +{ + memset(_uuid_cstr, 0, sizeof (_uuid_cstr)); + BLEUtils::uuidBT2String(serviceImp->bt_uuid(), _uuid_cstr); +} + +BLEService::~BLEService() +{ +} + +BLEService::operator bool() const +{ + return (strlen(_uuid_cstr) > 3); +} + +const char* BLEService::uuid() const +{ + return _uuid_cstr; +} + +void BLEService::addCharacteristic(BLECharacteristic& characteristic) +{ + BLEServiceImp* serviceImp = getServiceImp(); + + if (NULL != serviceImp) + { + serviceImp->addCharacteristic(_bledevice, characteristic); + } +} + +int BLEService::characteristicCount() const +{ + int count = 0; + BLEServiceImp* serviceImp = getServiceImp(); + if (NULL != serviceImp) + { + count = serviceImp->getCharacteristicCount(); + } + return count; +} + +bool BLEService::hasCharacteristic(const char* uuid) const +{ + BLECharacteristicImp* characteristicImp = NULL; + BLEServiceImp* serviceImp = getServiceImp(); + if (NULL != serviceImp) + { + characteristicImp = serviceImp->characteristic(uuid); + } + return (NULL != characteristicImp); +} + +bool BLEService::hasCharacteristic(const char* uuid, int index) const +{ + BLECharacteristicImp* characteristicImp = NULL; + BLEServiceImp* serviceImp = getServiceImp(); + if (NULL != serviceImp) + { + characteristicImp = serviceImp->characteristic(index); + if (false == characteristicImp->compareUuid(uuid)) + { + // UUID not align + characteristicImp = NULL; + } + } + return (NULL != characteristicImp); +} + +BLECharacteristic BLEService::characteristic(int index) const +{ + BLECharacteristicImp* characteristicImp = NULL; + BLEServiceImp* serviceImp = getServiceImp(); + if (NULL != serviceImp) + { + characteristicImp = serviceImp->characteristic(index); + } + if (NULL == characteristicImp) + { + BLECharacteristic temp; + return temp; + } + else + { + BLECharacteristic temp(characteristicImp, &_bledevice); + return temp; + } +} + +BLECharacteristic BLEService::characteristic(const char * uuid) const +{ + BLECharacteristicImp* characteristicImp = NULL; + BLEServiceImp* serviceImp = getServiceImp(); + if (NULL != serviceImp) + { + characteristicImp = serviceImp->characteristic(uuid); + } + + if (NULL == characteristicImp) + { + BLECharacteristic temp; + return temp; + } + else + { + BLECharacteristic temp(characteristicImp, &_bledevice); + return temp; + } +} + +BLECharacteristic BLEService::characteristic(const char * uuid, int index) const +{ + BLECharacteristicImp* characteristicImp = NULL; + BLEServiceImp* serviceImp = getServiceImp(); + if (NULL != serviceImp) + { + characteristicImp = serviceImp->characteristic(index); + if (false == characteristicImp->compareUuid(uuid)) + { + // UUID not align + characteristicImp = NULL; + } + } + if (NULL == characteristicImp) + { + BLECharacteristic temp; + return temp; + } + else + { + BLECharacteristic temp(characteristicImp, &_bledevice); + return temp; + } +} + +BLEServiceImp* BLEService::getServiceImp() +{ + if (NULL == _service_imp) + { + _service_imp = BLEProfileManager::instance()->service(_bledevice, uuid()); + } + return _service_imp; +} + +BLEServiceImp* BLEService::getServiceImp() const +{ + return _service_imp; +} + +void BLEService::setServiceImp(BLEServiceImp* serviceImp) +{ + _service_imp = serviceImp; +} + diff --git a/libraries/BLE/src/BLEService.h b/libraries/BLE/src/BLEService.h new file mode 100644 index 00000000..de4973dd --- /dev/null +++ b/libraries/BLE/src/BLEService.h @@ -0,0 +1,137 @@ +/* + BLE Service API + Copyright (c) 2016 Arduino LLC. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef ARDUINO_BLE_SERVICE_H +#define ARDUINO_BLE_SERVICE_H + +#include "ArduinoBLE.h" +#include "BLEDevice.h" + +//class BLECharacteristic; +class BLEServiceImp; + +class BLEService +{ +public: + BLEService(); + BLEService(const char* uuid); + virtual ~BLEService(); + + virtual operator bool() const; // is the service valid + + const char* uuid() const; + + /** + * @brief Add a characteristic in service + * + * @param characteristic The characteristic want to be added to service + * + * @return none + * + * @note none + */ + void addCharacteristic(BLECharacteristic& characteristic); + + /** + * @brief Get the number of characteristics the service has + * + * @param none + * + * @return none + * + * @note none + */ + int characteristicCount() const; + + /** + * @brief Does the service have a characteristic with the specified UUID + * + * @param uuid The UUID of the characteristic + * + * @return bool true - Yes. false - No + * + * @note none + */ + bool hasCharacteristic(const char* uuid) const; + + /** + * @brief Does the service have an nth characteristic with the + * specified UUID + * + * @param uuid The UUID of the characteristic + * + * @param index The index of characteristic + * + * @return bool true - Yes. false - No + * + * @note none + */ + bool hasCharacteristic(const char* uuid, int index) const; + + /** + * @brief Return the nth characteristic of the service + * + * @param index The index of characteristic + * + * @return BLECharacteristic The characteristic + * + * @note none + */ + BLECharacteristic characteristic(int index) const; + + /** + * @brief Return the characteristic with the specified UUID + * + * @param uuid The UUID of the characteristic + * + * @return BLECharacteristic The characteristic + * + * @note none + */ + BLECharacteristic characteristic(const char * uuid) const; + + /** + * @brief return the nth characteristic with the specified UUID + * + * @param uuid The UUID of the characteristic + * + * @param index The index of characteristic + * + * @return BLECharacteristic The characteristic + * + * @note none + */ + BLECharacteristic characteristic(const char * uuid, int index) const; + +protected: + friend class BLEDevice; + friend class BLEServiceImp; + BLEService(BLEServiceImp* serviceImp, const BLEDevice* bledev); + void setServiceImp(BLEServiceImp* serviceImp); +private: + BLEServiceImp* getServiceImp(); + BLEServiceImp* getServiceImp() const; + +private: + BLEDevice _bledevice; + BLEServiceImp* _service_imp; + char _uuid_cstr[37]; +}; + +#endif diff --git a/libraries/BLE/src/BLEServiceImp.cpp b/libraries/BLE/src/BLEServiceImp.cpp new file mode 100644 index 00000000..15628c15 --- /dev/null +++ b/libraries/BLE/src/BLEServiceImp.cpp @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2015 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "internal/ble_client.h" + +#include "BLEServiceImp.h" +#include "BLECallbacks.h" +#include "BLEUtils.h" +#include "BLECharacteristicImp.h" + +bt_uuid_16_t BLEServiceImp::_gatt_primary_uuid = {BT_UUID_TYPE_16, BT_UUID_GATT_PRIMARY_VAL}; + +bt_uuid_t *BLEServiceImp::getPrimayUuid(void) +{ + return (bt_uuid_t *)&_gatt_primary_uuid; +} + +BLEServiceImp::BLEServiceImp(BLEService& service): + BLEAttribute(service.uuid(), BLETypeService), + _start_handle(0), + _end_handle(0xFFFF) +{ + memset(&_characteristics_header, 0, sizeof(_characteristics_header)); + service.setServiceImp(this); +} + +BLEServiceImp::BLEServiceImp(const bt_uuid_t* uuid): + BLEAttribute(uuid, BLETypeService), + _start_handle(0), + _end_handle(0xFFFF) +{ + memset(&_characteristics_header, 0, sizeof(_characteristics_header)); +} + +BLEServiceImp::~BLEServiceImp() +{ + releaseCharacteristic(); +} + + +int BLEServiceImp::addCharacteristic(BLEDevice& bledevice, BLECharacteristic& characteristic) +{ + BLECharacteristicImp* characteristicImp = new BLECharacteristicImp(characteristic, bledevice); + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + if (NULL == characteristicImp) + { + return BLE_STATUS_NO_MEMORY; + } + + BLECharacteristicNodePtr node = link_node_create(characteristicImp); + if (NULL == node) + { + delete[] characteristicImp; + return BLE_STATUS_NO_MEMORY; + } + link_node_insert_last(&_characteristics_header, node); + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + return BLE_STATUS_SUCCESS; +} + +int BLEServiceImp::updateProfile(bt_gatt_attr_t *attr_start, int& index) +{ + bt_gatt_attr_t *start = attr_start; + int base_index = index; + int offset = 0; + int counter = 0; + start->uuid = BLEServiceImp::getPrimayUuid(); + start->perm = BT_GATT_PERM_READ; + start->read = bt_gatt_attr_read_service; + start->user_data = (void *)bt_uuid(); + + pr_debug(LOG_MODULE_BLE, "service-%p", start); + start++; + index++; + counter++; + + BLECharacteristicNodePtr node = _characteristics_header.next; + while (NULL != node) + { + BLECharacteristicImp *characteristicImp = node->value; + start = attr_start + index - base_index; + offset = characteristicImp->updateProfile(start, index); + counter += offset; + node = node->next; + } + return counter; +} + +int BLEServiceImp::getAttributeCount() +{ + int counter = 1; // Service itself + + BLECharacteristicNodePtr node = _characteristics_header.next; + while (NULL != node) + { + BLECharacteristicImp *characteristicImp = node->value; + + counter += characteristicImp->getAttributeCount(); + node = node->next; + } + return counter; +} + +int BLEServiceImp::getCharacteristicCount() +{ + return link_list_size(&_characteristics_header); +} + +void BLEServiceImp::releaseCharacteristic() +{ + BLECharacteristicNodePtr node = link_node_get_first(&_characteristics_header); + + while (NULL != node) + { + BLECharacteristicImp* characteristicImp = node->value; + delete[] characteristicImp; + link_node_remove_first(&_characteristics_header); + node = link_node_get_first(&_characteristics_header); + } +} + + +BLECharacteristicImp* BLEServiceImp::characteristic(int index) +{ + BLECharacteristicImp* characteristicImp = NULL; + BLECharacteristicNodePtr node = link_node_get_first(&_characteristics_header); + while (NULL != node) + { + if (0 >= index) + { + characteristicImp = node->value; + break; + } + index--; + node = node->next; + } + return characteristicImp; +} + +BLECharacteristicImp* BLEServiceImp::characteristic(uint16_t handle) +{ + BLECharacteristicImp* characteristicImp = NULL; + BLECharacteristicNodePtr node = link_node_get_first(&_characteristics_header); + while (NULL != node) + { + characteristicImp = node->value; + if (handle == characteristicImp->valueHandle()) + { + break; + } + node = node->next; + } + if (NULL == node) + { + characteristicImp = NULL; + } + return characteristicImp; +} + +BLECharacteristicImp* BLEServiceImp::characteristic(const char* uuid) +{ + BLECharacteristicImp* characteristicImp = NULL; + BLECharacteristicNodePtr node = link_node_get_first(&_characteristics_header); + while (NULL != node) + { + characteristicImp = node->value; + if (true == characteristicImp->compareUuid(uuid)) + { + break; + } + node = node->next; + } + + if (NULL == node) + { + characteristicImp = NULL; + } + return characteristicImp; +} + +bool BLEServiceImp::discoverAttributes(BLEDevice* device) +{ + + int err; + bt_conn_t* conn; + bt_gatt_discover_params_t* temp = NULL; + const bt_uuid_t* service_uuid = bt_uuid(); + + if (service_uuid->type == BT_UUID_TYPE_16) + { + uint16_t uuid_tmp = ((bt_uuid_16_t*)service_uuid)->val; + if (BT_UUID_GAP_VAL == uuid_tmp || + BT_UUID_GATT_VAL == uuid_tmp) + { + return false; + } + } + + conn = bt_conn_lookup_addr_le(device->bt_le_address()); + if (NULL == conn) + { + // Link lost + pr_debug(LOG_MODULE_BLE, "Can't find connection\n"); + return false; + } + temp = &_discover_params; + temp->start_handle = _start_handle; + temp->end_handle = _end_handle; + temp->uuid = NULL; + temp->type = BT_GATT_DISCOVER_CHARACTERISTIC; + temp->func = profile_discover_process; + + err = bt_gatt_discover(conn, temp); + bt_conn_unref(conn); + if (err) + { + pr_debug(LOG_MODULE_BLE, "Discover failed(err %d)\n", err); + return false; + } + return true; +} + +uint8_t BLEServiceImp::discoverResponseProc(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params) +{ + const bt_addr_le_t* dst_addr = bt_conn_get_dst(conn); + BLEDevice device(dst_addr); + uint8_t retVal = BT_GATT_ITER_STOP; + + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + if (NULL == attr) + { + return BT_GATT_ITER_STOP; + } + const bt_uuid_t* chrc_uuid = attr->uuid; + struct bt_gatt_chrc* psttemp = (struct bt_gatt_chrc*)attr->user_data; + char uuidbuffer[37]; + BLEUtils::uuidBT2String(chrc_uuid, uuidbuffer); + pr_debug(LOG_MODULE_BLE, "%s-%d: uuid type: %d\r\n%s", __FUNCTION__, __LINE__, chrc_uuid->type, uuidbuffer); + + BLEUtils::uuidBT2String(psttemp->uuid, uuidbuffer); + pr_debug(LOG_MODULE_BLE, "%s-%d:%p uuid type: %d properties: %d\r\n%s", __FUNCTION__, __LINE__,psttemp, psttemp->uuid->type, psttemp->properties, uuidbuffer); + // Process the service + switch (params->type) + { + case BT_GATT_DISCOVER_CHARACTERISTIC: + { + //characteristicDiscoverRsp(attr, attribute_tmp); + //send_discover = true; + break; + } + default: + { + //attribute_tmp->discover(attr, &_discover_params); + break; + } + } + + if (retVal == BT_GATT_ITER_STOP) + { + const BLECharacteristicLinkNodeHeader* serviceHeader = &_characteristics_header; + BLECharacteristicImp* chrcCurImp = NULL; + BLECharacteristicNodePtr node = serviceHeader->next; + + // Discover next service + while (node != NULL) + { + chrcCurImp = node->value; + + if (NULL == _cur_discover_chrc) + { + bool result = true;//serviceCurImp->discoverAttributes(&device); + if (result == true) + { + // Record the current discovering service + _cur_discover_chrc = chrcCurImp; + break; + } + } + else if (_cur_discover_chrc == chrcCurImp) + { + // Find next discoverable service + _cur_discover_chrc = NULL; + } + node = node->next; + } + } + return retVal; +} + + diff --git a/libraries/BLE/src/BLEServiceImp.h b/libraries/BLE/src/BLEServiceImp.h new file mode 100644 index 00000000..d9706edf --- /dev/null +++ b/libraries/BLE/src/BLEServiceImp.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2015 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _BLE_SERVICE_IMP_H_INCLUDED +#define _BLE_SERVICE_IMP_H_INCLUDED + +#include "ArduinoBLE.h" + +#include "BLEAttribute.h" + +#include "LinkList.h" + +/** + * BLE GATT Service + */ +class BLEServiceImp: public BLEAttribute{ +public: + /** + * Constructor for BLE Service + * + * @param[in] uuid 16-bit or 128-bit UUID (in string form) defined by BLE standard + */ + BLEServiceImp(BLEService& service); + BLEServiceImp(const bt_uuid_t* uuid); + ~BLEServiceImp(); + + /** + * @brief Add a characteristic in service + * + * @param[in] bledevice The BLE device want to add the characteristic + * + * @param[in] characteristic The characteristic want to be added to service + * + * @return none + * + * @note none + */ + int addCharacteristic(BLEDevice& bledevice, BLECharacteristic& characteristic); + + int getCharacteristicCount(); + + BLECharacteristicImp* characteristic(const char* uuid); + BLECharacteristicImp* characteristic(int index); + BLECharacteristicImp* characteristic(uint16_t handle); + inline void setHandle(uint16_t handle){_start_handle = handle;} + inline void setEndHandle(uint16_t handle){_end_handle = handle;} + inline uint16_t endHandle(){return _end_handle;} + inline uint16_t startHandle(){return _start_handle;} + + bool discoverAttributes(BLEDevice* device); + uint8_t discoverResponseProc(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params); +protected: + friend class BLEProfileManager; + + int getAttributeCount(); + + + int updateProfile(bt_gatt_attr_t *attr_start, int& index); + + static bt_uuid_t *getPrimayUuid(void); +private: + typedef LinkNode BLECharacteristicLinkNodeHeader; + typedef LinkNode* BLECharacteristicNodePtr; + typedef LinkNode BLECharacteristicNode; + + uint16_t _start_handle; + uint16_t _end_handle; + + void releaseCharacteristic(); + BLECharacteristicImp *_cur_discover_chrc; + + static bt_uuid_16_t _gatt_primary_uuid; + bt_gatt_discover_params_t _discover_params; + + BLECharacteristicLinkNodeHeader _characteristics_header; // The characteristic link list +}; + +#endif // _BLE_SERVICE_H_INCLUDED diff --git a/libraries/BLE/src/BLEUtils.cpp b/libraries/BLE/src/BLEUtils.cpp new file mode 100644 index 00000000..461b5485 --- /dev/null +++ b/libraries/BLE/src/BLEUtils.cpp @@ -0,0 +1,148 @@ + +#include "ArduinoBLE.h" +#include "BLEUtils.h" +#include "internal/ble_client.h" + +String BLEUtils::macAddressBT2String(const bt_addr_le_t &bd_addr) +{ + char mac_string[BT_ADDR_STR_LEN]; + snprintf(mac_string, BT_ADDR_STR_LEN, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", + bd_addr.val[5], bd_addr.val[4], bd_addr.val[3], + bd_addr.val[2], bd_addr.val[1], bd_addr.val[0]); + String temp(mac_string); + return temp; +} + +void BLEUtils::macAddressString2BT(const char* mac_str, bt_addr_le_t &bd_addr) +{ + char temp[] = {0, 0, 0}; + int strLength = strlen(mac_str); + int length = 0; + + bd_addr.type = BT_ADDR_LE_PUBLIC; + + for (int i = strLength - 1; i >= 0 && length < MAX_UUID_SIZE; i -= 2) + { + if (mac_str[i] == ':') + { + i++; + continue; + } + + temp[0] = mac_str[i - 1]; + temp[1] = mac_str[i]; + + bd_addr.val[length] = strtoul(temp, NULL, 16); + + length++; + } + +} + +bool BLEUtils::macAddressValid(const bt_addr_le_t &bd_addr) +{ + static const bt_addr_le_t zero = {0,{0,0,0,0,0,0}}; + bool temp = (memcmp(bd_addr.val, zero.val, 6) != 0);//false;// + #if 0 + for (int i = 0; i < 6; i++) + { + if (bd_addr.val[i] != zero.val[i]) + { + +pr_info(LOG_MODULE_BLE, "%s-idx %d-%.2x:%.2x", __FUNCTION__, i ,bd_addr.val[i], zero.val[i]); +pr_info(LOG_MODULE_BLE,"%s",BLEUtils::macAddressBT2String(zero).c_str()); + temp = true; + break; + } + } + #endif + return temp; +} + + +bt_addr_le_t* BLEUtils::bleGetLoalAddress() +{ + static bt_addr_le_t board_addr; + if (false == macAddressValid(board_addr)) + ble_client_get_mac_address(&board_addr); + return &board_addr; +} + + + +void BLEUtils::uuidString2BT(const char* uuid, bt_uuid_t* pstuuid) +{ + char temp[] = {0, 0, 0}; + int strLength = strlen(uuid); + int length = 0; + bt_uuid_128_t uuid_tmp; + + memset (&uuid_tmp, 0x00, sizeof(uuid_tmp)); + + for (int i = strLength - 1; i >= 0 && length < MAX_UUID_SIZE; i -= 2) + { + if (uuid[i] == '-') + { + i++; + continue; + } + + temp[0] = uuid[i - 1]; + temp[1] = uuid[i]; + + uuid_tmp.val[length] = strtoul(temp, NULL, 16); + + length++; + } + + if (length == 2) + { + uint16_t temp = (uuid_tmp.val[1] << 8)| uuid_tmp.val[0]; + uuid_tmp.uuid.type = BT_UUID_TYPE_16; + ((bt_uuid_16_t*)(&uuid_tmp.uuid))->val = temp; + } + else + { + uuid_tmp.uuid.type = BT_UUID_TYPE_128; + } + memcpy(pstuuid, &uuid_tmp, sizeof (uuid_tmp)); +} + +void BLEUtils::uuidBT2String(const bt_uuid_t* pstuuid, char* uuid) +{ + unsigned int tmp1, tmp5; + uint16_t tmp0, tmp2, tmp3, tmp4; + // TODO: Change the magic number 37 + switch (pstuuid->type) { + case BT_UUID_TYPE_16: + memcpy(&tmp0, &BT_UUID_16(pstuuid)->val, sizeof(tmp0)); + snprintf(uuid, 37, "%.4x", tmp0); + break; + case BT_UUID_TYPE_128: + memcpy(&tmp0, &BT_UUID_128(pstuuid)->val[0], sizeof(tmp0)); + memcpy(&tmp1, &BT_UUID_128(pstuuid)->val[2], sizeof(tmp1)); + memcpy(&tmp2, &BT_UUID_128(pstuuid)->val[6], sizeof(tmp2)); + memcpy(&tmp3, &BT_UUID_128(pstuuid)->val[8], sizeof(tmp3)); + memcpy(&tmp4, &BT_UUID_128(pstuuid)->val[10], sizeof(tmp4)); + memcpy(&tmp5, &BT_UUID_128(pstuuid)->val[12], sizeof(tmp5)); + snprintf(uuid, 37, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x", + tmp5, tmp4, tmp3, tmp2, tmp1, tmp0); + break; + default: + memset(uuid, 0, 37); + return; + } +} + +BLEDevice& BLEUtils::getLoacalBleDevice() +{ + return BLE; +} + +bool BLEUtils::isLocalBLE(BLEDevice& device) +{ + return (device == BLE); +} + + + diff --git a/libraries/BLE/src/BLEUtils.h b/libraries/BLE/src/BLEUtils.h new file mode 100644 index 00000000..e3bc241f --- /dev/null +++ b/libraries/BLE/src/BLEUtils.h @@ -0,0 +1,13 @@ + +namespace BLEUtils +{ + String macAddressBT2String(const bt_addr_le_t &bd_addr); + void macAddressString2BT(const char* mac_str, bt_addr_le_t &bd_addr); + bool macAddressValid(const bt_addr_le_t &bd_addr); + bt_addr_le_t* bleGetLoalAddress(); + void uuidString2BT(const char* uuid, bt_uuid_t* pstuuid); + void uuidBT2String(const bt_uuid_t* pstuuid, char* uuid); + BLEDevice& getLoacalBleDevice(); + bool isLocalBLE(BLEDevice& device); +} + diff --git a/libraries/BLE/src/LinkList.h b/libraries/BLE/src/LinkList.h new file mode 100644 index 00000000..65c34fe4 --- /dev/null +++ b/libraries/BLE/src/LinkList.h @@ -0,0 +1,78 @@ +#ifndef _LINKLIST_H_ +#define _LINKLIST_H_ + +template struct LinkNode { + LinkNode *next; + T value; +}; + +template LinkNode* link_node_create(T value) +{ + LinkNode* node = (LinkNode*)malloc(sizeof(LinkNode)); + node->value = value; + node->next = NULL; + return node; +} + +template void link_node_insert_last(LinkNode *root, LinkNode *node) +{ + while(root->next != 0) + { + root = root->next; + } + root->next = node; +} + +template void link_node_remove_last(LinkNode *root) +{ + LinkNode *temp1, *temp2; + if (root->next != NULL) + { + temp1 = root->next; + while(temp1->next != NULL) + { + temp2 = temp1; + temp1 = temp1->next; + } + + free(temp1); + temp2->next = NULL; + } +} + +template void link_node_remove_first(LinkNode *root) +{ + LinkNode *temp1; + if (root->next != NULL) + { + temp1 = root->next; + root->next = temp1->next; + free(temp1); + } +} + +template LinkNode * link_node_get_first(LinkNode *root) +{ + return root->next; +} + +template void link_node_insert_first(LinkNode *root, LinkNode *node) +{ + LinkNode* temp = root->next; + root->next = node; + node->next = temp; +} + +template int link_list_size(const LinkNode *root) +{ + int counter = 0; + while(root->next != 0) + { + root = root->next; + counter++; + } + return counter; +} + +#endif + diff --git a/libraries/BLE/src/internal/ble_client.c b/libraries/BLE/src/internal/ble_client.c new file mode 100644 index 00000000..5a9e6164 --- /dev/null +++ b/libraries/BLE/src/internal/ble_client.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2015, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include "cfw/cfw.h" +#include "cfw/cfw_debug.h" +#include "cfw/cfw_messages.h" +#include "cfw/cfw_internal.h" +#include "cfw/cfw_service.h" +#include "cfw_platform.h" +#include "infra/time.h" +#include "infra/factory_data.h" +#include "infra/version.h" +#include "curie_factory_data.h" +#include "portable.h" + +#include "uart.h" +#include "ipc_uart_ns16550.h" +#include "infra/ipc_uart.h" + +#include "ble_client.h" +#include "platform.h" + +#include "infra/log.h" + +// APP callback +static ble_client_connect_event_cb_t ble_client_connect_event_cb = NULL; +static void *ble_client_connect_event_param; + +static ble_client_disconnect_event_cb_t ble_client_disconnect_event_cb = NULL; +static void *ble_client_disconnect_event_param; + +static ble_client_update_param_event_cb_t ble_client_update_param_event_cb = NULL; +static void *ble_client_update_param_event_param; + + +#define NIBBLE_TO_CHAR(n) \ + ((n) >= 0xA ? ('A' + (n) - 0xA) : ('0' + (n))) + +#define BYTE_TO_STR(s, byte) \ + do { \ + *s++ = NIBBLE_TO_CHAR(byte >> 4); \ + *s++ = NIBBLE_TO_CHAR(byte & 0xF); \ + }while(0) + + +#ifdef __cplusplus +extern "C" { +#endif + +static void on_connected(bt_conn_t *conn, uint8_t err) +{ + if (ble_client_connect_event_cb) + { + ble_client_connect_event_cb(conn, err, ble_client_connect_event_param); + } +} + +static void on_disconnected(bt_conn_t *conn, uint8_t reason) +{ + if (ble_client_disconnect_event_cb) + { + ble_client_disconnect_event_cb(conn, reason, ble_client_disconnect_event_param); + } +} + +static void on_le_param_updated(bt_conn_t *conn, uint16_t interval, + uint16_t latency, uint16_t timeout) +{ + if (ble_client_update_param_event_cb) + { + ble_client_update_param_event_cb (conn, + interval, + latency, + timeout, + ble_client_update_param_event_param); + } +} + +static struct bt_conn_cb conn_callbacks = { + .connected = on_connected, + .disconnected = on_disconnected, + .le_param_updated = on_le_param_updated +}; + +void ble_client_get_mac_address(bt_addr_le_t *bda) +{ + struct curie_oem_data *p_oem = NULL; + unsigned i; + + /* Set the MAC address defined in Factory Data (if provided) + * Otherwise, the device will default to a static random address */ + if (bda) { + bda->type = BLE_DEVICE_ADDR_INVALID; + if (!strncmp((char*)global_factory_data->oem_data.magic, FACTORY_DATA_MAGIC, 4)) { + p_oem = (struct curie_oem_data *) &global_factory_data->oem_data.project_data; + if (p_oem->bt_mac_address_type < 2) { + bda->type = p_oem->bt_mac_address_type; + for (i = 0; i < BLE_ADDR_LEN; i++) + bda->val[i] = p_oem->bt_address[BLE_ADDR_LEN - 1 - i]; + } + } + } +} + +void ble_client_get_factory_config(bt_addr_le_t *bda, char *name) +{ + struct curie_oem_data *p_oem = NULL; + + ble_client_get_mac_address(bda); + + /* Set a default name if one has not been specified */ + if (name) { + + // Need to check in the OTP if there is some board name set + // If yes, let's read it, otherwise let's keep the default + // name set in BLE_DEVICE_NAME_DEFAULT_PREFIX + const struct customer_data* otp_data_ptr = (struct customer_data*)(FACTORY_DATA_ADDR + 0x200); + char *suffix; + + // checking the presence of key patterns + if ((otp_data_ptr->patternKeyStart == PATTERN_KEY_START) && + (otp_data_ptr->patternKeyEnd == PATTERN_KEY_END)) + { + // The board name is with OTP ar programmed + uint8_t len = otp_data_ptr->board_name_len; + + // We need to reserve 5 bytes for '-' and 4 last MAC address in ASCII + if (len > BLE_MAX_DEVICE_NAME - 5) len = BLE_MAX_DEVICE_NAME - 5; + strncpy(name, (const char *)otp_data_ptr->board_name, len); + suffix = name + len; + } + else + { + // There is no board name in the OTP area + suffix = name + strlen(BLE_DEVICE_NAME_DEFAULT_PREFIX); + strcpy(name, BLE_DEVICE_NAME_DEFAULT_PREFIX); + } + + // Adding the four last digits of MAC address separated by '-' sufix + if (bda && bda->type != BLE_DEVICE_ADDR_INVALID) + { + *suffix++ = '-'; + p_oem = (struct curie_oem_data *) &global_factory_data->oem_data.project_data; + BYTE_TO_STR(suffix, p_oem->bt_address[4]); + BYTE_TO_STR(suffix, p_oem->bt_address[5]); + *suffix = 0; /* NULL-terminate the string. Note the macro BYTE_TO_STR + automatically move the pointer */ + } + else + { + /* This code segment will be only reached if Curie module was not + provisioned properly with a BLE MAC address*/ + *suffix++ = 0; /* NULL-terminate the string */ + } + } +} + +void ble_client_init(ble_client_connect_event_cb_t connect_cb, void* connect_param, + ble_client_disconnect_event_cb_t disconnect_cb, void* disconnect_param, + ble_client_update_param_event_cb_t update_param_cb, void* update_param_param) +{ + //uint32_t delay_until; + pr_info(LOG_MODULE_BLE, "%s", __FUNCTION__); + ble_client_connect_event_cb = connect_cb; + ble_client_connect_event_param = connect_param; + + ble_client_disconnect_event_cb = disconnect_cb; + ble_client_disconnect_event_param = disconnect_param; + + ble_client_update_param_event_cb = update_param_cb; + ble_client_update_param_event_param = update_param_param; + + bt_conn_cb_register(&conn_callbacks); + return; +} + +BLE_STATUS_T errorno_to_ble_status(int err) +{ + BLE_STATUS_T err_code; + err = 0 - err; + + switch(err) { + case 0: + err_code = BLE_STATUS_SUCCESS; + break; + case EIO: + err_code = BLE_STATUS_WRONG_STATE; + break; + case EBUSY: + err_code = BLE_STATUS_TIMEOUT; + break; + case EFBIG: + case ENOTSUP: + err_code = BLE_STATUS_NOT_SUPPORTED; + break; + case EPERM: + case EACCES: + err_code = BLE_STATUS_NOT_ALLOWED; + break; + case ENOMEM: // No memeory + err_code = BLE_STATUS_NO_MEMORY; + break; + default: + err_code = BLE_STATUS_ERROR; + break; + } + return err_code; +} + + +#ifdef __cplusplus +} +#endif diff --git a/libraries/BLE/src/internal/ble_client.h b/libraries/BLE/src/internal/ble_client.h new file mode 100644 index 00000000..75c3d59f --- /dev/null +++ b/libraries/BLE/src/internal/ble_client.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2015, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _BLE_CLIENT_H_INCLUDED +#define _BLE_CLIENT_H_INCLUDED + +#include "BLECommon.h" + +enum { + UNIT_0_625_MS = 625, /**< Number of microseconds in 0.625 milliseconds. */ + UNIT_1_25_MS = 1250, /**< Number of microseconds in 1.25 milliseconds. */ + UNIT_10_MS = 10000 /**< Number of microseconds in 10 milliseconds. */ +}; + +#define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION)) +#define UNITS_TO_MSEC(TIME, RESOLUTION) (((TIME) * RESOLUTION) / 1000) + +/* Connection parameters used for Peripheral Preferred Connection Parameterss (PPCP) and update request */ +#define DEFAULT_MIN_CONN_INTERVAL MSEC_TO_UNITS(80, UNIT_1_25_MS) +#define DEFAULT_MAX_CONN_INTERVAL MSEC_TO_UNITS(150, UNIT_1_25_MS) +#define MIN_CONN_INTERVAL 0x0006 +#define MAX_CONN_INTERVAL 0x0C80 +#define SLAVE_LATENCY 0 +#define CONN_SUP_TIMEOUT MSEC_TO_UNITS(6000, UNIT_10_MS) + +/* Borrowed from ble_service_utils.h */ +#define UINT8_TO_LESTREAM(p, i) \ + do { *(p)++ = (uint8_t)(i); } \ + while (0) +#define UINT16_TO_LESTREAM(p, i) UINT8_TO_LESTREAM(p, i); UINT8_TO_LESTREAM(p, (i)>>8) +#define UINT32_TO_LESTREAM(p, i) UINT16_TO_LESTREAM(p, i); UINT16_TO_LESTREAM(p, (i)>>16) + +#define INT8_TO_LESTREAM(p, i) UINT8_TO_LESTREAM(p, (uint8_t)(i)) +#define INT16_TO_LESTREAM(p, i) UINT16_TO_LESTREAM(p, (uint16_t)(i)) +#define INT32_TO_LESTREAM(p, i) UINT32_TO_LESTREAM(p, (uint32_t)(i)) + +#define LESTREAM_TO_UINT8(p, i) \ + do { i = *p; p++; } \ + while (0) +#define LESTREAM_TO_UINT16(p, i) \ + do { uint16_t temp16; LESTREAM_TO_UINT8(p, i); LESTREAM_TO_UINT8(p, temp16); i |= (temp16 << 8); } \ + while (0) +#define LESTREAM_TO_UINT32(p, i) \ + do { uint32_t temp32; LESTREAM_TO_UINT16(p, i); LESTREAM_TO_UINT16(p, temp32); i |= (temp32 << 16); } \ + while (0) + +#define LESTREAM_TO_INT8(p, i) \ + do {uint8_t __i; LESTREAM_TO_UINT8(p, __i); i = (int8_t)__i; } while (0) +#define LESTREAM_TO_INT16(p, i) \ + do {uint16_t __i; LESTREAM_TO_UINT16(p, __i); i = (int16_t)__i; } while (0) +#define LESTREAM_TO_INT32(p, i) \ + do {uint32_t __i; LESTREAM_TO_UINT32(p, __i); i = (int32_t)__i; } while (0) + +#define BLE_BASE_UUID_OCTET_OFFSET 12 +#define BLE_UUID16_TO_UUID128(uuid, base) \ + do { \ + uint16_t uuid16 = uuid.uuid16; \ + memcpy(uuid.uuid128, base.uuid128, sizeof(uuid.uuid128)); \ + uint8_t *p = &uuid.uuid128[BLE_BASE_UUID_OCTET_OFFSET]; \ + UINT16_TO_LESTREAM(p, uuid16); \ + uuid.type = BT_UUID128; \ + } while(0) + + +typedef void (*ble_client_connect_event_cb_t)(struct bt_conn *conn, uint8_t err, void *param); +typedef void (*ble_client_disconnect_event_cb_t)(struct bt_conn *conn, uint8_t reason, void *param); +typedef void (*ble_client_update_param_event_cb_t)(struct bt_conn *conn, + uint16_t interval, + uint16_t latency, + uint16_t timeout, + void *param); + + +#ifdef __cplusplus +extern "C" { +#endif + +void ble_client_init(ble_client_connect_event_cb_t connect_cb, void* connect_param, + ble_client_disconnect_event_cb_t disconnect_cb, void* disconnect_param, + ble_client_update_param_event_cb_t update_param_cb, void* update_param_param); +void ble_client_get_factory_config(bt_addr_le_t *bda, char *name); +void ble_gap_set_tx_power(int8_t tx_power); +BLE_STATUS_T errorno_to_ble_status(int err); + +void ble_client_get_mac_address(bt_addr_le_t *bda); + +#ifdef __cplusplus +} +#endif + + +#endif // _BLE_CLIENT_H_INCLUDED diff --git a/libraries/CurieBLE/src/BLECharacteristic.h b/libraries/CurieBLE/src/BLECharacteristic.h index 58274f67..c80edd08 100644 --- a/libraries/CurieBLE/src/BLECharacteristic.h +++ b/libraries/CurieBLE/src/BLECharacteristic.h @@ -329,7 +329,7 @@ class BLECharacteristic : public BLEAttribute { bt_gatt_attr_t *_attr_cccd; // For central device to subscribe the Notification/Indication - bt_gatt_subscribe_params_t _sub_params; + bt_gatt_subscribe_params_t _sub_params; bool _reading; bt_gatt_read_params_t _read_params; diff --git a/libraries/CurieBLE/src/BLEPeripheral.h b/libraries/CurieBLE/src/BLEPeripheral.h index 33b8bef7..25c53395 100644 --- a/libraries/CurieBLE/src/BLEPeripheral.h +++ b/libraries/CurieBLE/src/BLEPeripheral.h @@ -92,7 +92,7 @@ class BLEPeripheral{ /** * @brief Set advertising interval * - * @param[in] advertisingInterval Advertising Interval (N * 0.625) + * @param[in] advertisingInterval Advertising Interval in ms * * @return none * diff --git a/libraries/CurieBLE/src/BLEProfile.cpp b/libraries/CurieBLE/src/BLEProfile.cpp index 10f0c91e..d333132f 100644 --- a/libraries/CurieBLE/src/BLEProfile.cpp +++ b/libraries/CurieBLE/src/BLEProfile.cpp @@ -146,7 +146,7 @@ uint8_t profile_discover_process(bt_conn_t *conn, return peripheral->discover(attr); } -// Only for central +// Only for GATT Client uint8_t profile_read_rsp_process(bt_conn_t *conn, int err, bt_gatt_read_params_t *params, const void *data, diff --git a/system/libarc32_arduino101/bootcode/init.S b/system/libarc32_arduino101/bootcode/init.S index 28aab7d6..b566b488 100644 --- a/system/libarc32_arduino101/bootcode/init.S +++ b/system/libarc32_arduino101/bootcode/init.S @@ -61,9 +61,10 @@ _do_reset: .balign 4 _do_fault: _exit_halt: - /* Set halt flag */ - flag 0x01 + /* Set halt flag + flag 0x0 */ nop + j @_Fault nop nop /* loop forever */ diff --git a/system/libarc32_arduino101/drivers/ipc_uart_ns16550.c b/system/libarc32_arduino101/drivers/ipc_uart_ns16550.c index b2f6c08f..9b9880a6 100644 --- a/system/libarc32_arduino101/drivers/ipc_uart_ns16550.c +++ b/system/libarc32_arduino101/drivers/ipc_uart_ns16550.c @@ -155,7 +155,7 @@ static void ipc_uart_push_frame(uint16_t len, uint8_t *p_data) // "len %d, src %d, channel %d", ipc.rx_hdr.len, len, // ipc.rx_hdr.src_cpu_id, // ipc.rx_hdr.channel); - pr_debug(LOG_MODULE_IPC,"data[0 - 1]: %x-%x", p_data[0], p_data[1]); + //pr_debug(LOG_MODULE_IPC,"data[0 - 1]: %x-%x", p_data[0], p_data[1]); if ((ipc.rx_hdr.channel < IPC_UART_MAX_CHANNEL) && (ipc.channels[ipc.rx_hdr.channel].cb != NULL)) { @@ -208,7 +208,7 @@ void ipc_uart_isr() if (ipc.rx_size == 0) { if (ipc.rx_state == STATUS_RX_HDR) { - pr_error(0, "%s-%d", __FUNCTION__, ipc.rx_hdr.len); + //pr_error(0, "%s-%d", __FUNCTION__, ipc.rx_hdr.len); ipc.rx_ptr = balloc( ipc.rx_hdr.len, NULL); @@ -368,7 +368,7 @@ int ipc_uart_ns16550_send_pdu(void *handle, int len, void *p_data) { struct ipc_uart_channels *chan = (struct ipc_uart_channels *)handle; - pr_debug(LOG_MODULE_IPC, "%s: %d", __FUNCTION__, ipc.tx_state); + //pr_debug(LOG_MODULE_IPC, "%s: %d", __FUNCTION__, ipc.tx_state); if (ipc.tx_state == STATUS_TX_BUSY) { return IPC_UART_TX_BUSY; diff --git a/system/libarc32_arduino101/framework/src/cfw/service_api.c b/system/libarc32_arduino101/framework/src/cfw/service_api.c index 28b5bfe5..183167b6 100644 --- a/system/libarc32_arduino101/framework/src/cfw/service_api.c +++ b/system/libarc32_arduino101/framework/src/cfw/service_api.c @@ -111,6 +111,11 @@ struct cfw_message * cfw_alloc_evt_msg(service_t *svc, int msg_id, int size) { struct cfw_message * cfw_alloc_internal_msg(int msg_id, int size, void * priv) { struct cfw_message * evt = (struct cfw_message *) cfw_alloc_message(size, NULL); + if (NULL == evt) + { + return NULL; + } + CFW_MESSAGE_TYPE(evt) = TYPE_INT; CFW_MESSAGE_ID(evt) = msg_id; CFW_MESSAGE_LEN(evt) = size; diff --git a/system/libarc32_arduino101/framework/src/infra/port.c b/system/libarc32_arduino101/framework/src/infra/port.c index e9f7eef6..b719d1d6 100644 --- a/system/libarc32_arduino101/framework/src/infra/port.c +++ b/system/libarc32_arduino101/framework/src/infra/port.c @@ -121,6 +121,7 @@ static struct port * get_port(uint16_t port_id) if (port_id == 0 || port_id > MAX_PORTS) { pr_error(LOG_MODULE_MAIN, "Invalid port: %d", port_id); panic(-1); /*TODO: replace with an assert */ + return NULL; } return &ports[port_id - 1]; } @@ -317,7 +318,7 @@ uint16_t queue_process_message(T_QUEUE queue) uint16_t id = 0; queue_get_message(queue, &m, OS_NO_WAIT, &err); message = (struct message *) m; - if ( message != NULL && err == E_OS_OK) { + if ( message != NULL) { // && err == E_OS_OK dismiss Klock scan issue id = MESSAGE_ID(message); port_process_message(message); } diff --git a/system/libarc32_arduino101/framework/src/os/panic.c b/system/libarc32_arduino101/framework/src/os/panic.c index 9fe88ef9..214d5bb5 100644 --- a/system/libarc32_arduino101/framework/src/os/panic.c +++ b/system/libarc32_arduino101/framework/src/os/panic.c @@ -1,6 +1,9 @@ #include "os/os.h" +#include "infra/log.h" +#include "aux_regs.h" + extern void _do_fault(); void panic(int x) { @@ -14,3 +17,16 @@ void __assert_fail() } +void __attribute__((weak)) _Fault(void) +{ + uint32_t exc_addr = aux_reg_read(ARC_V2_EFA); + uint32_t ecr = aux_reg_read(ARC_V2_ECR); + + pr_error(0, "Exception vector: 0x%x, cause code: 0x%x, parameter 0x%x\n", + ARC_V2_ECR_VECTOR(ecr), + ARC_V2_ECR_CODE(ecr), + ARC_V2_ECR_PARAMETER(ecr)); + pr_error(0, "Address 0x%x\n", exc_addr); + while (1); +} + diff --git a/system/libarc32_arduino101/framework/src/services/ble/gatt.c b/system/libarc32_arduino101/framework/src/services/ble/gatt.c index d3ef4c09..44c82e5c 100644 --- a/system/libarc32_arduino101/framework/src/services/ble/gatt.c +++ b/system/libarc32_arduino101/framework/src/services/ble/gatt.c @@ -1040,6 +1040,7 @@ void on_nble_gattc_discover_rsp(const struct nble_gattc_discover_rsp *rsp, struct bt_gatt_attr *attr = NULL; if (rsp->type == BT_GATT_DISCOVER_PRIMARY) { + //BT_DBG("%s-%d", __FUNCTION__, __LINE__); const struct nble_gattc_primary *gattr = (void *)&data[i * sizeof(*gattr)]; if ((gattr->range.start_handle < params->start_handle) && @@ -1052,7 +1053,7 @@ void on_nble_gattc_discover_rsp(const struct nble_gattc_discover_rsp *rsp, goto complete; } svc_value.end_handle = gattr->range.end_handle; - svc_value.uuid = params->uuid; + svc_value.uuid = (struct bt_uuid*)(&(gattr->uuid));//params->uuid; attr = (&(struct bt_gatt_attr)BT_GATT_PRIMARY_SERVICE(&svc_value)); attr->handle = gattr->handle; last_handle = svc_value.end_handle; diff --git a/system/libarc32_arduino101/framework/src/services/ble_service/ble_service.c b/system/libarc32_arduino101/framework/src/services/ble_service/ble_service.c index 405c984a..d6efdf63 100644 --- a/system/libarc32_arduino101/framework/src/services/ble_service/ble_service.c +++ b/system/libarc32_arduino101/framework/src/services/ble_service/ble_service.c @@ -114,8 +114,11 @@ static void handle_msg_id_ble_rpc_callin(struct message *msg, void *priv) struct ble_rpc_callin *rpc = container_of(msg, struct ble_rpc_callin, msg); /* handle incoming message */ rpc_deserialize(rpc->p_data, rpc->len); + //pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); bfree(rpc->p_data); + //pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); message_free(msg); + //pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); } static void ble_set_bda_cb(int status, void *user_data) diff --git a/variants/arduino_101/linker_scripts/flash.ld b/variants/arduino_101/linker_scripts/flash.ld index e75a7059..fca2693c 100644 --- a/variants/arduino_101/linker_scripts/flash.ld +++ b/variants/arduino_101/linker_scripts/flash.ld @@ -47,12 +47,12 @@ MEMORY /* Define default stack size and FIRQ stack size. * See below stack section for __stack_start and __firq_stack_start */ -__stack_size = 2048; -__firq_stack_size = 512; +__stack_size = 3072; +__firq_stack_size = 1024; -/* Minimum heap size to allocate +/* Minimum heap size to allocate 8192 * Actual heap size might be bigger due to page size alignment */ -__HEAP_SIZE_MIN = 8192; +__HEAP_SIZE_MIN = 9216; /* This should be set to the page size used by the malloc implementation */ __PAGE_SIZE = 4096; From 539af2e79689df255fdc5cbe72d32ef037b27f25 Mon Sep 17 00:00:00 2001 From: lianggao Date: Tue, 18 Oct 2016 08:36:36 +0800 Subject: [PATCH 2/4] Add BLE library.properties file --- libraries/BLE/library.properties | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 libraries/BLE/library.properties diff --git a/libraries/BLE/library.properties b/libraries/BLE/library.properties new file mode 100644 index 00000000..ecdb692d --- /dev/null +++ b/libraries/BLE/library.properties @@ -0,0 +1,9 @@ +name=BLE +version=0.0 +author=Lianggao +maintainer=Lianggao +sentence=Library to manage the Bluetooth Low Energy module with Curie Core boards. +paragraph=Using this library, it is possible to use BLE features to communicate and interact with other devices like smartphones and tablets. This library enables multiple types of functionalities through a number of different classes. +category=Communication +url= +architectures=arc32 From 473c1c91e26f34e369e053ae20cd02eb14018bed Mon Sep 17 00:00:00 2001 From: lianggao Date: Fri, 21 Oct 2016 22:22:24 +0800 Subject: [PATCH 3/4] Implement central feature 1. Add the auto discover 2. Resolve the connect request was not scheduled issue 3. Implement the read/write request --- libraries/BLE/src/BLECallbacks.cpp | 17 +- libraries/BLE/src/BLECallbacks.h | 6 + libraries/BLE/src/BLECharacteristic.cpp | 731 +++++++++--------- libraries/BLE/src/BLECharacteristicImp.cpp | 319 ++++++-- libraries/BLE/src/BLECharacteristicImp.h | 34 +- libraries/BLE/src/BLEDescriptorImp.cpp | 15 +- libraries/BLE/src/BLEDescriptorImp.h | 4 + libraries/BLE/src/BLEDevice.cpp | 16 +- libraries/BLE/src/BLEDevice.h | 2 +- libraries/BLE/src/BLEDeviceManager.cpp | 57 +- libraries/BLE/src/BLEDeviceManager.h | 2 + libraries/BLE/src/BLEProfileManager.cpp | 272 +++++-- libraries/BLE/src/BLEProfileManager.h | 19 +- libraries/BLE/src/BLEService.cpp | 7 + libraries/BLE/src/BLEService.h | 8 + libraries/BLE/src/BLEServiceImp.cpp | 125 ++- libraries/BLE/src/BLEServiceImp.h | 7 +- libraries/BLE/src/BLEUtils.cpp | 338 ++++---- libraries/BLE/src/BLEUtils.h | 6 +- .../framework/src/services/ble/uuid.c | 2 +- .../src/services/ble_service/ble_service.c | 3 +- 21 files changed, 1303 insertions(+), 687 deletions(-) diff --git a/libraries/BLE/src/BLECallbacks.cpp b/libraries/BLE/src/BLECallbacks.cpp index db298071..bd606b10 100644 --- a/libraries/BLE/src/BLECallbacks.cpp +++ b/libraries/BLE/src/BLECallbacks.cpp @@ -111,8 +111,11 @@ uint8_t profile_discover_process(bt_conn_t *conn, const bt_gatt_attr_t *attr, bt_gatt_discover_params_t *params) { + uint8_t ret = BT_GATT_ITER_STOP; pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); - return BLEProfileManager::instance()->discoverResponseProc(conn, attr, params); + ret = BLEProfileManager::instance()->discoverResponseProc(conn, attr, params); + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + return ret; } // GATT Client only @@ -133,9 +136,21 @@ uint8_t profile_read_rsp_process(bt_conn_t *conn, chrc = BLEProfileManager::instance()->characteristic(bleDevice, params->single.handle); chrc->setValue((const unsigned char *)data, length); + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); return BT_GATT_ITER_STOP; } +uint8_t profile_service_read_rsp_process(bt_conn_t *conn, + int err, + bt_gatt_read_params_t *params, + const void *data, + uint16_t length) +{ + uint8_t ret = BLEProfileManager::instance()->serviceReadRspProc(conn, err, params, data, length); + pr_debug(LOG_MODULE_BLE, "%s-%d:ret-%d", __FUNCTION__, __LINE__, ret); + return ret; +} + void bleConnectEventHandler(bt_conn_t *conn, diff --git a/libraries/BLE/src/BLECallbacks.h b/libraries/BLE/src/BLECallbacks.h index 76144392..868fc3a6 100644 --- a/libraries/BLE/src/BLECallbacks.h +++ b/libraries/BLE/src/BLECallbacks.h @@ -49,5 +49,11 @@ void ble_central_device_found(const bt_addr_le_t *addr, const uint8_t *ad, uint8_t len); +uint8_t profile_service_read_rsp_process(bt_conn_t *conn, + int err, + bt_gatt_read_params_t *params, + const void *data, + uint16_t length); + #endif diff --git a/libraries/BLE/src/BLECharacteristic.cpp b/libraries/BLE/src/BLECharacteristic.cpp index 88033529..071089fc 100644 --- a/libraries/BLE/src/BLECharacteristic.cpp +++ b/libraries/BLE/src/BLECharacteristic.cpp @@ -1,365 +1,366 @@ - -//#include "internal/ble_client.h" - -#include "BLECharacteristic.h" -#include "BLEProfileManager.h" -#include "BLEUtils.h" - -#include "BLECharacteristicImp.h" - -BLECharacteristic::BLECharacteristic(): - _bledev(), _internal(NULL), _properties(0), - _value_size(0), _value(NULL) -{ - memset(_uuid_cstr, 0, sizeof(_uuid_cstr)); -} - -BLECharacteristic::BLECharacteristic(const char* uuid, - unsigned char properties, - unsigned char valueSize): - _bledev(), _internal(NULL), _properties(properties), _value(NULL) -{ - bt_uuid_128 bt_uuid_tmp; - _value_size = valueSize > BLE_MAX_ATTR_LONGDATA_LEN ? BLE_MAX_ATTR_LONGDATA_LEN : valueSize; - BLEUtils::uuidString2BT(uuid, (bt_uuid_t *)&bt_uuid_tmp); - BLEUtils::uuidBT2String((const bt_uuid_t *)&bt_uuid_tmp, _uuid_cstr); - _bledev.setAddress(*BLEUtils::bleGetLoalAddress()); -} - -BLECharacteristic::BLECharacteristic(const char* uuid, - unsigned char properties, - const char* value): - BLECharacteristic(uuid, properties, strlen(value)) -{ - _setValue((const uint8_t*)value, strlen(value)); -} - -BLECharacteristic::BLECharacteristic(BLECharacteristicImp *characteristicImp, - const BLEDevice *bleDev): - _bledev(bleDev), _internal(characteristicImp), - _value(NULL) -{ - _properties = characteristicImp->properties(); - _value_size = characteristicImp->valueSize(); -} - -BLECharacteristic::~BLECharacteristic() -{ - if (_value) - { - bfree(_value); - _value = NULL; - } -} - -const char* BLECharacteristic::uuid() const -{ - return _uuid_cstr; -} - -unsigned char BLECharacteristic::properties() -{ - unsigned char property = 0; - BLECharacteristicImp *characteristicImp = getImplementation(); - if (NULL != characteristicImp) - { - property = characteristicImp->properties(); - } - return property; -} - -int BLECharacteristic::valueSize() //const -{ - int valuesize = 0; - BLECharacteristicImp *characteristicImp = getImplementation(); - if (NULL != characteristicImp) - { - valuesize = characteristicImp->valueSize(); - } - return valuesize; -} - -const byte* BLECharacteristic::value() //const -{ - const byte* value_temp = NULL; - BLECharacteristicImp *characteristicImp = getImplementation(); - if (NULL != characteristicImp) - { - value_temp = characteristicImp->value(); - } - return value_temp; -} - -int BLECharacteristic::valueLength() //const -{ - int valueLength = 0; - BLECharacteristicImp *characteristicImp = getImplementation(); - if (NULL != characteristicImp) - { - valueLength = characteristicImp->valueLength(); - } - return valueLength; -} - -BLECharacteristic::operator bool() const -{ - return (strlen(_uuid_cstr) > 3); -} - - -byte BLECharacteristic::operator[] (int offset) //const -{ - byte data = 0; - BLECharacteristicImp *characteristicImp = getImplementation(); - - if (NULL != characteristicImp) - { - data = (*characteristicImp)[offset]; - } - return data; -} - -bool BLECharacteristic::writeValue(const byte value[], int length) -{ - bool retVar = false; - BLECharacteristicImp *characteristicImp = getImplementation(); - - if (NULL != characteristicImp) - { - characteristicImp->setValue((const unsigned char *)value, (uint16_t)length); - retVar = true; - } - return retVar; -} - -bool BLECharacteristic::writeValue(const byte value[], int length, int offset) -{ - // TODO: Not support it now. - // Will add this feature. - return false; -} - -bool BLECharacteristic::writeValue(const char* value) -{ - return writeValue((const byte*)value, strlen(value)); -} - -bool BLECharacteristic::broadcast() -{ - // TODO: Need more information - return false; -} - -bool BLECharacteristic::written() -{ - bool retVar = false; - BLECharacteristicImp *characteristicImp = getImplementation(); - - if (NULL != characteristicImp) - { - retVar = characteristicImp->written(); - } - return retVar; -} - -bool BLECharacteristic::subscribed() -{ - bool retVar = false; - BLECharacteristicImp *characteristicImp = getImplementation(); - - if (NULL != characteristicImp) - { - retVar = characteristicImp->subscribed(); - } - return retVar; -} - -bool BLECharacteristic::canNotify() -{ - bool retVar = false; - BLECharacteristicImp *characteristicImp = getImplementation(); - - if (NULL != characteristicImp) - { - retVar = characteristicImp->canNotify(); - } - return retVar; -} - -bool BLECharacteristic::canIndicate() -{ - // TODO: Need more confirmation - return false; -} - -bool BLECharacteristic::canRead() -{ - // TODO: Need more confirmation - return false; -} -bool BLECharacteristic::canWrite() -{ - // TODO: Need more confirmation - return false; -} -bool BLECharacteristic::canSubscribe() -{ - // TODO: Need more confirmation - return false; -} -bool BLECharacteristic::canUnsubscribe() -{ - // TODO: Need more confirmation - return false; -} - -bool BLECharacteristic::read() -{ - bool retVar = false; - BLECharacteristicImp *characteristicImp = getImplementation(); - - if (NULL != characteristicImp) - { - retVar = characteristicImp->read(); - } - return retVar; -} - -bool BLECharacteristic::write(const unsigned char* value, int length) -{ - bool retVar = false; - BLECharacteristicImp *characteristicImp = getImplementation(); - - if (NULL != characteristicImp) - { - retVar = characteristicImp->write(value, (uint16_t)length); - } - return retVar; -} - -bool BLECharacteristic::subscribe() -{ - bool retVar = false; - BLECharacteristicImp *characteristicImp = getImplementation(); - - if (NULL != characteristicImp) - { - // TODO: Central feature. Peripheral first - //retVar = characteristicImp->s(); - } - return retVar; -} - -bool BLECharacteristic::unsubscribe() -{ - bool retVar = false; - BLECharacteristicImp *characteristicImp = getImplementation(); - - if (NULL != characteristicImp) - { - // TODO: Central feature - //retVar = characteristicImp->canNotify(); - } - return retVar; -} - -bool BLECharacteristic::valueUpdated() -{ - bool retVar = false; - BLECharacteristicImp *characteristicImp = getImplementation(); - - if (NULL != characteristicImp) - { - retVar = characteristicImp->valueUpdated(); - } - return retVar; -} - -int BLECharacteristic::addDescriptor(BLEDescriptor& descriptor) -{ - bool retVar = false; - BLECharacteristicImp *characteristicImp = getImplementation(); - - if (NULL != characteristicImp) - { - retVar = characteristicImp->addDescriptor(descriptor); - } - return retVar; -} - -int BLECharacteristic::descriptorCount() //const -{ - int count = 0; - BLECharacteristicImp *characteristicImp = getImplementation(); - - if (NULL != characteristicImp) - { - count = characteristicImp->descriptorCount(); - } - return count; -} - -bool BLECharacteristic::hasDescriptor(const char* uuid) const -{ - // TODO: Not support now - return false; -} -bool BLECharacteristic::hasDescriptor(const char* uuid, int index) const -{ - // TODO: Not support now - return false; -} -BLEDescriptor BLECharacteristic::descriptor(int index) const -{ - // TODO: Not support now - return BLEDescriptor(); -} -BLEDescriptor BLECharacteristic::descriptor(const char * uuid) const -{ - // TODO: Not support now - return BLEDescriptor(); -} -BLEDescriptor BLECharacteristic::descriptor(const char * uuid, int index) const -{ - // TODO: Not support now - return BLEDescriptor(); -} -void BLECharacteristic::setEventHandler(BLECharacteristicEvent event, - BLECharacteristicEventHandler eventHandler) -{} - - -void -BLECharacteristic::_setValue(const uint8_t value[], uint16_t length) -{ - if (length > _value_size) { - length = _value_size; - } - - if (NULL == _value) - { - // Allocate the buffer for characteristic - _value = (unsigned char*)balloc(_value_size, NULL);//malloc(_value_size) - } - if (NULL == _value) - { - return; - } - memcpy(_value, value, length); -} - -BLECharacteristicImp* BLECharacteristic::getImplementation() -{ - if (NULL == _internal) - { - _internal = BLEProfileManager::instance()->characteristic(_bledev, (const char*)_uuid_cstr); - } - return _internal; -} - -void BLECharacteristic::setBLECharacteristicImp(BLECharacteristicImp *characteristicImp) -{ - _internal = characteristicImp; -} - - + +//#include "internal/ble_client.h" + +#include "BLECharacteristic.h" +#include "BLEProfileManager.h" +#include "BLEUtils.h" + +#include "BLECharacteristicImp.h" + +BLECharacteristic::BLECharacteristic(): + _bledev(), _internal(NULL), _properties(0), + _value_size(0), _value(NULL) +{ + memset(_uuid_cstr, 0, sizeof(_uuid_cstr)); +} + +BLECharacteristic::BLECharacteristic(const char* uuid, + unsigned char properties, + unsigned char valueSize): + _bledev(), _internal(NULL), _properties(properties), _value(NULL) +{ + bt_uuid_128 bt_uuid_tmp; + _value_size = valueSize > BLE_MAX_ATTR_LONGDATA_LEN ? BLE_MAX_ATTR_LONGDATA_LEN : valueSize; + BLEUtils::uuidString2BT(uuid, (bt_uuid_t *)&bt_uuid_tmp); + BLEUtils::uuidBT2String((const bt_uuid_t *)&bt_uuid_tmp, _uuid_cstr); + _bledev.setAddress(*BLEUtils::bleGetLoalAddress()); +} + +BLECharacteristic::BLECharacteristic(const char* uuid, + unsigned char properties, + const char* value): + BLECharacteristic(uuid, properties, strlen(value)) +{ + _setValue((const uint8_t*)value, strlen(value)); +} + +BLECharacteristic::BLECharacteristic(BLECharacteristicImp *characteristicImp, + const BLEDevice *bleDev): + _bledev(bleDev), _internal(characteristicImp), + _value(NULL) +{ + BLEUtils::uuidBT2String(characteristicImp->bt_uuid(), _uuid_cstr); + _properties = characteristicImp->properties(); + _value_size = characteristicImp->valueSize(); +} + +BLECharacteristic::~BLECharacteristic() +{ + if (_value) + { + bfree(_value); + _value = NULL; + } +} + +const char* BLECharacteristic::uuid() const +{ + return _uuid_cstr; +} + +unsigned char BLECharacteristic::properties() +{ + unsigned char property = 0; + BLECharacteristicImp *characteristicImp = getImplementation(); + if (NULL != characteristicImp) + { + property = characteristicImp->properties(); + } + return property; +} + +int BLECharacteristic::valueSize() //const +{ + int valuesize = 0; + BLECharacteristicImp *characteristicImp = getImplementation(); + if (NULL != characteristicImp) + { + valuesize = characteristicImp->valueSize(); + } + return valuesize; +} + +const byte* BLECharacteristic::value() //const +{ + const byte* value_temp = NULL; + BLECharacteristicImp *characteristicImp = getImplementation(); + if (NULL != characteristicImp) + { + value_temp = characteristicImp->value(); + } + return value_temp; +} + +int BLECharacteristic::valueLength() //const +{ + int valueLength = 0; + BLECharacteristicImp *characteristicImp = getImplementation(); + if (NULL != characteristicImp) + { + valueLength = characteristicImp->valueLength(); + } + return valueLength; +} + +BLECharacteristic::operator bool() const +{ + return (strlen(_uuid_cstr) > 3); +} + + +byte BLECharacteristic::operator[] (int offset) //const +{ + byte data = 0; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + data = (*characteristicImp)[offset]; + } + return data; +} + +bool BLECharacteristic::writeValue(const byte value[], int length) +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + characteristicImp->setValue((const unsigned char *)value, (uint16_t)length); + retVar = true; + } + return retVar; +} + +bool BLECharacteristic::writeValue(const byte value[], int length, int offset) +{ + // TODO: Not support it now. + // Will add this feature. + return false; +} + +bool BLECharacteristic::writeValue(const char* value) +{ + return writeValue((const byte*)value, strlen(value)); +} + +bool BLECharacteristic::broadcast() +{ + // TODO: Need more information + return false; +} + +bool BLECharacteristic::written() +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->written(); + } + return retVar; +} + +bool BLECharacteristic::subscribed() +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->subscribed(); + } + return retVar; +} + +bool BLECharacteristic::canNotify() +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->canNotify(); + } + return retVar; +} + +bool BLECharacteristic::canIndicate() +{ + // TODO: Need more confirmation + return false; +} + +bool BLECharacteristic::canRead() +{ + // TODO: Need more confirmation + return false; +} +bool BLECharacteristic::canWrite() +{ + // TODO: Need more confirmation + return false; +} +bool BLECharacteristic::canSubscribe() +{ + // TODO: Need more confirmation + return false; +} +bool BLECharacteristic::canUnsubscribe() +{ + // TODO: Need more confirmation + return false; +} + +bool BLECharacteristic::read() +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->read(); + } + return retVar; +} + +bool BLECharacteristic::write(const unsigned char* value, int length) +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->write(value, (uint16_t)length); + } + return retVar; +} + +bool BLECharacteristic::subscribe() +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + // TODO: Central feature. Peripheral first + //retVar = characteristicImp->s(); + } + return retVar; +} + +bool BLECharacteristic::unsubscribe() +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + // TODO: Central feature + //retVar = characteristicImp->canNotify(); + } + return retVar; +} + +bool BLECharacteristic::valueUpdated() +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->valueUpdated(); + } + return retVar; +} + +int BLECharacteristic::addDescriptor(BLEDescriptor& descriptor) +{ + bool retVar = false; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + retVar = characteristicImp->addDescriptor(descriptor); + } + return retVar; +} + +int BLECharacteristic::descriptorCount() //const +{ + int count = 0; + BLECharacteristicImp *characteristicImp = getImplementation(); + + if (NULL != characteristicImp) + { + count = characteristicImp->descriptorCount(); + } + return count; +} + +bool BLECharacteristic::hasDescriptor(const char* uuid) const +{ + // TODO: Not support now + return false; +} +bool BLECharacteristic::hasDescriptor(const char* uuid, int index) const +{ + // TODO: Not support now + return false; +} +BLEDescriptor BLECharacteristic::descriptor(int index) const +{ + // TODO: Not support now + return BLEDescriptor(); +} +BLEDescriptor BLECharacteristic::descriptor(const char * uuid) const +{ + // TODO: Not support now + return BLEDescriptor(); +} +BLEDescriptor BLECharacteristic::descriptor(const char * uuid, int index) const +{ + // TODO: Not support now + return BLEDescriptor(); +} +void BLECharacteristic::setEventHandler(BLECharacteristicEvent event, + BLECharacteristicEventHandler eventHandler) +{} + + +void +BLECharacteristic::_setValue(const uint8_t value[], uint16_t length) +{ + if (length > _value_size) { + length = _value_size; + } + + if (NULL == _value) + { + // Allocate the buffer for characteristic + _value = (unsigned char*)balloc(_value_size, NULL);//malloc(_value_size) + } + if (NULL == _value) + { + return; + } + memcpy(_value, value, length); +} + +BLECharacteristicImp* BLECharacteristic::getImplementation() +{ + if (NULL == _internal) + { + _internal = BLEProfileManager::instance()->characteristic(_bledev, (const char*)_uuid_cstr); + } + return _internal; +} + +void BLECharacteristic::setBLECharacteristicImp(BLECharacteristicImp *characteristicImp) +{ + _internal = characteristicImp; +} + + diff --git a/libraries/BLE/src/BLECharacteristicImp.cpp b/libraries/BLE/src/BLECharacteristicImp.cpp index 34758015..fee461f4 100644 --- a/libraries/BLE/src/BLECharacteristicImp.cpp +++ b/libraries/BLE/src/BLECharacteristicImp.cpp @@ -26,13 +26,78 @@ bt_uuid_16_t BLECharacteristicImp::_gatt_chrc_uuid = {BT_UUID_TYPE_16, BT_UUID_GATT_CHRC_VAL}; bt_uuid_16_t BLECharacteristicImp::_gatt_ccc_uuid = {BT_UUID_TYPE_16, BT_UUID_GATT_CCC_VAL}; +BLECharacteristicImp::BLECharacteristicImp(const bt_uuid_t* uuid, + unsigned char properties, + uint16_t handle, + const BLEDevice& bledevice): + BLEAttribute(uuid, BLETypeCharacteristic), + _value_length(0), + _value_buffer(NULL), + _value_updated(false), + _value_handle(handle), + _cccd_handle(0), + _attr_chrc_value(NULL), + _attr_cccd(NULL), + _ble_device() +{ + _value_size = BLE_MAX_ATTR_DATA_LEN;// Set as MAX value. TODO: long read/write need to twist + _value = (unsigned char*)balloc(_value_size, NULL); + if (_value_size > BLE_MAX_ATTR_DATA_LEN) + { + _value_buffer = (unsigned char*)balloc(_value_size, NULL); + } + + memset(_value, 0, _value_size); + + memset(&_ccc_cfg, 0, sizeof(_ccc_cfg)); + memset(&_ccc_value, 0, sizeof(_ccc_value)); + memset(&_gatt_chrc, 0, sizeof(_gatt_chrc)); + memset(&_sub_params, 0, sizeof(_sub_params)); + memset(&_discover_params, 0, sizeof(_discover_params)); + + _ccc_value.cfg = &_ccc_cfg; + _ccc_value.cfg_len = 1; + if (BLERead & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_READ; + } + if (BLEWrite & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_WRITE; + } + if (BLEWriteWithoutResponse & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_WRITE_WITHOUT_RESP; + } + if (BLENotify & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_NOTIFY; + _sub_params.value |= BT_GATT_CCC_NOTIFY; + } + if (BLEIndicate & properties) + { + _gatt_chrc.properties |= BT_GATT_CHRC_INDICATE; + _sub_params.value |= BT_GATT_CCC_INDICATE; + } + _gatt_chrc.uuid = (bt_uuid_t*)this->bt_uuid();//&_characteristic_uuid;//this->uuid(); + memset(_event_handlers, 0, sizeof(_event_handlers)); + + _sub_params.notify = profile_notify_process; + + // Update BLE device object + _ble_device.setAddress(*bledevice.bt_le_address()); + + memset(&_descriptors_header, 0, sizeof(_descriptors_header)); +} + BLECharacteristicImp::BLECharacteristicImp(BLECharacteristic& characteristic, const BLEDevice& bledevice): BLEAttribute(characteristic.uuid(), BLETypeCharacteristic), _value_length(0), _value_buffer(NULL), _value_updated(false), - _attr_chrc_declaration(NULL), + _value_handle(0), + _cccd_handle(0), _attr_chrc_value(NULL), _attr_cccd(NULL), _ble_device() @@ -49,6 +114,7 @@ BLECharacteristicImp::BLECharacteristicImp(BLECharacteristic& characteristic, memset(&_ccc_value, 0, sizeof(_ccc_value)); memset(&_gatt_chrc, 0, sizeof(_gatt_chrc)); memset(&_sub_params, 0, sizeof(_sub_params)); + memset(&_discover_params, 0, sizeof(_discover_params)); _ccc_value.cfg = &_ccc_cfg; _ccc_value.cfg_len = 1; @@ -217,33 +283,43 @@ BLECharacteristicImp::setEventHandler(BLECharacteristicEvent event, BLECharacter interrupts(); } +void +BLECharacteristicImp::setHandle(uint16_t handle) +{ + // GATT client + _value_handle = handle; +} + +void +BLECharacteristicImp::setCCCDHandle(uint16_t handle) +{ + // GATT client + _cccd_handle = handle; +} + uint16_t BLECharacteristicImp::valueHandle() { uint16_t handle = 0; if (NULL != _attr_chrc_value) { + //GATT server handle = _attr_chrc_value->handle; } - - return handle; -} - -uint16_t -BLECharacteristicImp::cccdHandle() -{ - uint16_t handle = 0; - if (NULL != _attr_cccd) + else { - handle = _attr_cccd->handle; + // GATT client + handle = _value_handle; } + return handle; } void BLECharacteristicImp::_setValue(const uint8_t value[], uint16_t length) { - if (length > _value_size) { + if (length > _value_size) + { length = _value_size; } @@ -286,58 +362,6 @@ bt_uuid_t* BLECharacteristicImp::getClientCharacteristicConfigUuid(void) return (bt_uuid_t*) &_gatt_ccc_uuid; } -#if 0 -void BLECharacteristicImp::discover(bt_gatt_discover_params_t *params) -{ - params->type = BT_GATT_DISCOVER_CHARACTERISTIC; - params->uuid = this->uuid(); - // Start discovering - _discoverying = true; - // Re-Init the read/write parameter - _reading = false; -} - - -void BLECharacteristicImp::discover(const bt_gatt_attr_t *attr, - bt_gatt_discover_params_t *params) -{ - if (!attr) - { - // Discovery complete - _discoverying = false; - return; - } - - // Chracteristic Char - if (params->uuid == this->uuid()) - { - // Set Discover CCCD parameter - params->start_handle = attr->handle + 2; - if (subscribed()) - { - // Include CCCD - params->type = BT_GATT_DISCOVER_DESCRIPTOR; - params->uuid = this->getClientCharacteristicConfigUuid(); - } - else - { - // Complete the discover - _discoverying = false; - } - } - else if (params->uuid == this->getClientCharacteristicConfigUuid()) - { - params->start_handle = attr->handle + 1; - _discoverying = false; - } -} -#endif - -bt_gatt_subscribe_params_t *BLECharacteristicImp::getSubscribeParams() -{ - return &_sub_params; -} - bool BLECharacteristicImp::read() { int retval = 0; @@ -513,7 +537,13 @@ int BLECharacteristicImp::updateProfile(bt_gatt_attr_t *attr_start, int& index) int BLECharacteristicImp::addDescriptor(BLEDescriptor& descriptor) { - BLEDescriptorImp* descriptorImp = new BLEDescriptorImp(_ble_device, descriptor); + BLEDescriptorImp* descriptorImp = descrptor(descriptor.uuid()); + if (NULL != descriptorImp) + { + return BLE_STATUS_SUCCESS; + } + + descriptorImp = new BLEDescriptorImp(_ble_device, descriptor); pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); if (NULL == descriptorImp) { @@ -532,6 +562,61 @@ int BLECharacteristicImp::addDescriptor(BLEDescriptor& descriptor) return BLE_STATUS_SUCCESS; } +int BLECharacteristicImp::addDescriptor(const bt_uuid_t* uuid, + uint16_t handle) +{ + BLEDescriptorImp* descriptorImp = descrptor(uuid); + if (NULL != descriptorImp) + { + return BLE_STATUS_SUCCESS; + } + + descriptorImp = new BLEDescriptorImp(uuid, handle, _ble_device); + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + if (NULL == descriptorImp) + { + return BLE_STATUS_NO_MEMORY; + } + + BLEDescriptorNodePtr node = link_node_create(descriptorImp); + if (NULL == node) + { + delete[] descriptorImp; + return BLE_STATUS_NO_MEMORY; + } + link_node_insert_last(&_descriptors_header, node); + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + return BLE_STATUS_SUCCESS; +} + +BLEDescriptorImp* BLECharacteristicImp::descrptor(const bt_uuid_t* uuid) +{ + BLEDescriptorImp* descriptorImp = NULL; + BLEDescriptorNodePtr node = link_node_get_first(&_descriptors_header); + + while (NULL != node) + { + descriptorImp = node->value; + if (true == descriptorImp->compareUuid(uuid)) + { + break; + } + } + + if (NULL == node) + { + descriptorImp = NULL; + } + return descriptorImp; +} + +BLEDescriptorImp* BLECharacteristicImp::descrptor(const char* uuid) +{ + bt_uuid_128_t uuid_tmp; + BLEUtils::uuidString2BT(uuid, (bt_uuid_t *)&uuid_tmp); + return descrptor((const bt_uuid_t *)&uuid_tmp); +} + void BLECharacteristicImp::releaseDescriptors() { BLEDescriptorNodePtr node = link_node_get_first(&_descriptors_header); @@ -557,4 +642,108 @@ int BLECharacteristicImp::descriptorCount() const return counter; } +bool BLECharacteristicImp::discoverAttributes(BLEDevice* device) +{ + + int err; + bt_conn_t* conn; + bt_gatt_discover_params_t* temp = NULL; + const bt_uuid_t* service_uuid = bt_uuid(); + + if (service_uuid->type == BT_UUID_TYPE_16) + { + uint16_t uuid_tmp = ((bt_uuid_16_t*)service_uuid)->val; + if (BT_UUID_GAP_VAL == uuid_tmp || + BT_UUID_GATT_VAL == uuid_tmp) + { + return false; + } + } + + conn = bt_conn_lookup_addr_le(device->bt_le_address()); + if (NULL == conn) + { + // Link lost + pr_debug(LOG_MODULE_BLE, "Can't find connection\n"); + return false; + } + temp = &_discover_params; + temp->start_handle = _value_handle + 1; + temp->end_handle = _value_handle + 20; // TODO: the max descriptor is not more than 20 + temp->uuid = NULL; + temp->type = BT_GATT_DISCOVER_DESCRIPTOR; + temp->func = profile_discover_process; + pr_debug(LOG_MODULE_BLE, "%s-%d-charc",__FUNCTION__, __LINE__); + err = bt_gatt_discover(conn, temp); + bt_conn_unref(conn); + if (err) + { + pr_debug(LOG_MODULE_BLE, "Discover failed(err %d)\n", err); + return false; + } + return true; +} + +bool BLECharacteristicImp::isClientCharacteristicConfigurationDescriptor(const bt_uuid_t* uuid) +{ + bool ret = false; + uint16_t cccd_uuid = BT_UUID_GATT_CCC_VAL; + if (uuid->type == BT_UUID_TYPE_16) + { + if (0 == memcmp(&BT_UUID_16(uuid)->val, &cccd_uuid, sizeof(uint16_t))) + { + ret = true; + } + } + return ret; +} + +uint8_t BLECharacteristicImp::discoverResponseProc(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params) +{ + const bt_addr_le_t* dst_addr = bt_conn_get_dst(conn); + BLEDevice device(dst_addr); + uint8_t retVal = BT_GATT_ITER_STOP; + + pr_debug(LOG_MODULE_BLE, "%s-%d: type-%d", __FUNCTION__, __LINE__, params->type); + + // Process the service + switch (params->type) + { + case BT_GATT_DISCOVER_DESCRIPTOR: + { + if (NULL != attr) + { + const bt_uuid_t* desc_uuid = attr->uuid; + uint16_t desc_handle = attr->handle; + pr_debug(LOG_MODULE_BLE, "%s-%d:handle-%d:%d", __FUNCTION__, __LINE__,attr->handle, desc_handle); + if (isClientCharacteristicConfigurationDescriptor(desc_uuid)) + { + setCCCDHandle(desc_handle); + } + else + { + int retval = (int)addDescriptor(desc_uuid, + desc_handle); + + if (BLE_STATUS_SUCCESS != retval) + { + pr_error(LOG_MODULE_BLE, "%s-%d: Error-%d", + __FUNCTION__, __LINE__, retval); + } + + } + retVal = BT_GATT_ITER_CONTINUE; + } + break; + } + default: + { + break; + } + } + return retVal; +} + diff --git a/libraries/BLE/src/BLECharacteristicImp.h b/libraries/BLE/src/BLECharacteristicImp.h index be95fe59..c2ab8f22 100644 --- a/libraries/BLE/src/BLECharacteristicImp.h +++ b/libraries/BLE/src/BLECharacteristicImp.h @@ -51,6 +51,8 @@ class BLECharacteristicImp: public BLEAttribute{ * @note none */ int addDescriptor(BLEDescriptor& descriptor); + int addDescriptor(const bt_uuid_t* uuid, + uint16_t handle); void releaseDescriptors(); @@ -160,7 +162,16 @@ class BLECharacteristicImp: public BLEAttribute{ bool write(const unsigned char value[], uint16_t length); + void setCCCDHandle(uint16_t handle); + void setHandle(uint16_t handle); int descriptorCount() const; + uint8_t discoverResponseProc(bt_conn_t *conn, + const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params); + bool discoverAttributes(BLEDevice* device); + + BLEDescriptorImp* descrptor(const bt_uuid_t* uuid); + BLEDescriptorImp* descrptor(const char* uuid); protected: friend class BLEProfileManager; @@ -172,6 +183,10 @@ class BLECharacteristicImp: public BLEAttribute{ * @param[in] bledevice The device that has this characteristic */ BLECharacteristicImp(BLECharacteristic& characteristic, const BLEDevice& bledevice); + BLECharacteristicImp(const bt_uuid_t* uuid, + unsigned char properties, + uint16_t handle, + const BLEDevice& bledevice); friend int profile_longflush_process(struct bt_conn *conn, const struct bt_gatt_attr *attr, @@ -215,13 +230,9 @@ class BLECharacteristicImp: public BLEAttribute{ * @note Only for peripheral */ uint16_t cccdHandle(void); - - void setUserDescription(BLEDescriptor *descriptor); - void setPresentationFormat(BLEDescriptor *descriptor); - - _bt_gatt_ccc_t* getCccCfg(void); - bt_gatt_chrc_t* getCharacteristicAttValue(void); + inline _bt_gatt_ccc_t* getCccCfg(void); + inline bt_gatt_chrc_t* getCharacteristicAttValue(void); static bt_uuid_t* getCharacteristicAttributeUuid(void); static bt_uuid_t* getClientCharacteristicConfigUuid(void); @@ -274,6 +285,7 @@ class BLECharacteristicImp: public BLEAttribute{ private: void _setValue(const uint8_t value[], uint16_t length); + bool isClientCharacteristicConfigurationDescriptor(const bt_uuid_t* uuid); private: // Those 2 UUIDs are used for define the characteristic. @@ -286,14 +298,16 @@ class BLECharacteristicImp: public BLEAttribute{ unsigned char* _value_buffer; bool _value_updated; - uint16_t _value_handle; + uint16_t _value_handle; // GATT client only + uint16_t _cccd_handle; // GATT client only + bt_gatt_discover_params_t _discover_params;// GATT client only + bt_gatt_ccc_cfg_t _ccc_cfg; _bt_gatt_ccc_t _ccc_value; bt_gatt_chrc_t _gatt_chrc; - bt_gatt_attr_t *_attr_chrc_declaration; - bt_gatt_attr_t *_attr_chrc_value; - bt_gatt_attr_t *_attr_cccd; + bt_gatt_attr_t *_attr_chrc_value; // GATT server only + bt_gatt_attr_t *_attr_cccd; // GATT server only // For GATT Client to subscribe the Notification/Indication bt_gatt_subscribe_params_t _sub_params; diff --git a/libraries/BLE/src/BLEDescriptorImp.cpp b/libraries/BLE/src/BLEDescriptorImp.cpp index 9cdef95c..b0e673b4 100644 --- a/libraries/BLE/src/BLEDescriptorImp.cpp +++ b/libraries/BLE/src/BLEDescriptorImp.cpp @@ -25,7 +25,8 @@ #include "BLECallbacks.h" BLEDescriptorImp::BLEDescriptorImp(BLEDevice& bledevice, BLEDescriptor &descriptor): - BLEAttribute(descriptor.uuid(), BLETypeDescriptor) + BLEAttribute(descriptor.uuid(), BLETypeDescriptor), + _value_handle(0) { _value_length = descriptor.valueLength(); _value = (unsigned char*)balloc(_value_length, NULL); @@ -33,6 +34,18 @@ BLEDescriptorImp::BLEDescriptorImp(BLEDevice& bledevice, BLEDescriptor &descript memcpy(_value, descriptor.value(), _value_length); } +BLEDescriptorImp::BLEDescriptorImp(const bt_uuid_t* uuid, + uint16_t handle, + BLEDevice& bledevice): + BLEAttribute(uuid, BLETypeDescriptor), + _value_handle(handle) +{ + _value_length = BLE_MAX_ATTR_DATA_LEN; + _value = (unsigned char*)balloc(_value_length, NULL); + + memset(_value, 0, _value_length); +} + BLEDescriptorImp::~BLEDescriptorImp() { if (_value) { bfree(_value); diff --git a/libraries/BLE/src/BLEDescriptorImp.h b/libraries/BLE/src/BLEDescriptorImp.h index 5ea27caf..8d3a9f1c 100644 --- a/libraries/BLE/src/BLEDescriptorImp.h +++ b/libraries/BLE/src/BLEDescriptorImp.h @@ -35,6 +35,9 @@ class BLEDescriptorImp: public BLEAttribute{ * @param[in] valueLength Data length required for descriptor value (<= BLE_MAX_ATTR_DATA_LEN) */ BLEDescriptorImp(BLEDevice& bledevice, BLEDescriptor &descriptor); + BLEDescriptorImp(const bt_uuid_t* uuid, + uint16_t handle, + BLEDevice& bledevice); virtual ~BLEDescriptorImp(); @@ -61,6 +64,7 @@ class BLEDescriptorImp: public BLEAttribute{ private: unsigned short _value_length; + unsigned short _value_handle; unsigned char* _value; bt_uuid_128 _descriptor_uuid; diff --git a/libraries/BLE/src/BLEDevice.cpp b/libraries/BLE/src/BLEDevice.cpp index ece51365..65c28eae 100644 --- a/libraries/BLE/src/BLEDevice.cpp +++ b/libraries/BLE/src/BLEDevice.cpp @@ -186,14 +186,14 @@ BLEDevice::operator bool() const return BLEUtils::macAddressValid(_bt_addr); } -BLEDevice& BLEDevice::operator=(const BLEDevice& device) -{ - if (*this != device) - { - memcpy(&(this->_bt_addr), &(device._bt_addr), sizeof (bt_addr_le_t)); - } - return *this; -} +//BLEDevice& BLEDevice::operator=(const BLEDevice& device) +//{ +// if (*this != device) +// { +// memcpy(&(this->_bt_addr), &(device._bt_addr), sizeof (bt_addr_le_t)); +// } +// return *this; +//} bool BLEDevice::operator==(const BLEDevice& device) const { diff --git a/libraries/BLE/src/BLEDevice.h b/libraries/BLE/src/BLEDevice.h index 8beebf64..4d6b8933 100644 --- a/libraries/BLE/src/BLEDevice.h +++ b/libraries/BLE/src/BLEDevice.h @@ -344,7 +344,7 @@ class BLEDevice operator bool() const; bool operator==(const BLEDevice& device) const; bool operator!=(const BLEDevice& device) const; - BLEDevice& operator=(const BLEDevice& device); + //BLEDevice& operator=(const BLEDevice& device); // central mode void startScanning(String name); // start scanning for peripherals void startScanningWithDuplicates(); // start scanning for peripherals, and report all duplicates diff --git a/libraries/BLE/src/BLEDeviceManager.cpp b/libraries/BLE/src/BLEDeviceManager.cpp index 72db8c45..bdeb4b49 100644 --- a/libraries/BLE/src/BLEDeviceManager.cpp +++ b/libraries/BLE/src/BLEDeviceManager.cpp @@ -43,6 +43,7 @@ BLEDeviceManager::BLEDeviceManager(): _local_ble(NULL) { memset(&_local_bda, 0, sizeof(_local_bda)); + memset(&_wait_for_connect_peripheral, 0, sizeof(_wait_for_connect_peripheral)); memset(&_service_uuid, 0, sizeof(_service_uuid)); memset(&_service_solicit_uuid, 0, sizeof(_service_solicit_uuid)); @@ -106,17 +107,18 @@ void BLEDeviceManager::end() bool BLEDeviceManager::connected(BLEDevice *device) { bt_conn_t* conn = bt_conn_lookup_addr_le(device->bt_le_address()); + bool retval = false; //pr_debug(LOG_MODULE_BLE, "%s-%d: add-%s", __FUNCTION__, __LINE__, device->address().c_str()); if (NULL != conn) { //pr_debug(LOG_MODULE_BLE, "%s-%d: state-%d", __FUNCTION__, __LINE__,conn->state); if (conn->state == BT_CONN_CONNECTED) { - return true; + retval = true; } bt_conn_unref(conn); } - return false; + return retval; } bool BLEDeviceManager::disconnect(BLEDevice *device) @@ -495,6 +497,33 @@ int BLEDeviceManager::rssi() const } bool BLEDeviceManager::connect(BLEDevice &device) +{ + // + uint64_t timestamp = millis(); + uint64_t timestampcur = timestamp; + bool ret = true; + bt_addr_le_copy(&_wait_for_connect_peripheral, device.bt_le_address()); + startScanning(); + + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + // Wait for the connection + while (ret && (true == BLEUtils::macAddressValid(_wait_for_connect_peripheral))) + { + timestampcur = millis(); + // TODO: dismiss the magic number + ret = (timestampcur - timestamp < 3000); // Time out + } + + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + + if (ret == false) + { + memset(&_wait_for_connect_peripheral, 0, sizeof(_wait_for_connect_peripheral)); + } + return ret; +} + +bool BLEDeviceManager::connectToDevice(BLEDevice &device) { bt_addr_le_t* temp = NULL; bt_addr_le_t* unused = NULL; @@ -572,6 +601,7 @@ void BLEDeviceManager::handleConnectEvent(bt_conn_t *conn, uint8_t err) } else { + memset(&_wait_for_connect_peripheral, 0, sizeof(_wait_for_connect_peripheral)); // Peripheral has established the connection with this Central device BLEProfileManager::instance()->handleConnectedEvent(bt_conn_get_dst(conn)); } @@ -693,7 +723,7 @@ BLEDevice BLEDeviceManager::available() if (true == BLEUtils::macAddressValid(*temp)) { tempdevice.setAddress(*temp); - //pr_debug(LOG_MODULE_BLE, "%s-%d:Con addr-%s", __FUNCTION__, __LINE__, BLEUtils::macAddressBT2String(*temp).c_str()); + pr_debug(LOG_MODULE_BLE, "%s-%d:Con addr-%s", __FUNCTION__, __LINE__, BLEUtils::macAddressBT2String(*temp).c_str()); _peer_adv_mill[index] -= 2000; // Set it as expired } } @@ -775,17 +805,24 @@ void BLEDeviceManager::handleDeviceFound(const bt_addr_le_t *addr, if (true == advertiseDataProc(data[1], &data[2], len - 1)) { - // The critical is accepted - // Find the oldest and expired buffer - if(false == setAdvertiseBuffer(addr)) + if (true == BLEUtils::macAddressValid(_wait_for_connect_peripheral)) { - pr_info(LOG_MODULE_BLE, "No buffer to store the ADV\n"); + // Not add to the buffer when try to establish the connection + if (true == BLEUtils::macAddressSame(*addr, _wait_for_connect_peripheral)) + { + BLEDevice testdev(addr); + stopScanning(); + connectToDevice(testdev); + } } else { - BLEDevice testdev(addr); - stopScanning(); - connect(testdev); + // The critical is accepted + // Find the oldest and expired buffer + if(false == setAdvertiseBuffer(addr)) + { + pr_info(LOG_MODULE_BLE, "No buffer to store the ADV\n"); + } } pr_debug(LOG_MODULE_BLE, "%s-%d: Done", __FUNCTION__, __LINE__); return; diff --git a/libraries/BLE/src/BLEDeviceManager.h b/libraries/BLE/src/BLEDeviceManager.h index cbb597ed..d095a7b2 100644 --- a/libraries/BLE/src/BLEDeviceManager.h +++ b/libraries/BLE/src/BLEDeviceManager.h @@ -340,6 +340,7 @@ class BLEDeviceManager int rssi() const; // returns the RSSI of the peripheral at discovery bool connect(BLEDevice &device); // connect to the peripheral + bool connectToDevice(BLEDevice &device); String deviceName(); // read the device name attribute of the peripheral, and return String value int appearance(); // read the appearance attribute of the peripheral and return value as int @@ -380,6 +381,7 @@ class BLEDeviceManager bt_data_t _adv_accept_critical; // The filters for central device String _adv_critical_local_name; bt_uuid_128_t _dv_critical_service_uuid; + bt_addr_le_t _wait_for_connect_peripheral; // For peripheral struct bt_le_adv_param _adv_param; diff --git a/libraries/BLE/src/BLEProfileManager.cpp b/libraries/BLE/src/BLEProfileManager.cpp index da9ebc96..9eb441ef 100644 --- a/libraries/BLE/src/BLEProfileManager.cpp +++ b/libraries/BLE/src/BLEProfileManager.cpp @@ -40,6 +40,9 @@ BLEProfileManager* BLEProfileManager::instance() } BLEProfileManager::BLEProfileManager (): + _start_discover(false), + _discovering(false), + _discover_timestamp(0), _cur_discover_service(NULL), _attr_base(NULL), _attr_index(0), @@ -47,12 +50,17 @@ BLEProfileManager::BLEProfileManager (): _sub_param_idx(0), _profile_registered(false) { - memset(_service_header_array, 0, sizeof(_service_header_array)); + //memset(_service_header_array, 0, sizeof(_service_header_array)); memset(_discover_params, 0, sizeof(_discover_params)); memset(_addresses, 0, sizeof(_addresses)); - + memset(&_read_params, 0, sizeof(_read_params)); bt_addr_le_copy(&_addresses[BLE_MAX_CONN_CFG], BLEUtils::bleGetLoalAddress()); + for (int i = 0; i <= BLE_MAX_CONN_CFG; i++) + { + _service_header_array[i].next = NULL; + _service_header_array[i].value = NULL; + } pr_debug(LOG_MODULE_BLE, "%s-%d: Construct", __FUNCTION__, __LINE__); } @@ -80,8 +88,13 @@ BLEProfileManager::addService (BLEDevice &bledevice, BLEService& service) serviceheader = &_service_header_array[index]; bt_addr_le_copy(&_addresses[index], bledevice.bt_le_address()); } - - BLEServiceImp *serviceImp = new BLEServiceImp(service); + BLEServiceImp *serviceImp = this->service(bledevice, service.uuid()); + if (NULL != serviceImp) + { + // The service alreay exist + return BLE_STATUS_SUCCESS; + } + serviceImp = new BLEServiceImp(service); if (NULL == serviceImp) { return BLE_STATUS_NO_MEMORY; @@ -99,31 +112,8 @@ BLEProfileManager::addService (BLEDevice &bledevice, BLEService& service) BLE_STATUS_T BLEProfileManager::addService (BLEDevice &bledevice, const bt_uuid_t* uuid) { - BLEServiceLinkNodeHeader* serviceheader = getServiceHeader(bledevice); - if (NULL == serviceheader) - { - int index = getUnusedIndex(); - if (index >= BLE_MAX_CONN_CFG) - { - return BLE_STATUS_NO_MEMORY; - } - serviceheader = &_service_header_array[index]; - bt_addr_le_copy(&_addresses[index], bledevice.bt_le_address()); - } - - BLEServiceImp *serviceImp = new BLEServiceImp(uuid); - if (NULL == serviceImp) - { - return BLE_STATUS_NO_MEMORY; - } - BLEServiceNodePtr node = link_node_create(serviceImp); - if (NULL == node) - { - delete[] serviceImp; - return BLE_STATUS_NO_MEMORY; - } - link_node_insert_last(serviceheader, node); - return BLE_STATUS_SUCCESS; + BLEService svc_obj(uuid); + return addService(bledevice, svc_obj); } BLEProfileManager::BLEServiceLinkNodeHeader* BLEProfileManager::getServiceHeader(const BLEDevice &bledevice) @@ -133,6 +123,7 @@ BLEProfileManager::BLEServiceLinkNodeHeader* BLEProfileManager::getServiceHeader for (i = 0; i <= BLE_MAX_CONN_CFG; i++) { if ((bt_addr_le_cmp(bledevice.bt_le_address(), &_addresses[i]) == 0)) + //if (true == BLEUtils::macAddressSame(*bledevice.bt_le_address(), _addresses[i])) { break; } @@ -151,6 +142,7 @@ const BLEProfileManager::BLEServiceLinkNodeHeader* BLEProfileManager::getService for (i = 0; i <= BLE_MAX_CONN_CFG; i++) { if ((bt_addr_le_cmp(bledevice.bt_le_address(), &_addresses[i]) == 0)) + //if (true == BLEUtils::macAddressSame(*bledevice.bt_le_address(), _addresses[i])) { break; } @@ -398,14 +390,15 @@ BLECharacteristicImp* BLEProfileManager::characteristic(const BLEDevice &bledevi BLEServiceImp* BLEProfileManager::service(const BLEDevice &bledevice, const char * uuid) const { bt_uuid_128_t uuid_tmp; - BLEUtils::uuidString2BT(uuid, (bt_uuid_t *)&uuid_tmp); + //pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); return service(bledevice, (const bt_uuid_t *)&uuid_tmp); } BLEServiceImp* BLEProfileManager::service(const BLEDevice &bledevice, const bt_uuid_t* uuid) const { BLEServiceImp* serviceImp = NULL; + #if 1 const BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); if (NULL == serviceHeader) { @@ -413,9 +406,12 @@ BLEServiceImp* BLEProfileManager::service(const BLEDevice &bledevice, const bt_u return NULL; } BLEServiceNodePtr node = serviceHeader->next; + + // Just for debug char uuid_tmp[37]; BLEUtils::uuidBT2String(uuid, uuid_tmp); pr_debug(LOG_MODULE_BLE, "%s-%d: %s", __FUNCTION__, __LINE__, uuid_tmp); + while (node != NULL) { serviceImp = node->value; @@ -425,10 +421,12 @@ BLEServiceImp* BLEProfileManager::service(const BLEDevice &bledevice, const bt_u } node = node->next; } + if (NULL == node) { serviceImp = NULL; } + #endif return serviceImp; } @@ -512,6 +510,12 @@ bool BLEProfileManager::discoverAttributes(BLEDevice* device) pr_debug(LOG_MODULE_BLE, "Discover failed(err %d)\n", err); return ret; } + // Block it + _start_discover = true; + while (_start_discover) + { + delay(10); + } return true; } @@ -533,6 +537,26 @@ int BLEProfileManager::getDeviceIndex(const BLEDevice* device) return getDeviceIndex(device->bt_le_address()); } +bool BLEProfileManager::discovering() +{ + bool ret = (millis() - _discover_timestamp) < 1500; + ret = (_discovering && ret); + if (_cur_discover_service != NULL) + { + ret = ret || _cur_discover_service->discovering(); + } + return ret; +} + +void BLEProfileManager::setDiscovering(bool discover) +{ + if (discover) + { + _discover_timestamp = millis(); + } + _discovering = discover; +} + uint8_t BLEProfileManager::discoverResponseProc(bt_conn_t *conn, const bt_gatt_attr_t *attr, bt_gatt_discover_params_t *params) @@ -542,17 +566,18 @@ uint8_t BLEProfileManager::discoverResponseProc(bt_conn_t *conn, BLEDevice device(dst_addr); uint8_t retVal = BT_GATT_ITER_STOP; - pr_debug(LOG_MODULE_BLE, "%s-%d: index-%d", __FUNCTION__, __LINE__, i); + //pr_debug(LOG_MODULE_BLE, "%s-%d: index-%d", __FUNCTION__, __LINE__, i); if (i >= BLE_MAX_CONN_CFG) { return BT_GATT_ITER_STOP; } - + // Process the service switch (params->type) { case BT_GATT_DISCOVER_CHARACTERISTIC: + case BT_GATT_DISCOVER_DESCRIPTOR: { if (NULL != _cur_discover_service) { @@ -562,42 +587,55 @@ uint8_t BLEProfileManager::discoverResponseProc(bt_conn_t *conn, } break; } - case BT_GATT_DISCOVER_DESCRIPTOR: - { - //descriptorDiscoverRsp(attr, attribute_tmp); - break; - } case BT_GATT_DISCOVER_PRIMARY: { if (NULL != attr) { struct bt_gatt_service *svc_value = (struct bt_gatt_service *)attr->user_data; - int retval = (int)addService(device, svc_value->uuid); - BLEServiceImp* service_tmp = service(device, svc_value->uuid); - if (NULL != service_tmp) + const bt_uuid_t* svc_uuid = svc_value->uuid; + + setDiscovering(false); + // TODO: Pontential bugs + if (svc_uuid->type == BT_UUID_TYPE_16 && + ((const bt_uuid_16_t *)svc_uuid)->val == 0) { - pr_debug(LOG_MODULE_BLE, "%s-%d: handle-%d", - __FUNCTION__, __LINE__, attr->handle); - service_tmp->setHandle(attr->handle); + // Discover failed. The service may unknow type. + // Need read the value and discovery again. + readService(device, attr->handle); + } + else + { + int err_code = (int)addService(device, svc_value->uuid); + if (BLE_STATUS_SUCCESS == err_code) + { + BLEServiceImp* service_tmp = service(device, svc_value->uuid); + if (NULL != service_tmp) + { + service_tmp->setHandle(attr->handle); + service_tmp->setEndHandle(svc_value->end_handle); + } + } + else + { + pr_debug(LOG_MODULE_BLE, "%s-%d: Error-%d", + __FUNCTION__, __LINE__, err_code); + } } retVal = BT_GATT_ITER_CONTINUE; } else { // Service discover complete - serviceDiscoverComplete(device); retVal = BT_GATT_ITER_STOP; } - } - + } default: { - //attribute_tmp->discover(attr, &_discover_params); break; } } - if (retVal == BT_GATT_ITER_STOP) + if (retVal == BT_GATT_ITER_STOP && discovering() == false) { const BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(device); BLEServiceImp* serviceCurImp = NULL; @@ -631,6 +669,12 @@ uint8_t BLEProfileManager::discoverResponseProc(bt_conn_t *conn, node = node->next; } + if (NULL == node) + { + pr_debug(LOG_MODULE_BLE, "%s-%d: Discover completed", + __FUNCTION__, __LINE__); + _start_discover = false; + } } return retVal; } @@ -670,6 +714,138 @@ void BLEProfileManager::serviceDiscoverComplete(const BLEDevice &bledevice) return; } +void BLEProfileManager::readService(const BLEDevice &bledevice, uint16_t handle) +{ + int retval = 0; + bt_conn_t* conn = NULL; + + if (true == BLEUtils::isLocalBLE(bledevice)) + { + // GATT server can't write + return;// false; + } + + //if (_reading) + { + // Already in reading state + //return false; + } + + _read_params.func = profile_service_read_rsp_process; + _read_params.handle_count = 1; + _read_params.single.handle = handle; + _read_params.single.offset = 0; + + if (0 == _read_params.single.handle) + { + // Discover not complete + return ;//false; + } + + conn = bt_conn_lookup_addr_le(bledevice.bt_le_address()); + if (NULL == conn) + { + return ;//false; + } + setDiscovering(true); + // Send read request + retval = bt_gatt_read(conn, &_read_params); + bt_conn_unref(conn); + //if (0 == retval) + { + //_reading = true; + } + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + return ;//true; //_reading; +} + +bool BLEProfileManager::discoverService(BLEDevice* device, const bt_uuid_t* svc_uuid) +{ + int err = 0; + bt_conn_t* conn; + int i = getDeviceIndex(device); + bool ret = false; + bt_gatt_discover_params_t* temp = NULL; + + pr_debug(LOG_MODULE_BLE, "%s-%d: index-%d,fun-%p", __FUNCTION__, __LINE__, i,profile_discover_process); + + if (i >= BLE_MAX_CONN_CFG) + { + // The device already in the buffer. + // This function only be called after connection established. + return ret; + } + + BLEServiceImp* serviceImp = service(device, svc_uuid); + if (NULL == serviceImp) + { + return ret; + } + + conn = bt_conn_lookup_addr_le(device->bt_le_address()); + if (NULL == conn) + { + // Link lost + pr_debug(LOG_MODULE_BLE, "Can't find connection\n"); + return ret; + } + temp = &_discover_params[i]; + temp->start_handle = 1; + temp->end_handle = 0xFFFF; + temp->uuid = (bt_uuid_t*) serviceImp->bt_uuid(); + temp->type = BT_GATT_DISCOVER_PRIMARY; + temp->func = profile_discover_process; + + err = bt_gatt_discover(conn, temp); + bt_conn_unref(conn); + if (err) + { + pr_debug(LOG_MODULE_BLE, "Discover failed(err %d)\n", err); + return ret; + } + return true; +} + +uint8_t BLEProfileManager::serviceReadRspProc(bt_conn_t *conn, + int err, + bt_gatt_read_params_t *params, + const void *data, + uint16_t length) +{ + if (NULL == data) + { + return BT_GATT_ITER_STOP; + } + BLEDevice bleDevice(bt_conn_get_dst(conn)); + + pr_debug(LOG_MODULE_BLE, "%s-%d:length-%d", __FUNCTION__, __LINE__, length); + if (length == UUID_SIZE_128) + { + bt_uuid_128_t uuid_tmp; + uuid_tmp.uuid.type = BT_UUID_TYPE_128; + memcpy(uuid_tmp.val, data, UUID_SIZE_128); + /**/ + int retval = (int)BLEProfileManager::instance()->addService(bleDevice, (const bt_uuid_t *)&uuid_tmp); + if (BLE_STATUS_SUCCESS != retval) + { + pr_error(LOG_MODULE_BLE, "%s-%d: Error-%d", + __FUNCTION__, __LINE__, retval); + return BT_GATT_ITER_STOP; + } + + BLEServiceImp* serviceImp = service(&bleDevice, (const bt_uuid_t *)&uuid_tmp);//(BLEServiceImp*)testTTTTTTT();// + if (NULL == serviceImp) + { + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + return BT_GATT_ITER_STOP; + } + BLEProfileManager::instance()->discoverService(&bleDevice, (const bt_uuid_t *)&uuid_tmp); + } + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + + return BT_GATT_ITER_STOP; +} + #if 0 void BLEProfileManager::characteristicDiscoverRsp(const bt_gatt_attr_t *attr, BLEAttribute* bleattr) { diff --git a/libraries/BLE/src/BLEProfileManager.h b/libraries/BLE/src/BLEProfileManager.h index 70362c26..b203fedc 100644 --- a/libraries/BLE/src/BLEProfileManager.h +++ b/libraries/BLE/src/BLEProfileManager.h @@ -54,6 +54,7 @@ class BLEProfileManager{ * Or be called in discover process. */ BLE_STATUS_T addService (BLEDevice &bledevice, BLEService& service); + BLE_STATUS_T addService (BLEDevice &bledevice, const bt_uuid_t* uuid); /** * @brief Register the profile to Nordic BLE stack @@ -99,7 +100,13 @@ class BLEProfileManager{ bt_gatt_discover_params_t *params); bool discoverAttributes(BLEDevice* device); + bool discoverService(BLEDevice* device, const bt_uuid_t* svc_uuid); void handleConnectedEvent(const bt_addr_le_t* deviceAddr); + uint8_t serviceReadRspProc(bt_conn_t *conn, + int err, + bt_gatt_read_params_t *params, + const void *data, + uint16_t length); protected: friend ssize_t profile_write_process(bt_conn_t *conn, const bt_gatt_attr_t *attr, @@ -113,7 +120,6 @@ class BLEProfileManager{ BLEProfileManager(); ~BLEProfileManager (void); - BLE_STATUS_T addService (BLEDevice &bledevice, const bt_uuid_t* uuid); void serviceDiscoverComplete(const BLEDevice &bledevice); int getDeviceIndex(const bt_addr_le_t* macAddr); @@ -166,13 +172,22 @@ class BLEProfileManager{ */ void clearProfile(BLEDevice &bledevice); + void readService(const BLEDevice &bledevice, uint16_t handle); + bool discovering(); + void setDiscovering(bool discover); + private: // The last header is for local BLE BLEServiceLinkNodeHeader _service_header_array[BLE_MAX_CONN_CFG + 1]; // The connected devices' service and self service bt_addr_le_t _addresses[BLE_MAX_CONN_CFG + 1]; // The BLE devices' address - + + bool _start_discover; + + bool _discovering; + uint64_t _discover_timestamp; bt_gatt_discover_params_t _discover_params[BLE_MAX_CONN_CFG]; BLEServiceImp* _cur_discover_service; + bt_gatt_read_params_t _read_params; bt_gatt_attr_t *_attr_base; // Allocate the memory for BLE stack int _attr_index; diff --git a/libraries/BLE/src/BLEService.cpp b/libraries/BLE/src/BLEService.cpp index 2c2e80c5..b979511d 100644 --- a/libraries/BLE/src/BLEService.cpp +++ b/libraries/BLE/src/BLEService.cpp @@ -38,6 +38,13 @@ BLEService::BLEService(const char* uuid):_bledevice(),_service_imp(NULL) _bledevice.setAddress(*BLEUtils::bleGetLoalAddress()); } +BLEService::BLEService(const bt_uuid_t* uuid):_bledevice(),_service_imp(NULL) +{ + memset(_uuid_cstr, 0, sizeof (_uuid_cstr)); + BLEUtils::uuidBT2String(uuid, _uuid_cstr); + _bledevice.setAddress(*BLEUtils::bleGetLoalAddress()); +} + BLEService::BLEService(BLEServiceImp* serviceImp, const BLEDevice* bledev): _bledevice(bledev),_service_imp(serviceImp) { diff --git a/libraries/BLE/src/BLEService.h b/libraries/BLE/src/BLEService.h index de4973dd..538ea1d7 100644 --- a/libraries/BLE/src/BLEService.h +++ b/libraries/BLE/src/BLEService.h @@ -122,7 +122,15 @@ class BLEService protected: friend class BLEDevice; friend class BLEServiceImp; + friend class BLEProfileManager; + friend uint8_t profile_service_read_rsp_process(bt_conn_t *conn, + int err, + bt_gatt_read_params_t *params, + const void *data, + uint16_t length); + BLEService(BLEServiceImp* serviceImp, const BLEDevice* bledev); + BLEService(const bt_uuid_t* uuid); void setServiceImp(BLEServiceImp* serviceImp); private: BLEServiceImp* getServiceImp(); diff --git a/libraries/BLE/src/BLEServiceImp.cpp b/libraries/BLE/src/BLEServiceImp.cpp index 15628c15..5c0b4371 100644 --- a/libraries/BLE/src/BLEServiceImp.cpp +++ b/libraries/BLE/src/BLEServiceImp.cpp @@ -34,7 +34,8 @@ bt_uuid_t *BLEServiceImp::getPrimayUuid(void) BLEServiceImp::BLEServiceImp(BLEService& service): BLEAttribute(service.uuid(), BLETypeService), _start_handle(0), - _end_handle(0xFFFF) + _end_handle(0xFFFF), + _cur_discover_chrc(NULL) { memset(&_characteristics_header, 0, sizeof(_characteristics_header)); service.setServiceImp(this); @@ -43,7 +44,8 @@ BLEServiceImp::BLEServiceImp(BLEService& service): BLEServiceImp::BLEServiceImp(const bt_uuid_t* uuid): BLEAttribute(uuid, BLETypeService), _start_handle(0), - _end_handle(0xFFFF) + _end_handle(0xFFFF), + _cur_discover_chrc(NULL) { memset(&_characteristics_header, 0, sizeof(_characteristics_header)); } @@ -56,8 +58,46 @@ BLEServiceImp::~BLEServiceImp() int BLEServiceImp::addCharacteristic(BLEDevice& bledevice, BLECharacteristic& characteristic) { - BLECharacteristicImp* characteristicImp = new BLECharacteristicImp(characteristic, bledevice); + BLECharacteristicImp* characteristicImp = this->characteristic(characteristic.uuid()); + if (NULL != characteristicImp) + { + pr_info(LOG_MODULE_BLE, "%s-%d: Already exist",__FUNCTION__, __LINE__); + return BLE_STATUS_SUCCESS; + } + characteristicImp = new BLECharacteristicImp(characteristic, bledevice); + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + if (NULL == characteristicImp) + { + return BLE_STATUS_NO_MEMORY; + } + + BLECharacteristicNodePtr node = link_node_create(characteristicImp); + if (NULL == node) + { + delete[] characteristicImp; + return BLE_STATUS_NO_MEMORY; + } + link_node_insert_last(&_characteristics_header, node); pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + return BLE_STATUS_SUCCESS; +} + +int BLEServiceImp::addCharacteristic(BLEDevice& bledevice, + const bt_uuid_t* uuid, + uint16_t handle, + unsigned char properties) +{ + BLECharacteristicImp* characteristicImp = this->characteristic(uuid); + if (NULL != characteristicImp) + { + pr_info(LOG_MODULE_BLE, "%s-%d: Already exist",__FUNCTION__, __LINE__); + return BLE_STATUS_SUCCESS; + } + pr_debug(LOG_MODULE_BLE, "%s-%d:handle-%d",__FUNCTION__, __LINE__,handle); + characteristicImp = new BLECharacteristicImp(uuid, + properties, + handle, + bledevice); if (NULL == characteristicImp) { return BLE_STATUS_NO_MEMORY; @@ -173,10 +213,15 @@ BLECharacteristicImp* BLEServiceImp::characteristic(uint16_t handle) return characteristicImp; } -BLECharacteristicImp* BLEServiceImp::characteristic(const char* uuid) +BLECharacteristicImp* BLEServiceImp::characteristic(const bt_uuid_t* uuid) { BLECharacteristicImp* characteristicImp = NULL; BLECharacteristicNodePtr node = link_node_get_first(&_characteristics_header); + // Just for debug + //char uuid_tmp[37]; + //BLEUtils::uuidBT2String(uuid, uuid_tmp); + //pr_debug(LOG_MODULE_BLE, "%s-%d: %s", __FUNCTION__, __LINE__, uuid_tmp); + while (NULL != node) { characteristicImp = node->value; @@ -184,6 +229,8 @@ BLECharacteristicImp* BLEServiceImp::characteristic(const char* uuid) { break; } + //BLEUtils::uuidBT2String(characteristicImp->bt_uuid(), uuid_tmp); + //pr_debug(LOG_MODULE_BLE, "%s-%d: %s", __FUNCTION__, __LINE__, uuid_tmp); node = node->next; } @@ -194,9 +241,21 @@ BLECharacteristicImp* BLEServiceImp::characteristic(const char* uuid) return characteristicImp; } +BLECharacteristicImp* BLEServiceImp::characteristic(const char* uuid) +{ + bt_uuid_128_t uuid_tmp; + BLEUtils::uuidString2BT(uuid, (bt_uuid_t *)&uuid_tmp); + return characteristic((const bt_uuid_t *)&uuid_tmp); +} + +bool BLEServiceImp::discovering() +{ + return (_cur_discover_chrc != NULL); +} + bool BLEServiceImp::discoverAttributes(BLEDevice* device) { - + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); int err; bt_conn_t* conn; bt_gatt_discover_params_t* temp = NULL; @@ -244,26 +303,43 @@ uint8_t BLEServiceImp::discoverResponseProc(bt_conn_t *conn, BLEDevice device(dst_addr); uint8_t retVal = BT_GATT_ITER_STOP; - pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); - if (NULL == attr) - { - return BT_GATT_ITER_STOP; - } - const bt_uuid_t* chrc_uuid = attr->uuid; - struct bt_gatt_chrc* psttemp = (struct bt_gatt_chrc*)attr->user_data; - char uuidbuffer[37]; - BLEUtils::uuidBT2String(chrc_uuid, uuidbuffer); - pr_debug(LOG_MODULE_BLE, "%s-%d: uuid type: %d\r\n%s", __FUNCTION__, __LINE__, chrc_uuid->type, uuidbuffer); - - BLEUtils::uuidBT2String(psttemp->uuid, uuidbuffer); - pr_debug(LOG_MODULE_BLE, "%s-%d:%p uuid type: %d properties: %d\r\n%s", __FUNCTION__, __LINE__,psttemp, psttemp->uuid->type, psttemp->properties, uuidbuffer); + //pr_debug(LOG_MODULE_BLE, "%s-%d: type-%d", __FUNCTION__, __LINE__, params->type); + // Process the service switch (params->type) { case BT_GATT_DISCOVER_CHARACTERISTIC: { - //characteristicDiscoverRsp(attr, attribute_tmp); - //send_discover = true; + if (NULL != attr) + { + //const bt_uuid_t* chrc_uuid = attr->uuid; + uint16_t chrc_handle = attr->handle + 1; + struct bt_gatt_chrc* psttemp = (struct bt_gatt_chrc*)attr->user_data; + int retval = (int)addCharacteristic(device, + psttemp->uuid, + chrc_handle, + psttemp->properties); + + //pr_debug(LOG_MODULE_BLE, "%s-%d:handle-%d:%d", __FUNCTION__, __LINE__,attr->handle, chrc_handle); + if (BLE_STATUS_SUCCESS != retval) + { + pr_error(LOG_MODULE_BLE, "%s-%d: Error-%d", + __FUNCTION__, __LINE__, retval); + } + //retVal = BT_GATT_ITER_CONTINUE; + } + break; + } + case BT_GATT_DISCOVER_DESCRIPTOR: + { + // + + if (NULL != _cur_discover_chrc) + { + retVal = _cur_discover_chrc->discoverResponseProc(conn, + attr, + params); + } break; } default: @@ -273,12 +349,14 @@ uint8_t BLEServiceImp::discoverResponseProc(bt_conn_t *conn, } } + pr_debug(LOG_MODULE_BLE, "%s-%d:ret-%d",__FUNCTION__, __LINE__, retVal); if (retVal == BT_GATT_ITER_STOP) { - const BLECharacteristicLinkNodeHeader* serviceHeader = &_characteristics_header; + const BLECharacteristicLinkNodeHeader* chrcHeader = &_characteristics_header; BLECharacteristicImp* chrcCurImp = NULL; - BLECharacteristicNodePtr node = serviceHeader->next; + BLECharacteristicNodePtr node = chrcHeader->next; + pr_debug(LOG_MODULE_BLE, "%s-%d: node-%p",__FUNCTION__, __LINE__, node); // Discover next service while (node != NULL) { @@ -286,7 +364,8 @@ uint8_t BLEServiceImp::discoverResponseProc(bt_conn_t *conn, if (NULL == _cur_discover_chrc) { - bool result = true;//serviceCurImp->discoverAttributes(&device); + bool result = chrcCurImp->discoverAttributes(&device); + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); if (result == true) { // Record the current discovering service diff --git a/libraries/BLE/src/BLEServiceImp.h b/libraries/BLE/src/BLEServiceImp.h index d9706edf..765abd62 100644 --- a/libraries/BLE/src/BLEServiceImp.h +++ b/libraries/BLE/src/BLEServiceImp.h @@ -52,9 +52,13 @@ class BLEServiceImp: public BLEAttribute{ * @note none */ int addCharacteristic(BLEDevice& bledevice, BLECharacteristic& characteristic); - + int addCharacteristic(BLEDevice& bledevice, + const bt_uuid_t* uuid, + uint16_t handle, + unsigned char properties); int getCharacteristicCount(); + BLECharacteristicImp* characteristic(const bt_uuid_t* uuid); BLECharacteristicImp* characteristic(const char* uuid); BLECharacteristicImp* characteristic(int index); BLECharacteristicImp* characteristic(uint16_t handle); @@ -67,6 +71,7 @@ class BLEServiceImp: public BLEAttribute{ uint8_t discoverResponseProc(bt_conn_t *conn, const bt_gatt_attr_t *attr, bt_gatt_discover_params_t *params); + bool discovering(); protected: friend class BLEProfileManager; diff --git a/libraries/BLE/src/BLEUtils.cpp b/libraries/BLE/src/BLEUtils.cpp index 461b5485..de2ba942 100644 --- a/libraries/BLE/src/BLEUtils.cpp +++ b/libraries/BLE/src/BLEUtils.cpp @@ -1,148 +1,190 @@ - -#include "ArduinoBLE.h" -#include "BLEUtils.h" -#include "internal/ble_client.h" - -String BLEUtils::macAddressBT2String(const bt_addr_le_t &bd_addr) -{ - char mac_string[BT_ADDR_STR_LEN]; - snprintf(mac_string, BT_ADDR_STR_LEN, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", - bd_addr.val[5], bd_addr.val[4], bd_addr.val[3], - bd_addr.val[2], bd_addr.val[1], bd_addr.val[0]); - String temp(mac_string); - return temp; -} - -void BLEUtils::macAddressString2BT(const char* mac_str, bt_addr_le_t &bd_addr) -{ - char temp[] = {0, 0, 0}; - int strLength = strlen(mac_str); - int length = 0; - - bd_addr.type = BT_ADDR_LE_PUBLIC; - - for (int i = strLength - 1; i >= 0 && length < MAX_UUID_SIZE; i -= 2) - { - if (mac_str[i] == ':') - { - i++; - continue; - } - - temp[0] = mac_str[i - 1]; - temp[1] = mac_str[i]; - - bd_addr.val[length] = strtoul(temp, NULL, 16); - - length++; - } - -} - -bool BLEUtils::macAddressValid(const bt_addr_le_t &bd_addr) -{ - static const bt_addr_le_t zero = {0,{0,0,0,0,0,0}}; - bool temp = (memcmp(bd_addr.val, zero.val, 6) != 0);//false;// - #if 0 - for (int i = 0; i < 6; i++) - { - if (bd_addr.val[i] != zero.val[i]) - { - -pr_info(LOG_MODULE_BLE, "%s-idx %d-%.2x:%.2x", __FUNCTION__, i ,bd_addr.val[i], zero.val[i]); -pr_info(LOG_MODULE_BLE,"%s",BLEUtils::macAddressBT2String(zero).c_str()); - temp = true; - break; - } - } - #endif - return temp; -} - - -bt_addr_le_t* BLEUtils::bleGetLoalAddress() -{ - static bt_addr_le_t board_addr; - if (false == macAddressValid(board_addr)) - ble_client_get_mac_address(&board_addr); - return &board_addr; -} - - - -void BLEUtils::uuidString2BT(const char* uuid, bt_uuid_t* pstuuid) -{ - char temp[] = {0, 0, 0}; - int strLength = strlen(uuid); - int length = 0; - bt_uuid_128_t uuid_tmp; - - memset (&uuid_tmp, 0x00, sizeof(uuid_tmp)); - - for (int i = strLength - 1; i >= 0 && length < MAX_UUID_SIZE; i -= 2) - { - if (uuid[i] == '-') - { - i++; - continue; - } - - temp[0] = uuid[i - 1]; - temp[1] = uuid[i]; - - uuid_tmp.val[length] = strtoul(temp, NULL, 16); - - length++; - } - - if (length == 2) - { - uint16_t temp = (uuid_tmp.val[1] << 8)| uuid_tmp.val[0]; - uuid_tmp.uuid.type = BT_UUID_TYPE_16; - ((bt_uuid_16_t*)(&uuid_tmp.uuid))->val = temp; - } - else - { - uuid_tmp.uuid.type = BT_UUID_TYPE_128; - } - memcpy(pstuuid, &uuid_tmp, sizeof (uuid_tmp)); -} - -void BLEUtils::uuidBT2String(const bt_uuid_t* pstuuid, char* uuid) -{ - unsigned int tmp1, tmp5; - uint16_t tmp0, tmp2, tmp3, tmp4; - // TODO: Change the magic number 37 - switch (pstuuid->type) { - case BT_UUID_TYPE_16: - memcpy(&tmp0, &BT_UUID_16(pstuuid)->val, sizeof(tmp0)); - snprintf(uuid, 37, "%.4x", tmp0); - break; - case BT_UUID_TYPE_128: - memcpy(&tmp0, &BT_UUID_128(pstuuid)->val[0], sizeof(tmp0)); - memcpy(&tmp1, &BT_UUID_128(pstuuid)->val[2], sizeof(tmp1)); - memcpy(&tmp2, &BT_UUID_128(pstuuid)->val[6], sizeof(tmp2)); - memcpy(&tmp3, &BT_UUID_128(pstuuid)->val[8], sizeof(tmp3)); - memcpy(&tmp4, &BT_UUID_128(pstuuid)->val[10], sizeof(tmp4)); - memcpy(&tmp5, &BT_UUID_128(pstuuid)->val[12], sizeof(tmp5)); - snprintf(uuid, 37, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x", - tmp5, tmp4, tmp3, tmp2, tmp1, tmp0); - break; - default: - memset(uuid, 0, 37); - return; - } -} - -BLEDevice& BLEUtils::getLoacalBleDevice() -{ - return BLE; -} - -bool BLEUtils::isLocalBLE(BLEDevice& device) -{ - return (device == BLE); -} - - - + +#include "ArduinoBLE.h" +#include "BLEUtils.h" +#include "internal/ble_client.h" + +String BLEUtils::macAddressBT2String(const bt_addr_le_t &bd_addr) +{ + char mac_string[BT_ADDR_STR_LEN]; + snprintf(mac_string, BT_ADDR_STR_LEN, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", + bd_addr.val[5], bd_addr.val[4], bd_addr.val[3], + bd_addr.val[2], bd_addr.val[1], bd_addr.val[0]); + String temp(mac_string); + return temp; +} + +void BLEUtils::macAddressString2BT(const char* mac_str, bt_addr_le_t &bd_addr) +{ + char temp[] = {0, 0, 0}; + int strLength = strlen(mac_str); + int length = 0; + + bd_addr.type = BT_ADDR_LE_PUBLIC; + + for (int i = strLength - 1; i >= 0 && length < MAX_UUID_SIZE; i -= 2) + { + if (mac_str[i] == ':') + { + i++; + continue; + } + + temp[0] = mac_str[i - 1]; + temp[1] = mac_str[i]; + + bd_addr.val[length] = strtoul(temp, NULL, 16); + + length++; + } + +} + +bool BLEUtils::macAddressSame(const bt_addr_le_t &bd_addr1, + const bt_addr_le_t &bd_addr2) +{ + bool temp = true;//(memcmp(bd_addr1.val, bd_addr2.val, 6) != 0);// + #if 1 + for (int i = 0; i < 6; i++) + { + if (bd_addr1.val[i] != bd_addr2.val[i]) + { + +pr_info(LOG_MODULE_BLE, "%s-idx %d-%.2x:%.2x", __FUNCTION__, i ,bd_addr1.val[i], bd_addr2.val[i]); +pr_info(LOG_MODULE_BLE,"%s",BLEUtils::macAddressBT2String(bd_addr1).c_str()); +pr_info(LOG_MODULE_BLE,"%s",BLEUtils::macAddressBT2String(bd_addr2).c_str()); + temp = false; + break; + } + } + #endif + return temp; + +} + +bool BLEUtils::macAddressValid(const bt_addr_le_t &bd_addr) +{ + static const bt_addr_le_t zero = {0,{0,0,0,0,0,0}}; + bool temp = (memcmp(bd_addr.val, zero.val, 6) != 0);//false;// + #if 0 + for (int i = 0; i < 6; i++) + { + if (bd_addr.val[i] != zero.val[i]) + { + +pr_info(LOG_MODULE_BLE, "%s-idx %d-%.2x:%.2x", __FUNCTION__, i ,bd_addr.val[i], zero.val[i]); +pr_info(LOG_MODULE_BLE,"%s",BLEUtils::macAddressBT2String(zero).c_str()); + temp = true; + break; + } + } + #endif + return temp; +} + + +bt_addr_le_t* BLEUtils::bleGetLoalAddress() +{ + static bt_addr_le_t board_addr; + if (false == macAddressValid(board_addr)) + ble_client_get_mac_address(&board_addr); + return &board_addr; +} + + + +void BLEUtils::uuidString2BT(const char* uuid, bt_uuid_t* pstuuid) +{ + char temp[] = {0, 0, 0}; + int strLength = strlen(uuid); + int length = 0; + bt_uuid_128_t uuid_tmp; + + memset (&uuid_tmp, 0x00, sizeof(uuid_tmp)); + + for (int i = strLength - 1; i >= 0 && length < MAX_UUID_SIZE; i -= 2) + { + if (uuid[i] == '-') + { + i++; + continue; + } + + temp[0] = uuid[i - 1]; + temp[1] = uuid[i]; + + uuid_tmp.val[length] = strtoul(temp, NULL, 16); + + length++; + } + + if (length == 2) + { + uint16_t temp = (uuid_tmp.val[1] << 8)| uuid_tmp.val[0]; + uint8_t* uuid16_val = (uint8_t*)&((bt_uuid_16_t*)(&uuid_tmp.uuid))->val; + uuid_tmp.uuid.type = BT_UUID_TYPE_16; + memcpy(uuid16_val, &temp, sizeof (uint16_t)); + } + else + { + uuid_tmp.uuid.type = BT_UUID_TYPE_128; + } + memcpy(pstuuid, &uuid_tmp, sizeof (uuid_tmp)); +} + +void BLEUtils::uuidBT2String(const bt_uuid_t* pstuuid, char* uuid) +{ + unsigned int tmp1, tmp5; + uint16_t tmp0, tmp2, tmp3, tmp4; + // TODO: Change the magic number 37 + switch (pstuuid->type) { + case BT_UUID_TYPE_16: + memcpy(&tmp0, &BT_UUID_16(pstuuid)->val, sizeof(tmp0)); + snprintf(uuid, 37, "%.4x", tmp0); + break; + case BT_UUID_TYPE_128: + memcpy(&tmp0, &BT_UUID_128(pstuuid)->val[0], sizeof(tmp0)); + memcpy(&tmp1, &BT_UUID_128(pstuuid)->val[2], sizeof(tmp1)); + memcpy(&tmp2, &BT_UUID_128(pstuuid)->val[6], sizeof(tmp2)); + memcpy(&tmp3, &BT_UUID_128(pstuuid)->val[8], sizeof(tmp3)); + memcpy(&tmp4, &BT_UUID_128(pstuuid)->val[10], sizeof(tmp4)); + memcpy(&tmp5, &BT_UUID_128(pstuuid)->val[12], sizeof(tmp5)); + snprintf(uuid, 37, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x", + tmp5, tmp4, tmp3, tmp2, tmp1, tmp0); + break; + default: + memset(uuid, 0, 37); + return; + } +} + +bool BLEUtils::uuidBTSame(const bt_uuid_t* pstuuid1, + const bt_uuid_t* pstuuid2) +{ + bool temp = (pstuuid1->type == pstuuid2->type); + if (true == temp) + { + if (pstuuid1->type == BT_UUID_TYPE_16) + { + temp = (0 == memcmp(&BT_UUID_16(pstuuid1)->val, &BT_UUID_16(pstuuid2)->val, 2)); + } + else + { + temp = (0 == memcmp(BT_UUID_128(pstuuid1)->val, BT_UUID_128(pstuuid2)->val, 16)); + } + } + return temp; + +} + +BLEDevice& BLEUtils::getLoacalBleDevice() +{ + return BLE; +} + +bool BLEUtils::isLocalBLE(const BLEDevice& device) +{ + return (device == BLE); +} + + + diff --git a/libraries/BLE/src/BLEUtils.h b/libraries/BLE/src/BLEUtils.h index e3bc241f..5fb70831 100644 --- a/libraries/BLE/src/BLEUtils.h +++ b/libraries/BLE/src/BLEUtils.h @@ -4,10 +4,14 @@ namespace BLEUtils String macAddressBT2String(const bt_addr_le_t &bd_addr); void macAddressString2BT(const char* mac_str, bt_addr_le_t &bd_addr); bool macAddressValid(const bt_addr_le_t &bd_addr); + bool macAddressSame(const bt_addr_le_t &bd_addr1, const bt_addr_le_t &bd_addr2); bt_addr_le_t* bleGetLoalAddress(); void uuidString2BT(const char* uuid, bt_uuid_t* pstuuid); void uuidBT2String(const bt_uuid_t* pstuuid, char* uuid); + bool uuidBTSame(const bt_uuid_t* pstuuid1, + const bt_uuid_t* pstuuid2); + BLEDevice& getLoacalBleDevice(); - bool isLocalBLE(BLEDevice& device); + bool isLocalBLE(const BLEDevice& device); } diff --git a/system/libarc32_arduino101/framework/src/services/ble/uuid.c b/system/libarc32_arduino101/framework/src/services/ble/uuid.c index dbacfd94..365ea2c7 100644 --- a/system/libarc32_arduino101/framework/src/services/ble/uuid.c +++ b/system/libarc32_arduino101/framework/src/services/ble/uuid.c @@ -89,7 +89,7 @@ int bt_uuid_cmp(const struct bt_uuid *u1, const struct bt_uuid *u2) switch (u1->type) { case BT_UUID_TYPE_16: - return (int)BT_UUID_16(u1)->val - (int)BT_UUID_16(u2)->val; + return memcmp(&BT_UUID_16(u1)->val, &BT_UUID_16(u2)->val, 2);//(int)BT_UUID_16(u1)->val - (int)BT_UUID_16(u2)->val; case BT_UUID_TYPE_128: return memcmp(BT_UUID_128(u1)->val, BT_UUID_128(u2)->val, 16); } diff --git a/system/libarc32_arduino101/framework/src/services/ble_service/ble_service.c b/system/libarc32_arduino101/framework/src/services/ble_service/ble_service.c index d6efdf63..a056a643 100644 --- a/system/libarc32_arduino101/framework/src/services/ble_service/ble_service.c +++ b/system/libarc32_arduino101/framework/src/services/ble_service/ble_service.c @@ -113,10 +113,9 @@ static void handle_msg_id_ble_rpc_callin(struct message *msg, void *priv) { struct ble_rpc_callin *rpc = container_of(msg, struct ble_rpc_callin, msg); /* handle incoming message */ - rpc_deserialize(rpc->p_data, rpc->len); //pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + rpc_deserialize(rpc->p_data, rpc->len); bfree(rpc->p_data); - //pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); message_free(msg); //pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); } From e2da8b532820b45847e33244d8a383f58dd32e22 Mon Sep 17 00:00:00 2001 From: lianggao Date: Fri, 21 Oct 2016 22:26:18 +0800 Subject: [PATCH 4/4] Add central example sketch Note: need change the compiler parameter in platform.txt. "compiler.c.flags=-c -std=gnu11 -mcpu=quarkse_em -mlittle-endian -g -Os ..." to "compiler.c.flags=-c -std=gnu11 -mcpu=quarkse_em -mlittle-endian -g -O0" "compiler.cpp.flags=-c -mcpu=quarkse_em -mlittle-endian -g -Os ..." to "compiler.cpp.flags=-c -mcpu=quarkse_em -mlittle-endian -g -O0 ..." --- libraries/BLE/examples/central/central.ino | 94 ++++++++++++++++++ variants/arduino_101/libarc32drv_arduino101.a | Bin 782628 -> 782562 bytes 2 files changed, 94 insertions(+) create mode 100644 libraries/BLE/examples/central/central.ino diff --git a/libraries/BLE/examples/central/central.ino b/libraries/BLE/examples/central/central.ino new file mode 100644 index 00000000..212e1546 --- /dev/null +++ b/libraries/BLE/examples/central/central.ino @@ -0,0 +1,94 @@ + +#include "ArduinoBLE.h" +#include "BLEAttribute.h" +#include "BLECharacteristicImp.h" +#include "BLEProfileManager.h" + +// LED pin +#define LED_PIN 13 + +void setup() { + Serial1.begin(115200); + Serial1.println("test---"); + + // set LED pin to output mode + pinMode(LED_PIN, OUTPUT); + + // begin initialization + BLE.begin(); + Serial1.println(BLE.address()); + + BLE.startScanning("LED"); +} + +void controlLed(BLEDevice &peripheral) +{ + static bool discovered = false; + // connect to the peripheral + Serial1.print("Connecting ... "); + Serial1.println(peripheral.address()); + + if (peripheral.connect()) + { + Serial1.print("Connected: "); + Serial1.println(peripheral.address()); + } + else + { + Serial1.println("Failed to connect!"); + return; + } + + peripheral.discoverAttributes(); + + BLECharacteristic ledCharacteristic = peripheral.characteristic("19b10101-e8f2-537e-4f6c-d104768a1214"); + + if (!ledCharacteristic) + { + //peripheral.disconnect(); + while(1) + { + Serial1.println("Peripheral does not have LED characteristic!"); + delay(5000); + } + return; + } + + + unsigned char ledstate = 0; + + discovered = false; + while (peripheral.connected()) + { + if (ledstate == 1) + { + ledstate = 0; + } + else + { + ledstate = 1; + } + ledCharacteristic.write(&ledstate, sizeof(ledstate)); + delay(5000); + } + Serial1.print("Disconnected"); + Serial1.println(peripheral.address()); +} + +void loop() { + //pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + BLEDevice peripheral = BLE.available(); + //pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + if (peripheral) + { + Serial1.println(peripheral.address()); + BLE.stopScanning(); + delay (1000); + // central connected to peripheral + controlLed(peripheral); + delay (4000); + BLE.startScanning("LED"); + } +} + + diff --git a/variants/arduino_101/libarc32drv_arduino101.a b/variants/arduino_101/libarc32drv_arduino101.a index e0cb6b8ebca696bb969e406b16df05c24693f4f2..520775f28ea00b87acf9440fabc2d965cbfb1003 100644 GIT binary patch delta 38721 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

kB zas0R(c9>q@!_mb4FOs&V<9R)tkqMU~wL-|4F#D#H@Di{S252NyS58lA!y^ov;pqO;M}oe z$~;u_gw2ZjD-MQX)HEj%Umgy}(2vJ%ZEHOjezeZPGT|dFho#I&4=3}6@52$9JYPa~ zs;i#*p)bd^uZ)!|0$Kd_Z6VS~t8QG?7$bDSG(0tZ{cym3cg<5$Sg3!^nVpFZ-kZv$V82GwKUO_YtFF|F!Av!v<)rP0;TOiHM(9iAyy z9tkYMo0{oI10AZLn;euk91UD1UpX3JAC!MT8pv+MyS#KY%G!Ib=|Q_d!zhmrH$4`Z z?h?~w^*;k&$=IWKZn)$-tTwixkUizv?=Xw6uZ|m)F2DFL5UnsJf&H9S=a}`F6S*MQqlIIfocuq%B`}Qu