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:02:45 2015 +0000
Revision:
18:91c36071aafb
Parent:
17:0458759c40e4
Child:
19:f7c33fa88ca5
[[Debug]] Things are working now. Moved Radio callback to a ticker in the main.cpp file. I believe the issue i'm seeing is switching within an interrupt callback context.

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 11:73ea4ef7f5a4 231 defaultUidPower = power;
mbedAustin 11:73ea4ef7f5a4 232 memcpy(defaultUidNamespaceID, namespaceID, UID_NAMESPACEID_SIZE);
mbedAustin 11:73ea4ef7f5a4 233 memcpy(defaultUidInstanceID, instanceID, UID_INSTANCEID_SIZE);
mbedAustin 15:af8c24f34a9f 234 uidRFU = (uint16_t)RFU; // this is probably bad form, but it doesnt really matter yet.
mbedAustin 11:73ea4ef7f5a4 235 return;
mbedAustin 11:73ea4ef7f5a4 236 }
mbedAustin 15:af8c24f34a9f 237
mbedAustin 11:73ea4ef7f5a4 238 /*
mbedAustin 11:73ea4ef7f5a4 239 * Construct UID frame from private variables
mbedAustin 11:73ea4ef7f5a4 240 * @param[in/out] Data pointer to array to store constructed frame in
mbedAustin 11:73ea4ef7f5a4 241 * @param[in] maxSize number of bytes left in array, effectively how much emtpy space is available to write to
mbedAustin 15:af8c24f34a9f 242 * @return number of bytes used. negative number indicates error message.
mbedAustin 11:73ea4ef7f5a4 243 */
mbedAustin 15:af8c24f34a9f 244 int constructUIDFrame(uint8_t * Data, uint8_t maxSize) {
mbedAustin 17:0458759c40e4 245
mbedAustin 11:73ea4ef7f5a4 246 int index = 0;
mbedAustin 11:73ea4ef7f5a4 247 Data[index++] = FRAME_TYPE_UID; // 1B Type
mbedAustin 11:73ea4ef7f5a4 248 Data[index++] = defaultUidPower; // 1B Power @ 0meter
mbedAustin 15:af8c24f34a9f 249 for(int x = 0; x < UID_NAMESPACEID_SIZE; x++) { // 10B Namespce ID
mbedAustin 11:73ea4ef7f5a4 250 Data[index++] = defaultUidNamespaceID[x];
mbedAustin 15:af8c24f34a9f 251 }
mbedAustin 15:af8c24f34a9f 252 for(int x = 0; x< UID_INSTANCEID_SIZE; x++) { // 6B Instance ID
mbedAustin 11:73ea4ef7f5a4 253 Data[index++] = defaultUidInstanceID[x];
mbedAustin 15:af8c24f34a9f 254 }
mbedAustin 15:af8c24f34a9f 255 if(0x00 != uidRFU) { // 2B RFU, include if non-zero, otherwise ignore
mbedAustin 11:73ea4ef7f5a4 256 Data[index++] = (uint8_t)(uidRFU >> 8);
mbedAustin 11:73ea4ef7f5a4 257 Data[index++] = (uint8_t)uidRFU;
mbedAustin 11:73ea4ef7f5a4 258 }
mbedAustin 17:0458759c40e4 259 DBG("construcUIDFrame %d, %d",maxSize,index);
mbedAustin 11:73ea4ef7f5a4 260 return index;
mbedAustin 10:b5d19bcf23cf 261 }
mbedAustin 15:af8c24f34a9f 262
mbedAustin 10:b5d19bcf23cf 263 /*
mbedAustin 15:af8c24f34a9f 264 * Set Eddystone URL Frame information.
mbedAustin 10:b5d19bcf23cf 265 * @param[in] power TX Power in dB measured at 0 meters from the device.
mbedAustin 15:af8c24f34a9f 266 * @param url URL to encode
mbedAustin 10:b5d19bcf23cf 267 * @return false on success, true on failure.
mbedAustin 10:b5d19bcf23cf 268 */
mbedAustin 15:af8c24f34a9f 269 bool setURLFrameData(int8_t power, const char * url) {
mbedAustin 11:73ea4ef7f5a4 270 defaultUrlPower = power;
mbedAustin 11:73ea4ef7f5a4 271 encodeURI(url, defaultUriData, defaultUriDataLength); // encode URL to URL Formatting
mbedAustin 10:b5d19bcf23cf 272 if (defaultUriDataLength > URI_DATA_MAX) {
mbedAustin 10:b5d19bcf23cf 273 return true; // error, URL is too big
mbedAustin 10:b5d19bcf23cf 274 }
mbedAustin 11:73ea4ef7f5a4 275 return false;
mbedAustin 11:73ea4ef7f5a4 276 }
mbedAustin 15:af8c24f34a9f 277
mbedAustin 11:73ea4ef7f5a4 278 /*
mbedAustin 11:73ea4ef7f5a4 279 * Construct URL frame from private variables
mbedAustin 11:73ea4ef7f5a4 280 * @param[in/out] Data pointer to array to store constructed frame in
mbedAustin 11:73ea4ef7f5a4 281 * @param[in] maxSize number of bytes left in array, effectively how much emtpy space is available to write to
mbedAustin 15:af8c24f34a9f 282 * @return number of bytes used. negative number indicates error message.
mbedAustin 11:73ea4ef7f5a4 283 */
mbedAustin 15:af8c24f34a9f 284 int constructURLFrame(uint8_t * Data, uint8_t maxSize) {
mbedAustin 11:73ea4ef7f5a4 285 int index = 0;
mbedAustin 11:73ea4ef7f5a4 286 Data[index++] = FRAME_TYPE_URL; // 1B Type
mbedAustin 15:af8c24f34a9f 287 Data[index++] = params.txPowerMode; // 1B TX Power
mbedAustin 15:af8c24f34a9f 288 for(int x = 0; x < defaultUriDataLength; x++) { // 18B of URL Prefix + encoded URL
mbedAustin 11:73ea4ef7f5a4 289 Data[index++] = defaultUriData[x];
mbedAustin 11:73ea4ef7f5a4 290 }
mbedAustin 17:0458759c40e4 291 DBG("constructURLFrame: %d, %d",maxSize,index);
mbedAustin 15:af8c24f34a9f 292 return index;
mbedAustin 10:b5d19bcf23cf 293 }
mbedAustin 15:af8c24f34a9f 294
mbedAustin 10:b5d19bcf23cf 295 /*
mbedAustin 15:af8c24f34a9f 296 * Set Eddystone TLM Frame information.
mbedAustin 10:b5d19bcf23cf 297 * @param[in] Version of the TLM beacon data format
mbedAustin 10:b5d19bcf23cf 298 * @param batteryVoltage in milivolts
mbedAustin 10:b5d19bcf23cf 299 * @param beaconTemp in 8.8 floating point notation
mbedAustin 10:b5d19bcf23cf 300 *
mbedAustin 10:b5d19bcf23cf 301 */
mbedAustin 15:af8c24f34a9f 302 void setTLMFrameData(uint8_t version, uint16_t batteryVoltage, uint16_t beaconTemp, uint32_t pduCount = 0, uint32_t timeSinceBoot = 0) {
mbedAustin 10:b5d19bcf23cf 303 TlmVersion = version;
mbedAustin 10:b5d19bcf23cf 304 TlmBatteryVoltage = batteryVoltage;
mbedAustin 10:b5d19bcf23cf 305 TlmBeaconTemp = beaconTemp;
mbedAustin 10:b5d19bcf23cf 306 TlmPduCount = pduCount; // reset
mbedAustin 10:b5d19bcf23cf 307 TlmTimeSinceBoot = timeSinceBoot; // reset
mbedAustin 15:af8c24f34a9f 308 return;
mbedAustin 10:b5d19bcf23cf 309 }
mbedAustin 15:af8c24f34a9f 310
mbedAustin 11:73ea4ef7f5a4 311 /*
mbedAustin 11:73ea4ef7f5a4 312 * Construct TLM frame from private variables
mbedAustin 11:73ea4ef7f5a4 313 * @param[in/out] Data pointer to array to store constructed frame in
mbedAustin 11:73ea4ef7f5a4 314 * @param[in] maxSize number of bytes left in array, effectively how much emtpy space is available to write to
mbedAustin 15:af8c24f34a9f 315 * @return number of bytes used. negative number indicates error message.
mbedAustin 11:73ea4ef7f5a4 316 */
mbedAustin 15:af8c24f34a9f 317 int constructTLMFrame(uint8_t * Data, uint8_t maxSize) {
mbedAustin 11:73ea4ef7f5a4 318 int index = 0;
mbedAustin 11:73ea4ef7f5a4 319 Data[index++] = FRAME_TYPE_TLM; // Eddystone frame type = Telemetry
mbedAustin 11:73ea4ef7f5a4 320 Data[index++] = TlmVersion; // TLM Version Number
mbedAustin 11:73ea4ef7f5a4 321 Data[index++] = (uint8_t)(TlmBatteryVoltage>>0); // Battery Voltage[0]
mbedAustin 11:73ea4ef7f5a4 322 Data[index++] = (uint8_t)(TlmBatteryVoltage>>8); // Battery Voltage[1]
mbedAustin 11:73ea4ef7f5a4 323 Data[index++] = (uint8_t)(TlmBeaconTemp>>0); // Beacon Temp[0]
mbedAustin 11:73ea4ef7f5a4 324 Data[index++] = (uint8_t)(TlmBeaconTemp>>8); // Beacon Temp[1]
mbedAustin 11:73ea4ef7f5a4 325 Data[index++] = (uint8_t)(TlmPduCount>>0); // PDU Count [0]
mbedAustin 11:73ea4ef7f5a4 326 Data[index++] = (uint8_t)(TlmPduCount>>8); // PDU Count [1]
mbedAustin 11:73ea4ef7f5a4 327 Data[index++] = (uint8_t)(TlmPduCount>>16); // PDU Count [2]
mbedAustin 11:73ea4ef7f5a4 328 Data[index++] = (uint8_t)(TlmPduCount>>24); // PDU Count [3]
mbedAustin 11:73ea4ef7f5a4 329 Data[index++] = (uint8_t)(TlmTimeSinceBoot>>0); // Time Since Boot [0]
mbedAustin 11:73ea4ef7f5a4 330 Data[index++] = (uint8_t)(TlmTimeSinceBoot>>8); // Time Since Boot [1]
mbedAustin 11:73ea4ef7f5a4 331 Data[index++] = (uint8_t)(TlmTimeSinceBoot>>16); // Time Since Boot [2]
mbedAustin 11:73ea4ef7f5a4 332 Data[index++] = (uint8_t)(TlmTimeSinceBoot>>24); // Time Since Boot [3]
mbedAustin 17:0458759c40e4 333 DBG("constructURLFrame: %d, %d",maxSize,index);
mbedAustin 11:73ea4ef7f5a4 334 return index;
mbedAustin 11:73ea4ef7f5a4 335 }
mbedAustin 15:af8c24f34a9f 336
mbedAustin 12:ced5e837c511 337 /*
mbedAustin 12:ced5e837c511 338 * Update the TLM frame battery voltage value
mbedAustin 12:ced5e837c511 339 * @param[in] voltagemv Voltage to update the TLM field battery voltage with (in mV)
mbedAustin 12:ced5e837c511 340 * @return nothing
mbedAustin 12:ced5e837c511 341 */
mbedAustin 15:af8c24f34a9f 342 void updateTlmBatteryVoltage(uint16_t voltagemv) {
mbedAustin 12:ced5e837c511 343 TlmBatteryVoltage = voltagemv;
mbedAustin 15:af8c24f34a9f 344 return;
mbedAustin 12:ced5e837c511 345 }
mbedAustin 15:af8c24f34a9f 346
mbedAustin 12:ced5e837c511 347 /*
mbedAustin 15:af8c24f34a9f 348 * Update the TLM frame beacon temperature
mbedAustin 12:ced5e837c511 349 * @param[in] temp Temperature of beacon (in 8.8fpn)
mbedAustin 12:ced5e837c511 350 * @return nothing
mbedAustin 12:ced5e837c511 351 */
mbedAustin 15:af8c24f34a9f 352 void updateTlmBeaconTemp(uint16_t temp) {
mbedAustin 12:ced5e837c511 353 TlmBeaconTemp = temp;
mbedAustin 15:af8c24f34a9f 354 return;
mbedAustin 12:ced5e837c511 355 }
mbedAustin 15:af8c24f34a9f 356
mbedAustin 12:ced5e837c511 357 /*
mbedAustin 12:ced5e837c511 358 * Update the TLM frame PDU Count field
mbedAustin 12:ced5e837c511 359 * @param[in] pduCount Number of Advertisiting frames sent since powerup
mbedAustin 12:ced5e837c511 360 * @return nothing
mbedAustin 12:ced5e837c511 361 */
mbedAustin 15:af8c24f34a9f 362 void updateTlmPduCount(uint32_t pduCount) {
mbedAustin 12:ced5e837c511 363 TlmPduCount = pduCount;
mbedAustin 15:af8c24f34a9f 364 return;
mbedAustin 12:ced5e837c511 365 }
mbedAustin 15:af8c24f34a9f 366
mbedAustin 12:ced5e837c511 367 /*
mbedAustin 12:ced5e837c511 368 * Update the TLM frame Time since boot in 0.1s incriments
mbedAustin 12:ced5e837c511 369 * @param[in] timeSinceBoot Time since boot in 0.1s incriments
mbedAustin 12:ced5e837c511 370 * @return nothing
mbedAustin 12:ced5e837c511 371 */
mbedAustin 15:af8c24f34a9f 372 void updateTlmTimeSinceBoot(uint32_t timeSinceBoot) {
mbedAustin 12:ced5e837c511 373 TlmTimeSinceBoot = timeSinceBoot;
mbedAustin 15:af8c24f34a9f 374 return;
mbedAustin 12:ced5e837c511 375 }
mbedAustin 15:af8c24f34a9f 376
mbedAustin 14:5a2a104a21a8 377 /*
mbedAustin 16:a7d07ea94b31 378 * callback function, called every 0.1s, incriments the TimeSinceBoot field in the TLM frame
mbedAustin 16:a7d07ea94b31 379 * @return nothing
mbedAustin 16:a7d07ea94b31 380 */
mbedAustin 16:a7d07ea94b31 381 void tsbCallback(void) {
mbedAustin 16:a7d07ea94b31 382 TlmTimeSinceBoot++;
mbedAustin 16:a7d07ea94b31 383 }
mbedAustin 16:a7d07ea94b31 384
mbedAustin 16:a7d07ea94b31 385 /*
mbedAustin 16:a7d07ea94b31 386 * Update advertising data
mbedAustin 16:a7d07ea94b31 387 * @return true on success, false on failure
mbedAustin 16:a7d07ea94b31 388 */
mbedAustin 16:a7d07ea94b31 389 bool updateAdvPacket(uint8_t serviceData[], unsigned serviceDataLen) {
mbedAustin 16:a7d07ea94b31 390 // Fields from the Service
mbedAustin 17:0458759c40e4 391 DBG("Updating AdvFrame: %d", serviceDataLen);
mbedAustin 17:0458759c40e4 392 printf("\r\n");
mbedAustin 17:0458759c40e4 393 for(int x = 0; x<serviceDataLen; x++) {
mbedAustin 17:0458759c40e4 394 printf("%2.2x:",serviceData[x]);
mbedAustin 17:0458759c40e4 395 }
mbedAustin 17:0458759c40e4 396 printf("\r\n");
mbedAustin 16:a7d07ea94b31 397 ble.clearAdvertisingPayload();
mbedAustin 16:a7d07ea94b31 398 ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
mbedAustin 16:a7d07ea94b31 399 ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, BEACON_EDDYSTONE, sizeof(BEACON_EDDYSTONE));
mbedAustin 16:a7d07ea94b31 400 ble.accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, serviceData, serviceDataLen);
mbedAustin 16:a7d07ea94b31 401
mbedAustin 16:a7d07ea94b31 402 return true;
mbedAustin 16:a7d07ea94b31 403 }
mbedAustin 16:a7d07ea94b31 404
mbedAustin 16:a7d07ea94b31 405 /*
mbedAustin 14:5a2a104a21a8 406 * Callback from onRadioNotification(), used to update the PDUCounter and maybe other stuff eventually?
mbedAustin 14:5a2a104a21a8 407 *
mbedAustin 14:5a2a104a21a8 408 *
mbedAustin 14:5a2a104a21a8 409 */
mbedAustin 16:a7d07ea94b31 410 void radioNotificationCallback(bool radioActive) {
mbedAustin 17:0458759c40e4 411 DBG("RadioNotificationCallback : %d, %d, %d",radioActive,frameIndex,TlmPduCount);
mbedAustin 14:5a2a104a21a8 412 // Update Time and PDUCount
mbedAustin 16:a7d07ea94b31 413 // True just before an frame is sent, fale just after a frame is sent
mbedAustin 17:0458759c40e4 414
mbedAustin 17:0458759c40e4 415 TlmPduCount++;
mbedAustin 17:0458759c40e4 416 frameIndex = frameIndex % EDDYSTONE_MAX_FRAMETYPE;
mbedAustin 17:0458759c40e4 417 uint8_t serviceData[SERVICE_DATA_MAX];
mbedAustin 17:0458759c40e4 418 unsigned serviceDataLen = 0;
mbedAustin 17:0458759c40e4 419 //hard code in the eddystone UUID
mbedAustin 17:0458759c40e4 420 serviceData[serviceDataLen++] = BEACON_EDDYSTONE[0];
mbedAustin 17:0458759c40e4 421 serviceData[serviceDataLen++] = BEACON_EDDYSTONE[1];
mbedAustin 18:91c36071aafb 422 if(radioActive) {
mbedAustin 16:a7d07ea94b31 423 // Do Nothing
mbedAustin 16:a7d07ea94b31 424 } else {
mbedAustin 16:a7d07ea94b31 425 // state machine to control which packet is being sent
mbedAustin 15:af8c24f34a9f 426 switch(frameIndex) {
mbedAustin 14:5a2a104a21a8 427 case 0:
mbedAustin 17:0458759c40e4 428 serviceDataLen += constructTLMFrame(serviceData+serviceDataLen,20);
mbedAustin 17:0458759c40e4 429 DBG("\tconstructing TLM Frame: len= %d",serviceDataLen);
mbedAustin 16:a7d07ea94b31 430 updateAdvPacket(serviceData,serviceDataLen);
mbedAustin 16:a7d07ea94b31 431 frameIndex++;
mbedAustin 16:a7d07ea94b31 432 switchFlag = true;
mbedAustin 14:5a2a104a21a8 433 break;
mbedAustin 14:5a2a104a21a8 434 case 1:
mbedAustin 16:a7d07ea94b31 435 // switch out packets
mbedAustin 16:a7d07ea94b31 436 if(switchFlag) {
mbedAustin 17:0458759c40e4 437 serviceDataLen += constructURLFrame(serviceData+serviceDataLen,20);
mbedAustin 17:0458759c40e4 438 DBG("\tconstructing URL Frame: len = %d ",serviceDataLen);
mbedAustin 16:a7d07ea94b31 439 updateAdvPacket(serviceData,serviceDataLen);
mbedAustin 16:a7d07ea94b31 440 switchFlag = false;
mbedAustin 16:a7d07ea94b31 441 } else {
mbedAustin 16:a7d07ea94b31 442 if((TlmPduCount % 10) == 0) { // every 10 adv packets switch the frame
mbedAustin 16:a7d07ea94b31 443 switchFlag = true;
mbedAustin 16:a7d07ea94b31 444 frameIndex++;
mbedAustin 16:a7d07ea94b31 445 }
mbedAustin 16:a7d07ea94b31 446 }
mbedAustin 14:5a2a104a21a8 447 break;
mbedAustin 14:5a2a104a21a8 448 case 2:
mbedAustin 16:a7d07ea94b31 449 // switch out packets
mbedAustin 16:a7d07ea94b31 450 if(switchFlag) {
mbedAustin 17:0458759c40e4 451 serviceDataLen += constructUIDFrame(serviceData+serviceDataLen,20);
mbedAustin 17:0458759c40e4 452 DBG("\tconstructing UID Frame: len=%d",serviceDataLen);
mbedAustin 16:a7d07ea94b31 453 updateAdvPacket(serviceData,serviceDataLen);
mbedAustin 16:a7d07ea94b31 454 switchFlag = false;
mbedAustin 16:a7d07ea94b31 455 } else {
mbedAustin 16:a7d07ea94b31 456 if((TlmPduCount % 10) == 0) { // every 10 adv packets switch the frame
mbedAustin 16:a7d07ea94b31 457 switchFlag = true;
mbedAustin 16:a7d07ea94b31 458 frameIndex++;
mbedAustin 16:a7d07ea94b31 459 }
mbedAustin 16:a7d07ea94b31 460 }
mbedAustin 14:5a2a104a21a8 461 break;
mbedAustin 15:af8c24f34a9f 462 }
mbedAustin 14:5a2a104a21a8 463 }
mbedAustin 16:a7d07ea94b31 464
mbedAustin 15:af8c24f34a9f 465 return;
mbedAustin 14:5a2a104a21a8 466 }
screamer 0:c04d932e96c9 467
screamer 0:c04d932e96c9 468 /* Helper function to switch to the non-connectible normal mode for ZipBeacon. This gets called after a timeout. */
mbedAustin 15:af8c24f34a9f 469 void setupZipBeaconAdvertisements() {
mbedAustin 16:a7d07ea94b31 470 DBG("Switching Config -> adv");
screamer 0:c04d932e96c9 471 uint8_t serviceData[SERVICE_DATA_MAX];
screamer 0:c04d932e96c9 472 unsigned serviceDataLen = 0;
mbedAustin 16:a7d07ea94b31 473 unsigned beaconPeriod = params.beaconPeriod;
mbedAustin 16:a7d07ea94b31 474 unsigned txPowerMode = params.txPowerMode;
mbedAustin 16:a7d07ea94b31 475 unsigned uriDataLength = params.uriDataLength;
mbedAustin 16:a7d07ea94b31 476
mbedAustin 16:a7d07ea94b31 477 ZipBeaconConfigService::UriData_t &uriData = params.uriData;
mbedAustin 16:a7d07ea94b31 478 ZipBeaconConfigService::PowerLevels_t &advPowerLevels = params.advPowerLevels;
mbedAustin 16:a7d07ea94b31 479 uint8_t flags = params.flags;
mbedAustin 16:a7d07ea94b31 480
mbedAustin 14:5a2a104a21a8 481 // Initialize Frame transition
mbedAustin 17:0458759c40e4 482 frameIndex = 2;
mbedAustin 15:af8c24f34a9f 483 uidRFU = 0;
mbedAustin 17:0458759c40e4 484 switchFlag = true;
mbedAustin 17:0458759c40e4 485
screamer 0:c04d932e96c9 486
screamer 0:c04d932e96c9 487 /* Reinitialize the BLE stack. This will clear away the existing services and advertising state. */
screamer 0:c04d932e96c9 488 ble.shutdown();
screamer 0:c04d932e96c9 489 ble.init();
screamer 0:c04d932e96c9 490 ble.setTxPower(params.advPowerLevels[params.txPowerMode]);
screamer 0:c04d932e96c9 491 ble.setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
screamer 0:c04d932e96c9 492 ble.setAdvertisingInterval(beaconPeriod);
mbedAustin 16:a7d07ea94b31 493 extern void saveURIBeaconConfigParams(const Params_t *paramsP); /* forward declaration; necessary to avoid a circular dependency. */
mbedAustin 16:a7d07ea94b31 494 saveURIBeaconConfigParams(&params);
mbedAustin 15:af8c24f34a9f 495
mbedAustin 17:0458759c40e4 496 setTLMFrameData(0x00,0x2222,0x3333,0x01,0x02); // Initialize TLM Data, for testing, remove for release
mbedAustin 17:0458759c40e4 497 updateTlmPduCount(0x11);
mbedAustin 17:0458759c40e4 498 updateTlmTimeSinceBoot(0x22);
mbedAustin 15:af8c24f34a9f 499
mbedAustin 15:af8c24f34a9f 500 // Construct TLM Frame in initial advertising.
mbedAustin 14:5a2a104a21a8 501 serviceData[serviceDataLen++] = BEACON_EDDYSTONE[0];
mbedAustin 14:5a2a104a21a8 502 serviceData[serviceDataLen++] = BEACON_EDDYSTONE[1];
mbedAustin 17:0458759c40e4 503 serviceDataLen += constructURLFrame(serviceData+serviceDataLen,SERVICE_DATA_MAX);
mbedAustin 16:a7d07ea94b31 504
mbedAustin 16:a7d07ea94b31 505 updateAdvPacket(serviceData, serviceDataLen);
mbedAustin 17:0458759c40e4 506 ble.gap().startAdvertising();
mbedAustin 18:91c36071aafb 507 //ble.gap().onRadioNotification(this,&ZipBeaconConfigService::radioNotificationCallback);
mbedAustin 16:a7d07ea94b31 508 timeSinceBootTick.attach(this,&ZipBeaconConfigService::tsbCallback,0.1); // incriment the TimeSinceBoot ticker every 0.1s
screamer 0:c04d932e96c9 509 }
screamer 0:c04d932e96c9 510
mbedAustin 15:af8c24f34a9f 511 private:
mbedAustin 16:a7d07ea94b31 512 // True if the lock bits are non-zero
screamer 0:c04d932e96c9 513 bool isLocked() {
screamer 0:c04d932e96c9 514 Lock_t testLock;
screamer 0:c04d932e96c9 515 memset(testLock, 0, sizeof(Lock_t));
screamer 0:c04d932e96c9 516 return memcmp(params.lock, testLock, sizeof(Lock_t));
screamer 0:c04d932e96c9 517 }
screamer 0:c04d932e96c9 518
screamer 0:c04d932e96c9 519 /*
screamer 0:c04d932e96c9 520 * This callback is invoked when a GATT client attempts to modify any of the
screamer 0:c04d932e96c9 521 * characteristics of this service. Attempts to do so are also applied to
screamer 0:c04d932e96c9 522 * the internal state of this service object.
screamer 0:c04d932e96c9 523 */
rgrover1 7:e9800c45e065 524 void onDataWrittenCallback(const GattWriteCallbackParams *writeParams) {
rgrover1 7:e9800c45e065 525 uint16_t handle = writeParams->handle;
screamer 0:c04d932e96c9 526
screamer 0:c04d932e96c9 527 if (handle == lockChar.getValueHandle()) {
screamer 0:c04d932e96c9 528 // Validated earlier
screamer 0:c04d932e96c9 529 memcpy(params.lock, writeParams->data, sizeof(Lock_t));
screamer 0:c04d932e96c9 530 // use isLocked() in case bits are being set to all 0's
screamer 0:c04d932e96c9 531 lockedState = isLocked();
screamer 0:c04d932e96c9 532 } else if (handle == unlockChar.getValueHandle()) {
screamer 0:c04d932e96c9 533 // Validated earlier
screamer 0:c04d932e96c9 534 memset(params.lock, 0, sizeof(Lock_t));
screamer 0:c04d932e96c9 535 lockedState = false;
screamer 0:c04d932e96c9 536 } else if (handle == uriDataChar.getValueHandle()) {
screamer 0:c04d932e96c9 537 params.uriDataLength = writeParams->len;
screamer 0:c04d932e96c9 538 memcpy(params.uriData, writeParams->data, params.uriDataLength);
screamer 0:c04d932e96c9 539 } else if (handle == flagsChar.getValueHandle()) {
screamer 0:c04d932e96c9 540 params.flags = *(writeParams->data);
screamer 0:c04d932e96c9 541 } else if (handle == advPowerLevelsChar.getValueHandle()) {
screamer 0:c04d932e96c9 542 memcpy(params.advPowerLevels, writeParams->data, sizeof(PowerLevels_t));
screamer 0:c04d932e96c9 543 } else if (handle == txPowerModeChar.getValueHandle()) {
screamer 0:c04d932e96c9 544 params.txPowerMode = *(writeParams->data);
screamer 0:c04d932e96c9 545 } else if (handle == beaconPeriodChar.getValueHandle()) {
screamer 0:c04d932e96c9 546 params.beaconPeriod = *((uint16_t *)(writeParams->data));
rgrover1 4:4440953bde10 547
rgrover1 4:4440953bde10 548 /* Re-map beaconPeriod to within permissible bounds if necessary. */
rgrover1 4:4440953bde10 549 if (params.beaconPeriod != 0) {
rgrover1 4:4440953bde10 550 bool paramsUpdated = false;
rgrover1 4:4440953bde10 551 if (params.beaconPeriod < ble.getMinAdvertisingInterval()) {
rgrover1 4:4440953bde10 552 params.beaconPeriod = ble.getMinAdvertisingInterval();
rgrover1 4:4440953bde10 553 paramsUpdated = true;
rgrover1 4:4440953bde10 554 } else if (params.beaconPeriod > ble.getMaxAdvertisingInterval()) {
rgrover1 4:4440953bde10 555 params.beaconPeriod = ble.getMaxAdvertisingInterval();
rgrover1 4:4440953bde10 556 paramsUpdated = true;
rgrover1 4:4440953bde10 557 }
rgrover1 4:4440953bde10 558 if (paramsUpdated) {
rgrover1 4:4440953bde10 559 ble.updateCharacteristicValue(beaconPeriodChar.getValueHandle(), reinterpret_cast<uint8_t *>(&params.beaconPeriod), sizeof(uint16_t));
rgrover1 4:4440953bde10 560 }
rgrover1 4:4440953bde10 561 }
screamer 0:c04d932e96c9 562 } else if (handle == resetChar.getValueHandle()) {
screamer 0:c04d932e96c9 563 resetToDefaults();
screamer 0:c04d932e96c9 564 }
screamer 0:c04d932e96c9 565 }
screamer 0:c04d932e96c9 566
screamer 0:c04d932e96c9 567 /*
screamer 0:c04d932e96c9 568 * Reset the default values.
screamer 0:c04d932e96c9 569 */
screamer 0:c04d932e96c9 570 void resetToDefaults(void) {
screamer 0:c04d932e96c9 571 lockedState = false;
screamer 0:c04d932e96c9 572 memset(params.lock, 0, sizeof(Lock_t));
screamer 0:c04d932e96c9 573 memcpy(params.uriData, defaultUriData, URI_DATA_MAX);
screamer 0:c04d932e96c9 574 params.uriDataLength = defaultUriDataLength;
screamer 0:c04d932e96c9 575 params.flags = 0;
screamer 0:c04d932e96c9 576 memcpy(params.advPowerLevels, defaultAdvPowerLevels, sizeof(PowerLevels_t));
screamer 0:c04d932e96c9 577 params.txPowerMode = TX_POWER_MODE_LOW;
screamer 0:c04d932e96c9 578 params.beaconPeriod = 1000;
mbedAustin 10:b5d19bcf23cf 579 memcpy(params.uidNamespaceID, defaultUidNamespaceID, UID_NAMESPACEID_SIZE);
mbedAustin 10:b5d19bcf23cf 580 memcpy(params.uidInstanceID, defaultUidInstanceID, UID_INSTANCEID_SIZE);
mbedAustin 12:ced5e837c511 581 params.tlmVersion = 0;
screamer 0:c04d932e96c9 582 updateCharacteristicValues();
screamer 0:c04d932e96c9 583 }
screamer 0:c04d932e96c9 584
screamer 0:c04d932e96c9 585 /*
screamer 0:c04d932e96c9 586 * Internal helper function used to update the GATT database following any
screamer 0:c04d932e96c9 587 * change to the internal state of the service object.
screamer 0:c04d932e96c9 588 */
screamer 0:c04d932e96c9 589 void updateCharacteristicValues(void) {
screamer 0:c04d932e96c9 590 ble.updateCharacteristicValue(lockedStateChar.getValueHandle(), &lockedState, 1);
screamer 0:c04d932e96c9 591 ble.updateCharacteristicValue(uriDataChar.getValueHandle(), params.uriData, params.uriDataLength);
screamer 0:c04d932e96c9 592 ble.updateCharacteristicValue(flagsChar.getValueHandle(), &params.flags, 1);
screamer 0:c04d932e96c9 593 ble.updateCharacteristicValue(beaconPeriodChar.getValueHandle(),
screamer 0:c04d932e96c9 594 reinterpret_cast<uint8_t *>(&params.beaconPeriod), sizeof(uint16_t));
screamer 0:c04d932e96c9 595 ble.updateCharacteristicValue(txPowerModeChar.getValueHandle(), &params.txPowerMode, 1);
screamer 0:c04d932e96c9 596 ble.updateCharacteristicValue(advPowerLevelsChar.getValueHandle(),
screamer 0:c04d932e96c9 597 reinterpret_cast<uint8_t *>(params.advPowerLevels), sizeof(PowerLevels_t));
screamer 0:c04d932e96c9 598 }
screamer 0:c04d932e96c9 599
mbedAustin 15:af8c24f34a9f 600 private:
rgrover1 7:e9800c45e065 601 void lockAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
screamer 0:c04d932e96c9 602 if (lockedState) {
screamer 0:c04d932e96c9 603 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
screamer 0:c04d932e96c9 604 } else if (authParams->len != sizeof(Lock_t)) {
screamer 0:c04d932e96c9 605 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
screamer 0:c04d932e96c9 606 } else if (authParams->offset != 0) {
screamer 0:c04d932e96c9 607 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
screamer 0:c04d932e96c9 608 } else {
screamer 0:c04d932e96c9 609 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
screamer 0:c04d932e96c9 610 }
screamer 0:c04d932e96c9 611 }
screamer 0:c04d932e96c9 612
screamer 0:c04d932e96c9 613
rgrover1 7:e9800c45e065 614 void unlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
screamer 0:c04d932e96c9 615 if (!lockedState) {
screamer 0:c04d932e96c9 616 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
screamer 0:c04d932e96c9 617 } else if (authParams->len != sizeof(Lock_t)) {
screamer 0:c04d932e96c9 618 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
screamer 0:c04d932e96c9 619 } else if (authParams->offset != 0) {
screamer 0:c04d932e96c9 620 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
screamer 0:c04d932e96c9 621 } else if (memcmp(authParams->data, params.lock, sizeof(Lock_t)) != 0) {
screamer 0:c04d932e96c9 622 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
screamer 0:c04d932e96c9 623 } else {
screamer 0:c04d932e96c9 624 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
screamer 0:c04d932e96c9 625 }
screamer 0:c04d932e96c9 626 }
screamer 0:c04d932e96c9 627
rgrover1 7:e9800c45e065 628 void uriDataWriteAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
screamer 0:c04d932e96c9 629 if (lockedState) {
screamer 0:c04d932e96c9 630 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
screamer 0:c04d932e96c9 631 } else if (authParams->offset != 0) {
screamer 0:c04d932e96c9 632 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
screamer 0:c04d932e96c9 633 } else {
screamer 0:c04d932e96c9 634 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
screamer 0:c04d932e96c9 635 }
screamer 0:c04d932e96c9 636 }
screamer 0:c04d932e96c9 637
rgrover1 7:e9800c45e065 638 void powerModeAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
screamer 0:c04d932e96c9 639 if (lockedState) {
screamer 0:c04d932e96c9 640 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
screamer 0:c04d932e96c9 641 } else if (authParams->len != sizeof(uint8_t)) {
screamer 0:c04d932e96c9 642 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
screamer 0:c04d932e96c9 643 } else if (authParams->offset != 0) {
screamer 0:c04d932e96c9 644 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
screamer 0:c04d932e96c9 645 } else if (*((uint8_t *)authParams->data) >= NUM_POWER_MODES) {
screamer 0:c04d932e96c9 646 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
screamer 0:c04d932e96c9 647 } else {
screamer 0:c04d932e96c9 648 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
screamer 0:c04d932e96c9 649 }
screamer 0:c04d932e96c9 650 }
screamer 0:c04d932e96c9 651
screamer 0:c04d932e96c9 652 template <typename T>
rgrover1 7:e9800c45e065 653 void basicAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
screamer 0:c04d932e96c9 654 if (lockedState) {
screamer 0:c04d932e96c9 655 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
screamer 0:c04d932e96c9 656 } else if (authParams->len != sizeof(T)) {
screamer 0:c04d932e96c9 657 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
screamer 0:c04d932e96c9 658 } else if (authParams->offset != 0) {
screamer 0:c04d932e96c9 659 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
screamer 0:c04d932e96c9 660 } else {
screamer 0:c04d932e96c9 661 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
screamer 0:c04d932e96c9 662 }
screamer 0:c04d932e96c9 663 }
screamer 0:c04d932e96c9 664
mbedAustin 10:b5d19bcf23cf 665 BLEDevice &ble;
mbedAustin 10:b5d19bcf23cf 666 Params_t &params;
mbedAustin 14:5a2a104a21a8 667 Ticker callbackTick;
mbedAustin 16:a7d07ea94b31 668 Ticker timeSinceBootTick;
mbedAustin 16:a7d07ea94b31 669 // Default value that is restored on reset
mbedAustin 10:b5d19bcf23cf 670 size_t defaultUriDataLength;
mbedAustin 10:b5d19bcf23cf 671 UriData_t defaultUriData;
mbedAustin 10:b5d19bcf23cf 672 UIDNamespaceID_t defaultUidNamespaceID;
mbedAustin 10:b5d19bcf23cf 673 UIDInstanceID_t defaultUidInstanceID;
mbedAustin 11:73ea4ef7f5a4 674 int8_t defaultUidPower;
mbedAustin 11:73ea4ef7f5a4 675 int8_t defaultUrlPower;
mbedAustin 10:b5d19bcf23cf 676 uint16_t uidRFU;
mbedAustin 16:a7d07ea94b31 677 // Default value that is restored on reset
mbedAustin 10:b5d19bcf23cf 678 PowerLevels_t &defaultAdvPowerLevels;
mbedAustin 10:b5d19bcf23cf 679 uint8_t lockedState;
mbedAustin 10:b5d19bcf23cf 680 bool initSucceeded;
mbedAustin 10:b5d19bcf23cf 681 uint8_t resetFlag;
mbedAustin 17:0458759c40e4 682 bool switchFlag;
mbedAustin 15:af8c24f34a9f 683
mbedAustin 16:a7d07ea94b31 684 // Private Variables for Telemetry Data
mbedAustin 13:91c356fa928e 685 uint8_t TlmVersion;
mbedAustin 13:91c356fa928e 686 volatile uint16_t TlmBatteryVoltage;
mbedAustin 13:91c356fa928e 687 volatile uint16_t TlmBeaconTemp;
mbedAustin 13:91c356fa928e 688 volatile uint32_t TlmPduCount;
mbedAustin 13:91c356fa928e 689 volatile uint32_t TlmTimeSinceBoot;
screamer 0:c04d932e96c9 690
screamer 0:c04d932e96c9 691 ReadOnlyGattCharacteristic<uint8_t> lockedStateChar;
screamer 0:c04d932e96c9 692 WriteOnlyGattCharacteristic<Lock_t> lockChar;
screamer 0:c04d932e96c9 693 GattCharacteristic uriDataChar;
screamer 0:c04d932e96c9 694 WriteOnlyGattCharacteristic<Lock_t> unlockChar;
screamer 0:c04d932e96c9 695 ReadWriteGattCharacteristic<uint8_t> flagsChar;
screamer 0:c04d932e96c9 696 ReadWriteGattCharacteristic<PowerLevels_t> advPowerLevelsChar;
screamer 0:c04d932e96c9 697 ReadWriteGattCharacteristic<uint8_t> txPowerModeChar;
screamer 0:c04d932e96c9 698 ReadWriteGattCharacteristic<uint16_t> beaconPeriodChar;
screamer 0:c04d932e96c9 699 WriteOnlyGattCharacteristic<uint8_t> resetChar;
screamer 0:c04d932e96c9 700
mbedAustin 15:af8c24f34a9f 701 public:
screamer 0:c04d932e96c9 702 /*
screamer 0:c04d932e96c9 703 * Encode a human-readable URI into the binary format defined by URIBeacon spec (https://github.com/google/uribeacon/tree/master/specification).
screamer 0:c04d932e96c9 704 */
screamer 0:c04d932e96c9 705 static void encodeURI(const char *uriDataIn, UriData_t uriDataOut, size_t &sizeofURIDataOut) {
screamer 0:c04d932e96c9 706 const char *prefixes[] = {
screamer 0:c04d932e96c9 707 "http://www.",
screamer 0:c04d932e96c9 708 "https://www.",
screamer 0:c04d932e96c9 709 "http://",
screamer 0:c04d932e96c9 710 "https://",
screamer 0:c04d932e96c9 711 };
screamer 0:c04d932e96c9 712 const size_t NUM_PREFIXES = sizeof(prefixes) / sizeof(char *);
screamer 0:c04d932e96c9 713 const char *suffixes[] = {
screamer 0:c04d932e96c9 714 ".com/",
screamer 0:c04d932e96c9 715 ".org/",
screamer 0:c04d932e96c9 716 ".edu/",
screamer 0:c04d932e96c9 717 ".net/",
screamer 0:c04d932e96c9 718 ".info/",
screamer 0:c04d932e96c9 719 ".biz/",
screamer 0:c04d932e96c9 720 ".gov/",
screamer 0:c04d932e96c9 721 ".com",
screamer 0:c04d932e96c9 722 ".org",
screamer 0:c04d932e96c9 723 ".edu",
screamer 0:c04d932e96c9 724 ".net",
screamer 0:c04d932e96c9 725 ".info",
screamer 0:c04d932e96c9 726 ".biz",
screamer 0:c04d932e96c9 727 ".gov"
screamer 0:c04d932e96c9 728 };
screamer 0:c04d932e96c9 729 const size_t NUM_SUFFIXES = sizeof(suffixes) / sizeof(char *);
screamer 0:c04d932e96c9 730
screamer 0:c04d932e96c9 731 sizeofURIDataOut = 0;
screamer 0:c04d932e96c9 732 memset(uriDataOut, 0, sizeof(UriData_t));
screamer 0:c04d932e96c9 733
screamer 0:c04d932e96c9 734 if ((uriDataIn == NULL) || (strlen(uriDataIn) == 0)) {
screamer 0:c04d932e96c9 735 return;
screamer 0:c04d932e96c9 736 }
screamer 0:c04d932e96c9 737
screamer 0:c04d932e96c9 738 /*
screamer 0:c04d932e96c9 739 * handle prefix
screamer 0:c04d932e96c9 740 */
screamer 0:c04d932e96c9 741 for (unsigned i = 0; i < NUM_PREFIXES; i++) {
screamer 0:c04d932e96c9 742 size_t prefixLen = strlen(prefixes[i]);
screamer 0:c04d932e96c9 743 if (strncmp(uriDataIn, prefixes[i], prefixLen) == 0) {
screamer 0:c04d932e96c9 744 uriDataOut[sizeofURIDataOut++] = i;
screamer 0:c04d932e96c9 745 uriDataIn += prefixLen;
screamer 0:c04d932e96c9 746 break;
screamer 0:c04d932e96c9 747 }
screamer 0:c04d932e96c9 748 }
screamer 0:c04d932e96c9 749
screamer 0:c04d932e96c9 750 /*
screamer 0:c04d932e96c9 751 * handle suffixes
screamer 0:c04d932e96c9 752 */
screamer 0:c04d932e96c9 753 while (*uriDataIn && (sizeofURIDataOut < URI_DATA_MAX)) {
screamer 0:c04d932e96c9 754 /* check for suffix match */
screamer 0:c04d932e96c9 755 unsigned i;
screamer 0:c04d932e96c9 756 for (i = 0; i < NUM_SUFFIXES; i++) {
screamer 0:c04d932e96c9 757 size_t suffixLen = strlen(suffixes[i]);
screamer 0:c04d932e96c9 758 if (strncmp(uriDataIn, suffixes[i], suffixLen) == 0) {
screamer 0:c04d932e96c9 759 uriDataOut[sizeofURIDataOut++] = i;
screamer 0:c04d932e96c9 760 uriDataIn += suffixLen;
screamer 0:c04d932e96c9 761 break; /* from the for loop for checking against suffixes */
screamer 0:c04d932e96c9 762 }
screamer 0:c04d932e96c9 763 }
screamer 0:c04d932e96c9 764 /* This is the default case where we've got an ordinary character which doesn't match a suffix. */
screamer 0:c04d932e96c9 765 if (i == NUM_SUFFIXES) {
screamer 0:c04d932e96c9 766 uriDataOut[sizeofURIDataOut++] = *uriDataIn;
screamer 0:c04d932e96c9 767 ++uriDataIn;
screamer 0:c04d932e96c9 768 }
screamer 0:c04d932e96c9 769 }
screamer 0:c04d932e96c9 770 }
screamer 0:c04d932e96c9 771 };
screamer 0:c04d932e96c9 772
screamer 0:c04d932e96c9 773 #endif // SERVICES_ZIPBEACONCONFIGSERVICE_H_