Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of BLE_API by
URIBeaconConfigService.h
00001 /* mbed Microcontroller Library 00002 * Copyright (c) 2006-2013 ARM Limited 00003 * 00004 * Licensed under the Apache License, Version 2.0 (the "License"); 00005 * you may not use this file except in compliance with the License. 00006 * You may obtain a copy of the License at 00007 * 00008 * http://www.apache.org/licenses/LICENSE-2.0 00009 * 00010 * Unless required by applicable law or agreed to in writing, software 00011 * distributed under the License is distributed on an "AS IS" BASIS, 00012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00013 * See the License for the specific language governing permissions and 00014 * limitations under the License. 00015 */ 00016 00017 #ifndef __BLE_URI_BEACON_CONFIG_SERVICE_H__ 00018 #define __BLE_URI_BEACON_CONFIG_SERVICE_H__ 00019 00020 #include "BLEDevice.h" 00021 00022 #define URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(FIRST, SECOND) { \ 00023 0xee, 0x0c, FIRST, SECOND, 0x87, 0x86, 0x40, 0xba, \ 00024 0xab, 0x96, 0x99, 0xb9, 0x1a, 0xc9, 0x81, 0xd8, \ 00025 } 00026 static const uint8_t URIBeacon2ControlServiceUUID[] = URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(0x20, 0x80); 00027 static const uint8_t lockedStateCharUUID[] = URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(0x20, 0x81); 00028 static const uint8_t uriDataCharUUID[] = URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(0x20, 0x84); 00029 static const uint8_t flagsCharUUID[] = URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(0x20, 0x85); 00030 static const uint8_t txPowerLevelsCharUUID[] = URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(0x20, 0x86); 00031 static const uint8_t txPowerModeCharUUID[] = URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(0x20, 0x87); 00032 static const uint8_t beaconPeriodCharUUID[] = URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(0x20, 0x88); 00033 static const uint8_t resetCharUUID[] = URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(0x20, 0x89); 00034 00035 /** 00036 * @class URIBeaconConfigService 00037 * @brief UriBeacon Configuration Service. Can be used to set URL, adjust power levels, and set flags. 00038 */ 00039 class URIBeaconConfigService { 00040 public: 00041 /** 00042 * @enum TXPowerModes_t 00043 * @brief Transmission Power Modes for UriBeacon 00044 */ 00045 enum TXPowerModes_t { 00046 TX_POWER_MODE_LOWEST = 0, /*!< Lowest TX power mode */ 00047 TX_POWER_MODE_LOW = 1, /*!< Low TX power mode */ 00048 TX_POWER_MODE_MEDIUM = 2, /*!< Medium TX power mode */ 00049 TX_POWER_MODE_HIGH = 3, /*!< High TX power mode */ 00050 NUM_POWER_MODES /*!< Number of Power Modes defined */ 00051 }; 00052 00053 /** 00054 * @param[ref] ble 00055 * BLEDevice object for the underlying controller. 00056 * @param[in] uridata 00057 * URI as a null-terminated string. 00058 * @param[in] flagsIn 00059 * UriBeacon Flags. 00060 * @param[in] powerLevels[] 00061 * Table of UriBeacon Tx Power Levels in dBm. 00062 * @param[in] powerMode 00063 * Currently effective power mode. 00064 * @param[in] beaconPeriodIn 00065 * The period in milliseconds that a UriBeacon packet is 00066 * transmitted. A value of zero disables UriBeacon 00067 * transmissions. 00068 */ 00069 URIBeaconConfigService (BLEDevice &bleIn, 00070 const char *uriDataIn, 00071 uint8_t flagsIn = 0, 00072 const int8_t defaultPowerLevelsIn[NUM_POWER_MODES ] = NULL, 00073 const int8_t firmwarePowerLevelsIn[NUM_POWER_MODES ] = NULL, 00074 TXPowerModes_t powerModeIn = TX_POWER_MODE_LOW , 00075 uint16_t beaconPeriodIn = 1000) : 00076 ble(bleIn), 00077 payloadIndex(0), 00078 serviceDataPayload(), 00079 initSucceeded(false), 00080 lockedState(false), 00081 uriDataLength(0), 00082 uriData(), 00083 flags(flagsIn), 00084 beaconPeriod(beaconPeriodIn), 00085 lockedStateChar(lockedStateCharUUID, reinterpret_cast<uint8_t *>(&lockedState), 1, 1, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ), 00086 uriDataChar(uriDataCharUUID, uriData, MAX_SIZE_URI_DATA_CHAR_VALUE, MAX_SIZE_URI_DATA_CHAR_VALUE, 00087 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE), 00088 flagsChar(flagsCharUUID, &flags, 1, 1, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE), 00089 txPowerLevelsChar(txPowerLevelsCharUUID, reinterpret_cast<uint8_t *>(powerLevels), sizeof(powerLevels), sizeof(powerLevels), 00090 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE), 00091 txPowerModeChar(txPowerModeCharUUID, reinterpret_cast<uint8_t *>(&txPowerMode), sizeof(uint8_t), sizeof(uint8_t), 00092 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE), 00093 beaconPeriodChar(beaconPeriodCharUUID, reinterpret_cast<uint8_t *>(&beaconPeriod), 2, 2, 00094 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE), 00095 resetChar(resetCharUUID, reinterpret_cast<uint8_t *>(&resetFlag), 1, 1, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE) { 00096 if ((uriDataIn == NULL) || ((uriDataLength = strlen(uriDataIn)) == 0) || (uriDataLength > MAX_SIZE_URI_DATA_CHAR_VALUE)) { 00097 return; 00098 } 00099 strcpy(reinterpret_cast<char *>(uriData), uriDataIn); 00100 00101 if (defaultPowerLevelsIn != NULL) { 00102 memcpy(defaultPowerLevels, defaultPowerLevelsIn, sizeof(defaultPowerLevels)); 00103 memcpy(powerLevels, defaultPowerLevels, sizeof(powerLevels)); 00104 updateTxPowerLevelsCharacteristic(); 00105 } 00106 00107 if (firmwarePowerLevelsIn != NULL) { 00108 memcpy(firmwarePowerLevels, firmwarePowerLevelsIn, sizeof(firmwarePowerLevels)); 00109 } 00110 00111 configureGAP(); 00112 00113 GattCharacteristic *charTable[] = {&lockedStateChar, &uriDataChar, &flagsChar, &txPowerLevelsChar, &beaconPeriodChar, &resetChar}; 00114 GattService beaconControlService(URIBeacon2ControlServiceUUID, charTable, sizeof(charTable) / sizeof(GattCharacteristic *)); 00115 00116 ble.addService(beaconControlService); 00117 ble.onDataWritten(this, &URIBeaconConfigService::onDataWritten); 00118 } 00119 00120 bool configuredSuccessfully(void) const { 00121 return initSucceeded; 00122 } 00123 00124 /** 00125 * Please note that the following public APIs are offered to allow modifying 00126 * the service programmatically. It is also possible to do so over BLE GATT 00127 * transactions. 00128 */ 00129 public: 00130 /** 00131 * Update flags of the URIBeacon dynamically. 00132 * 00133 * @param[in] flagsIn 00134 * @verbatim 00135 * ### UriBeacon Flags 00136 * Bit | Description 00137 * :---- | :---------- 00138 * 0 | Invisible Hint 00139 * 1..7 | Reserved for future use. Must be zero. 00140 * @endverbatim 00141 * The `Invisible Hint` flag is a command for the user-agent that tells 00142 * it not to access or display the UriBeacon. This is a guideline only, 00143 * and is not a blocking method. User agents may, with user approval, 00144 * display invisible beacons. 00145 */ 00146 void setFlags(uint8_t flagsIn) { 00147 flags = flagsIn; 00148 configureGAP(); 00149 updateFlagsCharacteristic(); 00150 } 00151 00152 /** 00153 * @brief Update the txPowerLevels table. 00154 * 00155 * @param[in] powerLevelsIn 00156 * Array of power levels 00157 */ 00158 void setDefaultTxPowerLevels(const int8_t firmwarePowerLevelsIn[NUM_POWER_MODES ], 00159 const int8_t defaultPowerLevelsIn[NUM_POWER_MODES]) { 00160 memcpy(firmwarePowerLevels, firmwarePowerLevelsIn, sizeof(firmwarePowerLevels)); 00161 memcpy(defaultPowerLevels, defaultPowerLevelsIn, sizeof(defaultPowerLevels)); 00162 memcpy(powerLevels, defaultPowerLevelsIn, sizeof(powerLevels)); 00163 configureGAP(); 00164 updateTxPowerLevelsCharacteristic(); 00165 } 00166 00167 /** 00168 * @brief Set the effective power mode from one of the values in the powerLevels tables. 00169 * 00170 * @param[in] mode 00171 * Set the TX Power Mode. 00172 */ 00173 void setTxPowerMode(TXPowerModes_t mode) { 00174 txPowerMode = mode; 00175 configureGAP(); 00176 updateTxPowerModeCharacteristic(); 00177 } 00178 00179 /** 00180 * The period in milliseconds that a UriBeacon packet is transmitted. 00181 * 00182 * @note A value of zero disables UriBeacon transmissions. 00183 * 00184 * @param beaconPeriodIn 00185 * Beacon advertising period in milliseconds 00186 */ 00187 void setBeaconPeriod(uint16_t beaconPeriodIn) { 00188 beaconPeriod = beaconPeriodIn; 00189 configureGAP(); 00190 updateBeaconPeriodCharacteristic(); 00191 } 00192 00193 private: 00194 /* 00195 * Setup the advertisement payload and GAP settings. 00196 */ 00197 void configureGAP(void) { 00198 const uint8_t BEACON_UUID[] = {0xD8, 0xFE}; 00199 00200 payloadIndex = 0; 00201 serviceDataPayload[payloadIndex++] = BEACON_UUID[0]; 00202 serviceDataPayload[payloadIndex++] = BEACON_UUID[1]; 00203 serviceDataPayload[payloadIndex++] = flags; 00204 serviceDataPayload[payloadIndex++] = powerLevels[txPowerMode]; 00205 00206 const char *urlData = reinterpret_cast<char *>(uriData); 00207 size_t sizeofURLData = uriDataLength; 00208 size_t encodedBytes = encodeURISchemePrefix(urlData, sizeofURLData) + encodeURI(urlData, sizeofURLData); 00209 00210 ble.clearAdvertisingPayload(); 00211 ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, BEACON_UUID, sizeof(BEACON_UUID)); 00212 ble.accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, serviceDataPayload, encodedBytes + 4); 00213 00214 ble.setAdvertisingInterval (Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(beaconPeriod)); 00215 ble.setTxPower(firmwarePowerLevels[txPowerMode]); 00216 } 00217 00218 /* 00219 * Encode the URI Prefix to a single byte if possible. 00220 */ 00221 size_t encodeURISchemePrefix(const char *&urldata, size_t &sizeofURLData) { 00222 if (!sizeofURLData) { 00223 return 0; 00224 } 00225 00226 /* These are the URI Prefixes that can be abbreviated.*/ 00227 const char *prefixes[] = { 00228 "http://www.", 00229 "https://www.", 00230 "http://", 00231 "https://", 00232 "urn:uuid:" 00233 }; 00234 00235 size_t encodedBytes = 0; 00236 const size_t NUM_PREFIXES = sizeof(prefixes) / sizeof(char *); 00237 for (unsigned i = 0; i < NUM_PREFIXES; i++) { 00238 size_t prefixLen = strlen(prefixes[i]); 00239 if (strncmp(urldata, prefixes[i], prefixLen) == 0) { 00240 serviceDataPayload[payloadIndex++] = i; 00241 encodedBytes = 1; 00242 00243 urldata += prefixLen; 00244 sizeofURLData -= prefixLen; 00245 break; 00246 } 00247 } 00248 00249 return encodedBytes; 00250 } 00251 00252 /* 00253 * Encode the URI Suffix to a single byte if possible. 00254 */ 00255 size_t encodeURI(const char *urldata, size_t sizeofURLData) { 00256 /* These are the URI suffixes that can be abbreviated. */ 00257 const char *suffixes[] = { 00258 ".com/", 00259 ".org/", 00260 ".edu/", 00261 ".net/", 00262 ".info/", 00263 ".biz/", 00264 ".gov/", 00265 ".com", 00266 ".org", 00267 ".edu", 00268 ".net", 00269 ".info", 00270 ".biz", 00271 ".gov" 00272 }; 00273 const size_t NUM_SUFFIXES = sizeof(suffixes) / sizeof(char *); 00274 00275 size_t encodedBytes = 0; 00276 while (sizeofURLData && (payloadIndex < MAX_SIZEOF_SERVICE_DATA_PAYLOAD)) { 00277 /* check for suffix match */ 00278 unsigned i; 00279 for (i = 0; i < NUM_SUFFIXES; i++) { 00280 size_t suffixLen = strlen(suffixes[i]); 00281 if ((suffixLen == 0) || (sizeofURLData < suffixLen)) { 00282 continue; 00283 } 00284 00285 if (strncmp(urldata, suffixes[i], suffixLen) == 0) { 00286 serviceDataPayload[payloadIndex++] = i; 00287 ++encodedBytes; 00288 urldata += suffixLen; 00289 sizeofURLData -= suffixLen; 00290 break; /* from the for loop for checking against suffixes */ 00291 } 00292 } 00293 /* This is the default case where we've got an ordinary character which doesn't match a suffix. */ 00294 if (i == NUM_SUFFIXES) { 00295 serviceDataPayload[payloadIndex++] = *urldata; 00296 ++encodedBytes; 00297 ++urldata; 00298 --sizeofURLData; 00299 } 00300 } 00301 if (sizeofURLData == 0) { 00302 initSucceeded = true; 00303 } 00304 00305 return encodedBytes; 00306 } 00307 00308 inline uint16_t getHandle(GattCharacteristic& characteristic) { 00309 return characteristic.getValueAttribute().getHandle(); 00310 } 00311 00312 void onDataWritten(const GattCharacteristicWriteCBParams *params) { 00313 uint16_t handle = params->charHandle; 00314 if (handle == getHandle(uriDataChar)) { 00315 if (lockedState) { /* When locked, the device isn't allowed to update the uriData characteristic. */ 00316 /* Restore GATT database with previous value. */ 00317 updateURIDataCharacteristic(); 00318 return; 00319 } 00320 00321 /* We don't handle very large writes at the moment. */ 00322 if ((params->offset != 0) || (params->len > MAX_SIZE_URI_DATA_CHAR_VALUE)) { 00323 return; 00324 } 00325 00326 uriDataLength = params->len; 00327 memcpy(uriData, params->data, uriDataLength); 00328 } else if (handle == getHandle(flagsChar)) { 00329 if (lockedState) { /* When locked, the device isn't allowed to update the characteristic. */ 00330 /* Restore GATT database with previous value. */ 00331 updateFlagsCharacteristic(); 00332 return; 00333 } else { 00334 flags = *(params->data); 00335 } 00336 } else if (handle == getHandle(txPowerLevelsChar)) { 00337 if (lockedState) { /* When locked, the device isn't allowed to update the characteristic. */ 00338 /* Restore GATT database with previous value. */ 00339 updateTxPowerLevelsCharacteristic(); 00340 return; 00341 } else { 00342 memcpy(powerLevels, params->data, NUM_POWER_MODES * sizeof(int8_t)); 00343 } 00344 } else if (handle == getHandle(txPowerModeChar)) { 00345 if (lockedState) { /* When locked, the device isn't allowed to update the characteristic. */ 00346 /* Restore GATT database with previous value. */ 00347 updateTxPowerModeCharacteristic(); 00348 return; 00349 } else { 00350 txPowerMode = *reinterpret_cast<const TXPowerModes_t *>(params->data); 00351 } 00352 } else if (handle == getHandle(beaconPeriodChar)) { 00353 if (lockedState) { /* When locked, the device isn't allowed to update the characteristic. */ 00354 /* Restore GATT database with previous value. */ 00355 updateBeaconPeriodCharacteristic(); 00356 return; 00357 } else { 00358 beaconPeriod = *((uint16_t *)(params->data)); 00359 } 00360 } else if (handle == getHandle(resetChar)) { 00361 resetDefaults(); 00362 } 00363 configureGAP(); 00364 ble.setAdvertisingPayload(); 00365 } 00366 00367 /* 00368 * Reset the default values. 00369 */ 00370 void resetDefaults(void) { 00371 lockedState = false; 00372 uriDataLength = 0; 00373 memset(uriData, 0, MAX_SIZE_URI_DATA_CHAR_VALUE); 00374 flags = 0; 00375 memcpy(powerLevels, defaultPowerLevels, sizeof(powerLevels)); 00376 txPowerMode = TX_POWER_MODE_LOW ; 00377 beaconPeriod = 0; 00378 00379 updateGATT(); 00380 } 00381 00382 /* 00383 * Internal helper function used to update the GATT database following any 00384 * change to the internal state of the service object. 00385 */ 00386 void updateGATT(void) { 00387 updateLockedStateCharacteristic(); 00388 updateURIDataCharacteristic(); 00389 updateFlagsCharacteristic(); 00390 updateBeaconPeriodCharacteristic(); 00391 updateTxPowerLevelsCharacteristic(); 00392 updateTxPowerModeCharacteristic(); 00393 } 00394 00395 void updateLockedStateCharacteristic(void) { 00396 ble.updateCharacteristicValue (getHandle(lockedStateChar), reinterpret_cast<uint8_t *>(&lockedState), sizeof(lockedState)); 00397 } 00398 00399 void updateURIDataCharacteristic(void) { 00400 ble.updateCharacteristicValue (getHandle(uriDataChar), uriData, uriDataLength); 00401 } 00402 00403 void updateFlagsCharacteristic(void) { 00404 ble.updateCharacteristicValue (getHandle(flagsChar), &flags, 1 /* size */); 00405 } 00406 00407 void updateBeaconPeriodCharacteristic(void) { 00408 ble.updateCharacteristicValue (getHandle(beaconPeriodChar), reinterpret_cast<uint8_t *>(&beaconPeriod), sizeof(uint16_t)); 00409 } 00410 00411 void updateTxPowerModeCharacteristic(void) { 00412 ble.updateCharacteristicValue (getHandle(txPowerModeChar), reinterpret_cast<uint8_t *>(&txPowerMode), sizeof(uint8_t)); 00413 } 00414 00415 void updateTxPowerLevelsCharacteristic(void) { 00416 ble.updateCharacteristicValue (txPowerLevelsChar.getValueHandle(), reinterpret_cast<uint8_t *>(powerLevels), NUM_POWER_MODES * sizeof(int8_t)); 00417 } 00418 00419 private: 00420 void uriDataWriteAuthorizationCallback(GattCharacteristicWriteAuthCBParams *params) { 00421 if (lockedState || (params->offset != 0) || (params->len > MAX_SIZE_URI_DATA_CHAR_VALUE)) { 00422 params->authorizationReply = false; 00423 } 00424 } 00425 00426 void falgsAuthorizationCallback(GattCharacteristicWriteAuthCBParams *params) { 00427 if (lockedState || ((*(params->data) & 0xFE) != 0)) { 00428 params->authorizationReply = false; 00429 } 00430 } 00431 00432 void denyGATTWritesIfLocked(GattCharacteristicWriteAuthCBParams *params) { 00433 if (lockedState) { 00434 params->authorizationReply = false; 00435 } 00436 } 00437 00438 private: 00439 /** 00440 * For debugging only. Print Hex representation of ServiceDataPayload to the console. 00441 */ 00442 void dumpEncodedSeviceData() const { 00443 printf("encoded: '"); 00444 for (unsigned i = 0; i < payloadIndex; i++) { 00445 printf(" %02x", serviceDataPayload[i]); 00446 } 00447 printf("'\r\n"); 00448 } 00449 00450 private: 00451 static const size_t MAX_SIZEOF_SERVICE_DATA_PAYLOAD = 22; /* Uri Data must be between 0 and 18 bytes in length; and 00452 * together with the 4-byte header, the service data must 00453 * fit within 22 bytes. */ 00454 static const size_t MAX_SIZE_URI_DATA_CHAR_VALUE = 48; /* This is chosen arbitrarily. It should be large enough 00455 * to hold any reasonable uncompressed URI. */ 00456 00457 private: 00458 BLEDevice &ble; 00459 00460 size_t payloadIndex; 00461 uint8_t serviceDataPayload[MAX_SIZEOF_SERVICE_DATA_PAYLOAD]; 00462 bool initSucceeded; 00463 00464 bool lockedState; 00465 uint16_t uriDataLength; 00466 uint8_t uriData[MAX_SIZE_URI_DATA_CHAR_VALUE]; 00467 uint8_t flags; 00468 // Current value of AdvertisedPowerLevels 00469 int8_t powerLevels[NUM_POWER_MODES ]; 00470 // Default values to restore on Reset 00471 int8_t defaultPowerLevels[NUM_POWER_MODES ]; 00472 // Firmware power levels used with setTxPower() 00473 int8_t firmwarePowerLevels[NUM_POWER_MODES ]; 00474 TXPowerModes_t txPowerMode; 00475 uint16_t beaconPeriod; 00476 bool resetFlag; 00477 00478 GattCharacteristic lockedStateChar; 00479 GattCharacteristic uriDataChar; 00480 GattCharacteristic flagsChar; 00481 GattCharacteristic txPowerLevelsChar; 00482 GattCharacteristic txPowerModeChar; 00483 GattCharacteristic beaconPeriodChar; 00484 GattCharacteristic resetChar; 00485 }; 00486 00487 #endif /* #ifndef __BLE_URI_BEACON_CONFIG_SERVICE_H__*/
Generated on Tue Jul 12 2022 21:47:54 by
1.7.2
