diff --git a/DOXYGEN_FRONTPAGE.md b/DOXYGEN_FRONTPAGE.md new file mode 100644 index 0000000..6f286f8 --- /dev/null +++ b/DOXYGEN_FRONTPAGE.md @@ -0,0 +1,16 @@ +#BLE API + +The BLE module within mbed OS offers a high level abstraction for using +Bluetooth Low Energy on multiple platforms. + +This documentation describes the internal structure of mbed +(BLE_API)[https://github.com/armmbed/ble]. It was automatically generated from +specially formatted comment blocks in BLE_API's source code using Doxygen. +(See http://www.stack.nl/~dimitri/doxygen/ for more information on Doxygen). + +For getting started with BLE on mbed, check our (introduction +page)[https://docs.mbed.com/docs/ble-intros/en/latest/]. + +For mbed OS examples using BLE, check (this +repository)[https://github.com/armmbed/ble-examples]. For mbed-classic +examples, refer to (code under mbed.org)[https://developer.mbed.org/teams/Bluetooth-Low-Energy/code/]. diff --git a/README.md b/README.md index b3afc24..a91f8ef 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This is the Github repo for the `BLE_API` used by developer.mbed.org. Please see the [mbed BLE Homepage](https://developer.mbed.org/teams/Bluetooth-Low-Energy/) for all documentation, code examples and general help. # Supported Services -Supported GATT services and constantly being added and can be found in the `ble/source/services/` folder. +Supported GATT services and constantly being added and can be found in the (`ble/ble/services/`)[https://github.com/ARMmbed/ble/tree/master/ble/services] folder. Currently supported services include: * Battery diff --git a/ble/GapAdvertisingData.h b/ble/GapAdvertisingData.h index 4088a30..8d0ffb5 100644 --- a/ble/GapAdvertisingData.h +++ b/ble/GapAdvertisingData.h @@ -89,22 +89,23 @@ public: */ /**********************************************************************/ enum DataType_t { - FLAGS = 0x01, /**< \ref *Flags. */ - INCOMPLETE_LIST_16BIT_SERVICE_IDS = 0x02, /**< Incomplete list of 16-bit service IDs. */ - COMPLETE_LIST_16BIT_SERVICE_IDS = 0x03, /**< Complete list of 16-bit service IDs. */ - INCOMPLETE_LIST_32BIT_SERVICE_IDS = 0x04, /**< Incomplete list of 32-bit service IDs (not relevant for Bluetooth 4.0). */ - COMPLETE_LIST_32BIT_SERVICE_IDS = 0x05, /**< Complete list of 32-bit service IDs (not relevant for Bluetooth 4.0). */ - INCOMPLETE_LIST_128BIT_SERVICE_IDS = 0x06, /**< Incomplete list of 128-bit service IDs. */ - COMPLETE_LIST_128BIT_SERVICE_IDS = 0x07, /**< Complete list of 128-bit service IDs. */ - SHORTENED_LOCAL_NAME = 0x08, /**< Shortened local name. */ - COMPLETE_LOCAL_NAME = 0x09, /**< Complete local name. */ - TX_POWER_LEVEL = 0x0A, /**< TX power level (in dBm). */ - DEVICE_ID = 0x10, /**< Device ID. */ - SLAVE_CONNECTION_INTERVAL_RANGE = 0x12, /**< Slave connection interval range. */ - SERVICE_DATA = 0x16, /**< Service data. */ - APPEARANCE = 0x19, /**< \ref Appearance. */ - ADVERTISING_INTERVAL = 0x1A, /**< Advertising interval. */ - MANUFACTURER_SPECIFIC_DATA = 0xFF /**< Manufacturer specific data. */ + FLAGS = 0x01, /**< \ref *Flags */ + INCOMPLETE_LIST_16BIT_SERVICE_IDS = 0x02, /**< Incomplete list of 16-bit Service IDs */ + COMPLETE_LIST_16BIT_SERVICE_IDS = 0x03, /**< Complete list of 16-bit Service IDs */ + INCOMPLETE_LIST_32BIT_SERVICE_IDS = 0x04, /**< Incomplete list of 32-bit Service IDs (not relevant for Bluetooth 4.0) */ + COMPLETE_LIST_32BIT_SERVICE_IDS = 0x05, /**< Complete list of 32-bit Service IDs (not relevant for Bluetooth 4.0) */ + INCOMPLETE_LIST_128BIT_SERVICE_IDS = 0x06, /**< Incomplete list of 128-bit Service IDs */ + COMPLETE_LIST_128BIT_SERVICE_IDS = 0x07, /**< Complete list of 128-bit Service IDs */ + SHORTENED_LOCAL_NAME = 0x08, /**< Shortened Local Name */ + COMPLETE_LOCAL_NAME = 0x09, /**< Complete Local Name */ + TX_POWER_LEVEL = 0x0A, /**< TX Power Level (in dBm) */ + DEVICE_ID = 0x10, /**< Device ID */ + SLAVE_CONNECTION_INTERVAL_RANGE = 0x12, /**< Slave Connection Interval Range */ + LIST_128BIT_SOLICITATION_IDS = 0x15, /**< List of 128 bit service UUIDs the device is looking for */ + SERVICE_DATA = 0x16, /**< Service Data */ + APPEARANCE = 0x19, /**< \ref Appearance */ + ADVERTISING_INTERVAL = 0x1A, /**< Advertising Interval */ + MANUFACTURER_SPECIFIC_DATA = 0xFF /**< Manufacturer Specific Data */ }; typedef enum DataType_t DataType; /* Deprecated type alias. This may be dropped in a future release. */ @@ -210,27 +211,98 @@ public: */ ble_error_t addData(DataType advDataType, const uint8_t *payload, uint8_t len) { - /* To Do: Check if an AD type already exists and if the existing */ - /* value is exclusive or not (flags and so on). */ + ble_error_t result = BLE_ERROR_BUFFER_OVERFLOW; - /* Make sure we don't exceed the 31 byte payload limit. */ - if (_payloadLen + len + 2 > GAP_ADVERTISING_DATA_MAX_PAYLOAD) { - return BLE_ERROR_BUFFER_OVERFLOW; + // find field + uint8_t* field = findField(advDataType); + + // Field type already exist, either add to field or replace + if (field) { + switch(advDataType) { + // These fields will be overwritten with the new value + case FLAGS: + case SHORTENED_LOCAL_NAME: + case COMPLETE_LOCAL_NAME: + case TX_POWER_LEVEL: + case DEVICE_ID: + case SLAVE_CONNECTION_INTERVAL_RANGE: + case SERVICE_DATA: + case APPEARANCE: + case ADVERTISING_INTERVAL: + case MANUFACTURER_SPECIFIC_DATA: { + // current field length, with the type subtracted + uint8_t dataLength = field[0] - 1; + + // new data has same length, do in-order replacement + if (len == dataLength) { + for (uint8_t idx = 0; idx < dataLength; idx++) { + field[2 + idx] = payload[idx]; + } + } else { + // check if data fits + if ((_payloadLen - dataLength + len) <= GAP_ADVERTISING_DATA_MAX_PAYLOAD) { + + // remove old field + while ((field + dataLength + 2) < &_payload[_payloadLen]) { + *field = field[dataLength + 2]; + field++; + } + + // reduce length + _payloadLen -= dataLength + 2; + + // add new field + result = appendField(advDataType, payload, len); + } + } + + break; + } + // These fields will have the new data appended if there is sufficient space + case INCOMPLETE_LIST_16BIT_SERVICE_IDS: + case COMPLETE_LIST_16BIT_SERVICE_IDS: + case INCOMPLETE_LIST_32BIT_SERVICE_IDS: + case COMPLETE_LIST_32BIT_SERVICE_IDS: + case INCOMPLETE_LIST_128BIT_SERVICE_IDS: + case COMPLETE_LIST_128BIT_SERVICE_IDS: + case LIST_128BIT_SOLICITATION_IDS: { + // check if data fits + if ((_payloadLen + len) <= GAP_ADVERTISING_DATA_MAX_PAYLOAD) { + // make room for new field by moving the remainder of the + // advertisement payload "to the right" starting after the + // TYPE field. + uint8_t* end = &_payload[_payloadLen]; + + while (&field[1] < end) { + end[len] = *end; + end--; + } + + // insert new data + for (uint8_t idx = 0; idx < len; idx++) { + field[2 + idx] = payload[idx]; + } + + // increment lengths + field[0] += len; + _payloadLen += len; + + result = BLE_ERROR_NONE; + } + + break; + } + // Field exists but updating it is not supported. Abort operation. + default: + result = BLE_ERROR_NOT_IMPLEMENTED; + break; + } + } else { + // field doesn't exists, insert new + result = appendField(advDataType, payload, len); } - /* Field length. */ - memset(&_payload[_payloadLen], len + 1, 1); - _payloadLen++; - - /* Field ID. */ - memset(&_payload[_payloadLen], (uint8_t)advDataType, 1); - _payloadLen++; - - /* Payload. */ - memcpy(&_payload[_payloadLen], payload, len); - _payloadLen += len; - - return BLE_ERROR_NONE; + return result; } /** @@ -345,7 +417,63 @@ public: return (uint16_t)_appearance; } + /** + * Search advertisement data for field. + * Returns pointer to the first element in the field if found, NULL otherwise. + * Where the first element is the length of the field. + */ + const uint8_t* findField(DataType_t type) const { + return findField(type); + } + private: + /** + * Append advertising data based on the specified AD type (see DataType) + */ + ble_error_t appendField(DataType advDataType, const uint8_t *payload, uint8_t len) + { + /* Make sure we don't exceed the 31 byte payload limit */ + if (_payloadLen + len + 2 > GAP_ADVERTISING_DATA_MAX_PAYLOAD) { + return BLE_ERROR_BUFFER_OVERFLOW; + } + + /* Field length. */ + memset(&_payload[_payloadLen], len + 1, 1); + _payloadLen++; + + /* Field ID. */ + memset(&_payload[_payloadLen], (uint8_t)advDataType, 1); + _payloadLen++; + + /* Payload. */ + memcpy(&_payload[_payloadLen], payload, len); + _payloadLen += len; + + return BLE_ERROR_NONE; + } + + /** + * Search advertisement data for field. + * Returns pointer to the first element in the field if found, NULL otherwise. + * Where the first element is the length of the field. + */ + uint8_t* findField(DataType_t type) { + // scan through advertisement data + for (uint8_t idx = 0; idx < _payloadLen; ) { + uint8_t fieldType = _payload[idx + 1]; + + if (fieldType == type) { + return &_payload[idx]; + } + + // advance to next field + idx += _payload[idx] + 1; + } + + // field not found + return NULL; + } + uint8_t _payload[GAP_ADVERTISING_DATA_MAX_PAYLOAD]; uint8_t _payloadLen; uint16_t _appearance; diff --git a/ble/services/BatteryService.h b/ble/services/BatteryService.h index 965fb20..16f40b2 100644 --- a/ble/services/BatteryService.h +++ b/ble/services/BatteryService.h @@ -21,8 +21,8 @@ /** * @class BatteryService -* @brief BLE Battery Service. This service displays the battery level from 0%->100% represented as a 8bit number.
-* Service: https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.battery_service.xml
+* @brief BLE Battery Service. This service displays the battery level from 0% to 100%, represented as an 8bit number. +* Service: https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.battery_service.xml * Battery Level Char: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.battery_level.xml */ class BatteryService { @@ -45,11 +45,11 @@ public: } /** - * @brief Update the battery level with a new value. Valid values range from - * 0..100. Anything outside this range will be ignored. + * @brief Update the battery level with a new value. [Valid values lie between 0 and 100]; + * anything outside this range will be ignored. * * @param newLevel - * update to battery level. + * Update to battery level. */ void updateBatteryLevel(uint8_t newLevel) { batteryLevel = newLevel; diff --git a/ble/services/DFUService.h b/ble/services/DFUService.h index 253890e..032489a 100644 --- a/ble/services/DFUService.h +++ b/ble/services/DFUService.h @@ -37,20 +37,20 @@ extern const uint8_t DFUServicePacketCharacteristicUUID[]; class DFUService { public: /** - * @brief Signature for the handover callback. The application may provide such a - * callback when setting up the DFU service, in which case it will be + * @brief Signature for the handover callback. The application may provide this + * callback when setting up the DFU service. The callback is then * invoked before handing control over to the bootloader. */ typedef void (*ResetPrepare_t)(void); public: /** - * @brief Adds Device Firmware Update service to an existing ble object. + * @brief Adds Device Firmware Update Service to an existing BLE object. * * @param[ref] _ble * BLE object for the underlying controller. * @param[in] _handoverCallback - * Application specific handover callback. + * Application-specific handover callback. */ DFUService(BLE &_ble, ResetPrepare_t _handoverCallback = NULL) : ble(_ble), @@ -59,12 +59,12 @@ public: GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE), controlBytes(), packetBytes() { - static bool serviceAdded = false; /* We should only ever need to add the DFU service once. */ + static bool serviceAdded = false; /* We only add the DFU service once. */ if (serviceAdded) { return; } - /* Set an initial value for control bytes so that the application's DFUService can + /* Set an initial value for control bytes, so that the application's DFU service can * be distinguished from the real DFU service provided by the bootloader. */ controlBytes[0] = 0xFF; controlBytes[1] = 0xFF; @@ -80,7 +80,7 @@ public: } /** - * @brief get the handle for the value attribute of the control characteristic. + * @brief Get the handle for the value attribute of the control characteristic. */ uint16_t getControlHandle(void) const { return controlPoint.getValueHandle(); @@ -88,7 +88,7 @@ public: /** * @brief This callback allows the DFU service to receive the initial trigger to - * handover control to the bootloader; but first the application is given a + * hand control over to the bootloader. First, the application is given a * chance to clean up. * * @param[in] params @@ -96,7 +96,7 @@ public: */ virtual void onDataWritten(const GattWriteCallbackParams *params) { if (params->handle == controlPoint.getValueHandle()) { - /* At present, writing anything will do the trick--this needs to be improved. */ + /* At present, writing anything will do the trick - this needs to be improved. */ if (handoverCallback) { handoverCallback(); } @@ -112,22 +112,22 @@ protected: protected: BLE &ble; - /**< Writing to the control characteristic triggers the handover to dfu- - * bootloader. At present, writing anything will do the trick--this needs + /**< Writing to the control characteristic triggers the handover to DFU + * bootloader. At present, writing anything will do the trick - this needs * to be improved. */ WriteOnlyArrayGattCharacteristic controlPoint; - /**< The packet characteristic in this service doesn't do anything meaningful, but - * is only a placeholder to mimic the corresponding characteristic in the + /**< The packet characteristic in this service doesn't do anything meaningful; + * it is only a placeholder to mimic the corresponding characteristic in the * actual DFU service implemented by the bootloader. Without this, some - * FOTA clients might get confused as service definitions change after + * FOTA clients might get confused, because service definitions change after * handing control over to the bootloader. */ GattCharacteristic packet; uint8_t controlBytes[SIZEOF_CONTROL_BYTES]; uint8_t packetBytes[SIZEOF_PACKET_BYTES]; - static ResetPrepare_t handoverCallback; /**< application specific handover callback. */ + static ResetPrepare_t handoverCallback; /**< Application-specific handover callback. */ }; #endif /* #ifndef __BLE_DFU_SERVICE_H__*/ diff --git a/ble/services/DeviceInformationService.h b/ble/services/DeviceInformationService.h index 0151eeb..e0b8f87 100644 --- a/ble/services/DeviceInformationService.h +++ b/ble/services/DeviceInformationService.h @@ -21,41 +21,30 @@ /** * @class DeviceInformationService -* @brief BLE Device Information Service
-* Service: https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.device_information.xml
+* @brief BLE Device Information Service +* Service: https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.device_information.xml * Manufacturer Name String Char: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.manufacturer_name_string.xml */ class DeviceInformationService { public: /** - * @brief Device Information Service Constructor. + * @brief Device Information Service Constructor: copies device-specific information + * into the BLE stack. * * @param[ref] _ble * BLE object for the underlying controller. * @param[in] manufacturersName - * This characteristic represents the name of the - * manufacturer of the device. The name is copied into the - * BLE stack during this constructor. + * The name of the manufacturer of the device. * @param[in] modelNumber - * This characteristic represents the model number that is - * assigned by the device vendor. The value is copied into - * the BLE stack during this constructor. + * The model number that is assigned by the device vendor. * @param[in] serialNumber - * This characteristic represents the serial number for a - * particular instance of the device. The value is copied - * into the BLE stack during this constructor. + * The serial number for a particular instance of the device. * @param[in] hardwareRevision - * This characteristic represents the hardware revision for - * the hardware within the device. The value is copied - * into the BLE stack during this constructor. + * The hardware revision for the hardware within the device. * @param[in] firmwareRevision - * This characteristic represents the firmware revision for - * the firmware within the device. The value is copied - * into the BLE stack during this constructor. + * The device's firmware version. * @param[in] softwareRevision - * This characteristic represents the software revision for - * the software within the device. The value is copied - * into the BLE stack during this constructor. + * The device's software version. */ DeviceInformationService(BLE &_ble, const char *manufacturersName = NULL, @@ -67,36 +56,36 @@ public: ble(_ble), manufacturersNameStringCharacteristic(GattCharacteristic::UUID_MANUFACTURER_NAME_STRING_CHAR, (uint8_t *)manufacturersName, - (manufacturersName != NULL) ? strlen(manufacturersName) : 0, /* minLength */ - (manufacturersName != NULL) ? strlen(manufacturersName) : 0, /* maxLength */ + (manufacturersName != NULL) ? strlen(manufacturersName) : 0, /* Min length */ + (manufacturersName != NULL) ? strlen(manufacturersName) : 0, /* Max length */ GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ), modelNumberStringCharacteristic(GattCharacteristic::UUID_MODEL_NUMBER_STRING_CHAR, (uint8_t *)modelNumber, - (modelNumber != NULL) ? strlen(modelNumber) : 0, /* minLength */ - (modelNumber != NULL) ? strlen(modelNumber) : 0, /* maxLength */ + (modelNumber != NULL) ? strlen(modelNumber) : 0, /* Min length */ + (modelNumber != NULL) ? strlen(modelNumber) : 0, /* Max length */ GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ), serialNumberStringCharacteristic(GattCharacteristic::UUID_SERIAL_NUMBER_STRING_CHAR, (uint8_t *)serialNumber, - (serialNumber != NULL) ? strlen(serialNumber) : 0, /* minLength */ - (serialNumber != NULL) ? strlen(serialNumber) : 0, /* maxLength */ + (serialNumber != NULL) ? strlen(serialNumber) : 0, /* Min length */ + (serialNumber != NULL) ? strlen(serialNumber) : 0, /* Max length */ GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ), hardwareRevisionStringCharacteristic(GattCharacteristic::UUID_HARDWARE_REVISION_STRING_CHAR, (uint8_t *)hardwareRevision, - (hardwareRevision != NULL) ? strlen(hardwareRevision) : 0, /* minLength */ - (hardwareRevision != NULL) ? strlen(hardwareRevision) : 0, /* maxLength */ + (hardwareRevision != NULL) ? strlen(hardwareRevision) : 0, /* Min length */ + (hardwareRevision != NULL) ? strlen(hardwareRevision) : 0, /* Max length */ GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ), firmwareRevisionStringCharacteristic(GattCharacteristic::UUID_FIRMWARE_REVISION_STRING_CHAR, (uint8_t *)firmwareRevision, - (firmwareRevision != NULL) ? strlen(firmwareRevision) : 0, /* minLength */ - (firmwareRevision != NULL) ? strlen(firmwareRevision) : 0, /* maxLength */ + (firmwareRevision != NULL) ? strlen(firmwareRevision) : 0, /* Min length */ + (firmwareRevision != NULL) ? strlen(firmwareRevision) : 0, /* Max length */ GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ), softwareRevisionStringCharacteristic(GattCharacteristic::UUID_SOFTWARE_REVISION_STRING_CHAR, (uint8_t *)softwareRevision, - (softwareRevision != NULL) ? strlen(softwareRevision) : 0, /* minLength */ - (softwareRevision != NULL) ? strlen(softwareRevision) : 0, /* maxLength */ + (softwareRevision != NULL) ? strlen(softwareRevision) : 0, /* Min length */ + (softwareRevision != NULL) ? strlen(softwareRevision) : 0, /* Max length */ GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ) { - static bool serviceAdded = false; /* We should only ever need to add the information service once. */ + static bool serviceAdded = false; /* We only add the information service once. */ if (serviceAdded) { return; }