High level Bluetooth Low Energy API and radio abstraction layer

Fork of BLE_API by Bluetooth Low Energy

Committer:
mbedAustin
Date:
Thu Sep 03 02:55:30 2015 +0000
Revision:
798:8cea5d9c12c0
Parent:
797:8a542f0ebf3f
changed thing to make UID's work correctly;

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