Updated

Fork of BLE_API by Bluetooth Low Energy

Committer:
rgrover1
Date:
Thu Dec 10 09:15:01 2015 +0000
Revision:
1019:575852ad31a2
Parent:
1017:ccb8c0646e55
Child:
1022:306c409f6c09
Synchronized with git rev 13bf70b6
Author: Rohit Grover
Release 2.1.5
=============

A minor release to separate the concept of minlen and len in
GattCharacteristic. Also contains some improvements to documentation.

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 712:b04b5db36865 171 // UUID is in different order in the ADV frame (!)
rgrover1 712:b04b5db36865 172 uint8_t reversedServiceUUID[sizeof(UUID_URI_BEACON_SERVICE)];
rgrover1 712:b04b5db36865 173 for (unsigned int i = 0; i < sizeof(UUID_URI_BEACON_SERVICE); i++) {
rgrover1 712:b04b5db36865 174 reversedServiceUUID[i] = UUID_URI_BEACON_SERVICE[sizeof(UUID_URI_BEACON_SERVICE) - i - 1];
rgrover1 712:b04b5db36865 175 }
rgrover1 712:b04b5db36865 176 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, reversedServiceUUID, sizeof(reversedServiceUUID));
rgrover1 712:b04b5db36865 177 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG);
rgrover1 712:b04b5db36865 178 ble.gap().accumulateScanResponse(GapAdvertisingData::COMPLETE_LOCAL_NAME, reinterpret_cast<const uint8_t *>(&DEVICE_NAME), sizeof(DEVICE_NAME));
rgrover1 712:b04b5db36865 179 ble.gap().accumulateScanResponse(GapAdvertisingData::TX_POWER_LEVEL,
rgrover1 712:b04b5db36865 180 reinterpret_cast<uint8_t *>(&defaultAdvPowerLevels[URIBeaconConfigService::TX_POWER_MODE_LOW]),
rgrover1 712:b04b5db36865 181 sizeof(uint8_t));
rgrover1 712:b04b5db36865 182
rgrover1 712:b04b5db36865 183 ble.gap().setTxPower(params.advPowerLevels[params.txPowerMode]);
rgrover1 712:b04b5db36865 184 ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(&DEVICE_NAME));
rgrover1 712:b04b5db36865 185 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
rgrover1 766:03f1a26f132f 186 ble.gap().setAdvertisingInterval(GapAdvertisingParams::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(ADVERTISING_INTERVAL_MSEC));
rgrover1 712:b04b5db36865 187 }
rgrover1 712:b04b5db36865 188
rgrover1 1019:575852ad31a2 189 /* Helper function to switch to the non-connectible normal mode for UriBeacon. This gets called after a timeout. */
rgrover1 712:b04b5db36865 190 void setupURIBeaconAdvertisements()
rgrover1 712:b04b5db36865 191 {
rgrover1 712:b04b5db36865 192 /* Reinitialize the BLE stack. This will clear away the existing services and advertising state. */
rgrover1 712:b04b5db36865 193 ble.shutdown();
rgrover1 712:b04b5db36865 194 ble.init();
rgrover1 712:b04b5db36865 195
rgrover1 1019:575852ad31a2 196 // Fields from the service.
rgrover1 712:b04b5db36865 197 unsigned beaconPeriod = params.beaconPeriod;
rgrover1 712:b04b5db36865 198 unsigned txPowerMode = params.txPowerMode;
rgrover1 712:b04b5db36865 199 unsigned uriDataLength = params.uriDataLength;
rgrover1 712:b04b5db36865 200 URIBeaconConfigService::UriData_t &uriData = params.uriData;
rgrover1 712:b04b5db36865 201 URIBeaconConfigService::PowerLevels_t &advPowerLevels = params.advPowerLevels;
rgrover1 712:b04b5db36865 202 uint8_t flags = params.flags;
rgrover1 712:b04b5db36865 203
rgrover1 1019:575852ad31a2 204 extern void saveURIBeaconConfigParams(const Params_t *paramsP); /* Forward declaration; necessary to avoid a circular dependency. */
rgrover1 712:b04b5db36865 205 saveURIBeaconConfigParams(&params);
rgrover1 712:b04b5db36865 206
rgrover1 712:b04b5db36865 207 ble.gap().clearAdvertisingPayload();
rgrover1 712:b04b5db36865 208 ble.gap().setTxPower(params.advPowerLevels[params.txPowerMode]);
rgrover1 712:b04b5db36865 209 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
rgrover1 712:b04b5db36865 210 ble.gap().setAdvertisingInterval(beaconPeriod);
rgrover1 712:b04b5db36865 211 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
rgrover1 712:b04b5db36865 212 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, BEACON_UUID, sizeof(BEACON_UUID));
rgrover1 712:b04b5db36865 213
rgrover1 787:2a6a8313f393 214 uint8_t serviceData[SERVICE_DATA_MAX];
rgrover1 787:2a6a8313f393 215 unsigned serviceDataLen = 0;
rgrover1 712:b04b5db36865 216 serviceData[serviceDataLen++] = BEACON_UUID[0];
rgrover1 712:b04b5db36865 217 serviceData[serviceDataLen++] = BEACON_UUID[1];
rgrover1 712:b04b5db36865 218 serviceData[serviceDataLen++] = flags;
rgrover1 712:b04b5db36865 219 serviceData[serviceDataLen++] = advPowerLevels[txPowerMode];
rgrover1 712:b04b5db36865 220 for (unsigned j = 0; j < uriDataLength; j++) {
rgrover1 712:b04b5db36865 221 serviceData[serviceDataLen++] = uriData[j];
rgrover1 712:b04b5db36865 222 }
rgrover1 712:b04b5db36865 223 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, serviceData, serviceDataLen);
rgrover1 712:b04b5db36865 224 }
rgrover1 712:b04b5db36865 225
rgrover1 712:b04b5db36865 226 private:
rgrover1 1019:575852ad31a2 227 // True if the lock bits are non-zero.
rgrover1 712:b04b5db36865 228 bool isLocked() {
rgrover1 712:b04b5db36865 229 Lock_t testLock;
rgrover1 712:b04b5db36865 230 memset(testLock, 0, sizeof(Lock_t));
rgrover1 712:b04b5db36865 231 return memcmp(params.lock, testLock, sizeof(Lock_t));
rgrover1 712:b04b5db36865 232 }
rgrover1 712:b04b5db36865 233
rgrover1 712:b04b5db36865 234 /*
rgrover1 712:b04b5db36865 235 * This callback is invoked when a GATT client attempts to modify any of the
rgrover1 1019:575852ad31a2 236 * characteristics of this service. These attempts are also applied to
rgrover1 712:b04b5db36865 237 * the internal state of this service object.
rgrover1 712:b04b5db36865 238 */
rgrover1 712:b04b5db36865 239 void onDataWrittenCallback(const GattWriteCallbackParams *writeParams) {
rgrover1 712:b04b5db36865 240 uint16_t handle = writeParams->handle;
rgrover1 712:b04b5db36865 241
rgrover1 712:b04b5db36865 242 if (handle == lockChar.getValueHandle()) {
rgrover1 1019:575852ad31a2 243 // Validated earlier,
rgrover1 712:b04b5db36865 244 memcpy(params.lock, writeParams->data, sizeof(Lock_t));
rgrover1 1019:575852ad31a2 245 // Use isLocked() in case bits are being set to all zeros.
rgrover1 712:b04b5db36865 246 lockedState = isLocked();
rgrover1 712:b04b5db36865 247 } else if (handle == unlockChar.getValueHandle()) {
rgrover1 1019:575852ad31a2 248 // Validated earlier.
rgrover1 712:b04b5db36865 249 memset(params.lock, 0, sizeof(Lock_t));
rgrover1 712:b04b5db36865 250 lockedState = false;
rgrover1 712:b04b5db36865 251 } else if (handle == uriDataChar.getValueHandle()) {
rgrover1 712:b04b5db36865 252 params.uriDataLength = writeParams->len;
rgrover1 712:b04b5db36865 253 memcpy(params.uriData, writeParams->data, params.uriDataLength);
rgrover1 712:b04b5db36865 254 } else if (handle == flagsChar.getValueHandle()) {
rgrover1 712:b04b5db36865 255 params.flags = *(writeParams->data);
rgrover1 712:b04b5db36865 256 } else if (handle == advPowerLevelsChar.getValueHandle()) {
rgrover1 712:b04b5db36865 257 memcpy(params.advPowerLevels, writeParams->data, sizeof(PowerLevels_t));
rgrover1 712:b04b5db36865 258 } else if (handle == txPowerModeChar.getValueHandle()) {
rgrover1 712:b04b5db36865 259 params.txPowerMode = *(writeParams->data);
rgrover1 712:b04b5db36865 260 } else if (handle == beaconPeriodChar.getValueHandle()) {
rgrover1 712:b04b5db36865 261 params.beaconPeriod = *((uint16_t *)(writeParams->data));
rgrover1 712:b04b5db36865 262
rgrover1 1019:575852ad31a2 263 /* Remap beaconPeriod to within permissible bounds if necessary. */
rgrover1 712:b04b5db36865 264 if (params.beaconPeriod != 0) {
rgrover1 712:b04b5db36865 265 bool paramsUpdated = false;
rgrover1 712:b04b5db36865 266 if (params.beaconPeriod < ble.gap().getMinAdvertisingInterval()) {
rgrover1 712:b04b5db36865 267 params.beaconPeriod = ble.gap().getMinAdvertisingInterval();
rgrover1 712:b04b5db36865 268 paramsUpdated = true;
rgrover1 712:b04b5db36865 269 } else if (params.beaconPeriod > ble.gap().getMaxAdvertisingInterval()) {
rgrover1 712:b04b5db36865 270 params.beaconPeriod = ble.gap().getMaxAdvertisingInterval();
rgrover1 712:b04b5db36865 271 paramsUpdated = true;
rgrover1 712:b04b5db36865 272 }
rgrover1 712:b04b5db36865 273 if (paramsUpdated) {
rgrover1 728:997ba5e7b3b6 274 ble.gattServer().write(beaconPeriodChar.getValueHandle(), reinterpret_cast<uint8_t *>(&params.beaconPeriod), sizeof(uint16_t));
rgrover1 712:b04b5db36865 275 }
rgrover1 712:b04b5db36865 276 }
rgrover1 712:b04b5db36865 277 } else if (handle == resetChar.getValueHandle()) {
rgrover1 712:b04b5db36865 278 resetToDefaults();
rgrover1 712:b04b5db36865 279 }
rgrover1 712:b04b5db36865 280 }
rgrover1 712:b04b5db36865 281
rgrover1 712:b04b5db36865 282 /*
rgrover1 712:b04b5db36865 283 * Reset the default values.
rgrover1 712:b04b5db36865 284 */
rgrover1 712:b04b5db36865 285 void resetToDefaults(void) {
rgrover1 712:b04b5db36865 286 lockedState = false;
rgrover1 712:b04b5db36865 287 memset(params.lock, 0, sizeof(Lock_t));
rgrover1 712:b04b5db36865 288 memcpy(params.uriData, defaultUriData, URI_DATA_MAX);
rgrover1 712:b04b5db36865 289 params.uriDataLength = defaultUriDataLength;
rgrover1 712:b04b5db36865 290 params.flags = 0;
rgrover1 712:b04b5db36865 291 memcpy(params.advPowerLevels, defaultAdvPowerLevels, sizeof(PowerLevels_t));
rgrover1 712:b04b5db36865 292 params.txPowerMode = TX_POWER_MODE_LOW;
rgrover1 712:b04b5db36865 293 params.beaconPeriod = 1000;
rgrover1 712:b04b5db36865 294 updateCharacteristicValues();
rgrover1 712:b04b5db36865 295 }
rgrover1 712:b04b5db36865 296
rgrover1 712:b04b5db36865 297 /*
rgrover1 712:b04b5db36865 298 * Internal helper function used to update the GATT database following any
rgrover1 712:b04b5db36865 299 * change to the internal state of the service object.
rgrover1 712:b04b5db36865 300 */
rgrover1 712:b04b5db36865 301 void updateCharacteristicValues(void) {
rgrover1 728:997ba5e7b3b6 302 ble.gattServer().write(lockedStateChar.getValueHandle(), &lockedState, 1);
rgrover1 728:997ba5e7b3b6 303 ble.gattServer().write(uriDataChar.getValueHandle(), params.uriData, params.uriDataLength);
rgrover1 728:997ba5e7b3b6 304 ble.gattServer().write(flagsChar.getValueHandle(), &params.flags, 1);
rgrover1 728:997ba5e7b3b6 305 ble.gattServer().write(beaconPeriodChar.getValueHandle(),
rgrover1 712:b04b5db36865 306 reinterpret_cast<uint8_t *>(&params.beaconPeriod), sizeof(uint16_t));
rgrover1 728:997ba5e7b3b6 307 ble.gattServer().write(txPowerModeChar.getValueHandle(), &params.txPowerMode, 1);
rgrover1 728:997ba5e7b3b6 308 ble.gattServer().write(advPowerLevelsChar.getValueHandle(),
rgrover1 712:b04b5db36865 309 reinterpret_cast<uint8_t *>(params.advPowerLevels), sizeof(PowerLevels_t));
rgrover1 712:b04b5db36865 310 }
rgrover1 712:b04b5db36865 311
rgrover1 728:997ba5e7b3b6 312 protected:
rgrover1 712:b04b5db36865 313 void lockAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
rgrover1 712:b04b5db36865 314 if (lockedState) {
rgrover1 712:b04b5db36865 315 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
rgrover1 712:b04b5db36865 316 } else if (authParams->len != sizeof(Lock_t)) {
rgrover1 712:b04b5db36865 317 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
rgrover1 712:b04b5db36865 318 } else if (authParams->offset != 0) {
rgrover1 712:b04b5db36865 319 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
rgrover1 712:b04b5db36865 320 } else {
rgrover1 712:b04b5db36865 321 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
rgrover1 712:b04b5db36865 322 }
rgrover1 712:b04b5db36865 323 }
rgrover1 712:b04b5db36865 324
rgrover1 712:b04b5db36865 325
rgrover1 712:b04b5db36865 326 void unlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
rgrover1 712:b04b5db36865 327 if (!lockedState) {
rgrover1 712:b04b5db36865 328 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
rgrover1 712:b04b5db36865 329 } else if (authParams->len != sizeof(Lock_t)) {
rgrover1 712:b04b5db36865 330 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
rgrover1 712:b04b5db36865 331 } else if (authParams->offset != 0) {
rgrover1 712:b04b5db36865 332 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
rgrover1 712:b04b5db36865 333 } else if (memcmp(authParams->data, params.lock, sizeof(Lock_t)) != 0) {
rgrover1 712:b04b5db36865 334 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
rgrover1 712:b04b5db36865 335 } else {
rgrover1 712:b04b5db36865 336 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
rgrover1 712:b04b5db36865 337 }
rgrover1 712:b04b5db36865 338 }
rgrover1 712:b04b5db36865 339
rgrover1 712:b04b5db36865 340 void uriDataWriteAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
rgrover1 712:b04b5db36865 341 if (lockedState) {
rgrover1 712:b04b5db36865 342 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
rgrover1 712:b04b5db36865 343 } else if (authParams->offset != 0) {
rgrover1 712:b04b5db36865 344 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
rgrover1 712:b04b5db36865 345 } else {
rgrover1 712:b04b5db36865 346 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
rgrover1 712:b04b5db36865 347 }
rgrover1 712:b04b5db36865 348 }
rgrover1 712:b04b5db36865 349
rgrover1 712:b04b5db36865 350 void powerModeAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
rgrover1 712:b04b5db36865 351 if (lockedState) {
rgrover1 712:b04b5db36865 352 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
rgrover1 712:b04b5db36865 353 } else if (authParams->len != sizeof(uint8_t)) {
rgrover1 712:b04b5db36865 354 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
rgrover1 712:b04b5db36865 355 } else if (authParams->offset != 0) {
rgrover1 712:b04b5db36865 356 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
rgrover1 712:b04b5db36865 357 } else if (*((uint8_t *)authParams->data) >= NUM_POWER_MODES) {
rgrover1 712:b04b5db36865 358 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
rgrover1 712:b04b5db36865 359 } else {
rgrover1 712:b04b5db36865 360 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
rgrover1 712:b04b5db36865 361 }
rgrover1 712:b04b5db36865 362 }
rgrover1 712:b04b5db36865 363
rgrover1 712:b04b5db36865 364 template <typename T>
rgrover1 712:b04b5db36865 365 void basicAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
rgrover1 712:b04b5db36865 366 if (lockedState) {
rgrover1 712:b04b5db36865 367 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
rgrover1 712:b04b5db36865 368 } else if (authParams->len != sizeof(T)) {
rgrover1 712:b04b5db36865 369 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
rgrover1 712:b04b5db36865 370 } else if (authParams->offset != 0) {
rgrover1 712:b04b5db36865 371 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
rgrover1 712:b04b5db36865 372 } else {
rgrover1 712:b04b5db36865 373 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
rgrover1 712:b04b5db36865 374 }
rgrover1 712:b04b5db36865 375 }
rgrover1 712:b04b5db36865 376
rgrover1 712:b04b5db36865 377 protected:
rgrover1 712:b04b5db36865 378 BLE &ble;
rgrover1 712:b04b5db36865 379 Params_t &params;
rgrover1 712:b04b5db36865 380
rgrover1 1019:575852ad31a2 381 size_t defaultUriDataLength; // Default value that is restored on reset.
rgrover1 1019:575852ad31a2 382 UriData_t defaultUriData; // Default value that is restored on reset.
rgrover1 1019:575852ad31a2 383 PowerLevels_t &defaultAdvPowerLevels; // Default value that is restored on reset.
rgrover1 712:b04b5db36865 384
rgrover1 712:b04b5db36865 385 uint8_t lockedState;
rgrover1 712:b04b5db36865 386 bool initSucceeded;
rgrover1 712:b04b5db36865 387 uint8_t resetFlag;
rgrover1 712:b04b5db36865 388
rgrover1 712:b04b5db36865 389 ReadOnlyGattCharacteristic<uint8_t> lockedStateChar;
rgrover1 712:b04b5db36865 390 WriteOnlyGattCharacteristic<Lock_t> lockChar;
rgrover1 712:b04b5db36865 391 GattCharacteristic uriDataChar;
rgrover1 712:b04b5db36865 392 WriteOnlyGattCharacteristic<Lock_t> unlockChar;
rgrover1 712:b04b5db36865 393 ReadWriteGattCharacteristic<uint8_t> flagsChar;
rgrover1 712:b04b5db36865 394 ReadWriteGattCharacteristic<PowerLevels_t> advPowerLevelsChar;
rgrover1 712:b04b5db36865 395 ReadWriteGattCharacteristic<uint8_t> txPowerModeChar;
rgrover1 712:b04b5db36865 396 ReadWriteGattCharacteristic<uint16_t> beaconPeriodChar;
rgrover1 712:b04b5db36865 397 WriteOnlyGattCharacteristic<uint8_t> resetChar;
rgrover1 712:b04b5db36865 398
rgrover1 712:b04b5db36865 399 public:
rgrover1 712:b04b5db36865 400 /*
rgrover1 1019:575852ad31a2 401 * 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 402 */
rgrover1 712:b04b5db36865 403 static void encodeURI(const char *uriDataIn, UriData_t uriDataOut, size_t &sizeofURIDataOut) {
rgrover1 712:b04b5db36865 404 const char *prefixes[] = {
rgrover1 712:b04b5db36865 405 "http://www.",
rgrover1 712:b04b5db36865 406 "https://www.",
rgrover1 712:b04b5db36865 407 "http://",
rgrover1 712:b04b5db36865 408 "https://",
rgrover1 712:b04b5db36865 409 "urn:uuid:"
rgrover1 712:b04b5db36865 410 };
rgrover1 712:b04b5db36865 411 const size_t NUM_PREFIXES = sizeof(prefixes) / sizeof(char *);
rgrover1 712:b04b5db36865 412 const char *suffixes[] = {
rgrover1 712:b04b5db36865 413 ".com/",
rgrover1 712:b04b5db36865 414 ".org/",
rgrover1 712:b04b5db36865 415 ".edu/",
rgrover1 712:b04b5db36865 416 ".net/",
rgrover1 712:b04b5db36865 417 ".info/",
rgrover1 712:b04b5db36865 418 ".biz/",
rgrover1 712:b04b5db36865 419 ".gov/",
rgrover1 712:b04b5db36865 420 ".com",
rgrover1 712:b04b5db36865 421 ".org",
rgrover1 712:b04b5db36865 422 ".edu",
rgrover1 712:b04b5db36865 423 ".net",
rgrover1 712:b04b5db36865 424 ".info",
rgrover1 712:b04b5db36865 425 ".biz",
rgrover1 712:b04b5db36865 426 ".gov"
rgrover1 712:b04b5db36865 427 };
rgrover1 712:b04b5db36865 428 const size_t NUM_SUFFIXES = sizeof(suffixes) / sizeof(char *);
rgrover1 712:b04b5db36865 429
rgrover1 712:b04b5db36865 430 sizeofURIDataOut = 0;
rgrover1 712:b04b5db36865 431 memset(uriDataOut, 0, sizeof(UriData_t));
rgrover1 712:b04b5db36865 432
rgrover1 712:b04b5db36865 433 if ((uriDataIn == NULL) || (strlen(uriDataIn) == 0)) {
rgrover1 712:b04b5db36865 434 return;
rgrover1 712:b04b5db36865 435 }
rgrover1 712:b04b5db36865 436
rgrover1 712:b04b5db36865 437 /*
rgrover1 712:b04b5db36865 438 * handle prefix
rgrover1 712:b04b5db36865 439 */
rgrover1 712:b04b5db36865 440 for (unsigned i = 0; i < NUM_PREFIXES; i++) {
rgrover1 712:b04b5db36865 441 size_t prefixLen = strlen(prefixes[i]);
rgrover1 712:b04b5db36865 442 if (strncmp(uriDataIn, prefixes[i], prefixLen) == 0) {
rgrover1 712:b04b5db36865 443 uriDataOut[sizeofURIDataOut++] = i;
rgrover1 712:b04b5db36865 444 uriDataIn += prefixLen;
rgrover1 712:b04b5db36865 445 break;
rgrover1 712:b04b5db36865 446 }
rgrover1 712:b04b5db36865 447 }
rgrover1 712:b04b5db36865 448
rgrover1 712:b04b5db36865 449 /*
rgrover1 1019:575852ad31a2 450 * Handle suffixes.
rgrover1 712:b04b5db36865 451 */
rgrover1 712:b04b5db36865 452 while (*uriDataIn && (sizeofURIDataOut < URI_DATA_MAX)) {
rgrover1 712:b04b5db36865 453 /* check for suffix match */
rgrover1 712:b04b5db36865 454 unsigned i;
rgrover1 712:b04b5db36865 455 for (i = 0; i < NUM_SUFFIXES; i++) {
rgrover1 712:b04b5db36865 456 size_t suffixLen = strlen(suffixes[i]);
rgrover1 712:b04b5db36865 457 if (strncmp(uriDataIn, suffixes[i], suffixLen) == 0) {
rgrover1 712:b04b5db36865 458 uriDataOut[sizeofURIDataOut++] = i;
rgrover1 712:b04b5db36865 459 uriDataIn += suffixLen;
rgrover1 1019:575852ad31a2 460 break; /* From the for loop for checking against suffixes. */
rgrover1 712:b04b5db36865 461 }
rgrover1 712:b04b5db36865 462 }
rgrover1 1019:575852ad31a2 463 /* This is the default case where we've got an ordinary character that doesn't match a suffix. */
rgrover1 712:b04b5db36865 464 if (i == NUM_SUFFIXES) {
rgrover1 712:b04b5db36865 465 uriDataOut[sizeofURIDataOut++] = *uriDataIn;
rgrover1 712:b04b5db36865 466 ++uriDataIn;
rgrover1 712:b04b5db36865 467 }
rgrover1 712:b04b5db36865 468 }
rgrover1 712:b04b5db36865 469 }
rgrover1 712:b04b5db36865 470 };
rgrover1 712:b04b5db36865 471
rgrover1 712:b04b5db36865 472 #endif // SERVICES_URIBEACONCONFIGSERVICE_H_