Bill Schilit / BLE_API

Fork of BLE_API by Bluetooth Low Energy

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers URIBeaconConfigService.h Source File

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__*/