Kenji Arai / TYBLE16_mbedlized_os5_EddystoneBeacon

Dependencies:  

Committer:
kenjiArai
Date:
Tue Apr 10 12:50:20 2018 +0000
Revision:
0:47dfd44d8b78
Eddystone Beacon only for TYBLE16 Module

Who changed what in which revision?

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