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:
andresag
Date:
Tue Nov 24 10:04:38 2015 +0000
Revision:
3:d0f3e00cbfdf
Update example to use new Eddystone implementation NOT included in ble/services.

Who changed what in which revision?

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