Eddystone Beacon only for TYBLE16

Dependencies:   TYBLE16_BASE

Please refer flowing link.
/users/kenjiArai/notebook/tyble16-module-will-become-a-mbed-family--mbedliza/

Committer:
kenjiArai
Date:
Sun Feb 25 02:26:02 2018 +0000
Revision:
0:5087fec653b2
Eddystone Beacon only for TYBLE16

Who changed what in which revision?

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