Example program for the Eddystone Beacon service.

Dependencies:   BLE_API mbed nRF51822 X_NUCLEO_IDB0XA1

Fork of BLE_EddystoneBeacon by URIBeacon

This example demonstrates how to set up and initialize a Eddystone Beacon. For more details on the Eddystone specification please see the Eddystone Github Page.

The Basics

An Eddystone Beacon is a Bluetooth Low Energy beacon, that means it does all of its data transfer in GAP advertising packets. Eddystone beacons, unlike other beacons, broadcast multiple types of data. Currently Eddystone beacons have 3 frame types (UID, URL, TLM) that will get swapped out periodically. This swapping of frame data allows Eddystone beacons to send many different types of data, thus increasing their usefulness over traditional beacons. Note that the UID frame type provides the same 16Bytes of UUID namespace that iBeacons do and the URL frame type provides the same functionality as a URIBeacon.

For more details see the Eddystone Specification.

Smartphone Apps

nRF Master Control Panel - this program recognizes Eddystone beacons and will display the data for all frame types.

iPhone Physical Web app

Android App

Walkthrough of Physical Web application

Technical Details

The Eddystone Specification looks like the following image. Please note that this may change over time and for up to date information the official spec should be referenced. /media/uploads/mbedAustin/scratch-1-.png

The Eddystone Frames get swapped in and out depending on what frames you have enabled. The only required frame type is the TLM frame, all others are optional and you can have any number enabled. To disable the UID or URL frames give their values a 'NULL' in the Eddystone constructor. The Eddystone spec recommends broadcasting 10 frames a second.

Note

  • The current Eddystone mbed example does not allow for combining the eddystone service with other services, this will be changes in a future update.
  • There is an Eddystone Config service that allows for updating beacons in the field. We are working on an example for this, so keep your eyes pealed for a future update.
Committer:
mbedAustin
Date:
Fri Jul 17 21:43:56 2015 +0000
Revision:
12:ced5e837c511
Parent:
11:73ea4ef7f5a4
Child:
13:91c356fa928e
added functions to update TLM field values.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
screamer 0:c04d932e96c9 1 /* mbed Microcontroller Library
screamer 0:c04d932e96c9 2 * Copyright (c) 2006-2013 ARM Limited
screamer 0:c04d932e96c9 3 *
screamer 0:c04d932e96c9 4 * Licensed under the Apache License, Version 2.0 (the "License");
screamer 0:c04d932e96c9 5 * you may not use this file except in compliance with the License.
screamer 0:c04d932e96c9 6 * You may obtain a copy of the License at
screamer 0:c04d932e96c9 7 *
screamer 0:c04d932e96c9 8 * http://www.apache.org/licenses/LICENSE-2.0
screamer 0:c04d932e96c9 9 *
screamer 0:c04d932e96c9 10 * Unless required by applicable law or agreed to in writing, software
screamer 0:c04d932e96c9 11 * distributed under the License is distributed on an "AS IS" BASIS,
screamer 0:c04d932e96c9 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
screamer 0:c04d932e96c9 13 * See the License for the specific language governing permissions and
screamer 0:c04d932e96c9 14 * limitations under the License.
screamer 0:c04d932e96c9 15 */
screamer 0:c04d932e96c9 16
screamer 0:c04d932e96c9 17 #ifndef SERVICES_ZIPBEACONCONFIGSERVICE_H_
screamer 0:c04d932e96c9 18 #define SERVICES_ZIPBEACONCONFIGSERVICE_H_
screamer 0:c04d932e96c9 19
rgrover1 7:e9800c45e065 20 #include "BLE.h"
screamer 0:c04d932e96c9 21 #include "mbed.h"
screamer 0:c04d932e96c9 22
screamer 0:c04d932e96c9 23 #define UUID_URI_BEACON(FIRST, SECOND) { \
screamer 0:c04d932e96c9 24 0xee, 0x0c, FIRST, SECOND, 0x87, 0x86, 0x40, 0xba, \
screamer 0:c04d932e96c9 25 0xab, 0x96, 0x99, 0xb9, 0x1a, 0xc9, 0x81, 0xd8, \
screamer 0:c04d932e96c9 26 }
screamer 0:c04d932e96c9 27
screamer 0:c04d932e96c9 28 static const uint8_t UUID_URI_BEACON_SERVICE[] = UUID_URI_BEACON(0x20, 0x80);
screamer 0:c04d932e96c9 29 static const uint8_t UUID_LOCK_STATE_CHAR[] = UUID_URI_BEACON(0x20, 0x81);
screamer 0:c04d932e96c9 30 static const uint8_t UUID_LOCK_CHAR[] = UUID_URI_BEACON(0x20, 0x82);
screamer 0:c04d932e96c9 31 static const uint8_t UUID_UNLOCK_CHAR[] = UUID_URI_BEACON(0x20, 0x83);
screamer 0:c04d932e96c9 32 static const uint8_t UUID_URI_DATA_CHAR[] = UUID_URI_BEACON(0x20, 0x84);
screamer 0:c04d932e96c9 33 static const uint8_t UUID_FLAGS_CHAR[] = UUID_URI_BEACON(0x20, 0x85);
screamer 0:c04d932e96c9 34 static const uint8_t UUID_ADV_POWER_LEVELS_CHAR[] = UUID_URI_BEACON(0x20, 0x86);
screamer 0:c04d932e96c9 35 static const uint8_t UUID_TX_POWER_MODE_CHAR[] = UUID_URI_BEACON(0x20, 0x87);
screamer 0:c04d932e96c9 36 static const uint8_t UUID_BEACON_PERIOD_CHAR[] = UUID_URI_BEACON(0x20, 0x88);
screamer 0:c04d932e96c9 37 static const uint8_t UUID_RESET_CHAR[] = UUID_URI_BEACON(0x20, 0x89);
screamer 0:c04d932e96c9 38 static const uint8_t BEACON_UUID[] = {0xAA, 0xFE};
screamer 0:c04d932e96c9 39
screamer 0:c04d932e96c9 40 /**
screamer 0:c04d932e96c9 41 * @class ZipBeaconConfigService
screamer 0:c04d932e96c9 42 * @brief ZipBeacon Configuration Service. Can be used to set URL, adjust power levels, and set flags.
screamer 0:c04d932e96c9 43 * See http://uribeacon.org
screamer 0:c04d932e96c9 44 *
screamer 0:c04d932e96c9 45 */
screamer 0:c04d932e96c9 46 class ZipBeaconConfigService {
screamer 0:c04d932e96c9 47 public:
screamer 0:c04d932e96c9 48 /**
screamer 0:c04d932e96c9 49 * @brief Transmission Power Modes for UriBeacon
screamer 0:c04d932e96c9 50 */
screamer 0:c04d932e96c9 51 static const uint8_t TX_POWER_MODE_LOWEST = 0; /*!< Lowest TX power mode */
screamer 0:c04d932e96c9 52 static const uint8_t TX_POWER_MODE_LOW = 1; /*!< Low TX power mode */
screamer 0:c04d932e96c9 53 static const uint8_t TX_POWER_MODE_MEDIUM = 2; /*!< Medium TX power mode */
screamer 0:c04d932e96c9 54 static const uint8_t TX_POWER_MODE_HIGH = 3; /*!< High TX power mode */
screamer 0:c04d932e96c9 55 static const unsigned int NUM_POWER_MODES = 4; /*!< Number of Power Modes defined */
screamer 0:c04d932e96c9 56
screamer 0:c04d932e96c9 57 static const int ADVERTISING_INTERVAL_MSEC = 1000; // Advertising interval for config service.
screamer 0:c04d932e96c9 58 static const int SERVICE_DATA_MAX = 31; // Maximum size of service data in ADV packets
screamer 0:c04d932e96c9 59
screamer 0:c04d932e96c9 60 typedef uint8_t Lock_t[16]; /* 128 bits */
screamer 0:c04d932e96c9 61 typedef int8_t PowerLevels_t[NUM_POWER_MODES];
screamer 0:c04d932e96c9 62
mbedAustin 11:73ea4ef7f5a4 63
screamer 0:c04d932e96c9 64 static const int URI_DATA_MAX = 18;
screamer 0:c04d932e96c9 65 typedef uint8_t UriData_t[URI_DATA_MAX];
mbedAustin 10:b5d19bcf23cf 66
mbedAustin 11:73ea4ef7f5a4 67 // UID Frame Type subfields
mbedAustin 10:b5d19bcf23cf 68 static const int UID_NAMESPACEID_SIZE = 10;
mbedAustin 11:73ea4ef7f5a4 69 typedef uint8_t UIDNamespaceID_t[UID_NAMESPACEID_SIZE];
mbedAustin 10:b5d19bcf23cf 70 static const int UID_INSTANCEID_SIZE = 6;
mbedAustin 11:73ea4ef7f5a4 71 typedef uint8_t UIDInstanceID_t[UID_INSTANCEID_SIZE];
screamer 0:c04d932e96c9 72
mbedAustin 11:73ea4ef7f5a4 73 // Eddystone Frame Type ID
screamer 0:c04d932e96c9 74 static const uint8_t FRAME_TYPE_UID = 0x00;
screamer 0:c04d932e96c9 75 static const uint8_t FRAME_TYPE_URL = 0x10;
screamer 0:c04d932e96c9 76 static const uint8_t FRAME_TYPE_TLM = 0x20;
mbedAustin 11:73ea4ef7f5a4 77
mbedAustin 11:73ea4ef7f5a4 78 static const uint8_t FRAME_SIZE_TLM = 14; // TLM frame is a constant 14Bytes
mbedAustin 11:73ea4ef7f5a4 79 static const uint8_t FRAME_SIZE_UID = 20; // includes RFU bytes
screamer 0:c04d932e96c9 80
screamer 0:c04d932e96c9 81 struct Params_t {
screamer 0:c04d932e96c9 82 Lock_t lock;
screamer 0:c04d932e96c9 83 uint8_t uriDataLength;
screamer 0:c04d932e96c9 84 UriData_t uriData;
screamer 0:c04d932e96c9 85 uint8_t flags;
screamer 0:c04d932e96c9 86 PowerLevels_t advPowerLevels; // Current value of AdvertisedPowerLevels
screamer 0:c04d932e96c9 87 uint8_t txPowerMode; // Firmware power levels used with setTxPower()
screamer 0:c04d932e96c9 88 uint16_t beaconPeriod;
mbedAustin 10:b5d19bcf23cf 89 uint8_t tlmVersion; // version of TLM packet
mbedAustin 11:73ea4ef7f5a4 90 UIDNamespaceID_t uidNamespaceID; // UUID type, Namespace ID, 10B
mbedAustin 11:73ea4ef7f5a4 91 UIDInstanceID_t uidInstanceID; // UUID type, Instance ID, 6B
screamer 0:c04d932e96c9 92 };
screamer 0:c04d932e96c9 93
screamer 0:c04d932e96c9 94 /**
screamer 0:c04d932e96c9 95 * @param[ref] ble
screamer 0:c04d932e96c9 96 * BLEDevice object for the underlying controller.
screamer 0:c04d932e96c9 97 * @param[in/out] paramsIn
screamer 0:c04d932e96c9 98 * Reference to application-visible beacon state, loaded
screamer 0:c04d932e96c9 99 * from persistent storage at startup.
screamer 0:c04d932e96c9 100 * @paramsP[in] resetToDefaultsFlag
screamer 0:c04d932e96c9 101 * Applies to the state of the 'paramsIn' parameter.
screamer 0:c04d932e96c9 102 * If true, it indicates that paramsIn is potentially
screamer 0:c04d932e96c9 103 * un-initialized, and default values should be used
screamer 0:c04d932e96c9 104 * instead. Otherwise, paramsIn overrides the defaults.
screamer 0:c04d932e96c9 105 * @param[in] defaultUriDataIn
screamer 0:c04d932e96c9 106 * Default un-encoded URI; applies only if the resetToDefaultsFlag is true.
screamer 0:c04d932e96c9 107 * @param[in] defaultAdvPowerLevelsIn
screamer 0:c04d932e96c9 108 * Default power-levels array; applies only if the resetToDefaultsFlag is true.
screamer 0:c04d932e96c9 109 */
screamer 0:c04d932e96c9 110 ZipBeaconConfigService(BLEDevice &bleIn,
screamer 0:c04d932e96c9 111 Params_t &paramsIn,
screamer 0:c04d932e96c9 112 bool resetToDefaultsFlag,
screamer 0:c04d932e96c9 113 const char *defaultURIDataIn,
screamer 0:c04d932e96c9 114 PowerLevels_t &defaultAdvPowerLevelsIn) :
screamer 0:c04d932e96c9 115 ble(bleIn),
screamer 0:c04d932e96c9 116 params(paramsIn),
screamer 0:c04d932e96c9 117 defaultUriDataLength(),
screamer 0:c04d932e96c9 118 defaultUriData(),
screamer 0:c04d932e96c9 119 defaultAdvPowerLevels(defaultAdvPowerLevelsIn),
screamer 0:c04d932e96c9 120 initSucceeded(false),
screamer 0:c04d932e96c9 121 resetFlag(),
screamer 0:c04d932e96c9 122 lockedStateChar(UUID_LOCK_STATE_CHAR, &lockedState),
screamer 0:c04d932e96c9 123 lockChar(UUID_LOCK_CHAR, &params.lock),
screamer 0:c04d932e96c9 124 uriDataChar(UUID_URI_DATA_CHAR, params.uriData, 0, URI_DATA_MAX,
screamer 0:c04d932e96c9 125 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE),
screamer 0:c04d932e96c9 126 unlockChar(UUID_UNLOCK_CHAR, &params.lock),
screamer 0:c04d932e96c9 127 flagsChar(UUID_FLAGS_CHAR, &params.flags),
screamer 0:c04d932e96c9 128 advPowerLevelsChar(UUID_ADV_POWER_LEVELS_CHAR, &params.advPowerLevels),
screamer 0:c04d932e96c9 129 txPowerModeChar(UUID_TX_POWER_MODE_CHAR, &params.txPowerMode),
screamer 0:c04d932e96c9 130 beaconPeriodChar(UUID_BEACON_PERIOD_CHAR, &params.beaconPeriod),
screamer 0:c04d932e96c9 131 resetChar(UUID_RESET_CHAR, &resetFlag) {
screamer 0:c04d932e96c9 132
mbedAustin 10:b5d19bcf23cf 133 encodeURI(defaultURIDataIn, defaultUriData, defaultUriDataLength); // encode URL to URL Formatting
screamer 0:c04d932e96c9 134 if (defaultUriDataLength > URI_DATA_MAX) {
screamer 0:c04d932e96c9 135 return;
screamer 0:c04d932e96c9 136 }
screamer 0:c04d932e96c9 137
screamer 0:c04d932e96c9 138 if (!resetToDefaultsFlag && (params.uriDataLength > URI_DATA_MAX)) {
screamer 0:c04d932e96c9 139 resetToDefaultsFlag = true;
screamer 0:c04d932e96c9 140 }
screamer 0:c04d932e96c9 141 if (resetToDefaultsFlag) {
screamer 0:c04d932e96c9 142 resetToDefaults();
screamer 0:c04d932e96c9 143 } else {
screamer 0:c04d932e96c9 144 updateCharacteristicValues();
screamer 0:c04d932e96c9 145 }
screamer 0:c04d932e96c9 146
screamer 0:c04d932e96c9 147 lockedState = isLocked();
screamer 0:c04d932e96c9 148
screamer 0:c04d932e96c9 149 lockChar.setWriteAuthorizationCallback(this, &ZipBeaconConfigService::lockAuthorizationCallback);
screamer 0:c04d932e96c9 150 unlockChar.setWriteAuthorizationCallback(this, &ZipBeaconConfigService::unlockAuthorizationCallback);
screamer 0:c04d932e96c9 151 uriDataChar.setWriteAuthorizationCallback(this, &ZipBeaconConfigService::uriDataWriteAuthorizationCallback);
screamer 0:c04d932e96c9 152 flagsChar.setWriteAuthorizationCallback(this, &ZipBeaconConfigService::basicAuthorizationCallback<uint8_t>);
screamer 0:c04d932e96c9 153 advPowerLevelsChar.setWriteAuthorizationCallback(this, &ZipBeaconConfigService::basicAuthorizationCallback<PowerLevels_t>);
screamer 0:c04d932e96c9 154 txPowerModeChar.setWriteAuthorizationCallback(this, &ZipBeaconConfigService::powerModeAuthorizationCallback);
screamer 0:c04d932e96c9 155 beaconPeriodChar.setWriteAuthorizationCallback(this, &ZipBeaconConfigService::basicAuthorizationCallback<uint16_t>);
screamer 0:c04d932e96c9 156 resetChar.setWriteAuthorizationCallback(this, &ZipBeaconConfigService::basicAuthorizationCallback<uint8_t>);
screamer 0:c04d932e96c9 157
screamer 0:c04d932e96c9 158 static GattCharacteristic *charTable[] = {
screamer 0:c04d932e96c9 159 &lockedStateChar, &lockChar, &unlockChar, &uriDataChar,
screamer 0:c04d932e96c9 160 &flagsChar, &advPowerLevelsChar, &txPowerModeChar, &beaconPeriodChar, &resetChar
screamer 0:c04d932e96c9 161 };
screamer 0:c04d932e96c9 162
screamer 0:c04d932e96c9 163 GattService configService(UUID_URI_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
screamer 0:c04d932e96c9 164
screamer 0:c04d932e96c9 165 ble.addService(configService);
screamer 0:c04d932e96c9 166 ble.onDataWritten(this, &ZipBeaconConfigService::onDataWrittenCallback);
screamer 0:c04d932e96c9 167
screamer 0:c04d932e96c9 168 setupZipBeaconConfigAdvertisements(); /* Setup advertising for the configService. */
screamer 0:c04d932e96c9 169
screamer 0:c04d932e96c9 170 initSucceeded = true;
screamer 0:c04d932e96c9 171 }
screamer 0:c04d932e96c9 172
screamer 0:c04d932e96c9 173 bool configuredSuccessfully(void) const {
screamer 0:c04d932e96c9 174 return initSucceeded;
screamer 0:c04d932e96c9 175 }
screamer 0:c04d932e96c9 176
screamer 0:c04d932e96c9 177 /* Start out by advertising the configService for a limited time after
screamer 0:c04d932e96c9 178 * startup; and switch to the normal non-connectible beacon functionality
screamer 0:c04d932e96c9 179 * afterwards. */
screamer 0:c04d932e96c9 180 void setupZipBeaconConfigAdvertisements()
screamer 0:c04d932e96c9 181 {
mbedAustin 10:b5d19bcf23cf 182 const char DEVICE_NAME[] = "eddystone Config";
screamer 0:c04d932e96c9 183
screamer 0:c04d932e96c9 184 ble.clearAdvertisingPayload();
screamer 0:c04d932e96c9 185
screamer 0:c04d932e96c9 186 ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
screamer 0:c04d932e96c9 187
screamer 0:c04d932e96c9 188 // UUID is in different order in the ADV frame (!)
screamer 0:c04d932e96c9 189 uint8_t reversedServiceUUID[sizeof(UUID_URI_BEACON_SERVICE)];
screamer 0:c04d932e96c9 190 for (unsigned int i = 0; i < sizeof(UUID_URI_BEACON_SERVICE); i++) {
screamer 0:c04d932e96c9 191 reversedServiceUUID[i] = UUID_URI_BEACON_SERVICE[sizeof(UUID_URI_BEACON_SERVICE) - i - 1];
screamer 0:c04d932e96c9 192 }
screamer 0:c04d932e96c9 193 ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, reversedServiceUUID, sizeof(reversedServiceUUID));
screamer 0:c04d932e96c9 194 ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG);
screamer 0:c04d932e96c9 195 ble.accumulateScanResponse(GapAdvertisingData::COMPLETE_LOCAL_NAME, reinterpret_cast<const uint8_t *>(&DEVICE_NAME), sizeof(DEVICE_NAME));
screamer 0:c04d932e96c9 196 ble.accumulateScanResponse(
screamer 0:c04d932e96c9 197 GapAdvertisingData::TX_POWER_LEVEL,
screamer 0:c04d932e96c9 198 reinterpret_cast<uint8_t *>(&defaultAdvPowerLevels[ZipBeaconConfigService::TX_POWER_MODE_LOW]),
screamer 0:c04d932e96c9 199 sizeof(uint8_t));
screamer 0:c04d932e96c9 200
screamer 0:c04d932e96c9 201 ble.setTxPower(params.advPowerLevels[params.txPowerMode]);
screamer 0:c04d932e96c9 202 ble.setDeviceName(reinterpret_cast<const uint8_t *>(&DEVICE_NAME));
screamer 0:c04d932e96c9 203 ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
screamer 0:c04d932e96c9 204 ble.setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(ADVERTISING_INTERVAL_MSEC));
screamer 0:c04d932e96c9 205 }
mbedAustin 10:b5d19bcf23cf 206
mbedAustin 10:b5d19bcf23cf 207 /*
mbedAustin 10:b5d19bcf23cf 208 * Set Eddystone UID Frame information.
mbedAustin 10:b5d19bcf23cf 209 * @param[in] power TX Power in dB measured at 0 meters from the device. Range of -100 to +20 dB.
mbedAustin 10:b5d19bcf23cf 210 * @param namespaceID 10B namespace ID
mbedAustin 10:b5d19bcf23cf 211 * @param instanceID 6B instance ID
mbedAustin 10:b5d19bcf23cf 212 * @param RFU 2B of RFU, initialized to 0x0000 and not broadcast, included for future reference.
mbedAustin 10:b5d19bcf23cf 213 *
mbedAustin 10:b5d19bcf23cf 214 */
mbedAustin 11:73ea4ef7f5a4 215 void setUIDFrameData(int8_t power, UIDNamespaceID_t namespaceID, UIDInstanceID_t instanceID, uint16_t RFU = 0x00)
mbedAustin 11:73ea4ef7f5a4 216 {
mbedAustin 11:73ea4ef7f5a4 217 defaultUidPower = power;
mbedAustin 11:73ea4ef7f5a4 218 memcpy(defaultUidNamespaceID, namespaceID, UID_NAMESPACEID_SIZE);
mbedAustin 11:73ea4ef7f5a4 219 memcpy(defaultUidInstanceID, instanceID, UID_INSTANCEID_SIZE);
mbedAustin 11:73ea4ef7f5a4 220 uidRFU = (uint16_t)RFU; // this is probably bad form, but it doesnt really matter yet.
mbedAustin 11:73ea4ef7f5a4 221 return;
mbedAustin 11:73ea4ef7f5a4 222 }
mbedAustin 11:73ea4ef7f5a4 223
mbedAustin 11:73ea4ef7f5a4 224 /*
mbedAustin 11:73ea4ef7f5a4 225 * Construct UID frame from private variables
mbedAustin 11:73ea4ef7f5a4 226 * @param[in/out] Data pointer to array to store constructed frame in
mbedAustin 11:73ea4ef7f5a4 227 * @param[in] maxSize number of bytes left in array, effectively how much emtpy space is available to write to
mbedAustin 11:73ea4ef7f5a4 228 * @return number of bytes used. negative number indicates error message.
mbedAustin 11:73ea4ef7f5a4 229 */
mbedAustin 11:73ea4ef7f5a4 230 int constructUIDFrame(uint8_t * Data, uint8_t maxSize)
mbedAustin 10:b5d19bcf23cf 231 {
mbedAustin 11:73ea4ef7f5a4 232 if(maxSize < FRAME_SIZE_UID){
mbedAustin 11:73ea4ef7f5a4 233 return -1; // not enough space to encode UIDframe in advertising packet.
mbedAustin 11:73ea4ef7f5a4 234 }
mbedAustin 11:73ea4ef7f5a4 235 int index = 0;
mbedAustin 11:73ea4ef7f5a4 236 Data[index++] = FRAME_TYPE_UID; // 1B Type
mbedAustin 11:73ea4ef7f5a4 237 Data[index++] = defaultUidPower; // 1B Power @ 0meter
mbedAustin 11:73ea4ef7f5a4 238 for(int x = 0; x < UID_NAMESPACEID_SIZE; x++){ // 10B Namespce ID
mbedAustin 11:73ea4ef7f5a4 239 Data[index++] = defaultUidNamespaceID[x];
mbedAustin 11:73ea4ef7f5a4 240 }
mbedAustin 11:73ea4ef7f5a4 241 for(int x = 0; x< UID_INSTANCEID_SIZE; x++){ // 6B Instance ID
mbedAustin 11:73ea4ef7f5a4 242 Data[index++] = defaultUidInstanceID[x];
mbedAustin 11:73ea4ef7f5a4 243 }
mbedAustin 11:73ea4ef7f5a4 244 if(0x00 != uidRFU){ // 2B RFU, include if non-zero, otherwise ignore
mbedAustin 11:73ea4ef7f5a4 245 Data[index++] = (uint8_t)(uidRFU >> 8);
mbedAustin 11:73ea4ef7f5a4 246 Data[index++] = (uint8_t)uidRFU;
mbedAustin 11:73ea4ef7f5a4 247 }
mbedAustin 11:73ea4ef7f5a4 248 return index;
mbedAustin 10:b5d19bcf23cf 249 }
mbedAustin 10:b5d19bcf23cf 250
mbedAustin 10:b5d19bcf23cf 251 /*
mbedAustin 10:b5d19bcf23cf 252 * Set Eddystone URL Frame information.
mbedAustin 10:b5d19bcf23cf 253 * @param[in] power TX Power in dB measured at 0 meters from the device.
mbedAustin 10:b5d19bcf23cf 254 * @param url URL to encode
mbedAustin 10:b5d19bcf23cf 255 * @return false on success, true on failure.
mbedAustin 10:b5d19bcf23cf 256 */
mbedAustin 10:b5d19bcf23cf 257 bool setURLFrameData(int8_t power, const char * url)
mbedAustin 10:b5d19bcf23cf 258 {
mbedAustin 11:73ea4ef7f5a4 259 defaultUrlPower = power;
mbedAustin 11:73ea4ef7f5a4 260 encodeURI(url, defaultUriData, defaultUriDataLength); // encode URL to URL Formatting
mbedAustin 10:b5d19bcf23cf 261 if (defaultUriDataLength > URI_DATA_MAX) {
mbedAustin 10:b5d19bcf23cf 262 return true; // error, URL is too big
mbedAustin 10:b5d19bcf23cf 263 }
mbedAustin 11:73ea4ef7f5a4 264 return false;
mbedAustin 11:73ea4ef7f5a4 265 }
mbedAustin 11:73ea4ef7f5a4 266
mbedAustin 11:73ea4ef7f5a4 267 /*
mbedAustin 11:73ea4ef7f5a4 268 * Construct URL frame from private variables
mbedAustin 11:73ea4ef7f5a4 269 * @param[in/out] Data pointer to array to store constructed frame in
mbedAustin 11:73ea4ef7f5a4 270 * @param[in] maxSize number of bytes left in array, effectively how much emtpy space is available to write to
mbedAustin 11:73ea4ef7f5a4 271 * @return number of bytes used. negative number indicates error message.
mbedAustin 11:73ea4ef7f5a4 272 */
mbedAustin 11:73ea4ef7f5a4 273 int constructURLFrame(uint8_t * Data, uint8_t maxSize)
mbedAustin 11:73ea4ef7f5a4 274 {
mbedAustin 11:73ea4ef7f5a4 275 if(maxSize < (2 + defaultUriDataLength)){
mbedAustin 11:73ea4ef7f5a4 276 return -1; // not enough space to encode URL frame in advertising packet.
mbedAustin 11:73ea4ef7f5a4 277 }
mbedAustin 11:73ea4ef7f5a4 278 int index = 0;
mbedAustin 11:73ea4ef7f5a4 279 Data[index++] = FRAME_TYPE_URL; // 1B Type
mbedAustin 11:73ea4ef7f5a4 280 Data[index++] = defaultUrlPower; // 1B TX Power
mbedAustin 11:73ea4ef7f5a4 281 for(int x = 0; x < defaultUriDataLength; x++){ // 18B of URL Prefix + encoded URL
mbedAustin 11:73ea4ef7f5a4 282 Data[index++] = defaultUriData[x];
mbedAustin 11:73ea4ef7f5a4 283 }
mbedAustin 11:73ea4ef7f5a4 284 return index;
mbedAustin 10:b5d19bcf23cf 285 }
mbedAustin 10:b5d19bcf23cf 286
mbedAustin 10:b5d19bcf23cf 287 /*
mbedAustin 10:b5d19bcf23cf 288 * Set Eddystone TLM Frame information.
mbedAustin 10:b5d19bcf23cf 289 * @param[in] Version of the TLM beacon data format
mbedAustin 10:b5d19bcf23cf 290 * @param batteryVoltage in milivolts
mbedAustin 10:b5d19bcf23cf 291 * @param beaconTemp in 8.8 floating point notation
mbedAustin 10:b5d19bcf23cf 292 *
mbedAustin 10:b5d19bcf23cf 293 */
mbedAustin 10:b5d19bcf23cf 294 void setTLMFrameData(uint8_t version, uint16_t batteryVoltage, uint16_t beaconTemp, uint32_t pduCount = 0, uint32_t timeSinceBoot = 0)
mbedAustin 10:b5d19bcf23cf 295 {
mbedAustin 10:b5d19bcf23cf 296 TlmVersion = version;
mbedAustin 10:b5d19bcf23cf 297 TlmBatteryVoltage = batteryVoltage;
mbedAustin 10:b5d19bcf23cf 298 TlmBeaconTemp = beaconTemp;
mbedAustin 10:b5d19bcf23cf 299 TlmPduCount = pduCount; // reset
mbedAustin 10:b5d19bcf23cf 300 TlmTimeSinceBoot = timeSinceBoot; // reset
mbedAustin 10:b5d19bcf23cf 301 return;
mbedAustin 10:b5d19bcf23cf 302 }
mbedAustin 11:73ea4ef7f5a4 303
mbedAustin 11:73ea4ef7f5a4 304 /*
mbedAustin 11:73ea4ef7f5a4 305 * Construct TLM frame from private variables
mbedAustin 11:73ea4ef7f5a4 306 * @param[in/out] Data pointer to array to store constructed frame in
mbedAustin 11:73ea4ef7f5a4 307 * @param[in] maxSize number of bytes left in array, effectively how much emtpy space is available to write to
mbedAustin 11:73ea4ef7f5a4 308 * @return number of bytes used. negative number indicates error message.
mbedAustin 11:73ea4ef7f5a4 309 */
mbedAustin 11:73ea4ef7f5a4 310 int constructTLMFrame(uint8_t * Data, uint8_t maxSize)
mbedAustin 11:73ea4ef7f5a4 311 {
mbedAustin 11:73ea4ef7f5a4 312 if(maxSize < FRAME_SIZE_TLM){ // error, not enough space to add TLM frame. 14B, every time
mbedAustin 11:73ea4ef7f5a4 313 return -1;
mbedAustin 11:73ea4ef7f5a4 314 }
mbedAustin 11:73ea4ef7f5a4 315 int index = 0;
mbedAustin 11:73ea4ef7f5a4 316 Data[index++] = FRAME_TYPE_TLM; // Eddystone frame type = Telemetry
mbedAustin 11:73ea4ef7f5a4 317 Data[index++] = TlmVersion; // TLM Version Number
mbedAustin 11:73ea4ef7f5a4 318 Data[index++] = (uint8_t)(TlmBatteryVoltage>>0); // Battery Voltage[0]
mbedAustin 11:73ea4ef7f5a4 319 Data[index++] = (uint8_t)(TlmBatteryVoltage>>8); // Battery Voltage[1]
mbedAustin 11:73ea4ef7f5a4 320 Data[index++] = (uint8_t)(TlmBeaconTemp>>0); // Beacon Temp[0]
mbedAustin 11:73ea4ef7f5a4 321 Data[index++] = (uint8_t)(TlmBeaconTemp>>8); // Beacon Temp[1]
mbedAustin 11:73ea4ef7f5a4 322 Data[index++] = (uint8_t)(TlmPduCount>>0); // PDU Count [0]
mbedAustin 11:73ea4ef7f5a4 323 Data[index++] = (uint8_t)(TlmPduCount>>8); // PDU Count [1]
mbedAustin 11:73ea4ef7f5a4 324 Data[index++] = (uint8_t)(TlmPduCount>>16); // PDU Count [2]
mbedAustin 11:73ea4ef7f5a4 325 Data[index++] = (uint8_t)(TlmPduCount>>24); // PDU Count [3]
mbedAustin 11:73ea4ef7f5a4 326 Data[index++] = (uint8_t)(TlmTimeSinceBoot>>0); // Time Since Boot [0]
mbedAustin 11:73ea4ef7f5a4 327 Data[index++] = (uint8_t)(TlmTimeSinceBoot>>8); // Time Since Boot [1]
mbedAustin 11:73ea4ef7f5a4 328 Data[index++] = (uint8_t)(TlmTimeSinceBoot>>16); // Time Since Boot [2]
mbedAustin 11:73ea4ef7f5a4 329 Data[index++] = (uint8_t)(TlmTimeSinceBoot>>24); // Time Since Boot [3]
mbedAustin 11:73ea4ef7f5a4 330
mbedAustin 11:73ea4ef7f5a4 331 return index;
mbedAustin 11:73ea4ef7f5a4 332 }
mbedAustin 12:ced5e837c511 333
mbedAustin 12:ced5e837c511 334 /*
mbedAustin 12:ced5e837c511 335 * Update the TLM frame battery voltage value
mbedAustin 12:ced5e837c511 336 * @param[in] voltagemv Voltage to update the TLM field battery voltage with (in mV)
mbedAustin 12:ced5e837c511 337 * @return nothing
mbedAustin 12:ced5e837c511 338 */
mbedAustin 12:ced5e837c511 339 void updateTlmBatteryVoltage(uint16_t voltagemv){
mbedAustin 12:ced5e837c511 340 TlmBatteryVoltage = voltagemv;
mbedAustin 12:ced5e837c511 341 return;
mbedAustin 12:ced5e837c511 342 }
mbedAustin 12:ced5e837c511 343
mbedAustin 12:ced5e837c511 344 /*
mbedAustin 12:ced5e837c511 345 * Update the TLM frame beacon temperature
mbedAustin 12:ced5e837c511 346 * @param[in] temp Temperature of beacon (in 8.8fpn)
mbedAustin 12:ced5e837c511 347 * @return nothing
mbedAustin 12:ced5e837c511 348 */
mbedAustin 12:ced5e837c511 349 void updateTlmBeaconTemp(uint16_t temp){
mbedAustin 12:ced5e837c511 350 TlmBeaconTemp = temp;
mbedAustin 12:ced5e837c511 351 return;
mbedAustin 12:ced5e837c511 352 }
mbedAustin 12:ced5e837c511 353
mbedAustin 12:ced5e837c511 354 /*
mbedAustin 12:ced5e837c511 355 * Update the TLM frame PDU Count field
mbedAustin 12:ced5e837c511 356 * @param[in] pduCount Number of Advertisiting frames sent since powerup
mbedAustin 12:ced5e837c511 357 * @return nothing
mbedAustin 12:ced5e837c511 358 */
mbedAustin 12:ced5e837c511 359 void updateTlmPduCount(uint32_t pduCount){
mbedAustin 12:ced5e837c511 360 TlmPduCount = pduCount;
mbedAustin 12:ced5e837c511 361 return;
mbedAustin 12:ced5e837c511 362 }
mbedAustin 12:ced5e837c511 363
mbedAustin 12:ced5e837c511 364 /*
mbedAustin 12:ced5e837c511 365 * Update the TLM frame Time since boot in 0.1s incriments
mbedAustin 12:ced5e837c511 366 * @param[in] timeSinceBoot Time since boot in 0.1s incriments
mbedAustin 12:ced5e837c511 367 * @return nothing
mbedAustin 12:ced5e837c511 368 */
mbedAustin 12:ced5e837c511 369 void updateTlmTimeSinceBoot(uint32_t timeSinceBoot){
mbedAustin 12:ced5e837c511 370 TlmTimeSinceBoot = timeSinceBoot;
mbedAustin 12:ced5e837c511 371 return;
mbedAustin 12:ced5e837c511 372 }
screamer 0:c04d932e96c9 373
screamer 0:c04d932e96c9 374 /* Helper function to switch to the non-connectible normal mode for ZipBeacon. This gets called after a timeout. */
screamer 0:c04d932e96c9 375 void setupZipBeaconAdvertisements()
screamer 0:c04d932e96c9 376 {
screamer 0:c04d932e96c9 377 uint8_t serviceData[SERVICE_DATA_MAX];
screamer 0:c04d932e96c9 378 unsigned serviceDataLen = 0;
screamer 0:c04d932e96c9 379
screamer 0:c04d932e96c9 380 /* Reinitialize the BLE stack. This will clear away the existing services and advertising state. */
screamer 0:c04d932e96c9 381 ble.shutdown();
screamer 0:c04d932e96c9 382 ble.init();
screamer 0:c04d932e96c9 383
screamer 0:c04d932e96c9 384 // Fields from the Service
screamer 0:c04d932e96c9 385 unsigned beaconPeriod = params.beaconPeriod;
screamer 0:c04d932e96c9 386 unsigned txPowerMode = params.txPowerMode;
screamer 0:c04d932e96c9 387 unsigned uriDataLength = params.uriDataLength;
screamer 0:c04d932e96c9 388 ZipBeaconConfigService::UriData_t &uriData = params.uriData;
screamer 0:c04d932e96c9 389 ZipBeaconConfigService::PowerLevels_t &advPowerLevels = params.advPowerLevels;
screamer 0:c04d932e96c9 390 uint8_t flags = params.flags;
screamer 0:c04d932e96c9 391
rgrover1 6:e90c398b03e0 392 extern void saveURIBeaconConfigParams(const Params_t *paramsP); /* forward declaration; necessary to avoid a circular dependency. */
rgrover1 6:e90c398b03e0 393 saveURIBeaconConfigParams(&params);
rgrover1 6:e90c398b03e0 394
screamer 0:c04d932e96c9 395 ble.clearAdvertisingPayload();
screamer 0:c04d932e96c9 396 ble.setTxPower(params.advPowerLevels[params.txPowerMode]);
screamer 0:c04d932e96c9 397 ble.setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
screamer 0:c04d932e96c9 398 ble.setAdvertisingInterval(beaconPeriod);
screamer 0:c04d932e96c9 399 ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
screamer 0:c04d932e96c9 400 ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, BEACON_UUID, sizeof(BEACON_UUID));
screamer 0:c04d932e96c9 401
screamer 0:c04d932e96c9 402 serviceData[serviceDataLen++] = BEACON_UUID[0];
screamer 0:c04d932e96c9 403 serviceData[serviceDataLen++] = BEACON_UUID[1];
screamer 0:c04d932e96c9 404 serviceData[serviceDataLen++] = FRAME_TYPE_URL | flags;
screamer 0:c04d932e96c9 405 serviceData[serviceDataLen++] = advPowerLevels[txPowerMode];
screamer 0:c04d932e96c9 406 for (unsigned j = 0; j < uriDataLength; j++) {
screamer 0:c04d932e96c9 407 serviceData[serviceDataLen++] = uriData[j];
screamer 0:c04d932e96c9 408 }
screamer 0:c04d932e96c9 409 ble.accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, serviceData, serviceDataLen);
screamer 0:c04d932e96c9 410 }
screamer 0:c04d932e96c9 411
screamer 0:c04d932e96c9 412 private:
screamer 0:c04d932e96c9 413 // True if the lock bits are non-zero
screamer 0:c04d932e96c9 414 bool isLocked() {
screamer 0:c04d932e96c9 415 Lock_t testLock;
screamer 0:c04d932e96c9 416 memset(testLock, 0, sizeof(Lock_t));
screamer 0:c04d932e96c9 417 return memcmp(params.lock, testLock, sizeof(Lock_t));
screamer 0:c04d932e96c9 418 }
screamer 0:c04d932e96c9 419
screamer 0:c04d932e96c9 420 /*
screamer 0:c04d932e96c9 421 * This callback is invoked when a GATT client attempts to modify any of the
screamer 0:c04d932e96c9 422 * characteristics of this service. Attempts to do so are also applied to
screamer 0:c04d932e96c9 423 * the internal state of this service object.
screamer 0:c04d932e96c9 424 */
rgrover1 7:e9800c45e065 425 void onDataWrittenCallback(const GattWriteCallbackParams *writeParams) {
rgrover1 7:e9800c45e065 426 uint16_t handle = writeParams->handle;
screamer 0:c04d932e96c9 427
screamer 0:c04d932e96c9 428 if (handle == lockChar.getValueHandle()) {
screamer 0:c04d932e96c9 429 // Validated earlier
screamer 0:c04d932e96c9 430 memcpy(params.lock, writeParams->data, sizeof(Lock_t));
screamer 0:c04d932e96c9 431 // use isLocked() in case bits are being set to all 0's
screamer 0:c04d932e96c9 432 lockedState = isLocked();
screamer 0:c04d932e96c9 433 } else if (handle == unlockChar.getValueHandle()) {
screamer 0:c04d932e96c9 434 // Validated earlier
screamer 0:c04d932e96c9 435 memset(params.lock, 0, sizeof(Lock_t));
screamer 0:c04d932e96c9 436 lockedState = false;
screamer 0:c04d932e96c9 437 } else if (handle == uriDataChar.getValueHandle()) {
screamer 0:c04d932e96c9 438 params.uriDataLength = writeParams->len;
screamer 0:c04d932e96c9 439 memcpy(params.uriData, writeParams->data, params.uriDataLength);
screamer 0:c04d932e96c9 440 } else if (handle == flagsChar.getValueHandle()) {
screamer 0:c04d932e96c9 441 params.flags = *(writeParams->data);
screamer 0:c04d932e96c9 442 } else if (handle == advPowerLevelsChar.getValueHandle()) {
screamer 0:c04d932e96c9 443 memcpy(params.advPowerLevels, writeParams->data, sizeof(PowerLevels_t));
screamer 0:c04d932e96c9 444 } else if (handle == txPowerModeChar.getValueHandle()) {
screamer 0:c04d932e96c9 445 params.txPowerMode = *(writeParams->data);
screamer 0:c04d932e96c9 446 } else if (handle == beaconPeriodChar.getValueHandle()) {
screamer 0:c04d932e96c9 447 params.beaconPeriod = *((uint16_t *)(writeParams->data));
rgrover1 4:4440953bde10 448
rgrover1 4:4440953bde10 449 /* Re-map beaconPeriod to within permissible bounds if necessary. */
rgrover1 4:4440953bde10 450 if (params.beaconPeriod != 0) {
rgrover1 4:4440953bde10 451 bool paramsUpdated = false;
rgrover1 4:4440953bde10 452 if (params.beaconPeriod < ble.getMinAdvertisingInterval()) {
rgrover1 4:4440953bde10 453 params.beaconPeriod = ble.getMinAdvertisingInterval();
rgrover1 4:4440953bde10 454 paramsUpdated = true;
rgrover1 4:4440953bde10 455 } else if (params.beaconPeriod > ble.getMaxAdvertisingInterval()) {
rgrover1 4:4440953bde10 456 params.beaconPeriod = ble.getMaxAdvertisingInterval();
rgrover1 4:4440953bde10 457 paramsUpdated = true;
rgrover1 4:4440953bde10 458 }
rgrover1 4:4440953bde10 459 if (paramsUpdated) {
rgrover1 4:4440953bde10 460 ble.updateCharacteristicValue(beaconPeriodChar.getValueHandle(), reinterpret_cast<uint8_t *>(&params.beaconPeriod), sizeof(uint16_t));
rgrover1 4:4440953bde10 461 }
rgrover1 4:4440953bde10 462 }
screamer 0:c04d932e96c9 463 } else if (handle == resetChar.getValueHandle()) {
screamer 0:c04d932e96c9 464 resetToDefaults();
screamer 0:c04d932e96c9 465 }
screamer 0:c04d932e96c9 466 }
screamer 0:c04d932e96c9 467
screamer 0:c04d932e96c9 468 /*
screamer 0:c04d932e96c9 469 * Reset the default values.
screamer 0:c04d932e96c9 470 */
screamer 0:c04d932e96c9 471 void resetToDefaults(void) {
screamer 0:c04d932e96c9 472 lockedState = false;
screamer 0:c04d932e96c9 473 memset(params.lock, 0, sizeof(Lock_t));
screamer 0:c04d932e96c9 474 memcpy(params.uriData, defaultUriData, URI_DATA_MAX);
screamer 0:c04d932e96c9 475 params.uriDataLength = defaultUriDataLength;
screamer 0:c04d932e96c9 476 params.flags = 0;
screamer 0:c04d932e96c9 477 memcpy(params.advPowerLevels, defaultAdvPowerLevels, sizeof(PowerLevels_t));
screamer 0:c04d932e96c9 478 params.txPowerMode = TX_POWER_MODE_LOW;
screamer 0:c04d932e96c9 479 params.beaconPeriod = 1000;
mbedAustin 10:b5d19bcf23cf 480 memcpy(params.uidNamespaceID, defaultUidNamespaceID, UID_NAMESPACEID_SIZE);
mbedAustin 10:b5d19bcf23cf 481 memcpy(params.uidInstanceID, defaultUidInstanceID, UID_INSTANCEID_SIZE);
mbedAustin 12:ced5e837c511 482 params.tlmVersion = 0;
screamer 0:c04d932e96c9 483 updateCharacteristicValues();
screamer 0:c04d932e96c9 484 }
screamer 0:c04d932e96c9 485
screamer 0:c04d932e96c9 486 /*
screamer 0:c04d932e96c9 487 * Internal helper function used to update the GATT database following any
screamer 0:c04d932e96c9 488 * change to the internal state of the service object.
screamer 0:c04d932e96c9 489 */
screamer 0:c04d932e96c9 490 void updateCharacteristicValues(void) {
screamer 0:c04d932e96c9 491 ble.updateCharacteristicValue(lockedStateChar.getValueHandle(), &lockedState, 1);
screamer 0:c04d932e96c9 492 ble.updateCharacteristicValue(uriDataChar.getValueHandle(), params.uriData, params.uriDataLength);
screamer 0:c04d932e96c9 493 ble.updateCharacteristicValue(flagsChar.getValueHandle(), &params.flags, 1);
screamer 0:c04d932e96c9 494 ble.updateCharacteristicValue(beaconPeriodChar.getValueHandle(),
screamer 0:c04d932e96c9 495 reinterpret_cast<uint8_t *>(&params.beaconPeriod), sizeof(uint16_t));
screamer 0:c04d932e96c9 496 ble.updateCharacteristicValue(txPowerModeChar.getValueHandle(), &params.txPowerMode, 1);
screamer 0:c04d932e96c9 497 ble.updateCharacteristicValue(advPowerLevelsChar.getValueHandle(),
screamer 0:c04d932e96c9 498 reinterpret_cast<uint8_t *>(params.advPowerLevels), sizeof(PowerLevels_t));
screamer 0:c04d932e96c9 499 }
screamer 0:c04d932e96c9 500
screamer 0:c04d932e96c9 501 private:
rgrover1 7:e9800c45e065 502 void lockAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
screamer 0:c04d932e96c9 503 if (lockedState) {
screamer 0:c04d932e96c9 504 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
screamer 0:c04d932e96c9 505 } else if (authParams->len != sizeof(Lock_t)) {
screamer 0:c04d932e96c9 506 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
screamer 0:c04d932e96c9 507 } else if (authParams->offset != 0) {
screamer 0:c04d932e96c9 508 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
screamer 0:c04d932e96c9 509 } else {
screamer 0:c04d932e96c9 510 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
screamer 0:c04d932e96c9 511 }
screamer 0:c04d932e96c9 512 }
screamer 0:c04d932e96c9 513
screamer 0:c04d932e96c9 514
rgrover1 7:e9800c45e065 515 void unlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
screamer 0:c04d932e96c9 516 if (!lockedState) {
screamer 0:c04d932e96c9 517 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
screamer 0:c04d932e96c9 518 } else if (authParams->len != sizeof(Lock_t)) {
screamer 0:c04d932e96c9 519 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
screamer 0:c04d932e96c9 520 } else if (authParams->offset != 0) {
screamer 0:c04d932e96c9 521 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
screamer 0:c04d932e96c9 522 } else if (memcmp(authParams->data, params.lock, sizeof(Lock_t)) != 0) {
screamer 0:c04d932e96c9 523 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
screamer 0:c04d932e96c9 524 } else {
screamer 0:c04d932e96c9 525 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
screamer 0:c04d932e96c9 526 }
screamer 0:c04d932e96c9 527 }
screamer 0:c04d932e96c9 528
rgrover1 7:e9800c45e065 529 void uriDataWriteAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
screamer 0:c04d932e96c9 530 if (lockedState) {
screamer 0:c04d932e96c9 531 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
screamer 0:c04d932e96c9 532 } else if (authParams->offset != 0) {
screamer 0:c04d932e96c9 533 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
screamer 0:c04d932e96c9 534 } else {
screamer 0:c04d932e96c9 535 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
screamer 0:c04d932e96c9 536 }
screamer 0:c04d932e96c9 537 }
screamer 0:c04d932e96c9 538
rgrover1 7:e9800c45e065 539 void powerModeAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
screamer 0:c04d932e96c9 540 if (lockedState) {
screamer 0:c04d932e96c9 541 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
screamer 0:c04d932e96c9 542 } else if (authParams->len != sizeof(uint8_t)) {
screamer 0:c04d932e96c9 543 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
screamer 0:c04d932e96c9 544 } else if (authParams->offset != 0) {
screamer 0:c04d932e96c9 545 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
screamer 0:c04d932e96c9 546 } else if (*((uint8_t *)authParams->data) >= NUM_POWER_MODES) {
screamer 0:c04d932e96c9 547 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
screamer 0:c04d932e96c9 548 } else {
screamer 0:c04d932e96c9 549 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
screamer 0:c04d932e96c9 550 }
screamer 0:c04d932e96c9 551 }
screamer 0:c04d932e96c9 552
screamer 0:c04d932e96c9 553 template <typename T>
rgrover1 7:e9800c45e065 554 void basicAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
screamer 0:c04d932e96c9 555 if (lockedState) {
screamer 0:c04d932e96c9 556 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
screamer 0:c04d932e96c9 557 } else if (authParams->len != sizeof(T)) {
screamer 0:c04d932e96c9 558 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
screamer 0:c04d932e96c9 559 } else if (authParams->offset != 0) {
screamer 0:c04d932e96c9 560 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
screamer 0:c04d932e96c9 561 } else {
screamer 0:c04d932e96c9 562 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
screamer 0:c04d932e96c9 563 }
screamer 0:c04d932e96c9 564 }
screamer 0:c04d932e96c9 565
mbedAustin 10:b5d19bcf23cf 566 BLEDevice &ble;
mbedAustin 10:b5d19bcf23cf 567 Params_t &params;
mbedAustin 10:b5d19bcf23cf 568 // Default value that is restored on reset
mbedAustin 10:b5d19bcf23cf 569 size_t defaultUriDataLength;
mbedAustin 10:b5d19bcf23cf 570 UriData_t defaultUriData;
mbedAustin 10:b5d19bcf23cf 571 UIDNamespaceID_t defaultUidNamespaceID;
mbedAustin 10:b5d19bcf23cf 572 UIDInstanceID_t defaultUidInstanceID;
mbedAustin 11:73ea4ef7f5a4 573 int8_t defaultUidPower;
mbedAustin 11:73ea4ef7f5a4 574 int8_t defaultUrlPower;
mbedAustin 10:b5d19bcf23cf 575 uint16_t uidRFU;
screamer 0:c04d932e96c9 576 // Default value that is restored on reset
mbedAustin 10:b5d19bcf23cf 577 PowerLevels_t &defaultAdvPowerLevels;
mbedAustin 10:b5d19bcf23cf 578 uint8_t lockedState;
mbedAustin 10:b5d19bcf23cf 579 bool initSucceeded;
mbedAustin 10:b5d19bcf23cf 580 uint8_t resetFlag;
mbedAustin 10:b5d19bcf23cf 581
mbedAustin 10:b5d19bcf23cf 582 // Private Variables for Telemetry Data
mbedAustin 10:b5d19bcf23cf 583 uint8_t TlmVersion;
mbedAustin 10:b5d19bcf23cf 584 uint16_t TlmBatteryVoltage;
mbedAustin 10:b5d19bcf23cf 585 uint16_t TlmBeaconTemp;
mbedAustin 10:b5d19bcf23cf 586 uint32_t TlmPduCount;
mbedAustin 10:b5d19bcf23cf 587 uint32_t TlmTimeSinceBoot;
screamer 0:c04d932e96c9 588
screamer 0:c04d932e96c9 589 ReadOnlyGattCharacteristic<uint8_t> lockedStateChar;
screamer 0:c04d932e96c9 590 WriteOnlyGattCharacteristic<Lock_t> lockChar;
screamer 0:c04d932e96c9 591 GattCharacteristic uriDataChar;
screamer 0:c04d932e96c9 592 WriteOnlyGattCharacteristic<Lock_t> unlockChar;
screamer 0:c04d932e96c9 593 ReadWriteGattCharacteristic<uint8_t> flagsChar;
screamer 0:c04d932e96c9 594 ReadWriteGattCharacteristic<PowerLevels_t> advPowerLevelsChar;
screamer 0:c04d932e96c9 595 ReadWriteGattCharacteristic<uint8_t> txPowerModeChar;
screamer 0:c04d932e96c9 596 ReadWriteGattCharacteristic<uint16_t> beaconPeriodChar;
screamer 0:c04d932e96c9 597 WriteOnlyGattCharacteristic<uint8_t> resetChar;
screamer 0:c04d932e96c9 598
screamer 0:c04d932e96c9 599 public:
screamer 0:c04d932e96c9 600 /*
screamer 0:c04d932e96c9 601 * Encode a human-readable URI into the binary format defined by URIBeacon spec (https://github.com/google/uribeacon/tree/master/specification).
screamer 0:c04d932e96c9 602 */
screamer 0:c04d932e96c9 603 static void encodeURI(const char *uriDataIn, UriData_t uriDataOut, size_t &sizeofURIDataOut) {
screamer 0:c04d932e96c9 604 const char *prefixes[] = {
screamer 0:c04d932e96c9 605 "http://www.",
screamer 0:c04d932e96c9 606 "https://www.",
screamer 0:c04d932e96c9 607 "http://",
screamer 0:c04d932e96c9 608 "https://",
screamer 0:c04d932e96c9 609 };
screamer 0:c04d932e96c9 610 const size_t NUM_PREFIXES = sizeof(prefixes) / sizeof(char *);
screamer 0:c04d932e96c9 611 const char *suffixes[] = {
screamer 0:c04d932e96c9 612 ".com/",
screamer 0:c04d932e96c9 613 ".org/",
screamer 0:c04d932e96c9 614 ".edu/",
screamer 0:c04d932e96c9 615 ".net/",
screamer 0:c04d932e96c9 616 ".info/",
screamer 0:c04d932e96c9 617 ".biz/",
screamer 0:c04d932e96c9 618 ".gov/",
screamer 0:c04d932e96c9 619 ".com",
screamer 0:c04d932e96c9 620 ".org",
screamer 0:c04d932e96c9 621 ".edu",
screamer 0:c04d932e96c9 622 ".net",
screamer 0:c04d932e96c9 623 ".info",
screamer 0:c04d932e96c9 624 ".biz",
screamer 0:c04d932e96c9 625 ".gov"
screamer 0:c04d932e96c9 626 };
screamer 0:c04d932e96c9 627 const size_t NUM_SUFFIXES = sizeof(suffixes) / sizeof(char *);
screamer 0:c04d932e96c9 628
screamer 0:c04d932e96c9 629 sizeofURIDataOut = 0;
screamer 0:c04d932e96c9 630 memset(uriDataOut, 0, sizeof(UriData_t));
screamer 0:c04d932e96c9 631
screamer 0:c04d932e96c9 632 if ((uriDataIn == NULL) || (strlen(uriDataIn) == 0)) {
screamer 0:c04d932e96c9 633 return;
screamer 0:c04d932e96c9 634 }
screamer 0:c04d932e96c9 635
screamer 0:c04d932e96c9 636 /*
screamer 0:c04d932e96c9 637 * handle prefix
screamer 0:c04d932e96c9 638 */
screamer 0:c04d932e96c9 639 for (unsigned i = 0; i < NUM_PREFIXES; i++) {
screamer 0:c04d932e96c9 640 size_t prefixLen = strlen(prefixes[i]);
screamer 0:c04d932e96c9 641 if (strncmp(uriDataIn, prefixes[i], prefixLen) == 0) {
screamer 0:c04d932e96c9 642 uriDataOut[sizeofURIDataOut++] = i;
screamer 0:c04d932e96c9 643 uriDataIn += prefixLen;
screamer 0:c04d932e96c9 644 break;
screamer 0:c04d932e96c9 645 }
screamer 0:c04d932e96c9 646 }
screamer 0:c04d932e96c9 647
screamer 0:c04d932e96c9 648 /*
screamer 0:c04d932e96c9 649 * handle suffixes
screamer 0:c04d932e96c9 650 */
screamer 0:c04d932e96c9 651 while (*uriDataIn && (sizeofURIDataOut < URI_DATA_MAX)) {
screamer 0:c04d932e96c9 652 /* check for suffix match */
screamer 0:c04d932e96c9 653 unsigned i;
screamer 0:c04d932e96c9 654 for (i = 0; i < NUM_SUFFIXES; i++) {
screamer 0:c04d932e96c9 655 size_t suffixLen = strlen(suffixes[i]);
screamer 0:c04d932e96c9 656 if (strncmp(uriDataIn, suffixes[i], suffixLen) == 0) {
screamer 0:c04d932e96c9 657 uriDataOut[sizeofURIDataOut++] = i;
screamer 0:c04d932e96c9 658 uriDataIn += suffixLen;
screamer 0:c04d932e96c9 659 break; /* from the for loop for checking against suffixes */
screamer 0:c04d932e96c9 660 }
screamer 0:c04d932e96c9 661 }
screamer 0:c04d932e96c9 662 /* This is the default case where we've got an ordinary character which doesn't match a suffix. */
screamer 0:c04d932e96c9 663 if (i == NUM_SUFFIXES) {
screamer 0:c04d932e96c9 664 uriDataOut[sizeofURIDataOut++] = *uriDataIn;
screamer 0:c04d932e96c9 665 ++uriDataIn;
screamer 0:c04d932e96c9 666 }
screamer 0:c04d932e96c9 667 }
screamer 0:c04d932e96c9 668 }
screamer 0:c04d932e96c9 669 };
screamer 0:c04d932e96c9 670
screamer 0:c04d932e96c9 671 #endif // SERVICES_ZIPBEACONCONFIGSERVICE_H_