Updated

Fork of BLE_API by Bluetooth Low Energy

Committer:
rgrover1
Date:
Fri Aug 07 15:53:50 2015 +0100
Revision:
766:03f1a26f132f
Parent:
728:997ba5e7b3b6
Child:
787:2a6a8313f393
Synchronized with git rev 0f2ba674
Author: Joshua Slater
Conversion from advertisement duration units to ms moved from Gap to GapAdvertisingParams. getInterval converts to ms. Added conversion in GapScanningParams

Who changed what in which revision?

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