The eddystone config service allows you to configure the eddystone frame data over BLE for a set period of time and then starts an eddystone beacon. This example defaults to 30 seconds of config time.

Dependencies:   BLE_API mbed nRF51822 X_NUCLEO_IDB0XA1

Fork of BLE_EddystoneBeaconConfigServiceRelease by Austin Blackstone

This is the eddystone config service. This code starts up and for a user configured time period (default 30 seconds) will advertise the configuration service.

The configuration service allows for modifying various frames of the eddystone specification.

For more details on the Configuration Service please see : https://github.com/google/eddystone/blob/master/eddystone-url/docs/config-service-spec.md

Committer:
Vincent Coubard
Date:
Tue Sep 20 14:21:04 2016 +0100
Revision:
8:f53d48e5d64f
Parent:
6:321047f0190a
Update libraries and add support of ST shield.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
andresag 6:321047f0190a 1 /* mbed Microcontroller Library
andresag 6:321047f0190a 2 * Copyright (c) 2006-2015 ARM Limited
andresag 6:321047f0190a 3 *
andresag 6:321047f0190a 4 * Licensed under the Apache License, Version 2.0 (the "License");
andresag 6:321047f0190a 5 * you may not use this file except in compliance with the License.
andresag 6:321047f0190a 6 * You may obtain a copy of the License at
andresag 6:321047f0190a 7 *
andresag 6:321047f0190a 8 * http://www.apache.org/licenses/LICENSE-2.0
andresag 6:321047f0190a 9 *
andresag 6:321047f0190a 10 * Unless required by applicable law or agreed to in writing, software
andresag 6:321047f0190a 11 * distributed under the License is distributed on an "AS IS" BASIS,
andresag 6:321047f0190a 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
andresag 6:321047f0190a 13 * See the License for the specific language governing permissions and
andresag 6:321047f0190a 14 * limitations under the License.
andresag 6:321047f0190a 15 */
andresag 6:321047f0190a 16
andresag 6:321047f0190a 17 #include "EddystoneService.h"
andresag 6:321047f0190a 18
andresag 6:321047f0190a 19 /* Initialise the EddystoneService using parameters from persistent storage */
andresag 6:321047f0190a 20 EddystoneService::EddystoneService(BLE &bleIn,
andresag 6:321047f0190a 21 EddystoneParams_t &paramsIn,
andresag 6:321047f0190a 22 const PowerLevels_t &advPowerLevelsIn,
andresag 6:321047f0190a 23 const PowerLevels_t &radioPowerLevelsIn,
andresag 6:321047f0190a 24 uint32_t advConfigIntervalIn) :
andresag 6:321047f0190a 25 ble(bleIn),
andresag 6:321047f0190a 26 operationMode(EDDYSTONE_MODE_NONE),
andresag 6:321047f0190a 27 urlFrame(paramsIn.urlData, paramsIn.urlDataLength),
andresag 6:321047f0190a 28 uidFrame(paramsIn.uidNamespaceID, paramsIn.uidInstanceID),
andresag 6:321047f0190a 29 tlmFrame(paramsIn.tlmVersion),
andresag 6:321047f0190a 30 resetFlag(false),
andresag 6:321047f0190a 31 tlmBatteryVoltageCallback(NULL),
andresag 6:321047f0190a 32 tlmBeaconTemperatureCallback(NULL)
andresag 6:321047f0190a 33 {
andresag 6:321047f0190a 34 lockState = paramsIn.lockState;
andresag 6:321047f0190a 35 flags = paramsIn.flags;
andresag 6:321047f0190a 36 txPowerMode = paramsIn.txPowerMode;
andresag 6:321047f0190a 37 beaconPeriod = correctAdvertisementPeriod(paramsIn.beaconPeriod);
andresag 6:321047f0190a 38
andresag 6:321047f0190a 39 memcpy(lock, paramsIn.lock, sizeof(Lock_t));
andresag 6:321047f0190a 40 memcpy(unlock, paramsIn.unlock, sizeof(Lock_t));
andresag 6:321047f0190a 41
andresag 6:321047f0190a 42 eddystoneConstructorHelper(advPowerLevelsIn, radioPowerLevelsIn, advConfigIntervalIn);
andresag 6:321047f0190a 43 }
andresag 6:321047f0190a 44
andresag 6:321047f0190a 45 /* When using this constructor we need to call setURLData,
andresag 6:321047f0190a 46 * setTMLData and setUIDData to initialise values manually
andresag 6:321047f0190a 47 */
andresag 6:321047f0190a 48 EddystoneService::EddystoneService(BLE &bleIn,
andresag 6:321047f0190a 49 const PowerLevels_t &advPowerLevelsIn,
andresag 6:321047f0190a 50 const PowerLevels_t &radioPowerLevelsIn,
andresag 6:321047f0190a 51 uint32_t advConfigIntervalIn) :
andresag 6:321047f0190a 52 ble(bleIn),
andresag 6:321047f0190a 53 operationMode(EDDYSTONE_MODE_NONE),
andresag 6:321047f0190a 54 urlFrame(),
andresag 6:321047f0190a 55 uidFrame(),
andresag 6:321047f0190a 56 tlmFrame(),
andresag 6:321047f0190a 57 lockState(false),
andresag 6:321047f0190a 58 resetFlag(false),
andresag 6:321047f0190a 59 lock(),
andresag 6:321047f0190a 60 unlock(),
andresag 6:321047f0190a 61 flags(0),
andresag 6:321047f0190a 62 txPowerMode(0),
andresag 6:321047f0190a 63 beaconPeriod(DEFAULT_BEACON_PERIOD_MSEC),
andresag 6:321047f0190a 64 tlmBatteryVoltageCallback(NULL),
andresag 6:321047f0190a 65 tlmBeaconTemperatureCallback(NULL)
andresag 6:321047f0190a 66 {
andresag 6:321047f0190a 67 eddystoneConstructorHelper(advPowerLevelsIn, radioPowerLevelsIn, advConfigIntervalIn);
andresag 6:321047f0190a 68 }
andresag 6:321047f0190a 69
andresag 6:321047f0190a 70 /* Setup callback to update BatteryVoltage in TLM frame */
andresag 6:321047f0190a 71 void EddystoneService::onTLMBatteryVoltageUpdate(TlmUpdateCallback_t tlmBatteryVoltageCallbackIn)
andresag 6:321047f0190a 72 {
andresag 6:321047f0190a 73 tlmBatteryVoltageCallback = tlmBatteryVoltageCallbackIn;
andresag 6:321047f0190a 74 }
andresag 6:321047f0190a 75
andresag 6:321047f0190a 76 /* Setup callback to update BeaconTemperature in TLM frame */
andresag 6:321047f0190a 77 void EddystoneService::onTLMBeaconTemperatureUpdate(TlmUpdateCallback_t tlmBeaconTemperatureCallbackIn)
andresag 6:321047f0190a 78 {
andresag 6:321047f0190a 79 tlmBeaconTemperatureCallback = tlmBeaconTemperatureCallbackIn;
andresag 6:321047f0190a 80 }
andresag 6:321047f0190a 81
andresag 6:321047f0190a 82 void EddystoneService::setTLMData(uint8_t tlmVersionIn)
andresag 6:321047f0190a 83 {
andresag 6:321047f0190a 84 tlmFrame.setTLMData(tlmVersionIn);
andresag 6:321047f0190a 85 }
andresag 6:321047f0190a 86
andresag 6:321047f0190a 87 void EddystoneService::setURLData(const char *urlDataIn)
andresag 6:321047f0190a 88 {
andresag 6:321047f0190a 89 urlFrame.setURLData(urlDataIn);
andresag 6:321047f0190a 90 }
andresag 6:321047f0190a 91
andresag 6:321047f0190a 92 void EddystoneService::setUIDData(const UIDNamespaceID_t *uidNamespaceIDIn, const UIDInstanceID_t *uidInstanceIDIn)
andresag 6:321047f0190a 93 {
andresag 6:321047f0190a 94 uidFrame.setUIDData(uidNamespaceIDIn, uidInstanceIDIn);
andresag 6:321047f0190a 95 }
andresag 6:321047f0190a 96
andresag 6:321047f0190a 97 EddystoneService::EddystoneError_t EddystoneService::startConfigService(void)
andresag 6:321047f0190a 98 {
andresag 6:321047f0190a 99 if (operationMode == EDDYSTONE_MODE_CONFIG) {
andresag 6:321047f0190a 100 /* Nothing to do, we are already in config mode */
andresag 6:321047f0190a 101 return EDDYSTONE_ERROR_NONE;
andresag 6:321047f0190a 102 } else if (advConfigInterval == 0) {
andresag 6:321047f0190a 103 /* Nothing to do, the advertisement interval is 0 */
andresag 6:321047f0190a 104 return EDDYSTONE_ERROR_INVALID_ADVERTISING_INTERVAL;
andresag 6:321047f0190a 105 }
andresag 6:321047f0190a 106
andresag 6:321047f0190a 107 if (operationMode == EDDYSTONE_MODE_BEACON) {
andresag 6:321047f0190a 108 ble.shutdown();
andresag 6:321047f0190a 109 /* Free unused memory */
andresag 6:321047f0190a 110 freeBeaconFrames();
andresag 6:321047f0190a 111 operationMode = EDDYSTONE_MODE_CONFIG;
andresag 6:321047f0190a 112 ble.init(this, &EddystoneService::bleInitComplete);
andresag 6:321047f0190a 113 return EDDYSTONE_ERROR_NONE;
andresag 6:321047f0190a 114 }
andresag 6:321047f0190a 115
andresag 6:321047f0190a 116 operationMode = EDDYSTONE_MODE_CONFIG;
andresag 6:321047f0190a 117 setupConfigService();
andresag 6:321047f0190a 118 return EDDYSTONE_ERROR_NONE;
andresag 6:321047f0190a 119 }
andresag 6:321047f0190a 120
andresag 6:321047f0190a 121 EddystoneService::EddystoneError_t EddystoneService::startBeaconService(uint16_t consecUrlFramesIn, uint16_t consecUidFramesIn, uint16_t consecTlmFramesIn)
andresag 6:321047f0190a 122 {
andresag 6:321047f0190a 123 if (operationMode == EDDYSTONE_MODE_BEACON) {
andresag 6:321047f0190a 124 /* Nothing to do, we are already in beacon mode */
andresag 6:321047f0190a 125 return EDDYSTONE_ERROR_NONE;
andresag 6:321047f0190a 126 } else if (!consecUrlFramesIn && !consecUidFramesIn && !consecTlmFramesIn) {
andresag 6:321047f0190a 127 /* Nothing to do, the user wants 0 consecutive frames of everything */
andresag 6:321047f0190a 128 return EDDYSTONE_ERROR_INVALID_CONSEC_FRAMES;
andresag 6:321047f0190a 129 } else if (!beaconPeriod) {
andresag 6:321047f0190a 130 /* Nothing to do, the period is 0 for all frames */
andresag 6:321047f0190a 131 return EDDYSTONE_ERROR_INVALID_BEACON_PERIOD;
andresag 6:321047f0190a 132 }
andresag 6:321047f0190a 133
andresag 6:321047f0190a 134 /* Setup tracking of the current advertised frame. Note that this will
andresag 6:321047f0190a 135 * cause URL or UID frames to be advertised first!
andresag 6:321047f0190a 136 */
andresag 6:321047f0190a 137 currentAdvertisedFrame = EDDYSTONE_FRAME_TLM;
andresag 6:321047f0190a 138 consecFrames[EDDYSTONE_FRAME_URL] = consecUrlFramesIn;
andresag 6:321047f0190a 139 consecFrames[EDDYSTONE_FRAME_UID] = consecUidFramesIn;
andresag 6:321047f0190a 140 consecFrames[EDDYSTONE_FRAME_TLM] = consecTlmFramesIn;
andresag 6:321047f0190a 141
andresag 6:321047f0190a 142 memset(currentConsecFrames, 0, sizeof(uint16_t) * NUM_EDDYSTONE_FRAMES);
andresag 6:321047f0190a 143
andresag 6:321047f0190a 144 if (operationMode == EDDYSTONE_MODE_CONFIG) {
andresag 6:321047f0190a 145 ble.shutdown();
andresag 6:321047f0190a 146 /* Free unused memory */
andresag 6:321047f0190a 147 freeConfigCharacteristics();
andresag 6:321047f0190a 148 operationMode = EDDYSTONE_MODE_BEACON;
andresag 6:321047f0190a 149 ble.init(this, &EddystoneService::bleInitComplete);
andresag 6:321047f0190a 150 return EDDYSTONE_ERROR_NONE;
andresag 6:321047f0190a 151 }
andresag 6:321047f0190a 152
andresag 6:321047f0190a 153 operationMode = EDDYSTONE_MODE_BEACON;
andresag 6:321047f0190a 154 setupBeaconService();
andresag 6:321047f0190a 155 return EDDYSTONE_ERROR_NONE;
andresag 6:321047f0190a 156 }
andresag 6:321047f0190a 157
andresag 6:321047f0190a 158 /* It is not the responsibility of the Eddystone implementation to store
andresag 6:321047f0190a 159 * the configured parameters in persistent storage since this is
andresag 6:321047f0190a 160 * platform-specific. So we provide this function that returns the
andresag 6:321047f0190a 161 * configured values that need to be stored and the main application
andresag 6:321047f0190a 162 * takes care of storing them.
andresag 6:321047f0190a 163 */
andresag 6:321047f0190a 164 void EddystoneService::getEddystoneParams(EddystoneParams_t *params)
andresag 6:321047f0190a 165 {
andresag 6:321047f0190a 166 params->lockState = lockState;
andresag 6:321047f0190a 167 params->flags = flags;
andresag 6:321047f0190a 168 params->txPowerMode = txPowerMode;
andresag 6:321047f0190a 169 params->beaconPeriod = beaconPeriod;
andresag 6:321047f0190a 170 params->tlmVersion = tlmFrame.getTLMVersion();
andresag 6:321047f0190a 171 params->urlDataLength = urlFrame.getEncodedURLDataLength();
andresag 6:321047f0190a 172
andresag 6:321047f0190a 173 memcpy(params->advPowerLevels, advPowerLevels, sizeof(PowerLevels_t));
andresag 6:321047f0190a 174 memcpy(params->lock, lock, sizeof(Lock_t));
andresag 6:321047f0190a 175 memcpy(params->unlock, unlock, sizeof(Lock_t));
andresag 6:321047f0190a 176 memcpy(params->urlData, urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength());
andresag 6:321047f0190a 177 memcpy(params->uidNamespaceID, uidFrame.getUIDNamespaceID(), sizeof(UIDNamespaceID_t));
andresag 6:321047f0190a 178 memcpy(params->uidInstanceID, uidFrame.getUIDInstanceID(), sizeof(UIDInstanceID_t));
andresag 6:321047f0190a 179 }
andresag 6:321047f0190a 180
andresag 6:321047f0190a 181 /* Helper function used only once during constructing the object to avoid
andresag 6:321047f0190a 182 * duplicated code.
andresag 6:321047f0190a 183 */
andresag 6:321047f0190a 184 void EddystoneService::eddystoneConstructorHelper(const PowerLevels_t &advPowerLevelsIn,
andresag 6:321047f0190a 185 const PowerLevels_t &radioPowerLevelsIn,
andresag 6:321047f0190a 186 uint32_t advConfigIntervalIn)
andresag 6:321047f0190a 187 {
andresag 6:321047f0190a 188 advConfigInterval = (advConfigIntervalIn > 0) ? correctAdvertisementPeriod(advConfigIntervalIn) : 0;
andresag 6:321047f0190a 189
andresag 6:321047f0190a 190 memcpy(radioPowerLevels, radioPowerLevelsIn, sizeof(PowerLevels_t));
andresag 6:321047f0190a 191 memcpy(advPowerLevels, advPowerLevelsIn, sizeof(PowerLevels_t));
andresag 6:321047f0190a 192
andresag 6:321047f0190a 193 /* TODO: Note that this timer is started from the time EddystoneService
andresag 6:321047f0190a 194 * is initialised and NOT from when the device is booted. So app needs
andresag 6:321047f0190a 195 * to take care that EddystoneService is one of the first things to be
andresag 6:321047f0190a 196 * started!
andresag 6:321047f0190a 197 */
andresag 6:321047f0190a 198 timeSinceBootTimer.start();
andresag 6:321047f0190a 199 }
andresag 6:321047f0190a 200
andresag 6:321047f0190a 201 /* When changing modes, we shutdown and init the BLE instance, so
andresag 6:321047f0190a 202 * this is needed to complete the initialisation task.
andresag 6:321047f0190a 203 */
andresag 6:321047f0190a 204 void EddystoneService::bleInitComplete(BLE::InitializationCompleteCallbackContext* initContext)
andresag 6:321047f0190a 205 {
andresag 6:321047f0190a 206 if (initContext->error != BLE_ERROR_NONE) {
andresag 6:321047f0190a 207 /* Initialisation failed */
andresag 6:321047f0190a 208 return;
andresag 6:321047f0190a 209 }
andresag 6:321047f0190a 210
andresag 6:321047f0190a 211 switch (operationMode) {
andresag 6:321047f0190a 212 case EDDYSTONE_MODE_CONFIG:
andresag 6:321047f0190a 213 setupConfigService();
andresag 6:321047f0190a 214 break;
andresag 6:321047f0190a 215 case EDDYSTONE_MODE_BEACON:
andresag 6:321047f0190a 216 setupBeaconService();
andresag 6:321047f0190a 217 break;
andresag 6:321047f0190a 218 default:
andresag 6:321047f0190a 219 /* Some error occurred */
andresag 6:321047f0190a 220 break;
andresag 6:321047f0190a 221 }
andresag 6:321047f0190a 222 }
andresag 6:321047f0190a 223
andresag 6:321047f0190a 224 void EddystoneService::swapAdvertisedFrame(void)
andresag 6:321047f0190a 225 {
andresag 6:321047f0190a 226 /* This essentially works out which is the next frame to be swapped in
andresag 6:321047f0190a 227 * and updated the advertised packets. It will eventually terminate
andresag 6:321047f0190a 228 * and in the worst case the frame swapped in is the current advertised
andresag 6:321047f0190a 229 * frame.
andresag 6:321047f0190a 230 */
andresag 6:321047f0190a 231 while (true) {
andresag 6:321047f0190a 232 currentAdvertisedFrame = (currentAdvertisedFrame + 1) % NUM_EDDYSTONE_FRAMES;
andresag 6:321047f0190a 233
andresag 6:321047f0190a 234 if (currentAdvertisedFrame == EDDYSTONE_FRAME_URL && consecFrames[EDDYSTONE_FRAME_URL] > 0) {
andresag 6:321047f0190a 235 updateAdvertisementPacket(rawUrlFrame, urlFrame.getRawFrameSize());
andresag 6:321047f0190a 236 return;
andresag 6:321047f0190a 237 } else if (currentAdvertisedFrame == EDDYSTONE_FRAME_UID && consecFrames[EDDYSTONE_FRAME_UID] > 0) {
andresag 6:321047f0190a 238 updateAdvertisementPacket(rawUidFrame, uidFrame.getRawFrameSize());
andresag 6:321047f0190a 239 return;
andresag 6:321047f0190a 240 } else if (currentAdvertisedFrame == EDDYSTONE_FRAME_TLM && consecFrames[EDDYSTONE_FRAME_UID] > 0) {
andresag 6:321047f0190a 241 updateRawTLMFrame();
andresag 6:321047f0190a 242 updateAdvertisementPacket(rawTlmFrame, tlmFrame.getRawFrameSize());
andresag 6:321047f0190a 243 return;
andresag 6:321047f0190a 244 }
andresag 6:321047f0190a 245 }
andresag 6:321047f0190a 246 }
andresag 6:321047f0190a 247
andresag 6:321047f0190a 248 /* Helper function that calls user-defined functions to update Battery Voltage and Temperature (if available),
andresag 6:321047f0190a 249 * then updates the raw frame data and finally updates the actual advertised packet. This operation must be
andresag 6:321047f0190a 250 * done fairly often because the TLM frame TimeSinceBoot must have a 0.1 secs resolution according to the
andresag 6:321047f0190a 251 * Eddystone specification.
andresag 6:321047f0190a 252 */
andresag 6:321047f0190a 253 void EddystoneService::updateRawTLMFrame(void)
andresag 6:321047f0190a 254 {
andresag 6:321047f0190a 255 if (tlmBeaconTemperatureCallback != NULL) {
andresag 6:321047f0190a 256 tlmFrame.updateBeaconTemperature((*tlmBeaconTemperatureCallback)(tlmFrame.getBeaconTemperature()));
andresag 6:321047f0190a 257 }
andresag 6:321047f0190a 258 if (tlmBatteryVoltageCallback != NULL) {
andresag 6:321047f0190a 259 tlmFrame.updateBatteryVoltage((*tlmBatteryVoltageCallback)(tlmFrame.getBatteryVoltage()));
andresag 6:321047f0190a 260 }
andresag 6:321047f0190a 261 tlmFrame.updateTimeSinceBoot(timeSinceBootTimer.read_ms());
andresag 6:321047f0190a 262 tlmFrame.constructTLMFrame(rawTlmFrame);
andresag 6:321047f0190a 263 }
andresag 6:321047f0190a 264
andresag 6:321047f0190a 265 void EddystoneService::updateAdvertisementPacket(const uint8_t* rawFrame, size_t rawFrameLength)
andresag 6:321047f0190a 266 {
andresag 6:321047f0190a 267 ble.gap().clearAdvertisingPayload();
andresag 6:321047f0190a 268 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
andresag 6:321047f0190a 269 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, EDDYSTONE_UUID, sizeof(EDDYSTONE_UUID));
andresag 6:321047f0190a 270 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, rawFrame, rawFrameLength);
andresag 6:321047f0190a 271 }
andresag 6:321047f0190a 272
andresag 6:321047f0190a 273 void EddystoneService::setupBeaconService(void)
andresag 6:321047f0190a 274 {
andresag 6:321047f0190a 275 /* Initialise arrays to hold constructed raw frames */
andresag 6:321047f0190a 276 if (consecFrames[EDDYSTONE_FRAME_URL] > 0) {
andresag 6:321047f0190a 277 rawUrlFrame = new uint8_t[urlFrame.getRawFrameSize()];
andresag 6:321047f0190a 278 urlFrame.constructURLFrame(rawUrlFrame, advPowerLevels[txPowerMode]);
andresag 6:321047f0190a 279 }
andresag 6:321047f0190a 280
andresag 6:321047f0190a 281 if (consecFrames[EDDYSTONE_FRAME_UID] > 0) {
andresag 6:321047f0190a 282 rawUidFrame = new uint8_t[uidFrame.getRawFrameSize()];
andresag 6:321047f0190a 283 uidFrame.constructUIDFrame(rawUidFrame, advPowerLevels[txPowerMode]);
andresag 6:321047f0190a 284 }
andresag 6:321047f0190a 285
andresag 6:321047f0190a 286 if (consecFrames[EDDYSTONE_FRAME_TLM] > 0) {
andresag 6:321047f0190a 287 rawTlmFrame = new uint8_t[tlmFrame.getRawFrameSize()];
andresag 6:321047f0190a 288 /* Do not initialise because we have to reconstruct every 0.1 secs */
andresag 6:321047f0190a 289 }
andresag 6:321047f0190a 290
andresag 6:321047f0190a 291 /* Configure advertisements */
andresag 6:321047f0190a 292 ble.gap().setTxPower(radioPowerLevels[txPowerMode]);
andresag 6:321047f0190a 293 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
andresag 6:321047f0190a 294 ble.gap().setAdvertisingInterval(beaconPeriod);
andresag 6:321047f0190a 295 ble.gap().onRadioNotification(this, &EddystoneService::radioNotificationCallback);
andresag 6:321047f0190a 296 ble.gap().initRadioNotification();
andresag 6:321047f0190a 297
andresag 6:321047f0190a 298 /* Set advertisement packet payload */
andresag 6:321047f0190a 299 swapAdvertisedFrame();
andresag 6:321047f0190a 300
andresag 6:321047f0190a 301 /* Start advertising */
andresag 6:321047f0190a 302 ble.gap().startAdvertising();
andresag 6:321047f0190a 303 }
andresag 6:321047f0190a 304
andresag 6:321047f0190a 305 void EddystoneService::setupConfigService(void)
andresag 6:321047f0190a 306 {
andresag 6:321047f0190a 307 lockStateChar = new ReadOnlyGattCharacteristic<bool>(UUID_LOCK_STATE_CHAR, &lockState);
andresag 6:321047f0190a 308 lockChar = new WriteOnlyArrayGattCharacteristic<uint8_t, sizeof(Lock_t)>(UUID_LOCK_CHAR, lock);
andresag 6:321047f0190a 309 unlockChar = new WriteOnlyArrayGattCharacteristic<uint8_t, sizeof(Lock_t)>(UUID_UNLOCK_CHAR, unlock);
andresag 6:321047f0190a 310 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);
andresag 6:321047f0190a 311 flagsChar = new ReadWriteGattCharacteristic<uint8_t>(UUID_FLAGS_CHAR, &flags);
andresag 6:321047f0190a 312 advPowerLevelsChar = new ReadWriteArrayGattCharacteristic<int8_t, sizeof(PowerLevels_t)>(UUID_ADV_POWER_LEVELS_CHAR, advPowerLevels);
andresag 6:321047f0190a 313 txPowerModeChar = new ReadWriteGattCharacteristic<uint8_t>(UUID_TX_POWER_MODE_CHAR, &txPowerMode);
andresag 6:321047f0190a 314 beaconPeriodChar = new ReadWriteGattCharacteristic<uint16_t>(UUID_BEACON_PERIOD_CHAR, &beaconPeriod);
andresag 6:321047f0190a 315 resetChar = new WriteOnlyGattCharacteristic<bool>(UUID_RESET_CHAR, &resetFlag);
andresag 6:321047f0190a 316
andresag 6:321047f0190a 317 lockChar->setWriteAuthorizationCallback(this, &EddystoneService::lockAuthorizationCallback);
andresag 6:321047f0190a 318 unlockChar->setWriteAuthorizationCallback(this, &EddystoneService::unlockAuthorizationCallback);
andresag 6:321047f0190a 319 urlDataChar->setWriteAuthorizationCallback(this, &EddystoneService::urlDataWriteAuthorizationCallback);
andresag 6:321047f0190a 320 flagsChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<uint8_t>);
andresag 6:321047f0190a 321 advPowerLevelsChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<PowerLevels_t>);
andresag 6:321047f0190a 322 txPowerModeChar->setWriteAuthorizationCallback(this, &EddystoneService::powerModeAuthorizationCallback);
andresag 6:321047f0190a 323 beaconPeriodChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<uint16_t>);
andresag 6:321047f0190a 324 resetChar->setWriteAuthorizationCallback(this, &EddystoneService::basicAuthorizationCallback<bool>);
andresag 6:321047f0190a 325
andresag 6:321047f0190a 326 charTable[0] = lockStateChar;
andresag 6:321047f0190a 327 charTable[1] = lockChar;
andresag 6:321047f0190a 328 charTable[2] = unlockChar;
andresag 6:321047f0190a 329 charTable[3] = urlDataChar;
andresag 6:321047f0190a 330 charTable[4] = flagsChar;
andresag 6:321047f0190a 331 charTable[5] = advPowerLevelsChar;
andresag 6:321047f0190a 332 charTable[6] = txPowerModeChar;
andresag 6:321047f0190a 333 charTable[7] = beaconPeriodChar;
andresag 6:321047f0190a 334 charTable[8] = resetChar;
andresag 6:321047f0190a 335
andresag 6:321047f0190a 336 GattService configService(UUID_URL_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
andresag 6:321047f0190a 337
andresag 6:321047f0190a 338 ble.gattServer().addService(configService);
andresag 6:321047f0190a 339 ble.gattServer().onDataWritten(this, &EddystoneService::onDataWrittenCallback);
andresag 6:321047f0190a 340 updateCharacteristicValues();
andresag 6:321047f0190a 341 setupEddystoneConfigAdvertisements();
andresag 6:321047f0190a 342 }
andresag 6:321047f0190a 343
andresag 6:321047f0190a 344 void EddystoneService::freeConfigCharacteristics(void)
andresag 6:321047f0190a 345 {
andresag 6:321047f0190a 346 delete lockStateChar;
andresag 6:321047f0190a 347 delete lockChar;
andresag 6:321047f0190a 348 delete unlockChar;
andresag 6:321047f0190a 349 delete urlDataChar;
andresag 6:321047f0190a 350 delete flagsChar;
andresag 6:321047f0190a 351 delete advPowerLevelsChar;
andresag 6:321047f0190a 352 delete txPowerModeChar;
andresag 6:321047f0190a 353 delete beaconPeriodChar;
andresag 6:321047f0190a 354 delete resetChar;
andresag 6:321047f0190a 355 }
andresag 6:321047f0190a 356
andresag 6:321047f0190a 357 void EddystoneService::freeBeaconFrames(void)
andresag 6:321047f0190a 358 {
andresag 6:321047f0190a 359 delete[] rawUrlFrame;
andresag 6:321047f0190a 360 delete[] rawUidFrame;
andresag 6:321047f0190a 361 delete[] rawTlmFrame;
andresag 6:321047f0190a 362 }
andresag 6:321047f0190a 363
andresag 6:321047f0190a 364 void EddystoneService::radioNotificationCallback(bool radioActive)
andresag 6:321047f0190a 365 {
andresag 6:321047f0190a 366 if (radioActive) {
andresag 6:321047f0190a 367 /* Do nothing */
andresag 6:321047f0190a 368 return;
andresag 6:321047f0190a 369 }
andresag 6:321047f0190a 370
andresag 6:321047f0190a 371 tlmFrame.updatePduCount();
andresag 6:321047f0190a 372 currentConsecFrames[currentAdvertisedFrame]++;
andresag 6:321047f0190a 373
andresag 6:321047f0190a 374 if (consecFrames[currentAdvertisedFrame] > currentConsecFrames[currentAdvertisedFrame]) {
andresag 6:321047f0190a 375 if (currentAdvertisedFrame == EDDYSTONE_FRAME_TLM) {
andresag 6:321047f0190a 376 /* Update the TLM frame otherwise we will not meet the 0.1 secs resolution of
andresag 6:321047f0190a 377 * the Eddystone specification.
andresag 6:321047f0190a 378 */
andresag 6:321047f0190a 379 updateRawTLMFrame();
andresag 6:321047f0190a 380 updateAdvertisementPacket(rawTlmFrame, tlmFrame.getRawFrameSize());
andresag 6:321047f0190a 381 }
andresag 6:321047f0190a 382 /* Keep advertising the same frame */
andresag 6:321047f0190a 383 return;
andresag 6:321047f0190a 384 }
andresag 6:321047f0190a 385
andresag 6:321047f0190a 386 currentConsecFrames[currentAdvertisedFrame] = 0;
andresag 6:321047f0190a 387
andresag 6:321047f0190a 388 #ifdef YOTTA_CFG_MBED_OS
andresag 6:321047f0190a 389 minar::Scheduler::postCallback(this, &EddystoneService::swapAdvertisedFrame);
andresag 6:321047f0190a 390 #else
andresag 6:321047f0190a 391 swapAdvertisedFrameTimeout.attach_us(this, &EddystoneService::swapAdvertisedFrame, 1);
andresag 6:321047f0190a 392 #endif
andresag 6:321047f0190a 393 }
andresag 6:321047f0190a 394
andresag 6:321047f0190a 395 /*
andresag 6:321047f0190a 396 * Internal helper function used to update the GATT database following any
andresag 6:321047f0190a 397 * change to the internal state of the service object.
andresag 6:321047f0190a 398 */
andresag 6:321047f0190a 399 void EddystoneService::updateCharacteristicValues(void)
andresag 6:321047f0190a 400 {
andresag 6:321047f0190a 401 ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(bool));
andresag 6:321047f0190a 402 ble.gattServer().write(urlDataChar->getValueHandle(), urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength());
andresag 6:321047f0190a 403 ble.gattServer().write(flagsChar->getValueHandle(), &flags, sizeof(uint8_t));
andresag 6:321047f0190a 404 ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beaconPeriod), sizeof(uint16_t));
andresag 6:321047f0190a 405 ble.gattServer().write(txPowerModeChar->getValueHandle(), &txPowerMode, sizeof(uint8_t));
andresag 6:321047f0190a 406 ble.gattServer().write(advPowerLevelsChar->getValueHandle(), reinterpret_cast<uint8_t *>(advPowerLevels), sizeof(PowerLevels_t));
andresag 6:321047f0190a 407 ble.gattServer().write(lockChar->getValueHandle(), lock, sizeof(PowerLevels_t));
andresag 6:321047f0190a 408 ble.gattServer().write(unlockChar->getValueHandle(), unlock, sizeof(PowerLevels_t));
andresag 6:321047f0190a 409 }
andresag 6:321047f0190a 410
andresag 6:321047f0190a 411 void EddystoneService::setupEddystoneConfigAdvertisements(void)
andresag 6:321047f0190a 412 {
andresag 6:321047f0190a 413 ble.gap().clearAdvertisingPayload();
andresag 6:321047f0190a 414 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
andresag 6:321047f0190a 415
andresag 6:321047f0190a 416 /* UUID is in different order in the ADV frame (!) */
andresag 6:321047f0190a 417 uint8_t reversedServiceUUID[sizeof(UUID_URL_BEACON_SERVICE)];
andresag 6:321047f0190a 418 for (size_t i = 0; i < sizeof(UUID_URL_BEACON_SERVICE); i++) {
andresag 6:321047f0190a 419 reversedServiceUUID[i] = UUID_URL_BEACON_SERVICE[sizeof(UUID_URL_BEACON_SERVICE) - i - 1];
andresag 6:321047f0190a 420 }
andresag 6:321047f0190a 421 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, reversedServiceUUID, sizeof(reversedServiceUUID));
andresag 6:321047f0190a 422 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG);
andresag 6:321047f0190a 423 ble.gap().accumulateScanResponse(GapAdvertisingData::COMPLETE_LOCAL_NAME, reinterpret_cast<const uint8_t *>(&DEVICE_NAME), sizeof(DEVICE_NAME));
andresag 6:321047f0190a 424 ble.gap().accumulateScanResponse(
andresag 6:321047f0190a 425 GapAdvertisingData::TX_POWER_LEVEL,
andresag 6:321047f0190a 426 reinterpret_cast<uint8_t *>(&advPowerLevels[TX_POWER_MODE_LOW]),
andresag 6:321047f0190a 427 sizeof(uint8_t));
andresag 6:321047f0190a 428
andresag 6:321047f0190a 429 ble.gap().setTxPower(radioPowerLevels[txPowerMode]);
andresag 6:321047f0190a 430 ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(&DEVICE_NAME));
andresag 6:321047f0190a 431 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
andresag 6:321047f0190a 432 ble.gap().setAdvertisingInterval(advConfigInterval);
andresag 6:321047f0190a 433 ble.gap().startAdvertising();
andresag 6:321047f0190a 434 }
andresag 6:321047f0190a 435
andresag 6:321047f0190a 436 void EddystoneService::lockAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
andresag 6:321047f0190a 437 {
andresag 6:321047f0190a 438 if (lockState) {
andresag 6:321047f0190a 439 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
andresag 6:321047f0190a 440 } else if (authParams->len != sizeof(Lock_t)) {
andresag 6:321047f0190a 441 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
andresag 6:321047f0190a 442 } else if (authParams->offset != 0) {
andresag 6:321047f0190a 443 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
andresag 6:321047f0190a 444 } else {
andresag 6:321047f0190a 445 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
andresag 6:321047f0190a 446 }
andresag 6:321047f0190a 447 }
andresag 6:321047f0190a 448
andresag 6:321047f0190a 449 void EddystoneService::unlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
andresag 6:321047f0190a 450 {
andresag 6:321047f0190a 451 if (!lockState && (authParams->len == sizeof(Lock_t))) {
andresag 6:321047f0190a 452 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
andresag 6:321047f0190a 453 } else if (authParams->len != sizeof(Lock_t)) {
andresag 6:321047f0190a 454 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
andresag 6:321047f0190a 455 } else if (authParams->offset != 0) {
andresag 6:321047f0190a 456 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
andresag 6:321047f0190a 457 } else if (memcmp(authParams->data, lock, sizeof(Lock_t)) != 0) {
andresag 6:321047f0190a 458 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
andresag 6:321047f0190a 459 } else {
andresag 6:321047f0190a 460 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
andresag 6:321047f0190a 461 }
andresag 6:321047f0190a 462 }
andresag 6:321047f0190a 463
andresag 6:321047f0190a 464 void EddystoneService::urlDataWriteAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
andresag 6:321047f0190a 465 {
andresag 6:321047f0190a 466 if (lockState) {
andresag 6:321047f0190a 467 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
andresag 6:321047f0190a 468 } else if (authParams->offset != 0) {
andresag 6:321047f0190a 469 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
andresag 6:321047f0190a 470 } else {
andresag 6:321047f0190a 471 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
andresag 6:321047f0190a 472 }
andresag 6:321047f0190a 473 }
andresag 6:321047f0190a 474
andresag 6:321047f0190a 475 void EddystoneService::powerModeAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
andresag 6:321047f0190a 476 {
andresag 6:321047f0190a 477 if (lockState) {
andresag 6:321047f0190a 478 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
andresag 6:321047f0190a 479 } else if (authParams->len != sizeof(uint8_t)) {
andresag 6:321047f0190a 480 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
andresag 6:321047f0190a 481 } else if (authParams->offset != 0) {
andresag 6:321047f0190a 482 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
andresag 6:321047f0190a 483 } else if (*((uint8_t *)authParams->data) >= NUM_POWER_MODES) {
andresag 6:321047f0190a 484 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
andresag 6:321047f0190a 485 } else {
andresag 6:321047f0190a 486 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
andresag 6:321047f0190a 487 }
andresag 6:321047f0190a 488 }
andresag 6:321047f0190a 489
andresag 6:321047f0190a 490 template <typename T>
andresag 6:321047f0190a 491 void EddystoneService::basicAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
andresag 6:321047f0190a 492 {
andresag 6:321047f0190a 493 if (lockState) {
andresag 6:321047f0190a 494 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
andresag 6:321047f0190a 495 } else if (authParams->len != sizeof(T)) {
andresag 6:321047f0190a 496 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
andresag 6:321047f0190a 497 } else if (authParams->offset != 0) {
andresag 6:321047f0190a 498 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
andresag 6:321047f0190a 499 } else {
andresag 6:321047f0190a 500 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
andresag 6:321047f0190a 501 }
andresag 6:321047f0190a 502 }
andresag 6:321047f0190a 503
andresag 6:321047f0190a 504 /*
andresag 6:321047f0190a 505 * This callback is invoked when a GATT client attempts to modify any of the
andresag 6:321047f0190a 506 * characteristics of this service. Attempts to do so are also applied to
andresag 6:321047f0190a 507 * the internal state of this service object.
andresag 6:321047f0190a 508 */
andresag 6:321047f0190a 509 void EddystoneService::onDataWrittenCallback(const GattWriteCallbackParams *writeParams)
andresag 6:321047f0190a 510 {
andresag 6:321047f0190a 511 uint16_t handle = writeParams->handle;
andresag 6:321047f0190a 512
andresag 6:321047f0190a 513 if (handle == lockChar->getValueHandle()) {
andresag 6:321047f0190a 514 memcpy(lock, writeParams->data, sizeof(Lock_t));
andresag 6:321047f0190a 515 /* Set the state to be locked by the lock code (note: zeros are a valid lock) */
andresag 6:321047f0190a 516 lockState = true;
andresag 6:321047f0190a 517 ble.gattServer().write(lockChar->getValueHandle(), lock, sizeof(PowerLevels_t));
andresag 6:321047f0190a 518 ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(bool));
andresag 6:321047f0190a 519 } else if (handle == unlockChar->getValueHandle()) {
andresag 6:321047f0190a 520 /* Validated earlier */
andresag 6:321047f0190a 521 lockState = false;
andresag 6:321047f0190a 522 ble.gattServer().write(unlockChar->getValueHandle(), unlock, sizeof(PowerLevels_t));
andresag 6:321047f0190a 523 ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(bool));
andresag 6:321047f0190a 524 } else if (handle == urlDataChar->getValueHandle()) {
andresag 6:321047f0190a 525 urlFrame.setEncodedURLData(writeParams->data, writeParams->len);
andresag 6:321047f0190a 526 ble.gattServer().write(urlDataChar->getValueHandle(), urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength());
andresag 6:321047f0190a 527 } else if (handle == flagsChar->getValueHandle()) {
andresag 6:321047f0190a 528 flags = *(writeParams->data);
andresag 6:321047f0190a 529 ble.gattServer().write(flagsChar->getValueHandle(), &flags, sizeof(uint8_t));
andresag 6:321047f0190a 530 } else if (handle == advPowerLevelsChar->getValueHandle()) {
andresag 6:321047f0190a 531 memcpy(advPowerLevels, writeParams->data, sizeof(PowerLevels_t));
andresag 6:321047f0190a 532 ble.gattServer().write(advPowerLevelsChar->getValueHandle(), reinterpret_cast<uint8_t *>(advPowerLevels), sizeof(PowerLevels_t));
andresag 6:321047f0190a 533 } else if (handle == txPowerModeChar->getValueHandle()) {
andresag 6:321047f0190a 534 txPowerMode = *(writeParams->data);
andresag 6:321047f0190a 535 ble.gattServer().write(txPowerModeChar->getValueHandle(), &txPowerMode, sizeof(uint8_t));
andresag 6:321047f0190a 536 } else if (handle == beaconPeriodChar->getValueHandle()) {
andresag 6:321047f0190a 537 uint16_t tmpBeaconPeriod = correctAdvertisementPeriod(*((uint16_t *)(writeParams->data)));
andresag 6:321047f0190a 538 if (tmpBeaconPeriod != beaconPeriod) {
andresag 6:321047f0190a 539 beaconPeriod = tmpBeaconPeriod;
andresag 6:321047f0190a 540 ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beaconPeriod), sizeof(uint16_t));
andresag 6:321047f0190a 541 }
andresag 6:321047f0190a 542 } else if (handle == resetChar->getValueHandle() && (*((uint8_t *)writeParams->data) != 0)) {
andresag 6:321047f0190a 543 /* Reset characteristics to default values */
andresag 6:321047f0190a 544 flags = 0;
andresag 6:321047f0190a 545 txPowerMode = TX_POWER_MODE_LOW;
andresag 6:321047f0190a 546 beaconPeriod = DEFAULT_BEACON_PERIOD_MSEC;
andresag 6:321047f0190a 547
andresag 6:321047f0190a 548 urlFrame.setURLData(DEFAULT_URL);
andresag 6:321047f0190a 549 memset(lock, 0, sizeof(Lock_t));
andresag 6:321047f0190a 550
andresag 6:321047f0190a 551 ble.gattServer().write(urlDataChar->getValueHandle(), urlFrame.getEncodedURLData(), urlFrame.getEncodedURLDataLength());
andresag 6:321047f0190a 552 ble.gattServer().write(flagsChar->getValueHandle(), &flags, sizeof(uint8_t));
andresag 6:321047f0190a 553 ble.gattServer().write(txPowerModeChar->getValueHandle(), &txPowerMode, sizeof(uint8_t));
andresag 6:321047f0190a 554 ble.gattServer().write(beaconPeriodChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beaconPeriod), sizeof(uint16_t));
andresag 6:321047f0190a 555 ble.gattServer().write(lockChar->getValueHandle(), lock, sizeof(PowerLevels_t));
andresag 6:321047f0190a 556 }
andresag 6:321047f0190a 557 }
andresag 6:321047f0190a 558
andresag 6:321047f0190a 559 uint16_t EddystoneService::correctAdvertisementPeriod(uint16_t beaconPeriodIn) const
andresag 6:321047f0190a 560 {
andresag 6:321047f0190a 561 /* Re-map beaconPeriod to within permissible bounds if necessary. */
andresag 6:321047f0190a 562 if (beaconPeriodIn != 0) {
andresag 6:321047f0190a 563 if (beaconPeriodIn < ble.gap().getMinAdvertisingInterval()) {
andresag 6:321047f0190a 564 return ble.gap().getMinAdvertisingInterval();
andresag 6:321047f0190a 565 } else if (beaconPeriodIn > ble.gap().getMaxAdvertisingInterval()) {
andresag 6:321047f0190a 566 return ble.gap().getMaxAdvertisingInterval();
andresag 6:321047f0190a 567 }
andresag 6:321047f0190a 568 }
andresag 6:321047f0190a 569 return beaconPeriodIn;
andresag 6:321047f0190a 570 }