High level Bluetooth Low Energy API and radio abstraction layer

Fork of BLE_API by Bluetooth Low Energy

Committer:
rgrover1
Date:
Thu Dec 10 09:15:02 2015 +0000
Revision:
1022:306c409f6c09
Parent:
1019:575852ad31a2
Child:
1023:a072b59caddb
Synchronized with git rev ea69dba5
Author: Marcus Chang
Reversed internal representation of long UUID in the UUID class to little endian to be consistent with the short UUID.

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 912:f728aa46e7df 21
rgrover1 912:f728aa46e7df 22 #ifdef YOTTA_CFG_MBED_OS
rgrover1 912:f728aa46e7df 23 #include "mbed-drivers/mbed.h"
rgrover1 912:f728aa46e7df 24 #else
rgrover1 712:b04b5db36865 25 #include "mbed.h"
rgrover1 912:f728aa46e7df 26 #endif
rgrover1 712:b04b5db36865 27
rgrover1 712:b04b5db36865 28 extern const uint8_t UUID_URI_BEACON_SERVICE[UUID::LENGTH_OF_LONG_UUID];
rgrover1 712:b04b5db36865 29 extern const uint8_t UUID_LOCK_STATE_CHAR[UUID::LENGTH_OF_LONG_UUID];
rgrover1 712:b04b5db36865 30 extern const uint8_t UUID_LOCK_CHAR[UUID::LENGTH_OF_LONG_UUID];
rgrover1 712:b04b5db36865 31 extern const uint8_t UUID_UNLOCK_CHAR[UUID::LENGTH_OF_LONG_UUID];
rgrover1 712:b04b5db36865 32 extern const uint8_t UUID_URI_DATA_CHAR[UUID::LENGTH_OF_LONG_UUID];
rgrover1 712:b04b5db36865 33 extern const uint8_t UUID_FLAGS_CHAR[UUID::LENGTH_OF_LONG_UUID];
rgrover1 712:b04b5db36865 34 extern const uint8_t UUID_ADV_POWER_LEVELS_CHAR[UUID::LENGTH_OF_LONG_UUID];
rgrover1 712:b04b5db36865 35 extern const uint8_t UUID_TX_POWER_MODE_CHAR[UUID::LENGTH_OF_LONG_UUID];
rgrover1 712:b04b5db36865 36 extern const uint8_t UUID_BEACON_PERIOD_CHAR[UUID::LENGTH_OF_LONG_UUID];
rgrover1 712:b04b5db36865 37 extern const uint8_t UUID_RESET_CHAR[UUID::LENGTH_OF_LONG_UUID];
rgrover1 712:b04b5db36865 38
rgrover1 712:b04b5db36865 39 extern const uint8_t BEACON_UUID[sizeof(UUID::ShortUUIDBytes_t)];
rgrover1 712:b04b5db36865 40
rgrover1 712:b04b5db36865 41 /**
rgrover1 712:b04b5db36865 42 * @class URIBeaconConfigService
rgrover1 712:b04b5db36865 43 * @brief UriBeacon Configuration Service. Can be used to set URL, adjust power levels, and set flags.
rgrover1 712:b04b5db36865 44 * See http://uribeacon.org
rgrover1 712:b04b5db36865 45 *
rgrover1 712:b04b5db36865 46 */
rgrover1 712:b04b5db36865 47 class URIBeaconConfigService {
rgrover1 712:b04b5db36865 48 public:
rgrover1 712:b04b5db36865 49 /**
rgrover1 1019:575852ad31a2 50 * @brief Transmission power modes for UriBeacon.
rgrover1 712:b04b5db36865 51 */
rgrover1 1019:575852ad31a2 52 static const uint8_t TX_POWER_MODE_LOWEST = 0; /*!< Lowest TX power mode. */
rgrover1 1019:575852ad31a2 53 static const uint8_t TX_POWER_MODE_LOW = 1; /*!< Low TX power mode. */
rgrover1 1019:575852ad31a2 54 static const uint8_t TX_POWER_MODE_MEDIUM = 2; /*!< Medium TX power mode. */
rgrover1 1019:575852ad31a2 55 static const uint8_t TX_POWER_MODE_HIGH = 3; /*!< High TX power mode. */
rgrover1 1019:575852ad31a2 56 static const unsigned NUM_POWER_MODES = 4; /*!< Number of power modes defined. */
rgrover1 712:b04b5db36865 57
rgrover1 712:b04b5db36865 58 static const int ADVERTISING_INTERVAL_MSEC = 1000; // Advertising interval for config service.
rgrover1 1019:575852ad31a2 59 static const int SERVICE_DATA_MAX = 31; // Maximum size of service data in ADV packets.
rgrover1 712:b04b5db36865 60
rgrover1 1019:575852ad31a2 61 typedef uint8_t Lock_t[16]; /* 128 bits. */
rgrover1 712:b04b5db36865 62 typedef int8_t PowerLevels_t[NUM_POWER_MODES];
rgrover1 712:b04b5db36865 63
rgrover1 712:b04b5db36865 64 static const int URI_DATA_MAX = 18;
rgrover1 712:b04b5db36865 65 typedef uint8_t UriData_t[URI_DATA_MAX];
rgrover1 712:b04b5db36865 66
rgrover1 712:b04b5db36865 67 struct Params_t {
rgrover1 712:b04b5db36865 68 Lock_t lock;
rgrover1 712:b04b5db36865 69 uint8_t uriDataLength;
rgrover1 712:b04b5db36865 70 UriData_t uriData;
rgrover1 712:b04b5db36865 71 uint8_t flags;
rgrover1 1019:575852ad31a2 72 PowerLevels_t advPowerLevels; // Current value of AdvertisedPowerLevels.
rgrover1 1019:575852ad31a2 73 uint8_t txPowerMode; // Firmware power levels used with setTxPower().
rgrover1 712:b04b5db36865 74 uint16_t beaconPeriod;
rgrover1 712:b04b5db36865 75 };
rgrover1 712:b04b5db36865 76
rgrover1 712:b04b5db36865 77 /**
rgrover1 712:b04b5db36865 78 * @param[ref] ble
rgrover1 712:b04b5db36865 79 * BLE object for the underlying controller.
rgrover1 712:b04b5db36865 80 * @param[in/out] paramsIn
rgrover1 712:b04b5db36865 81 * Reference to application-visible beacon state, loaded
rgrover1 712:b04b5db36865 82 * from persistent storage at startup.
rgrover1 712:b04b5db36865 83 * @paramsP[in] resetToDefaultsFlag
rgrover1 712:b04b5db36865 84 * Applies to the state of the 'paramsIn' parameter.
rgrover1 712:b04b5db36865 85 * If true, it indicates that paramsIn is potentially
rgrover1 712:b04b5db36865 86 * un-initialized, and default values should be used
rgrover1 712:b04b5db36865 87 * instead. Otherwise, paramsIn overrides the defaults.
rgrover1 712:b04b5db36865 88 * @param[in] defaultUriDataIn
rgrover1 1019:575852ad31a2 89 * Default un-encoded URI. Applies only if the resetToDefaultsFlag is true.
rgrover1 712:b04b5db36865 90 * @param[in] defaultAdvPowerLevelsIn
rgrover1 1019:575852ad31a2 91 * Default power-levels array. Applies only if the resetToDefaultsFlag is true.
rgrover1 712:b04b5db36865 92 */
rgrover1 712:b04b5db36865 93 URIBeaconConfigService(BLE &bleIn,
rgrover1 712:b04b5db36865 94 Params_t &paramsIn,
rgrover1 712:b04b5db36865 95 bool resetToDefaultsFlag,
rgrover1 712:b04b5db36865 96 const char *defaultURIDataIn,
rgrover1 712:b04b5db36865 97 PowerLevels_t &defaultAdvPowerLevelsIn) :
rgrover1 712:b04b5db36865 98 ble(bleIn),
rgrover1 712:b04b5db36865 99 params(paramsIn),
rgrover1 712:b04b5db36865 100 defaultUriDataLength(),
rgrover1 712:b04b5db36865 101 defaultUriData(),
rgrover1 712:b04b5db36865 102 defaultAdvPowerLevels(defaultAdvPowerLevelsIn),
rgrover1 712:b04b5db36865 103 initSucceeded(false),
rgrover1 712:b04b5db36865 104 resetFlag(),
rgrover1 712:b04b5db36865 105 lockedStateChar(UUID_LOCK_STATE_CHAR, &lockedState),
rgrover1 712:b04b5db36865 106 lockChar(UUID_LOCK_CHAR, &params.lock),
rgrover1 712:b04b5db36865 107 uriDataChar(UUID_URI_DATA_CHAR, params.uriData, 0, URI_DATA_MAX,
rgrover1 712:b04b5db36865 108 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE),
rgrover1 712:b04b5db36865 109 unlockChar(UUID_UNLOCK_CHAR, &params.lock),
rgrover1 712:b04b5db36865 110 flagsChar(UUID_FLAGS_CHAR, &params.flags),
rgrover1 712:b04b5db36865 111 advPowerLevelsChar(UUID_ADV_POWER_LEVELS_CHAR, &params.advPowerLevels),
rgrover1 712:b04b5db36865 112 txPowerModeChar(UUID_TX_POWER_MODE_CHAR, &params.txPowerMode),
rgrover1 712:b04b5db36865 113 beaconPeriodChar(UUID_BEACON_PERIOD_CHAR, &params.beaconPeriod),
rgrover1 712:b04b5db36865 114 resetChar(UUID_RESET_CHAR, &resetFlag) {
rgrover1 712:b04b5db36865 115
rgrover1 712:b04b5db36865 116 encodeURI(defaultURIDataIn, defaultUriData, defaultUriDataLength);
rgrover1 712:b04b5db36865 117 if (defaultUriDataLength > URI_DATA_MAX) {
rgrover1 712:b04b5db36865 118 return;
rgrover1 712:b04b5db36865 119 }
rgrover1 712:b04b5db36865 120
rgrover1 712:b04b5db36865 121 if (!resetToDefaultsFlag && (params.uriDataLength > URI_DATA_MAX)) {
rgrover1 712:b04b5db36865 122 resetToDefaultsFlag = true;
rgrover1 712:b04b5db36865 123 }
rgrover1 712:b04b5db36865 124 if (resetToDefaultsFlag) {
rgrover1 712:b04b5db36865 125 resetToDefaults();
rgrover1 712:b04b5db36865 126 } else {
rgrover1 712:b04b5db36865 127 updateCharacteristicValues();
rgrover1 712:b04b5db36865 128 }
rgrover1 712:b04b5db36865 129
rgrover1 712:b04b5db36865 130 lockedState = isLocked();
rgrover1 712:b04b5db36865 131
rgrover1 712:b04b5db36865 132 lockChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::lockAuthorizationCallback);
rgrover1 712:b04b5db36865 133 unlockChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::unlockAuthorizationCallback);
rgrover1 712:b04b5db36865 134 uriDataChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::uriDataWriteAuthorizationCallback);
rgrover1 712:b04b5db36865 135 flagsChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::basicAuthorizationCallback<uint8_t>);
rgrover1 712:b04b5db36865 136 advPowerLevelsChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::basicAuthorizationCallback<PowerLevels_t>);
rgrover1 712:b04b5db36865 137 txPowerModeChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::powerModeAuthorizationCallback);
rgrover1 712:b04b5db36865 138 beaconPeriodChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::basicAuthorizationCallback<uint16_t>);
rgrover1 712:b04b5db36865 139 resetChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::basicAuthorizationCallback<uint8_t>);
rgrover1 712:b04b5db36865 140
rgrover1 712:b04b5db36865 141 static GattCharacteristic *charTable[] = {
rgrover1 712:b04b5db36865 142 &lockedStateChar, &lockChar, &unlockChar, &uriDataChar,
rgrover1 712:b04b5db36865 143 &flagsChar, &advPowerLevelsChar, &txPowerModeChar, &beaconPeriodChar, &resetChar
rgrover1 712:b04b5db36865 144 };
rgrover1 712:b04b5db36865 145
rgrover1 712:b04b5db36865 146 GattService configService(UUID_URI_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
rgrover1 712:b04b5db36865 147
rgrover1 712:b04b5db36865 148 ble.addService(configService);
rgrover1 712:b04b5db36865 149 ble.onDataWritten(this, &URIBeaconConfigService::onDataWrittenCallback);
rgrover1 712:b04b5db36865 150
rgrover1 1019:575852ad31a2 151 setupURIBeaconConfigAdvertisements(); /* Set up advertising for the config service. */
rgrover1 712:b04b5db36865 152
rgrover1 712:b04b5db36865 153 initSucceeded = true;
rgrover1 712:b04b5db36865 154 }
rgrover1 712:b04b5db36865 155
rgrover1 712:b04b5db36865 156 bool configuredSuccessfully(void) const {
rgrover1 712:b04b5db36865 157 return initSucceeded;
rgrover1 712:b04b5db36865 158 }
rgrover1 712:b04b5db36865 159
rgrover1 1019:575852ad31a2 160 /* Start out by advertising the config service for a limited time after
rgrover1 1019:575852ad31a2 161 * startup. Then switch to the normal non-connectible beacon functionality.
rgrover1 1019:575852ad31a2 162 */
rgrover1 712:b04b5db36865 163 void setupURIBeaconConfigAdvertisements()
rgrover1 712:b04b5db36865 164 {
rgrover1 712:b04b5db36865 165 const char DEVICE_NAME[] = "mUriBeacon Config";
rgrover1 712:b04b5db36865 166
rgrover1 712:b04b5db36865 167 ble.gap().clearAdvertisingPayload();
rgrover1 712:b04b5db36865 168
rgrover1 712:b04b5db36865 169 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
rgrover1 712:b04b5db36865 170
rgrover1 1022:306c409f6c09 171 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, UUID_URI_BEACON_SERVICE, sizeof(UUID_URI_BEACON_SERVICE));
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 1019:575852ad31a2 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 1019:575852ad31a2 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 1019:575852ad31a2 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 1019:575852ad31a2 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 1019:575852ad31a2 231 * characteristics of this service. These attempts 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 1019:575852ad31a2 238 // Validated earlier,
rgrover1 712:b04b5db36865 239 memcpy(params.lock, writeParams->data, sizeof(Lock_t));
rgrover1 1019:575852ad31a2 240 // Use isLocked() in case bits are being set to all zeros.
rgrover1 712:b04b5db36865 241 lockedState = isLocked();
rgrover1 712:b04b5db36865 242 } else if (handle == unlockChar.getValueHandle()) {
rgrover1 1019:575852ad31a2 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 1019:575852ad31a2 258 /* Remap 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 1019:575852ad31a2 376 size_t defaultUriDataLength; // Default value that is restored on reset.
rgrover1 1019:575852ad31a2 377 UriData_t defaultUriData; // Default value that is restored on reset.
rgrover1 1019:575852ad31a2 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 1019:575852ad31a2 396 * Encode a human-readable URI into the binary format defined by the 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 1019:575852ad31a2 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 1019:575852ad31a2 455 break; /* From the for loop for checking against suffixes. */
rgrover1 712:b04b5db36865 456 }
rgrover1 712:b04b5db36865 457 }
rgrover1 1019:575852ad31a2 458 /* This is the default case where we've got an ordinary character that 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_