Added an EddystoneURLConfigService in addition to UriBeaconConfigService. Updated README and converted comments that used UriBeacon to EddystoneURL in the EddystoneService.h

Dependents:   mbed_EddystoneURL_Beacon_ssci mbed_EddystoneURL_Beacon_ssci mbed_EddystoneURL_Beacon_ssci

Fork of BLE_API by Bluetooth Low Energy

Committer:
roywant
Date:
Wed Aug 19 04:27:52 2015 +0000
Revision:
797:13164356b568
Parent:
795:6401965b88ee
Updated EddystoneURLConfigService.h : 1) lockedState now is a member of params.lockedState ; zeros are not the unlock value (and a valid key), this now passes the Validator, 2) After disconnect the timeADV is disabled, and ADV params recreated.

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