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