Added an EddystoneURLConfigService in addition to UriBeaconConfigService. Updated README and converted comments that used UriBeacon to EddystoneURL in the EddystoneService.h

Dependents:   mbed_EddystoneURL_Beacon_ssci mbed_EddystoneURL_Beacon_ssci mbed_EddystoneURL_Beacon_ssci

Fork of BLE_API by Bluetooth Low Energy

Committer:
roywant
Date:
Wed Aug 19 04:27:52 2015 +0000
Revision:
797:13164356b568
Parent:
787:2a6a8313f393
Updated EddystoneURLConfigService.h : 1) lockedState now is a member of params.lockedState ; zeros are not the unlock value (and a valid key), this now passes the Validator, 2) After disconnect the timeADV is disabled, and ADV params recreated.

Who changed what in which revision?

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