Roy Want / Mbed OS beaconCompileReadyFork
Committer:
roywant
Date:
Mon Sep 19 00:59:11 2016 +0000
Revision:
0:ed0152b5c495
Initial commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
roywant 0:ed0152b5c495 1 /* mbed Microcontroller Library
roywant 0:ed0152b5c495 2 * Copyright (c) 2006-2015 ARM Limited
roywant 0:ed0152b5c495 3 *
roywant 0:ed0152b5c495 4 * Licensed under the Apache License, Version 2.0 (the "License");
roywant 0:ed0152b5c495 5 * you may not use this file except in compliance with the License.
roywant 0:ed0152b5c495 6 * You may obtain a copy of the License at
roywant 0:ed0152b5c495 7 *
roywant 0:ed0152b5c495 8 * http://www.apache.org/licenses/LICENSE-2.0
roywant 0:ed0152b5c495 9 *
roywant 0:ed0152b5c495 10 * Unless required by applicable law or agreed to in writing, software
roywant 0:ed0152b5c495 11 * distributed under the License is distributed on an "AS IS" BASIS,
roywant 0:ed0152b5c495 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
roywant 0:ed0152b5c495 13 * See the License for the specific language governing permissions and
roywant 0:ed0152b5c495 14 * limitations under the License.
roywant 0:ed0152b5c495 15 */
roywant 0:ed0152b5c495 16
roywant 0:ed0152b5c495 17 #include "EddystoneService.h"
roywant 0:ed0152b5c495 18 #include "EntropySource/EntropySource.h"
roywant 0:ed0152b5c495 19
roywant 0:ed0152b5c495 20 /* Use define zero for production, 1 for testing to allow connection at any time */
roywant 0:ed0152b5c495 21 #define DEFAULT_REMAIN_CONNECTABLE 0x01
roywant 0:ed0152b5c495 22
roywant 0:ed0152b5c495 23 const char * const EddystoneService::slotDefaultUrls[] = EDDYSTONE_DEFAULT_SLOT_URLS;
roywant 0:ed0152b5c495 24
roywant 0:ed0152b5c495 25 // Static timer used as time since boot
roywant 0:ed0152b5c495 26 Timer EddystoneService::timeSinceBootTimer;
roywant 0:ed0152b5c495 27
roywant 0:ed0152b5c495 28 /*
roywant 0:ed0152b5c495 29 * CONSTRUCTOR #1 Used on 1st boot (after reflash)
roywant 0:ed0152b5c495 30 */
roywant 0:ed0152b5c495 31 EddystoneService::EddystoneService(BLE &bleIn,
roywant 0:ed0152b5c495 32 const PowerLevels_t &advTxPowerLevelsIn,
roywant 0:ed0152b5c495 33 const PowerLevels_t &radioTxPowerLevelsIn,
roywant 0:ed0152b5c495 34 event_queue_t &evQ,
roywant 0:ed0152b5c495 35 uint32_t advConfigIntervalIn) :
roywant 0:ed0152b5c495 36 ble(bleIn),
roywant 0:ed0152b5c495 37 operationMode(EDDYSTONE_MODE_NONE),
roywant 0:ed0152b5c495 38 uidFrame(),
roywant 0:ed0152b5c495 39 urlFrame(),
roywant 0:ed0152b5c495 40 tlmFrame(),
roywant 0:ed0152b5c495 41 eidFrame(),
roywant 0:ed0152b5c495 42 tlmBatteryVoltageCallback(NULL),
roywant 0:ed0152b5c495 43 tlmBeaconTemperatureCallback(NULL),
roywant 0:ed0152b5c495 44 radioManagerCallbackHandle(NULL),
roywant 0:ed0152b5c495 45 deviceName(DEFAULT_DEVICE_NAME),
roywant 0:ed0152b5c495 46 eventQueue(evQ),
roywant 0:ed0152b5c495 47 nextEidSlot(0)
roywant 0:ed0152b5c495 48 {
roywant 0:ed0152b5c495 49 LOG(("1st Boot: "));
roywant 0:ed0152b5c495 50 LOG((BUILD_VERSION_STR));
roywant 0:ed0152b5c495 51 if (advConfigIntervalIn != 0) {
roywant 0:ed0152b5c495 52 if (advConfigIntervalIn < ble.gap().getMinAdvertisingInterval()) {
roywant 0:ed0152b5c495 53 advConfigInterval = ble.gap().getMinAdvertisingInterval();
roywant 0:ed0152b5c495 54 } else if (advConfigIntervalIn > ble.gap().getMaxAdvertisingInterval()) {
roywant 0:ed0152b5c495 55 advConfigInterval = ble.gap().getMaxAdvertisingInterval();
roywant 0:ed0152b5c495 56 } else {
roywant 0:ed0152b5c495 57 advConfigInterval = advConfigIntervalIn;
roywant 0:ed0152b5c495 58 }
roywant 0:ed0152b5c495 59 }
roywant 0:ed0152b5c495 60 memcpy(radioTxPowerLevels, radioTxPowerLevelsIn, sizeof(PowerLevels_t));
roywant 0:ed0152b5c495 61 memcpy(advTxPowerLevels, advTxPowerLevelsIn, sizeof(PowerLevels_t));
roywant 0:ed0152b5c495 62
roywant 0:ed0152b5c495 63 // 1st Boot so reset everything to factory values
roywant 0:ed0152b5c495 64 doFactoryReset(); // includes genBeaconKeys
roywant 0:ed0152b5c495 65
roywant 0:ed0152b5c495 66 LOG(("After FactoryReset in 1st Boot Init: genBeaconKeyRC=%d\r\n", genBeaconKeyRC));
roywant 0:ed0152b5c495 67 /* TODO: Note that this timer is started from the time EddystoneService
roywant 0:ed0152b5c495 68 * is initialised and NOT from when the device is booted.
roywant 0:ed0152b5c495 69 */
roywant 0:ed0152b5c495 70 timeSinceBootTimer.start();
roywant 0:ed0152b5c495 71
roywant 0:ed0152b5c495 72 /* Set the device name at startup */
roywant 0:ed0152b5c495 73 ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(deviceName));
roywant 0:ed0152b5c495 74
roywant 0:ed0152b5c495 75 }
roywant 0:ed0152b5c495 76
roywant 0:ed0152b5c495 77 /*
roywant 0:ed0152b5c495 78 * Constuctor #2: Used on 2nd+ boot: EddystoneService parameters derived from persistent storage
roywant 0:ed0152b5c495 79 */
roywant 0:ed0152b5c495 80 EddystoneService::EddystoneService(BLE &bleIn,
roywant 0:ed0152b5c495 81 EddystoneParams_t &paramsIn,
roywant 0:ed0152b5c495 82 const PowerLevels_t &radioTxPowerLevelsIn,
roywant 0:ed0152b5c495 83 event_queue_t &evQ,
roywant 0:ed0152b5c495 84 uint32_t advConfigIntervalIn) :
roywant 0:ed0152b5c495 85 ble(bleIn),
roywant 0:ed0152b5c495 86 operationMode(EDDYSTONE_MODE_NONE),
roywant 0:ed0152b5c495 87 uidFrame(),
roywant 0:ed0152b5c495 88 urlFrame(),
roywant 0:ed0152b5c495 89 tlmFrame(),
roywant 0:ed0152b5c495 90 eidFrame(),
roywant 0:ed0152b5c495 91 tlmBatteryVoltageCallback(NULL),
roywant 0:ed0152b5c495 92 tlmBeaconTemperatureCallback(NULL),
roywant 0:ed0152b5c495 93 radioManagerCallbackHandle(NULL),
roywant 0:ed0152b5c495 94 deviceName(DEFAULT_DEVICE_NAME),
roywant 0:ed0152b5c495 95 eventQueue(evQ),
roywant 0:ed0152b5c495 96 nextEidSlot(0)
roywant 0:ed0152b5c495 97 {
roywant 0:ed0152b5c495 98 LOG(("2nd (>=) Boot: "));
roywant 0:ed0152b5c495 99 LOG((BUILD_VERSION_STR));
roywant 0:ed0152b5c495 100 memcpy(capabilities, paramsIn.capabilities, sizeof(Capability_t));
roywant 0:ed0152b5c495 101 activeSlot = paramsIn.activeSlot;
roywant 0:ed0152b5c495 102 memcpy(radioTxPowerLevels, radioTxPowerLevelsIn, sizeof(PowerLevels_t));
roywant 0:ed0152b5c495 103 memcpy(slotRadioTxPowerLevels, paramsIn.slotRadioTxPowerLevels, sizeof(SlotTxPowerLevels_t));
roywant 0:ed0152b5c495 104 memcpy(advTxPowerLevels, paramsIn.advTxPowerLevels, sizeof(PowerLevels_t));
roywant 0:ed0152b5c495 105 memcpy(slotAdvTxPowerLevels, paramsIn.slotAdvTxPowerLevels, sizeof(SlotTxPowerLevels_t));
roywant 0:ed0152b5c495 106 memcpy(slotAdvIntervals, paramsIn.slotAdvIntervals, sizeof(SlotAdvIntervals_t));
roywant 0:ed0152b5c495 107 lockState = paramsIn.lockState;
roywant 0:ed0152b5c495 108 memcpy(unlockKey, paramsIn.unlockKey, sizeof(Lock_t));
roywant 0:ed0152b5c495 109 memcpy(unlockToken, paramsIn.unlockToken, sizeof(Lock_t));
roywant 0:ed0152b5c495 110 memcpy(challenge, paramsIn.challenge, sizeof(Lock_t));
roywant 0:ed0152b5c495 111 memset(slotCallbackHandles, 0, sizeof(SlotCallbackHandles_t));
roywant 0:ed0152b5c495 112 memcpy(slotStorage, paramsIn.slotStorage, sizeof(SlotStorage_t));
roywant 0:ed0152b5c495 113 memcpy(slotFrameTypes, paramsIn.slotFrameTypes, sizeof(SlotFrameTypes_t));
roywant 0:ed0152b5c495 114 memcpy(slotEidRotationPeriodExps, paramsIn.slotEidRotationPeriodExps, sizeof(SlotEidRotationPeriodExps_t));
roywant 0:ed0152b5c495 115 memcpy(slotEidIdentityKeys, paramsIn.slotEidIdentityKeys, sizeof(SlotEidIdentityKeys_t));
roywant 0:ed0152b5c495 116 remainConnectable = paramsIn.remainConnectable;
roywant 0:ed0152b5c495 117
roywant 0:ed0152b5c495 118 if (advConfigIntervalIn != 0) {
roywant 0:ed0152b5c495 119 if (advConfigIntervalIn < ble.gap().getMinAdvertisingInterval()) {
roywant 0:ed0152b5c495 120 advConfigInterval = ble.gap().getMinAdvertisingInterval();
roywant 0:ed0152b5c495 121 } else if (advConfigIntervalIn > ble.gap().getMaxAdvertisingInterval()) {
roywant 0:ed0152b5c495 122 advConfigInterval = ble.gap().getMaxAdvertisingInterval();
roywant 0:ed0152b5c495 123 } else {
roywant 0:ed0152b5c495 124 advConfigInterval = advConfigIntervalIn;
roywant 0:ed0152b5c495 125 }
roywant 0:ed0152b5c495 126 }
roywant 0:ed0152b5c495 127
roywant 0:ed0152b5c495 128 // Generate fresh private and public ECDH keys for EID
roywant 0:ed0152b5c495 129 genEIDBeaconKeys();
roywant 0:ed0152b5c495 130
roywant 0:ed0152b5c495 131 // Recompute EID Slot Data
roywant 0:ed0152b5c495 132 for (int slot = 0; slot < MAX_ADV_SLOTS; slot++) {
roywant 0:ed0152b5c495 133 uint8_t* frame = slotToFrame(slot);
roywant 0:ed0152b5c495 134 switch (slotFrameTypes[slot]) {
roywant 0:ed0152b5c495 135 case EDDYSTONE_FRAME_EID:
roywant 0:ed0152b5c495 136 eidFrame.update(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], timeSinceBootTimer.read_ms() / 1000);
roywant 0:ed0152b5c495 137 eidFrame.setAdvTxPower(frame, slotAdvTxPowerLevels[slot]);
roywant 0:ed0152b5c495 138 break;
roywant 0:ed0152b5c495 139 default: ;
roywant 0:ed0152b5c495 140 }
roywant 0:ed0152b5c495 141 }
roywant 0:ed0152b5c495 142
roywant 0:ed0152b5c495 143
roywant 0:ed0152b5c495 144 /* TODO: Note that this timer is started from the time EddystoneService
roywant 0:ed0152b5c495 145 * is initialised and NOT from when the device is booted.
roywant 0:ed0152b5c495 146 */
roywant 0:ed0152b5c495 147 timeSinceBootTimer.start();
roywant 0:ed0152b5c495 148
roywant 0:ed0152b5c495 149 /* Set the device name at startup */
roywant 0:ed0152b5c495 150 ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(deviceName));
roywant 0:ed0152b5c495 151 }
roywant 0:ed0152b5c495 152
roywant 0:ed0152b5c495 153 // Regenerate the beacon keys
roywant 0:ed0152b5c495 154 void EddystoneService::genEIDBeaconKeys(void) {
roywant 0:ed0152b5c495 155 genBeaconKeyRC = -1;
roywant 0:ed0152b5c495 156 #ifdef GEN_BEACON_KEYS_AT_INIT
roywant 0:ed0152b5c495 157 memset(privateEcdhKey, 0, 32);
roywant 0:ed0152b5c495 158 memset(publicEcdhKey, 0, 32);
roywant 0:ed0152b5c495 159 genBeaconKeyRC = eidFrame.genBeaconKeys(privateEcdhKey, publicEcdhKey);
roywant 0:ed0152b5c495 160 swapEndianArray(publicEcdhKey, publicEcdhKeyLE, 32);
roywant 0:ed0152b5c495 161 #endif
roywant 0:ed0152b5c495 162 }
roywant 0:ed0152b5c495 163
roywant 0:ed0152b5c495 164 /**
roywant 0:ed0152b5c495 165 * Factory reset all parmeters: used at initial boot, and activated from Char 11
roywant 0:ed0152b5c495 166 */
roywant 0:ed0152b5c495 167 void EddystoneService::doFactoryReset(void)
roywant 0:ed0152b5c495 168 {
roywant 0:ed0152b5c495 169 memset(slotCallbackHandles, 0, sizeof(SlotCallbackHandles_t));
roywant 0:ed0152b5c495 170 radioManagerCallbackHandle = NULL;
roywant 0:ed0152b5c495 171 memcpy(capabilities, CAPABILITIES_DEFAULT, CAP_HDR_LEN);
roywant 0:ed0152b5c495 172 // Line above leaves powerlevels blank; Line below fills them in
roywant 0:ed0152b5c495 173 memcpy(capabilities + CAP_HDR_LEN, radioTxPowerLevels, sizeof(PowerLevels_t));
roywant 0:ed0152b5c495 174 activeSlot = DEFAULT_SLOT;
roywant 0:ed0152b5c495 175 // Intervals
roywant 0:ed0152b5c495 176 uint16_t buf1[] = EDDYSTONE_DEFAULT_SLOT_INTERVALS;
roywant 0:ed0152b5c495 177 for (int i = 0; i < MAX_ADV_SLOTS; i++) {
roywant 0:ed0152b5c495 178 // Ensure all slot periods are in range
roywant 0:ed0152b5c495 179 buf1[i] = correctAdvertisementPeriod(buf1[i]);
roywant 0:ed0152b5c495 180 }
roywant 0:ed0152b5c495 181 memcpy(slotAdvIntervals, buf1, sizeof(SlotAdvIntervals_t));
roywant 0:ed0152b5c495 182 // Radio and Adv TX Power
roywant 0:ed0152b5c495 183 int8_t buf2[] = EDDYSTONE_DEFAULT_SLOT_TX_POWERS;
roywant 0:ed0152b5c495 184 for (int i = 0; i< MAX_ADV_SLOTS; i++) {
roywant 0:ed0152b5c495 185 slotRadioTxPowerLevels[i] = buf2[i];
roywant 0:ed0152b5c495 186 slotAdvTxPowerLevels[i] = advTxPowerLevels[radioTxPowerToIndex(buf2[i])];
roywant 0:ed0152b5c495 187 }
roywant 0:ed0152b5c495 188 // Lock
roywant 0:ed0152b5c495 189 lockState = UNLOCKED;
roywant 0:ed0152b5c495 190 uint8_t defKeyBuf[] = EDDYSTONE_DEFAULT_UNLOCK_KEY;
roywant 0:ed0152b5c495 191 memcpy(unlockKey, defKeyBuf, sizeof(Lock_t));
roywant 0:ed0152b5c495 192 memset(unlockToken, 0, sizeof(Lock_t));
roywant 0:ed0152b5c495 193 memset(challenge, 0, sizeof(Lock_t)); // NOTE: challenge is randomized on first unlockChar read;
roywant 0:ed0152b5c495 194
roywant 0:ed0152b5c495 195 // Generate ECDH Beacon Key Pair (Private/Public)
roywant 0:ed0152b5c495 196 genEIDBeaconKeys();
roywant 0:ed0152b5c495 197
roywant 0:ed0152b5c495 198 memcpy(slotEidIdentityKeys, slotDefaultEidIdentityKeys, sizeof(SlotEidIdentityKeys_t));
roywant 0:ed0152b5c495 199 uint8_t buf4[] = EDDYSTONE_DEFAULT_SLOT_EID_ROTATION_PERIOD_EXPS;
roywant 0:ed0152b5c495 200 memcpy(slotEidRotationPeriodExps, buf4, sizeof(SlotEidRotationPeriodExps_t));
roywant 0:ed0152b5c495 201 memset(slotEidNextRotationTimes, 0, sizeof(SlotEidNextRotationTimes_t));
roywant 0:ed0152b5c495 202 // Slot Data Type Defaults
roywant 0:ed0152b5c495 203 uint8_t buf3[] = EDDYSTONE_DEFAULT_SLOT_TYPES;
roywant 0:ed0152b5c495 204 memcpy(slotFrameTypes, buf3, sizeof(SlotFrameTypes_t));
roywant 0:ed0152b5c495 205 // Initialize Slot Data Defaults
roywant 0:ed0152b5c495 206 int eidSlot;
roywant 0:ed0152b5c495 207 for (int slot = 0; slot < MAX_ADV_SLOTS; slot++) {
roywant 0:ed0152b5c495 208 uint8_t* frame = slotToFrame(slot);
roywant 0:ed0152b5c495 209 switch (slotFrameTypes[slot]) {
roywant 0:ed0152b5c495 210 case EDDYSTONE_FRAME_UID:
roywant 0:ed0152b5c495 211 uidFrame.setData(frame, slotAdvTxPowerLevels[slot], reinterpret_cast<const uint8_t*>(slotDefaultUids[slot]));
roywant 0:ed0152b5c495 212 break;
roywant 0:ed0152b5c495 213 case EDDYSTONE_FRAME_URL:
roywant 0:ed0152b5c495 214 urlFrame.setUnencodedUrlData(frame, slotAdvTxPowerLevels[slot], slotDefaultUrls[slot]);
roywant 0:ed0152b5c495 215 break;
roywant 0:ed0152b5c495 216 case EDDYSTONE_FRAME_TLM:
roywant 0:ed0152b5c495 217 tlmFrame.setTLMData(TLMFrame::DEFAULT_TLM_VERSION);
roywant 0:ed0152b5c495 218 tlmFrame.setData(frame);
roywant 0:ed0152b5c495 219 eidSlot = getEidSlot();
roywant 0:ed0152b5c495 220 if (eidSlot != NO_EID_SLOT_SET) {
roywant 0:ed0152b5c495 221 LOG(("EID slot Set in FactoryReset\r\n"));
roywant 0:ed0152b5c495 222 tlmFrame.encryptData(frame, slotEidIdentityKeys[eidSlot], slotEidRotationPeriodExps[eidSlot], timeSinceBootTimer.read_ms() / 1000);
roywant 0:ed0152b5c495 223 }
roywant 0:ed0152b5c495 224 break;
roywant 0:ed0152b5c495 225 case EDDYSTONE_FRAME_EID:
roywant 0:ed0152b5c495 226 nextEidSlot = slot;
roywant 0:ed0152b5c495 227 eidFrame.setData(frame, slotAdvTxPowerLevels[slot], reinterpret_cast<const uint8_t*>(allSlotsDefaultEid));
roywant 0:ed0152b5c495 228 eidFrame.update(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], timeSinceBootTimer.read_ms() / 1000);
roywant 0:ed0152b5c495 229 break;
roywant 0:ed0152b5c495 230 }
roywant 0:ed0152b5c495 231 }
roywant 0:ed0152b5c495 232 #ifdef DONT_REMAIN_CONNECTABLE
roywant 0:ed0152b5c495 233 remainConnectable = REMAIN_CONNECTABLE_UNSET;
roywant 0:ed0152b5c495 234 #else
roywant 0:ed0152b5c495 235 remainConnectable = REMAIN_CONNECTABLE_SET;
roywant 0:ed0152b5c495 236 #endif
roywant 0:ed0152b5c495 237 factoryReset = false;
roywant 0:ed0152b5c495 238 }
roywant 0:ed0152b5c495 239
roywant 0:ed0152b5c495 240 /* Setup callback to update BatteryVoltage in TLM frame */
roywant 0:ed0152b5c495 241 void EddystoneService::onTLMBatteryVoltageUpdate(TlmUpdateCallback_t tlmBatteryVoltageCallbackIn)
roywant 0:ed0152b5c495 242 {
roywant 0:ed0152b5c495 243 tlmBatteryVoltageCallback = tlmBatteryVoltageCallbackIn;
roywant 0:ed0152b5c495 244 }
roywant 0:ed0152b5c495 245
roywant 0:ed0152b5c495 246 /* Setup callback to update BeaconTemperature in TLM frame */
roywant 0:ed0152b5c495 247 void EddystoneService::onTLMBeaconTemperatureUpdate(TlmUpdateCallback_t tlmBeaconTemperatureCallbackIn)
roywant 0:ed0152b5c495 248 {
roywant 0:ed0152b5c495 249 tlmBeaconTemperatureCallback = tlmBeaconTemperatureCallbackIn;
roywant 0:ed0152b5c495 250 }
roywant 0:ed0152b5c495 251
roywant 0:ed0152b5c495 252 EddystoneService::EddystoneError_t EddystoneService::startEddystoneBeaconAdvertisements(void)
roywant 0:ed0152b5c495 253 {
roywant 0:ed0152b5c495 254 stopEddystoneBeaconAdvertisements();
roywant 0:ed0152b5c495 255
roywant 0:ed0152b5c495 256 bool intervalValidFlag = false;
roywant 0:ed0152b5c495 257 for (int i = 0; i < MAX_ADV_SLOTS; i++) {
roywant 0:ed0152b5c495 258 if (slotAdvIntervals[i] != 0) {
roywant 0:ed0152b5c495 259 intervalValidFlag = true;
roywant 0:ed0152b5c495 260 }
roywant 0:ed0152b5c495 261 }
roywant 0:ed0152b5c495 262
roywant 0:ed0152b5c495 263 if (!intervalValidFlag) {
roywant 0:ed0152b5c495 264 /* Nothing to do, the period is 0 for all frames */
roywant 0:ed0152b5c495 265 return EDDYSTONE_ERROR_INVALID_ADVERTISING_INTERVAL;
roywant 0:ed0152b5c495 266 }
roywant 0:ed0152b5c495 267
roywant 0:ed0152b5c495 268 // In case left over from Config Adv Mode
roywant 0:ed0152b5c495 269 ble.gap().clearScanResponse();
roywant 0:ed0152b5c495 270
roywant 0:ed0152b5c495 271 operationMode = EDDYSTONE_MODE_BEACON;
roywant 0:ed0152b5c495 272
roywant 0:ed0152b5c495 273 /* Configure advertisements initially at power of active slot*/
roywant 0:ed0152b5c495 274 ble.gap().setTxPower(slotRadioTxPowerLevels[activeSlot]);
roywant 0:ed0152b5c495 275
roywant 0:ed0152b5c495 276 if (remainConnectable) {
roywant 0:ed0152b5c495 277 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
roywant 0:ed0152b5c495 278 } else {
roywant 0:ed0152b5c495 279 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
roywant 0:ed0152b5c495 280 }
roywant 0:ed0152b5c495 281 ble.gap().setAdvertisingInterval(ble.gap().getMaxAdvertisingInterval());
roywant 0:ed0152b5c495 282
roywant 0:ed0152b5c495 283 /* Make sure the queue is currently empty */
roywant 0:ed0152b5c495 284 advFrameQueue.reset();
roywant 0:ed0152b5c495 285 /* Setup callbacks to periodically add frames to be advertised to the queue and
roywant 0:ed0152b5c495 286 * add initial frame so that we have something to advertise on startup */
roywant 0:ed0152b5c495 287 for (int slot = 0; slot < MAX_ADV_SLOTS; slot++) {
roywant 0:ed0152b5c495 288 uint8_t* frame = slotToFrame(slot);
roywant 0:ed0152b5c495 289 if (slotAdvIntervals[slot] && testValidFrame(frame)) {
roywant 0:ed0152b5c495 290 advFrameQueue.push(slot);
roywant 0:ed0152b5c495 291 slotCallbackHandles[slot] = eventQueue.post_every(
roywant 0:ed0152b5c495 292 &EddystoneService::enqueueFrame, this, slot,
roywant 0:ed0152b5c495 293 slotAdvIntervals[slot] /* ms */
roywant 0:ed0152b5c495 294 );
roywant 0:ed0152b5c495 295 }
roywant 0:ed0152b5c495 296 }
roywant 0:ed0152b5c495 297 /* Start advertising */
roywant 0:ed0152b5c495 298 manageRadio();
roywant 0:ed0152b5c495 299
roywant 0:ed0152b5c495 300 return EDDYSTONE_ERROR_NONE;
roywant 0:ed0152b5c495 301 }
roywant 0:ed0152b5c495 302
roywant 0:ed0152b5c495 303 ble_error_t EddystoneService::setCompleteDeviceName(const char *deviceNameIn)
roywant 0:ed0152b5c495 304 {
roywant 0:ed0152b5c495 305 /* Make sure the device name is safe */
roywant 0:ed0152b5c495 306 ble_error_t error = ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(deviceNameIn));
roywant 0:ed0152b5c495 307 if (error == BLE_ERROR_NONE) {
roywant 0:ed0152b5c495 308 deviceName = deviceNameIn;
roywant 0:ed0152b5c495 309 if (operationMode == EDDYSTONE_MODE_CONFIG) {
roywant 0:ed0152b5c495 310 /* Need to update the advertising packets to the new name */
roywant 0:ed0152b5c495 311 setupEddystoneConfigScanResponse();
roywant 0:ed0152b5c495 312 }
roywant 0:ed0152b5c495 313 }
roywant 0:ed0152b5c495 314
roywant 0:ed0152b5c495 315 return error;
roywant 0:ed0152b5c495 316 }
roywant 0:ed0152b5c495 317
roywant 0:ed0152b5c495 318 /* It is not the responsibility of the Eddystone implementation to store
roywant 0:ed0152b5c495 319 * the configured parameters in persistent storage since this is
roywant 0:ed0152b5c495 320 * platform-specific. So we provide this function that returns the
roywant 0:ed0152b5c495 321 * configured values that need to be stored and the main application
roywant 0:ed0152b5c495 322 * takes care of storing them.
roywant 0:ed0152b5c495 323 */
roywant 0:ed0152b5c495 324 void EddystoneService::getEddystoneParams(EddystoneParams_t &params)
roywant 0:ed0152b5c495 325 {
roywant 0:ed0152b5c495 326 // Capabilities
roywant 0:ed0152b5c495 327 memcpy(params.capabilities, capabilities, sizeof(Capability_t));
roywant 0:ed0152b5c495 328 // Active Slot
roywant 0:ed0152b5c495 329 params.activeSlot = activeSlot;
roywant 0:ed0152b5c495 330 // Intervals
roywant 0:ed0152b5c495 331 memcpy(params.slotAdvIntervals, slotAdvIntervals, sizeof(SlotAdvIntervals_t));
roywant 0:ed0152b5c495 332 // Power Levels
roywant 0:ed0152b5c495 333 memcpy(params.radioTxPowerLevels, radioTxPowerLevels, sizeof(PowerLevels_t));
roywant 0:ed0152b5c495 334 memcpy(params.advTxPowerLevels, advTxPowerLevels, sizeof(PowerLevels_t));
roywant 0:ed0152b5c495 335 // Slot Power Levels
roywant 0:ed0152b5c495 336 memcpy(params.slotRadioTxPowerLevels, slotRadioTxPowerLevels, sizeof(MAX_ADV_SLOTS));
roywant 0:ed0152b5c495 337 memcpy(params.slotAdvTxPowerLevels, slotAdvTxPowerLevels, sizeof(MAX_ADV_SLOTS));
roywant 0:ed0152b5c495 338 // Lock
roywant 0:ed0152b5c495 339 params.lockState = lockState;
roywant 0:ed0152b5c495 340 memcpy(params.unlockKey, unlockKey, sizeof(Lock_t));
roywant 0:ed0152b5c495 341 memcpy(params.unlockToken, unlockToken, sizeof(Lock_t));
roywant 0:ed0152b5c495 342 memcpy(params.challenge, challenge, sizeof(Lock_t));
roywant 0:ed0152b5c495 343 // Slots
roywant 0:ed0152b5c495 344 memcpy(params.slotFrameTypes, slotFrameTypes, sizeof(SlotFrameTypes_t));
roywant 0:ed0152b5c495 345 memcpy(params.slotStorage, slotStorage, sizeof(SlotStorage_t));
roywant 0:ed0152b5c495 346 memcpy(params.slotEidRotationPeriodExps, slotEidRotationPeriodExps, sizeof(SlotEidRotationPeriodExps_t));
roywant 0:ed0152b5c495 347 memcpy(params.slotEidIdentityKeys, slotEidIdentityKeys, sizeof(SlotEidIdentityKeys_t));
roywant 0:ed0152b5c495 348 // Testing and Management
roywant 0:ed0152b5c495 349 params.remainConnectable = remainConnectable;
roywant 0:ed0152b5c495 350 }
roywant 0:ed0152b5c495 351
roywant 0:ed0152b5c495 352 void EddystoneService::swapAdvertisedFrame(int slot)
roywant 0:ed0152b5c495 353 {
roywant 0:ed0152b5c495 354 uint8_t* frame = slotToFrame(slot);
roywant 0:ed0152b5c495 355 uint8_t frameType = slotFrameTypes[slot];
roywant 0:ed0152b5c495 356 uint32_t timeSecs = timeSinceBootTimer.read_ms() / 1000;
roywant 0:ed0152b5c495 357 switch (frameType) {
roywant 0:ed0152b5c495 358 case EDDYSTONE_FRAME_UID:
roywant 0:ed0152b5c495 359 updateAdvertisementPacket(uidFrame.getAdvFrame(frame), uidFrame.getAdvFrameLength(frame));
roywant 0:ed0152b5c495 360 break;
roywant 0:ed0152b5c495 361 case EDDYSTONE_FRAME_URL:
roywant 0:ed0152b5c495 362 updateAdvertisementPacket(urlFrame.getAdvFrame(frame), urlFrame.getAdvFrameLength(frame));
roywant 0:ed0152b5c495 363 break;
roywant 0:ed0152b5c495 364 case EDDYSTONE_FRAME_TLM:
roywant 0:ed0152b5c495 365 updateRawTLMFrame(frame);
roywant 0:ed0152b5c495 366 updateAdvertisementPacket(tlmFrame.getAdvFrame(frame), tlmFrame.getAdvFrameLength(frame));
roywant 0:ed0152b5c495 367 break;
roywant 0:ed0152b5c495 368 case EDDYSTONE_FRAME_EID:
roywant 0:ed0152b5c495 369 // only update the frame if the rotation period is due
roywant 0:ed0152b5c495 370 if (timeSecs >= slotEidNextRotationTimes[slot]) {
roywant 0:ed0152b5c495 371 eidFrame.update(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], timeSecs);
roywant 0:ed0152b5c495 372 slotEidNextRotationTimes[slot] = timeSecs + (1 << slotEidRotationPeriodExps[slot]);
roywant 0:ed0152b5c495 373 setRandomMacAddress(); // selects a new MAC address so the beacon is not trackable
roywant 0:ed0152b5c495 374 LOG(("EID ROTATED: Time=%lu\r\n", timeSecs));
roywant 0:ed0152b5c495 375 }
roywant 0:ed0152b5c495 376 updateAdvertisementPacket(eidFrame.getAdvFrame(frame), eidFrame.getAdvFrameLength(frame));
roywant 0:ed0152b5c495 377 break;
roywant 0:ed0152b5c495 378 default:
roywant 0:ed0152b5c495 379 //Some error occurred
roywant 0:ed0152b5c495 380 error("Frame to swap in does not specify a valid type");
roywant 0:ed0152b5c495 381 break;
roywant 0:ed0152b5c495 382 }
roywant 0:ed0152b5c495 383 ble.gap().setTxPower(slotRadioTxPowerLevels[slot]);
roywant 0:ed0152b5c495 384 }
roywant 0:ed0152b5c495 385
roywant 0:ed0152b5c495 386
roywant 0:ed0152b5c495 387 /* Helper function that calls user-defined functions to update Battery Voltage and Temperature (if available),
roywant 0:ed0152b5c495 388 * then updates the raw frame data and finally updates the actual advertised packet. This operation must be
roywant 0:ed0152b5c495 389 * done fairly often because the TLM frame TimeSinceBoot must have a 0.1 secs resolution according to the
roywant 0:ed0152b5c495 390 * Eddystone specification.
roywant 0:ed0152b5c495 391 */
roywant 0:ed0152b5c495 392 void EddystoneService::updateRawTLMFrame(uint8_t* frame)
roywant 0:ed0152b5c495 393 {
roywant 0:ed0152b5c495 394 if (tlmBeaconTemperatureCallback != NULL) {
roywant 0:ed0152b5c495 395 tlmFrame.updateBeaconTemperature((*tlmBeaconTemperatureCallback)(tlmFrame.getBeaconTemperature()));
roywant 0:ed0152b5c495 396 }
roywant 0:ed0152b5c495 397 if (tlmBatteryVoltageCallback != NULL) {
roywant 0:ed0152b5c495 398 tlmFrame.updateBatteryVoltage((*tlmBatteryVoltageCallback)(tlmFrame.getBatteryVoltage()));
roywant 0:ed0152b5c495 399 }
roywant 0:ed0152b5c495 400 tlmFrame.updateTimeSinceBoot(timeSinceBootTimer.read_ms());
roywant 0:ed0152b5c495 401 tlmFrame.setData(frame);
roywant 0:ed0152b5c495 402 int slot = getEidSlot();
roywant 0:ed0152b5c495 403 LOG(("TLMHelper Method slot=%d\r\n", slot));
roywant 0:ed0152b5c495 404 if (slot != NO_EID_SLOT_SET) {
roywant 0:ed0152b5c495 405 LOG(("TLMHelper: Before Encrypting TLM\r\n"));
roywant 0:ed0152b5c495 406 tlmFrame.encryptData(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], timeSinceBootTimer.read_ms() / 1000);
roywant 0:ed0152b5c495 407 LOG(("TLMHelper: Before Encrypting TLM\r\n"));
roywant 0:ed0152b5c495 408 }
roywant 0:ed0152b5c495 409 }
roywant 0:ed0152b5c495 410
roywant 0:ed0152b5c495 411 void EddystoneService::updateAdvertisementPacket(const uint8_t* rawFrame, size_t rawFrameLength)
roywant 0:ed0152b5c495 412 {
roywant 0:ed0152b5c495 413 ble.gap().clearAdvertisingPayload();
roywant 0:ed0152b5c495 414 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
roywant 0:ed0152b5c495 415 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, EDDYSTONE_UUID, sizeof(EDDYSTONE_UUID));
roywant 0:ed0152b5c495 416 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, rawFrame, rawFrameLength);
roywant 0:ed0152b5c495 417 }
roywant 0:ed0152b5c495 418
roywant 0:ed0152b5c495 419 uint8_t* EddystoneService::slotToFrame(int slot)
roywant 0:ed0152b5c495 420 {
roywant 0:ed0152b5c495 421 return reinterpret_cast<uint8_t *>(&slotStorage[slot * sizeof(Slot_t)]);
roywant 0:ed0152b5c495 422 }
roywant 0:ed0152b5c495 423
roywant 0:ed0152b5c495 424 void EddystoneService::enqueueFrame(int slot)
roywant 0:ed0152b5c495 425 {
roywant 0:ed0152b5c495 426 advFrameQueue.push(slot);
roywant 0:ed0152b5c495 427 if (!radioManagerCallbackHandle) {
roywant 0:ed0152b5c495 428 /* Advertising stopped and there is not callback posted in the event queue. Just
roywant 0:ed0152b5c495 429 * execute the manager to resume advertising */
roywant 0:ed0152b5c495 430 manageRadio();
roywant 0:ed0152b5c495 431 }
roywant 0:ed0152b5c495 432 }
roywant 0:ed0152b5c495 433
roywant 0:ed0152b5c495 434 void EddystoneService::manageRadio(void)
roywant 0:ed0152b5c495 435 {
roywant 0:ed0152b5c495 436 uint8_t slot;
roywant 0:ed0152b5c495 437 uint32_t startTimeManageRadio = timeSinceBootTimer.read_ms();
roywant 0:ed0152b5c495 438
roywant 0:ed0152b5c495 439 /* Signal that there is currently no callback posted */
roywant 0:ed0152b5c495 440 radioManagerCallbackHandle = NULL;
roywant 0:ed0152b5c495 441
roywant 0:ed0152b5c495 442 if (advFrameQueue.pop(slot)) {
roywant 0:ed0152b5c495 443 /* We have something to advertise */
roywant 0:ed0152b5c495 444 if (ble.gap().getState().advertising) {
roywant 0:ed0152b5c495 445 ble.gap().stopAdvertising();
roywant 0:ed0152b5c495 446 }
roywant 0:ed0152b5c495 447 swapAdvertisedFrame(slot);
roywant 0:ed0152b5c495 448 ble.gap().startAdvertising();
roywant 0:ed0152b5c495 449
roywant 0:ed0152b5c495 450 /* Increase the advertised packet count in TLM frame */
roywant 0:ed0152b5c495 451 tlmFrame.updatePduCount();
roywant 0:ed0152b5c495 452
roywant 0:ed0152b5c495 453 /* Post a callback to itself to stop the advertisement or pop the next
roywant 0:ed0152b5c495 454 * frame from the queue. However, take into account the time taken to
roywant 0:ed0152b5c495 455 * swap in this frame. */
roywant 0:ed0152b5c495 456 radioManagerCallbackHandle = eventQueue.post_in(
roywant 0:ed0152b5c495 457 &EddystoneService::manageRadio, this,
roywant 0:ed0152b5c495 458 ble.gap().getMinNonConnectableAdvertisingInterval() - (timeSinceBootTimer.read_ms() - startTimeManageRadio) /* ms */
roywant 0:ed0152b5c495 459 );
roywant 0:ed0152b5c495 460 } else if (ble.gap().getState().advertising) {
roywant 0:ed0152b5c495 461 /* Nothing else to advertise, stop advertising and do not schedule any callbacks */
roywant 0:ed0152b5c495 462 ble.gap().stopAdvertising();
roywant 0:ed0152b5c495 463 }
roywant 0:ed0152b5c495 464 }
roywant 0:ed0152b5c495 465
roywant 0:ed0152b5c495 466 void EddystoneService::startEddystoneConfigService(void)
roywant 0:ed0152b5c495 467 {
roywant 0:ed0152b5c495 468 uint16_t beAdvInterval = swapEndian(slotAdvIntervals[activeSlot]);
roywant 0:ed0152b5c495 469 int8_t radioTxPower = slotRadioTxPowerLevels[activeSlot];
roywant 0:ed0152b5c495 470 int8_t advTxPower = slotAdvTxPowerLevels[activeSlot];
roywant 0:ed0152b5c495 471 uint8_t* slotData = slotToFrame(activeSlot) + 1;
roywant 0:ed0152b5c495 472 aes128Encrypt(unlockKey, slotEidIdentityKeys[activeSlot], encryptedEidIdentityKey);
roywant 0:ed0152b5c495 473
roywant 0:ed0152b5c495 474 capabilitiesChar = new ReadOnlyArrayGattCharacteristic<uint8_t, sizeof(Capability_t)>(UUID_CAPABILITIES_CHAR, capabilities);
roywant 0:ed0152b5c495 475 activeSlotChar = new ReadWriteGattCharacteristic<uint8_t>(UUID_ACTIVE_SLOT_CHAR, &activeSlot);
roywant 0:ed0152b5c495 476 advIntervalChar = new ReadWriteGattCharacteristic<uint16_t>(UUID_ADV_INTERVAL_CHAR, &beAdvInterval);
roywant 0:ed0152b5c495 477 radioTxPowerChar = new ReadWriteGattCharacteristic<int8_t>(UUID_RADIO_TX_POWER_CHAR, &radioTxPower);
roywant 0:ed0152b5c495 478 advTxPowerChar = new ReadWriteGattCharacteristic<int8_t>(UUID_ADV_TX_POWER_CHAR, &advTxPower);
roywant 0:ed0152b5c495 479 lockStateChar = new GattCharacteristic(UUID_LOCK_STATE_CHAR, &lockState, sizeof(uint8_t), sizeof(LockState_t), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
roywant 0:ed0152b5c495 480 unlockChar = new ReadWriteArrayGattCharacteristic<uint8_t, sizeof(Lock_t)>(UUID_UNLOCK_CHAR, unlockToken);
roywant 0:ed0152b5c495 481 publicEcdhKeyChar = new GattCharacteristic(UUID_PUBLIC_ECDH_KEY_CHAR, publicEcdhKey, 0, sizeof(PublicEcdhKey_t), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
roywant 0:ed0152b5c495 482 eidIdentityKeyChar = new GattCharacteristic(UUID_EID_IDENTITY_KEY_CHAR, encryptedEidIdentityKey, 0, sizeof(EidIdentityKey_t), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
roywant 0:ed0152b5c495 483 advSlotDataChar = new GattCharacteristic(UUID_ADV_SLOT_DATA_CHAR, slotData, 0, 34, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
roywant 0:ed0152b5c495 484 factoryResetChar = new WriteOnlyGattCharacteristic<uint8_t>(UUID_FACTORY_RESET_CHAR, &factoryReset);
roywant 0:ed0152b5c495 485 remainConnectableChar = new ReadWriteGattCharacteristic<uint8_t>(UUID_REMAIN_CONNECTABLE_CHAR, &remainConnectable);
roywant 0:ed0152b5c495 486
roywant 0:ed0152b5c495 487 // CHAR-1 capabilities (READ ONLY)
roywant 0:ed0152b5c495 488 capabilitiesChar->setReadAuthorizationCallback(this, &EddystoneService::readBasicTestLockAuthorizationCallback);
roywant 0:ed0152b5c495 489 // CHAR-2 Active Slot
roywant 0:ed0152b5c495 490 activeSlotChar->setReadAuthorizationCallback(this, &EddystoneService::readBasicTestLockAuthorizationCallback);
roywant 0:ed0152b5c495 491 activeSlotChar->setWriteAuthorizationCallback(this, &EddystoneService::writeActiveSlotAuthorizationCallback<uint8_t>);
roywant 0:ed0152b5c495 492 // CHAR-3 Adv Interval
roywant 0:ed0152b5c495 493 advIntervalChar->setReadAuthorizationCallback(this, &EddystoneService::readAdvIntervalAuthorizationCallback);
roywant 0:ed0152b5c495 494 advIntervalChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<uint16_t>);
roywant 0:ed0152b5c495 495 // CHAR-4 Radio TX Power
roywant 0:ed0152b5c495 496 radioTxPowerChar->setReadAuthorizationCallback(this, &EddystoneService::readRadioTxPowerAuthorizationCallback);
roywant 0:ed0152b5c495 497 radioTxPowerChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<uint8_t>);
roywant 0:ed0152b5c495 498 // CHAR-5
roywant 0:ed0152b5c495 499 advTxPowerChar->setReadAuthorizationCallback(this, &EddystoneService::readAdvTxPowerAuthorizationCallback);
roywant 0:ed0152b5c495 500 advTxPowerChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<uint8_t>);
roywant 0:ed0152b5c495 501 // CHAR-6 Lock State
roywant 0:ed0152b5c495 502 lockStateChar->setWriteAuthorizationCallback(this, &EddystoneService::writeLockStateAuthorizationCallback);
roywant 0:ed0152b5c495 503 // CHAR-7 Unlock
roywant 0:ed0152b5c495 504 unlockChar->setReadAuthorizationCallback(this, &EddystoneService::readUnlockAuthorizationCallback);
roywant 0:ed0152b5c495 505 unlockChar->setWriteAuthorizationCallback(this, &EddystoneService::writeUnlockAuthorizationCallback);
roywant 0:ed0152b5c495 506 // CHAR-8 Public Ecdh Key (READ ONLY)
roywant 0:ed0152b5c495 507 publicEcdhKeyChar->setReadAuthorizationCallback(this, &EddystoneService::readPublicEcdhKeyAuthorizationCallback);
roywant 0:ed0152b5c495 508 // CHAR-9 EID Identity Key (READ ONLY)
roywant 0:ed0152b5c495 509 eidIdentityKeyChar->setReadAuthorizationCallback(this, &EddystoneService::readEidIdentityAuthorizationCallback);
roywant 0:ed0152b5c495 510 // CHAR-10 Adv Slot Data
roywant 0:ed0152b5c495 511 advSlotDataChar->setReadAuthorizationCallback(this, &EddystoneService::readDataAuthorizationCallback);
roywant 0:ed0152b5c495 512 advSlotDataChar->setWriteAuthorizationCallback(this, &EddystoneService::writeVarLengthDataAuthorizationCallback);
roywant 0:ed0152b5c495 513 // CHAR-11 Factory Reset
roywant 0:ed0152b5c495 514 factoryResetChar->setReadAuthorizationCallback(this, &EddystoneService::readBasicTestLockAuthorizationCallback);
roywant 0:ed0152b5c495 515 factoryResetChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<bool>);
roywant 0:ed0152b5c495 516 // CHAR-12 Remain Connectable
roywant 0:ed0152b5c495 517 remainConnectableChar->setReadAuthorizationCallback(this, &EddystoneService::readBasicTestLockAuthorizationCallback);
roywant 0:ed0152b5c495 518 remainConnectableChar->setWriteAuthorizationCallback(this, &EddystoneService::writeBasicAuthorizationCallback<bool>);
roywant 0:ed0152b5c495 519
roywant 0:ed0152b5c495 520 // Create pointers to all characteristics in the GATT service
roywant 0:ed0152b5c495 521 charTable[0] = capabilitiesChar;
roywant 0:ed0152b5c495 522 charTable[1] = activeSlotChar;
roywant 0:ed0152b5c495 523 charTable[2] = advIntervalChar;
roywant 0:ed0152b5c495 524 charTable[3] = radioTxPowerChar;
roywant 0:ed0152b5c495 525 charTable[4] = advTxPowerChar;
roywant 0:ed0152b5c495 526 charTable[5] = lockStateChar;
roywant 0:ed0152b5c495 527 charTable[6] = unlockChar;
roywant 0:ed0152b5c495 528 charTable[7] = publicEcdhKeyChar;
roywant 0:ed0152b5c495 529 charTable[8] = eidIdentityKeyChar;
roywant 0:ed0152b5c495 530 charTable[9] = advSlotDataChar;
roywant 0:ed0152b5c495 531 charTable[10] = factoryResetChar;
roywant 0:ed0152b5c495 532 charTable[11] = remainConnectableChar;
roywant 0:ed0152b5c495 533
roywant 0:ed0152b5c495 534 GattService configService(UUID_ES_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
roywant 0:ed0152b5c495 535
roywant 0:ed0152b5c495 536 ble.gattServer().addService(configService);
roywant 0:ed0152b5c495 537 ble.gattServer().onDataWritten(this, &EddystoneService::onDataWrittenCallback);
roywant 0:ed0152b5c495 538 updateCharacteristicValues();
roywant 0:ed0152b5c495 539 }
roywant 0:ed0152b5c495 540
roywant 0:ed0152b5c495 541
roywant 0:ed0152b5c495 542 void EddystoneService::freeConfigCharacteristics(void)
roywant 0:ed0152b5c495 543 {
roywant 0:ed0152b5c495 544 delete capabilitiesChar;
roywant 0:ed0152b5c495 545 delete activeSlotChar;
roywant 0:ed0152b5c495 546 delete advIntervalChar;
roywant 0:ed0152b5c495 547 delete radioTxPowerChar;
roywant 0:ed0152b5c495 548 delete advTxPowerChar;
roywant 0:ed0152b5c495 549 delete lockStateChar;
roywant 0:ed0152b5c495 550 delete unlockChar;
roywant 0:ed0152b5c495 551 delete publicEcdhKeyChar;
roywant 0:ed0152b5c495 552 delete eidIdentityKeyChar;
roywant 0:ed0152b5c495 553 delete advSlotDataChar;
roywant 0:ed0152b5c495 554 delete factoryResetChar;
roywant 0:ed0152b5c495 555 delete remainConnectableChar;
roywant 0:ed0152b5c495 556 }
roywant 0:ed0152b5c495 557
roywant 0:ed0152b5c495 558 void EddystoneService::stopEddystoneBeaconAdvertisements(void)
roywant 0:ed0152b5c495 559 {
roywant 0:ed0152b5c495 560 /* Unschedule callbacks */
roywant 0:ed0152b5c495 561
roywant 0:ed0152b5c495 562 for (int slot = 0; slot < MAX_ADV_SLOTS; slot++) {
roywant 0:ed0152b5c495 563 if (slotCallbackHandles[slot]) {
roywant 0:ed0152b5c495 564 eventQueue.cancel(slotCallbackHandles[slot]);
roywant 0:ed0152b5c495 565 slotCallbackHandles[slot] = NULL;
roywant 0:ed0152b5c495 566 }
roywant 0:ed0152b5c495 567 }
roywant 0:ed0152b5c495 568
roywant 0:ed0152b5c495 569 if (radioManagerCallbackHandle) {
roywant 0:ed0152b5c495 570 eventQueue.cancel(radioManagerCallbackHandle);
roywant 0:ed0152b5c495 571 radioManagerCallbackHandle = NULL;
roywant 0:ed0152b5c495 572 }
roywant 0:ed0152b5c495 573
roywant 0:ed0152b5c495 574 /* Stop any current Advs (ES Config or Beacon) */
roywant 0:ed0152b5c495 575 BLE::Instance().gap().stopAdvertising();
roywant 0:ed0152b5c495 576 }
roywant 0:ed0152b5c495 577
roywant 0:ed0152b5c495 578 /*
roywant 0:ed0152b5c495 579 * Internal helper function used to update the GATT database following any
roywant 0:ed0152b5c495 580 * change to the internal state of the service object.
roywant 0:ed0152b5c495 581 */
roywant 0:ed0152b5c495 582 void EddystoneService::updateCharacteristicValues(void)
roywant 0:ed0152b5c495 583 {
roywant 0:ed0152b5c495 584 // Init variables for update
roywant 0:ed0152b5c495 585 uint16_t beAdvInterval = swapEndian(slotAdvIntervals[activeSlot]);
roywant 0:ed0152b5c495 586 int8_t radioTxPower = slotRadioTxPowerLevels[activeSlot];
roywant 0:ed0152b5c495 587 int8_t advTxPower = slotAdvTxPowerLevels[activeSlot];
roywant 0:ed0152b5c495 588 uint8_t* frame = slotToFrame(activeSlot);
roywant 0:ed0152b5c495 589 uint8_t slotLength = 0;
roywant 0:ed0152b5c495 590 uint8_t* slotData = NULL;
roywant 0:ed0152b5c495 591 memset(encryptedEidIdentityKey, 0, sizeof(encryptedEidIdentityKey));
roywant 0:ed0152b5c495 592
roywant 0:ed0152b5c495 593 switch(slotFrameTypes[activeSlot]) {
roywant 0:ed0152b5c495 594 case EDDYSTONE_FRAME_UID:
roywant 0:ed0152b5c495 595 slotLength = uidFrame.getDataLength(frame);
roywant 0:ed0152b5c495 596 slotData = uidFrame.getData(frame);
roywant 0:ed0152b5c495 597 break;
roywant 0:ed0152b5c495 598 case EDDYSTONE_FRAME_URL:
roywant 0:ed0152b5c495 599 slotLength = urlFrame.getDataLength(frame);
roywant 0:ed0152b5c495 600 slotData = urlFrame.getData(frame);
roywant 0:ed0152b5c495 601 break;
roywant 0:ed0152b5c495 602 case EDDYSTONE_FRAME_TLM:
roywant 0:ed0152b5c495 603 updateRawTLMFrame(frame);
roywant 0:ed0152b5c495 604 slotLength = tlmFrame.getDataLength(frame);
roywant 0:ed0152b5c495 605 slotData = tlmFrame.getData(frame);
roywant 0:ed0152b5c495 606 break;
roywant 0:ed0152b5c495 607 case EDDYSTONE_FRAME_EID:
roywant 0:ed0152b5c495 608 slotLength = eidFrame.getDataLength(frame);
roywant 0:ed0152b5c495 609 slotData = eidFrame.getData(frame);
roywant 0:ed0152b5c495 610 aes128Encrypt(unlockKey, slotEidIdentityKeys[activeSlot], encryptedEidIdentityKey);
roywant 0:ed0152b5c495 611 break;
roywant 0:ed0152b5c495 612 }
roywant 0:ed0152b5c495 613
roywant 0:ed0152b5c495 614 ble.gattServer().write(capabilitiesChar->getValueHandle(), reinterpret_cast<uint8_t *>(capabilities), sizeof(Capability_t));
roywant 0:ed0152b5c495 615 ble.gattServer().write(activeSlotChar->getValueHandle(), &activeSlot, sizeof(uint8_t));
roywant 0:ed0152b5c495 616 ble.gattServer().write(advIntervalChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beAdvInterval), sizeof(uint16_t));
roywant 0:ed0152b5c495 617 ble.gattServer().write(radioTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&radioTxPower), sizeof(int8_t));
roywant 0:ed0152b5c495 618 ble.gattServer().write(advTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&advTxPower), sizeof(int8_t));
roywant 0:ed0152b5c495 619 ble.gattServer().write(lockStateChar->getValueHandle(), &lockState, sizeof(uint8_t));
roywant 0:ed0152b5c495 620 ble.gattServer().write(unlockChar->getValueHandle(), unlockToken, sizeof(Lock_t));
roywant 0:ed0152b5c495 621 ble.gattServer().write(publicEcdhKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(publicEcdhKey), sizeof(PublicEcdhKey_t));
roywant 0:ed0152b5c495 622 ble.gattServer().write(eidIdentityKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(encryptedEidIdentityKey), sizeof(EidIdentityKey_t));
roywant 0:ed0152b5c495 623 ble.gattServer().write(advSlotDataChar->getValueHandle(), slotData, slotLength);
roywant 0:ed0152b5c495 624 ble.gattServer().write(factoryResetChar->getValueHandle(), &factoryReset, sizeof(uint8_t));
roywant 0:ed0152b5c495 625 ble.gattServer().write(remainConnectableChar->getValueHandle(), &remainConnectable, sizeof(uint8_t));
roywant 0:ed0152b5c495 626 }
roywant 0:ed0152b5c495 627
roywant 0:ed0152b5c495 628 EddystoneService::EddystoneError_t EddystoneService::startEddystoneConfigAdvertisements(void)
roywant 0:ed0152b5c495 629 {
roywant 0:ed0152b5c495 630 stopEddystoneBeaconAdvertisements();
roywant 0:ed0152b5c495 631
roywant 0:ed0152b5c495 632 if (advConfigInterval == 0) {
roywant 0:ed0152b5c495 633 // Nothing to do, the advertisement interval is 0
roywant 0:ed0152b5c495 634 return EDDYSTONE_ERROR_INVALID_ADVERTISING_INTERVAL;
roywant 0:ed0152b5c495 635 }
roywant 0:ed0152b5c495 636
roywant 0:ed0152b5c495 637 operationMode = EDDYSTONE_MODE_CONFIG;
roywant 0:ed0152b5c495 638
roywant 0:ed0152b5c495 639 ble.gap().clearAdvertisingPayload();
roywant 0:ed0152b5c495 640
roywant 0:ed0152b5c495 641 /* Accumulate the new payload */
roywant 0:ed0152b5c495 642 ble.gap().accumulateAdvertisingPayload(
roywant 0:ed0152b5c495 643 GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE
roywant 0:ed0152b5c495 644 );
roywant 0:ed0152b5c495 645 /* UUID is in different order in the ADV frame (!) */
roywant 0:ed0152b5c495 646 uint8_t reversedServiceUUID[sizeof(UUID_ES_BEACON_SERVICE)];
roywant 0:ed0152b5c495 647 for (size_t i = 0; i < sizeof(UUID_ES_BEACON_SERVICE); i++) {
roywant 0:ed0152b5c495 648 reversedServiceUUID[i] = UUID_ES_BEACON_SERVICE[sizeof(UUID_ES_BEACON_SERVICE) - i - 1];
roywant 0:ed0152b5c495 649 }
roywant 0:ed0152b5c495 650 ble.gap().accumulateAdvertisingPayload(
roywant 0:ed0152b5c495 651 GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS,
roywant 0:ed0152b5c495 652 reversedServiceUUID,
roywant 0:ed0152b5c495 653 sizeof(reversedServiceUUID)
roywant 0:ed0152b5c495 654 );
roywant 0:ed0152b5c495 655 ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG);
roywant 0:ed0152b5c495 656 setupEddystoneConfigScanResponse();
roywant 0:ed0152b5c495 657
roywant 0:ed0152b5c495 658 ble.gap().setTxPower(radioTxPowerLevels[sizeof(PowerLevels_t)-1]); // Max Power for Config
roywant 0:ed0152b5c495 659 ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
roywant 0:ed0152b5c495 660 ble.gap().setAdvertisingInterval(advConfigInterval);
roywant 0:ed0152b5c495 661 ble.gap().startAdvertising();
roywant 0:ed0152b5c495 662
roywant 0:ed0152b5c495 663 return EDDYSTONE_ERROR_NONE;
roywant 0:ed0152b5c495 664 }
roywant 0:ed0152b5c495 665
roywant 0:ed0152b5c495 666 void EddystoneService::setupEddystoneConfigScanResponse(void)
roywant 0:ed0152b5c495 667 {
roywant 0:ed0152b5c495 668 ble.gap().clearScanResponse();
roywant 0:ed0152b5c495 669 ble.gap().accumulateScanResponse(
roywant 0:ed0152b5c495 670 GapAdvertisingData::COMPLETE_LOCAL_NAME,
roywant 0:ed0152b5c495 671 reinterpret_cast<const uint8_t *>(deviceName),
roywant 0:ed0152b5c495 672 strlen(deviceName)
roywant 0:ed0152b5c495 673 );
roywant 0:ed0152b5c495 674 ble.gap().accumulateScanResponse(
roywant 0:ed0152b5c495 675 GapAdvertisingData::TX_POWER_LEVEL,
roywant 0:ed0152b5c495 676 reinterpret_cast<uint8_t *>(&advTxPowerLevels[sizeof(PowerLevels_t)-1]),
roywant 0:ed0152b5c495 677 sizeof(uint8_t)
roywant 0:ed0152b5c495 678 );
roywant 0:ed0152b5c495 679 }
roywant 0:ed0152b5c495 680
roywant 0:ed0152b5c495 681 /* WRITE AUTHORIZATION */
roywant 0:ed0152b5c495 682
roywant 0:ed0152b5c495 683 void EddystoneService::writeUnlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
roywant 0:ed0152b5c495 684 {
roywant 0:ed0152b5c495 685 if (lockState == UNLOCKED) {
roywant 0:ed0152b5c495 686 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
roywant 0:ed0152b5c495 687 } else if (authParams->len != sizeof(Lock_t)) {
roywant 0:ed0152b5c495 688 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
roywant 0:ed0152b5c495 689 } else if (authParams->offset != 0) {
roywant 0:ed0152b5c495 690 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
roywant 0:ed0152b5c495 691 } else if (memcmp(authParams->data, unlockToken, sizeof(Lock_t)) != 0) {
roywant 0:ed0152b5c495 692 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
roywant 0:ed0152b5c495 693 } else {
roywant 0:ed0152b5c495 694 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
roywant 0:ed0152b5c495 695 }
roywant 0:ed0152b5c495 696 }
roywant 0:ed0152b5c495 697
roywant 0:ed0152b5c495 698 void EddystoneService::writeVarLengthDataAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
roywant 0:ed0152b5c495 699 {
roywant 0:ed0152b5c495 700 if (lockState == LOCKED) {
roywant 0:ed0152b5c495 701 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
roywant 0:ed0152b5c495 702 } else if ((authParams->len > 34) || (authParams->len == 0)) {
roywant 0:ed0152b5c495 703 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
roywant 0:ed0152b5c495 704 } else {
roywant 0:ed0152b5c495 705 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
roywant 0:ed0152b5c495 706 }
roywant 0:ed0152b5c495 707 }
roywant 0:ed0152b5c495 708
roywant 0:ed0152b5c495 709
roywant 0:ed0152b5c495 710 void EddystoneService::writeLockStateAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
roywant 0:ed0152b5c495 711 {
roywant 0:ed0152b5c495 712 if (lockState == LOCKED) {
roywant 0:ed0152b5c495 713 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
roywant 0:ed0152b5c495 714 } else if ((authParams->len != sizeof(uint8_t)) && (authParams->len != (sizeof(uint8_t) + sizeof(Lock_t)))) {
roywant 0:ed0152b5c495 715 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
roywant 0:ed0152b5c495 716 } else if (authParams->offset != 0) {
roywant 0:ed0152b5c495 717 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
roywant 0:ed0152b5c495 718 } else {
roywant 0:ed0152b5c495 719 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
roywant 0:ed0152b5c495 720 }
roywant 0:ed0152b5c495 721 }
roywant 0:ed0152b5c495 722
roywant 0:ed0152b5c495 723 template <typename T>
roywant 0:ed0152b5c495 724 void EddystoneService::writeBasicAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
roywant 0:ed0152b5c495 725 {
roywant 0:ed0152b5c495 726 if (lockState == LOCKED) {
roywant 0:ed0152b5c495 727 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
roywant 0:ed0152b5c495 728 } else if (authParams->len != sizeof(T)) {
roywant 0:ed0152b5c495 729 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
roywant 0:ed0152b5c495 730 } else if (authParams->offset != 0) {
roywant 0:ed0152b5c495 731 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
roywant 0:ed0152b5c495 732 } else {
roywant 0:ed0152b5c495 733 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
roywant 0:ed0152b5c495 734 }
roywant 0:ed0152b5c495 735 }
roywant 0:ed0152b5c495 736
roywant 0:ed0152b5c495 737 template <typename T>
roywant 0:ed0152b5c495 738 void EddystoneService::writeActiveSlotAuthorizationCallback(GattWriteAuthCallbackParams *authParams)
roywant 0:ed0152b5c495 739 {
roywant 0:ed0152b5c495 740 if (lockState == LOCKED) {
roywant 0:ed0152b5c495 741 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
roywant 0:ed0152b5c495 742 } else if (authParams->len != sizeof(T)) {
roywant 0:ed0152b5c495 743 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
roywant 0:ed0152b5c495 744 } else if (*(authParams->data) > MAX_ADV_SLOTS -1) {
roywant 0:ed0152b5c495 745 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
roywant 0:ed0152b5c495 746 } else if (authParams->offset != 0) {
roywant 0:ed0152b5c495 747 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
roywant 0:ed0152b5c495 748 } else {
roywant 0:ed0152b5c495 749 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
roywant 0:ed0152b5c495 750 }
roywant 0:ed0152b5c495 751 }
roywant 0:ed0152b5c495 752
roywant 0:ed0152b5c495 753 /* READ AUTHORIZTION */
roywant 0:ed0152b5c495 754
roywant 0:ed0152b5c495 755 void EddystoneService::readBasicTestLockAuthorizationCallback(GattReadAuthCallbackParams *authParams)
roywant 0:ed0152b5c495 756 {
roywant 0:ed0152b5c495 757 LOG(("\r\nDO READ BASIC TEST LOCK slot=%d\r\n", activeSlot));
roywant 0:ed0152b5c495 758 if (lockState == LOCKED) {
roywant 0:ed0152b5c495 759 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
roywant 0:ed0152b5c495 760 } else {
roywant 0:ed0152b5c495 761 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
roywant 0:ed0152b5c495 762 }
roywant 0:ed0152b5c495 763 }
roywant 0:ed0152b5c495 764
roywant 0:ed0152b5c495 765 void EddystoneService::readEidIdentityAuthorizationCallback(GattReadAuthCallbackParams *authParams)
roywant 0:ed0152b5c495 766 {
roywant 0:ed0152b5c495 767 LOG(("\r\nDO READ EID IDENTITY slot=%d\r\n", activeSlot));
roywant 0:ed0152b5c495 768 aes128Encrypt(unlockKey, slotEidIdentityKeys[activeSlot], encryptedEidIdentityKey);
roywant 0:ed0152b5c495 769 int sum = 0;
roywant 0:ed0152b5c495 770 // Test if the IdentityKey is all zeros for this slot
roywant 0:ed0152b5c495 771 for (uint8_t i = 0; i < sizeof(EidIdentityKey_t); i++) {
roywant 0:ed0152b5c495 772 sum = sum + slotEidIdentityKeys[activeSlot][i];
roywant 0:ed0152b5c495 773 }
roywant 0:ed0152b5c495 774 ble.gattServer().write(eidIdentityKeyChar->getValueHandle(), encryptedEidIdentityKey, sizeof(EidIdentityKey_t));
roywant 0:ed0152b5c495 775
roywant 0:ed0152b5c495 776 // When the array is all zeros, the key has not been set, so return fault
roywant 0:ed0152b5c495 777 if ((lockState == LOCKED) || (sum == 0)) {
roywant 0:ed0152b5c495 778 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
roywant 0:ed0152b5c495 779 } else {
roywant 0:ed0152b5c495 780 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
roywant 0:ed0152b5c495 781 }
roywant 0:ed0152b5c495 782 }
roywant 0:ed0152b5c495 783
roywant 0:ed0152b5c495 784 void EddystoneService::readPublicEcdhKeyAuthorizationCallback(GattReadAuthCallbackParams *authParams)
roywant 0:ed0152b5c495 785 {
roywant 0:ed0152b5c495 786 LOG(("\r\nDO READ BEACON PUBLIC ECDH KEY (LE) slot=%d\r\n", activeSlot));
roywant 0:ed0152b5c495 787
roywant 0:ed0152b5c495 788 ble.gattServer().write(publicEcdhKeyChar->getValueHandle(), publicEcdhKeyLE, sizeof(PublicEcdhKey_t));
roywant 0:ed0152b5c495 789
roywant 0:ed0152b5c495 790 // When the array is all zeros, the key has not been set, so return fault
roywant 0:ed0152b5c495 791 if (lockState == LOCKED) {
roywant 0:ed0152b5c495 792 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
roywant 0:ed0152b5c495 793 } else {
roywant 0:ed0152b5c495 794 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
roywant 0:ed0152b5c495 795 }
roywant 0:ed0152b5c495 796 }
roywant 0:ed0152b5c495 797
roywant 0:ed0152b5c495 798 void EddystoneService::readDataAuthorizationCallback(GattReadAuthCallbackParams *authParams)
roywant 0:ed0152b5c495 799 {
roywant 0:ed0152b5c495 800 LOG(("\r\nDO READ ADV-DATA : slot=%d\r\n", activeSlot));
roywant 0:ed0152b5c495 801 uint8_t frameType = slotFrameTypes[activeSlot];
roywant 0:ed0152b5c495 802 uint8_t* frame = slotToFrame(activeSlot);
roywant 0:ed0152b5c495 803 uint8_t slotLength = 1;
roywant 0:ed0152b5c495 804 uint8_t buf[14] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0};
roywant 0:ed0152b5c495 805 uint8_t* slotData = buf;
roywant 0:ed0152b5c495 806
roywant 0:ed0152b5c495 807 if (lockState == LOCKED) {
roywant 0:ed0152b5c495 808 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
roywant 0:ed0152b5c495 809 return;
roywant 0:ed0152b5c495 810 }
roywant 0:ed0152b5c495 811 LOG(("IN READ ADV-DATA AFTER LOCK TEST frameType=%d\r\n", frameType));
roywant 0:ed0152b5c495 812 if (testValidFrame(frame) ) { // Check the frame has valid data before proceeding
roywant 0:ed0152b5c495 813 switch(frameType) {
roywant 0:ed0152b5c495 814 case EDDYSTONE_FRAME_UID:
roywant 0:ed0152b5c495 815 LOG(("READ ADV-DATA UID SLOT DATA slot=%d\r\n", activeSlot));
roywant 0:ed0152b5c495 816 slotLength = uidFrame.getDataLength(frame);
roywant 0:ed0152b5c495 817 slotData = uidFrame.getData(frame);
roywant 0:ed0152b5c495 818 break;
roywant 0:ed0152b5c495 819 case EDDYSTONE_FRAME_URL:
roywant 0:ed0152b5c495 820 LOG(("READ ADV-DATA URL SLOT DATA slot=%d\r\n", activeSlot));
roywant 0:ed0152b5c495 821 slotLength = urlFrame.getDataLength(frame);
roywant 0:ed0152b5c495 822 slotData = urlFrame.getData(frame);
roywant 0:ed0152b5c495 823 break;
roywant 0:ed0152b5c495 824 case EDDYSTONE_FRAME_TLM:
roywant 0:ed0152b5c495 825 LOG(("READ ADV-DATA TLM SLOT DATA slot=%d\r\n", activeSlot));
roywant 0:ed0152b5c495 826 updateRawTLMFrame(frame);
roywant 0:ed0152b5c495 827 slotLength = tlmFrame.getDataLength(frame);
roywant 0:ed0152b5c495 828 slotData = tlmFrame.getData(frame);
roywant 0:ed0152b5c495 829 LOG(("READ ADV-DATA AFTER T/E TLM length=%d\r\n", slotLength));
roywant 0:ed0152b5c495 830 LOG(("Data=")); logPrintHex(slotData, 18);
roywant 0:ed0152b5c495 831 break;
roywant 0:ed0152b5c495 832 case EDDYSTONE_FRAME_EID:
roywant 0:ed0152b5c495 833 LOG(("READ ADV-DATA EID SLOT DATA slot=%d\r\n", activeSlot));
roywant 0:ed0152b5c495 834 slotLength = 14;
roywant 0:ed0152b5c495 835 buf[0] = EIDFrame::FRAME_TYPE_EID;
roywant 0:ed0152b5c495 836 buf[1] = slotEidRotationPeriodExps[activeSlot];
roywant 0:ed0152b5c495 837 // Add time as a big endian 32 bit number
roywant 0:ed0152b5c495 838 uint32_t timeSecs = timeSinceBootTimer.read_ms() / 1000;
roywant 0:ed0152b5c495 839 buf[2] = (timeSecs >> 24) & 0xff;
roywant 0:ed0152b5c495 840 buf[3] = (timeSecs >> 16) & 0xff;
roywant 0:ed0152b5c495 841 buf[4] = (timeSecs >> 8) & 0xff;
roywant 0:ed0152b5c495 842 buf[5] = timeSecs & 0xff;
roywant 0:ed0152b5c495 843 memcpy(buf + 6, eidFrame.getEid(frame), 8);
roywant 0:ed0152b5c495 844 slotData = buf;
roywant 0:ed0152b5c495 845 break;
roywant 0:ed0152b5c495 846 }
roywant 0:ed0152b5c495 847 }
roywant 0:ed0152b5c495 848 LOG(("IN READ ADV-DATA AFTER FRAME PROCESSING slot=%d\r\n", activeSlot));
roywant 0:ed0152b5c495 849 ble.gattServer().write(advSlotDataChar->getValueHandle(), slotData, slotLength);
roywant 0:ed0152b5c495 850 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
roywant 0:ed0152b5c495 851 }
roywant 0:ed0152b5c495 852
roywant 0:ed0152b5c495 853 bool EddystoneService::testValidFrame(uint8_t* frame) {
roywant 0:ed0152b5c495 854 return (frame[0] != 0 ) ? true : false;
roywant 0:ed0152b5c495 855 }
roywant 0:ed0152b5c495 856
roywant 0:ed0152b5c495 857 void EddystoneService::readUnlockAuthorizationCallback(GattReadAuthCallbackParams *authParams)
roywant 0:ed0152b5c495 858 {
roywant 0:ed0152b5c495 859 LOG(("\r\nDO READ UNLOCK slot=%d\r\n", activeSlot));
roywant 0:ed0152b5c495 860 if (lockState == UNLOCKED) {
roywant 0:ed0152b5c495 861 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
roywant 0:ed0152b5c495 862 return;
roywant 0:ed0152b5c495 863 }
roywant 0:ed0152b5c495 864 // Update the challenge ready for the characteristic read
roywant 0:ed0152b5c495 865 generateRandom(challenge, sizeof(Lock_t));
roywant 0:ed0152b5c495 866 aes128Encrypt(unlockKey, challenge, unlockToken);
roywant 0:ed0152b5c495 867 ble.gattServer().write(unlockChar->getValueHandle(), reinterpret_cast<uint8_t *>(challenge), sizeof(Lock_t));
roywant 0:ed0152b5c495 868 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
roywant 0:ed0152b5c495 869 }
roywant 0:ed0152b5c495 870
roywant 0:ed0152b5c495 871 void EddystoneService::readAdvIntervalAuthorizationCallback(GattReadAuthCallbackParams *authParams)
roywant 0:ed0152b5c495 872 {
roywant 0:ed0152b5c495 873 LOG(("\r\nDO READ ADV INTERVAL slot=%d\r\n", activeSlot));
roywant 0:ed0152b5c495 874 if (lockState == LOCKED) {
roywant 0:ed0152b5c495 875 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
roywant 0:ed0152b5c495 876 return;
roywant 0:ed0152b5c495 877 }
roywant 0:ed0152b5c495 878 uint16_t beAdvInterval = swapEndian(slotAdvIntervals[activeSlot]);
roywant 0:ed0152b5c495 879 ble.gattServer().write(advIntervalChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beAdvInterval), sizeof(uint16_t));
roywant 0:ed0152b5c495 880 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
roywant 0:ed0152b5c495 881 }
roywant 0:ed0152b5c495 882
roywant 0:ed0152b5c495 883 void EddystoneService::readRadioTxPowerAuthorizationCallback(GattReadAuthCallbackParams *authParams)
roywant 0:ed0152b5c495 884 {
roywant 0:ed0152b5c495 885 LOG(("\r\nDO READ RADIO TXPOWER slot=%d\r\n", activeSlot));
roywant 0:ed0152b5c495 886 if (lockState == LOCKED) {
roywant 0:ed0152b5c495 887 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
roywant 0:ed0152b5c495 888 return;
roywant 0:ed0152b5c495 889 }
roywant 0:ed0152b5c495 890 int8_t radioTxPower = slotRadioTxPowerLevels[activeSlot];
roywant 0:ed0152b5c495 891 ble.gattServer().write(radioTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&radioTxPower), sizeof(int8_t));
roywant 0:ed0152b5c495 892 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
roywant 0:ed0152b5c495 893 }
roywant 0:ed0152b5c495 894
roywant 0:ed0152b5c495 895 void EddystoneService::readAdvTxPowerAuthorizationCallback(GattReadAuthCallbackParams *authParams)
roywant 0:ed0152b5c495 896 {
roywant 0:ed0152b5c495 897 LOG(("\r\nDO READ ADV TXPOWER slot=%d\r\n", activeSlot));
roywant 0:ed0152b5c495 898 if (lockState == LOCKED) {
roywant 0:ed0152b5c495 899 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_READ_NOT_PERMITTED;
roywant 0:ed0152b5c495 900 return;
roywant 0:ed0152b5c495 901 }
roywant 0:ed0152b5c495 902 int8_t advTxPower = slotAdvTxPowerLevels[activeSlot];
roywant 0:ed0152b5c495 903 ble.gattServer().write(advTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&advTxPower), sizeof(int8_t));
roywant 0:ed0152b5c495 904 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
roywant 0:ed0152b5c495 905 }
roywant 0:ed0152b5c495 906
roywant 0:ed0152b5c495 907 /*
roywant 0:ed0152b5c495 908 * This callback is invoked when a GATT client attempts to modify any of the
roywant 0:ed0152b5c495 909 * characteristics of this service. Attempts to do so are also applied to
roywant 0:ed0152b5c495 910 * the internal state of this service object.
roywant 0:ed0152b5c495 911 */
roywant 0:ed0152b5c495 912 void EddystoneService::onDataWrittenCallback(const GattWriteCallbackParams *writeParams)
roywant 0:ed0152b5c495 913 {
roywant 0:ed0152b5c495 914 uint16_t handle = writeParams->handle;
roywant 0:ed0152b5c495 915 LOG(("\r\nDO WRITE: Handle=%d Len=%d\r\n", handle, writeParams->len));
roywant 0:ed0152b5c495 916 // CHAR-1 CAPABILITIES
roywant 0:ed0152b5c495 917 /* capabilitySlotChar is READ ONLY */
roywant 0:ed0152b5c495 918 // CHAR-2 ACTIVE SLOT
roywant 0:ed0152b5c495 919 if (handle == activeSlotChar->getValueHandle()) {
roywant 0:ed0152b5c495 920 LOG(("Write: Active Slot Handle=%d\r\n", handle));
roywant 0:ed0152b5c495 921 uint8_t slot = *(writeParams->data);
roywant 0:ed0152b5c495 922 LOG(("Active Slot=%d\r\n", slot));
roywant 0:ed0152b5c495 923 // Ensure slot does not exceed limit, or set highest slot
roywant 0:ed0152b5c495 924 if (slot < MAX_ADV_SLOTS) {
roywant 0:ed0152b5c495 925 activeSlot = slot;
roywant 0:ed0152b5c495 926 }
roywant 0:ed0152b5c495 927 ble.gattServer().write(activeSlotChar->getValueHandle(), &activeSlot, sizeof(uint8_t));
roywant 0:ed0152b5c495 928 // CHAR-3 ADV INTERVAL
roywant 0:ed0152b5c495 929 } else if (handle == advIntervalChar->getValueHandle()) {
roywant 0:ed0152b5c495 930 LOG(("Write: Interval Handle=%d\r\n", handle));
roywant 0:ed0152b5c495 931 uint16_t interval = correctAdvertisementPeriod(swapEndian(*((uint16_t *)(writeParams->data))));
roywant 0:ed0152b5c495 932 slotAdvIntervals[activeSlot] = interval; // Store this value for reading
roywant 0:ed0152b5c495 933 uint16_t beAdvInterval = swapEndian(slotAdvIntervals[activeSlot]);
roywant 0:ed0152b5c495 934 ble.gattServer().write(advIntervalChar->getValueHandle(), reinterpret_cast<uint8_t *>(&beAdvInterval), sizeof(uint16_t));
roywant 0:ed0152b5c495 935 // CHAR-4 RADIO TX POWER
roywant 0:ed0152b5c495 936 } else if (handle == radioTxPowerChar->getValueHandle()) {
roywant 0:ed0152b5c495 937 LOG(("Write: RADIO Power Handle=%d\r\n", handle));
roywant 0:ed0152b5c495 938 int8_t radioTxPower = *(writeParams->data);
roywant 0:ed0152b5c495 939 uint8_t index = radioTxPowerToIndex(radioTxPower);
roywant 0:ed0152b5c495 940 radioTxPower = radioTxPowerLevels[index]; // Power now corrected to nearest allowed power
roywant 0:ed0152b5c495 941 slotRadioTxPowerLevels[activeSlot] = radioTxPower; // Store by slot number
roywant 0:ed0152b5c495 942 int8_t advTxPower = advTxPowerLevels[index]; // Determine adv power equivalent
roywant 0:ed0152b5c495 943 slotAdvTxPowerLevels[activeSlot] = advTxPower;
roywant 0:ed0152b5c495 944 setFrameTxPower(activeSlot, advTxPower); // Set the actual frame radio TxPower for this slot
roywant 0:ed0152b5c495 945 ble.gattServer().write(radioTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&radioTxPower), sizeof(int8_t));
roywant 0:ed0152b5c495 946 // CHAR-5 ADV TX POWER
roywant 0:ed0152b5c495 947 } else if (handle == advTxPowerChar->getValueHandle()) {
roywant 0:ed0152b5c495 948 LOG(("Write: ADV Power Handle=%d\r\n", handle));
roywant 0:ed0152b5c495 949 int8_t advTxPower = *(writeParams->data);
roywant 0:ed0152b5c495 950 slotAdvTxPowerLevels[activeSlot] = advTxPower;
roywant 0:ed0152b5c495 951 setFrameTxPower(activeSlot, advTxPower); // Update the actual frame Adv TxPower for this slot
roywant 0:ed0152b5c495 952 ble.gattServer().write(advTxPowerChar->getValueHandle(), reinterpret_cast<uint8_t *>(&advTxPower), sizeof(int8_t));
roywant 0:ed0152b5c495 953 // CHAR-6 LOCK STATE
roywant 0:ed0152b5c495 954 } else if (handle == lockStateChar->getValueHandle()) {
roywant 0:ed0152b5c495 955 LOG(("Write: Lock State Handle=%d\r\n", handle));
roywant 0:ed0152b5c495 956 uint8_t newLockState = *(writeParams->data);
roywant 0:ed0152b5c495 957 if ((writeParams->len == sizeof(uint8_t)) || (writeParams->len == sizeof(uint8_t) + sizeof(Lock_t))) {
roywant 0:ed0152b5c495 958 if ((newLockState == LOCKED) || (newLockState == UNLOCKED) || (newLockState == UNLOCKED_AUTO_RELOCK_DISABLED)) {
roywant 0:ed0152b5c495 959 lockState = newLockState;
roywant 0:ed0152b5c495 960 }
roywant 0:ed0152b5c495 961 }
roywant 0:ed0152b5c495 962 if ((newLockState == LOCKED) && (writeParams->len == (sizeof(uint8_t) + sizeof(Lock_t))) ) {
roywant 0:ed0152b5c495 963 // And sets the new secret lock code if present
roywant 0:ed0152b5c495 964 uint8_t encryptedNewKey[sizeof(Lock_t)];
roywant 0:ed0152b5c495 965 uint8_t newKey[sizeof(Lock_t)];
roywant 0:ed0152b5c495 966 memcpy(encryptedNewKey, (writeParams->data)+1, sizeof(Lock_t));
roywant 0:ed0152b5c495 967 // Decrypt the new key
roywant 0:ed0152b5c495 968 aes128Decrypt(unlockKey, encryptedNewKey, newKey);
roywant 0:ed0152b5c495 969 memcpy(unlockKey, newKey, sizeof(Lock_t));
roywant 0:ed0152b5c495 970 }
roywant 0:ed0152b5c495 971 ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(uint8_t));
roywant 0:ed0152b5c495 972 // CHAR-7 UNLOCK
roywant 0:ed0152b5c495 973 } else if (handle == unlockChar->getValueHandle()) {
roywant 0:ed0152b5c495 974 LOG(("Write: Unlock Handle=%d\r\n", handle));
roywant 0:ed0152b5c495 975 // NOTE: Actual comparison with unlock code is done in:
roywant 0:ed0152b5c495 976 // writeUnlockAuthorizationCallback(...) which is executed before this method call.
roywant 0:ed0152b5c495 977 lockState = UNLOCKED;
roywant 0:ed0152b5c495 978 // Regenerate challenge and expected unlockToken for Next unlock operation
roywant 0:ed0152b5c495 979 generateRandom(challenge, sizeof(Lock_t));
roywant 0:ed0152b5c495 980 aes128Encrypt(unlockKey, challenge, unlockToken);
roywant 0:ed0152b5c495 981 // Update Chars
roywant 0:ed0152b5c495 982 ble.gattServer().write(unlockChar->getValueHandle(), reinterpret_cast<uint8_t *>(challenge), sizeof(Lock_t)); // Update the challenge
roywant 0:ed0152b5c495 983 ble.gattServer().write(lockStateChar->getValueHandle(), reinterpret_cast<uint8_t *>(&lockState), sizeof(uint8_t)); // Update the lock
roywant 0:ed0152b5c495 984 // CHAR-8 PUBLIC ECDH KEY
roywant 0:ed0152b5c495 985 /* PublicEchdChar is READ ONLY */
roywant 0:ed0152b5c495 986 // CHAR-9 EID INDENTITY KEY
roywant 0:ed0152b5c495 987 /* EidIdentityChar is READ ONLY */
roywant 0:ed0152b5c495 988 // CHAR-10 ADV DATA
roywant 0:ed0152b5c495 989 } else if (handle == advSlotDataChar->getValueHandle()) {
roywant 0:ed0152b5c495 990 LOG(("Write: Adv Slot DATA Handle=%d\r\n", handle));
roywant 0:ed0152b5c495 991 uint8_t* frame = slotToFrame(activeSlot);
roywant 0:ed0152b5c495 992 int8_t advTxPower = slotAdvTxPowerLevels[activeSlot];
roywant 0:ed0152b5c495 993 uint8_t writeFrameFormat = *(writeParams->data);
roywant 0:ed0152b5c495 994 uint8_t writeFrameLen = (writeParams->len);
roywant 0:ed0152b5c495 995 uint8_t writeData[34];
roywant 0:ed0152b5c495 996 uint8_t serverPublicEcdhKey[32];
roywant 0:ed0152b5c495 997
roywant 0:ed0152b5c495 998 if (writeFrameLen != 0) {
roywant 0:ed0152b5c495 999 writeFrameLen--; // Remove the Format byte from the count
roywant 0:ed0152b5c495 1000 } else {
roywant 0:ed0152b5c495 1001 writeFrameFormat = UNDEFINED_FRAME_FORMAT; // Undefined format
roywant 0:ed0152b5c495 1002 }
roywant 0:ed0152b5c495 1003
roywant 0:ed0152b5c495 1004 memcpy(writeData, (writeParams->data) + 1, writeFrameLen);
roywant 0:ed0152b5c495 1005 LOG(("ADV Data Write=%d,%d\r\n", writeFrameFormat, writeFrameLen));
roywant 0:ed0152b5c495 1006 switch(writeFrameFormat) {
roywant 0:ed0152b5c495 1007 case UIDFrame::FRAME_TYPE_UID:
roywant 0:ed0152b5c495 1008 if (writeFrameLen == 16) {
roywant 0:ed0152b5c495 1009 uidFrame.setData(frame, advTxPower,reinterpret_cast<const uint8_t *>((writeParams->data) + 1));
roywant 0:ed0152b5c495 1010 slotFrameTypes[activeSlot] = EDDYSTONE_FRAME_UID;
roywant 0:ed0152b5c495 1011 } else if (writeFrameLen == 0) {
roywant 0:ed0152b5c495 1012 uidFrame.clearFrame(frame);
roywant 0:ed0152b5c495 1013 }
roywant 0:ed0152b5c495 1014 break;
roywant 0:ed0152b5c495 1015 case URLFrame::FRAME_TYPE_URL:
roywant 0:ed0152b5c495 1016 if (writeFrameLen <= 18) {
roywant 0:ed0152b5c495 1017 urlFrame.setData(frame, advTxPower, reinterpret_cast<const uint8_t*>((writeParams->data) + 1), writeFrameLen );
roywant 0:ed0152b5c495 1018 slotFrameTypes[activeSlot] = EDDYSTONE_FRAME_URL;
roywant 0:ed0152b5c495 1019 } else if (writeFrameLen == 0) {
roywant 0:ed0152b5c495 1020 urlFrame.clearFrame(frame);
roywant 0:ed0152b5c495 1021 }
roywant 0:ed0152b5c495 1022 break;
roywant 0:ed0152b5c495 1023 case TLMFrame::FRAME_TYPE_TLM:
roywant 0:ed0152b5c495 1024 if (writeFrameLen == 0) {
roywant 0:ed0152b5c495 1025 updateRawTLMFrame(frame);
roywant 0:ed0152b5c495 1026 tlmFrame.setData(frame);
roywant 0:ed0152b5c495 1027 int slot = getEidSlot();
roywant 0:ed0152b5c495 1028 LOG(("WRITE: Testing if TLM or ETLM=%d\r\n", slot));
roywant 0:ed0152b5c495 1029 if (slot != NO_EID_SLOT_SET) {
roywant 0:ed0152b5c495 1030 LOG(("WRITE: Configuring ETLM Slot time=%d\r\n", timeSinceBootTimer.read_ms() / 1000));
roywant 0:ed0152b5c495 1031 tlmFrame.encryptData(frame, slotEidIdentityKeys[slot], slotEidRotationPeriodExps[slot], timeSinceBootTimer.read_ms() / 1000);
roywant 0:ed0152b5c495 1032 }
roywant 0:ed0152b5c495 1033 slotFrameTypes[activeSlot] = EDDYSTONE_FRAME_TLM;
roywant 0:ed0152b5c495 1034 }
roywant 0:ed0152b5c495 1035 break;
roywant 0:ed0152b5c495 1036 case EIDFrame::FRAME_TYPE_EID:
roywant 0:ed0152b5c495 1037 LOG(("EID Len=%d\r\n", writeFrameLen));
roywant 0:ed0152b5c495 1038 if (writeFrameLen == 17) {
roywant 0:ed0152b5c495 1039 // Least secure
roywant 0:ed0152b5c495 1040 LOG(("EID Insecure branch\r\n"));
roywant 0:ed0152b5c495 1041 aes128Decrypt(unlockKey, writeData, slotEidIdentityKeys[activeSlot]);
roywant 0:ed0152b5c495 1042 slotEidRotationPeriodExps[activeSlot] = writeData[16]; // index 16 is the exponent
roywant 0:ed0152b5c495 1043 ble.gattServer().write(eidIdentityKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(&writeData), sizeof(EidIdentityKey_t));
roywant 0:ed0152b5c495 1044 } else if (writeFrameLen == 33 ) {
roywant 0:ed0152b5c495 1045 // Most secure
roywant 0:ed0152b5c495 1046 memcpy(serverPublicEcdhKey, writeData, 32);
roywant 0:ed0152b5c495 1047 ble.gattServer().write(publicEcdhKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(&serverPublicEcdhKey), sizeof(PublicEcdhKey_t));
roywant 0:ed0152b5c495 1048 LOG(("ServerPublicEcdhKey=")); logPrintHex(serverPublicEcdhKey, 32);
roywant 0:ed0152b5c495 1049 slotEidRotationPeriodExps[activeSlot] = writeData[32]; // index 32 is the exponent
roywant 0:ed0152b5c495 1050 LOG(("Exponent=%i\r\n", writeData[32]));
roywant 0:ed0152b5c495 1051 LOG(("genBeaconKeyRC=%x\r\n", genBeaconKeyRC));
roywant 0:ed0152b5c495 1052 LOG(("BeaconPrivateEcdhKey=")); logPrintHex(privateEcdhKey, 32);
roywant 0:ed0152b5c495 1053 LOG(("BeaconPublicEcdhKey=")); logPrintHex(publicEcdhKey, 32);
roywant 0:ed0152b5c495 1054 LOG(("genECDHShareKey\r\n"));
roywant 0:ed0152b5c495 1055 int rc = eidFrame.genEcdhSharedKey(privateEcdhKey, publicEcdhKey, serverPublicEcdhKey, slotEidIdentityKeys[activeSlot]);
roywant 0:ed0152b5c495 1056 LOG(("Gen Keys RC = %x\r\n", rc));
roywant 0:ed0152b5c495 1057 LOG(("Generated eidIdentityKey=")); logPrintHex(slotEidIdentityKeys[activeSlot], 16);
roywant 0:ed0152b5c495 1058 aes128Encrypt(unlockKey, slotEidIdentityKeys[activeSlot], encryptedEidIdentityKey);
roywant 0:ed0152b5c495 1059 LOG(("encryptedEidIdentityKey=")); logPrintHex(encryptedEidIdentityKey, 16);
roywant 0:ed0152b5c495 1060 ble.gattServer().write(eidIdentityKeyChar->getValueHandle(), reinterpret_cast<uint8_t *>(&encryptedEidIdentityKey), sizeof(EidIdentityKey_t));
roywant 0:ed0152b5c495 1061 } else if (writeFrameLen == 0) {
roywant 0:ed0152b5c495 1062 // Reset eidFrame
roywant 0:ed0152b5c495 1063 eidFrame.clearFrame(frame);
roywant 0:ed0152b5c495 1064 break;
roywant 0:ed0152b5c495 1065 } else {
roywant 0:ed0152b5c495 1066 break; // Do nothing, this is not a recognized Frame length
roywant 0:ed0152b5c495 1067 }
roywant 0:ed0152b5c495 1068 // Establish the new frame type
roywant 0:ed0152b5c495 1069 slotFrameTypes[activeSlot] = EDDYSTONE_FRAME_EID;
roywant 0:ed0152b5c495 1070 nextEidSlot = activeSlot; // This was the last one updated
roywant 0:ed0152b5c495 1071 LOG(("update Eid Frame\r\n"));
roywant 0:ed0152b5c495 1072 // Generate ADV frame packet from EidIdentity Key
roywant 0:ed0152b5c495 1073 eidFrame.update(frame, slotEidIdentityKeys[activeSlot], slotEidRotationPeriodExps[activeSlot], timeSinceBootTimer.read_ms() / 1000);
roywant 0:ed0152b5c495 1074 LOG(("END update Eid Frame\r\n"));
roywant 0:ed0152b5c495 1075 break;
roywant 0:ed0152b5c495 1076 default:
roywant 0:ed0152b5c495 1077 frame[0] = 0; // Frame format unknown so clear the entire frame by writing 0 to its length
roywant 0:ed0152b5c495 1078 break;
roywant 0:ed0152b5c495 1079 }
roywant 0:ed0152b5c495 1080 // Read takes care of setting the Characteristic Value
roywant 0:ed0152b5c495 1081 // CHAR-11 FACTORY RESET
roywant 0:ed0152b5c495 1082 } else if (handle == factoryResetChar->getValueHandle() && (*((uint8_t *)writeParams->data) != 0)) {
roywant 0:ed0152b5c495 1083 LOG(("Write: Factory Reset: Handle=%d\r\n", handle));
roywant 0:ed0152b5c495 1084 // Reset params to default values
roywant 0:ed0152b5c495 1085 doFactoryReset();
roywant 0:ed0152b5c495 1086 // Update all characteristics based on params
roywant 0:ed0152b5c495 1087 updateCharacteristicValues();
roywant 0:ed0152b5c495 1088 // CHAR-12 REMAIN CONNECTABLE
roywant 0:ed0152b5c495 1089 } else if (handle == remainConnectableChar->getValueHandle()) {
roywant 0:ed0152b5c495 1090 LOG(("Write: Remain Connectable Handle=%d\r\n", handle));
roywant 0:ed0152b5c495 1091 remainConnectable = *(writeParams->data);
roywant 0:ed0152b5c495 1092 ble.gattServer().write(remainConnectableChar->getValueHandle(), &remainConnectable, sizeof(uint8_t));
roywant 0:ed0152b5c495 1093 }
roywant 0:ed0152b5c495 1094
roywant 0:ed0152b5c495 1095 }
roywant 0:ed0152b5c495 1096
roywant 0:ed0152b5c495 1097 void EddystoneService::setFrameTxPower(uint8_t slot, int8_t advTxPower) {
roywant 0:ed0152b5c495 1098 uint8_t* frame = slotToFrame(slot);
roywant 0:ed0152b5c495 1099 uint8_t frameType = slotFrameTypes[slot];
roywant 0:ed0152b5c495 1100 switch (frameType) {
roywant 0:ed0152b5c495 1101 case UIDFrame::FRAME_TYPE_UID:
roywant 0:ed0152b5c495 1102 uidFrame.setAdvTxPower(frame, advTxPower);
roywant 0:ed0152b5c495 1103 break;
roywant 0:ed0152b5c495 1104 case URLFrame::FRAME_TYPE_URL:
roywant 0:ed0152b5c495 1105 urlFrame.setAdvTxPower(frame, advTxPower);
roywant 0:ed0152b5c495 1106 break;
roywant 0:ed0152b5c495 1107 case EIDFrame::FRAME_TYPE_EID:
roywant 0:ed0152b5c495 1108 eidFrame.setAdvTxPower(frame, advTxPower);
roywant 0:ed0152b5c495 1109 break;
roywant 0:ed0152b5c495 1110 }
roywant 0:ed0152b5c495 1111 }
roywant 0:ed0152b5c495 1112
roywant 0:ed0152b5c495 1113 uint8_t EddystoneService::radioTxPowerToIndex(int8_t txPower) {
roywant 0:ed0152b5c495 1114 // NOTE: txPower is an 8-bit signed number
roywant 0:ed0152b5c495 1115 uint8_t size = sizeof(PowerLevels_t);
roywant 0:ed0152b5c495 1116 // Look for the value in range (or next biggest value)
roywant 0:ed0152b5c495 1117 for (uint8_t i = 0; i < size; i++) {
roywant 0:ed0152b5c495 1118 if (txPower <= radioTxPowerLevels[i]) {
roywant 0:ed0152b5c495 1119 return i;
roywant 0:ed0152b5c495 1120 }
roywant 0:ed0152b5c495 1121 }
roywant 0:ed0152b5c495 1122 return size - 1;
roywant 0:ed0152b5c495 1123 }
roywant 0:ed0152b5c495 1124
roywant 0:ed0152b5c495 1125 /** AES128 encrypts a 16-byte input array with a key, resulting in a 16-byte output array */
roywant 0:ed0152b5c495 1126 void EddystoneService::aes128Encrypt(uint8_t key[], uint8_t input[], uint8_t output[]) {
roywant 0:ed0152b5c495 1127 mbedtls_aes_context ctx;
roywant 0:ed0152b5c495 1128 mbedtls_aes_init(&ctx);
roywant 0:ed0152b5c495 1129 mbedtls_aes_setkey_enc(&ctx, key, 8 * sizeof(Lock_t));
roywant 0:ed0152b5c495 1130 mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, input, output);
roywant 0:ed0152b5c495 1131 mbedtls_aes_free(&ctx);
roywant 0:ed0152b5c495 1132 }
roywant 0:ed0152b5c495 1133
roywant 0:ed0152b5c495 1134 /** AES128 decrypts a 16-byte input array with a key, resulting in a 16-byte output array */
roywant 0:ed0152b5c495 1135 void EddystoneService::aes128Decrypt(uint8_t key[], uint8_t input[], uint8_t output[]) {
roywant 0:ed0152b5c495 1136 mbedtls_aes_context ctx;
roywant 0:ed0152b5c495 1137 mbedtls_aes_init(&ctx);
roywant 0:ed0152b5c495 1138 mbedtls_aes_setkey_dec(&ctx, key, 8 * sizeof(Lock_t));
roywant 0:ed0152b5c495 1139 mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_DECRYPT, input, output);
roywant 0:ed0152b5c495 1140 mbedtls_aes_free(&ctx);
roywant 0:ed0152b5c495 1141 }
roywant 0:ed0152b5c495 1142
roywant 0:ed0152b5c495 1143
roywant 0:ed0152b5c495 1144
roywant 0:ed0152b5c495 1145 #ifdef HARDWARE_RANDOM_NUM_GENERATOR
roywant 0:ed0152b5c495 1146 // Generates a set of random values in byte array[size] based on hardware source
roywant 0:ed0152b5c495 1147 void EddystoneService::generateRandom(uint8_t ain[], int size) {
roywant 0:ed0152b5c495 1148 mbedtls_entropy_context entropy;
roywant 0:ed0152b5c495 1149 mbedtls_entropy_init(&entropy);
roywant 0:ed0152b5c495 1150 // init entropy source
roywant 0:ed0152b5c495 1151 eddystoneRegisterEntropySource(&entropy);
roywant 0:ed0152b5c495 1152 mbedtls_ctr_drbg_context ctr_drbg;
roywant 0:ed0152b5c495 1153 mbedtls_ctr_drbg_init(&ctr_drbg);
roywant 0:ed0152b5c495 1154 mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
roywant 0:ed0152b5c495 1155 mbedtls_ctr_drbg_random(&ctr_drbg, ain, size);
roywant 0:ed0152b5c495 1156 mbedtls_ctr_drbg_free(&ctr_drbg);
roywant 0:ed0152b5c495 1157 mbedtls_entropy_free(&entropy);
roywant 0:ed0152b5c495 1158 return;
roywant 0:ed0152b5c495 1159 }
roywant 0:ed0152b5c495 1160 #else
roywant 0:ed0152b5c495 1161 // Generates a set of random values in byte array[size] seeded by the clock(ms)
roywant 0:ed0152b5c495 1162 void EddystoneService::generateRandom(uint8_t ain[], int size) {
roywant 0:ed0152b5c495 1163 int i;
roywant 0:ed0152b5c495 1164 // Random seed based on boot time in milliseconds
roywant 0:ed0152b5c495 1165 srand(timeSinceBootTimer.read_ms());
roywant 0:ed0152b5c495 1166 for (i = 0; i < size; i++) {
roywant 0:ed0152b5c495 1167 ain[i] = rand() % 256;
roywant 0:ed0152b5c495 1168 }
roywant 0:ed0152b5c495 1169 return;
roywant 0:ed0152b5c495 1170 }
roywant 0:ed0152b5c495 1171 #endif
roywant 0:ed0152b5c495 1172
roywant 0:ed0152b5c495 1173 /*
roywant 0:ed0152b5c495 1174 // ALTERNATE Better Random number generator (but has Memory usage issues)
roywant 0:ed0152b5c495 1175 // Generates a set of random values in byte array[size]
roywant 0:ed0152b5c495 1176
roywant 0:ed0152b5c495 1177 */
roywant 0:ed0152b5c495 1178
roywant 0:ed0152b5c495 1179 /** Reverse Even sized Array endianess: Big to Little or Little to Big */
roywant 0:ed0152b5c495 1180 void EddystoneService::swapEndianArray(uint8_t ptrIn[], uint8_t ptrOut[], int size) {
roywant 0:ed0152b5c495 1181 int i;
roywant 0:ed0152b5c495 1182 for (i = 0; i < size; i++) {
roywant 0:ed0152b5c495 1183 ptrOut[i] = ptrIn[size - i - 1];
roywant 0:ed0152b5c495 1184 }
roywant 0:ed0152b5c495 1185 return;
roywant 0:ed0152b5c495 1186 }
roywant 0:ed0152b5c495 1187
roywant 0:ed0152b5c495 1188 /** Reverse endianess: Big to Little or Little to Big */
roywant 0:ed0152b5c495 1189 uint16_t EddystoneService::swapEndian(uint16_t arg) {
roywant 0:ed0152b5c495 1190 return (arg / 256) + (arg % 256) * 256;
roywant 0:ed0152b5c495 1191 }
roywant 0:ed0152b5c495 1192
roywant 0:ed0152b5c495 1193 uint16_t EddystoneService::correctAdvertisementPeriod(uint16_t beaconPeriodIn) const
roywant 0:ed0152b5c495 1194 {
roywant 0:ed0152b5c495 1195 /* Re-map beaconPeriod to within permissible bounds if necessary. */
roywant 0:ed0152b5c495 1196 if (beaconPeriodIn != 0) {
roywant 0:ed0152b5c495 1197 if (beaconPeriodIn < ble.gap().getMinNonConnectableAdvertisingInterval()) {
roywant 0:ed0152b5c495 1198 return ble.gap().getMinNonConnectableAdvertisingInterval();
roywant 0:ed0152b5c495 1199 } else if (beaconPeriodIn > ble.gap().getMaxAdvertisingInterval()) {
roywant 0:ed0152b5c495 1200 return ble.gap().getMaxAdvertisingInterval();
roywant 0:ed0152b5c495 1201 }
roywant 0:ed0152b5c495 1202 }
roywant 0:ed0152b5c495 1203 return beaconPeriodIn;
roywant 0:ed0152b5c495 1204 }
roywant 0:ed0152b5c495 1205
roywant 0:ed0152b5c495 1206 void EddystoneService::logPrintHex(uint8_t* a, int len) {
roywant 0:ed0152b5c495 1207 for (int i = 0; i < len; i++) {
roywant 0:ed0152b5c495 1208 LOG(("%x%x", a[i] >> 4, a[i] & 0x0f ));
roywant 0:ed0152b5c495 1209 }
roywant 0:ed0152b5c495 1210 LOG(("\r\n"));
roywant 0:ed0152b5c495 1211 }
roywant 0:ed0152b5c495 1212
roywant 0:ed0152b5c495 1213 void EddystoneService::setRandomMacAddress(void) {
roywant 0:ed0152b5c495 1214 #ifdef EID_RANDOM_MAC
roywant 0:ed0152b5c495 1215 uint8_t macAddress[6]; // 48 bit Mac Address
roywant 0:ed0152b5c495 1216 generateRandom(macAddress, 6);
roywant 0:ed0152b5c495 1217 macAddress[5] |= 0xc0; // Ensure upper two bits are 11's for Random Add
roywant 0:ed0152b5c495 1218 ble.setAddress(BLEProtocol::AddressType::RANDOM_STATIC, macAddress);
roywant 0:ed0152b5c495 1219 #endif
roywant 0:ed0152b5c495 1220 }
roywant 0:ed0152b5c495 1221
roywant 0:ed0152b5c495 1222 int EddystoneService::getEidSlot(void) {
roywant 0:ed0152b5c495 1223 int eidSlot = NO_EID_SLOT_SET; // by default;
roywant 0:ed0152b5c495 1224 for (int i = 0; i < MAX_ADV_SLOTS; i++) {
roywant 0:ed0152b5c495 1225 if (slotFrameTypes[nextEidSlot] == EDDYSTONE_FRAME_EID) {
roywant 0:ed0152b5c495 1226 eidSlot = nextEidSlot;
roywant 0:ed0152b5c495 1227 nextEidSlot = (nextEidSlot-1) % MAX_ADV_SLOTS;
roywant 0:ed0152b5c495 1228 break;
roywant 0:ed0152b5c495 1229 }
roywant 0:ed0152b5c495 1230 nextEidSlot = (nextEidSlot-1) % MAX_ADV_SLOTS; // ensure the slot numbers wrap
roywant 0:ed0152b5c495 1231 }
roywant 0:ed0152b5c495 1232 return eidSlot;
roywant 0:ed0152b5c495 1233 }
roywant 0:ed0152b5c495 1234
roywant 0:ed0152b5c495 1235
roywant 0:ed0152b5c495 1236 const uint8_t EddystoneService::slotDefaultUids[MAX_ADV_SLOTS][16] = EDDYSTONE_DEFAULT_SLOT_UIDS;
roywant 0:ed0152b5c495 1237
roywant 0:ed0152b5c495 1238 const uint8_t EddystoneService::slotDefaultEidIdentityKeys[MAX_ADV_SLOTS][16] = EDDYSTONE_DEFAULT_SLOT_EID_IDENTITY_KEYS;
roywant 0:ed0152b5c495 1239
roywant 0:ed0152b5c495 1240 const uint8_t EddystoneService::allSlotsDefaultEid[8] = {0,0,0,0,0,0,0,0};