Updated

Fork of BLE_API by Bluetooth Low Energy

Committer:
rgrover1
Date:
Tue Aug 11 15:13:13 2015 +0100
Revision:
784:1e817a72a166
Parent:
783:9959291e9a3f
Child:
785:c74f567162d4
Synchronized with git rev 3e2ef66d
Author: Rohit Grover
Merge branch 'eddystoneUpdate' of https://github.com/BlackstoneEngineering/BLE_API into BlackstoneEngineering-eddystoneUpdate

Who changed what in which revision?

UserRevisionLine numberNew contents of line
rgrover1 771:26b809199308 1 /* mbed Microcontroller Library
rgrover1 771:26b809199308 2 * Copyright (c) 2006-2015 ARM Limited
rgrover1 771:26b809199308 3 *
rgrover1 771:26b809199308 4 * Licensed under the Apache License, Version 2.0 (the "License");
rgrover1 771:26b809199308 5 * you may not use this file except in compliance with the License.
rgrover1 771:26b809199308 6 * You may obtain a copy of the License at
rgrover1 771:26b809199308 7 *
rgrover1 771:26b809199308 8 * http://www.apache.org/licenses/LICENSE-2.0
rgrover1 771:26b809199308 9 *
rgrover1 771:26b809199308 10 * Unless required by applicable law or agreed to in writing, software
rgrover1 771:26b809199308 11 * distributed under the License is distributed on an "AS IS" BASIS,
rgrover1 771:26b809199308 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rgrover1 771:26b809199308 13 * See the License for the specific language governing permissions and
rgrover1 771:26b809199308 14 * limitations under the License.
rgrover1 771:26b809199308 15 */
rgrover1 771:26b809199308 16
rgrover1 771:26b809199308 17 #ifndef SERVICES_EDDYSTONEBEACON_H_
rgrover1 771:26b809199308 18 #define SERVICES_EDDYSTONEBEACON_H_
rgrover1 771:26b809199308 19
rgrover1 771:26b809199308 20 #include "ble/BLE.h"
rgrover1 771:26b809199308 21 #include "mbed.h"
rgrover1 771:26b809199308 22
rgrover1 771:26b809199308 23 static const uint8_t BEACON_EDDYSTONE[] = {0xAA, 0xFE};
rgrover1 771:26b809199308 24
rgrover1 784:1e817a72a166 25 //Debug is disabled by default
rgrover1 771:26b809199308 26 #if 0
rgrover1 784:1e817a72a166 27 #define DBG(x, ...) printf("[EddyStone: DBG]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__);
rgrover1 784:1e817a72a166 28 #define WARN(x, ...) printf("[EddyStone: WARN]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__);
rgrover1 784:1e817a72a166 29 #define ERR(x, ...) printf("[EddyStone: ERR]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__);
rgrover1 771:26b809199308 30 #else
rgrover1 784:1e817a72a166 31 #define DBG(x, ...) //wait_us(10);
rgrover1 784:1e817a72a166 32 #define WARN(x, ...) //wait_us(10);
rgrover1 784:1e817a72a166 33 #define ERR(x, ...)
rgrover1 784:1e817a72a166 34 #endif
rgrover1 784:1e817a72a166 35
rgrover1 784:1e817a72a166 36 #if 0
rgrover1 784:1e817a72a166 37 #define INFO(x, ...) printf("[EddyStone: INFO]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__);
rgrover1 784:1e817a72a166 38 #else
rgrover1 784:1e817a72a166 39 #define INFO(x, ...)
rgrover1 784:1e817a72a166 40 #endif
rgrover1 771:26b809199308 41
rgrover1 771:26b809199308 42 /**
rgrover1 771:26b809199308 43 * @class Eddystone
rgrover1 771:26b809199308 44 * @brief Eddystone Configuration Service. Can be used to set URL, adjust power levels, and set flags.
rgrover1 771:26b809199308 45 * See https://github.com/google/eddystone
rgrover1 771:26b809199308 46 *
rgrover1 771:26b809199308 47 */
rgrover1 771:26b809199308 48 class EddystoneService
rgrover1 771:26b809199308 49 {
rgrover1 771:26b809199308 50 public:
rgrover1 784:1e817a72a166 51 /**
rgrover1 784:1e817a72a166 52 * @brief Transmission Power Modes for UriBeacon
rgrover1 784:1e817a72a166 53 */
rgrover1 784:1e817a72a166 54
rgrover1 784:1e817a72a166 55 static const int ADVERTISING_INTERVAL_MSEC = 1000; // Advertising interval for config service.
rgrover1 784:1e817a72a166 56 static const int SERVICE_DATA_MAX = 31; // Maximum size of service data in ADV packets
rgrover1 771:26b809199308 57
rgrover1 784:1e817a72a166 58 // There are currently 3 subframes defined, URI, UID, and TLM
rgrover1 784:1e817a72a166 59 #define EDDYSTONE_MAX_FRAMETYPE 3
rgrover1 784:1e817a72a166 60 void (*frames[EDDYSTONE_MAX_FRAMETYPE])(uint8_t *, uint32_t);
rgrover1 784:1e817a72a166 61 uint8_t frameIndex;
rgrover1 784:1e817a72a166 62 static const int URI_DATA_MAX = 18;
rgrover1 784:1e817a72a166 63 typedef uint8_t UriData_t[URI_DATA_MAX];
rgrover1 771:26b809199308 64
rgrover1 771:26b809199308 65 // UID Frame Type subfields
rgrover1 784:1e817a72a166 66 static const int UID_NAMESPACEID_SIZE = 10;
rgrover1 784:1e817a72a166 67 typedef uint8_t UIDNamespaceID_t[UID_NAMESPACEID_SIZE];
rgrover1 784:1e817a72a166 68 static const int UID_INSTANCEID_SIZE = 6;
rgrover1 784:1e817a72a166 69 typedef uint8_t UIDInstanceID_t[UID_INSTANCEID_SIZE];
rgrover1 771:26b809199308 70
rgrover1 771:26b809199308 71 // Eddystone Frame Type ID
rgrover1 784:1e817a72a166 72 static const uint8_t FRAME_TYPE_UID = 0x00;
rgrover1 784:1e817a72a166 73 static const uint8_t FRAME_TYPE_URL = 0x10;
rgrover1 784:1e817a72a166 74 static const uint8_t FRAME_TYPE_TLM = 0x20;
rgrover1 783:9959291e9a3f 75
rgrover1 784:1e817a72a166 76 static const uint8_t FRAME_SIZE_TLM = 14; // TLM frame is a constant 14Bytes
rgrover1 783:9959291e9a3f 77 static const uint8_t FRAME_SIZE_UID = 20; // includes RFU bytes
rgrover1 771:26b809199308 78
rgrover1 784:1e817a72a166 79 /*
rgrover1 784:1e817a72a166 80 * Set Eddystone UID Frame information.
rgrover1 784:1e817a72a166 81 * @param[in] power TX Power in dB measured at 0 meters from the device. Range of -100 to +20 dB.
rgrover1 784:1e817a72a166 82 * @param namespaceID 10B namespace ID
rgrover1 784:1e817a72a166 83 * @param instanceID 6B instance ID
rgrover1 784:1e817a72a166 84 * @param RFU 2B of RFU, initialized to 0x0000 and not broadcast, included for future reference.
rgrover1 784:1e817a72a166 85 *
rgrover1 784:1e817a72a166 86 */
rgrover1 784:1e817a72a166 87 void setUIDFrameData(int8_t power, UIDNamespaceID_t namespaceID, UIDInstanceID_t instanceID, uint16_t RFU = 0x0000) {
rgrover1 784:1e817a72a166 88 if(power > 20) {
rgrover1 784:1e817a72a166 89 power = 20;
rgrover1 783:9959291e9a3f 90 }
rgrover1 784:1e817a72a166 91 if(power < -100) {
rgrover1 784:1e817a72a166 92 power = -100;
rgrover1 784:1e817a72a166 93 }
rgrover1 784:1e817a72a166 94 defaultUidPower = power;
rgrover1 784:1e817a72a166 95 memcpy(defaultUidNamespaceID, namespaceID, UID_NAMESPACEID_SIZE);
rgrover1 784:1e817a72a166 96 memcpy(defaultUidInstanceID, instanceID, UID_INSTANCEID_SIZE);
rgrover1 784:1e817a72a166 97 uidRFU = (uint16_t)RFU; // this is probably bad form, but it doesnt really matter yet.
rgrover1 784:1e817a72a166 98 return;
rgrover1 783:9959291e9a3f 99 }
rgrover1 771:26b809199308 100
rgrover1 782:5736312322d9 101 /*
rgrover1 784:1e817a72a166 102 * Construct UID frame from private variables
rgrover1 784:1e817a72a166 103 * @param[in/out] Data pointer to array to store constructed frame in
rgrover1 784:1e817a72a166 104 * @param[in] maxSize number of bytes left in array, effectively how much emtpy space is available to write to
rgrover1 784:1e817a72a166 105 * @return number of bytes used. negative number indicates error message.
rgrover1 784:1e817a72a166 106 */
rgrover1 784:1e817a72a166 107 int constructUIDFrame(uint8_t * Data, uint8_t maxSize) {
rgrover1 783:9959291e9a3f 108
rgrover1 784:1e817a72a166 109 int index = 0;
rgrover1 784:1e817a72a166 110 Data[index++] = FRAME_TYPE_UID; // 1B Type
rgrover1 784:1e817a72a166 111 if(defaultUidPower > 20) {
rgrover1 784:1e817a72a166 112 defaultUidPower = 20; // enforce range of vaild values.
rgrover1 782:5736312322d9 113 }
rgrover1 784:1e817a72a166 114 if(defaultUidPower < -100) {
rgrover1 771:26b809199308 115 defaultUidPower = -100;
rgrover1 771:26b809199308 116 }
rgrover1 784:1e817a72a166 117 Data[index++] = defaultUidPower; // 1B Power @ 0meter
rgrover1 784:1e817a72a166 118 for(int x = 0; x < UID_NAMESPACEID_SIZE; x++) { // 10B Namespce ID
rgrover1 784:1e817a72a166 119 Data[index++] = defaultUidNamespaceID[x];
rgrover1 771:26b809199308 120 }
rgrover1 784:1e817a72a166 121 for(int x = 0; x< UID_INSTANCEID_SIZE; x++) { // 6B Instance ID
rgrover1 784:1e817a72a166 122 Data[index++] = defaultUidInstanceID[x];
rgrover1 771:26b809199308 123 }
rgrover1 784:1e817a72a166 124 if(0 != uidRFU) { // 2B RFU, include if non-zero, otherwise ignore
rgrover1 784:1e817a72a166 125 Data[index++] = (uint8_t)(uidRFU >> 0);
rgrover1 784:1e817a72a166 126 Data[index++] = (uint8_t)(uidRFU >> 8);
rgrover1 771:26b809199308 127 }
rgrover1 784:1e817a72a166 128 DBG("construcUIDFrame %d, %d",maxSize,index);
rgrover1 771:26b809199308 129 return index;
rgrover1 771:26b809199308 130 }
rgrover1 771:26b809199308 131
rgrover1 771:26b809199308 132 /*
rgrover1 784:1e817a72a166 133 * Set Eddystone URL Frame information.
rgrover1 784:1e817a72a166 134 * @param[in] power TX Power in dB measured at 0 meters from the device.
rgrover1 784:1e817a72a166 135 * @param url URL to encode
rgrover1 784:1e817a72a166 136 * @return false on success, true on failure.
rgrover1 784:1e817a72a166 137 */
rgrover1 784:1e817a72a166 138 bool setURLFrameData(int8_t power, const char * url) {
rgrover1 771:26b809199308 139 defaultUrlPower = power;
rgrover1 771:26b809199308 140 encodeURL(url, defaultUriData, defaultUriDataLength); // encode URL to URL Formatting
rgrover1 771:26b809199308 141 if (defaultUriDataLength > URI_DATA_MAX) {
rgrover1 771:26b809199308 142 return true; // error, URL is too big
rgrover1 771:26b809199308 143 }
rgrover1 771:26b809199308 144 return false;
rgrover1 771:26b809199308 145 }
rgrover1 771:26b809199308 146
rgrover1 784:1e817a72a166 147 /*
rgrover1 784:1e817a72a166 148 * Construct URL frame from private variables
rgrover1 784:1e817a72a166 149 * @param[in/out] Data pointer to array to store constructed frame in
rgrover1 784:1e817a72a166 150 * @param[in] maxSize number of bytes left in array, effectively how much emtpy space is available to write to
rgrover1 784:1e817a72a166 151 * @return number of bytes used. negative number indicates error message.
rgrover1 784:1e817a72a166 152 */
rgrover1 784:1e817a72a166 153 int constructURLFrame(uint8_t * Data, uint8_t maxSize) {
rgrover1 784:1e817a72a166 154 int index = 0;
rgrover1 784:1e817a72a166 155 Data[index++] = FRAME_TYPE_URL; // 1B Type
rgrover1 784:1e817a72a166 156 Data[index++] = defaultUrlPower; // 1B TX Power
rgrover1 784:1e817a72a166 157 for(int x = 0; x < defaultUriDataLength; x++) { // 18B of URL Prefix + encoded URL
rgrover1 784:1e817a72a166 158 Data[index++] = defaultUriData[x];
rgrover1 771:26b809199308 159 }
rgrover1 784:1e817a72a166 160 DBG("constructURLFrame: %d, %d",maxSize,index);
rgrover1 784:1e817a72a166 161 return index;
rgrover1 784:1e817a72a166 162 }
rgrover1 771:26b809199308 163
rgrover1 784:1e817a72a166 164 /*
rgrover1 784:1e817a72a166 165 * Set Eddystone TLM Frame information.
rgrover1 784:1e817a72a166 166 * @param[in] Version of the TLM beacon data format
rgrover1 784:1e817a72a166 167 * @param batteryVoltage in milivolts
rgrover1 784:1e817a72a166 168 * @param beaconTemp in 8.8 floating point notation
rgrover1 784:1e817a72a166 169 *
rgrover1 784:1e817a72a166 170 */
rgrover1 784:1e817a72a166 171 void setTLMFrameData(uint8_t version, uint16_t batteryVoltage, uint16_t beaconTemp, uint32_t pduCount = 0, uint32_t timeSinceBoot = 0) {
rgrover1 784:1e817a72a166 172 TlmVersion = version;
rgrover1 784:1e817a72a166 173 TlmBatteryVoltage = batteryVoltage;
rgrover1 784:1e817a72a166 174 TlmBeaconTemp = beaconTemp;
rgrover1 784:1e817a72a166 175 TlmPduCount = pduCount; // reset
rgrover1 784:1e817a72a166 176 TlmTimeSinceBoot = timeSinceBoot; // reset
rgrover1 784:1e817a72a166 177 return;
rgrover1 784:1e817a72a166 178 }
rgrover1 771:26b809199308 179
rgrover1 784:1e817a72a166 180 /*
rgrover1 784:1e817a72a166 181 * Construct TLM frame from private variables
rgrover1 784:1e817a72a166 182 * @param[in/out] Data pointer to array to store constructed frame in
rgrover1 784:1e817a72a166 183 * @param[in] maxSize number of bytes left in array, effectively how much emtpy space is available to write to
rgrover1 784:1e817a72a166 184 * @return number of bytes used. negative number indicates error message.
rgrover1 784:1e817a72a166 185 */
rgrover1 784:1e817a72a166 186 int constructTLMFrame(uint8_t * Data, uint8_t maxSize) {
rgrover1 784:1e817a72a166 187 int index = 0;
rgrover1 784:1e817a72a166 188 Data[index++] = FRAME_TYPE_TLM; // Eddystone frame type = Telemetry
rgrover1 784:1e817a72a166 189 Data[index++] = TlmVersion; // TLM Version Number
rgrover1 784:1e817a72a166 190 Data[index++] = (uint8_t)(TlmBatteryVoltage>>8); // Battery Voltage[0]
rgrover1 784:1e817a72a166 191 Data[index++] = (uint8_t)(TlmBatteryVoltage>>0); // Battery Voltage[1]
rgrover1 784:1e817a72a166 192 Data[index++] = (uint8_t)(TlmBeaconTemp>>8); // Beacon Temp[0]
rgrover1 784:1e817a72a166 193 Data[index++] = (uint8_t)(TlmBeaconTemp>>0); // Beacon Temp[1]
rgrover1 784:1e817a72a166 194 Data[index++] = (uint8_t)(TlmPduCount>>24); // PDU Count [0]
rgrover1 784:1e817a72a166 195 Data[index++] = (uint8_t)(TlmPduCount>>16); // PDU Count [1]
rgrover1 784:1e817a72a166 196 Data[index++] = (uint8_t)(TlmPduCount>>8); // PDU Count [2]
rgrover1 784:1e817a72a166 197 Data[index++] = (uint8_t)(TlmPduCount>>0); // PDU Count [3]
rgrover1 784:1e817a72a166 198 Data[index++] = (uint8_t)(TlmTimeSinceBoot>>24); // Time Since Boot [0]
rgrover1 784:1e817a72a166 199 Data[index++] = (uint8_t)(TlmTimeSinceBoot>>16); // Time Since Boot [1]
rgrover1 784:1e817a72a166 200 Data[index++] = (uint8_t)(TlmTimeSinceBoot>>8); // Time Since Boot [2]
rgrover1 784:1e817a72a166 201 Data[index++] = (uint8_t)(TlmTimeSinceBoot>>0); // Time Since Boot [3]
rgrover1 784:1e817a72a166 202 DBG("constructURLFrame: %d, %d",maxSize,index);
rgrover1 771:26b809199308 203 return index;
rgrover1 771:26b809199308 204 }
rgrover1 771:26b809199308 205
rgrover1 771:26b809199308 206 /*
rgrover1 784:1e817a72a166 207 * Update the TLM frame battery voltage value
rgrover1 784:1e817a72a166 208 * @param[in] voltagemv Voltage to update the TLM field battery voltage with (in mV)
rgrover1 784:1e817a72a166 209 * @return nothing
rgrover1 784:1e817a72a166 210 */
rgrover1 784:1e817a72a166 211 void updateTlmBatteryVoltage(uint16_t voltagemv) {
rgrover1 784:1e817a72a166 212 TlmBatteryVoltage = voltagemv;
rgrover1 784:1e817a72a166 213 return;
rgrover1 784:1e817a72a166 214 }
rgrover1 784:1e817a72a166 215
rgrover1 784:1e817a72a166 216 /*
rgrover1 784:1e817a72a166 217 * Update the TLM frame beacon temperature
rgrover1 784:1e817a72a166 218 * @param[in] temp Temperature of beacon (in 8.8fpn)
rgrover1 784:1e817a72a166 219 * @return nothing
rgrover1 784:1e817a72a166 220 */
rgrover1 784:1e817a72a166 221 void updateTlmBeaconTemp(uint16_t temp) {
rgrover1 784:1e817a72a166 222 TlmBeaconTemp = temp;
rgrover1 784:1e817a72a166 223 return;
rgrover1 784:1e817a72a166 224 }
rgrover1 784:1e817a72a166 225
rgrover1 784:1e817a72a166 226 /*
rgrover1 784:1e817a72a166 227 * Update the TLM frame PDU Count field
rgrover1 784:1e817a72a166 228 * @param[in] pduCount Number of Advertisiting frames sent since powerup
rgrover1 784:1e817a72a166 229 * @return nothing
rgrover1 784:1e817a72a166 230 */
rgrover1 784:1e817a72a166 231 void updateTlmPduCount(uint32_t pduCount) {
rgrover1 784:1e817a72a166 232 TlmPduCount = pduCount;
rgrover1 784:1e817a72a166 233 return;
rgrover1 771:26b809199308 234 }
rgrover1 771:26b809199308 235
rgrover1 771:26b809199308 236 /*
rgrover1 784:1e817a72a166 237 * Update the TLM frame Time since boot in 0.1s incriments
rgrover1 784:1e817a72a166 238 * @param[in] timeSinceBoot Time since boot in 0.1s incriments
rgrover1 784:1e817a72a166 239 * @return nothing
rgrover1 784:1e817a72a166 240 */
rgrover1 784:1e817a72a166 241 void updateTlmTimeSinceBoot(uint32_t timeSinceBoot) {
rgrover1 784:1e817a72a166 242 TlmTimeSinceBoot = timeSinceBoot;
rgrover1 784:1e817a72a166 243 return;
rgrover1 784:1e817a72a166 244 }
rgrover1 771:26b809199308 245
rgrover1 784:1e817a72a166 246 /*
rgrover1 784:1e817a72a166 247 * callback function, called every 0.1s, incriments the TimeSinceBoot field in the TLM frame
rgrover1 784:1e817a72a166 248 * @return nothing
rgrover1 784:1e817a72a166 249 */
rgrover1 784:1e817a72a166 250 void tsbCallback(void) {
rgrover1 784:1e817a72a166 251 TlmTimeSinceBoot++;
rgrover1 771:26b809199308 252 }
rgrover1 771:26b809199308 253
rgrover1 771:26b809199308 254 /*
rgrover1 784:1e817a72a166 255 * Update advertising data
rgrover1 784:1e817a72a166 256 * @return true on success, false on failure
rgrover1 784:1e817a72a166 257 */
rgrover1 784:1e817a72a166 258 bool updateAdvPacket(uint8_t serviceData[], unsigned serviceDataLen) {
rgrover1 784:1e817a72a166 259 // Fields from the Service
rgrover1 784:1e817a72a166 260 DBG("Updating AdvFrame: %d", serviceDataLen);
rgrover1 784:1e817a72a166 261 // printf("\r\n");
rgrover1 784:1e817a72a166 262 // for(int x = 0; x<serviceDataLen; x++) {
rgrover1 784:1e817a72a166 263 // printf("%2.2x:",serviceData[x]);
rgrover1 784:1e817a72a166 264 // }
rgrover1 784:1e817a72a166 265 // printf("\r\n");
rgrover1 784:1e817a72a166 266 ble.clearAdvertisingPayload();
rgrover1 784:1e817a72a166 267 ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
rgrover1 784:1e817a72a166 268 ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, BEACON_EDDYSTONE, sizeof(BEACON_EDDYSTONE));
rgrover1 784:1e817a72a166 269 ble.accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, serviceData, serviceDataLen);
rgrover1 784:1e817a72a166 270
rgrover1 784:1e817a72a166 271 return true;
rgrover1 784:1e817a72a166 272 }
rgrover1 784:1e817a72a166 273
rgrover1 784:1e817a72a166 274 /*
rgrover1 784:1e817a72a166 275 * State machine for switching out frames.
rgrover1 784:1e817a72a166 276 * This function is called by the radioNotificationCallback when a frame needs to get swapped out.
rgrover1 784:1e817a72a166 277 * This function exists because of time constraints in the radioNotificationCallback, so it is effectively
rgrover1 784:1e817a72a166 278 * broken up into two functions.
rgrover1 784:1e817a72a166 279 */
rgrover1 771:26b809199308 280 void swapOutFrames(void) {
rgrover1 771:26b809199308 281 uint8_t serviceData[SERVICE_DATA_MAX];
rgrover1 771:26b809199308 282 unsigned serviceDataLen = 0;
rgrover1 784:1e817a72a166 283 //hard code in the eddystone UUID
rgrover1 771:26b809199308 284 serviceData[serviceDataLen++] = BEACON_EDDYSTONE[0];
rgrover1 771:26b809199308 285 serviceData[serviceDataLen++] = BEACON_EDDYSTONE[1];
rgrover1 771:26b809199308 286
rgrover1 771:26b809199308 287 // if certain frames are not enabled, then skip them. Worst case TLM is always enabled
rgrover1 784:1e817a72a166 288 switch(frameIndex) {
rgrover1 771:26b809199308 289 case 1:
rgrover1 771:26b809199308 290 // URL Frame
rgrover1 784:1e817a72a166 291 if(urlIsSet) {
rgrover1 784:1e817a72a166 292 INFO("Swapping in URL Frame: Power: %d",defaultUrlPower);
rgrover1 784:1e817a72a166 293 serviceDataLen += constructURLFrame(serviceData+serviceDataLen,20);
rgrover1 784:1e817a72a166 294 DBG("\t Swapping in URL Frame: len=%d ",serviceDataLen);
rgrover1 784:1e817a72a166 295 updateAdvPacket(serviceData,serviceDataLen);
rgrover1 771:26b809199308 296 switchFlag = false;
rgrover1 771:26b809199308 297 frameIndex++;
rgrover1 771:26b809199308 298 break;
rgrover1 771:26b809199308 299 }
rgrover1 771:26b809199308 300 case 2:
rgrover1 771:26b809199308 301 // UID Frame
rgrover1 784:1e817a72a166 302 if(uidIsSet) {
rgrover1 784:1e817a72a166 303 INFO("Swapping in UID Frame: Power: %d",defaultUidPower);
rgrover1 784:1e817a72a166 304 serviceDataLen += constructUIDFrame(serviceData+serviceDataLen,20);
rgrover1 784:1e817a72a166 305 DBG("\t Swapping in UID Frame: len=%d",serviceDataLen);
rgrover1 784:1e817a72a166 306 updateAdvPacket(serviceData,serviceDataLen);
rgrover1 771:26b809199308 307 switchFlag = false;
rgrover1 771:26b809199308 308 frameIndex++;
rgrover1 771:26b809199308 309 break;
rgrover1 771:26b809199308 310 }
rgrover1 771:26b809199308 311 default:
rgrover1 771:26b809199308 312 // TLM frame
rgrover1 784:1e817a72a166 313 INFO("Swapping in TLM Frame: version=%x, Batt=%d, Temp = %d, PDUCnt = %d, TimeSinceBoot=%d",TlmVersion, TlmBatteryVoltage, TlmBeaconTemp, TlmPduCount, TlmTimeSinceBoot);
rgrover1 784:1e817a72a166 314 serviceDataLen += constructTLMFrame(serviceData+serviceDataLen,20);
rgrover1 784:1e817a72a166 315 DBG("\t Swapping in TLM Frame: len=%d",serviceDataLen);
rgrover1 784:1e817a72a166 316 updateAdvPacket(serviceData,serviceDataLen);
rgrover1 771:26b809199308 317 frameIndex++;
rgrover1 771:26b809199308 318 break;
rgrover1 771:26b809199308 319 }
rgrover1 771:26b809199308 320 }
rgrover1 771:26b809199308 321
rgrover1 771:26b809199308 322 /*
rgrover1 784:1e817a72a166 323 * Callback from onRadioNotification(), used to update the PDUCounter and process next state.
rgrover1 784:1e817a72a166 324 */
rgrover1 784:1e817a72a166 325 #define EDDYSTONE_SWAPFRAME_DELAYMS 1
rgrover1 771:26b809199308 326 void radioNotificationCallback(bool radioActive) {
rgrover1 784:1e817a72a166 327 //DBG("RadioNotificationCallback : %d, %d, %d, %d",radioActive,frameIndex,TlmPduCount,TlmTimeSinceBoot);
rgrover1 771:26b809199308 328 // Update PDUCount
rgrover1 771:26b809199308 329 TlmPduCount++;
rgrover1 784:1e817a72a166 330 frameIndex = frameIndex % EDDYSTONE_MAX_FRAMETYPE;
rgrover1 784:1e817a72a166 331
rgrover1 771:26b809199308 332
rgrover1 784:1e817a72a166 333 // True just before an frame is sent, fale just after a frame is sent
rgrover1 784:1e817a72a166 334 if(radioActive) {
rgrover1 784:1e817a72a166 335 // Do Nothing
rgrover1 784:1e817a72a166 336 } else {
rgrover1 771:26b809199308 337 // state machine to control which packet is being sent
rgrover1 784:1e817a72a166 338 switch(frameIndex) {
rgrover1 771:26b809199308 339 case 0: // TLM Frame
rgrover1 771:26b809199308 340 switchFrame.attach_us(this, &EddystoneService::swapOutFrames, EDDYSTONE_SWAPFRAME_DELAYMS);
rgrover1 771:26b809199308 341 switchFlag = true;
rgrover1 771:26b809199308 342 break;
rgrover1 771:26b809199308 343 case 1: // URL Frame
rgrover1 771:26b809199308 344 // switch out packets
rgrover1 784:1e817a72a166 345 if(switchFlag) {
rgrover1 771:26b809199308 346 switchFrame.attach_us(this, &EddystoneService::swapOutFrames, EDDYSTONE_SWAPFRAME_DELAYMS);
rgrover1 771:26b809199308 347 switchFlag = false;
rgrover1 771:26b809199308 348 } else {
rgrover1 784:1e817a72a166 349 if((TlmPduCount % 10) == 0) { // every 10 adv packets switch the frame
rgrover1 771:26b809199308 350 switchFlag = true;
rgrover1 771:26b809199308 351 }
rgrover1 771:26b809199308 352 }
rgrover1 771:26b809199308 353 break;
rgrover1 771:26b809199308 354 case 2: // UIDFrame
rgrover1 771:26b809199308 355 // switch out packets
rgrover1 784:1e817a72a166 356 if(switchFlag ) {
rgrover1 771:26b809199308 357 switchFrame.attach_us(this, &EddystoneService::swapOutFrames, EDDYSTONE_SWAPFRAME_DELAYMS);
rgrover1 771:26b809199308 358 switchFlag = false;
rgrover1 771:26b809199308 359 } else {
rgrover1 784:1e817a72a166 360 if((TlmPduCount % 10) == 0) { // every 10 adv packets switch the frame
rgrover1 771:26b809199308 361 switchFlag = true;
rgrover1 771:26b809199308 362 }
rgrover1 771:26b809199308 363 }
rgrover1 771:26b809199308 364 break;
rgrover1 771:26b809199308 365 }
rgrover1 771:26b809199308 366 }
rgrover1 784:1e817a72a166 367
rgrover1 784:1e817a72a166 368 return;
rgrover1 784:1e817a72a166 369 }
rgrover1 784:1e817a72a166 370
rgrover1 784:1e817a72a166 371 /*
rgrover1 784:1e817a72a166 372 * This function explicityly sets the parameters used by the Eddystone beacon.
rgrover1 784:1e817a72a166 373 * this function should be used in leu of the config service.
rgrover1 784:1e817a72a166 374 *
rgrover1 784:1e817a72a166 375 * @param bleIn ble object used to broadcast eddystone information
rgrover1 784:1e817a72a166 376 * @oaram beaconPeriodus is how often ble broadcasts are mde, in mili seconds
rgrover1 784:1e817a72a166 377 * @param txPowerLevel sets the broadcasting power level.
rgrover1 784:1e817a72a166 378 * @param uidNamespaceID 10Byte Namespace UUID
rgrover1 784:1e817a72a166 379 * @param uidInstanceID 6Byte Instance UUID
rgrover1 784:1e817a72a166 380 * @param url shortened URL to broadcast (pass in as a string)
rgrover1 784:1e817a72a166 381 * @param urlLen length of shortened url
rgrover1 784:1e817a72a166 382 * @param tlmVersion version of telemetry data field to use (default to 0x00)
rgrover1 784:1e817a72a166 383 *
rgrover1 784:1e817a72a166 384 */
rgrover1 784:1e817a72a166 385 EddystoneService(BLEDevice &bleIn,
rgrover1 784:1e817a72a166 386 uint16_t beaconPeriodus = 100,
rgrover1 784:1e817a72a166 387 uint8_t txPowerLevel = 0,
rgrover1 784:1e817a72a166 388 uint8_t * uidNamespaceID = NULL,
rgrover1 784:1e817a72a166 389 uint8_t * uidInstanceID = NULL,
rgrover1 784:1e817a72a166 390 const char * url = NULL,
rgrover1 784:1e817a72a166 391 uint8_t urlLen = 0,
rgrover1 784:1e817a72a166 392 uint8_t tlmVersion = 0) :
rgrover1 784:1e817a72a166 393 ble(bleIn)
rgrover1 784:1e817a72a166 394 {
rgrover1 784:1e817a72a166 395 uint8_t serviceData[SERVICE_DATA_MAX];
rgrover1 784:1e817a72a166 396 unsigned serviceDataLen = 0;
rgrover1 784:1e817a72a166 397 ERR("This function is not fully implemented yet, dont use it!!");
rgrover1 784:1e817a72a166 398 // Check optional frames, set their 'isSet' flags appropriately
rgrover1 784:1e817a72a166 399 if((uidNamespaceID != NULL) & (uidInstanceID != NULL)) {
rgrover1 784:1e817a72a166 400 uidIsSet = true;
rgrover1 784:1e817a72a166 401 setUIDFrameData(txPowerLevel,uidNamespaceID, uidInstanceID);
rgrover1 784:1e817a72a166 402 } else {
rgrover1 784:1e817a72a166 403 uidIsSet = false;
rgrover1 784:1e817a72a166 404 }
rgrover1 784:1e817a72a166 405 if(url != NULL) {
rgrover1 784:1e817a72a166 406 urlIsSet = true;
rgrover1 784:1e817a72a166 407 setURLFrameData(txPowerLevel,url);
rgrover1 784:1e817a72a166 408 } else {
rgrover1 784:1e817a72a166 409 uidIsSet = false;
rgrover1 784:1e817a72a166 410 }
rgrover1 784:1e817a72a166 411 // Default TLM frame to version 0x00, start all values at zero to be spec compliant.
rgrover1 784:1e817a72a166 412 setTLMFrameData(tlmVersion, 0x00,0x00);
rgrover1 784:1e817a72a166 413
rgrover1 784:1e817a72a166 414 // Initialize Frame transition
rgrover1 784:1e817a72a166 415 frameIndex = 0;
rgrover1 784:1e817a72a166 416 uidRFU = 0;
rgrover1 784:1e817a72a166 417 switchFlag = true;
rgrover1 784:1e817a72a166 418
rgrover1 784:1e817a72a166 419 /* Reinitialize the BLE stack. This will clear away the existing services and advertising state. */
rgrover1 784:1e817a72a166 420 ble.shutdown();
rgrover1 784:1e817a72a166 421 ble.init();
rgrover1 784:1e817a72a166 422 ble.setTxPower(txPowerLevel);
rgrover1 784:1e817a72a166 423 ble.setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
rgrover1 784:1e817a72a166 424 ble.setAdvertisingInterval(beaconPeriodus);
rgrover1 784:1e817a72a166 425
rgrover1 784:1e817a72a166 426 // Make double sure the PDUCount and TimeSinceBoot fields are set to zero at reset
rgrover1 784:1e817a72a166 427 updateTlmPduCount(0);
rgrover1 784:1e817a72a166 428 updateTlmTimeSinceBoot(0);
rgrover1 784:1e817a72a166 429
rgrover1 784:1e817a72a166 430 // Construct TLM Frame in initial advertising.
rgrover1 784:1e817a72a166 431 serviceData[serviceDataLen++] = BEACON_EDDYSTONE[0];
rgrover1 784:1e817a72a166 432 serviceData[serviceDataLen++] = BEACON_EDDYSTONE[1];
rgrover1 784:1e817a72a166 433 serviceDataLen += constructTLMFrame(serviceData+serviceDataLen,SERVICE_DATA_MAX);
rgrover1 784:1e817a72a166 434
rgrover1 784:1e817a72a166 435 updateAdvPacket(serviceData, serviceDataLen);
rgrover1 784:1e817a72a166 436 ble.gap().startAdvertising();
rgrover1 784:1e817a72a166 437 ble.gap().onRadioNotification(this,&EddystoneService::radioNotificationCallback);
rgrover1 784:1e817a72a166 438 timeSinceBootTick.attach(this,&EddystoneService::tsbCallback,0.1); // incriment the TimeSinceBoot ticker every 0.1s
rgrover1 784:1e817a72a166 439
rgrover1 771:26b809199308 440 }
rgrover1 771:26b809199308 441
rgrover1 771:26b809199308 442 private:
rgrover1 784:1e817a72a166 443
rgrover1 771:26b809199308 444
rgrover1 784:1e817a72a166 445 BLEDevice &ble;
rgrover1 784:1e817a72a166 446 Ticker timeSinceBootTick;
rgrover1 784:1e817a72a166 447 Timeout switchFrame;
rgrover1 784:1e817a72a166 448 // Default value that is restored on reset
rgrover1 784:1e817a72a166 449 size_t defaultUriDataLength;
rgrover1 784:1e817a72a166 450 UriData_t defaultUriData;
rgrover1 784:1e817a72a166 451 UIDNamespaceID_t defaultUidNamespaceID;
rgrover1 784:1e817a72a166 452 UIDInstanceID_t defaultUidInstanceID;
rgrover1 784:1e817a72a166 453 int8_t defaultUidPower;
rgrover1 784:1e817a72a166 454 int8_t defaultUrlPower;
rgrover1 784:1e817a72a166 455 uint16_t uidRFU;
rgrover1 784:1e817a72a166 456 bool uidIsSet;
rgrover1 784:1e817a72a166 457 bool urlIsSet;
rgrover1 784:1e817a72a166 458 bool switchFlag;
rgrover1 784:1e817a72a166 459
rgrover1 784:1e817a72a166 460 // Private Variables for Telemetry Data
rgrover1 784:1e817a72a166 461 uint8_t TlmVersion;
rgrover1 784:1e817a72a166 462 volatile uint16_t TlmBatteryVoltage;
rgrover1 784:1e817a72a166 463 volatile uint16_t TlmBeaconTemp;
rgrover1 784:1e817a72a166 464 volatile uint32_t TlmPduCount;
rgrover1 784:1e817a72a166 465 volatile uint32_t TlmTimeSinceBoot;
rgrover1 771:26b809199308 466
rgrover1 771:26b809199308 467 public:
rgrover1 771:26b809199308 468 /*
rgrover1 771:26b809199308 469 * Encode a human-readable URI into the binary format defined by URIBeacon spec (https://github.com/google/uribeacon/tree/master/specification).
rgrover1 771:26b809199308 470 */
rgrover1 771:26b809199308 471 static void encodeURL(const char *uriDataIn, UriData_t uriDataOut, size_t &sizeofURIDataOut) {
rgrover1 771:26b809199308 472 const char *prefixes[] = {
rgrover1 771:26b809199308 473 "http://www.",
rgrover1 771:26b809199308 474 "https://www.",
rgrover1 771:26b809199308 475 "http://",
rgrover1 771:26b809199308 476 "https://",
rgrover1 771:26b809199308 477 };
rgrover1 771:26b809199308 478 const size_t NUM_PREFIXES = sizeof(prefixes) / sizeof(char *);
rgrover1 771:26b809199308 479 const char *suffixes[] = {
rgrover1 771:26b809199308 480 ".com/",
rgrover1 771:26b809199308 481 ".org/",
rgrover1 771:26b809199308 482 ".edu/",
rgrover1 771:26b809199308 483 ".net/",
rgrover1 771:26b809199308 484 ".info/",
rgrover1 771:26b809199308 485 ".biz/",
rgrover1 771:26b809199308 486 ".gov/",
rgrover1 771:26b809199308 487 ".com",
rgrover1 771:26b809199308 488 ".org",
rgrover1 771:26b809199308 489 ".edu",
rgrover1 771:26b809199308 490 ".net",
rgrover1 771:26b809199308 491 ".info",
rgrover1 771:26b809199308 492 ".biz",
rgrover1 771:26b809199308 493 ".gov"
rgrover1 771:26b809199308 494 };
rgrover1 771:26b809199308 495 const size_t NUM_SUFFIXES = sizeof(suffixes) / sizeof(char *);
rgrover1 771:26b809199308 496
rgrover1 771:26b809199308 497 sizeofURIDataOut = 0;
rgrover1 771:26b809199308 498 memset(uriDataOut, 0, sizeof(UriData_t));
rgrover1 771:26b809199308 499
rgrover1 771:26b809199308 500 if ((uriDataIn == NULL) || (strlen(uriDataIn) == 0)) {
rgrover1 771:26b809199308 501 return;
rgrover1 771:26b809199308 502 }
rgrover1 771:26b809199308 503
rgrover1 771:26b809199308 504 /*
rgrover1 771:26b809199308 505 * handle prefix
rgrover1 771:26b809199308 506 */
rgrover1 771:26b809199308 507 for (unsigned i = 0; i < NUM_PREFIXES; i++) {
rgrover1 771:26b809199308 508 size_t prefixLen = strlen(prefixes[i]);
rgrover1 771:26b809199308 509 if (strncmp(uriDataIn, prefixes[i], prefixLen) == 0) {
rgrover1 771:26b809199308 510 uriDataOut[sizeofURIDataOut++] = i;
rgrover1 771:26b809199308 511 uriDataIn += prefixLen;
rgrover1 771:26b809199308 512 break;
rgrover1 771:26b809199308 513 }
rgrover1 771:26b809199308 514 }
rgrover1 771:26b809199308 515
rgrover1 771:26b809199308 516 /*
rgrover1 771:26b809199308 517 * handle suffixes
rgrover1 771:26b809199308 518 */
rgrover1 771:26b809199308 519 while (*uriDataIn && (sizeofURIDataOut < URI_DATA_MAX)) {
rgrover1 771:26b809199308 520 /* check for suffix match */
rgrover1 771:26b809199308 521 unsigned i;
rgrover1 771:26b809199308 522 for (i = 0; i < NUM_SUFFIXES; i++) {
rgrover1 771:26b809199308 523 size_t suffixLen = strlen(suffixes[i]);
rgrover1 771:26b809199308 524 if (strncmp(uriDataIn, suffixes[i], suffixLen) == 0) {
rgrover1 771:26b809199308 525 uriDataOut[sizeofURIDataOut++] = i;
rgrover1 771:26b809199308 526 uriDataIn += suffixLen;
rgrover1 771:26b809199308 527 break; /* from the for loop for checking against suffixes */
rgrover1 771:26b809199308 528 }
rgrover1 771:26b809199308 529 }
rgrover1 771:26b809199308 530 /* This is the default case where we've got an ordinary character which doesn't match a suffix. */
rgrover1 771:26b809199308 531 if (i == NUM_SUFFIXES) {
rgrover1 771:26b809199308 532 uriDataOut[sizeofURIDataOut++] = *uriDataIn;
rgrover1 771:26b809199308 533 ++uriDataIn;
rgrover1 771:26b809199308 534 }
rgrover1 771:26b809199308 535 }
rgrover1 771:26b809199308 536 }
rgrover1 771:26b809199308 537 };
rgrover1 771:26b809199308 538
rgrover1 771:26b809199308 539 #endif // SERVICES_EDDYSTONEBEACON_H_