Pinned to some recent date
features/FEATURE_BLE/ble/GapAdvertisingData.h
- Committer:
- Simon Cooksey
- Date:
- 2016-11-17
- Revision:
- 0:fb7af294d5d9
File content as of revision 0:fb7af294d5d9:
/* mbed Microcontroller Library * Copyright (c) 2006-2013 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __GAP_ADVERTISING_DATA_H__ #define __GAP_ADVERTISING_DATA_H__ #include <stdint.h> #include <string.h> #include "blecommon.h" #define GAP_ADVERTISING_DATA_MAX_PAYLOAD (31) /** * @brief This class provides several helper functions to generate properly * formatted GAP Advertising and Scan Response data payloads. * * @note See Bluetooth Specification 4.0 (Vol. 3), Part C, Sections 11 and 18 * for further information on Advertising and Scan Response data. * * @par Advertising and Scan Response Payloads * Advertising data and Scan Response data are organized around a set of * data types called 'AD types' in Bluetooth 4.0 (see the Bluetooth Core * Specification v4.0, Vol. 3, Part C, Sections 11 and 18). * * @par * Each AD type has its own standardized assigned number, as defined * by the Bluetooth SIG: * https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile. * * @par * For convenience, all appropriate AD types are encapsulated * in GapAdvertisingData::DataType. * * @par * Before the AD Types and their payload (if any) can be inserted into * the Advertising or Scan Response frames, they need to be formatted as * follows: * * @li @c Record length (1 byte). * @li @c AD Type (1 byte). * @li @c AD payload (optional; only present if record length > 1). * * @par * This class takes care of properly formatting the payload, performs * some basic checks on the payload length, and tries to avoid common * errors like adding an exclusive AD field twice in the Advertising * or Scan Response payload. * * @par EXAMPLE * * @code * * // ToDo * * @endcode */ class GapAdvertisingData { public: /*! * @brief A list of Advertising Data types commonly used by peripherals. * These AD types are used to describe the capabilities of the * peripheral, and are inserted inside the advertising or scan * response payloads. * * @par Source * * @li @c Bluetooth Core Specification 4.0 (Vol. 3), Part C, Section 11, 18. * @li @c https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile. */ enum DataType_t { FLAGS = 0x01, /**< Flags, refer to GapAdvertisingData::Flags_t. */ 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, /**< Appearance, refer to GapAdvertisingData::Appearance_t. */ ADVERTISING_INTERVAL = 0x1A, /**< Advertising Interval. */ MANUFACTURER_SPECIFIC_DATA = 0xFF /**< Manufacturer Specific Data. */ }; /** * Type alias for GapAdvertisingData::DataType_t. * * @deprecated This type alias will be dropped in future releases. */ typedef enum DataType_t DataType; /** * @brief A list of values for the FLAGS AD Type. * * @note You can use more than one value in the FLAGS AD Type (ex. * LE_GENERAL_DISCOVERABLE and BREDR_NOT_SUPPORTED). * * @par Source * * @li @c Bluetooth Core Specification 4.0 (Vol. 3), Part C, Section 18.1. */ enum Flags_t { LE_LIMITED_DISCOVERABLE = 0x01, /**< Peripheral device is discoverable for a limited period of time. */ LE_GENERAL_DISCOVERABLE = 0x02, /**< Peripheral device is discoverable at any moment. */ BREDR_NOT_SUPPORTED = 0x04, /**< Peripheral device is LE only. */ SIMULTANEOUS_LE_BREDR_C = 0x08, /**< Not relevant - central mode only. */ SIMULTANEOUS_LE_BREDR_H = 0x10 /**< Not relevant - central mode only. */ }; /** * Type alias for GapAdvertisingData::Flags_t. * * @deprecated This type alias will be dropped in future releases. */ typedef enum Flags_t Flags; /** * @brief * A list of values for the APPEARANCE AD Type, which describes the * physical shape or appearance of the device. * * @par Source * * @li @c Bluetooth Core Specification Supplement, Part A, Section 1.12. * @li @c Bluetooth Core Specification 4.0 (Vol. 3), Part C, Section 12.2. * @li @c https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml. */ enum Appearance_t { UNKNOWN = 0, /**< Unknown or unspecified appearance type. */ GENERIC_PHONE = 64, /**< Generic Phone. */ GENERIC_COMPUTER = 128, /**< Generic Computer. */ GENERIC_WATCH = 192, /**< Generic Watch. */ WATCH_SPORTS_WATCH = 193, /**< Sports Watch. */ GENERIC_CLOCK = 256, /**< Generic Clock. */ GENERIC_DISPLAY = 320, /**< Generic Display. */ GENERIC_REMOTE_CONTROL = 384, /**< Generic Remote Control. */ GENERIC_EYE_GLASSES = 448, /**< Generic Eye Glasses. */ GENERIC_TAG = 512, /**< Generic Tag. */ GENERIC_KEYRING = 576, /**< Generic Keyring. */ GENERIC_MEDIA_PLAYER = 640, /**< Generic Media Player. */ GENERIC_BARCODE_SCANNER = 704, /**< Generic Barcode Scanner. */ GENERIC_THERMOMETER = 768, /**< Generic Thermometer. */ THERMOMETER_EAR = 769, /**< Ear Thermometer. */ GENERIC_HEART_RATE_SENSOR = 832, /**< Generic Heart Rate Sensor. */ HEART_RATE_SENSOR_HEART_RATE_BELT = 833, /**< Belt Heart Rate Sensor. */ GENERIC_BLOOD_PRESSURE = 896, /**< Generic Blood Pressure. */ BLOOD_PRESSURE_ARM = 897, /**< Arm Blood Pressure. */ BLOOD_PRESSURE_WRIST = 898, /**< Wrist Blood Pressure. */ HUMAN_INTERFACE_DEVICE_HID = 960, /**< Human Interface Device (HID). */ KEYBOARD = 961, /**< Keyboard. */ MOUSE = 962, /**< Mouse. */ JOYSTICK = 963, /**< Joystick. */ GAMEPAD = 964, /**< Gamepad. */ DIGITIZER_TABLET = 965, /**< Digitizer Tablet. */ CARD_READER = 966, /**< Card Reader. */ DIGITAL_PEN = 967, /**< Digital Pen. */ BARCODE_SCANNER = 968, /**< Barcode Scanner. */ GENERIC_GLUCOSE_METER = 1024, /**< Generic Glucose Meter. */ GENERIC_RUNNING_WALKING_SENSOR = 1088, /**< Generic Running/Walking Sensor. */ RUNNING_WALKING_SENSOR_IN_SHOE = 1089, /**< In Shoe Running/Walking Sensor. */ RUNNING_WALKING_SENSOR_ON_SHOE = 1090, /**< On Shoe Running/Walking Sensor. */ RUNNING_WALKING_SENSOR_ON_HIP = 1091, /**< On Hip Running/Walking Sensor. */ GENERIC_CYCLING = 1152, /**< Generic Cycling. */ CYCLING_CYCLING_COMPUTER = 1153, /**< Cycling Computer. */ CYCLING_SPEED_SENSOR = 1154, /**< Cycling Speed Sensor. */ CYCLING_CADENCE_SENSOR = 1155, /**< Cycling Cadence Sensor. */ CYCLING_POWER_SENSOR = 1156, /**< Cycling Power Sensor. */ CYCLING_SPEED_AND_CADENCE_SENSOR = 1157, /**< Cycling Speed and Cadence Sensor. */ PULSE_OXIMETER_GENERIC = 3136, /**< Generic Pulse Oximeter. */ PULSE_OXIMETER_FINGERTIP = 3137, /**< Fingertip Pulse Oximeter. */ PULSE_OXIMETER_WRIST_WORN = 3138, /**< Wrist Worn Pulse Oximeter. */ GENERIC_WEIGHT_SCALE = 3200, /**< Generic Weight Scale. */ OUTDOOR_GENERIC = 5184, /**< Generic Outdoor. */ OUTDOOR_LOCATION_DISPLAY_DEVICE = 5185, /**< Outdoor Location Display Device. */ OUTDOOR_LOCATION_AND_NAVIGATION_DISPLAY_DEVICE = 5186, /**< Outdoor Location and Navigation Display Device. */ OUTDOOR_LOCATION_POD = 5187, /**< Outdoor Location Pod. */ OUTDOOR_LOCATION_AND_NAVIGATION_POD = 5188 /**< Outdoor Location and Navigation Pod. */ }; /** * Type alias for GapAdvertisingData::Appearance_t. * * @deprecated This type alias will be dropped in future releases. */ typedef enum Appearance_t Appearance; /** * Empty constructor. */ GapAdvertisingData(void) : _payload(), _payloadLen(0), _appearance(GENERIC_TAG) { /* empty */ } /** * Adds advertising data based on the specified AD type (see GapAdvertisingData::DataType_t). * If the supplied AD type is already present in the advertising * payload, then the value is updated. * * @param[in] advDataType The Advertising 'DataType' to add. * @param[in] payload Pointer to the payload contents. * @param[in] len Size of the payload in bytes. * * @return BLE_ERROR_BUFFER_OVERFLOW if the new value causes the * advertising buffer to overflow. BLE_ERROR_NONE is returned * on success. * * @note When the specified AD type is INCOMPLETE_LIST_16BIT_SERVICE_IDS, * COMPLETE_LIST_16BIT_SERVICE_IDS, INCOMPLETE_LIST_32BIT_SERVICE_IDS, * COMPLETE_LIST_32BIT_SERVICE_IDS, INCOMPLETE_LIST_128BIT_SERVICE_IDS, * COMPLETE_LIST_128BIT_SERVICE_IDS or LIST_128BIT_SOLICITATION_IDS the * supplied value is appended to the values previously added to the * payload. */ ble_error_t addData(DataType_t advDataType, const uint8_t *payload, uint8_t len) { /* Find field */ uint8_t* field = findField(advDataType); if (field) { /* Field type already exist, either add to field or replace */ return addField(advDataType, payload, len, field); } else { /* Field doesn't exists, insert new */ return appendField(advDataType, payload, len); } } /** * Update a particular ADV field in the advertising payload (based on * matching type). * * @param[in] advDataType The Advertising 'DataType' to add. * @param[in] payload Pointer to the payload contents. * @param[in] len Size of the payload in bytes. * * @return BLE_ERROR_UNSPECIFIED if the specified field is not found, * BLE_ERROR_BUFFER_OVERFLOW if the new value causes the * advertising buffer to overflow. BLE_ERROR_NONE is returned * on success. */ ble_error_t updateData(DataType_t advDataType, const uint8_t *payload, uint8_t len) { /* Find field */ uint8_t* field = findField(advDataType); if (field) { /* Field type already exist, replace field contents */ return updateField(advDataType, payload, len, field); } else { /* field doesn't exists, return an error */ return BLE_ERROR_UNSPECIFIED; } } /** * Helper function to add APPEARANCE data to the advertising payload. * * @param appearance * The APPEARANCE value to add. * * @return BLE_ERROR_BUFFER_OVERFLOW if the specified data would cause the * advertising buffer to overflow, else BLE_ERROR_NONE. */ ble_error_t addAppearance(Appearance appearance = GENERIC_TAG) { _appearance = appearance; return addData(GapAdvertisingData::APPEARANCE, (uint8_t *)&appearance, 2); } /** * Helper function to add FLAGS data to the advertising payload. * * @param[in] flags * LE_LIMITED_DISCOVERABLE * The peripheral is discoverable for a limited period of time. * LE_GENERAL_DISCOVERABLE * The peripheral is permanently discoverable. * BREDR_NOT_SUPPORTED * This peripheral is a Bluetooth Low Energy only device (no EDR support). * * @return BLE_ERROR_BUFFER_OVERFLOW if the specified data would cause the * advertising buffer to overflow, else BLE_ERROR_NONE. */ ble_error_t addFlags(uint8_t flags = LE_GENERAL_DISCOVERABLE) { return addData(GapAdvertisingData::FLAGS, &flags, 1); } /** * Helper function to add TX_POWER_LEVEL data to the advertising payload. * * @return BLE_ERROR_BUFFER_OVERFLOW if the specified data would cause the * advertising buffer to overflow, else BLE_ERROR_NONE. */ ble_error_t addTxPower(int8_t txPower) { /* To Do: Basic error checking to make sure txPower is in range. */ return addData(GapAdvertisingData::TX_POWER_LEVEL, (uint8_t *)&txPower, 1); } /** * Clears the payload and resets the payload length counter. */ void clear(void) { memset(&_payload, 0, GAP_ADVERTISING_DATA_MAX_PAYLOAD); _payloadLen = 0; } /** * Access the current payload. * * @return A pointer to the current payload. */ const uint8_t *getPayload(void) const { return _payload; } /** * Get the current payload length. * * @return The current payload length (0..31 bytes). */ uint8_t getPayloadLen(void) const { return _payloadLen; } /** * Get the current appearance value. * * @return The 16-bit appearance value for this device. */ uint16_t getAppearance(void) const { return (uint16_t)_appearance; } /** * Search advertisement data for a specific field. * * @param[in] type * The type of the field to find. * * @return A 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 * GapAdvertisingData::DataType_t). * * @param[in] advDataType * The type of the new data. * @param[in] payload * A pointer to the data to be appended to the advertising * payload. * @param[in] len * The length of th data pointed to by @p payload. * * @return BLE_ERROR_BUFFER_OVERFLOW if the specified data would cause the * advertising buffer to overflow, else BLE_ERROR_NONE. */ 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. * * @param[in] type * The type of the data to find. * * @return A 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; } /** * Given the a pointer to a field in the advertising payload it replaces * the existing data in the field with the supplied data. * * @param[in] advDataType * The type of the new data. * @param[in] payload * A pointer to the data to be added to the advertising * payload. * @param[in] len * The length of th data pointed to by @p payload. * @param[in] field * A pointer to the field of type @p advDataType in the * advertising buffer. * * When the specified AD type is INCOMPLETE_LIST_16BIT_SERVICE_IDS, * COMPLETE_LIST_16BIT_SERVICE_IDS, INCOMPLETE_LIST_32BIT_SERVICE_IDS, * COMPLETE_LIST_32BIT_SERVICE_IDS, INCOMPLETE_LIST_128BIT_SERVICE_IDS, * COMPLETE_LIST_128BIT_SERVICE_IDS or LIST_128BIT_SOLICITATION_IDS the * supplied value is appended to the values previously added to the * payload. * * @return BLE_ERROR_NONE on success. */ ble_error_t addField(DataType_t advDataType, const uint8_t *payload, uint8_t len, uint8_t* field) { ble_error_t result = BLE_ERROR_BUFFER_OVERFLOW; switch(advDataType) { /* 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; } /* These fields will be overwritten with the new value */ default: { result = updateField(advDataType, payload, len, field); break; } } return result; } /** * Given the a pointer to a field in the advertising payload it replaces * the existing data in the field with the supplied data. * * @param[in] advDataType * The type of the data to be updated. * @param[in] payload * A pointer to the data to be updated to the advertising * payload. * @param[in] len * The length of th data pointed to by @p payload. * @param[in] field * A pointer to the field of type @p advDataType in the * advertising buffer. * * @return BLE_ERROR_NONE on success. */ ble_error_t updateField(DataType_t advDataType, const uint8_t *payload, uint8_t len, uint8_t* field) { ble_error_t result = BLE_ERROR_BUFFER_OVERFLOW; 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]; } result = BLE_ERROR_NONE; } 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); } } return result; } /** * The advertising data buffer */ uint8_t _payload[GAP_ADVERTISING_DATA_MAX_PAYLOAD]; /** * The length of the data added to the advertising buffer. */ uint8_t _payloadLen; /** * Appearance value. */ uint16_t _appearance; }; #endif /* ifndef __GAP_ADVERTISING_DATA_H__ */