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:
Tue Aug 18 00:15:23 2015 +0000
Revision:
795:6401965b88ee
Child:
797:13164356b568
Added EddystoneURLConfigService files (.h, .cpp) and updated comments in EddystoneService.h to remove UriBeacon references, and edited README to reflect changes.

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