Preliminary main mbed library for nexpaq development

Committer:
nexpaq
Date:
Fri Nov 04 20:27:58 2016 +0000
Revision:
0:6c56fb4bc5f0
Moving to library for sharing updates

Who changed what in which revision?

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