Example program for the Eddystone Beacon service.

Dependencies:   BLE_API mbed nRF51822 X_NUCLEO_IDB0XA1

Fork of BLE_EddystoneBeacon by URIBeacon

This example demonstrates how to set up and initialize a Eddystone Beacon. For more details on the Eddystone specification please see the Eddystone Github Page.

The Basics

An Eddystone Beacon is a Bluetooth Low Energy beacon, that means it does all of its data transfer in GAP advertising packets. Eddystone beacons, unlike other beacons, broadcast multiple types of data. Currently Eddystone beacons have 3 frame types (UID, URL, TLM) that will get swapped out periodically. This swapping of frame data allows Eddystone beacons to send many different types of data, thus increasing their usefulness over traditional beacons. Note that the UID frame type provides the same 16Bytes of UUID namespace that iBeacons do and the URL frame type provides the same functionality as a URIBeacon.

For more details see the Eddystone Specification.

Smartphone Apps

nRF Master Control Panel - this program recognizes Eddystone beacons and will display the data for all frame types.

iPhone Physical Web app

Android App

Walkthrough of Physical Web application

Technical Details

The Eddystone Specification looks like the following image. Please note that this may change over time and for up to date information the official spec should be referenced. /media/uploads/mbedAustin/scratch-1-.png

The Eddystone Frames get swapped in and out depending on what frames you have enabled. The only required frame type is the TLM frame, all others are optional and you can have any number enabled. To disable the UID or URL frames give their values a 'NULL' in the Eddystone constructor. The Eddystone spec recommends broadcasting 10 frames a second.

Note

  • The current Eddystone mbed example does not allow for combining the eddystone service with other services, this will be changes in a future update.
  • There is an Eddystone Config service that allows for updating beacons in the field. We are working on an example for this, so keep your eyes pealed for a future update.
Committer:
Vincent Coubard
Date:
Tue Sep 20 14:13:43 2016 +0100
Revision:
36:17b4a0ff9abb
Parent:
34:f6d4a699a1ea
Update libraries and add support of the ST shield.

Who changed what in which revision?

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