BLE EddystoneService example

This example is a fork of the following mbed-os example:

https://developer.mbed.org/teams/mbed-os-examples/code/mbed-os-example-ble-EddystoneService/

Please read the documentation in this page.

Committer:
mbed_official
Date:
Fri Jul 29 22:45:40 2016 +0100
Revision:
3:5120491ba317
Parent:
2:9ee673e0b86a
Child:
12:b31d7c0f906c
Merge branch 'master' of https://github.com/ARMmbed/mbed-os-example-ble


Commit copied from https://github.com/ARMmbed/mbed-os-example-ble

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mbed_official 3:5120491ba317 1 /* mbed Microcontroller Library
mbed_official 3:5120491ba317 2 * Copyright (c) 2006-2015 ARM Limited
mbed_official 3:5120491ba317 3 *
mbed_official 3:5120491ba317 4 * Licensed under the Apache License, Version 2.0 (the "License");
mbed_official 3:5120491ba317 5 * you may not use this file except in compliance with the License.
mbed_official 3:5120491ba317 6 * You may obtain a copy of the License at
mbed_official 3:5120491ba317 7 *
mbed_official 3:5120491ba317 8 * http://www.apache.org/licenses/LICENSE-2.0
mbed_official 3:5120491ba317 9 *
mbed_official 3:5120491ba317 10 * Unless required by applicable law or agreed to in writing, software
mbed_official 3:5120491ba317 11 * distributed under the License is distributed on an "AS IS" BASIS,
mbed_official 3:5120491ba317 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
mbed_official 3:5120491ba317 13 * See the License for the specific language governing permissions and
mbed_official 3:5120491ba317 14 * limitations under the License.
mbed_official 3:5120491ba317 15 */
mbed_official 3:5120491ba317 16
mbed_official 3:5120491ba317 17 #include "EddystoneService.h"
mbed_official 3:5120491ba317 18
mbed_official 3:5120491ba317 19 /* Initialise the EddystoneService using parameters from persistent storage */
mbed_official 3:5120491ba317 20 EddystoneService::EddystoneService(BLE &bleIn,
mbed_official 3:5120491ba317 21 EddystoneParams_t &paramsIn,
mbed_official 3:5120491ba317 22 const PowerLevels_t &radioPowerLevelsIn,
mbed_official 3:5120491ba317 23 EventQueue &evQ,
mbed_official 3:5120491ba317 24 uint32_t advConfigIntervalIn) :
mbed_official 3:5120491ba317 25 ble(bleIn),
mbed_official 3:5120491ba317 26 operationMode(EDDYSTONE_MODE_NONE),
mbed_official 3:5120491ba317 27 urlFrame(paramsIn.urlData, paramsIn.urlDataLength),
mbed_official 3:5120491ba317 28 uidFrame(paramsIn.uidNamespaceID, paramsIn.uidInstanceID),
mbed_official 3:5120491ba317 29 tlmFrame(paramsIn.tlmVersion),
mbed_official 3:5120491ba317 30 resetFlag(false),
mbed_official 3:5120491ba317 31 rawUrlFrame(NULL),
mbed_official 3:5120491ba317 32 rawUidFrame(NULL),
mbed_official 3:5120491ba317 33 rawTlmFrame(NULL),
mbed_official 3:5120491ba317 34 tlmBatteryVoltageCallback(NULL),
mbed_official 3:5120491ba317 35 tlmBeaconTemperatureCallback(NULL),
mbed_official 3:5120491ba317 36 uidFrameCallbackHandle(),
mbed_official 3:5120491ba317 37 urlFrameCallbackHandle(),
mbed_official 3:5120491ba317 38 tlmFrameCallbackHandle(),
mbed_official 3:5120491ba317 39 radioManagerCallbackHandle(),
mbed_official 3:5120491ba317 40 deviceName(DEFAULT_DEVICE_NAME),
mbed_official 3:5120491ba317 41 eventQueue(evQ)
mbed_official 3:5120491ba317 42 {
mbed_official 3:5120491ba317 43 lockState = paramsIn.lockState;
mbed_official 3:5120491ba317 44 flags = paramsIn.flags;
mbed_official 3:5120491ba317 45 txPowerMode = paramsIn.txPowerMode;
mbed_official 3:5120491ba317 46 urlFramePeriod = correctAdvertisementPeriod(paramsIn.urlFramePeriod);
mbed_official 3:5120491ba317 47 uidFramePeriod = correctAdvertisementPeriod(paramsIn.uidFramePeriod);
mbed_official 3:5120491ba317 48 tlmFramePeriod = correctAdvertisementPeriod(paramsIn.tlmFramePeriod);
mbed_official 3:5120491ba317 49
mbed_official 3:5120491ba317 50 memcpy(lock, paramsIn.lock, sizeof(Lock_t));
mbed_official 3:5120491ba317 51 memcpy(unlock, paramsIn.unlock, sizeof(Lock_t));
mbed_official 3:5120491ba317 52
mbed_official 3:5120491ba317 53 eddystoneConstructorHelper(paramsIn.advPowerLevels, radioPowerLevelsIn, advConfigIntervalIn);
mbed_official 3:5120491ba317 54 }
mbed_official 3:5120491ba317 55
mbed_official 3:5120491ba317 56 /* When using this constructor we need to call setURLData,
mbed_official 3:5120491ba317 57 * setTMLData and setUIDData to initialise values manually
mbed_official 3:5120491ba317 58 */
mbed_official 3:5120491ba317 59 EddystoneService::EddystoneService(BLE &bleIn,
mbed_official 3:5120491ba317 60 const PowerLevels_t &advPowerLevelsIn,
mbed_official 3:5120491ba317 61 const PowerLevels_t &radioPowerLevelsIn,
mbed_official 3:5120491ba317 62 EventQueue &evQ,
mbed_official 3:5120491ba317 63 uint32_t advConfigIntervalIn) :
mbed_official 3:5120491ba317 64 ble(bleIn),
mbed_official 3:5120491ba317 65 operationMode(EDDYSTONE_MODE_NONE),
mbed_official 3:5120491ba317 66 urlFrame(),
mbed_official 3:5120491ba317 67 uidFrame(),
mbed_official 3:5120491ba317 68 tlmFrame(),
mbed_official 3:5120491ba317 69 lockState(false),
mbed_official 3:5120491ba317 70 resetFlag(false),
mbed_official 3:5120491ba317 71 lock(),
mbed_official 3:5120491ba317 72 unlock(),
mbed_official 3:5120491ba317 73 flags(0),
mbed_official 3:5120491ba317 74 txPowerMode(0),
mbed_official 3:5120491ba317 75 urlFramePeriod(DEFAULT_URL_FRAME_PERIOD_MSEC),
mbed_official 3:5120491ba317 76 uidFramePeriod(DEFAULT_UID_FRAME_PERIOD_MSEC),
mbed_official 3:5120491ba317 77 tlmFramePeriod(DEFAULT_TLM_FRAME_PERIOD_MSEC),
mbed_official 3:5120491ba317 78 rawUrlFrame(NULL),
mbed_official 3:5120491ba317 79 rawUidFrame(NULL),
mbed_official 3:5120491ba317 80 rawTlmFrame(NULL),
mbed_official 3:5120491ba317 81 tlmBatteryVoltageCallback(NULL),
mbed_official 3:5120491ba317 82 tlmBeaconTemperatureCallback(NULL),
mbed_official 3:5120491ba317 83 uidFrameCallbackHandle(),
mbed_official 3:5120491ba317 84 urlFrameCallbackHandle(),
mbed_official 3:5120491ba317 85 tlmFrameCallbackHandle(),
mbed_official 3:5120491ba317 86 radioManagerCallbackHandle(),
mbed_official 3:5120491ba317 87 deviceName(DEFAULT_DEVICE_NAME),
mbed_official 3:5120491ba317 88 eventQueue(evQ)
mbed_official 3:5120491ba317 89 {
mbed_official 3:5120491ba317 90 eddystoneConstructorHelper(advPowerLevelsIn, radioPowerLevelsIn, advConfigIntervalIn);
mbed_official 3:5120491ba317 91 }
mbed_official 3:5120491ba317 92
mbed_official 3:5120491ba317 93 /* Setup callback to update BatteryVoltage in TLM frame */
mbed_official 3:5120491ba317 94 void EddystoneService::onTLMBatteryVoltageUpdate(TlmUpdateCallback_t tlmBatteryVoltageCallbackIn)
mbed_official 3:5120491ba317 95 {
mbed_official 3:5120491ba317 96 tlmBatteryVoltageCallback = tlmBatteryVoltageCallbackIn;
mbed_official 3:5120491ba317 97 }
mbed_official 3:5120491ba317 98
mbed_official 3:5120491ba317 99 /* Setup callback to update BeaconTemperature in TLM frame */
mbed_official 3:5120491ba317 100 void EddystoneService::onTLMBeaconTemperatureUpdate(TlmUpdateCallback_t tlmBeaconTemperatureCallbackIn)
mbed_official 3:5120491ba317 101 {
mbed_official 3:5120491ba317 102 tlmBeaconTemperatureCallback = tlmBeaconTemperatureCallbackIn;
mbed_official 3:5120491ba317 103 }
mbed_official 3:5120491ba317 104
mbed_official 3:5120491ba317 105 void EddystoneService::setTLMData(uint8_t tlmVersionIn)
mbed_official 3:5120491ba317 106 {
mbed_official 3:5120491ba317 107 tlmFrame.setTLMData(tlmVersionIn);
mbed_official 3:5120491ba317 108 }
mbed_official 3:5120491ba317 109
mbed_official 3:5120491ba317 110 void EddystoneService::setURLData(const char *urlDataIn)
mbed_official 3:5120491ba317 111 {
mbed_official 3:5120491ba317 112 urlFrame.setURLData(urlDataIn);
mbed_official 3:5120491ba317 113 }
mbed_official 3:5120491ba317 114
mbed_official 3:5120491ba317 115 void EddystoneService::setUIDData(const UIDNamespaceID_t &uidNamespaceIDIn, const UIDInstanceID_t &uidInstanceIDIn)
mbed_official 3:5120491ba317 116 {
mbed_official 3:5120491ba317 117 uidFrame.setUIDData(uidNamespaceIDIn, uidInstanceIDIn);
mbed_official 3:5120491ba317 118 }
mbed_official 3:5120491ba317 119
mbed_official 3:5120491ba317 120 EddystoneService::EddystoneError_t EddystoneService::startConfigService(void)
mbed_official 3:5120491ba317 121 {
mbed_official 3:5120491ba317 122 if (operationMode == EDDYSTONE_MODE_CONFIG) {
mbed_official 3:5120491ba317 123 /* Nothing to do, we are already in config mode */
mbed_official 3:5120491ba317 124 return EDDYSTONE_ERROR_NONE;
mbed_official 3:5120491ba317 125 } else if (advConfigInterval == 0) {
mbed_official 3:5120491ba317 126 /* Nothing to do, the advertisement interval is 0 */
mbed_official 3:5120491ba317 127 return EDDYSTONE_ERROR_INVALID_ADVERTISING_INTERVAL;
mbed_official 3:5120491ba317 128 }
mbed_official 3:5120491ba317 129
mbed_official 3:5120491ba317 130 if (operationMode == EDDYSTONE_MODE_BEACON) {
mbed_official 3:5120491ba317 131 ble.shutdown();
mbed_official 3:5120491ba317 132 stopBeaconService();
mbed_official 3:5120491ba317 133 }
mbed_official 3:5120491ba317 134
mbed_official 3:5120491ba317 135 if (!ble.hasInitialized()) {
mbed_official 3:5120491ba317 136 operationMode = EDDYSTONE_MODE_CONFIG;
mbed_official 3:5120491ba317 137 ble.init(this, &EddystoneService::bleInitComplete);
mbed_official 3:5120491ba317 138 /* Set the device name once more */
mbed_official 3:5120491ba317 139 ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(deviceName));
mbed_official 3:5120491ba317 140 return EDDYSTONE_ERROR_NONE;
mbed_official 3:5120491ba317 141 }
mbed_official 3:5120491ba317 142
mbed_official 3:5120491ba317 143 operationMode = EDDYSTONE_MODE_CONFIG;
mbed_official 3:5120491ba317 144 setupConfigService();
mbed_official 3:5120491ba317 145 return EDDYSTONE_ERROR_NONE;
mbed_official 3:5120491ba317 146 }
mbed_official 3:5120491ba317 147
mbed_official 3:5120491ba317 148 EddystoneService::EddystoneError_t EddystoneService::startBeaconService(void)
mbed_official 3:5120491ba317 149 {
mbed_official 3:5120491ba317 150 if (operationMode == EDDYSTONE_MODE_BEACON) {
mbed_official 3:5120491ba317 151 /* Nothing to do, we are already in beacon mode */
mbed_official 3:5120491ba317 152 return EDDYSTONE_ERROR_NONE;
mbed_official 3:5120491ba317 153 } else if (!urlFramePeriod && !uidFramePeriod && !tlmFramePeriod) {
mbed_official 3:5120491ba317 154 /* Nothing to do, the period is 0 for all frames */
mbed_official 3:5120491ba317 155 return EDDYSTONE_ERROR_INVALID_ADVERTISING_INTERVAL;
mbed_official 3:5120491ba317 156 }
mbed_official 3:5120491ba317 157
mbed_official 3:5120491ba317 158 if (operationMode == EDDYSTONE_MODE_CONFIG) {
mbed_official 3:5120491ba317 159 ble.shutdown();
mbed_official 3:5120491ba317 160 /* Free unused memory */
mbed_official 3:5120491ba317 161 freeConfigCharacteristics();
mbed_official 3:5120491ba317 162 }
mbed_official 3:5120491ba317 163
mbed_official 3:5120491ba317 164 if (!ble.hasInitialized()) {
mbed_official 3:5120491ba317 165 operationMode = EDDYSTONE_MODE_BEACON;
mbed_official 3:5120491ba317 166 ble.init(this, &EddystoneService::bleInitComplete);
mbed_official 3:5120491ba317 167 /* Set the device name once more */
mbed_official 3:5120491ba317 168 ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(deviceName));
mbed_official 3:5120491ba317 169 return EDDYSTONE_ERROR_NONE;
mbed_official 3:5120491ba317 170 }
mbed_official 3:5120491ba317 171
mbed_official 3:5120491ba317 172 operationMode = EDDYSTONE_MODE_BEACON;
mbed_official 3:5120491ba317 173 setupBeaconService();
mbed_official 3:5120491ba317 174
mbed_official 3:5120491ba317 175 return EDDYSTONE_ERROR_NONE;
mbed_official 3:5120491ba317 176 }
mbed_official 3:5120491ba317 177
mbed_official 3:5120491ba317 178 EddystoneService::EddystoneError_t EddystoneService::stopCurrentService(void)
mbed_official 3:5120491ba317 179 {
mbed_official 3:5120491ba317 180 switch (operationMode) {
mbed_official 3:5120491ba317 181 case EDDYSTONE_MODE_NONE:
mbed_official 3:5120491ba317 182 return EDDYSTONE_ERROR_INVALID_STATE;
mbed_official 3:5120491ba317 183 case EDDYSTONE_MODE_BEACON:
mbed_official 3:5120491ba317 184 ble.shutdown();
mbed_official 3:5120491ba317 185 stopBeaconService();
mbed_official 3:5120491ba317 186 break;
mbed_official 3:5120491ba317 187 case EDDYSTONE_MODE_CONFIG:
mbed_official 3:5120491ba317 188 ble.shutdown();
mbed_official 3:5120491ba317 189 freeConfigCharacteristics();
mbed_official 3:5120491ba317 190 break;
mbed_official 3:5120491ba317 191 default:
mbed_official 3:5120491ba317 192 /* Some error occurred */
mbed_official 3:5120491ba317 193 error("Invalid EddystonService mode");
mbed_official 3:5120491ba317 194 break;
mbed_official 3:5120491ba317 195 }
mbed_official 3:5120491ba317 196 operationMode = EDDYSTONE_MODE_NONE;
mbed_official 3:5120491ba317 197 /* Currently on some platforms, the BLE stack handles power management,
mbed_official 3:5120491ba317 198 * so we should bring it up again, but not configure it.
mbed_official 3:5120491ba317 199 * Once the system sleep without BLE initialised is fixed, remove this
mbed_official 3:5120491ba317 200 */
mbed_official 3:5120491ba317 201 ble.init(this, &EddystoneService::bleInitComplete);
mbed_official 3:5120491ba317 202
mbed_official 3:5120491ba317 203 return EDDYSTONE_ERROR_NONE;
mbed_official 3:5120491ba317 204 }
mbed_official 3:5120491ba317 205
mbed_official 3:5120491ba317 206 ble_error_t EddystoneService::setCompleteDeviceName(const char *deviceNameIn)
mbed_official 3:5120491ba317 207 {
mbed_official 3:5120491ba317 208 /* Make sure the device name is safe */
mbed_official 3:5120491ba317 209 ble_error_t error = ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(deviceNameIn));
mbed_official 3:5120491ba317 210 if (error == BLE_ERROR_NONE) {
mbed_official 3:5120491ba317 211 deviceName = deviceNameIn;
mbed_official 3:5120491ba317 212 if (operationMode == EDDYSTONE_MODE_CONFIG) {
mbed_official 3:5120491ba317 213 /* Need to update the advertising packets to the new name */
mbed_official 3:5120491ba317 214 setupEddystoneConfigScanResponse();
mbed_official 3:5120491ba317 215 }
mbed_official 3:5120491ba317 216 }
mbed_official 3:5120491ba317 217
mbed_official 3:5120491ba317 218 return error;
mbed_official 3:5120491ba317 219 }
mbed_official 3:5120491ba317 220
mbed_official 3:5120491ba317 221 /* It is not the responsibility of the Eddystone implementation to store
mbed_official 3:5120491ba317 222 * the configured parameters in persistent storage since this is
mbed_official 3:5120491ba317 223 * platform-specific. So we provide this function that returns the
mbed_official 3:5120491ba317 224 * configured values that need to be stored and the main application
mbed_official 3:5120491ba317 225 * takes care of storing them.
mbed_official 3:5120491ba317 226 */
mbed_official 3:5120491ba317 227 void EddystoneService::getEddystoneParams(EddystoneParams_t &params)
mbed_official 3:5120491ba317 228 {
mbed_official 3:5120491ba317 229 params.lockState = lockState;
mbed_official 3:5120491ba317 230 params.flags = flags;
mbed_official 3:5120491ba317 231 params.txPowerMode = txPowerMode;
mbed_official 3:5120491ba317 232 params.urlFramePeriod = urlFramePeriod;
mbed_official 3:5120491ba317 233 params.tlmFramePeriod = tlmFramePeriod;
mbed_official 3:5120491ba317 234 params.uidFramePeriod = uidFramePeriod;
mbed_official 3:5120491ba317 235 params.tlmVersion = tlmFrame.getTLMVersion();
mbed_official 3:5120491ba317 236 params.urlDataLength = urlFrame.getEncodedURLDataLength();
mbed_official 3:5120491ba317 237
mbed_official 3:5120491ba317 238 memcpy(params.advPowerLevels, advPowerLevels, sizeof(PowerLevels_t));
mbed_official 3:5120491ba317 239 memcpy(params.lock, lock, sizeof(Lock_t));
mbed_official 3:5120491ba317 240 memcpy(params.unlock, unlock, sizeof(Lock_t));
mbed_official 3:5120491ba317 241 memcpy(params.urlData, urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength());
mbed_official 3:5120491ba317 242 memcpy(params.uidNamespaceID, uidFrame.getUIDNamespaceID(), sizeof(UIDNamespaceID_t));
mbed_official 3:5120491ba317 243 memcpy(params.uidInstanceID, uidFrame.getUIDInstanceID(), sizeof(UIDInstanceID_t));
mbed_official 3:5120491ba317 244 }
mbed_official 3:5120491ba317 245
mbed_official 3:5120491ba317 246 /* Helper function used only once during constructing the object to avoid
mbed_official 3:5120491ba317 247 * duplicated code.
mbed_official 3:5120491ba317 248 */
mbed_official 3:5120491ba317 249 void EddystoneService::eddystoneConstructorHelper(const PowerLevels_t &advPowerLevelsIn,
mbed_official 3:5120491ba317 250 const PowerLevels_t &radioPowerLevelsIn,
mbed_official 3:5120491ba317 251 uint32_t advConfigIntervalIn)
mbed_official 3:5120491ba317 252 {
mbed_official 3:5120491ba317 253 /* We cannot use correctAdvertisementPeriod() for this check because the function
mbed_official 3:5120491ba317 254 * call to get the minimum advertising interval in the BLE API is different for
mbed_official 3:5120491ba317 255 * connectable and non-connectable advertising.
mbed_official 3:5120491ba317 256 */
mbed_official 3:5120491ba317 257 if (advConfigIntervalIn != 0) {
mbed_official 3:5120491ba317 258 if (advConfigIntervalIn < ble.gap().getMinAdvertisingInterval()) {
mbed_official 3:5120491ba317 259 advConfigInterval = ble.gap().getMinAdvertisingInterval();
mbed_official 3:5120491ba317 260 } else if (advConfigIntervalIn > ble.gap().getMaxAdvertisingInterval()) {
mbed_official 3:5120491ba317 261 advConfigInterval = ble.gap().getMaxAdvertisingInterval();
mbed_official 3:5120491ba317 262 } else {
mbed_official 3:5120491ba317 263 advConfigInterval = advConfigIntervalIn;
mbed_official 3:5120491ba317 264 }
mbed_official 3:5120491ba317 265 }
mbed_official 3:5120491ba317 266
mbed_official 3:5120491ba317 267 memcpy(radioPowerLevels, radioPowerLevelsIn, sizeof(PowerLevels_t));
mbed_official 3:5120491ba317 268 memcpy(advPowerLevels, advPowerLevelsIn, sizeof(PowerLevels_t));
mbed_official 3:5120491ba317 269
mbed_official 3:5120491ba317 270 /* TODO: Note that this timer is started from the time EddystoneService
mbed_official 3:5120491ba317 271 * is initialised and NOT from when the device is booted. So app needs
mbed_official 3:5120491ba317 272 * to take care that EddystoneService is one of the first things to be
mbed_official 3:5120491ba317 273 * started!
mbed_official 3:5120491ba317 274 */
mbed_official 3:5120491ba317 275 timeSinceBootTimer.start();
mbed_official 3:5120491ba317 276
mbed_official 3:5120491ba317 277 /* Set the device name at startup */
mbed_official 3:5120491ba317 278 ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(deviceName));
mbed_official 3:5120491ba317 279 }
mbed_official 3:5120491ba317 280
mbed_official 3:5120491ba317 281 /* When changing modes, we shutdown and init the BLE instance, so
mbed_official 3:5120491ba317 282 * this is needed to complete the initialisation task.
mbed_official 3:5120491ba317 283 */
mbed_official 3:5120491ba317 284 void EddystoneService::bleInitComplete(BLE::InitializationCompleteCallbackContext* initContext)
mbed_official 3:5120491ba317 285 {
mbed_official 3:5120491ba317 286 if (initContext->error != BLE_ERROR_NONE) {
mbed_official 3:5120491ba317 287 /* Initialisation failed */
mbed_official 3:5120491ba317 288 return;
mbed_official 3:5120491ba317 289 }
mbed_official 3:5120491ba317 290
mbed_official 3:5120491ba317 291 switch (operationMode) {
mbed_official 3:5120491ba317 292 case EDDYSTONE_MODE_CONFIG:
mbed_official 3:5120491ba317 293 setupConfigService();
mbed_official 3:5120491ba317 294 break;
mbed_official 3:5120491ba317 295 case EDDYSTONE_MODE_BEACON:
mbed_official 3:5120491ba317 296 setupBeaconService();
mbed_official 3:5120491ba317 297 break;
mbed_official 3:5120491ba317 298 case EDDYSTONE_MODE_NONE:
mbed_official 3:5120491ba317 299 /* We don't need to do anything here, but it isn't an error */
mbed_official 3:5120491ba317 300 break;
mbed_official 3:5120491ba317 301 default:
mbed_official 3:5120491ba317 302 /* Some error occurred */
mbed_official 3:5120491ba317 303 error("Invalid EddystonService mode");
mbed_official 3:5120491ba317 304 break;
mbed_official 3:5120491ba317 305 }
mbed_official 3:5120491ba317 306 }
mbed_official 3:5120491ba317 307
mbed_official 3:5120491ba317 308 void EddystoneService::swapAdvertisedFrame(FrameType frameType)
mbed_official 3:5120491ba317 309 {
mbed_official 3:5120491ba317 310 switch(frameType) {
mbed_official 3:5120491ba317 311 case EDDYSTONE_FRAME_URL:
mbed_official 3:5120491ba317 312 updateAdvertisementPacket(rawUrlFrame, urlFrame.getRawFrameSize());
mbed_official 3:5120491ba317 313 break;
mbed_official 3:5120491ba317 314 case EDDYSTONE_FRAME_UID:
mbed_official 3:5120491ba317 315 updateAdvertisementPacket(rawUidFrame, uidFrame.getRawFrameSize());
mbed_official 3:5120491ba317 316 break;
mbed_official 3:5120491ba317 317 case EDDYSTONE_FRAME_TLM:
mbed_official 3:5120491ba317 318 updateRawTLMFrame();
mbed_official 3:5120491ba317 319 updateAdvertisementPacket(rawTlmFrame, tlmFrame.getRawFrameSize());
mbed_official 3:5120491ba317 320 break;
mbed_official 3:5120491ba317 321 default:
mbed_official 3:5120491ba317 322 /* Some error occurred */
mbed_official 3:5120491ba317 323 error("Frame to swap in does not specify a valid type");
mbed_official 3:5120491ba317 324 break;
mbed_official 3:5120491ba317 325 }
mbed_official 3:5120491ba317 326 }
mbed_official 3:5120491ba317 327
mbed_official 3:5120491ba317 328 /* Helper function that calls user-defined functions to update Battery Voltage and Temperature (if available),
mbed_official 3:5120491ba317 329 * then updates the raw frame data and finally updates the actual advertised packet. This operation must be
mbed_official 3:5120491ba317 330 * done fairly often because the TLM frame TimeSinceBoot must have a 0.1 secs resolution according to the
mbed_official 3:5120491ba317 331 * Eddystone specification.
mbed_official 3:5120491ba317 332 */
mbed_official 3:5120491ba317 333 void EddystoneService::updateRawTLMFrame(void)
mbed_official 3:5120491ba317 334 {
mbed_official 3:5120491ba317 335 if (tlmBeaconTemperatureCallback != NULL) {
mbed_official 3:5120491ba317 336 tlmFrame.updateBeaconTemperature((*tlmBeaconTemperatureCallback)(tlmFrame.getBeaconTemperature()));
mbed_official 3:5120491ba317 337 }
mbed_official 3:5120491ba317 338 if (tlmBatteryVoltageCallback != NULL) {
mbed_official 3:5120491ba317 339 tlmFrame.updateBatteryVoltage((*tlmBatteryVoltageCallback)(tlmFrame.getBatteryVoltage()));
mbed_official 3:5120491ba317 340 }
mbed_official 3:5120491ba317 341 tlmFrame.updateTimeSinceBoot(timeSinceBootTimer.read_ms());
mbed_official 3:5120491ba317 342 tlmFrame.constructTLMFrame(rawTlmFrame);
mbed_official 3:5120491ba317 343 }
mbed_official 3:5120491ba317 344
mbed_official 3:5120491ba317 345 void EddystoneService::updateAdvertisementPacket(const uint8_t* rawFrame, size_t rawFrameLength)
mbed_official 3:5120491ba317 346 {
mbed_official 3:5120491ba317 347 ble.gap().clearAdvertisingPayload();
mbed_official 3:5120491ba317 348 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
mbed_official 3:5120491ba317 349 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, EDDYSTONE_UUID, sizeof(EDDYSTONE_UUID));
mbed_official 3:5120491ba317 350 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, rawFrame, rawFrameLength);
mbed_official 3:5120491ba317 351 }
mbed_official 3:5120491ba317 352
mbed_official 3:5120491ba317 353 void EddystoneService::setupBeaconService(void)
mbed_official 3:5120491ba317 354 {
mbed_official 3:5120491ba317 355 /* Initialise arrays to hold constructed raw frames */
mbed_official 3:5120491ba317 356 if (urlFramePeriod) {
mbed_official 3:5120491ba317 357 rawUrlFrame = new uint8_t[urlFrame.getRawFrameSize()];
mbed_official 3:5120491ba317 358 urlFrame.constructURLFrame(rawUrlFrame, advPowerLevels[txPowerMode]);
mbed_official 3:5120491ba317 359 }
mbed_official 3:5120491ba317 360
mbed_official 3:5120491ba317 361 if (uidFramePeriod) {
mbed_official 3:5120491ba317 362 rawUidFrame = new uint8_t[uidFrame.getRawFrameSize()];
mbed_official 3:5120491ba317 363 uidFrame.constructUIDFrame(rawUidFrame, advPowerLevels[txPowerMode]);
mbed_official 3:5120491ba317 364 }
mbed_official 3:5120491ba317 365
mbed_official 3:5120491ba317 366 if (tlmFramePeriod) {
mbed_official 3:5120491ba317 367 rawTlmFrame = new uint8_t[tlmFrame.getRawFrameSize()];
mbed_official 3:5120491ba317 368 /* Do not initialise because we have to reconstruct every 0.1 secs */
mbed_official 3:5120491ba317 369 }
mbed_official 3:5120491ba317 370
mbed_official 3:5120491ba317 371 /* Configure advertisements */
mbed_official 3:5120491ba317 372 ble.gap().setTxPower(radioPowerLevels[txPowerMode]);
mbed_official 3:5120491ba317 373 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
mbed_official 3:5120491ba317 374 ble.gap().setAdvertisingInterval(ble.gap().getMaxAdvertisingInterval());
mbed_official 3:5120491ba317 375
mbed_official 3:5120491ba317 376 /* Make sure the queue is currently empty */
mbed_official 3:5120491ba317 377 advFrameQueue.reset();
mbed_official 3:5120491ba317 378 /* Setup callbacks to periodically add frames to be advertised to the queue and
mbed_official 3:5120491ba317 379 * add initial frame so that we have something to advertise on startup */
mbed_official 3:5120491ba317 380 if (uidFramePeriod) {
mbed_official 3:5120491ba317 381 advFrameQueue.push(EDDYSTONE_FRAME_UID);
mbed_official 3:5120491ba317 382 uidFrameCallbackHandle = eventQueue.post_every(
mbed_official 3:5120491ba317 383 uidFramePeriod,
mbed_official 3:5120491ba317 384 Callback<void(FrameType)>(this, &EddystoneService::enqueueFrame),
mbed_official 3:5120491ba317 385 EDDYSTONE_FRAME_UID
mbed_official 3:5120491ba317 386 );
mbed_official 3:5120491ba317 387 }
mbed_official 3:5120491ba317 388 if (tlmFramePeriod) {
mbed_official 3:5120491ba317 389 advFrameQueue.push(EDDYSTONE_FRAME_TLM);
mbed_official 3:5120491ba317 390 tlmFrameCallbackHandle = eventQueue.post_every(
mbed_official 3:5120491ba317 391 tlmFramePeriod,
mbed_official 3:5120491ba317 392 Callback<void(FrameType)>(this, &EddystoneService::enqueueFrame),
mbed_official 3:5120491ba317 393 EDDYSTONE_FRAME_TLM
mbed_official 3:5120491ba317 394 );
mbed_official 3:5120491ba317 395 }
mbed_official 3:5120491ba317 396 if (urlFramePeriod) {
mbed_official 3:5120491ba317 397 advFrameQueue.push(EDDYSTONE_FRAME_URL);
mbed_official 3:5120491ba317 398 tlmFrameCallbackHandle = eventQueue.post_every(
mbed_official 3:5120491ba317 399 urlFramePeriod,
mbed_official 3:5120491ba317 400 Callback<void(FrameType)>(this, &EddystoneService::enqueueFrame),
mbed_official 3:5120491ba317 401 EDDYSTONE_FRAME_URL
mbed_official 3:5120491ba317 402 );
mbed_official 3:5120491ba317 403 }
mbed_official 3:5120491ba317 404
mbed_official 3:5120491ba317 405 /* Start advertising */
mbed_official 3:5120491ba317 406 manageRadio();
mbed_official 3:5120491ba317 407 }
mbed_official 3:5120491ba317 408
mbed_official 3:5120491ba317 409 void EddystoneService::enqueueFrame(FrameType frameType)
mbed_official 3:5120491ba317 410 {
mbed_official 3:5120491ba317 411 advFrameQueue.push(frameType);
mbed_official 3:5120491ba317 412 if (!radioManagerCallbackHandle) {
mbed_official 3:5120491ba317 413 /* Advertising stopped and there is not callback posted in the scheduler. Just
mbed_official 3:5120491ba317 414 * execute the manager to resume advertising */
mbed_official 3:5120491ba317 415 manageRadio();
mbed_official 3:5120491ba317 416 }
mbed_official 3:5120491ba317 417 }
mbed_official 3:5120491ba317 418
mbed_official 3:5120491ba317 419 void EddystoneService::manageRadio(void)
mbed_official 3:5120491ba317 420 {
mbed_official 3:5120491ba317 421 FrameType frameType;
mbed_official 3:5120491ba317 422 uint32_t startTimeManageRadio = timeSinceBootTimer.read_ms();
mbed_official 3:5120491ba317 423
mbed_official 3:5120491ba317 424 /* Signal that there is currently no callback posted */
mbed_official 3:5120491ba317 425 radioManagerCallbackHandle = 0;
mbed_official 3:5120491ba317 426
mbed_official 3:5120491ba317 427 if (advFrameQueue.pop(frameType)) {
mbed_official 3:5120491ba317 428 /* We have something to advertise */
mbed_official 3:5120491ba317 429 if (ble.gap().getState().advertising) {
mbed_official 3:5120491ba317 430 ble.gap().stopAdvertising();
mbed_official 3:5120491ba317 431 }
mbed_official 3:5120491ba317 432 swapAdvertisedFrame(frameType);
mbed_official 3:5120491ba317 433 ble.gap().startAdvertising();
mbed_official 3:5120491ba317 434
mbed_official 3:5120491ba317 435 /* Increase the advertised packet count in TLM frame */
mbed_official 3:5120491ba317 436 tlmFrame.updatePduCount();
mbed_official 3:5120491ba317 437
mbed_official 3:5120491ba317 438 /* Post a callback to itself to stop the advertisement or pop the next
mbed_official 3:5120491ba317 439 * frame from the queue. However, take into account the time taken to
mbed_official 3:5120491ba317 440 * swap in this frame. */
mbed_official 3:5120491ba317 441 radioManagerCallbackHandle = eventQueue.post_in(
mbed_official 3:5120491ba317 442 ble.gap().getMinNonConnectableAdvertisingInterval() - (timeSinceBootTimer.read_ms() - startTimeManageRadio),
mbed_official 3:5120491ba317 443 Callback<void()>(this, &EddystoneService::manageRadio)
mbed_official 3:5120491ba317 444 );
mbed_official 3:5120491ba317 445 } else if (ble.gap().getState().advertising) {
mbed_official 3:5120491ba317 446 /* Nothing else to advertise, stop advertising and do not schedule any callbacks */
mbed_official 3:5120491ba317 447 ble.gap().stopAdvertising();
mbed_official 3:5120491ba317 448 }
mbed_official 3:5120491ba317 449 }
mbed_official 3:5120491ba317 450
mbed_official 3:5120491ba317 451 void EddystoneService::setupConfigService(void)
mbed_official 3:5120491ba317 452 {
mbed_official 3:5120491ba317 453 lockStateChar = new ReadOnlyGattCharacteristic<bool>(UUID_LOCK_STATE_CHAR, &lockState);
mbed_official 3:5120491ba317 454 lockChar = new WriteOnlyArrayGattCharacteristic<uint8_t, sizeof(Lock_t)>(UUID_LOCK_CHAR, lock);
mbed_official 3:5120491ba317 455 unlockChar = new WriteOnlyArrayGattCharacteristic<uint8_t, sizeof(Lock_t)>(UUID_UNLOCK_CHAR, unlock);
mbed_official 3:5120491ba317 456 urlDataChar = new GattCharacteristic(UUID_URL_DATA_CHAR, urlFrame.getEncodedURLData(), 0, URL_DATA_MAX, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
mbed_official 3:5120491ba317 457 flagsChar = new ReadWriteGattCharacteristic<uint8_t>(UUID_FLAGS_CHAR, &flags);
mbed_official 3:5120491ba317 458 advPowerLevelsChar = new ReadWriteArrayGattCharacteristic<int8_t, sizeof(PowerLevels_t)>(UUID_ADV_POWER_LEVELS_CHAR, advPowerLevels);
mbed_official 3:5120491ba317 459 txPowerModeChar = new ReadWriteGattCharacteristic<uint8_t>(UUID_TX_POWER_MODE_CHAR, &txPowerMode);
mbed_official 3:5120491ba317 460 beaconPeriodChar = new ReadWriteGattCharacteristic<uint16_t>(UUID_BEACON_PERIOD_CHAR, &urlFramePeriod);
mbed_official 3:5120491ba317 461 resetChar = new WriteOnlyGattCharacteristic<bool>(UUID_RESET_CHAR, &resetFlag);
mbed_official 3:5120491ba317 462
mbed_official 3:5120491ba317 463 lockChar->setWriteAuthorizationCallback(this, &EddystoneService::lockAuthorizationCallback);
mbed_official 3:5120491ba317 464 unlockChar->setWriteAuthorizationCallback(this, &EddystoneService::unlockAuthorizationCallback);
mbed_official 3:5120491ba317 465 urlDataChar->setWriteAuthorizationCallback(this, &EddystoneService::urlDataWriteAuthorizationCallback);
mbed_official 3:5120491ba317 466 flagsChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<uint8_t>);
mbed_official 3:5120491ba317 467 advPowerLevelsChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<PowerLevels_t>);
mbed_official 3:5120491ba317 468 txPowerModeChar->setWriteAuthorizationCallback(this, &EddystoneService::powerModeAuthorizationCallback);
mbed_official 3:5120491ba317 469 beaconPeriodChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<uint16_t>);
mbed_official 3:5120491ba317 470 resetChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<bool>);
mbed_official 3:5120491ba317 471
mbed_official 3:5120491ba317 472 charTable[0] = lockStateChar;
mbed_official 3:5120491ba317 473 charTable[1] = lockChar;
mbed_official 3:5120491ba317 474 charTable[2] = unlockChar;
mbed_official 3:5120491ba317 475 charTable[3] = urlDataChar;
mbed_official 3:5120491ba317 476 charTable[4] = flagsChar;
mbed_official 3:5120491ba317 477 charTable[5] = advPowerLevelsChar;
mbed_official 3:5120491ba317 478 charTable[6] = txPowerModeChar;
mbed_official 3:5120491ba317 479 charTable[7] = beaconPeriodChar;
mbed_official 3:5120491ba317 480 charTable[8] = resetChar;
mbed_official 3:5120491ba317 481
mbed_official 3:5120491ba317 482 GattService configService(UUID_URL_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
mbed_official 3:5120491ba317 483
mbed_official 3:5120491ba317 484 ble.gattServer().addService(configService);
mbed_official 3:5120491ba317 485 ble.gattServer().onDataWritten(this, &EddystoneService::onDataWrittenCallback);
mbed_official 3:5120491ba317 486 updateCharacteristicValues();
mbed_official 3:5120491ba317 487 setupEddystoneConfigAdvertisements();
mbed_official 3:5120491ba317 488 }
mbed_official 3:5120491ba317 489
mbed_official 3:5120491ba317 490 void EddystoneService::freeConfigCharacteristics(void)
mbed_official 3:5120491ba317 491 {
mbed_official 3:5120491ba317 492 delete lockStateChar;
mbed_official 3:5120491ba317 493 delete lockChar;
mbed_official 3:5120491ba317 494 delete unlockChar;
mbed_official 3:5120491ba317 495 delete urlDataChar;
mbed_official 3:5120491ba317 496 delete flagsChar;
mbed_official 3:5120491ba317 497 delete advPowerLevelsChar;
mbed_official 3:5120491ba317 498 delete txPowerModeChar;
mbed_official 3:5120491ba317 499 delete beaconPeriodChar;
mbed_official 3:5120491ba317 500 delete resetChar;
mbed_official 3:5120491ba317 501 }
mbed_official 3:5120491ba317 502
mbed_official 3:5120491ba317 503 void EddystoneService::stopBeaconService(void)
mbed_official 3:5120491ba317 504 {
mbed_official 3:5120491ba317 505 /* Free unused memory */
mbed_official 3:5120491ba317 506 if (rawUrlFrame) {
mbed_official 3:5120491ba317 507 delete[] rawUrlFrame;
mbed_official 3:5120491ba317 508 rawUrlFrame = NULL;
mbed_official 3:5120491ba317 509 }
mbed_official 3:5120491ba317 510 if (rawUidFrame) {
mbed_official 3:5120491ba317 511 delete[] rawUidFrame;
mbed_official 3:5120491ba317 512 rawUidFrame = NULL;
mbed_official 3:5120491ba317 513 }
mbed_official 3:5120491ba317 514 if (rawTlmFrame) {
mbed_official 3:5120491ba317 515 delete[] rawTlmFrame;
mbed_official 3:5120491ba317 516 rawTlmFrame = NULL;
mbed_official 3:5120491ba317 517 }
mbed_official 3:5120491ba317 518
mbed_official 3:5120491ba317 519 /* Unschedule callbacks */
mbed_official 3:5120491ba317 520 if (urlFrameCallbackHandle) {
mbed_official 3:5120491ba317 521 eventQueue.cancel(urlFrameCallbackHandle);
mbed_official 3:5120491ba317 522 urlFrameCallbackHandle = 0;
mbed_official 3:5120491ba317 523 }
mbed_official 3:5120491ba317 524 if (uidFrameCallbackHandle) {
mbed_official 3:5120491ba317 525 eventQueue.cancel(uidFrameCallbackHandle);
mbed_official 3:5120491ba317 526 uidFrameCallbackHandle = 0;
mbed_official 3:5120491ba317 527 }
mbed_official 3:5120491ba317 528 if (tlmFrameCallbackHandle) {
mbed_official 3:5120491ba317 529 eventQueue.cancel(tlmFrameCallbackHandle);
mbed_official 3:5120491ba317 530 tlmFrameCallbackHandle = 0;
mbed_official 3:5120491ba317 531 }
mbed_official 3:5120491ba317 532 if (radioManagerCallbackHandle) {
mbed_official 3:5120491ba317 533 eventQueue.cancel(radioManagerCallbackHandle);
mbed_official 3:5120491ba317 534 radioManagerCallbackHandle = 0;
mbed_official 3:5120491ba317 535 }
mbed_official 3:5120491ba317 536 }
mbed_official 3:5120491ba317 537
mbed_official 3:5120491ba317 538 /*
mbed_official 3:5120491ba317 539 * Internal helper function used to update the GATT database following any
mbed_official 3:5120491ba317 540 * change to the internal state of the service object.
mbed_official 3:5120491ba317 541 */
mbed_official 3:5120491ba317 542 void EddystoneService::updateCharacteristicValues(void)
mbed_official 3:5120491ba317 543 {
mbed_official 3:5120491ba317 544 ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(bool));
mbed_official 3:5120491ba317 545 ble.gattServer().write(urlDataChar->getValueHandle(), urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength());
mbed_official 3:5120491ba317 546 ble.gattServer().write(flagsChar->getValueHandle(), &flags, sizeof(uint8_t));
mbed_official 3:5120491ba317 547 ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&urlFramePeriod), sizeof(uint16_t));
mbed_official 3:5120491ba317 548 ble.gattServer().write(txPowerModeChar->getValueHandle(), &txPowerMode, sizeof(uint8_t));
mbed_official 3:5120491ba317 549 ble.gattServer().write(advPowerLevelsChar->getValueHandle(), reinterpret_cast<uint8_t *>(advPowerLevels), sizeof(PowerLevels_t));
mbed_official 3:5120491ba317 550 ble.gattServer().write(lockChar->getValueHandle(), lock, sizeof(PowerLevels_t));
mbed_official 3:5120491ba317 551 ble.gattServer().write(unlockChar->getValueHandle(), unlock, sizeof(PowerLevels_t));
mbed_official 3:5120491ba317 552 }
mbed_official 3:5120491ba317 553
mbed_official 3:5120491ba317 554 void EddystoneService::setupEddystoneConfigAdvertisements(void)
mbed_official 3:5120491ba317 555 {
mbed_official 3:5120491ba317 556 ble.gap().clearAdvertisingPayload();
mbed_official 3:5120491ba317 557
mbed_official 3:5120491ba317 558 /* Accumulate the new payload */
mbed_official 3:5120491ba317 559 ble.gap().accumulateAdvertisingPayload(
mbed_official 3:5120491ba317 560 GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE
mbed_official 3:5120491ba317 561 );
mbed_official 3:5120491ba317 562 /* UUID is in different order in the ADV frame (!) */
mbed_official 3:5120491ba317 563 uint8_t reversedServiceUUID[sizeof(UUID_URL_BEACON_SERVICE)];
mbed_official 3:5120491ba317 564 for (size_t i = 0; i < sizeof(UUID_URL_BEACON_SERVICE); i++) {
mbed_official 3:5120491ba317 565 reversedServiceUUID[i] = UUID_URL_BEACON_SERVICE[sizeof(UUID_URL_BEACON_SERVICE) - i - 1];
mbed_official 3:5120491ba317 566 }
mbed_official 3:5120491ba317 567 ble.gap().accumulateAdvertisingPayload(
mbed_official 3:5120491ba317 568 GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,
mbed_official 3:5120491ba317 569 reversedServiceUUID,
mbed_official 3:5120491ba317 570 sizeof(reversedServiceUUID)
mbed_official 3:5120491ba317 571 );
mbed_official 3:5120491ba317 572 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG);
mbed_official 3:5120491ba317 573 setupEddystoneConfigScanResponse();
mbed_official 3:5120491ba317 574
mbed_official 3:5120491ba317 575 ble.gap().setTxPower(radioPowerLevels[txPowerMode]);
mbed_official 3:5120491ba317 576 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
mbed_official 3:5120491ba317 577 ble.gap().setAdvertisingInterval(advConfigInterval);
mbed_official 3:5120491ba317 578 ble.gap().startAdvertising();
mbed_official 3:5120491ba317 579 }
mbed_official 3:5120491ba317 580
mbed_official 3:5120491ba317 581 void EddystoneService::setupEddystoneConfigScanResponse(void)
mbed_official 3:5120491ba317 582 {
mbed_official 3:5120491ba317 583 ble.gap().clearScanResponse();
mbed_official 3:5120491ba317 584 ble.gap().accumulateScanResponse(
mbed_official 3:5120491ba317 585 GapAdvertisingData::COMPLETE_LOCAL_NAME,
mbed_official 3:5120491ba317 586 reinterpret_cast<const uint8_t *>(deviceName),
mbed_official 3:5120491ba317 587 strlen(deviceName)
mbed_official 3:5120491ba317 588 );
mbed_official 3:5120491ba317 589 ble.gap().accumulateScanResponse(
mbed_official 3:5120491ba317 590 GapAdvertisingData::TX_POWER_LEVEL,
mbed_official 3:5120491ba317 591 reinterpret_cast<uint8_t *>(&advPowerLevels[TX_POWER_MODE_LOW]),
mbed_official 3:5120491ba317 592 sizeof(uint8_t)
mbed_official 3:5120491ba317 593 );
mbed_official 3:5120491ba317 594 }
mbed_official 3:5120491ba317 595
mbed_official 3:5120491ba317 596 void EddystoneService::lockAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
mbed_official 3:5120491ba317 597 {
mbed_official 3:5120491ba317 598 if (lockState) {
mbed_official 3:5120491ba317 599 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
mbed_official 3:5120491ba317 600 } else if (authParams->len != sizeof(Lock_t)) {
mbed_official 3:5120491ba317 601 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
mbed_official 3:5120491ba317 602 } else if (authParams->offset != 0) {
mbed_official 3:5120491ba317 603 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
mbed_official 3:5120491ba317 604 } else {
mbed_official 3:5120491ba317 605 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
mbed_official 3:5120491ba317 606 }
mbed_official 3:5120491ba317 607 }
mbed_official 3:5120491ba317 608
mbed_official 3:5120491ba317 609 void EddystoneService::unlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
mbed_official 3:5120491ba317 610 {
mbed_official 3:5120491ba317 611 if (!lockState && (authParams->len == sizeof(Lock_t))) {
mbed_official 3:5120491ba317 612 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
mbed_official 3:5120491ba317 613 } else if (authParams->len != sizeof(Lock_t)) {
mbed_official 3:5120491ba317 614 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
mbed_official 3:5120491ba317 615 } else if (authParams->offset != 0) {
mbed_official 3:5120491ba317 616 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
mbed_official 3:5120491ba317 617 } else if (memcmp(authParams->data, lock, sizeof(Lock_t)) != 0) {
mbed_official 3:5120491ba317 618 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
mbed_official 3:5120491ba317 619 } else {
mbed_official 3:5120491ba317 620 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
mbed_official 3:5120491ba317 621 }
mbed_official 3:5120491ba317 622 }
mbed_official 3:5120491ba317 623
mbed_official 3:5120491ba317 624 void EddystoneService::urlDataWriteAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
mbed_official 3:5120491ba317 625 {
mbed_official 3:5120491ba317 626 if (lockState) {
mbed_official 3:5120491ba317 627 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
mbed_official 3:5120491ba317 628 } else if (authParams->offset != 0) {
mbed_official 3:5120491ba317 629 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
mbed_official 3:5120491ba317 630 } else {
mbed_official 3:5120491ba317 631 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
mbed_official 3:5120491ba317 632 }
mbed_official 3:5120491ba317 633 }
mbed_official 3:5120491ba317 634
mbed_official 3:5120491ba317 635 void EddystoneService::powerModeAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
mbed_official 3:5120491ba317 636 {
mbed_official 3:5120491ba317 637 if (lockState) {
mbed_official 3:5120491ba317 638 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
mbed_official 3:5120491ba317 639 } else if (authParams->len != sizeof(uint8_t)) {
mbed_official 3:5120491ba317 640 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
mbed_official 3:5120491ba317 641 } else if (authParams->offset != 0) {
mbed_official 3:5120491ba317 642 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
mbed_official 3:5120491ba317 643 } else if (*((uint8_t *)authParams->data) >= NUM_POWER_MODES) {
mbed_official 3:5120491ba317 644 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
mbed_official 3:5120491ba317 645 } else {
mbed_official 3:5120491ba317 646 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
mbed_official 3:5120491ba317 647 }
mbed_official 3:5120491ba317 648 }
mbed_official 3:5120491ba317 649
mbed_official 3:5120491ba317 650 template <typename T>
mbed_official 3:5120491ba317 651 void EddystoneService::basicAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
mbed_official 3:5120491ba317 652 {
mbed_official 3:5120491ba317 653 if (lockState) {
mbed_official 3:5120491ba317 654 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
mbed_official 3:5120491ba317 655 } else if (authParams->len != sizeof(T)) {
mbed_official 3:5120491ba317 656 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
mbed_official 3:5120491ba317 657 } else if (authParams->offset != 0) {
mbed_official 3:5120491ba317 658 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
mbed_official 3:5120491ba317 659 } else {
mbed_official 3:5120491ba317 660 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
mbed_official 3:5120491ba317 661 }
mbed_official 3:5120491ba317 662 }
mbed_official 3:5120491ba317 663
mbed_official 3:5120491ba317 664 /*
mbed_official 3:5120491ba317 665 * This callback is invoked when a GATT client attempts to modify any of the
mbed_official 3:5120491ba317 666 * characteristics of this service. Attempts to do so are also applied to
mbed_official 3:5120491ba317 667 * the internal state of this service object.
mbed_official 3:5120491ba317 668 */
mbed_official 3:5120491ba317 669 void EddystoneService::onDataWrittenCallback(const GattWriteCallbackParams *writeParams)
mbed_official 3:5120491ba317 670 {
mbed_official 3:5120491ba317 671 uint16_t handle = writeParams->handle;
mbed_official 3:5120491ba317 672
mbed_official 3:5120491ba317 673 if (handle == lockChar->getValueHandle()) {
mbed_official 3:5120491ba317 674 memcpy(lock, writeParams->data, sizeof(Lock_t));
mbed_official 3:5120491ba317 675 /* Set the state to be locked by the lock code (note: zeros are a valid lock) */
mbed_official 3:5120491ba317 676 lockState = true;
mbed_official 3:5120491ba317 677 ble.gattServer().write(lockChar->getValueHandle(), lock, sizeof(PowerLevels_t));
mbed_official 3:5120491ba317 678 ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(bool));
mbed_official 3:5120491ba317 679 } else if (handle == unlockChar->getValueHandle()) {
mbed_official 3:5120491ba317 680 /* Validated earlier */
mbed_official 3:5120491ba317 681 lockState = false;
mbed_official 3:5120491ba317 682 ble.gattServer().write(unlockChar->getValueHandle(), unlock, sizeof(PowerLevels_t));
mbed_official 3:5120491ba317 683 ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(bool));
mbed_official 3:5120491ba317 684 } else if (handle == urlDataChar->getValueHandle()) {
mbed_official 3:5120491ba317 685 urlFrame.setEncodedURLData(writeParams->data, writeParams->len);
mbed_official 3:5120491ba317 686 ble.gattServer().write(urlDataChar->getValueHandle(), urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength());
mbed_official 3:5120491ba317 687 } else if (handle == flagsChar->getValueHandle()) {
mbed_official 3:5120491ba317 688 flags = *(writeParams->data);
mbed_official 3:5120491ba317 689 ble.gattServer().write(flagsChar->getValueHandle(), &flags, sizeof(uint8_t));
mbed_official 3:5120491ba317 690 } else if (handle == advPowerLevelsChar->getValueHandle()) {
mbed_official 3:5120491ba317 691 memcpy(advPowerLevels, writeParams->data, sizeof(PowerLevels_t));
mbed_official 3:5120491ba317 692 ble.gattServer().write(advPowerLevelsChar->getValueHandle(), reinterpret_cast<uint8_t *>(advPowerLevels), sizeof(PowerLevels_t));
mbed_official 3:5120491ba317 693 } else if (handle == txPowerModeChar->getValueHandle()) {
mbed_official 3:5120491ba317 694 txPowerMode = *(writeParams->data);
mbed_official 3:5120491ba317 695 ble.gattServer().write(txPowerModeChar->getValueHandle(), &txPowerMode, sizeof(uint8_t));
mbed_official 3:5120491ba317 696 } else if (handle == beaconPeriodChar->getValueHandle()) {
mbed_official 3:5120491ba317 697 uint16_t tmpBeaconPeriod = correctAdvertisementPeriod(*((uint16_t *)(writeParams->data)));
mbed_official 3:5120491ba317 698 if (tmpBeaconPeriod != urlFramePeriod) {
mbed_official 3:5120491ba317 699 urlFramePeriod = tmpBeaconPeriod;
mbed_official 3:5120491ba317 700 ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&urlFramePeriod), sizeof(uint16_t));
mbed_official 3:5120491ba317 701 }
mbed_official 3:5120491ba317 702 } else if (handle == resetChar->getValueHandle() && (*((uint8_t *)writeParams->data) != 0)) {
mbed_official 3:5120491ba317 703 /* Reset characteristics to default values */
mbed_official 3:5120491ba317 704 flags = 0;
mbed_official 3:5120491ba317 705 txPowerMode = TX_POWER_MODE_LOW;
mbed_official 3:5120491ba317 706 urlFramePeriod = DEFAULT_URL_FRAME_PERIOD_MSEC;
mbed_official 3:5120491ba317 707
mbed_official 3:5120491ba317 708 urlFrame.setURLData(DEFAULT_URL);
mbed_official 3:5120491ba317 709 memset(lock, 0, sizeof(Lock_t));
mbed_official 3:5120491ba317 710
mbed_official 3:5120491ba317 711 ble.gattServer().write(urlDataChar->getValueHandle(), urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength());
mbed_official 3:5120491ba317 712 ble.gattServer().write(flagsChar->getValueHandle(), &flags, sizeof(uint8_t));
mbed_official 3:5120491ba317 713 ble.gattServer().write(txPowerModeChar->getValueHandle(), &txPowerMode, sizeof(uint8_t));
mbed_official 3:5120491ba317 714 ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&urlFramePeriod), sizeof(uint16_t));
mbed_official 3:5120491ba317 715 ble.gattServer().write(lockChar->getValueHandle(), lock, sizeof(PowerLevels_t));
mbed_official 3:5120491ba317 716 }
mbed_official 3:5120491ba317 717 }
mbed_official 3:5120491ba317 718
mbed_official 3:5120491ba317 719 uint16_t EddystoneService::correctAdvertisementPeriod(uint16_t beaconPeriodIn) const
mbed_official 3:5120491ba317 720 {
mbed_official 3:5120491ba317 721 /* Re-map beaconPeriod to within permissible bounds if necessary. */
mbed_official 3:5120491ba317 722 if (beaconPeriodIn != 0) {
mbed_official 3:5120491ba317 723 if (beaconPeriodIn < ble.gap().getMinNonConnectableAdvertisingInterval()) {
mbed_official 3:5120491ba317 724 return ble.gap().getMinNonConnectableAdvertisingInterval();
mbed_official 3:5120491ba317 725 } else if (beaconPeriodIn > ble.gap().getMaxAdvertisingInterval()) {
mbed_official 3:5120491ba317 726 return ble.gap().getMaxAdvertisingInterval();
mbed_official 3:5120491ba317 727 }
mbed_official 3:5120491ba317 728 }
mbed_official 3:5120491ba317 729 return beaconPeriodIn;
mbed_official 3:5120491ba317 730 }
mbed_official 3:5120491ba317 731
mbed_official 3:5120491ba317 732 void EddystoneService::setURLFrameAdvertisingInterval(uint16_t urlFrameIntervalIn)
mbed_official 3:5120491ba317 733 {
mbed_official 3:5120491ba317 734 if (urlFrameIntervalIn == urlFramePeriod) {
mbed_official 3:5120491ba317 735 /* Do nothing */
mbed_official 3:5120491ba317 736 return;
mbed_official 3:5120491ba317 737 }
mbed_official 3:5120491ba317 738
mbed_official 3:5120491ba317 739 /* Make sure the input period is within bounds */
mbed_official 3:5120491ba317 740 urlFramePeriod = correctAdvertisementPeriod(urlFrameIntervalIn);
mbed_official 3:5120491ba317 741
mbed_official 3:5120491ba317 742 if (operationMode == EDDYSTONE_MODE_BEACON) {
mbed_official 3:5120491ba317 743 if (urlFrameCallbackHandle) {
mbed_official 3:5120491ba317 744 eventQueue.cancel(urlFrameCallbackHandle);
mbed_official 3:5120491ba317 745 } else {
mbed_official 3:5120491ba317 746 /* This frame was just enabled */
mbed_official 3:5120491ba317 747 if (!rawUidFrame && urlFramePeriod) {
mbed_official 3:5120491ba317 748 /* Allocate memory for this frame and construct it */
mbed_official 3:5120491ba317 749 rawUrlFrame = new uint8_t[urlFrame.getRawFrameSize()];
mbed_official 3:5120491ba317 750 urlFrame.constructURLFrame(rawUrlFrame, advPowerLevels[txPowerMode]);
mbed_official 3:5120491ba317 751 }
mbed_official 3:5120491ba317 752 }
mbed_official 3:5120491ba317 753
mbed_official 3:5120491ba317 754 if (urlFramePeriod) {
mbed_official 3:5120491ba317 755 /* Currently the only way to change the period of a callback
mbed_official 3:5120491ba317 756 * is to cancel it and reschedule
mbed_official 3:5120491ba317 757 */
mbed_official 3:5120491ba317 758 urlFrameCallbackHandle = eventQueue.post_every(
mbed_official 3:5120491ba317 759 urlFramePeriod,
mbed_official 3:5120491ba317 760 Callback<void(FrameType)>(this, &EddystoneService::enqueueFrame),
mbed_official 3:5120491ba317 761 EDDYSTONE_FRAME_URL
mbed_official 3:5120491ba317 762 );
mbed_official 3:5120491ba317 763 } else {
mbed_official 3:5120491ba317 764 urlFrameCallbackHandle = 0;
mbed_official 3:5120491ba317 765 }
mbed_official 3:5120491ba317 766 } else if (operationMode == EDDYSTONE_MODE_CONFIG) {
mbed_official 3:5120491ba317 767 ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&urlFramePeriod), sizeof(uint16_t));
mbed_official 3:5120491ba317 768 }
mbed_official 3:5120491ba317 769 }
mbed_official 3:5120491ba317 770
mbed_official 3:5120491ba317 771 void EddystoneService::setUIDFrameAdvertisingInterval(uint16_t uidFrameIntervalIn)
mbed_official 3:5120491ba317 772 {
mbed_official 3:5120491ba317 773 if (uidFrameIntervalIn == uidFramePeriod) {
mbed_official 3:5120491ba317 774 /* Do nothing */
mbed_official 3:5120491ba317 775 return;
mbed_official 3:5120491ba317 776 }
mbed_official 3:5120491ba317 777
mbed_official 3:5120491ba317 778 /* Make sure the input period is within bounds */
mbed_official 3:5120491ba317 779 uidFramePeriod = correctAdvertisementPeriod(uidFrameIntervalIn);
mbed_official 3:5120491ba317 780
mbed_official 3:5120491ba317 781 if (operationMode == EDDYSTONE_MODE_BEACON) {
mbed_official 3:5120491ba317 782 if (uidFrameCallbackHandle) {
mbed_official 3:5120491ba317 783 /* The advertisement interval changes, update the periodic callback */
mbed_official 3:5120491ba317 784 eventQueue.cancel(uidFrameCallbackHandle);
mbed_official 3:5120491ba317 785 } else {
mbed_official 3:5120491ba317 786 /* This frame was just enabled */
mbed_official 3:5120491ba317 787 if (!rawUidFrame && uidFramePeriod) {
mbed_official 3:5120491ba317 788 /* Allocate memory for this frame and construct it */
mbed_official 3:5120491ba317 789 rawUidFrame = new uint8_t[uidFrame.getRawFrameSize()];
mbed_official 3:5120491ba317 790 uidFrame.constructUIDFrame(rawUidFrame, advPowerLevels[txPowerMode]);
mbed_official 3:5120491ba317 791 }
mbed_official 3:5120491ba317 792 }
mbed_official 3:5120491ba317 793
mbed_official 3:5120491ba317 794 if (uidFramePeriod) {
mbed_official 3:5120491ba317 795 /* Currently the only way to change the period of a callback
mbed_official 3:5120491ba317 796 * is to cancel it and reschedule
mbed_official 3:5120491ba317 797 */
mbed_official 3:5120491ba317 798 uidFrameCallbackHandle = eventQueue.post_every(
mbed_official 3:5120491ba317 799 uidFramePeriod,
mbed_official 3:5120491ba317 800 Callback<void(FrameType)>(this, &EddystoneService::enqueueFrame),
mbed_official 3:5120491ba317 801 EDDYSTONE_FRAME_UID
mbed_official 3:5120491ba317 802 );
mbed_official 3:5120491ba317 803 } else {
mbed_official 3:5120491ba317 804 uidFrameCallbackHandle = 0;
mbed_official 3:5120491ba317 805 }
mbed_official 3:5120491ba317 806 }
mbed_official 3:5120491ba317 807 }
mbed_official 3:5120491ba317 808
mbed_official 3:5120491ba317 809 void EddystoneService::setTLMFrameAdvertisingInterval(uint16_t tlmFrameIntervalIn)
mbed_official 3:5120491ba317 810 {
mbed_official 3:5120491ba317 811 if (tlmFrameIntervalIn == tlmFramePeriod) {
mbed_official 3:5120491ba317 812 /* Do nothing */
mbed_official 3:5120491ba317 813 return;
mbed_official 3:5120491ba317 814 }
mbed_official 3:5120491ba317 815
mbed_official 3:5120491ba317 816 /* Make sure the input period is within bounds */
mbed_official 3:5120491ba317 817 tlmFramePeriod = correctAdvertisementPeriod(tlmFrameIntervalIn);
mbed_official 3:5120491ba317 818
mbed_official 3:5120491ba317 819 if (operationMode == EDDYSTONE_MODE_BEACON) {
mbed_official 3:5120491ba317 820 if (tlmFrameCallbackHandle) {
mbed_official 3:5120491ba317 821 /* The advertisement interval changes, update periodic callback */
mbed_official 3:5120491ba317 822 eventQueue.cancel(tlmFrameCallbackHandle);
mbed_official 3:5120491ba317 823 } else {
mbed_official 3:5120491ba317 824 /* This frame was just enabled */
mbed_official 3:5120491ba317 825 if (!rawTlmFrame && tlmFramePeriod) {
mbed_official 3:5120491ba317 826 /* Allocate memory for this frame and construct it */
mbed_official 3:5120491ba317 827 rawTlmFrame = new uint8_t[tlmFrame.getRawFrameSize()];
mbed_official 3:5120491ba317 828 /* Do not construct the TLM frame because this changes every 0.1 seconds */
mbed_official 3:5120491ba317 829 }
mbed_official 3:5120491ba317 830 }
mbed_official 3:5120491ba317 831
mbed_official 3:5120491ba317 832 if (tlmFramePeriod) {
mbed_official 3:5120491ba317 833 /* Currently the only way to change the period of a callback
mbed_official 3:5120491ba317 834 * is to cancel it and reschedule
mbed_official 3:5120491ba317 835 */
mbed_official 3:5120491ba317 836 tlmFrameCallbackHandle = eventQueue.post_every(
mbed_official 3:5120491ba317 837 tlmFramePeriod,
mbed_official 3:5120491ba317 838 Callback<void(FrameType)>(this, &EddystoneService::enqueueFrame),
mbed_official 3:5120491ba317 839 EDDYSTONE_FRAME_TLM
mbed_official 3:5120491ba317 840 );
mbed_official 3:5120491ba317 841 } else {
mbed_official 3:5120491ba317 842 tlmFrameCallbackHandle = 0;
mbed_official 3:5120491ba317 843 }
mbed_official 3:5120491ba317 844 }
mbed_official 3:5120491ba317 845 }