Rizky Ardi Maulana / mbed-os
Committer:
elessair
Date:
Sun Oct 23 15:10:02 2016 +0000
Revision:
0:f269e3021894
Initial commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
elessair 0:f269e3021894 1 /* mbed Microcontroller Library
elessair 0:f269e3021894 2 * Copyright (c) 2006-2013 ARM Limited
elessair 0:f269e3021894 3 *
elessair 0:f269e3021894 4 * Licensed under the Apache License, Version 2.0 (the "License");
elessair 0:f269e3021894 5 * you may not use this file except in compliance with the License.
elessair 0:f269e3021894 6 * You may obtain a copy of the License at
elessair 0:f269e3021894 7 *
elessair 0:f269e3021894 8 * http://www.apache.org/licenses/LICENSE-2.0
elessair 0:f269e3021894 9 *
elessair 0:f269e3021894 10 * Unless required by applicable law or agreed to in writing, software
elessair 0:f269e3021894 11 * distributed under the License is distributed on an "AS IS" BASIS,
elessair 0:f269e3021894 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
elessair 0:f269e3021894 13 * See the License for the specific language governing permissions and
elessair 0:f269e3021894 14 * limitations under the License.
elessair 0:f269e3021894 15 */
elessair 0:f269e3021894 16
elessair 0:f269e3021894 17 #ifndef SERVICES_EDDYSTONE_BEACON_CONFIG_SERVICE_H_
elessair 0:f269e3021894 18 #define SERVICES_EDDYSTONE_BEACON_CONFIG_SERVICE_H_
elessair 0:f269e3021894 19
elessair 0:f269e3021894 20 #warning ble/services/EddystoneConfigService.h is deprecated. Please use the example in 'github.com/ARMmbed/ble-examples/tree/master/BLE_EddystoneService'.
elessair 0:f269e3021894 21
elessair 0:f269e3021894 22 #include "mbed.h"
elessair 0:f269e3021894 23 #include "ble/BLE.h"
elessair 0:f269e3021894 24 #include "ble/services/EddystoneService.h"
elessair 0:f269e3021894 25
elessair 0:f269e3021894 26 #define UUID_URI_BEACON(FIRST, SECOND) { \
elessair 0:f269e3021894 27 0xee, 0x0c, FIRST, SECOND, 0x87, 0x86, 0x40, 0xba, \
elessair 0:f269e3021894 28 0xab, 0x96, 0x99, 0xb9, 0x1a, 0xc9, 0x81, 0xd8, \
elessair 0:f269e3021894 29 }
elessair 0:f269e3021894 30
elessair 0:f269e3021894 31 static const uint8_t UUID_URI_BEACON_SERVICE[] = UUID_URI_BEACON(0x20, 0x80);
elessair 0:f269e3021894 32 static const uint8_t UUID_LOCK_STATE_CHAR[] = UUID_URI_BEACON(0x20, 0x81);
elessair 0:f269e3021894 33 static const uint8_t UUID_LOCK_CHAR[] = UUID_URI_BEACON(0x20, 0x82);
elessair 0:f269e3021894 34 static const uint8_t UUID_UNLOCK_CHAR[] = UUID_URI_BEACON(0x20, 0x83);
elessair 0:f269e3021894 35 static const uint8_t UUID_URI_DATA_CHAR[] = UUID_URI_BEACON(0x20, 0x84);
elessair 0:f269e3021894 36 static const uint8_t UUID_FLAGS_CHAR[] = UUID_URI_BEACON(0x20, 0x85);
elessair 0:f269e3021894 37 static const uint8_t UUID_ADV_POWER_LEVELS_CHAR[] = UUID_URI_BEACON(0x20, 0x86);
elessair 0:f269e3021894 38 static const uint8_t UUID_TX_POWER_MODE_CHAR[] = UUID_URI_BEACON(0x20, 0x87);
elessair 0:f269e3021894 39 static const uint8_t UUID_BEACON_PERIOD_CHAR[] = UUID_URI_BEACON(0x20, 0x88);
elessair 0:f269e3021894 40 static const uint8_t UUID_RESET_CHAR[] = UUID_URI_BEACON(0x20, 0x89);
elessair 0:f269e3021894 41 extern const uint8_t BEACON_EDDYSTONE[2];
elessair 0:f269e3021894 42
elessair 0:f269e3021894 43 /**
elessair 0:f269e3021894 44 * @class EddystoneConfigService
elessair 0:f269e3021894 45 * @brief Eddystone Configuration Service. Used to set URL, adjust power levels, and set flags.
elessair 0:f269e3021894 46 * See https://github.com/google/eddystone
elessair 0:f269e3021894 47 *
elessair 0:f269e3021894 48 */
elessair 0:f269e3021894 49 class EddystoneConfigService
elessair 0:f269e3021894 50 {
elessair 0:f269e3021894 51 public:
elessair 0:f269e3021894 52 /**
elessair 0:f269e3021894 53 * @brief Transmission Power Modes for UriBeacon
elessair 0:f269e3021894 54 */
elessair 0:f269e3021894 55 enum {
elessair 0:f269e3021894 56 TX_POWER_MODE_LOWEST,
elessair 0:f269e3021894 57 TX_POWER_MODE_LOW,
elessair 0:f269e3021894 58 TX_POWER_MODE_MEDIUM,
elessair 0:f269e3021894 59 TX_POWER_MODE_HIGH,
elessair 0:f269e3021894 60 NUM_POWER_MODES
elessair 0:f269e3021894 61 };
elessair 0:f269e3021894 62
elessair 0:f269e3021894 63 static const unsigned ADVERTISING_INTERVAL_MSEC = 1000; // Advertising interval for config service.
elessair 0:f269e3021894 64 static const unsigned SERVICE_DATA_MAX = 31; // Maximum size of service data in ADV packets.
elessair 0:f269e3021894 65
elessair 0:f269e3021894 66 typedef uint8_t Lock_t[16]; /* 128 bits. */
elessair 0:f269e3021894 67 typedef int8_t PowerLevels_t[NUM_POWER_MODES];
elessair 0:f269e3021894 68
elessair 0:f269e3021894 69 // There are currently three subframes defined: URI, UID, and TLM.
elessair 0:f269e3021894 70 #define EDDYSTONE_MAX_FRAMETYPE 3
elessair 0:f269e3021894 71 static const unsigned URI_DATA_MAX = 18;
elessair 0:f269e3021894 72 typedef uint8_t UriData_t[URI_DATA_MAX];
elessair 0:f269e3021894 73
elessair 0:f269e3021894 74 // UID Frame Type subfields.
elessair 0:f269e3021894 75 static const size_t UID_NAMESPACEID_SIZE = 10;
elessair 0:f269e3021894 76 typedef uint8_t UIDNamespaceID_t[UID_NAMESPACEID_SIZE];
elessair 0:f269e3021894 77 static const size_t UID_INSTANCEID_SIZE = 6;
elessair 0:f269e3021894 78 typedef uint8_t UIDInstanceID_t[UID_INSTANCEID_SIZE];
elessair 0:f269e3021894 79
elessair 0:f269e3021894 80 // Eddystone Frame Type ID.
elessair 0:f269e3021894 81 static const uint8_t FRAME_TYPE_UID = 0x00;
elessair 0:f269e3021894 82 static const uint8_t FRAME_TYPE_URL = 0x10;
elessair 0:f269e3021894 83 static const uint8_t FRAME_TYPE_TLM = 0x20;
elessair 0:f269e3021894 84
elessair 0:f269e3021894 85 static const uint8_t FRAME_SIZE_TLM = 14; // TLM frame is a constant 14B.
elessair 0:f269e3021894 86 static const uint8_t FRAME_SIZE_UID = 20; // includes RFU bytes.
elessair 0:f269e3021894 87
elessair 0:f269e3021894 88 struct Params_t {
elessair 0:f269e3021894 89 // Config Data
elessair 0:f269e3021894 90 bool isConfigured; // Flag for configuration being complete:
elessair 0:f269e3021894 91 // True = configured, False = not configured. Reset at instantiation, used for external callbacks.
elessair 0:f269e3021894 92 uint8_t lockedState;
elessair 0:f269e3021894 93 Lock_t lock;
elessair 0:f269e3021894 94 uint8_t flags;
elessair 0:f269e3021894 95 PowerLevels_t advPowerLevels; // Current value of AdvertisedPowerLevels.
elessair 0:f269e3021894 96 uint8_t txPowerMode; // Firmware power levels used with setTxPower().
elessair 0:f269e3021894 97 uint16_t beaconPeriod;
elessair 0:f269e3021894 98 // TLM Frame Data
elessair 0:f269e3021894 99 uint8_t tlmVersion; // Version of TLM packet.
elessair 0:f269e3021894 100 bool tlmEnabled;
elessair 0:f269e3021894 101 float tlmBeaconPeriod; // How often to broadcat TLM frame, in seconds.
elessair 0:f269e3021894 102 // URI Frame Data
elessair 0:f269e3021894 103 uint8_t uriDataLength;
elessair 0:f269e3021894 104 UriData_t uriData;
elessair 0:f269e3021894 105 bool uriEnabled;
elessair 0:f269e3021894 106 float uriBeaconPeriod; // How often to broadcast URIFrame, in seconds.
elessair 0:f269e3021894 107 // UID Frame Data
elessair 0:f269e3021894 108 UIDNamespaceID_t uidNamespaceID; // UUID type, Namespace ID, 10B.
elessair 0:f269e3021894 109 UIDInstanceID_t uidInstanceID; // UUID type, Instance ID, 6B.
elessair 0:f269e3021894 110 bool uidEnabled;
elessair 0:f269e3021894 111 float uidBeaconPeriod; // How often to broadcast UID Frame, in seconds.
elessair 0:f269e3021894 112 };
elessair 0:f269e3021894 113
elessair 0:f269e3021894 114 /**
elessair 0:f269e3021894 115 * @param[ref] ble
elessair 0:f269e3021894 116 * BLEDevice object for the underlying controller.
elessair 0:f269e3021894 117 * @param[in/out] paramsIn
elessair 0:f269e3021894 118 * Reference to application-visible beacon state, loaded
elessair 0:f269e3021894 119 * from persistent storage at startup.
elessair 0:f269e3021894 120 * @param[in] defaultAdvPowerLevelsIn
elessair 0:f269e3021894 121 * Default power-levels array; applies only if resetToDefaultsFlag is true.
elessair 0:f269e3021894 122 */
elessair 0:f269e3021894 123 EddystoneConfigService(BLEDevice &bleIn,
elessair 0:f269e3021894 124 Params_t &paramsIn,
elessair 0:f269e3021894 125 PowerLevels_t &defaultAdvPowerLevelsIn,
elessair 0:f269e3021894 126 PowerLevels_t &radioPowerLevelsIn) :
elessair 0:f269e3021894 127 ble(bleIn),
elessair 0:f269e3021894 128 params(paramsIn), // Initialize URL data.
elessair 0:f269e3021894 129 defaultAdvPowerLevels(defaultAdvPowerLevelsIn),
elessair 0:f269e3021894 130 radioPowerLevels(radioPowerLevelsIn),
elessair 0:f269e3021894 131 initSucceeded(false),
elessair 0:f269e3021894 132 resetFlag(),
elessair 0:f269e3021894 133 defaultUidNamespaceID(), // Initialize UID data.
elessair 0:f269e3021894 134 defaultUidInstanceID(),
elessair 0:f269e3021894 135 defaultUidPower(defaultAdvPowerLevelsIn[params.txPowerMode]),
elessair 0:f269e3021894 136 uidIsSet(false),
elessair 0:f269e3021894 137 defaultUriDataLength(),
elessair 0:f269e3021894 138 defaultUriData(),
elessair 0:f269e3021894 139 defaultUrlPower(defaultAdvPowerLevelsIn[params.txPowerMode]),
elessair 0:f269e3021894 140 urlIsSet(false),
elessair 0:f269e3021894 141 tlmIsSet(false),
elessair 0:f269e3021894 142 lockedStateChar(UUID_LOCK_STATE_CHAR, &params.lockedState),
elessair 0:f269e3021894 143 lockChar(UUID_LOCK_CHAR, &params.lock),
elessair 0:f269e3021894 144 uriDataChar(UUID_URI_DATA_CHAR, params.uriData, 0, URI_DATA_MAX,
elessair 0:f269e3021894 145 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE),
elessair 0:f269e3021894 146 unlockChar(UUID_UNLOCK_CHAR, &params.lock),
elessair 0:f269e3021894 147 flagsChar(UUID_FLAGS_CHAR, &params.flags),
elessair 0:f269e3021894 148 advPowerLevelsChar(UUID_ADV_POWER_LEVELS_CHAR, &params.advPowerLevels),
elessair 0:f269e3021894 149 txPowerModeChar(UUID_TX_POWER_MODE_CHAR, &params.txPowerMode),
elessair 0:f269e3021894 150 beaconPeriodChar(UUID_BEACON_PERIOD_CHAR, &params.beaconPeriod),
elessair 0:f269e3021894 151 resetChar(UUID_RESET_CHAR, &resetFlag) {
elessair 0:f269e3021894 152 // Set Eddystone as not configured yet. Used to exit config before timeout if GATT services are written to.
elessair 0:f269e3021894 153 params.isConfigured = false;
elessair 0:f269e3021894 154
elessair 0:f269e3021894 155 lockChar.setWriteAuthorizationCallback(this, &EddystoneConfigService::lockAuthorizationCallback);
elessair 0:f269e3021894 156 unlockChar.setWriteAuthorizationCallback(this, &EddystoneConfigService::unlockAuthorizationCallback);
elessair 0:f269e3021894 157 uriDataChar.setWriteAuthorizationCallback(this, &EddystoneConfigService::uriDataWriteAuthorizationCallback);
elessair 0:f269e3021894 158 flagsChar.setWriteAuthorizationCallback(this, &EddystoneConfigService::basicAuthorizationCallback<uint8_t>);
elessair 0:f269e3021894 159 advPowerLevelsChar.setWriteAuthorizationCallback(this, &EddystoneConfigService::basicAuthorizationCallback<PowerLevels_t>);
elessair 0:f269e3021894 160 txPowerModeChar.setWriteAuthorizationCallback(this, &EddystoneConfigService::powerModeAuthorizationCallback);
elessair 0:f269e3021894 161 beaconPeriodChar.setWriteAuthorizationCallback(this, &EddystoneConfigService::basicAuthorizationCallback<uint16_t>);
elessair 0:f269e3021894 162 resetChar.setWriteAuthorizationCallback(this, &EddystoneConfigService::basicAuthorizationCallback<uint8_t>);
elessair 0:f269e3021894 163
elessair 0:f269e3021894 164 static GattCharacteristic *charTable[] = {
elessair 0:f269e3021894 165 &lockedStateChar, &lockChar, &unlockChar, &uriDataChar,
elessair 0:f269e3021894 166 &flagsChar, &advPowerLevelsChar, &txPowerModeChar, &beaconPeriodChar, &resetChar
elessair 0:f269e3021894 167 };
elessair 0:f269e3021894 168
elessair 0:f269e3021894 169 GattService configService(UUID_URI_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
elessair 0:f269e3021894 170
elessair 0:f269e3021894 171 ble.addService(configService);
elessair 0:f269e3021894 172 ble.onDataWritten(this, &EddystoneConfigService::onDataWrittenCallback);
elessair 0:f269e3021894 173 }
elessair 0:f269e3021894 174
elessair 0:f269e3021894 175 /**
elessair 0:f269e3021894 176 * @brief Start EddystoneConfig advertising. This function should be called
elessair 0:f269e3021894 177 * after the EddystoneConfig constructor and after all the frames have been added.
elessair 0:f269e3021894 178 *
elessair 0:f269e3021894 179 * @paramsP[in] resetToDefaultsFlag
elessair 0:f269e3021894 180 * Applies to the state of the 'paramsIn' parameter.
elessair 0:f269e3021894 181 * If true, it indicates that paramsIn is potentially
elessair 0:f269e3021894 182 * un-initialized, and default values should be used
elessair 0:f269e3021894 183 * instead. Otherwise, paramsIn overrides the defaults.
elessair 0:f269e3021894 184 */
elessair 0:f269e3021894 185 void start(bool resetToDefaultsFlag){
elessair 0:f269e3021894 186 INFO("reset to defaults flag = %d", resetToDefaultsFlag);
elessair 0:f269e3021894 187 if (!resetToDefaultsFlag && (params.uriDataLength > URI_DATA_MAX)) {
elessair 0:f269e3021894 188 INFO("Reset to Defaults triggered");
elessair 0:f269e3021894 189 resetToDefaultsFlag = true;
elessair 0:f269e3021894 190 }
elessair 0:f269e3021894 191
elessair 0:f269e3021894 192 if (resetToDefaultsFlag) {
elessair 0:f269e3021894 193 resetToDefaults();
elessair 0:f269e3021894 194 } else {
elessair 0:f269e3021894 195 updateCharacteristicValues();
elessair 0:f269e3021894 196 }
elessair 0:f269e3021894 197
elessair 0:f269e3021894 198 setupEddystoneConfigAdvertisements(); /* Set up advertising for the config service. */
elessair 0:f269e3021894 199 initSucceeded = true;
elessair 0:f269e3021894 200 }
elessair 0:f269e3021894 201
elessair 0:f269e3021894 202 /*
elessair 0:f269e3021894 203 * Check if Eddystone initialized successfully.
elessair 0:f269e3021894 204 */
elessair 0:f269e3021894 205 bool initSuccessfully(void) const {
elessair 0:f269e3021894 206 return initSucceeded;
elessair 0:f269e3021894 207 }
elessair 0:f269e3021894 208
elessair 0:f269e3021894 209 /*
elessair 0:f269e3021894 210 * @brief Function to update the default values for the TLM frame. Only applied if Reset Defaults is applied.
elessair 0:f269e3021894 211 *
elessair 0:f269e3021894 212 * @param[in] tlmVersionIn Version of the TLM frame being used.
elessair 0:f269e3021894 213 * @param[in] advPeriodInMin How long between TLM frames being advertised, measured in minutes.
elessair 0:f269e3021894 214 *
elessair 0:f269e3021894 215 */
elessair 0:f269e3021894 216 void setDefaultTLMFrameData(uint8_t tlmVersionIn = 0, float advPeriodInSec = 60){
elessair 0:f269e3021894 217 DBG("Setting Default TLM Data, version = %d, advPeriodInMind= %f", tlmVersionIn, advPeriodInSec);
elessair 0:f269e3021894 218 defaultTlmVersion = tlmVersionIn;
elessair 0:f269e3021894 219 TlmBatteryVoltage = 0;
elessair 0:f269e3021894 220 TlmBeaconTemp = 0x8000;
elessair 0:f269e3021894 221 TlmPduCount = 0;
elessair 0:f269e3021894 222 TlmTimeSinceBoot = 0;
elessair 0:f269e3021894 223 defaultTlmAdvPeriod = advPeriodInSec;
elessair 0:f269e3021894 224 tlmIsSet = true; // Flag to add this to Eddystone service when config is done.
elessair 0:f269e3021894 225 }
elessair 0:f269e3021894 226
elessair 0:f269e3021894 227 /*
elessair 0:f269e3021894 228 * @brief Function to update the default values for the URI frame. Only applied if Reset Defaults is applied.
elessair 0:f269e3021894 229 *
elessair 0:f269e3021894 230 * @param[in] uriIn URL to advertise.
elessair 0:f269e3021894 231 * @param[in] advPeriod How long to advertise the URL, measured in number of ADV frames.
elessair 0:f269e3021894 232 *
elessair 0:f269e3021894 233 */
elessair 0:f269e3021894 234 void setDefaultURIFrameData(const char *uriIn, float advPeriod = 1){
elessair 0:f269e3021894 235 DBG("Setting Default URI Data");
elessair 0:f269e3021894 236 // Set URL Frame
elessair 0:f269e3021894 237 EddystoneService::encodeURL(uriIn, defaultUriData, defaultUriDataLength); // Encode URL to URL Formatting.
elessair 0:f269e3021894 238 if (defaultUriDataLength > URI_DATA_MAX) {
elessair 0:f269e3021894 239 return;
elessair 0:f269e3021894 240 }
elessair 0:f269e3021894 241 INFO("\t URI input = %s : %d", uriIn, defaultUriDataLength);
elessair 0:f269e3021894 242 INFO("\t default URI = %s : %d ", defaultUriData, defaultUriDataLength );
elessair 0:f269e3021894 243 defaultUriAdvPeriod = advPeriod;
elessair 0:f269e3021894 244 urlIsSet = true; // Flag to add this to Eddystone service when config is done.
elessair 0:f269e3021894 245 }
elessair 0:f269e3021894 246
elessair 0:f269e3021894 247 /*
elessair 0:f269e3021894 248 * @brief Function to update the default values for the UID frame. Only applied if Reset Defaults is applied.
elessair 0:f269e3021894 249 *
elessair 0:f269e3021894 250 * @param[in] namespaceID 10Byte Namespace ID.
elessair 0:f269e3021894 251 * @param[in] instanceID 6Byte Instance ID.
elessair 0:f269e3021894 252 * @param[in] advPeriod How long to advertise the URL, measured in the number of ADV frames.
elessair 0:f269e3021894 253 *
elessair 0:f269e3021894 254 */
elessair 0:f269e3021894 255 void setDefaultUIDFrameData(UIDNamespaceID_t *namespaceID, UIDInstanceID_t *instanceID, float advPeriod = 10){
elessair 0:f269e3021894 256 //Set UID frame
elessair 0:f269e3021894 257 DBG("Setting default UID Data");
elessair 0:f269e3021894 258 memcpy(defaultUidNamespaceID, namespaceID, UID_NAMESPACEID_SIZE);
elessair 0:f269e3021894 259 memcpy(defaultUidInstanceID, instanceID, UID_INSTANCEID_SIZE);
elessair 0:f269e3021894 260 defaultUidAdvPeriod = advPeriod;
elessair 0:f269e3021894 261 uidIsSet = true; // Flag to add this to Eddystone service when config is done.
elessair 0:f269e3021894 262 }
elessair 0:f269e3021894 263
elessair 0:f269e3021894 264 /* Start out by advertising the config service for a limited time after
elessair 0:f269e3021894 265 * startup, then switch to the normal non-connectible beacon functionality.
elessair 0:f269e3021894 266 */
elessair 0:f269e3021894 267 void setupEddystoneConfigAdvertisements() {
elessair 0:f269e3021894 268 const char DEVICE_NAME[] = "eddystone Config";
elessair 0:f269e3021894 269
elessair 0:f269e3021894 270 ble.clearAdvertisingPayload();
elessair 0:f269e3021894 271
elessair 0:f269e3021894 272 ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
elessair 0:f269e3021894 273
elessair 0:f269e3021894 274 // UUID is in a different order in the ADV frame (!)
elessair 0:f269e3021894 275 uint8_t reversedServiceUUID[sizeof(UUID_URI_BEACON_SERVICE)];
elessair 0:f269e3021894 276 for (unsigned int i = 0; i < sizeof(UUID_URI_BEACON_SERVICE); i++) {
elessair 0:f269e3021894 277 reversedServiceUUID[i] = UUID_URI_BEACON_SERVICE[sizeof(UUID_URI_BEACON_SERVICE) - i - 1];
elessair 0:f269e3021894 278 }
elessair 0:f269e3021894 279 ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, reversedServiceUUID, sizeof(reversedServiceUUID));
elessair 0:f269e3021894 280 ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG);
elessair 0:f269e3021894 281 ble.accumulateScanResponse(GapAdvertisingData::COMPLETE_LOCAL_NAME, reinterpret_cast<const uint8_t *>(&DEVICE_NAME), sizeof(DEVICE_NAME));
elessair 0:f269e3021894 282 ble.accumulateScanResponse(
elessair 0:f269e3021894 283 GapAdvertisingData::TX_POWER_LEVEL,
elessair 0:f269e3021894 284 reinterpret_cast<uint8_t *>(&defaultAdvPowerLevels[EddystoneConfigService::TX_POWER_MODE_LOW]),
elessair 0:f269e3021894 285 sizeof(uint8_t));
elessair 0:f269e3021894 286
elessair 0:f269e3021894 287 ble.setTxPower(radioPowerLevels[params.txPowerMode]);
elessair 0:f269e3021894 288 ble.setDeviceName(reinterpret_cast<const uint8_t *>(&DEVICE_NAME));
elessair 0:f269e3021894 289 ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
elessair 0:f269e3021894 290 ble.setAdvertisingInterval(ADVERTISING_INTERVAL_MSEC);
elessair 0:f269e3021894 291 }
elessair 0:f269e3021894 292
elessair 0:f269e3021894 293 /*
elessair 0:f269e3021894 294 * This function actually impliments the Eddystone Beacon service. It can be called with the help of the wrapper function
elessair 0:f269e3021894 295 * to load saved config params, or it can be called explicitly to reset the Eddystone beacon to hardcoded values on each reset.
elessair 0:f269e3021894 296 *
elessair 0:f269e3021894 297 */
elessair 0:f269e3021894 298 void setupEddystoneAdvertisements() {
elessair 0:f269e3021894 299 DBG("Switching Config -> adv");
elessair 0:f269e3021894 300 // Save params to storage.
elessair 0:f269e3021894 301 extern void saveURIBeaconConfigParams(const Params_t *paramsP); /* forward declaration; necessary to avoid a circular dependency. */
elessair 0:f269e3021894 302 saveURIBeaconConfigParams(&params);
elessair 0:f269e3021894 303 INFO("Saved Params to Memory.")
elessair 0:f269e3021894 304 // Set up Eddystone Service.
elessair 0:f269e3021894 305 static EddystoneService eddyServ(ble, params.beaconPeriod, radioPowerLevels[params.txPowerMode]);
elessair 0:f269e3021894 306 // Set configured frames (TLM, UID, URI and so on).
elessair 0:f269e3021894 307 if (params.tlmEnabled) {
elessair 0:f269e3021894 308 eddyServ.setTLMFrameData(params.tlmVersion, params.tlmBeaconPeriod);
elessair 0:f269e3021894 309 }
elessair 0:f269e3021894 310 if (params.uriEnabled) {
elessair 0:f269e3021894 311 eddyServ.setURLFrameEncodedData(params.advPowerLevels[params.txPowerMode], (const char *) params.uriData, params.uriDataLength, params.uriBeaconPeriod);
elessair 0:f269e3021894 312 }
elessair 0:f269e3021894 313 if (params.uidEnabled) {
elessair 0:f269e3021894 314 eddyServ.setUIDFrameData(params.advPowerLevels[params.txPowerMode],
elessair 0:f269e3021894 315 (uint8_t *)params.uidNamespaceID,
elessair 0:f269e3021894 316 (uint8_t *)params.uidInstanceID,
elessair 0:f269e3021894 317 params.uidBeaconPeriod);
elessair 0:f269e3021894 318 }
elessair 0:f269e3021894 319 // Start advertising the Eddystone service.
elessair 0:f269e3021894 320 eddyServ.start();
elessair 0:f269e3021894 321 }
elessair 0:f269e3021894 322
elessair 0:f269e3021894 323 private:
elessair 0:f269e3021894 324 /*
elessair 0:f269e3021894 325 * This callback is invoked when a GATT client attempts to modify any of the
elessair 0:f269e3021894 326 * characteristics of this service. Attempts to do so are also applied to
elessair 0:f269e3021894 327 * the internal state of this service object.
elessair 0:f269e3021894 328 */
elessair 0:f269e3021894 329 void onDataWrittenCallback(const GattWriteCallbackParams *writeParams) {
elessair 0:f269e3021894 330 uint16_t handle = writeParams->handle;
elessair 0:f269e3021894 331
elessair 0:f269e3021894 332 if (handle == lockChar.getValueHandle()) {
elessair 0:f269e3021894 333 // Validated earlier.
elessair 0:f269e3021894 334 memcpy(params.lock, writeParams->data, sizeof(Lock_t));
elessair 0:f269e3021894 335 // Set the state to be locked by the lock code (note: zeros are a valid lock).
elessair 0:f269e3021894 336 params.lockedState = true;
elessair 0:f269e3021894 337 INFO("Device Locked");
elessair 0:f269e3021894 338 } else if (handle == unlockChar.getValueHandle()) {
elessair 0:f269e3021894 339 // Validated earlier.
elessair 0:f269e3021894 340 params.lockedState = false;
elessair 0:f269e3021894 341 INFO("Device Unlocked");
elessair 0:f269e3021894 342 } else if (handle == uriDataChar.getValueHandle()) {
elessair 0:f269e3021894 343 params.uriDataLength = writeParams->len;
elessair 0:f269e3021894 344 memset(params.uriData, 0x00, URI_DATA_MAX); // Clear URI string.
elessair 0:f269e3021894 345 memcpy(params.uriData, writeParams->data, writeParams->len); // Set URI string.
elessair 0:f269e3021894 346 params.uriEnabled = true;
elessair 0:f269e3021894 347 INFO("URI = %s, URILen = %d", writeParams->data, writeParams->len);
elessair 0:f269e3021894 348 } else if (handle == flagsChar.getValueHandle()) {
elessair 0:f269e3021894 349 params.flags = *(writeParams->data);
elessair 0:f269e3021894 350 INFO("flagsChar = 0x%x", params.flags);
elessair 0:f269e3021894 351 } else if (handle == advPowerLevelsChar.getValueHandle()) {
elessair 0:f269e3021894 352 memcpy(params.advPowerLevels, writeParams->data, sizeof(PowerLevels_t));
elessair 0:f269e3021894 353 INFO("PowerLevelsChar = %4x", params.advPowerLevels);
elessair 0:f269e3021894 354 } else if (handle == txPowerModeChar.getValueHandle()) {
elessair 0:f269e3021894 355 params.txPowerMode = *(writeParams->data);
elessair 0:f269e3021894 356 INFO("TxPowerModeChar = %d", params.txPowerMode);
elessair 0:f269e3021894 357 } else if (handle == beaconPeriodChar.getValueHandle()) {
elessair 0:f269e3021894 358 params.beaconPeriod = *((uint16_t *)(writeParams->data));
elessair 0:f269e3021894 359 INFO("BeaconPeriod = %d", params.beaconPeriod);
elessair 0:f269e3021894 360
elessair 0:f269e3021894 361 /* Re-map beaconPeriod to within permissible bounds if necessary. */
elessair 0:f269e3021894 362 if (params.beaconPeriod != 0) {
elessair 0:f269e3021894 363 bool paramsUpdated = false;
elessair 0:f269e3021894 364 if (params.beaconPeriod < ble.getMinAdvertisingInterval()) {
elessair 0:f269e3021894 365 params.beaconPeriod = ble.getMinAdvertisingInterval();
elessair 0:f269e3021894 366 paramsUpdated = true;
elessair 0:f269e3021894 367 } else if (params.beaconPeriod > ble.getMaxAdvertisingInterval()) {
elessair 0:f269e3021894 368 params.beaconPeriod = ble.getMaxAdvertisingInterval();
elessair 0:f269e3021894 369 paramsUpdated = true;
elessair 0:f269e3021894 370 }
elessair 0:f269e3021894 371 if (paramsUpdated) {
elessair 0:f269e3021894 372 ble.updateCharacteristicValue(beaconPeriodChar.getValueHandle(), reinterpret_cast<uint8_t *>(&params.beaconPeriod), sizeof(uint16_t));
elessair 0:f269e3021894 373 }
elessair 0:f269e3021894 374 }
elessair 0:f269e3021894 375 } else if (handle == resetChar.getValueHandle()) {
elessair 0:f269e3021894 376 INFO("Reset triggered from Config Service, resetting to defaults");
elessair 0:f269e3021894 377 resetToDefaults();
elessair 0:f269e3021894 378 }
elessair 0:f269e3021894 379 updateCharacteristicValues();
elessair 0:f269e3021894 380 params.isConfigured = true; // Some configuration data has been passed; on disconnect switch to advertising mode.
elessair 0:f269e3021894 381 }
elessair 0:f269e3021894 382
elessair 0:f269e3021894 383 /*
elessair 0:f269e3021894 384 * Reset the default values.
elessair 0:f269e3021894 385 */
elessair 0:f269e3021894 386 void resetToDefaults(void) {
elessair 0:f269e3021894 387 INFO("Resetting to defaults");
elessair 0:f269e3021894 388 // General.
elessair 0:f269e3021894 389 params.lockedState = false;
elessair 0:f269e3021894 390 memset(params.lock, 0, sizeof(Lock_t));
elessair 0:f269e3021894 391 params.flags = 0x10;
elessair 0:f269e3021894 392 memcpy(params.advPowerLevels, defaultAdvPowerLevels, sizeof(PowerLevels_t));
elessair 0:f269e3021894 393 params.txPowerMode = TX_POWER_MODE_LOW;
elessair 0:f269e3021894 394 params.beaconPeriod = (uint16_t) defaultUriAdvPeriod * 1000;
elessair 0:f269e3021894 395
elessair 0:f269e3021894 396 // TLM Frame.
elessair 0:f269e3021894 397 params.tlmVersion = defaultTlmVersion;
elessair 0:f269e3021894 398 params.tlmBeaconPeriod = defaultTlmAdvPeriod;
elessair 0:f269e3021894 399 params.tlmEnabled = tlmIsSet;
elessair 0:f269e3021894 400
elessair 0:f269e3021894 401 // URL Frame.
elessair 0:f269e3021894 402 memcpy(params.uriData, defaultUriData, URI_DATA_MAX);
elessair 0:f269e3021894 403 params.uriDataLength = defaultUriDataLength;
elessair 0:f269e3021894 404 params.uriBeaconPeriod = defaultUriAdvPeriod;
elessair 0:f269e3021894 405 params.uriEnabled = urlIsSet;
elessair 0:f269e3021894 406
elessair 0:f269e3021894 407 // UID Frame.
elessair 0:f269e3021894 408 memcpy(params.uidNamespaceID, defaultUidNamespaceID, UID_NAMESPACEID_SIZE);
elessair 0:f269e3021894 409 memcpy(params.uidInstanceID, defaultUidInstanceID, UID_INSTANCEID_SIZE);
elessair 0:f269e3021894 410 params.uidBeaconPeriod = defaultUidAdvPeriod;
elessair 0:f269e3021894 411 params.uidEnabled = uidIsSet;
elessair 0:f269e3021894 412
elessair 0:f269e3021894 413 updateCharacteristicValues();
elessair 0:f269e3021894 414 }
elessair 0:f269e3021894 415
elessair 0:f269e3021894 416 /*
elessair 0:f269e3021894 417 * Internal helper function used to update the GATT database following any
elessair 0:f269e3021894 418 * change to the internal state of the service object.
elessair 0:f269e3021894 419 */
elessair 0:f269e3021894 420 void updateCharacteristicValues(void) {
elessair 0:f269e3021894 421 ble.updateCharacteristicValue(lockedStateChar.getValueHandle(), &params.lockedState, 1);
elessair 0:f269e3021894 422 ble.updateCharacteristicValue(uriDataChar.getValueHandle(), params.uriData, params.uriDataLength);
elessair 0:f269e3021894 423 ble.updateCharacteristicValue(flagsChar.getValueHandle(), &params.flags, 1);
elessair 0:f269e3021894 424 ble.updateCharacteristicValue(beaconPeriodChar.getValueHandle(),
elessair 0:f269e3021894 425 reinterpret_cast<uint8_t *>(&params.beaconPeriod), sizeof(uint16_t));
elessair 0:f269e3021894 426 ble.updateCharacteristicValue(txPowerModeChar.getValueHandle(), &params.txPowerMode, 1);
elessair 0:f269e3021894 427 ble.updateCharacteristicValue(advPowerLevelsChar.getValueHandle(),
elessair 0:f269e3021894 428 reinterpret_cast<uint8_t *>(params.advPowerLevels), sizeof(PowerLevels_t));
elessair 0:f269e3021894 429 }
elessair 0:f269e3021894 430
elessair 0:f269e3021894 431 private:
elessair 0:f269e3021894 432 void lockAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
elessair 0:f269e3021894 433 if (params.lockedState) {
elessair 0:f269e3021894 434 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
elessair 0:f269e3021894 435 } else if (authParams->len != sizeof(Lock_t)) {
elessair 0:f269e3021894 436 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
elessair 0:f269e3021894 437 } else if (authParams->offset != 0) {
elessair 0:f269e3021894 438 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
elessair 0:f269e3021894 439 } else {
elessair 0:f269e3021894 440 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
elessair 0:f269e3021894 441 }
elessair 0:f269e3021894 442 }
elessair 0:f269e3021894 443
elessair 0:f269e3021894 444 void unlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
elessair 0:f269e3021894 445 if ((!params.lockedState) && (authParams->len == sizeof(Lock_t))) {
elessair 0:f269e3021894 446 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
elessair 0:f269e3021894 447 } else if (authParams->len != sizeof(Lock_t)) {
elessair 0:f269e3021894 448 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
elessair 0:f269e3021894 449 } else if (authParams->offset != 0) {
elessair 0:f269e3021894 450 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
elessair 0:f269e3021894 451 } else if (memcmp(authParams->data, params.lock, sizeof(Lock_t)) != 0) {
elessair 0:f269e3021894 452 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
elessair 0:f269e3021894 453 } else {
elessair 0:f269e3021894 454 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
elessair 0:f269e3021894 455 }
elessair 0:f269e3021894 456 }
elessair 0:f269e3021894 457
elessair 0:f269e3021894 458 void uriDataWriteAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
elessair 0:f269e3021894 459 if (params.lockedState) {
elessair 0:f269e3021894 460 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
elessair 0:f269e3021894 461 } else if (authParams->offset != 0) {
elessair 0:f269e3021894 462 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
elessair 0:f269e3021894 463 } else {
elessair 0:f269e3021894 464 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
elessair 0:f269e3021894 465 }
elessair 0:f269e3021894 466 }
elessair 0:f269e3021894 467
elessair 0:f269e3021894 468 void powerModeAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
elessair 0:f269e3021894 469 if (params.lockedState) {
elessair 0:f269e3021894 470 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
elessair 0:f269e3021894 471 } else if (authParams->len != sizeof(uint8_t)) {
elessair 0:f269e3021894 472 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
elessair 0:f269e3021894 473 } else if (authParams->offset != 0) {
elessair 0:f269e3021894 474 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
elessair 0:f269e3021894 475 } else if (*((uint8_t *)authParams->data) >= NUM_POWER_MODES) {
elessair 0:f269e3021894 476 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
elessair 0:f269e3021894 477 } else {
elessair 0:f269e3021894 478 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
elessair 0:f269e3021894 479 }
elessair 0:f269e3021894 480 }
elessair 0:f269e3021894 481
elessair 0:f269e3021894 482 template <typename T>
elessair 0:f269e3021894 483 void basicAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
elessair 0:f269e3021894 484 if (params.lockedState) {
elessair 0:f269e3021894 485 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
elessair 0:f269e3021894 486 } else if (authParams->len != sizeof(T)) {
elessair 0:f269e3021894 487 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
elessair 0:f269e3021894 488 } else if (authParams->offset != 0) {
elessair 0:f269e3021894 489 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
elessair 0:f269e3021894 490 } else {
elessair 0:f269e3021894 491 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
elessair 0:f269e3021894 492 }
elessair 0:f269e3021894 493 }
elessair 0:f269e3021894 494
elessair 0:f269e3021894 495 BLEDevice &ble;
elessair 0:f269e3021894 496 Params_t &params;
elessair 0:f269e3021894 497 Ticker timeSinceBootTick;
elessair 0:f269e3021894 498 Timeout switchFrame;
elessair 0:f269e3021894 499 // Default value that is restored on reset.
elessair 0:f269e3021894 500 PowerLevels_t &defaultAdvPowerLevels; // This goes into the advertising frames (radio power measured at 1m from device).
elessair 0:f269e3021894 501 PowerLevels_t &radioPowerLevels; // This configures the power levels of the radio.
elessair 0:f269e3021894 502 uint8_t lockedState;
elessair 0:f269e3021894 503 bool initSucceeded;
elessair 0:f269e3021894 504 uint8_t resetFlag;
elessair 0:f269e3021894 505 bool switchFlag;
elessair 0:f269e3021894 506
elessair 0:f269e3021894 507 //UID default value that is restored on reset.
elessair 0:f269e3021894 508 UIDNamespaceID_t defaultUidNamespaceID;
elessair 0:f269e3021894 509 UIDInstanceID_t defaultUidInstanceID;
elessair 0:f269e3021894 510 float defaultUidAdvPeriod;
elessair 0:f269e3021894 511 int8_t defaultUidPower;
elessair 0:f269e3021894 512 uint16_t uidRFU;
elessair 0:f269e3021894 513 bool uidIsSet;
elessair 0:f269e3021894 514
elessair 0:f269e3021894 515 //URI default value that is restored on reset.
elessair 0:f269e3021894 516 uint8_t defaultUriDataLength;
elessair 0:f269e3021894 517 UriData_t defaultUriData;
elessair 0:f269e3021894 518 int8_t defaultUrlPower;
elessair 0:f269e3021894 519 float defaultUriAdvPeriod;
elessair 0:f269e3021894 520 bool urlIsSet;
elessair 0:f269e3021894 521
elessair 0:f269e3021894 522 //TLM default value that is restored on reset.
elessair 0:f269e3021894 523 uint8_t defaultTlmVersion;
elessair 0:f269e3021894 524 float defaultTlmAdvPeriod;
elessair 0:f269e3021894 525 volatile uint16_t TlmBatteryVoltage;
elessair 0:f269e3021894 526 volatile uint16_t TlmBeaconTemp;
elessair 0:f269e3021894 527 volatile uint32_t TlmPduCount;
elessair 0:f269e3021894 528 volatile uint32_t TlmTimeSinceBoot;
elessair 0:f269e3021894 529 bool tlmIsSet;
elessair 0:f269e3021894 530
elessair 0:f269e3021894 531 ReadOnlyGattCharacteristic<uint8_t> lockedStateChar;
elessair 0:f269e3021894 532 WriteOnlyGattCharacteristic<Lock_t> lockChar;
elessair 0:f269e3021894 533 GattCharacteristic uriDataChar;
elessair 0:f269e3021894 534 WriteOnlyGattCharacteristic<Lock_t> unlockChar;
elessair 0:f269e3021894 535 ReadWriteGattCharacteristic<uint8_t> flagsChar;
elessair 0:f269e3021894 536 ReadWriteGattCharacteristic<PowerLevels_t> advPowerLevelsChar;
elessair 0:f269e3021894 537 ReadWriteGattCharacteristic<uint8_t> txPowerModeChar;
elessair 0:f269e3021894 538 ReadWriteGattCharacteristic<uint16_t> beaconPeriodChar;
elessair 0:f269e3021894 539 WriteOnlyGattCharacteristic<uint8_t> resetChar;
elessair 0:f269e3021894 540 };
elessair 0:f269e3021894 541
elessair 0:f269e3021894 542 #endif // SERVICES_EDDYSTONE_BEACON_CONFIG_SERVICE_H_