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:
Mon Jul 20 04:29:07 2015 +0000
Revision:
14:5a2a104a21a8
Parent:
13:91c356fa928e
Child:
15:af8c24f34a9f
[[Testing Commit]]; ; Added button interrupt that will swap out the frame types.

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