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:
mbedAustin
Date:
Wed Sep 02 20:55:32 2015 +0000
Revision:
796:1b0cbb2e48a1
Parent:
795:47db33f1a619
Child:
797:8a542f0ebf3f
changed eddystone config service to be a wrapper around eddystone service

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
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
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 // Check optional frames, set their 'isSet' flags appropriately
rgrover1 786:d6d7087d8377 398 if((uidNamespaceID != NULL) & (uidInstanceID != NULL)) {
rgrover1 786:d6d7087d8377 399 uidIsSet = true;
rgrover1 786:d6d7087d8377 400 setUIDFrameData(txPowerLevel,uidNamespaceID, uidInstanceID);
rgrover1 786:d6d7087d8377 401 } else {
rgrover1 786:d6d7087d8377 402 uidIsSet = false;
rgrover1 786:d6d7087d8377 403 }
rgrover1 786:d6d7087d8377 404 if(url != NULL) {
rgrover1 786:d6d7087d8377 405 urlIsSet = true;
rgrover1 786:d6d7087d8377 406 setURLFrameData(txPowerLevel,url);
rgrover1 786:d6d7087d8377 407 } else {
rgrover1 786:d6d7087d8377 408 uidIsSet = false;
rgrover1 786:d6d7087d8377 409 }
rgrover1 786:d6d7087d8377 410 // Default TLM frame to version 0x00, start all values at zero to be spec compliant.
rgrover1 786:d6d7087d8377 411 setTLMFrameData(tlmVersion, 0x00,0x00);
rgrover1 786:d6d7087d8377 412
rgrover1 786:d6d7087d8377 413 // Initialize Frame transition
rgrover1 786:d6d7087d8377 414 frameIndex = 0;
rgrover1 786:d6d7087d8377 415 uidRFU = 0;
rgrover1 786:d6d7087d8377 416 switchFlag = true;
rgrover1 786:d6d7087d8377 417
rgrover1 786:d6d7087d8377 418 /* Reinitialize the BLE stack. This will clear away the existing services and advertising state. */
rgrover1 786:d6d7087d8377 419 ble.shutdown();
rgrover1 786:d6d7087d8377 420 ble.init();
rgrover1 786:d6d7087d8377 421 ble.setTxPower(txPowerLevel);
rgrover1 786:d6d7087d8377 422 ble.setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
rgrover1 786:d6d7087d8377 423 ble.setAdvertisingInterval(beaconPeriodus);
rgrover1 786:d6d7087d8377 424
rgrover1 786:d6d7087d8377 425 // Make double sure the PDUCount and TimeSinceBoot fields are set to zero at reset
rgrover1 786:d6d7087d8377 426 updateTlmPduCount(0);
rgrover1 786:d6d7087d8377 427 updateTlmTimeSinceBoot(0);
rgrover1 786:d6d7087d8377 428
rgrover1 786:d6d7087d8377 429 // Construct TLM Frame in initial advertising.
rgrover1 786:d6d7087d8377 430 serviceData[serviceDataLen++] = BEACON_EDDYSTONE[0];
rgrover1 786:d6d7087d8377 431 serviceData[serviceDataLen++] = BEACON_EDDYSTONE[1];
rgrover1 786:d6d7087d8377 432 serviceDataLen += constructTLMFrame(serviceData+serviceDataLen,SERVICE_DATA_MAX);
rgrover1 786:d6d7087d8377 433
rgrover1 786:d6d7087d8377 434 updateAdvPacket(serviceData, serviceDataLen);
rgrover1 786:d6d7087d8377 435 ble.gap().startAdvertising();
rgrover1 786:d6d7087d8377 436 ble.gap().onRadioNotification(this,&EddystoneService::radioNotificationCallback);
rgrover1 786:d6d7087d8377 437 timeSinceBootTick.attach(this,&EddystoneService::tsbCallback,0.1); // incriment the TimeSinceBoot ticker every 0.1s
rgrover1 786:d6d7087d8377 438
rgrover1 771:26b809199308 439 }
rgrover1 771:26b809199308 440
rgrover1 771:26b809199308 441 private:
rgrover1 786:d6d7087d8377 442
rgrover1 771:26b809199308 443
rgrover1 786:d6d7087d8377 444 BLEDevice &ble;
rgrover1 786:d6d7087d8377 445 Ticker timeSinceBootTick;
rgrover1 786:d6d7087d8377 446 Timeout switchFrame;
rgrover1 786:d6d7087d8377 447 // Default value that is restored on reset
rgrover1 786:d6d7087d8377 448 size_t defaultUriDataLength;
rgrover1 786:d6d7087d8377 449 UriData_t defaultUriData;
rgrover1 786:d6d7087d8377 450 UIDNamespaceID_t defaultUidNamespaceID;
rgrover1 786:d6d7087d8377 451 UIDInstanceID_t defaultUidInstanceID;
rgrover1 786:d6d7087d8377 452 int8_t defaultUidPower;
rgrover1 786:d6d7087d8377 453 int8_t defaultUrlPower;
rgrover1 786:d6d7087d8377 454 uint16_t uidRFU;
rgrover1 786:d6d7087d8377 455 bool uidIsSet;
rgrover1 786:d6d7087d8377 456 bool urlIsSet;
rgrover1 786:d6d7087d8377 457 bool switchFlag;
rgrover1 786:d6d7087d8377 458
rgrover1 786:d6d7087d8377 459 // Private Variables for Telemetry Data
rgrover1 786:d6d7087d8377 460 uint8_t TlmVersion;
rgrover1 786:d6d7087d8377 461 volatile uint16_t TlmBatteryVoltage;
rgrover1 786:d6d7087d8377 462 volatile uint16_t TlmBeaconTemp;
rgrover1 786:d6d7087d8377 463 volatile uint32_t TlmPduCount;
rgrover1 786:d6d7087d8377 464 volatile uint32_t TlmTimeSinceBoot;
rgrover1 771:26b809199308 465
rgrover1 771:26b809199308 466 public:
rgrover1 771:26b809199308 467 /*
rgrover1 771:26b809199308 468 * Encode a human-readable URI into the binary format defined by URIBeacon spec (https://github.com/google/uribeacon/tree/master/specification).
rgrover1 771:26b809199308 469 */
rgrover1 771:26b809199308 470 static void encodeURL(const char *uriDataIn, UriData_t uriDataOut, size_t &sizeofURIDataOut) {
rgrover1 771:26b809199308 471 const char *prefixes[] = {
rgrover1 771:26b809199308 472 "http://www.",
rgrover1 771:26b809199308 473 "https://www.",
rgrover1 771:26b809199308 474 "http://",
rgrover1 771:26b809199308 475 "https://",
rgrover1 771:26b809199308 476 };
rgrover1 771:26b809199308 477 const size_t NUM_PREFIXES = sizeof(prefixes) / sizeof(char *);
rgrover1 771:26b809199308 478 const char *suffixes[] = {
rgrover1 771:26b809199308 479 ".com/",
rgrover1 771:26b809199308 480 ".org/",
rgrover1 771:26b809199308 481 ".edu/",
rgrover1 771:26b809199308 482 ".net/",
rgrover1 771:26b809199308 483 ".info/",
rgrover1 771:26b809199308 484 ".biz/",
rgrover1 771:26b809199308 485 ".gov/",
rgrover1 771:26b809199308 486 ".com",
rgrover1 771:26b809199308 487 ".org",
rgrover1 771:26b809199308 488 ".edu",
rgrover1 771:26b809199308 489 ".net",
rgrover1 771:26b809199308 490 ".info",
rgrover1 771:26b809199308 491 ".biz",
rgrover1 771:26b809199308 492 ".gov"
rgrover1 771:26b809199308 493 };
rgrover1 771:26b809199308 494 const size_t NUM_SUFFIXES = sizeof(suffixes) / sizeof(char *);
rgrover1 771:26b809199308 495
rgrover1 771:26b809199308 496 sizeofURIDataOut = 0;
rgrover1 771:26b809199308 497 memset(uriDataOut, 0, sizeof(UriData_t));
rgrover1 771:26b809199308 498
rgrover1 771:26b809199308 499 if ((uriDataIn == NULL) || (strlen(uriDataIn) == 0)) {
rgrover1 771:26b809199308 500 return;
rgrover1 771:26b809199308 501 }
rgrover1 771:26b809199308 502
rgrover1 771:26b809199308 503 /*
rgrover1 771:26b809199308 504 * handle prefix
rgrover1 771:26b809199308 505 */
rgrover1 771:26b809199308 506 for (unsigned i = 0; i < NUM_PREFIXES; i++) {
rgrover1 771:26b809199308 507 size_t prefixLen = strlen(prefixes[i]);
rgrover1 771:26b809199308 508 if (strncmp(uriDataIn, prefixes[i], prefixLen) == 0) {
rgrover1 771:26b809199308 509 uriDataOut[sizeofURIDataOut++] = i;
rgrover1 771:26b809199308 510 uriDataIn += prefixLen;
rgrover1 771:26b809199308 511 break;
rgrover1 771:26b809199308 512 }
rgrover1 771:26b809199308 513 }
rgrover1 771:26b809199308 514
rgrover1 771:26b809199308 515 /*
rgrover1 771:26b809199308 516 * handle suffixes
rgrover1 771:26b809199308 517 */
rgrover1 771:26b809199308 518 while (*uriDataIn && (sizeofURIDataOut < URI_DATA_MAX)) {
rgrover1 771:26b809199308 519 /* check for suffix match */
rgrover1 771:26b809199308 520 unsigned i;
rgrover1 771:26b809199308 521 for (i = 0; i < NUM_SUFFIXES; i++) {
rgrover1 771:26b809199308 522 size_t suffixLen = strlen(suffixes[i]);
rgrover1 771:26b809199308 523 if (strncmp(uriDataIn, suffixes[i], suffixLen) == 0) {
rgrover1 771:26b809199308 524 uriDataOut[sizeofURIDataOut++] = i;
rgrover1 771:26b809199308 525 uriDataIn += suffixLen;
rgrover1 771:26b809199308 526 break; /* from the for loop for checking against suffixes */
rgrover1 771:26b809199308 527 }
rgrover1 771:26b809199308 528 }
rgrover1 771:26b809199308 529 /* This is the default case where we've got an ordinary character which doesn't match a suffix. */
rgrover1 771:26b809199308 530 if (i == NUM_SUFFIXES) {
rgrover1 771:26b809199308 531 uriDataOut[sizeofURIDataOut++] = *uriDataIn;
rgrover1 771:26b809199308 532 ++uriDataIn;
rgrover1 771:26b809199308 533 }
rgrover1 771:26b809199308 534 }
rgrover1 771:26b809199308 535 }
rgrover1 771:26b809199308 536 };
rgrover1 771:26b809199308 537
rgrover1 771:26b809199308 538 #endif // SERVICES_EDDYSTONEBEACON_H_