Lancaster University's fork of the mbed BLE API. Lives on github, https://github.com/lancaster-university/BLE_API

Dependents:   microbit-dal microbit-dal microbit-ble-open microbit-dal ... more

Fork of BLE_API by Bluetooth Low Energy

Committer:
rgrover1
Date:
Tue Aug 11 15:13:13 2015 +0100
Revision:
782:5736312322d9
Parent:
771:26b809199308
Child:
783:9959291e9a3f
Synchronized with git rev f09b0b63
Author: Austin Blackstone
modified eddystone service, currently blows away any associated services but it works

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 782:5736312322d9 25 //Debug is disabled by default
rgrover1 771:26b809199308 26 #if 0
rgrover1 782:5736312322d9 27 #define DBG(x, ...) printf("[EddyStone: DBG]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__);
rgrover1 782:5736312322d9 28 #define WARN(x, ...) printf("[EddyStone: WARN]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__);
rgrover1 782:5736312322d9 29 #define ERR(x, ...) printf("[EddyStone: ERR]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__);
rgrover1 771:26b809199308 30 #else
rgrover1 782:5736312322d9 31 #define DBG(x, ...) //wait_us(10);
rgrover1 782:5736312322d9 32 #define WARN(x, ...) //wait_us(10);
rgrover1 782:5736312322d9 33 #define ERR(x, ...)
rgrover1 782:5736312322d9 34 #endif
rgrover1 782:5736312322d9 35
rgrover1 782:5736312322d9 36 #if 0
rgrover1 782:5736312322d9 37 #define INFO(x, ...) printf("[EddyStone: INFO]"x" \t[%s,%d]\r\n", ##__VA_ARGS__,__FILE__,__LINE__);
rgrover1 782:5736312322d9 38 #else
rgrover1 782:5736312322d9 39 #define INFO(x, ...)
rgrover1 782:5736312322d9 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 782:5736312322d9 51 /**
rgrover1 782:5736312322d9 52 * @brief Transmission Power Modes for UriBeacon
rgrover1 782:5736312322d9 53 */
rgrover1 782:5736312322d9 54
rgrover1 782:5736312322d9 55 static const int ADVERTISING_INTERVAL_MSEC = 1000; // Advertising interval for config service.
rgrover1 782:5736312322d9 56 static const int SERVICE_DATA_MAX = 31; // Maximum size of service data in ADV packets
rgrover1 771:26b809199308 57
rgrover1 782:5736312322d9 58 // There are currently 3 subframes defined, URI, UID, and TLM
rgrover1 782:5736312322d9 59 #define EDDYSTONE_MAX_FRAMETYPE 3
rgrover1 782:5736312322d9 60 void (*frames[EDDYSTONE_MAX_FRAMETYPE])(uint8_t *, uint32_t);
rgrover1 782:5736312322d9 61 uint8_t frameIndex;
rgrover1 782:5736312322d9 62 static const int URI_DATA_MAX = 18;
rgrover1 782:5736312322d9 63 typedef uint8_t UriData_t[URI_DATA_MAX];
rgrover1 771:26b809199308 64
rgrover1 771:26b809199308 65 // UID Frame Type subfields
rgrover1 782:5736312322d9 66 static const int UID_NAMESPACEID_SIZE = 10;
rgrover1 782:5736312322d9 67 typedef uint8_t UIDNamespaceID_t[UID_NAMESPACEID_SIZE];
rgrover1 782:5736312322d9 68 static const int UID_INSTANCEID_SIZE = 6;
rgrover1 782:5736312322d9 69 typedef uint8_t UIDInstanceID_t[UID_INSTANCEID_SIZE];
rgrover1 771:26b809199308 70
rgrover1 771:26b809199308 71 // Eddystone Frame Type ID
rgrover1 782:5736312322d9 72 static const uint8_t FRAME_TYPE_UID = 0x00;
rgrover1 782:5736312322d9 73 static const uint8_t FRAME_TYPE_URL = 0x10;
rgrover1 782:5736312322d9 74 static const uint8_t FRAME_TYPE_TLM = 0x20;
rgrover1 771:26b809199308 75
rgrover1 782:5736312322d9 76 static const uint8_t FRAME_SIZE_TLM = 14; // TLM frame is a constant 14Bytes
rgrover1 771:26b809199308 77 static const uint8_t FRAME_SIZE_UID = 20; // includes RFU bytes
rgrover1 771:26b809199308 78
rgrover1 782:5736312322d9 79 /*
rgrover1 782:5736312322d9 80 * Set Eddystone UID Frame information.
rgrover1 782:5736312322d9 81 * @param[in] power TX Power in dB measured at 0 meters from the device. Range of -100 to +20 dB.
rgrover1 782:5736312322d9 82 * @param namespaceID 10B namespace ID
rgrover1 782:5736312322d9 83 * @param instanceID 6B instance ID
rgrover1 782:5736312322d9 84 * @param RFU 2B of RFU, initialized to 0x0000 and not broadcast, included for future reference.
rgrover1 782:5736312322d9 85 *
rgrover1 782:5736312322d9 86 */
rgrover1 782:5736312322d9 87 void setUIDFrameData(int8_t power, UIDNamespaceID_t namespaceID, UIDInstanceID_t instanceID, uint16_t RFU = 0x0000) {
rgrover1 782:5736312322d9 88 if(power > 20) {
rgrover1 782:5736312322d9 89 power = 20;
rgrover1 771:26b809199308 90 }
rgrover1 782:5736312322d9 91 if(power < -100) {
rgrover1 782:5736312322d9 92 power = -100;
rgrover1 782:5736312322d9 93 }
rgrover1 782:5736312322d9 94 defaultUidPower = power;
rgrover1 782:5736312322d9 95 memcpy(defaultUidNamespaceID, namespaceID, UID_NAMESPACEID_SIZE);
rgrover1 782:5736312322d9 96 memcpy(defaultUidInstanceID, instanceID, UID_INSTANCEID_SIZE);
rgrover1 782:5736312322d9 97 uidRFU = (uint16_t)RFU; // this is probably bad form, but it doesnt really matter yet.
rgrover1 782:5736312322d9 98 return;
rgrover1 771:26b809199308 99 }
rgrover1 771:26b809199308 100
rgrover1 771:26b809199308 101 /*
rgrover1 782:5736312322d9 102 * Construct UID frame from private variables
rgrover1 782:5736312322d9 103 * @param[in/out] Data pointer to array to store constructed frame in
rgrover1 782:5736312322d9 104 * @param[in] maxSize number of bytes left in array, effectively how much emtpy space is available to write to
rgrover1 782:5736312322d9 105 * @return number of bytes used. negative number indicates error message.
rgrover1 782:5736312322d9 106 */
rgrover1 782:5736312322d9 107 int constructUIDFrame(uint8_t * Data, uint8_t maxSize) {
rgrover1 771:26b809199308 108
rgrover1 782:5736312322d9 109 int index = 0;
rgrover1 782:5736312322d9 110 Data[index++] = FRAME_TYPE_UID; // 1B Type
rgrover1 782:5736312322d9 111 if(defaultUidPower > 20) {
rgrover1 782:5736312322d9 112 defaultUidPower = 20; // enforce range of vaild values.
rgrover1 771:26b809199308 113 }
rgrover1 782:5736312322d9 114 if(defaultUidPower < -100) {
rgrover1 771:26b809199308 115 defaultUidPower = -100;
rgrover1 771:26b809199308 116 }
rgrover1 782:5736312322d9 117 Data[index++] = defaultUidPower; // 1B Power @ 0meter
rgrover1 782:5736312322d9 118 for(int x = 0; x < UID_NAMESPACEID_SIZE; x++) { // 10B Namespce ID
rgrover1 782:5736312322d9 119 Data[index++] = defaultUidNamespaceID[x];
rgrover1 771:26b809199308 120 }
rgrover1 782:5736312322d9 121 for(int x = 0; x< UID_INSTANCEID_SIZE; x++) { // 6B Instance ID
rgrover1 782:5736312322d9 122 Data[index++] = defaultUidInstanceID[x];
rgrover1 771:26b809199308 123 }
rgrover1 782:5736312322d9 124 if(0 != uidRFU) { // 2B RFU, include if non-zero, otherwise ignore
rgrover1 782:5736312322d9 125 Data[index++] = (uint8_t)(uidRFU >> 0);
rgrover1 782:5736312322d9 126 Data[index++] = (uint8_t)(uidRFU >> 8);
rgrover1 771:26b809199308 127 }
rgrover1 782:5736312322d9 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 782:5736312322d9 133 * Set Eddystone URL Frame information.
rgrover1 782:5736312322d9 134 * @param[in] power TX Power in dB measured at 0 meters from the device.
rgrover1 782:5736312322d9 135 * @param url URL to encode
rgrover1 782:5736312322d9 136 * @return false on success, true on failure.
rgrover1 782:5736312322d9 137 */
rgrover1 782:5736312322d9 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 782:5736312322d9 147 /*
rgrover1 782:5736312322d9 148 * Construct URL frame from private variables
rgrover1 782:5736312322d9 149 * @param[in/out] Data pointer to array to store constructed frame in
rgrover1 782:5736312322d9 150 * @param[in] maxSize number of bytes left in array, effectively how much emtpy space is available to write to
rgrover1 782:5736312322d9 151 * @return number of bytes used. negative number indicates error message.
rgrover1 782:5736312322d9 152 */
rgrover1 782:5736312322d9 153 int constructURLFrame(uint8_t * Data, uint8_t maxSize) {
rgrover1 782:5736312322d9 154 int index = 0;
rgrover1 782:5736312322d9 155 Data[index++] = FRAME_TYPE_URL; // 1B Type
rgrover1 782:5736312322d9 156 Data[index++] = defaultUrlPower; // 1B TX Power
rgrover1 782:5736312322d9 157 for(int x = 0; x < defaultUriDataLength; x++) { // 18B of URL Prefix + encoded URL
rgrover1 782:5736312322d9 158 Data[index++] = defaultUriData[x];
rgrover1 771:26b809199308 159 }
rgrover1 782:5736312322d9 160 DBG("constructURLFrame: %d, %d",maxSize,index);
rgrover1 782:5736312322d9 161 return index;
rgrover1 782:5736312322d9 162 }
rgrover1 771:26b809199308 163
rgrover1 782:5736312322d9 164 /*
rgrover1 782:5736312322d9 165 * Set Eddystone TLM Frame information.
rgrover1 782:5736312322d9 166 * @param[in] Version of the TLM beacon data format
rgrover1 782:5736312322d9 167 * @param batteryVoltage in milivolts
rgrover1 782:5736312322d9 168 * @param beaconTemp in 8.8 floating point notation
rgrover1 782:5736312322d9 169 *
rgrover1 782:5736312322d9 170 */
rgrover1 782:5736312322d9 171 void setTLMFrameData(uint8_t version, uint16_t batteryVoltage, uint16_t beaconTemp, uint32_t pduCount = 0, uint32_t timeSinceBoot = 0) {
rgrover1 782:5736312322d9 172 TlmVersion = version;
rgrover1 782:5736312322d9 173 TlmBatteryVoltage = batteryVoltage;
rgrover1 782:5736312322d9 174 TlmBeaconTemp = beaconTemp;
rgrover1 782:5736312322d9 175 TlmPduCount = pduCount; // reset
rgrover1 782:5736312322d9 176 TlmTimeSinceBoot = timeSinceBoot; // reset
rgrover1 782:5736312322d9 177 return;
rgrover1 782:5736312322d9 178 }
rgrover1 771:26b809199308 179
rgrover1 782:5736312322d9 180 /*
rgrover1 782:5736312322d9 181 * Construct TLM frame from private variables
rgrover1 782:5736312322d9 182 * @param[in/out] Data pointer to array to store constructed frame in
rgrover1 782:5736312322d9 183 * @param[in] maxSize number of bytes left in array, effectively how much emtpy space is available to write to
rgrover1 782:5736312322d9 184 * @return number of bytes used. negative number indicates error message.
rgrover1 782:5736312322d9 185 */
rgrover1 782:5736312322d9 186 int constructTLMFrame(uint8_t * Data, uint8_t maxSize) {
rgrover1 782:5736312322d9 187 int index = 0;
rgrover1 782:5736312322d9 188 Data[index++] = FRAME_TYPE_TLM; // Eddystone frame type = Telemetry
rgrover1 782:5736312322d9 189 Data[index++] = TlmVersion; // TLM Version Number
rgrover1 782:5736312322d9 190 Data[index++] = (uint8_t)(TlmBatteryVoltage>>8); // Battery Voltage[0]
rgrover1 782:5736312322d9 191 Data[index++] = (uint8_t)(TlmBatteryVoltage>>0); // Battery Voltage[1]
rgrover1 782:5736312322d9 192 Data[index++] = (uint8_t)(TlmBeaconTemp>>8); // Beacon Temp[0]
rgrover1 782:5736312322d9 193 Data[index++] = (uint8_t)(TlmBeaconTemp>>0); // Beacon Temp[1]
rgrover1 782:5736312322d9 194 Data[index++] = (uint8_t)(TlmPduCount>>24); // PDU Count [0]
rgrover1 782:5736312322d9 195 Data[index++] = (uint8_t)(TlmPduCount>>16); // PDU Count [1]
rgrover1 782:5736312322d9 196 Data[index++] = (uint8_t)(TlmPduCount>>8); // PDU Count [2]
rgrover1 782:5736312322d9 197 Data[index++] = (uint8_t)(TlmPduCount>>0); // PDU Count [3]
rgrover1 782:5736312322d9 198 Data[index++] = (uint8_t)(TlmTimeSinceBoot>>24); // Time Since Boot [0]
rgrover1 782:5736312322d9 199 Data[index++] = (uint8_t)(TlmTimeSinceBoot>>16); // Time Since Boot [1]
rgrover1 782:5736312322d9 200 Data[index++] = (uint8_t)(TlmTimeSinceBoot>>8); // Time Since Boot [2]
rgrover1 782:5736312322d9 201 Data[index++] = (uint8_t)(TlmTimeSinceBoot>>0); // Time Since Boot [3]
rgrover1 782:5736312322d9 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 782:5736312322d9 207 * Update the TLM frame battery voltage value
rgrover1 782:5736312322d9 208 * @param[in] voltagemv Voltage to update the TLM field battery voltage with (in mV)
rgrover1 782:5736312322d9 209 * @return nothing
rgrover1 782:5736312322d9 210 */
rgrover1 782:5736312322d9 211 void updateTlmBatteryVoltage(uint16_t voltagemv) {
rgrover1 782:5736312322d9 212 TlmBatteryVoltage = voltagemv;
rgrover1 782:5736312322d9 213 return;
rgrover1 782:5736312322d9 214 }
rgrover1 782:5736312322d9 215
rgrover1 782:5736312322d9 216 /*
rgrover1 782:5736312322d9 217 * Update the TLM frame beacon temperature
rgrover1 782:5736312322d9 218 * @param[in] temp Temperature of beacon (in 8.8fpn)
rgrover1 782:5736312322d9 219 * @return nothing
rgrover1 782:5736312322d9 220 */
rgrover1 782:5736312322d9 221 void updateTlmBeaconTemp(uint16_t temp) {
rgrover1 782:5736312322d9 222 TlmBeaconTemp = temp;
rgrover1 782:5736312322d9 223 return;
rgrover1 782:5736312322d9 224 }
rgrover1 782:5736312322d9 225
rgrover1 782:5736312322d9 226 /*
rgrover1 782:5736312322d9 227 * Update the TLM frame PDU Count field
rgrover1 782:5736312322d9 228 * @param[in] pduCount Number of Advertisiting frames sent since powerup
rgrover1 782:5736312322d9 229 * @return nothing
rgrover1 782:5736312322d9 230 */
rgrover1 782:5736312322d9 231 void updateTlmPduCount(uint32_t pduCount) {
rgrover1 782:5736312322d9 232 TlmPduCount = pduCount;
rgrover1 782:5736312322d9 233 return;
rgrover1 771:26b809199308 234 }
rgrover1 771:26b809199308 235
rgrover1 771:26b809199308 236 /*
rgrover1 782:5736312322d9 237 * Update the TLM frame Time since boot in 0.1s incriments
rgrover1 782:5736312322d9 238 * @param[in] timeSinceBoot Time since boot in 0.1s incriments
rgrover1 782:5736312322d9 239 * @return nothing
rgrover1 782:5736312322d9 240 */
rgrover1 782:5736312322d9 241 void updateTlmTimeSinceBoot(uint32_t timeSinceBoot) {
rgrover1 782:5736312322d9 242 TlmTimeSinceBoot = timeSinceBoot;
rgrover1 782:5736312322d9 243 return;
rgrover1 782:5736312322d9 244 }
rgrover1 771:26b809199308 245
rgrover1 782:5736312322d9 246 /*
rgrover1 782:5736312322d9 247 * callback function, called every 0.1s, incriments the TimeSinceBoot field in the TLM frame
rgrover1 782:5736312322d9 248 * @return nothing
rgrover1 782:5736312322d9 249 */
rgrover1 782:5736312322d9 250 void tsbCallback(void) {
rgrover1 782:5736312322d9 251 TlmTimeSinceBoot++;
rgrover1 771:26b809199308 252 }
rgrover1 771:26b809199308 253
rgrover1 771:26b809199308 254 /*
rgrover1 782:5736312322d9 255 * Update advertising data
rgrover1 782:5736312322d9 256 * @return true on success, false on failure
rgrover1 782:5736312322d9 257 */
rgrover1 782:5736312322d9 258 bool updateAdvPacket(uint8_t serviceData[], unsigned serviceDataLen) {
rgrover1 782:5736312322d9 259 // Fields from the Service
rgrover1 782:5736312322d9 260 DBG("Updating AdvFrame: %d", serviceDataLen);
rgrover1 782:5736312322d9 261 // printf("\r\n");
rgrover1 782:5736312322d9 262 // for(int x = 0; x<serviceDataLen; x++) {
rgrover1 782:5736312322d9 263 // printf("%2.2x:",serviceData[x]);
rgrover1 782:5736312322d9 264 // }
rgrover1 782:5736312322d9 265 // printf("\r\n");
rgrover1 782:5736312322d9 266 ble.clearAdvertisingPayload();
rgrover1 782:5736312322d9 267 ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
rgrover1 782:5736312322d9 268 ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, BEACON_EDDYSTONE, sizeof(BEACON_EDDYSTONE));
rgrover1 782:5736312322d9 269 ble.accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, serviceData, serviceDataLen);
rgrover1 782:5736312322d9 270
rgrover1 782:5736312322d9 271 return true;
rgrover1 782:5736312322d9 272 }
rgrover1 782:5736312322d9 273
rgrover1 782:5736312322d9 274 /*
rgrover1 782:5736312322d9 275 * State machine for switching out frames.
rgrover1 782:5736312322d9 276 * This function is called by the radioNotificationCallback when a frame needs to get swapped out.
rgrover1 782:5736312322d9 277 * This function exists because of time constraints in the radioNotificationCallback, so it is effectively
rgrover1 782:5736312322d9 278 * broken up into two functions.
rgrover1 782:5736312322d9 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 782:5736312322d9 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 782:5736312322d9 288 switch(frameIndex) {
rgrover1 771:26b809199308 289 case 1:
rgrover1 771:26b809199308 290 // URL Frame
rgrover1 782:5736312322d9 291 if(urlIsSet) {
rgrover1 782:5736312322d9 292 INFO("Swapping in URL Frame: Power: %d",defaultUrlPower);
rgrover1 782:5736312322d9 293 serviceDataLen += constructURLFrame(serviceData+serviceDataLen,20);
rgrover1 782:5736312322d9 294 DBG("\t Swapping in URL Frame: len=%d ",serviceDataLen);
rgrover1 782:5736312322d9 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 782:5736312322d9 302 if(uidIsSet) {
rgrover1 782:5736312322d9 303 INFO("Swapping in UID Frame: Power: %d",defaultUidPower);
rgrover1 782:5736312322d9 304 serviceDataLen += constructUIDFrame(serviceData+serviceDataLen,20);
rgrover1 782:5736312322d9 305 DBG("\t Swapping in UID Frame: len=%d",serviceDataLen);
rgrover1 782:5736312322d9 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 782:5736312322d9 313 INFO("Swapping in TLM Frame: version=%x, Batt=%d, Temp = %d, PDUCnt = %d, TimeSinceBoot=%d",TlmVersion, TlmBatteryVoltage, TlmBeaconTemp, TlmPduCount, TlmTimeSinceBoot);
rgrover1 782:5736312322d9 314 serviceDataLen += constructTLMFrame(serviceData+serviceDataLen,20);
rgrover1 782:5736312322d9 315 DBG("\t Swapping in TLM Frame: len=%d",serviceDataLen);
rgrover1 782:5736312322d9 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 782:5736312322d9 323 * Callback from onRadioNotification(), used to update the PDUCounter and process next state.
rgrover1 782:5736312322d9 324 */
rgrover1 782:5736312322d9 325 #define EDDYSTONE_SWAPFRAME_DELAYMS 1
rgrover1 771:26b809199308 326 void radioNotificationCallback(bool radioActive) {
rgrover1 782:5736312322d9 327 //DBG("RadioNotificationCallback : %d, %d, %d, %d",radioActive,frameIndex,TlmPduCount,TlmTimeSinceBoot);
rgrover1 771:26b809199308 328 // Update PDUCount
rgrover1 771:26b809199308 329 TlmPduCount++;
rgrover1 782:5736312322d9 330 frameIndex = frameIndex % EDDYSTONE_MAX_FRAMETYPE;
rgrover1 782:5736312322d9 331
rgrover1 771:26b809199308 332
rgrover1 782:5736312322d9 333 // True just before an frame is sent, fale just after a frame is sent
rgrover1 782:5736312322d9 334 if(radioActive) {
rgrover1 782:5736312322d9 335 // Do Nothing
rgrover1 782:5736312322d9 336 } else {
rgrover1 771:26b809199308 337 // state machine to control which packet is being sent
rgrover1 782:5736312322d9 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 782:5736312322d9 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 782:5736312322d9 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 782:5736312322d9 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 782:5736312322d9 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 782:5736312322d9 367
rgrover1 782:5736312322d9 368 return;
rgrover1 782:5736312322d9 369 }
rgrover1 782:5736312322d9 370
rgrover1 782:5736312322d9 371 /*
rgrover1 782:5736312322d9 372 * This function explicityly sets the parameters used by the Eddystone beacon.
rgrover1 782:5736312322d9 373 * this function should be used in leu of the config service.
rgrover1 782:5736312322d9 374 *
rgrover1 782:5736312322d9 375 * @param bleIn ble object used to broadcast eddystone information
rgrover1 782:5736312322d9 376 * @oaram beaconPeriodus is how often ble broadcasts are mde, in mili seconds
rgrover1 782:5736312322d9 377 * @param txPowerLevel sets the broadcasting power level.
rgrover1 782:5736312322d9 378 * @param uidNamespaceID 10Byte Namespace UUID
rgrover1 782:5736312322d9 379 * @param uidInstanceID 6Byte Instance UUID
rgrover1 782:5736312322d9 380 * @param url shortened URL to broadcast (pass in as a string)
rgrover1 782:5736312322d9 381 * @param urlLen length of shortened url
rgrover1 782:5736312322d9 382 * @param tlmVersion version of telemetry data field to use (default to 0x00)
rgrover1 782:5736312322d9 383 *
rgrover1 782:5736312322d9 384 */
rgrover1 782:5736312322d9 385 EddystoneService(BLEDevice &bleIn,
rgrover1 782:5736312322d9 386 uint16_t beaconPeriodus = 100,
rgrover1 782:5736312322d9 387 uint8_t txPowerLevel = 0,
rgrover1 782:5736312322d9 388 uint8_t * uidNamespaceID = NULL,
rgrover1 782:5736312322d9 389 uint8_t * uidInstanceID = NULL,
rgrover1 782:5736312322d9 390 const char * url = NULL,
rgrover1 782:5736312322d9 391 uint8_t urlLen = 0,
rgrover1 782:5736312322d9 392 uint8_t tlmVersion = 0) :
rgrover1 782:5736312322d9 393 ble(bleIn)
rgrover1 782:5736312322d9 394 {
rgrover1 782:5736312322d9 395 uint8_t serviceData[SERVICE_DATA_MAX];
rgrover1 782:5736312322d9 396 unsigned serviceDataLen = 0;
rgrover1 782:5736312322d9 397 ERR("This function is not fully implemented yet, dont use it!!");
rgrover1 782:5736312322d9 398 // Check optional frames, set their 'isSet' flags appropriately
rgrover1 782:5736312322d9 399 if((uidNamespaceID != NULL) & (uidInstanceID != NULL)) {
rgrover1 782:5736312322d9 400 uidIsSet = true;
rgrover1 782:5736312322d9 401 setUIDFrameData(txPowerLevel,uidNamespaceID, uidInstanceID);
rgrover1 782:5736312322d9 402 } else {
rgrover1 782:5736312322d9 403 uidIsSet = false;
rgrover1 782:5736312322d9 404 }
rgrover1 782:5736312322d9 405 if(url != NULL) {
rgrover1 782:5736312322d9 406 urlIsSet = true;
rgrover1 782:5736312322d9 407 setURLFrameData(txPowerLevel,url);
rgrover1 782:5736312322d9 408 } else {
rgrover1 782:5736312322d9 409 uidIsSet = false;
rgrover1 782:5736312322d9 410 }
rgrover1 782:5736312322d9 411 // Default TLM frame to version 0x00, start all values at zero to be spec compliant.
rgrover1 782:5736312322d9 412 setTLMFrameData(tlmVersion, 0x00,0x00);
rgrover1 782:5736312322d9 413
rgrover1 782:5736312322d9 414 // Initialize Frame transition
rgrover1 782:5736312322d9 415 frameIndex = 0;
rgrover1 782:5736312322d9 416 uidRFU = 0;
rgrover1 782:5736312322d9 417 switchFlag = true;
rgrover1 782:5736312322d9 418
rgrover1 782:5736312322d9 419 /* Reinitialize the BLE stack. This will clear away the existing services and advertising state. */
rgrover1 782:5736312322d9 420 ble.shutdown();
rgrover1 782:5736312322d9 421 ble.init();
rgrover1 782:5736312322d9 422 ble.setTxPower(txPowerLevel);
rgrover1 782:5736312322d9 423 ble.setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
rgrover1 782:5736312322d9 424 ble.setAdvertisingInterval(beaconPeriodus);
rgrover1 782:5736312322d9 425
rgrover1 782:5736312322d9 426 // Make double sure the PDUCount and TimeSinceBoot fields are set to zero at reset
rgrover1 782:5736312322d9 427 updateTlmPduCount(0);
rgrover1 782:5736312322d9 428 updateTlmTimeSinceBoot(0);
rgrover1 782:5736312322d9 429
rgrover1 782:5736312322d9 430 // Construct TLM Frame in initial advertising.
rgrover1 782:5736312322d9 431 serviceData[serviceDataLen++] = BEACON_EDDYSTONE[0];
rgrover1 782:5736312322d9 432 serviceData[serviceDataLen++] = BEACON_EDDYSTONE[1];
rgrover1 782:5736312322d9 433 serviceDataLen += constructTLMFrame(serviceData+serviceDataLen,SERVICE_DATA_MAX);
rgrover1 782:5736312322d9 434
rgrover1 782:5736312322d9 435 updateAdvPacket(serviceData, serviceDataLen);
rgrover1 782:5736312322d9 436 ble.gap().startAdvertising();
rgrover1 782:5736312322d9 437 ble.gap().onRadioNotification(this,&EddystoneService::radioNotificationCallback);
rgrover1 782:5736312322d9 438 timeSinceBootTick.attach(this,&EddystoneService::tsbCallback,0.1); // incriment the TimeSinceBoot ticker every 0.1s
rgrover1 782:5736312322d9 439
rgrover1 771:26b809199308 440 }
rgrover1 771:26b809199308 441
rgrover1 771:26b809199308 442 private:
rgrover1 782:5736312322d9 443
rgrover1 771:26b809199308 444
rgrover1 782:5736312322d9 445 BLEDevice &ble;
rgrover1 782:5736312322d9 446 Ticker timeSinceBootTick;
rgrover1 782:5736312322d9 447 Timeout switchFrame;
rgrover1 782:5736312322d9 448 // Default value that is restored on reset
rgrover1 782:5736312322d9 449 size_t defaultUriDataLength;
rgrover1 782:5736312322d9 450 UriData_t defaultUriData;
rgrover1 782:5736312322d9 451 UIDNamespaceID_t defaultUidNamespaceID;
rgrover1 782:5736312322d9 452 UIDInstanceID_t defaultUidInstanceID;
rgrover1 782:5736312322d9 453 int8_t defaultUidPower;
rgrover1 782:5736312322d9 454 int8_t defaultUrlPower;
rgrover1 782:5736312322d9 455 uint16_t uidRFU;
rgrover1 782:5736312322d9 456 bool uidIsSet;
rgrover1 782:5736312322d9 457 bool urlIsSet;
rgrover1 782:5736312322d9 458 bool switchFlag;
rgrover1 782:5736312322d9 459
rgrover1 782:5736312322d9 460 // Private Variables for Telemetry Data
rgrover1 782:5736312322d9 461 uint8_t TlmVersion;
rgrover1 782:5736312322d9 462 volatile uint16_t TlmBatteryVoltage;
rgrover1 782:5736312322d9 463 volatile uint16_t TlmBeaconTemp;
rgrover1 782:5736312322d9 464 volatile uint32_t TlmPduCount;
rgrover1 782:5736312322d9 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_