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:
Thu Jul 23 06:48:50 2015 +0000
Revision:
21:f4646308f363
Parent:
20:3b133cfafc39
Child:
22:160766614338
[[Fix]] made RFU field in UID frame BigEndian Compliant (again, facepalm)

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