I don't know why this is happening.

Fork of BLE_API by Bluetooth Low Energy

Committer:
rgrover1
Date:
Fri Mar 13 09:57:57 2015 +0000
Revision:
318:fe467afccd02
Parent:
317:50643fab3ecf
Child:
319:3782a0d47b00
Synchronized with git rev 41e18622
Author: Rohit Grover
URIBeaconConfigService: Pull out the advertisement switch from the 'service' class.
This can be left in main.cpp for the application developer.

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 312:e2e52a7477bf 74 uint32_t persistenceSignature; /* This isn't really a parameter, but having the expected magic value in
rgrover1 312:e2e52a7477bf 75 * this field indicates persistence. */
rgrover1 312:e2e52a7477bf 76 static const uint32_t MAGIC = 0x1BEAC000; // Magic that identifies persistence
rgrover1 175:4e85f7225f8f 77 };
rgrover1 175:4e85f7225f8f 78
rgrover1 181:bbb6ce1082c3 79 /**
rgrover1 287:cca148e535b1 80 * @param[ref] ble
rgrover1 287:cca148e535b1 81 * BLEDevice object for the underlying controller.
rgrover1 287:cca148e535b1 82 * @param[in/out] paramsIn
rgrover1 287:cca148e535b1 83 * Reference to application-visible beacon state, loaded
rgrover1 287:cca148e535b1 84 * from persistent storage at startup.
rgrover1 287:cca148e535b1 85 * @param[in] defaultUriDataIn
rgrover1 309:bc91f7ba346d 86 * Default un-encoded URI; applies only if the resetToDefaultsFlag is true.
rgrover1 287:cca148e535b1 87 * @param[in] defaultAdvPowerLevelsIn
rgrover1 287:cca148e535b1 88 * Default power-levels array; applies only if the resetToDefaultsFlag is true.
rgrover1 181:bbb6ce1082c3 89 */
rgrover1 287:cca148e535b1 90 URIBeaconConfigService(BLEDevice &bleIn,
rgrover1 287:cca148e535b1 91 Params_t &paramsIn,
rgrover1 309:bc91f7ba346d 92 const char *defaultURIDataIn,
rgrover1 287:cca148e535b1 93 PowerLevels_t &defaultAdvPowerLevelsIn) :
rgrover1 192:3fd3dcf49005 94 ble(bleIn),
rgrover1 287:cca148e535b1 95 params(paramsIn),
rgrover1 309:bc91f7ba346d 96 defaultUriDataLength(),
rgrover1 309:bc91f7ba346d 97 defaultUriData(),
rgrover1 287:cca148e535b1 98 defaultAdvPowerLevels(defaultAdvPowerLevelsIn),
rgrover1 192:3fd3dcf49005 99 initSucceeded(false),
rgrover1 309:bc91f7ba346d 100 resetFlag(),
rgrover1 287:cca148e535b1 101 lockedStateChar(UUID_LOCK_STATE_CHAR, &lockedState),
rgrover1 287:cca148e535b1 102 lockChar(UUID_LOCK_CHAR, &params.lock),
rgrover1 300:d9a39f759a6a 103 uriDataChar(UUID_URI_DATA_CHAR, params.uriData, 0, URI_DATA_MAX,
rgrover1 287:cca148e535b1 104 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE),
rgrover1 287:cca148e535b1 105 unlockChar(UUID_UNLOCK_CHAR, &params.lock),
rgrover1 287:cca148e535b1 106 flagsChar(UUID_FLAGS_CHAR, &params.flags),
rgrover1 287:cca148e535b1 107 advPowerLevelsChar(UUID_ADV_POWER_LEVELS_CHAR, &params.advPowerLevels),
rgrover1 287:cca148e535b1 108 txPowerModeChar(UUID_TX_POWER_MODE_CHAR, &params.txPowerMode),
rgrover1 287:cca148e535b1 109 beaconPeriodChar(UUID_BEACON_PERIOD_CHAR, &params.beaconPeriod),
rgrover1 287:cca148e535b1 110 resetChar(UUID_RESET_CHAR, &resetFlag) {
rgrover1 309:bc91f7ba346d 111
rgrover1 309:bc91f7ba346d 112 encodeURI(defaultURIDataIn, defaultUriData, defaultUriDataLength);
rgrover1 309:bc91f7ba346d 113 if (defaultUriDataLength > URI_DATA_MAX) {
rgrover1 192:3fd3dcf49005 114 return;
rgrover1 192:3fd3dcf49005 115 }
rgrover1 192:3fd3dcf49005 116
rgrover1 312:e2e52a7477bf 117 bool resetToDefaultsFlag = params.persistenceSignature != Params_t::MAGIC;
rgrover1 312:e2e52a7477bf 118 if (!resetToDefaultsFlag && (params.uriDataLength > URI_DATA_MAX)) {
rgrover1 287:cca148e535b1 119 resetToDefaultsFlag = true;
rgrover1 218:8ae02569fab9 120 }
rgrover1 218:8ae02569fab9 121
rgrover1 287:cca148e535b1 122 lockedState = isLocked();
rgrover1 171:6092e61690dc 123
rgrover1 287:cca148e535b1 124 if (resetToDefaultsFlag) {
rgrover1 287:cca148e535b1 125 resetToDefaults();
rgrover1 287:cca148e535b1 126 } else {
rgrover1 287:cca148e535b1 127 updateCharacteristicValues();
rgrover1 287:cca148e535b1 128 }
rgrover1 274:f540619754bb 129
rgrover1 272:65b9d9c87ed1 130 lockChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::lockAuthorizationCallback);
rgrover1 272:65b9d9c87ed1 131 unlockChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::unlockAuthorizationCallback);
rgrover1 250:6862d374e613 132 uriDataChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::uriDataWriteAuthorizationCallback);
rgrover1 255:cdb7231f83df 133 flagsChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::flagsAuthorizationCallback);
rgrover1 287:cca148e535b1 134 advPowerLevelsChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::denyGATTWritesIfLocked);
rgrover1 287:cca148e535b1 135 txPowerModeChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::denyGATTWritesIfLocked);
rgrover1 250:6862d374e613 136 beaconPeriodChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::denyGATTWritesIfLocked);
rgrover1 250:6862d374e613 137 resetChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::denyGATTWritesIfLocked);
rgrover1 250:6862d374e613 138
rgrover1 287:cca148e535b1 139 static GattCharacteristic *charTable[] = {
rgrover1 287:cca148e535b1 140 &lockedStateChar, &lockChar, &unlockChar, &uriDataChar,
rgrover1 287:cca148e535b1 141 &flagsChar, &advPowerLevelsChar, &txPowerModeChar, &beaconPeriodChar, &resetChar
rgrover1 287:cca148e535b1 142 };
rgrover1 182:d16f8c11816b 143
rgrover1 287:cca148e535b1 144 GattService configService(UUID_URI_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
rgrover1 287:cca148e535b1 145
rgrover1 287:cca148e535b1 146 ble.addService(configService);
rgrover1 287:cca148e535b1 147 ble.onDataWritten(this, &URIBeaconConfigService::onDataWrittenCallback);
rgrover1 287:cca148e535b1 148
rgrover1 318:fe467afccd02 149 setupURIBeaconConfigAdvertisements(); /* Setup advertising for the configService. */
rgrover1 309:bc91f7ba346d 150
rgrover1 287:cca148e535b1 151 initSucceeded = true;
rgrover1 192:3fd3dcf49005 152 }
rgrover1 192:3fd3dcf49005 153
rgrover1 193:ac2feceb7e87 154 bool configuredSuccessfully(void) const {
rgrover1 192:3fd3dcf49005 155 return initSucceeded;
rgrover1 171:6092e61690dc 156 }
rgrover1 171:6092e61690dc 157
rgrover1 309:bc91f7ba346d 158 /* Start out by advertising the configService for a limited time after
rgrover1 309:bc91f7ba346d 159 * startup; and switch to the normal non-connectible beacon functionality
rgrover1 309:bc91f7ba346d 160 * afterwards. */
rgrover1 313:c4599a1aba23 161 void setupURIBeaconConfigAdvertisements()
rgrover1 309:bc91f7ba346d 162 {
rgrover1 309:bc91f7ba346d 163 char DEVICE_NAME[] = "mUriBeacon Config";
rgrover1 309:bc91f7ba346d 164
rgrover1 309:bc91f7ba346d 165 ble.clearAdvertisingPayload();
rgrover1 309:bc91f7ba346d 166
rgrover1 309:bc91f7ba346d 167 ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
rgrover1 309:bc91f7ba346d 168
rgrover1 309:bc91f7ba346d 169 // UUID is in different order in the ADV frame (!)
rgrover1 309:bc91f7ba346d 170 uint8_t reversedServiceUUID[sizeof(UUID_URI_BEACON_SERVICE)];
rgrover1 309:bc91f7ba346d 171 for (unsigned int i = 0; i < sizeof(UUID_URI_BEACON_SERVICE); i++) {
rgrover1 309:bc91f7ba346d 172 reversedServiceUUID[i] =
rgrover1 309:bc91f7ba346d 173 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 309:bc91f7ba346d 177 ble.accumulateScanResponse(GapAdvertisingData::COMPLETE_LOCAL_NAME, reinterpret_cast<uint8_t *>(&DEVICE_NAME), sizeof(DEVICE_NAME));
rgrover1 309:bc91f7ba346d 178 ble.accumulateScanResponse(
rgrover1 309:bc91f7ba346d 179 GapAdvertisingData::TX_POWER_LEVEL,
rgrover1 309:bc91f7ba346d 180 reinterpret_cast<uint8_t *>(
rgrover1 309:bc91f7ba346d 181 &defaultAdvPowerLevels[URIBeaconConfigService::TX_POWER_MODE_LOW]),
rgrover1 309:bc91f7ba346d 182 sizeof(uint8_t));
rgrover1 309:bc91f7ba346d 183
rgrover1 314:193908f2b13b 184 ble.setTxPower(params.advPowerLevels[params.txPowerMode]);
rgrover1 309:bc91f7ba346d 185 ble.setDeviceName(reinterpret_cast<uint8_t *>(&DEVICE_NAME));
rgrover1 309:bc91f7ba346d 186 ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
rgrover1 309:bc91f7ba346d 187 ble.setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(ADVERTISING_INTERVAL_MSEC));
rgrover1 309:bc91f7ba346d 188 }
rgrover1 309:bc91f7ba346d 189
rgrover1 313:c4599a1aba23 190 /* Helper function to switch to the non-connectible normal mode for URIBeacon. This gets called after a timeout. */
rgrover1 313:c4599a1aba23 191 void setupURIBeaconAdvertisements()
rgrover1 309:bc91f7ba346d 192 {
rgrover1 314:193908f2b13b 193 uint8_t serviceData[SERVICE_DATA_MAX];
rgrover1 314:193908f2b13b 194 int serviceDataLen = 0;
rgrover1 309:bc91f7ba346d 195
rgrover1 314:193908f2b13b 196 /* Reinitialize the BLE stack. This will clear away the existing services and advertising state. */
rgrover1 309:bc91f7ba346d 197 ble.shutdown();
rgrover1 309:bc91f7ba346d 198 ble.init();
rgrover1 309:bc91f7ba346d 199
rgrover1 309:bc91f7ba346d 200 // Fields from the Service
rgrover1 314:193908f2b13b 201 int beaconPeriod = params.beaconPeriod;
rgrover1 314:193908f2b13b 202 int txPowerMode = params.txPowerMode;
rgrover1 314:193908f2b13b 203 int uriDataLength = params.uriDataLength;
rgrover1 314:193908f2b13b 204 URIBeaconConfigService::UriData_t &uriData = params.uriData;
rgrover1 314:193908f2b13b 205 URIBeaconConfigService::PowerLevels_t &advPowerLevels = params.advPowerLevels;
rgrover1 314:193908f2b13b 206 uint8_t flags = params.flags;
rgrover1 309:bc91f7ba346d 207
rgrover1 314:193908f2b13b 208 extern void saveURIBeaconConfigParams(Params_t *paramsP); /* forward declaration; necessary to avoid a circular dependency. */
rgrover1 314:193908f2b13b 209 saveURIBeaconConfigParams(&params);
rgrover1 309:bc91f7ba346d 210
rgrover1 314:193908f2b13b 211 ble.clearAdvertisingPayload();
rgrover1 314:193908f2b13b 212 ble.setTxPower(params.advPowerLevels[params.txPowerMode]);
rgrover1 314:193908f2b13b 213 ble.setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
rgrover1 314:193908f2b13b 214 ble.setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(beaconPeriod));
rgrover1 314:193908f2b13b 215 ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
rgrover1 314:193908f2b13b 216 ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, BEACON_UUID, sizeof(BEACON_UUID));
rgrover1 309:bc91f7ba346d 217
rgrover1 314:193908f2b13b 218 serviceData[serviceDataLen++] = BEACON_UUID[0];
rgrover1 314:193908f2b13b 219 serviceData[serviceDataLen++] = BEACON_UUID[1];
rgrover1 314:193908f2b13b 220 serviceData[serviceDataLen++] = flags;
rgrover1 314:193908f2b13b 221 serviceData[serviceDataLen++] = advPowerLevels[txPowerMode];
rgrover1 314:193908f2b13b 222 for (int j=0; j < uriDataLength; j++) {
rgrover1 314:193908f2b13b 223 serviceData[serviceDataLen++] = uriData[j];
rgrover1 314:193908f2b13b 224 }
rgrover1 314:193908f2b13b 225 ble.accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, serviceData, serviceDataLen);
rgrover1 309:bc91f7ba346d 226 }
rgrover1 287:cca148e535b1 227
rgrover1 287:cca148e535b1 228 private:
rgrover1 287:cca148e535b1 229 // True if the lock bits are non-zero
rgrover1 287:cca148e535b1 230 bool isLocked() {
rgrover1 287:cca148e535b1 231 Lock_t testLock;
rgrover1 287:cca148e535b1 232 memset(testLock, 0, sizeof(Lock_t));
rgrover1 287:cca148e535b1 233 return memcmp(params.lock, testLock, sizeof(Lock_t));
rgrover1 171:6092e61690dc 234 }
rgrover1 171:6092e61690dc 235
rgrover1 287:cca148e535b1 236 /*
rgrover1 287:cca148e535b1 237 * This callback is invoked when a GATT client attempts to modify any of the
rgrover1 287:cca148e535b1 238 * characteristics of this service. Attempts to do so are also applied to
rgrover1 287:cca148e535b1 239 * the internal state of this service object.
rgrover1 286:898ff71b9502 240 */
rgrover1 287:cca148e535b1 241 void onDataWrittenCallback(const GattCharacteristicWriteCBParams *writeParams) {
rgrover1 287:cca148e535b1 242 uint16_t handle = writeParams->charHandle;
rgrover1 286:898ff71b9502 243
rgrover1 287:cca148e535b1 244 if (handle == lockChar.getValueHandle()) {
rgrover1 287:cca148e535b1 245 // Validated earlier
rgrover1 287:cca148e535b1 246 memcpy(params.lock, writeParams->data, sizeof(Lock_t));
rgrover1 287:cca148e535b1 247 // use isLocked() in case bits are being set to all 0's
rgrover1 287:cca148e535b1 248 lockedState = isLocked();
rgrover1 287:cca148e535b1 249 } else if (handle == unlockChar.getValueHandle()) {
rgrover1 287:cca148e535b1 250 // Validated earlier
rgrover1 287:cca148e535b1 251 memset(params.lock, 0, sizeof(Lock_t));
rgrover1 287:cca148e535b1 252 lockedState = false;
rgrover1 287:cca148e535b1 253 } else if (handle == uriDataChar.getValueHandle()) {
rgrover1 287:cca148e535b1 254 params.uriDataLength = writeParams->len;
rgrover1 287:cca148e535b1 255 memcpy(params.uriData, writeParams->data, params.uriDataLength);
rgrover1 287:cca148e535b1 256 } else if (handle == flagsChar.getValueHandle()) {
rgrover1 287:cca148e535b1 257 params.flags = *(writeParams->data);
rgrover1 287:cca148e535b1 258 } else if (handle == advPowerLevelsChar.getValueHandle()) {
rgrover1 287:cca148e535b1 259 memcpy(params.advPowerLevels, writeParams->data, sizeof(PowerLevels_t));
rgrover1 287:cca148e535b1 260 } else if (handle == txPowerModeChar.getValueHandle()) {
rgrover1 287:cca148e535b1 261 params.txPowerMode = *(writeParams->data);
rgrover1 287:cca148e535b1 262 } else if (handle == beaconPeriodChar.getValueHandle()) {
rgrover1 287:cca148e535b1 263 params.beaconPeriod = *((uint16_t *)(writeParams->data));
rgrover1 287:cca148e535b1 264 } else if (handle == resetChar.getValueHandle()) {
rgrover1 287:cca148e535b1 265 resetToDefaults();
rgrover1 185:7cd70497aec8 266 }
rgrover1 185:7cd70497aec8 267 }
rgrover1 185:7cd70497aec8 268
rgrover1 287:cca148e535b1 269 /*
rgrover1 287:cca148e535b1 270 * Reset the default values.
rgrover1 242:0e9201b67e2f 271 */
rgrover1 287:cca148e535b1 272 void resetToDefaults(void) {
rgrover1 287:cca148e535b1 273 lockedState = false;
rgrover1 287:cca148e535b1 274 memset(params.lock, 0, sizeof(Lock_t));
rgrover1 287:cca148e535b1 275 memcpy(params.uriData, defaultUriData, URI_DATA_MAX);
rgrover1 287:cca148e535b1 276 params.uriDataLength = defaultUriDataLength;
rgrover1 287:cca148e535b1 277 params.flags = 0;
rgrover1 287:cca148e535b1 278 memcpy(params.advPowerLevels, defaultAdvPowerLevels, sizeof(PowerLevels_t));
rgrover1 287:cca148e535b1 279 params.txPowerMode = TX_POWER_MODE_LOW;
rgrover1 287:cca148e535b1 280 params.beaconPeriod = 1000;
rgrover1 287:cca148e535b1 281 updateCharacteristicValues();
rgrover1 211:f181effe5de3 282 }
rgrover1 211:f181effe5de3 283
mbedAustin 232:4cfb5b8a4fb9 284 /*
rgrover1 287:cca148e535b1 285 * Internal helper function used to update the GATT database following any
rgrover1 287:cca148e535b1 286 * change to the internal state of the service object.
rgrover1 242:0e9201b67e2f 287 */
rgrover1 287:cca148e535b1 288 void updateCharacteristicValues(void) {
rgrover1 287:cca148e535b1 289 ble.updateCharacteristicValue(lockedStateChar.getValueHandle(), &lockedState, 1);
rgrover1 287:cca148e535b1 290 ble.updateCharacteristicValue(uriDataChar.getValueHandle(), params.uriData, params.uriDataLength);
rgrover1 287:cca148e535b1 291 ble.updateCharacteristicValue(flagsChar.getValueHandle(), &params.flags, 1);
rgrover1 287:cca148e535b1 292 ble.updateCharacteristicValue(beaconPeriodChar.getValueHandle(),
rgrover1 287:cca148e535b1 293 reinterpret_cast<uint8_t *>(&params.beaconPeriod), sizeof(uint16_t));
rgrover1 287:cca148e535b1 294 ble.updateCharacteristicValue(txPowerModeChar.getValueHandle(), &params.txPowerMode, 1);
rgrover1 287:cca148e535b1 295 ble.updateCharacteristicValue(advPowerLevelsChar.getValueHandle(),
rgrover1 287:cca148e535b1 296 reinterpret_cast<uint8_t *>(params.advPowerLevels), sizeof(PowerLevels_t));
rgrover1 287:cca148e535b1 297 }
rgrover1 287:cca148e535b1 298
rgrover1 287:cca148e535b1 299 private:
rgrover1 287:cca148e535b1 300 void lockAuthorizationCallback(GattCharacteristicWriteAuthCBParams *authParams) {
rgrover1 287:cca148e535b1 301 authParams->authorizationReply = (authParams->len == sizeof(Lock_t)) && !lockedState;
rgrover1 287:cca148e535b1 302 }
rgrover1 287:cca148e535b1 303
rgrover1 287:cca148e535b1 304
rgrover1 287:cca148e535b1 305 void unlockAuthorizationCallback(GattCharacteristicWriteAuthCBParams *authParams) {
rgrover1 287:cca148e535b1 306 if (!lockedState || (authParams->len == sizeof(Lock_t) && (memcmp(authParams->data, params.lock, sizeof(Lock_t)) == 0))) {
rgrover1 287:cca148e535b1 307 authParams->authorizationReply = true;
rgrover1 287:cca148e535b1 308 } else {
rgrover1 287:cca148e535b1 309 authParams->authorizationReply = false;
rgrover1 287:cca148e535b1 310 }
rgrover1 287:cca148e535b1 311 }
rgrover1 287:cca148e535b1 312
rgrover1 287:cca148e535b1 313 void uriDataWriteAuthorizationCallback(GattCharacteristicWriteAuthCBParams *authParams) {
rgrover1 287:cca148e535b1 314 if (lockedState || (authParams->offset != 0) || (authParams->len > URI_DATA_MAX)) {
rgrover1 287:cca148e535b1 315 authParams->authorizationReply = false;
rgrover1 287:cca148e535b1 316 }
rgrover1 287:cca148e535b1 317 }
rgrover1 287:cca148e535b1 318
rgrover1 287:cca148e535b1 319 void flagsAuthorizationCallback(GattCharacteristicWriteAuthCBParams *authParams) {
rgrover1 287:cca148e535b1 320 if (lockedState || authParams->len != 1) {
rgrover1 287:cca148e535b1 321 authParams->authorizationReply = false;
rgrover1 287:cca148e535b1 322 }
rgrover1 287:cca148e535b1 323 }
rgrover1 287:cca148e535b1 324
rgrover1 287:cca148e535b1 325 void denyGATTWritesIfLocked(GattCharacteristicWriteAuthCBParams *authParams) {
rgrover1 287:cca148e535b1 326 if (lockedState) {
rgrover1 287:cca148e535b1 327 authParams->authorizationReply = false;
rgrover1 287:cca148e535b1 328 }
rgrover1 287:cca148e535b1 329 }
rgrover1 287:cca148e535b1 330
rgrover1 287:cca148e535b1 331 BLEDevice &ble;
rgrover1 287:cca148e535b1 332 Params_t &params;
rgrover1 287:cca148e535b1 333 // Default value that is restored on reset
rgrover1 309:bc91f7ba346d 334 size_t defaultUriDataLength;
rgrover1 309:bc91f7ba346d 335 UriData_t defaultUriData;
rgrover1 287:cca148e535b1 336 // Default value that is restored on reset
rgrover1 287:cca148e535b1 337 PowerLevels_t &defaultAdvPowerLevels;
rgrover1 287:cca148e535b1 338 uint8_t lockedState;
rgrover1 287:cca148e535b1 339 bool initSucceeded;
rgrover1 287:cca148e535b1 340 uint8_t resetFlag;
rgrover1 287:cca148e535b1 341
rgrover1 287:cca148e535b1 342 ReadOnlyGattCharacteristic<uint8_t> lockedStateChar;
rgrover1 287:cca148e535b1 343 WriteOnlyGattCharacteristic<Lock_t> lockChar;
rgrover1 287:cca148e535b1 344 GattCharacteristic uriDataChar;
rgrover1 287:cca148e535b1 345 WriteOnlyGattCharacteristic<Lock_t> unlockChar;
rgrover1 287:cca148e535b1 346 ReadWriteGattCharacteristic<uint8_t> flagsChar;
rgrover1 287:cca148e535b1 347 ReadWriteGattCharacteristic<PowerLevels_t> advPowerLevelsChar;
rgrover1 287:cca148e535b1 348 ReadWriteGattCharacteristic<uint8_t> txPowerModeChar;
rgrover1 287:cca148e535b1 349 ReadWriteGattCharacteristic<uint16_t> beaconPeriodChar;
rgrover1 287:cca148e535b1 350 WriteOnlyGattCharacteristic<uint8_t> resetChar;
rgrover1 287:cca148e535b1 351
rgrover1 287:cca148e535b1 352 public:
rgrover1 287:cca148e535b1 353 /*
rgrover1 287:cca148e535b1 354 * Encode a human-readable URI into the binary format defined by URIBeacon spec (https://github.com/google/uribeacon/tree/master/specification).
rgrover1 287:cca148e535b1 355 */
rgrover1 287:cca148e535b1 356 static void encodeURI(const char *uriDataIn, UriData_t uriDataOut, size_t &sizeofURIDataOut) {
rgrover1 284:82fefb5be8ed 357 const char *prefixes[] = {
rgrover1 284:82fefb5be8ed 358 "http://www.",
rgrover1 284:82fefb5be8ed 359 "https://www.",
rgrover1 284:82fefb5be8ed 360 "http://",
rgrover1 284:82fefb5be8ed 361 "https://",
rgrover1 284:82fefb5be8ed 362 "urn:uuid:"
rgrover1 284:82fefb5be8ed 363 };
rgrover1 315:943225af2cf4 364 const size_t NUM_PREFIXES = sizeof(prefixes) / sizeof(char *);
rgrover1 284:82fefb5be8ed 365 const char *suffixes[] = {
rgrover1 284:82fefb5be8ed 366 ".com/",
rgrover1 284:82fefb5be8ed 367 ".org/",
rgrover1 284:82fefb5be8ed 368 ".edu/",
rgrover1 284:82fefb5be8ed 369 ".net/",
rgrover1 284:82fefb5be8ed 370 ".info/",
rgrover1 284:82fefb5be8ed 371 ".biz/",
rgrover1 284:82fefb5be8ed 372 ".gov/",
rgrover1 284:82fefb5be8ed 373 ".com",
rgrover1 284:82fefb5be8ed 374 ".org",
rgrover1 284:82fefb5be8ed 375 ".edu",
rgrover1 284:82fefb5be8ed 376 ".net",
rgrover1 284:82fefb5be8ed 377 ".info",
rgrover1 284:82fefb5be8ed 378 ".biz",
rgrover1 284:82fefb5be8ed 379 ".gov"
rgrover1 284:82fefb5be8ed 380 };
rgrover1 284:82fefb5be8ed 381 const size_t NUM_SUFFIXES = sizeof(suffixes) / sizeof(char *);
rgrover1 317:50643fab3ecf 382
rgrover1 317:50643fab3ecf 383 sizeofURIDataOut = 0;
rgrover1 317:50643fab3ecf 384 memset(uriDataOut, 0, sizeof(UriData_t));
rgrover1 317:50643fab3ecf 385
rgrover1 317:50643fab3ecf 386 if ((uriDataIn == NULL) || (strlen(uriDataIn) == 0)) {
rgrover1 317:50643fab3ecf 387 return;
rgrover1 317:50643fab3ecf 388 }
rgrover1 317:50643fab3ecf 389
rgrover1 317:50643fab3ecf 390 /*
rgrover1 317:50643fab3ecf 391 * handle prefix
rgrover1 317:50643fab3ecf 392 */
rgrover1 317:50643fab3ecf 393 for (unsigned i = 0; i < NUM_PREFIXES; i++) {
rgrover1 317:50643fab3ecf 394 size_t prefixLen = strlen(prefixes[i]);
rgrover1 317:50643fab3ecf 395 if (strncmp(uriDataIn, prefixes[i], prefixLen) == 0) {
rgrover1 317:50643fab3ecf 396 uriDataOut[sizeofURIDataOut++] = i;
rgrover1 317:50643fab3ecf 397 uriDataIn += prefixLen;
rgrover1 317:50643fab3ecf 398 break;
rgrover1 317:50643fab3ecf 399 }
rgrover1 317:50643fab3ecf 400 }
rgrover1 317:50643fab3ecf 401
rgrover1 317:50643fab3ecf 402 /*
rgrover1 317:50643fab3ecf 403 * handle suffixes
rgrover1 317:50643fab3ecf 404 */
rgrover1 287:cca148e535b1 405 while (*uriDataIn && (sizeofURIDataOut < URI_DATA_MAX)) {
rgrover1 284:82fefb5be8ed 406 /* check for suffix match */
rgrover1 284:82fefb5be8ed 407 unsigned i;
rgrover1 284:82fefb5be8ed 408 for (i = 0; i < NUM_SUFFIXES; i++) {
rgrover1 284:82fefb5be8ed 409 size_t suffixLen = strlen(suffixes[i]);
rgrover1 287:cca148e535b1 410 if (strncmp(uriDataIn, suffixes[i], suffixLen) == 0) {
rgrover1 315:943225af2cf4 411 uriDataOut[sizeofURIDataOut++] = i;
rgrover1 315:943225af2cf4 412 uriDataIn += suffixLen;
rgrover1 284:82fefb5be8ed 413 break; /* from the for loop for checking against suffixes */
rgrover1 284:82fefb5be8ed 414 }
rgrover1 284:82fefb5be8ed 415 }
rgrover1 284:82fefb5be8ed 416 /* This is the default case where we've got an ordinary character which doesn't match a suffix. */
rgrover1 284:82fefb5be8ed 417 if (i == NUM_SUFFIXES) {
rgrover1 287:cca148e535b1 418 uriDataOut[sizeofURIDataOut++] = *uriDataIn;
rgrover1 287:cca148e535b1 419 ++uriDataIn;
rgrover1 284:82fefb5be8ed 420 }
rgrover1 284:82fefb5be8ed 421 }
rgrover1 284:82fefb5be8ed 422 }
rgrover1 171:6092e61690dc 423 };
rgrover1 171:6092e61690dc 424
rgrover1 287:cca148e535b1 425 #endif // SERVICES_URIBEACONCONFIGSERVICE_H_