I don't know why this is happening.

Fork of BLE_API by Bluetooth Low Energy

Committer:
rgrover1
Date:
Fri Jun 19 15:53:01 2015 +0100
Revision:
669:7179b4a5aa7d
Parent:
667:875aecb84719
Child:
670:5e4aecd9af5b
Synchronized with git rev 9bcd7433
Author: Rohit Grover
Rename BLEDevice as BLE. Retain an alias to BLEDevice for the sake of compatibility with old code.

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