I don't know why this is happening.

Fork of BLE_API by Bluetooth Low Energy

Committer:
rgrover1
Date:
Mon Mar 23 16:28:09 2015 +0000
Revision:
325:501ad8b8bbe5
Parent:
323:d87182a62c1b
Child:
403:f17326985627
Synchronized with git rev c7a2b9bb
Author: Rohit Grover
Release 0.3.0
==============

Enhancements
~~~~~~~~~~~~

* BLEDevice::setAdvertisingInterval() uses milliseconds as the unit for its
argument. If advertising interval is set to 0, advertising is stopped. If
advertising interval is smaller than the minimum value supported, then the
minimum value is used instead.

* Add an enumeration called GattCharacteristicAuthCBReply_t to capture a
status code from read/write authorization callback. Previously it used to
return only a bool.

* Add APIs for getMinAdvertisingInterval() and variants.

Bugfixes
~~~~~~~~

* URIBeaconConfigService uses better write-authorization filters. It now
passes tests defined in Google's validator app.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
rgrover1 171:6092e61690dc 1 /* mbed Microcontroller Library
rgrover1 171:6092e61690dc 2 * Copyright (c) 2006-2013 ARM Limited
rgrover1 171:6092e61690dc 3 *
rgrover1 171:6092e61690dc 4 * Licensed under the Apache License, Version 2.0 (the "License");
rgrover1 171:6092e61690dc 5 * you may not use this file except in compliance with the License.
rgrover1 171:6092e61690dc 6 * You may obtain a copy of the License at
rgrover1 171:6092e61690dc 7 *
rgrover1 171:6092e61690dc 8 * http://www.apache.org/licenses/LICENSE-2.0
rgrover1 171:6092e61690dc 9 *
rgrover1 171:6092e61690dc 10 * Unless required by applicable law or agreed to in writing, software
rgrover1 171:6092e61690dc 11 * distributed under the License is distributed on an "AS IS" BASIS,
rgrover1 171:6092e61690dc 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rgrover1 171:6092e61690dc 13 * See the License for the specific language governing permissions and
rgrover1 171:6092e61690dc 14 * limitations under the License.
rgrover1 171:6092e61690dc 15 */
rgrover1 171:6092e61690dc 16
rgrover1 287:cca148e535b1 17 #ifndef SERVICES_URIBEACONCONFIGSERVICE_H_
rgrover1 287:cca148e535b1 18 #define SERVICES_URIBEACONCONFIGSERVICE_H_
rgrover1 171:6092e61690dc 19
rgrover1 314:193908f2b13b 20 #include "BLEDevice.h"
rgrover1 314:193908f2b13b 21 #include "mbed.h"
rgrover1 314:193908f2b13b 22
rgrover1 311:11417b970b12 23 #define UUID_URI_BEACON(FIRST, SECOND) { \
rgrover1 225:f6cbfd817d16 24 0xee, 0x0c, FIRST, SECOND, 0x87, 0x86, 0x40, 0xba, \
rgrover1 225:f6cbfd817d16 25 0xab, 0x96, 0x99, 0xb9, 0x1a, 0xc9, 0x81, 0xd8, \
rgrover1 171:6092e61690dc 26 }
rgrover1 287:cca148e535b1 27
rgrover1 287:cca148e535b1 28 static const uint8_t UUID_URI_BEACON_SERVICE[] = UUID_URI_BEACON(0x20, 0x80);
rgrover1 287:cca148e535b1 29 static const uint8_t UUID_LOCK_STATE_CHAR[] = UUID_URI_BEACON(0x20, 0x81);
rgrover1 287:cca148e535b1 30 static const uint8_t UUID_LOCK_CHAR[] = UUID_URI_BEACON(0x20, 0x82);
rgrover1 287:cca148e535b1 31 static const uint8_t UUID_UNLOCK_CHAR[] = UUID_URI_BEACON(0x20, 0x83);
rgrover1 287:cca148e535b1 32 static const uint8_t UUID_URI_DATA_CHAR[] = UUID_URI_BEACON(0x20, 0x84);
rgrover1 287:cca148e535b1 33 static const uint8_t UUID_FLAGS_CHAR[] = UUID_URI_BEACON(0x20, 0x85);
rgrover1 287:cca148e535b1 34 static const uint8_t UUID_ADV_POWER_LEVELS_CHAR[] = UUID_URI_BEACON(0x20, 0x86);
rgrover1 287:cca148e535b1 35 static const uint8_t UUID_TX_POWER_MODE_CHAR[] = UUID_URI_BEACON(0x20, 0x87);
rgrover1 287:cca148e535b1 36 static const uint8_t UUID_BEACON_PERIOD_CHAR[] = UUID_URI_BEACON(0x20, 0x88);
rgrover1 287:cca148e535b1 37 static const uint8_t UUID_RESET_CHAR[] = UUID_URI_BEACON(0x20, 0x89);
rgrover1 287:cca148e535b1 38 static const uint8_t BEACON_UUID[] = {0xD8, 0xFE};
rgrover1 171:6092e61690dc 39
mbedAustin 231:1c4a4fd961a5 40 /**
mbedAustin 231:1c4a4fd961a5 41 * @class URIBeaconConfigService
rgrover1 242:0e9201b67e2f 42 * @brief UriBeacon Configuration Service. Can be used to set URL, adjust power levels, and set flags.
rgrover1 287:cca148e535b1 43 * See http://uribeacon.org
rgrover1 287:cca148e535b1 44 *
mbedAustin 231:1c4a4fd961a5 45 */
rgrover1 207:e88130dc254c 46 class URIBeaconConfigService {
rgrover1 287:cca148e535b1 47 public:
mbedAustin 231:1c4a4fd961a5 48 /**
rgrover1 242:0e9201b67e2f 49 * @brief Transmission Power Modes for UriBeacon
rgrover1 242:0e9201b67e2f 50 */
rgrover1 287:cca148e535b1 51 static const uint8_t TX_POWER_MODE_LOWEST = 0; /*!< Lowest TX power mode */
rgrover1 287:cca148e535b1 52 static const uint8_t TX_POWER_MODE_LOW = 1; /*!< Low TX power mode */
rgrover1 287:cca148e535b1 53 static const uint8_t TX_POWER_MODE_MEDIUM = 2; /*!< Medium TX power mode */
rgrover1 287:cca148e535b1 54 static const uint8_t TX_POWER_MODE_HIGH = 3; /*!< High TX power mode */
rgrover1 287:cca148e535b1 55 static const unsigned int NUM_POWER_MODES = 4; /*!< Number of Power Modes defined */
rgrover1 287:cca148e535b1 56
rgrover1 309:bc91f7ba346d 57 static const int ADVERTISING_INTERVAL_MSEC = 1000; // Advertising interval for config service.
rgrover1 309:bc91f7ba346d 58 static const int SERVICE_DATA_MAX = 31; // Maximum size of service data in ADV packets
rgrover1 287:cca148e535b1 59
rgrover1 287:cca148e535b1 60 typedef uint8_t Lock_t[16]; /* 128 bits */
rgrover1 287:cca148e535b1 61 typedef int8_t PowerLevels_t[NUM_POWER_MODES];
rgrover1 287:cca148e535b1 62
rgrover1 287:cca148e535b1 63 static const int URI_DATA_MAX = 18;
rgrover1 287:cca148e535b1 64 typedef uint8_t UriData_t[URI_DATA_MAX];
rgrover1 287:cca148e535b1 65
rgrover1 287:cca148e535b1 66 struct Params_t {
rgrover1 311:11417b970b12 67 Lock_t lock;
rgrover1 311:11417b970b12 68 uint8_t uriDataLength;
rgrover1 311:11417b970b12 69 UriData_t uriData;
rgrover1 311:11417b970b12 70 uint8_t flags;
rgrover1 311:11417b970b12 71 PowerLevels_t advPowerLevels; // Current value of AdvertisedPowerLevels
rgrover1 311:11417b970b12 72 uint8_t txPowerMode; // Firmware power levels used with setTxPower()
rgrover1 311:11417b970b12 73 uint16_t beaconPeriod;
rgrover1 175:4e85f7225f8f 74 };
rgrover1 175:4e85f7225f8f 75
rgrover1 181:bbb6ce1082c3 76 /**
rgrover1 287:cca148e535b1 77 * @param[ref] ble
rgrover1 287:cca148e535b1 78 * BLEDevice object for the underlying controller.
rgrover1 287:cca148e535b1 79 * @param[in/out] paramsIn
rgrover1 287:cca148e535b1 80 * Reference to application-visible beacon state, loaded
rgrover1 287:cca148e535b1 81 * from persistent storage at startup.
rgrover1 321:6330d9b08295 82 * @paramsP[in] resetToDefaultsFlag
rgrover1 321:6330d9b08295 83 * Applies to the state of the 'paramsIn' parameter.
rgrover1 321:6330d9b08295 84 * If true, it indicates that paramsIn is potentially
rgrover1 321:6330d9b08295 85 * un-initialized, and default values should be used
rgrover1 321:6330d9b08295 86 * instead. Otherwise, paramsIn overrides the defaults.
rgrover1 287:cca148e535b1 87 * @param[in] defaultUriDataIn
rgrover1 309:bc91f7ba346d 88 * Default un-encoded URI; applies only if the resetToDefaultsFlag is true.
rgrover1 287:cca148e535b1 89 * @param[in] defaultAdvPowerLevelsIn
rgrover1 287:cca148e535b1 90 * Default power-levels array; applies only if the resetToDefaultsFlag is true.
rgrover1 181:bbb6ce1082c3 91 */
rgrover1 287:cca148e535b1 92 URIBeaconConfigService(BLEDevice &bleIn,
rgrover1 287:cca148e535b1 93 Params_t &paramsIn,
rgrover1 321:6330d9b08295 94 bool resetToDefaultsFlag,
rgrover1 309:bc91f7ba346d 95 const char *defaultURIDataIn,
rgrover1 287:cca148e535b1 96 PowerLevels_t &defaultAdvPowerLevelsIn) :
rgrover1 192:3fd3dcf49005 97 ble(bleIn),
rgrover1 287:cca148e535b1 98 params(paramsIn),
rgrover1 309:bc91f7ba346d 99 defaultUriDataLength(),
rgrover1 309:bc91f7ba346d 100 defaultUriData(),
rgrover1 287:cca148e535b1 101 defaultAdvPowerLevels(defaultAdvPowerLevelsIn),
rgrover1 192:3fd3dcf49005 102 initSucceeded(false),
rgrover1 309:bc91f7ba346d 103 resetFlag(),
rgrover1 287:cca148e535b1 104 lockedStateChar(UUID_LOCK_STATE_CHAR, &lockedState),
rgrover1 287:cca148e535b1 105 lockChar(UUID_LOCK_CHAR, &params.lock),
rgrover1 300:d9a39f759a6a 106 uriDataChar(UUID_URI_DATA_CHAR, params.uriData, 0, URI_DATA_MAX,
rgrover1 287:cca148e535b1 107 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE),
rgrover1 287:cca148e535b1 108 unlockChar(UUID_UNLOCK_CHAR, &params.lock),
rgrover1 287:cca148e535b1 109 flagsChar(UUID_FLAGS_CHAR, &params.flags),
rgrover1 287:cca148e535b1 110 advPowerLevelsChar(UUID_ADV_POWER_LEVELS_CHAR, &params.advPowerLevels),
rgrover1 287:cca148e535b1 111 txPowerModeChar(UUID_TX_POWER_MODE_CHAR, &params.txPowerMode),
rgrover1 287:cca148e535b1 112 beaconPeriodChar(UUID_BEACON_PERIOD_CHAR, &params.beaconPeriod),
rgrover1 287:cca148e535b1 113 resetChar(UUID_RESET_CHAR, &resetFlag) {
rgrover1 309:bc91f7ba346d 114
rgrover1 309:bc91f7ba346d 115 encodeURI(defaultURIDataIn, defaultUriData, defaultUriDataLength);
rgrover1 309:bc91f7ba346d 116 if (defaultUriDataLength > URI_DATA_MAX) {
rgrover1 192:3fd3dcf49005 117 return;
rgrover1 192:3fd3dcf49005 118 }
rgrover1 192:3fd3dcf49005 119
rgrover1 312:e2e52a7477bf 120 if (!resetToDefaultsFlag && (params.uriDataLength > URI_DATA_MAX)) {
rgrover1 287:cca148e535b1 121 resetToDefaultsFlag = true;
rgrover1 218:8ae02569fab9 122 }
rgrover1 287:cca148e535b1 123 if (resetToDefaultsFlag) {
rgrover1 287:cca148e535b1 124 resetToDefaults();
rgrover1 287:cca148e535b1 125 } else {
rgrover1 287:cca148e535b1 126 updateCharacteristicValues();
rgrover1 287:cca148e535b1 127 }
rgrover1 274:f540619754bb 128
rgrover1 321:6330d9b08295 129 lockedState = isLocked();
rgrover1 321:6330d9b08295 130
rgrover1 272:65b9d9c87ed1 131 lockChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::lockAuthorizationCallback);
rgrover1 272:65b9d9c87ed1 132 unlockChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::unlockAuthorizationCallback);
rgrover1 250:6862d374e613 133 uriDataChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::uriDataWriteAuthorizationCallback);
rgrover1 325:501ad8b8bbe5 134 flagsChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::basicAuthorizationCallback<uint8_t>);
rgrover1 325:501ad8b8bbe5 135 advPowerLevelsChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::basicAuthorizationCallback<PowerLevels_t>);
rgrover1 325:501ad8b8bbe5 136 txPowerModeChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::powerModeAuthorizationCallback);
rgrover1 325:501ad8b8bbe5 137 beaconPeriodChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::basicAuthorizationCallback<uint16_t>);
rgrover1 325:501ad8b8bbe5 138 resetChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::basicAuthorizationCallback<uint8_t>);
rgrover1 250:6862d374e613 139
rgrover1 287:cca148e535b1 140 static GattCharacteristic *charTable[] = {
rgrover1 287:cca148e535b1 141 &lockedStateChar, &lockChar, &unlockChar, &uriDataChar,
rgrover1 287:cca148e535b1 142 &flagsChar, &advPowerLevelsChar, &txPowerModeChar, &beaconPeriodChar, &resetChar
rgrover1 287:cca148e535b1 143 };
rgrover1 182:d16f8c11816b 144
rgrover1 287:cca148e535b1 145 GattService configService(UUID_URI_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
rgrover1 287:cca148e535b1 146
rgrover1 287:cca148e535b1 147 ble.addService(configService);
rgrover1 287:cca148e535b1 148 ble.onDataWritten(this, &URIBeaconConfigService::onDataWrittenCallback);
rgrover1 287:cca148e535b1 149
rgrover1 318:fe467afccd02 150 setupURIBeaconConfigAdvertisements(); /* Setup advertising for the configService. */
rgrover1 309:bc91f7ba346d 151
rgrover1 287:cca148e535b1 152 initSucceeded = true;
rgrover1 192:3fd3dcf49005 153 }
rgrover1 192:3fd3dcf49005 154
rgrover1 193:ac2feceb7e87 155 bool configuredSuccessfully(void) const {
rgrover1 192:3fd3dcf49005 156 return initSucceeded;
rgrover1 171:6092e61690dc 157 }
rgrover1 171:6092e61690dc 158
rgrover1 309:bc91f7ba346d 159 /* Start out by advertising the configService for a limited time after
rgrover1 309:bc91f7ba346d 160 * startup; and switch to the normal non-connectible beacon functionality
rgrover1 309:bc91f7ba346d 161 * afterwards. */
rgrover1 313:c4599a1aba23 162 void setupURIBeaconConfigAdvertisements()
rgrover1 309:bc91f7ba346d 163 {
rgrover1 319:3782a0d47b00 164 const char DEVICE_NAME[] = "mUriBeacon Config";
rgrover1 309:bc91f7ba346d 165
rgrover1 309:bc91f7ba346d 166 ble.clearAdvertisingPayload();
rgrover1 309:bc91f7ba346d 167
rgrover1 309:bc91f7ba346d 168 ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
rgrover1 309:bc91f7ba346d 169
rgrover1 309:bc91f7ba346d 170 // UUID is in different order in the ADV frame (!)
rgrover1 309:bc91f7ba346d 171 uint8_t reversedServiceUUID[sizeof(UUID_URI_BEACON_SERVICE)];
rgrover1 309:bc91f7ba346d 172 for (unsigned int i = 0; i < sizeof(UUID_URI_BEACON_SERVICE); i++) {
rgrover1 322:28cd17c1b6dd 173 reversedServiceUUID[i] = UUID_URI_BEACON_SERVICE[sizeof(UUID_URI_BEACON_SERVICE) - i - 1];
rgrover1 309:bc91f7ba346d 174 }
rgrover1 309:bc91f7ba346d 175 ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, reversedServiceUUID, sizeof(reversedServiceUUID));
rgrover1 309:bc91f7ba346d 176 ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG);
rgrover1 319:3782a0d47b00 177 ble.accumulateScanResponse(GapAdvertisingData::COMPLETE_LOCAL_NAME, reinterpret_cast<const uint8_t *>(&DEVICE_NAME), sizeof(DEVICE_NAME));
rgrover1 309:bc91f7ba346d 178 ble.accumulateScanResponse(
rgrover1 309:bc91f7ba346d 179 GapAdvertisingData::TX_POWER_LEVEL,
rgrover1 322:28cd17c1b6dd 180 reinterpret_cast<uint8_t *>(&defaultAdvPowerLevels[URIBeaconConfigService::TX_POWER_MODE_LOW]),
rgrover1 309:bc91f7ba346d 181 sizeof(uint8_t));
rgrover1 309:bc91f7ba346d 182
rgrover1 314:193908f2b13b 183 ble.setTxPower(params.advPowerLevels[params.txPowerMode]);
rgrover1 319:3782a0d47b00 184 ble.setDeviceName(reinterpret_cast<const uint8_t *>(&DEVICE_NAME));
rgrover1 309:bc91f7ba346d 185 ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
rgrover1 309:bc91f7ba346d 186 ble.setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(ADVERTISING_INTERVAL_MSEC));
rgrover1 309:bc91f7ba346d 187 }
rgrover1 309:bc91f7ba346d 188
rgrover1 313:c4599a1aba23 189 /* Helper function to switch to the non-connectible normal mode for URIBeacon. This gets called after a timeout. */
rgrover1 313:c4599a1aba23 190 void setupURIBeaconAdvertisements()
rgrover1 309:bc91f7ba346d 191 {
rgrover1 314:193908f2b13b 192 uint8_t serviceData[SERVICE_DATA_MAX];
rgrover1 320:c39b901dca81 193 unsigned serviceDataLen = 0;
rgrover1 309:bc91f7ba346d 194
rgrover1 314:193908f2b13b 195 /* Reinitialize the BLE stack. This will clear away the existing services and advertising state. */
rgrover1 309:bc91f7ba346d 196 ble.shutdown();
rgrover1 309:bc91f7ba346d 197 ble.init();
rgrover1 309:bc91f7ba346d 198
rgrover1 309:bc91f7ba346d 199 // Fields from the Service
rgrover1 320:c39b901dca81 200 unsigned beaconPeriod = params.beaconPeriod;
rgrover1 320:c39b901dca81 201 unsigned txPowerMode = params.txPowerMode;
rgrover1 320:c39b901dca81 202 unsigned uriDataLength = params.uriDataLength;
rgrover1 314:193908f2b13b 203 URIBeaconConfigService::UriData_t &uriData = params.uriData;
rgrover1 314:193908f2b13b 204 URIBeaconConfigService::PowerLevels_t &advPowerLevels = params.advPowerLevels;
rgrover1 314:193908f2b13b 205 uint8_t flags = params.flags;
rgrover1 309:bc91f7ba346d 206
rgrover1 323:d87182a62c1b 207 extern void saveURIBeaconConfigParams(const Params_t *paramsP); /* forward declaration; necessary to avoid a circular dependency. */
rgrover1 314:193908f2b13b 208 saveURIBeaconConfigParams(&params);
rgrover1 309:bc91f7ba346d 209
rgrover1 314:193908f2b13b 210 ble.clearAdvertisingPayload();
rgrover1 314:193908f2b13b 211 ble.setTxPower(params.advPowerLevels[params.txPowerMode]);
rgrover1 314:193908f2b13b 212 ble.setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
rgrover1 325:501ad8b8bbe5 213 ble.setAdvertisingInterval(beaconPeriod);
rgrover1 314:193908f2b13b 214 ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
rgrover1 314:193908f2b13b 215 ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, BEACON_UUID, sizeof(BEACON_UUID));
rgrover1 309:bc91f7ba346d 216
rgrover1 314:193908f2b13b 217 serviceData[serviceDataLen++] = BEACON_UUID[0];
rgrover1 314:193908f2b13b 218 serviceData[serviceDataLen++] = BEACON_UUID[1];
rgrover1 314:193908f2b13b 219 serviceData[serviceDataLen++] = flags;
rgrover1 314:193908f2b13b 220 serviceData[serviceDataLen++] = advPowerLevels[txPowerMode];
rgrover1 320:c39b901dca81 221 for (unsigned j = 0; j < uriDataLength; j++) {
rgrover1 314:193908f2b13b 222 serviceData[serviceDataLen++] = uriData[j];
rgrover1 314:193908f2b13b 223 }
rgrover1 314:193908f2b13b 224 ble.accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, serviceData, serviceDataLen);
rgrover1 309:bc91f7ba346d 225 }
rgrover1 287:cca148e535b1 226
rgrover1 287:cca148e535b1 227 private:
rgrover1 287:cca148e535b1 228 // True if the lock bits are non-zero
rgrover1 287:cca148e535b1 229 bool isLocked() {
rgrover1 287:cca148e535b1 230 Lock_t testLock;
rgrover1 287:cca148e535b1 231 memset(testLock, 0, sizeof(Lock_t));
rgrover1 287:cca148e535b1 232 return memcmp(params.lock, testLock, sizeof(Lock_t));
rgrover1 171:6092e61690dc 233 }
rgrover1 171:6092e61690dc 234
rgrover1 287:cca148e535b1 235 /*
rgrover1 287:cca148e535b1 236 * This callback is invoked when a GATT client attempts to modify any of the
rgrover1 287:cca148e535b1 237 * characteristics of this service. Attempts to do so are also applied to
rgrover1 287:cca148e535b1 238 * the internal state of this service object.
rgrover1 286:898ff71b9502 239 */
rgrover1 287:cca148e535b1 240 void onDataWrittenCallback(const GattCharacteristicWriteCBParams *writeParams) {
rgrover1 287:cca148e535b1 241 uint16_t handle = writeParams->charHandle;
rgrover1 286:898ff71b9502 242
rgrover1 287:cca148e535b1 243 if (handle == lockChar.getValueHandle()) {
rgrover1 287:cca148e535b1 244 // Validated earlier
rgrover1 287:cca148e535b1 245 memcpy(params.lock, writeParams->data, sizeof(Lock_t));
rgrover1 287:cca148e535b1 246 // use isLocked() in case bits are being set to all 0's
rgrover1 287:cca148e535b1 247 lockedState = isLocked();
rgrover1 287:cca148e535b1 248 } else if (handle == unlockChar.getValueHandle()) {
rgrover1 287:cca148e535b1 249 // Validated earlier
rgrover1 287:cca148e535b1 250 memset(params.lock, 0, sizeof(Lock_t));
rgrover1 287:cca148e535b1 251 lockedState = false;
rgrover1 287:cca148e535b1 252 } else if (handle == uriDataChar.getValueHandle()) {
rgrover1 287:cca148e535b1 253 params.uriDataLength = writeParams->len;
rgrover1 287:cca148e535b1 254 memcpy(params.uriData, writeParams->data, params.uriDataLength);
rgrover1 287:cca148e535b1 255 } else if (handle == flagsChar.getValueHandle()) {
rgrover1 287:cca148e535b1 256 params.flags = *(writeParams->data);
rgrover1 287:cca148e535b1 257 } else if (handle == advPowerLevelsChar.getValueHandle()) {
rgrover1 287:cca148e535b1 258 memcpy(params.advPowerLevels, writeParams->data, sizeof(PowerLevels_t));
rgrover1 287:cca148e535b1 259 } else if (handle == txPowerModeChar.getValueHandle()) {
rgrover1 287:cca148e535b1 260 params.txPowerMode = *(writeParams->data);
rgrover1 287:cca148e535b1 261 } else if (handle == beaconPeriodChar.getValueHandle()) {
rgrover1 287:cca148e535b1 262 params.beaconPeriod = *((uint16_t *)(writeParams->data));
rgrover1 287:cca148e535b1 263 } else if (handle == resetChar.getValueHandle()) {
rgrover1 287:cca148e535b1 264 resetToDefaults();
rgrover1 185:7cd70497aec8 265 }
rgrover1 185:7cd70497aec8 266 }
rgrover1 185:7cd70497aec8 267
rgrover1 287:cca148e535b1 268 /*
rgrover1 287:cca148e535b1 269 * Reset the default values.
rgrover1 242:0e9201b67e2f 270 */
rgrover1 287:cca148e535b1 271 void resetToDefaults(void) {
rgrover1 320:c39b901dca81 272 lockedState = false;
rgrover1 287:cca148e535b1 273 memset(params.lock, 0, sizeof(Lock_t));
rgrover1 287:cca148e535b1 274 memcpy(params.uriData, defaultUriData, URI_DATA_MAX);
rgrover1 287:cca148e535b1 275 params.uriDataLength = defaultUriDataLength;
rgrover1 287:cca148e535b1 276 params.flags = 0;
rgrover1 287:cca148e535b1 277 memcpy(params.advPowerLevels, defaultAdvPowerLevels, sizeof(PowerLevels_t));
rgrover1 287:cca148e535b1 278 params.txPowerMode = TX_POWER_MODE_LOW;
rgrover1 287:cca148e535b1 279 params.beaconPeriod = 1000;
rgrover1 287:cca148e535b1 280 updateCharacteristicValues();
rgrover1 211:f181effe5de3 281 }
rgrover1 211:f181effe5de3 282
mbedAustin 232:4cfb5b8a4fb9 283 /*
rgrover1 287:cca148e535b1 284 * Internal helper function used to update the GATT database following any
rgrover1 287:cca148e535b1 285 * change to the internal state of the service object.
rgrover1 242:0e9201b67e2f 286 */
rgrover1 287:cca148e535b1 287 void updateCharacteristicValues(void) {
rgrover1 287:cca148e535b1 288 ble.updateCharacteristicValue(lockedStateChar.getValueHandle(), &lockedState, 1);
rgrover1 287:cca148e535b1 289 ble.updateCharacteristicValue(uriDataChar.getValueHandle(), params.uriData, params.uriDataLength);
rgrover1 287:cca148e535b1 290 ble.updateCharacteristicValue(flagsChar.getValueHandle(), &params.flags, 1);
rgrover1 287:cca148e535b1 291 ble.updateCharacteristicValue(beaconPeriodChar.getValueHandle(),
rgrover1 287:cca148e535b1 292 reinterpret_cast<uint8_t *>(&params.beaconPeriod), sizeof(uint16_t));
rgrover1 287:cca148e535b1 293 ble.updateCharacteristicValue(txPowerModeChar.getValueHandle(), &params.txPowerMode, 1);
rgrover1 287:cca148e535b1 294 ble.updateCharacteristicValue(advPowerLevelsChar.getValueHandle(),
rgrover1 287:cca148e535b1 295 reinterpret_cast<uint8_t *>(params.advPowerLevels), sizeof(PowerLevels_t));
rgrover1 287:cca148e535b1 296 }
rgrover1 287:cca148e535b1 297
rgrover1 287:cca148e535b1 298 private:
rgrover1 287:cca148e535b1 299 void lockAuthorizationCallback(GattCharacteristicWriteAuthCBParams *authParams) {
rgrover1 325:501ad8b8bbe5 300 if (lockedState) {
rgrover1 325:501ad8b8bbe5 301 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
rgrover1 325:501ad8b8bbe5 302 } else if (authParams->len != sizeof(Lock_t)) {
rgrover1 325:501ad8b8bbe5 303 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
rgrover1 325:501ad8b8bbe5 304 } else if (authParams->offset != 0) {
rgrover1 325:501ad8b8bbe5 305 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
rgrover1 325:501ad8b8bbe5 306 } else {
rgrover1 325:501ad8b8bbe5 307 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
rgrover1 325:501ad8b8bbe5 308 }
rgrover1 287:cca148e535b1 309 }
rgrover1 287:cca148e535b1 310
rgrover1 287:cca148e535b1 311
rgrover1 287:cca148e535b1 312 void unlockAuthorizationCallback(GattCharacteristicWriteAuthCBParams *authParams) {
rgrover1 325:501ad8b8bbe5 313 if (!lockedState) {
rgrover1 325:501ad8b8bbe5 314 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
rgrover1 325:501ad8b8bbe5 315 } else if (authParams->len != sizeof(Lock_t)) {
rgrover1 325:501ad8b8bbe5 316 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
rgrover1 325:501ad8b8bbe5 317 } else if (authParams->offset != 0) {
rgrover1 325:501ad8b8bbe5 318 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
rgrover1 325:501ad8b8bbe5 319 } else if (memcmp(authParams->data, params.lock, sizeof(Lock_t)) != 0) {
rgrover1 325:501ad8b8bbe5 320 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
rgrover1 287:cca148e535b1 321 } else {
rgrover1 325:501ad8b8bbe5 322 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
rgrover1 287:cca148e535b1 323 }
rgrover1 287:cca148e535b1 324 }
rgrover1 287:cca148e535b1 325
rgrover1 287:cca148e535b1 326 void uriDataWriteAuthorizationCallback(GattCharacteristicWriteAuthCBParams *authParams) {
rgrover1 325:501ad8b8bbe5 327 if (lockedState) {
rgrover1 325:501ad8b8bbe5 328 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
rgrover1 325:501ad8b8bbe5 329 } else if (authParams->offset != 0) {
rgrover1 325:501ad8b8bbe5 330 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
rgrover1 325:501ad8b8bbe5 331 } else {
rgrover1 325:501ad8b8bbe5 332 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
rgrover1 287:cca148e535b1 333 }
rgrover1 287:cca148e535b1 334 }
rgrover1 287:cca148e535b1 335
rgrover1 325:501ad8b8bbe5 336 void powerModeAuthorizationCallback(GattCharacteristicWriteAuthCBParams *authParams) {
rgrover1 325:501ad8b8bbe5 337 if (lockedState) {
rgrover1 325:501ad8b8bbe5 338 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
rgrover1 325:501ad8b8bbe5 339 } else if (authParams->len != sizeof(uint8_t)) {
rgrover1 325:501ad8b8bbe5 340 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
rgrover1 325:501ad8b8bbe5 341 } else if (authParams->offset != 0) {
rgrover1 325:501ad8b8bbe5 342 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
rgrover1 325:501ad8b8bbe5 343 } else if (*((uint8_t *)authParams->data) >= NUM_POWER_MODES) {
rgrover1 325:501ad8b8bbe5 344 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
rgrover1 325:501ad8b8bbe5 345 } else {
rgrover1 325:501ad8b8bbe5 346 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
rgrover1 287:cca148e535b1 347 }
rgrover1 287:cca148e535b1 348 }
rgrover1 287:cca148e535b1 349
rgrover1 325:501ad8b8bbe5 350 template <typename T>
rgrover1 325:501ad8b8bbe5 351 void basicAuthorizationCallback(GattCharacteristicWriteAuthCBParams *authParams) {
rgrover1 287:cca148e535b1 352 if (lockedState) {
rgrover1 325:501ad8b8bbe5 353 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
rgrover1 325:501ad8b8bbe5 354 } else if (authParams->len != sizeof(T)) {
rgrover1 325:501ad8b8bbe5 355 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
rgrover1 325:501ad8b8bbe5 356 } else if (authParams->offset != 0) {
rgrover1 325:501ad8b8bbe5 357 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
rgrover1 325:501ad8b8bbe5 358 } else {
rgrover1 325:501ad8b8bbe5 359 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
rgrover1 287:cca148e535b1 360 }
rgrover1 287:cca148e535b1 361 }
rgrover1 287:cca148e535b1 362
rgrover1 287:cca148e535b1 363 BLEDevice &ble;
rgrover1 287:cca148e535b1 364 Params_t &params;
rgrover1 287:cca148e535b1 365 // Default value that is restored on reset
rgrover1 309:bc91f7ba346d 366 size_t defaultUriDataLength;
rgrover1 309:bc91f7ba346d 367 UriData_t defaultUriData;
rgrover1 287:cca148e535b1 368 // Default value that is restored on reset
rgrover1 287:cca148e535b1 369 PowerLevels_t &defaultAdvPowerLevels;
rgrover1 287:cca148e535b1 370 uint8_t lockedState;
rgrover1 287:cca148e535b1 371 bool initSucceeded;
rgrover1 287:cca148e535b1 372 uint8_t resetFlag;
rgrover1 287:cca148e535b1 373
rgrover1 287:cca148e535b1 374 ReadOnlyGattCharacteristic<uint8_t> lockedStateChar;
rgrover1 287:cca148e535b1 375 WriteOnlyGattCharacteristic<Lock_t> lockChar;
rgrover1 287:cca148e535b1 376 GattCharacteristic uriDataChar;
rgrover1 287:cca148e535b1 377 WriteOnlyGattCharacteristic<Lock_t> unlockChar;
rgrover1 287:cca148e535b1 378 ReadWriteGattCharacteristic<uint8_t> flagsChar;
rgrover1 287:cca148e535b1 379 ReadWriteGattCharacteristic<PowerLevels_t> advPowerLevelsChar;
rgrover1 287:cca148e535b1 380 ReadWriteGattCharacteristic<uint8_t> txPowerModeChar;
rgrover1 287:cca148e535b1 381 ReadWriteGattCharacteristic<uint16_t> beaconPeriodChar;
rgrover1 287:cca148e535b1 382 WriteOnlyGattCharacteristic<uint8_t> resetChar;
rgrover1 287:cca148e535b1 383
rgrover1 287:cca148e535b1 384 public:
rgrover1 287:cca148e535b1 385 /*
rgrover1 287:cca148e535b1 386 * Encode a human-readable URI into the binary format defined by URIBeacon spec (https://github.com/google/uribeacon/tree/master/specification).
rgrover1 287:cca148e535b1 387 */
rgrover1 287:cca148e535b1 388 static void encodeURI(const char *uriDataIn, UriData_t uriDataOut, size_t &sizeofURIDataOut) {
rgrover1 284:82fefb5be8ed 389 const char *prefixes[] = {
rgrover1 284:82fefb5be8ed 390 "http://www.",
rgrover1 284:82fefb5be8ed 391 "https://www.",
rgrover1 284:82fefb5be8ed 392 "http://",
rgrover1 284:82fefb5be8ed 393 "https://",
rgrover1 284:82fefb5be8ed 394 "urn:uuid:"
rgrover1 284:82fefb5be8ed 395 };
rgrover1 315:943225af2cf4 396 const size_t NUM_PREFIXES = sizeof(prefixes) / sizeof(char *);
rgrover1 284:82fefb5be8ed 397 const char *suffixes[] = {
rgrover1 284:82fefb5be8ed 398 ".com/",
rgrover1 284:82fefb5be8ed 399 ".org/",
rgrover1 284:82fefb5be8ed 400 ".edu/",
rgrover1 284:82fefb5be8ed 401 ".net/",
rgrover1 284:82fefb5be8ed 402 ".info/",
rgrover1 284:82fefb5be8ed 403 ".biz/",
rgrover1 284:82fefb5be8ed 404 ".gov/",
rgrover1 284:82fefb5be8ed 405 ".com",
rgrover1 284:82fefb5be8ed 406 ".org",
rgrover1 284:82fefb5be8ed 407 ".edu",
rgrover1 284:82fefb5be8ed 408 ".net",
rgrover1 284:82fefb5be8ed 409 ".info",
rgrover1 284:82fefb5be8ed 410 ".biz",
rgrover1 284:82fefb5be8ed 411 ".gov"
rgrover1 284:82fefb5be8ed 412 };
rgrover1 284:82fefb5be8ed 413 const size_t NUM_SUFFIXES = sizeof(suffixes) / sizeof(char *);
rgrover1 317:50643fab3ecf 414
rgrover1 317:50643fab3ecf 415 sizeofURIDataOut = 0;
rgrover1 317:50643fab3ecf 416 memset(uriDataOut, 0, sizeof(UriData_t));
rgrover1 317:50643fab3ecf 417
rgrover1 317:50643fab3ecf 418 if ((uriDataIn == NULL) || (strlen(uriDataIn) == 0)) {
rgrover1 317:50643fab3ecf 419 return;
rgrover1 317:50643fab3ecf 420 }
rgrover1 317:50643fab3ecf 421
rgrover1 317:50643fab3ecf 422 /*
rgrover1 317:50643fab3ecf 423 * handle prefix
rgrover1 317:50643fab3ecf 424 */
rgrover1 317:50643fab3ecf 425 for (unsigned i = 0; i < NUM_PREFIXES; i++) {
rgrover1 317:50643fab3ecf 426 size_t prefixLen = strlen(prefixes[i]);
rgrover1 317:50643fab3ecf 427 if (strncmp(uriDataIn, prefixes[i], prefixLen) == 0) {
rgrover1 317:50643fab3ecf 428 uriDataOut[sizeofURIDataOut++] = i;
rgrover1 317:50643fab3ecf 429 uriDataIn += prefixLen;
rgrover1 317:50643fab3ecf 430 break;
rgrover1 317:50643fab3ecf 431 }
rgrover1 317:50643fab3ecf 432 }
rgrover1 317:50643fab3ecf 433
rgrover1 317:50643fab3ecf 434 /*
rgrover1 317:50643fab3ecf 435 * handle suffixes
rgrover1 317:50643fab3ecf 436 */
rgrover1 287:cca148e535b1 437 while (*uriDataIn && (sizeofURIDataOut < URI_DATA_MAX)) {
rgrover1 284:82fefb5be8ed 438 /* check for suffix match */
rgrover1 284:82fefb5be8ed 439 unsigned i;
rgrover1 284:82fefb5be8ed 440 for (i = 0; i < NUM_SUFFIXES; i++) {
rgrover1 284:82fefb5be8ed 441 size_t suffixLen = strlen(suffixes[i]);
rgrover1 287:cca148e535b1 442 if (strncmp(uriDataIn, suffixes[i], suffixLen) == 0) {
rgrover1 315:943225af2cf4 443 uriDataOut[sizeofURIDataOut++] = i;
rgrover1 315:943225af2cf4 444 uriDataIn += suffixLen;
rgrover1 284:82fefb5be8ed 445 break; /* from the for loop for checking against suffixes */
rgrover1 284:82fefb5be8ed 446 }
rgrover1 284:82fefb5be8ed 447 }
rgrover1 284:82fefb5be8ed 448 /* This is the default case where we've got an ordinary character which doesn't match a suffix. */
rgrover1 284:82fefb5be8ed 449 if (i == NUM_SUFFIXES) {
rgrover1 287:cca148e535b1 450 uriDataOut[sizeofURIDataOut++] = *uriDataIn;
rgrover1 287:cca148e535b1 451 ++uriDataIn;
rgrover1 284:82fefb5be8ed 452 }
rgrover1 284:82fefb5be8ed 453 }
rgrover1 284:82fefb5be8ed 454 }
rgrover1 171:6092e61690dc 455 };
rgrover1 171:6092e61690dc 456
rgrover1 287:cca148e535b1 457 #endif // SERVICES_URIBEACONCONFIGSERVICE_H_