GhostInTheShell / Mbed 2 deprecated Elearnning

Dependencies:   mbed

Fork of Elearnning by Pablo Trujillo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers EddystoneService.h Source File

EddystoneService.h

00001 /* mbed Microcontroller Library
00002  * Copyright (c) 2006-2015 ARM Limited
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 
00017 #ifndef SERVICES_EDDYSTONEBEACON_H_
00018 #define SERVICES_EDDYSTONEBEACON_H_
00019 
00020 #include "ble/BLE.h"
00021 #include "mbed.h"
00022 #include "CircularBuffer.h"
00023 static const uint8_t BEACON_EDDYSTONE[] = {0xAA, 0xFE};
00024 
00025 //Debug is disabled by default
00026 #if 0
00027 #define DBG(MSG, ...)  printf("[EddyStone: DBG]" MSG " \t[%s,%d]\r\n", \
00028                             ## __VA_ARGS__,                            \
00029                             __FILE__,                                  \
00030                             __LINE__);
00031 #define WARN(MSG, ...) printf("[EddyStone: WARN]" MSG " \t[%s,%d]\r\n", \
00032                             ## __VA_ARGS__,                             \
00033                             __FILE__,                                   \
00034                             __LINE__);
00035 #define ERR(MSG, ...)  printf("[EddyStone: ERR]" MSG " \t[%s,%d]\r\n", \
00036                             ## __VA_ARGS__,                            \
00037                             __FILE__,                                  \
00038                             __LINE__);
00039 #else // if 0
00040 #define DBG(x, ...) //wait_us(10);
00041 #define WARN(x, ...) //wait_us(10);
00042 #define ERR(x, ...)
00043 #endif // if 0
00044 
00045 #if 0
00046 #define INFO(x, ...)  printf("[EddyStone: INFO]"x " \t[%s,%d]\r\n", \
00047                              ## __VA_ARGS__,                        \
00048                              __FILE__,                              \
00049                              __LINE__);
00050 #else // if 0
00051 #define INFO(x, ...)
00052 #endif // if 0
00053 
00054 /**
00055 * @class Eddystone
00056 * @brief Eddystone Configuration Service. Can be used to set URL, adjust power levels, and set flags.
00057 * See https://github.com/google/eddystone
00058 *
00059 */
00060 class EddystoneService
00061 {
00062 public:
00063     enum FrameTypes {
00064         NONE,
00065         url,
00066         uid,
00067         tlm
00068     };
00069 
00070     static const int SERVICE_DATA_MAX = 31;             // Maximum size of service data in ADV packets
00071 
00072     // There are currently 3 subframes defined, URI, UID, and TLM
00073 #define EDDYSTONE_MAX_FRAMETYPE 3
00074     void (*frames[EDDYSTONE_MAX_FRAMETYPE])(uint8_t *, uint32_t);
00075     static const int URI_DATA_MAX = 18;
00076     typedef uint8_t  UriData_t[URI_DATA_MAX];
00077     CircularBuffer<FrameTypes, EDDYSTONE_MAX_FRAMETYPE> overflow;
00078 
00079     // UID Frame Type subfields
00080     static const int UID_NAMESPACEID_SIZE = 10;
00081     typedef uint8_t  UIDNamespaceID_t[UID_NAMESPACEID_SIZE];
00082     static const int UID_INSTANCEID_SIZE = 6;
00083     typedef uint8_t  UIDInstanceID_t[UID_INSTANCEID_SIZE];
00084 
00085     // Eddystone Frame Type ID
00086     static const uint8_t FRAME_TYPE_UID = 0x00;
00087     static const uint8_t FRAME_TYPE_URL = 0x10;
00088     static const uint8_t FRAME_TYPE_TLM = 0x20;
00089 
00090     static const uint8_t FRAME_SIZE_TLM = 14; // TLM frame is a constant 14Bytes
00091     static const uint8_t FRAME_SIZE_UID = 20; // includes RFU bytes
00092 
00093     /**
00094      *  Set Eddystone UID Frame information.
00095      *
00096      *  @param[in] power       TX Power in dB measured at 0 meters from the device. Range of -100 to +20 dB.
00097      *  @param[in] namespaceID 10B namespace ID
00098      *  @param[in] instanceID  6B instance ID
00099      *  @param[in] RFU         2B of RFU, initialized to 0x0000 and not broadcast, included for future reference.
00100      */
00101     void setUIDFrameData(int8_t           power,
00102                          UIDNamespaceID_t namespaceID,
00103                          UIDInstanceID_t  instanceID,
00104                          uint32_t         uidAdvPeriodIn,
00105                          uint16_t         RFU = 0x0000) {
00106         if (0 == uidAdvPeriodIn) {
00107             uidIsSet = false;
00108             return;
00109         }
00110         if (power > 20) {
00111             power = 20;
00112         }
00113         if (power < -100) {
00114             power = -100;
00115         }
00116 
00117         defaultUidPower = power;
00118         memcpy(defaultUidNamespaceID, namespaceID, UID_NAMESPACEID_SIZE);
00119         memcpy(defaultUidInstanceID,  instanceID,  UID_INSTANCEID_SIZE);
00120         uidRFU       = (uint16_t)RFU; // this is probably bad form, but it doesn't really matter yet.
00121         uidAdvPeriod = uidAdvPeriodIn;
00122         uidIsSet     = true;          // set toggle to advertise UID frames
00123     }
00124 
00125     /*
00126     *  Construct UID frame from private variables
00127     *  @param[in/out] Data pointer to array to store constructed frame in
00128     *  @param[in] maxSize number of bytes left in array, effectively how much empty space is available to write to
00129     *  @return number of bytes used. negative number indicates error message.
00130     */
00131     unsigned constructUIDFrame(uint8_t *Data, uint8_t maxSize) {
00132         unsigned index = 0;
00133 
00134         Data[index++] = FRAME_TYPE_UID;                     // 1B  Type
00135 
00136         if (defaultUidPower > 20) {
00137             defaultUidPower = 20;                           // enforce range of vaild values.
00138         }
00139         if (defaultUidPower < -100) {
00140             defaultUidPower = -100;
00141         }
00142         Data[index++] = defaultUidPower;                    // 1B  Power @ 0meter
00143 
00144         DBG("UID NamespaceID = '0x");
00145         for (size_t x = 0; x < UID_NAMESPACEID_SIZE; x++) { // 10B Namespace ID
00146             Data[index++] = defaultUidNamespaceID[x];
00147             DBG("%x,", defaultUidNamespaceID[x]);
00148         }
00149         DBG("'\r\n");
00150 
00151         DBG("UID InstanceID = '0x");
00152         for (size_t x = 0; x< UID_INSTANCEID_SIZE; x++) {   // 6B  Instance ID
00153             Data[index++] = defaultUidInstanceID[x];
00154             DBG("%x,", defaultUidInstanceID[x]);
00155         }
00156         DBG("'\r\n");
00157 
00158         if (0 != uidRFU) {                                  // 2B RFU, include if non-zero, otherwise ignore
00159             Data[index++] = (uint8_t)(uidRFU >> 0);
00160             Data[index++] = (uint8_t)(uidRFU >> 8);
00161         }
00162         DBG("construcUIDFrame %d, %d", maxSize, index);
00163         return index;
00164     }
00165 
00166     /**
00167      *  Set Eddystone URL Frame information.
00168      *  @param[in] power          TX Power in dB measured at 0 meters from the device.
00169      *  @param[in] url            URL to encode
00170      *  @param[in] urlAdvPeriodIn How long to advertise the URL frame (measured in # of adv periods)
00171      *  @return false on success, true on failure.
00172      */
00173     bool setURLFrameData(int8_t power, const char *urlIn, uint32_t urlAdvPeriodIn) {
00174         if (0 == urlAdvPeriodIn) {
00175             urlIsSet = false;
00176             return false;
00177         }
00178         defaultUrlPower = power;
00179         encodeURL(urlIn, defaultUriData, defaultUriDataLength); // encode URL to URL Formatting
00180         if (defaultUriDataLength > URI_DATA_MAX) {
00181             return true;                                        // error, URL is too big
00182         }
00183         urlAdvPeriod = urlAdvPeriodIn;
00184         urlIsSet     = true;
00185         return false;
00186     }
00187 
00188     /*
00189     *  Construct URL frame from private variables
00190     *  @param[in/out] Data pointer to array to store constructed frame in
00191     *  @param[in] maxSize number of bytes left in array, effectively how much emtpy space is available to write to
00192     *  @return number of bytes used. negative number indicates error message.
00193     */
00194     int constructURLFrame(uint8_t *Data, uint8_t maxSize) {
00195         int index = 0;
00196         Data[index++] = FRAME_TYPE_URL;                     // 1B  Type
00197         Data[index++] = defaultUrlPower;                    // 1B  TX Power
00198         for (int x = 0; x < defaultUriDataLength; x++) {    // 18B of URL Prefix + encoded URL
00199             Data[index++] = defaultUriData[x];
00200         }
00201         DBG("constructURLFrame: %d, %d", maxSize, index);
00202         return index;
00203     }
00204 
00205     /*
00206     *  Set Eddystone TLM Frame information.
00207     *  @param[in] Version    of the TLM beacon data format
00208     *  @param[in] advPeriod  how often to advertise the TLM frame for (in minutes)
00209     *  @param batteryVoltage in milivolts
00210     *  @param beaconTemp     in 8.8 floating point notation
00211     *
00212     */
00213     void setTLMFrameData(uint8_t  version        = 0,
00214                          uint32_t advPeriod      = 60,
00215                          uint16_t batteryVoltage = 0,
00216                          uint16_t beaconTemp     = 0,
00217                          uint32_t pduCount       = 0,
00218                          uint32_t timeSinceBoot  = 0) {
00219         if (0 == advPeriod) {
00220             tlmIsSet = false;
00221             return;
00222         }
00223         TlmVersion        = version;
00224         TlmBatteryVoltage = batteryVoltage;
00225         TlmBeaconTemp     = beaconTemp;
00226         TlmPduCount       = pduCount;      // reset
00227         TlmTimeSinceBoot  = timeSinceBoot; // reset
00228         TlmAdvPeriod      = advPeriod;
00229         tlmIsSet          = true;          // TLM Data has been enabled
00230     }
00231 
00232     /*
00233     *  Construct TLM frame from private variables
00234     *  @param[in/out] Data pointer to array to store constructed frame in
00235     *  @param[in] maxSize number of bytes left in array, effectively how much emtpy space is available to write to
00236     *  @return number of bytes used. negative number indicates error message.
00237     */
00238     int constructTLMFrame(uint8_t *Data, uint8_t maxSize) {
00239         int index = 0;
00240         Data[index++] = FRAME_TYPE_TLM;                    // Eddystone frame type = Telemetry
00241         Data[index++] = TlmVersion;                        // TLM Version Number
00242         Data[index++] = (uint8_t)(TlmBatteryVoltage >> 8); // Battery Voltage[0]
00243         Data[index++] = (uint8_t)(TlmBatteryVoltage >> 0); // Battery Voltage[1]
00244         Data[index++] = (uint8_t)(TlmBeaconTemp >> 8);     // Beacon Temp[0]
00245         Data[index++] = (uint8_t)(TlmBeaconTemp >> 0);     // Beacon Temp[1]
00246         Data[index++] = (uint8_t)(TlmPduCount >> 24);      // PDU Count [0]
00247         Data[index++] = (uint8_t)(TlmPduCount >> 16);      // PDU Count [1]
00248         Data[index++] = (uint8_t)(TlmPduCount >> 8);       // PDU Count [2]
00249         Data[index++] = (uint8_t)(TlmPduCount >> 0);       // PDU Count [3]
00250         Data[index++] = (uint8_t)(TlmTimeSinceBoot >> 24); // Time Since Boot [0]
00251         Data[index++] = (uint8_t)(TlmTimeSinceBoot >> 16); // Time Since Boot [1]
00252         Data[index++] = (uint8_t)(TlmTimeSinceBoot >> 8);  // Time Since Boot [2]
00253         Data[index++] = (uint8_t)(TlmTimeSinceBoot >> 0);  // Time Since Boot [3]
00254         DBG("constructURLFrame: %d, %d", maxSize, index);
00255         return index;
00256     }
00257 
00258     /*
00259     *  Update the TLM frame battery voltage value
00260     *  @param[in] voltagemv Voltage to update the TLM field battery voltage with (in mV)
00261     *  @return nothing
00262     */
00263     void updateTlmBatteryVoltage(uint16_t voltagemv) {
00264         TlmBatteryVoltage = voltagemv;
00265     }
00266 
00267     /*
00268     *  Update the TLM frame beacon temperature
00269     *  @param[in] temp Temperature of beacon (in 8.8fpn)
00270     *  @return nothing
00271     */
00272     void updateTlmBeaconTemp(uint16_t temp) {
00273         TlmBeaconTemp = temp;
00274     }
00275 
00276     /*
00277     *  Update the TLM frame PDU Count field
00278     *  @param[in] pduCount Number of Advertisiting frames sent since powerup
00279     *  @return nothing
00280     */
00281     void updateTlmPduCount(uint32_t pduCount) {
00282         TlmPduCount = pduCount;
00283     }
00284 
00285     /*
00286     *  Update the TLM frame Time since boot in 0.1s incriments
00287     *  @param[in] timeSinceBoot Time since boot in 0.1s incriments
00288     *  @return nothing
00289     */
00290     void updateTlmTimeSinceBoot(uint32_t timeSinceBoot) {
00291         TlmTimeSinceBoot = timeSinceBoot;
00292     }
00293 
00294     /*
00295     *  callback function, called every 0.1s, incriments the TimeSinceBoot field in the TLM frame
00296     *  @return nothing
00297     */
00298     void tsbCallback(void) {
00299         TlmTimeSinceBoot++;
00300     }
00301 
00302     /*
00303     * Update advertising data
00304     * @return true on success, false on failure
00305     */
00306     bool updateAdvPacket(uint8_t serviceData[], unsigned serviceDataLen) {
00307         // Fields from the Service
00308         DBG("Updating AdvFrame: %d", serviceDataLen);
00309 
00310         ble.clearAdvertisingPayload();
00311         ble.setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
00312         ble.setAdvertisingInterval (100);
00313         ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
00314         ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, BEACON_EDDYSTONE, sizeof(BEACON_EDDYSTONE));
00315         ble.accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, serviceData, serviceDataLen);
00316 
00317 
00318         return true;
00319     }
00320 
00321     /*
00322     *   State machine for switching out frames.
00323     *   This function is called by the radioNotificationCallback when a frame needs to get swapped out.
00324     *   This function exists because of time constraints in the radioNotificationCallback, so it is effectively
00325     *   broken up into two functions.
00326     */
00327     void swapOutFrames(FrameTypes frameType) {
00328         uint8_t  serviceData[SERVICE_DATA_MAX];
00329         unsigned serviceDataLen = 0;
00330         //hard code in the eddystone UUID
00331         serviceData[serviceDataLen++] = BEACON_EDDYSTONE[0];
00332         serviceData[serviceDataLen++] = BEACON_EDDYSTONE[1];
00333 
00334         // if certain frames are not enabled, then skip them. Worst case TLM is always enabled
00335         switch (frameType) {
00336             case tlm:
00337                 // TLM frame
00338                 if (tlmIsSet) {
00339                     DBG("Swapping in TLM Frame: version=%x, Batt=%d, Temp = %d, PDUCnt = %d, TimeSinceBoot=%d",
00340                         TlmVersion,
00341                         TlmBatteryVoltage,
00342                         TlmBeaconTemp,
00343                         TlmPduCount,
00344                         TlmTimeSinceBoot);
00345                     serviceDataLen += constructTLMFrame(serviceData + serviceDataLen, 20);
00346                     DBG("\t Swapping in TLM Frame: len=%d", serviceDataLen);
00347                     updateAdvPacket(serviceData, serviceDataLen);
00348                 }
00349                 break;
00350             case url:
00351                 // URL Frame
00352                 if (urlIsSet) {
00353                     DBG("Swapping in URL Frame: Power: %d", defaultUrlPower);
00354                     serviceDataLen += constructURLFrame(serviceData + serviceDataLen, 20);
00355                     DBG("\t Swapping in URL Frame: len=%d ", serviceDataLen);
00356                     updateAdvPacket(serviceData, serviceDataLen);
00357                     //switchFlag = false;
00358                 }
00359                 break;
00360             case uid:
00361                 // UID Frame
00362                 if (uidIsSet) {
00363                     DBG("Swapping in UID Frame: Power: %d", defaultUidPower);
00364                     serviceDataLen += constructUIDFrame(serviceData + serviceDataLen, 20);
00365                     DBG("\t Swapping in UID Frame: len=%d", serviceDataLen);
00366                     updateAdvPacket(serviceData, serviceDataLen);
00367                     //switchFlag = false;
00368                 }
00369                 break;
00370             default:
00371                 ERR("You have not initialized a Frame yet, please initialize one before starting a beacon");
00372                 ERR("uidIsSet = %d, urlIsSet = %d, tlmIsSet = %d", uidIsSet, urlIsSet, tlmIsSet);
00373         }
00374     }
00375 
00376     /*
00377     * Callback to swap in URL frame
00378     */
00379     void urlCallback(void) {
00380         DBG("urlCallback");
00381         if (false == advLock) {
00382             advLock = true;
00383             DBG("advLock = url")
00384             frameIndex = url;
00385             swapOutFrames(frameIndex);
00386             ble.startAdvertising();
00387         } else {
00388             // Someone else is broadcasting, toss it into the overflow buffer to retransmit when free
00389             INFO("URI(%d) cannot complete, %d is currently broadcasting", url, frameIndex);
00390             FrameTypes x = url;
00391             overflow.push(x);
00392         }
00393     }
00394 
00395     /*
00396     * Callback to swap in UID frame
00397     */
00398     void uidCallback(void) {
00399         DBG("uidCallback");
00400         if (false == advLock) {
00401             advLock = true;
00402             DBG("advLock = uid")
00403             frameIndex = uid;
00404             swapOutFrames(frameIndex);
00405             ble.startAdvertising();
00406         } else {
00407             // Someone else is broadcasting, toss it into the overflow buffer to retransmit when free
00408             INFO("UID(%d) cannot complete, %d is currently broadcasting", uid, frameIndex);
00409             FrameTypes x = uid; // have to do this to satisfy cont vs volatile keywords... sigh...
00410             overflow.push(x);
00411         }
00412     }
00413 
00414     /*
00415     * Callback to swap in TLM frame
00416     */
00417     void tlmCallback(void) {
00418         DBG("tlmCallback");
00419         if (false == advLock) {
00420             // OK to broadcast
00421             advLock = true;
00422             DBG("advLock = tlm")
00423             frameIndex = tlm;
00424             swapOutFrames(frameIndex);
00425             ble.startAdvertising();
00426         } else {
00427             // Someone else is broadcasting, toss it into the overflow buffer to retransmit when free
00428             INFO("TLM(%d) cannot complete, %d is currently broadcasting", tlm, frameIndex);
00429             FrameTypes x = tlm;
00430             overflow.push(x);
00431         }
00432     }
00433 
00434     void stopAdvCallback(void) {
00435         if (overflow.empty()) {
00436             // if nothing left to transmit, stop
00437             ble.stopAdvertising();
00438             advLock = false; // unlock lock
00439         } else {
00440             // transmit other packets at current time index
00441             FrameTypes x = NONE;
00442             overflow.pop(x);
00443             INFO("Re-Transmitting %d", x);
00444             swapOutFrames(x);
00445         }
00446     }
00447 
00448     /*
00449     *  Callback from onRadioNotification(), used to update the PDUCounter and process next state.
00450     */
00451 #define EDDYSTONE_SWAPFRAME_DELAYMS 1
00452     void radioNotificationCallback(bool radioActive) {
00453         // Update PDUCount
00454         TlmPduCount++;
00455         // True just before an frame is sent, false just after a frame is sent
00456         if (radioActive) {
00457             // Do Nothing
00458         } else {
00459             // Packet has been sent, disable advertising
00460             stopAdv.attach_us(this, &EddystoneService::stopAdvCallback, 1);
00461         }
00462     }
00463 
00464     /*
00465     *   This function explicityly sets the parameters used by the Eddystone beacon.
00466     *   this function should be used in leu of the config service.
00467     *
00468     *   @param bleIn ble object used to broadcast eddystone information
00469     *   @param beaconPeriodus is how often ble broadcasts are mde, in mili seconds
00470     *   @param txPowerLevel sets the broadcasting power level.
00471     *
00472     */
00473     EddystoneService(BLEDevice &bleIn,
00474                      uint16_t   beaconPeriodus = 100,
00475                      uint8_t    txPowerIn      = 0) :
00476         ble(bleIn),
00477         advPeriodus(beaconPeriodus),
00478         txPower(txPowerIn),
00479         advLock(false),
00480         frameIndex(NONE) {
00481     }
00482 
00483     /*
00484     * @breif this function starts eddystone advertising based on configured frames.
00485     */
00486     void start(void) {
00487         // Initialize Frame transition, start with URL to pass eddystone validator app on first try
00488         if (urlIsSet) {
00489             frameIndex = url;
00490             urlTicker.attach(this, &EddystoneService::urlCallback, urlAdvPeriod);
00491             DBG("attached urlCallback every %d seconds", urlAdvPeriod);
00492         }
00493         if (uidIsSet) {
00494             frameIndex = uid;
00495             uidTicker.attach(this, &EddystoneService::uidCallback, uidAdvPeriod);
00496             DBG("attached uidCallback every %d seconds", uidAdvPeriod);
00497         }
00498         if (tlmIsSet) {
00499             frameIndex = tlm;
00500             // Make double sure the PDUCount and TimeSinceBoot fields are set to zero at reset
00501             updateTlmPduCount(0);
00502             updateTlmTimeSinceBoot(0);
00503             timeSinceBootTick.attach(this, &EddystoneService::tsbCallback, 0.1); // incriment the TimeSinceBoot ticker every 0.1s
00504             tlmTicker.attach(this, &EddystoneService::tlmCallback, TlmAdvPeriod);
00505             DBG("attached tlmCallback every %d seconds", TlmAdvPeriod);
00506         }
00507         if (NONE == frameIndex) {
00508             error("No Frames were Initialized! Please initialize a frame before starting an eddystone beacon.");
00509         }
00510         //uidRFU = 0;
00511 
00512         ble.setTxPower(txPower);
00513         ble.gap().onRadioNotification(this, &EddystoneService::radioNotificationCallback);
00514     }
00515 
00516 private:
00517 
00518     // Eddystone Variables
00519     BLEDevice           &ble;
00520     uint16_t            advPeriodus;
00521     uint8_t             txPower;
00522     Ticker              timeSinceBootTick;  // counter that counts time since boot
00523     volatile bool       advLock;
00524     volatile FrameTypes frameIndex;
00525     Timeout             stopAdv;
00526 
00527 
00528     // URI Frame Variables
00529     uint8_t             defaultUriDataLength;
00530     UriData_t           defaultUriData;
00531     int8_t              defaultUrlPower;
00532     bool                urlIsSet;       // flag that enables / disable URI Frames
00533     uint32_t            urlAdvPeriod;   // how long the url frame will be advertised for
00534     Ticker              urlTicker;
00535 
00536     // UID Frame Variables
00537     UIDNamespaceID_t    defaultUidNamespaceID;
00538     UIDInstanceID_t     defaultUidInstanceID;
00539     int8_t              defaultUidPower;
00540     uint16_t            uidRFU;
00541     bool                uidIsSet;       // flag that enables / disable UID Frames
00542     uint32_t            uidAdvPeriod;   // how long the uid frame will be advertised for
00543     Ticker              uidTicker;
00544 
00545     // TLM Frame Variables
00546     uint8_t             TlmVersion;
00547     volatile uint16_t   TlmBatteryVoltage;
00548     volatile uint16_t   TlmBeaconTemp;
00549     volatile uint32_t   TlmPduCount;
00550     volatile uint32_t   TlmTimeSinceBoot;
00551     bool                tlmIsSet;          // flag that enables / disables TLM frames
00552     uint32_t            TlmAdvPeriod;      // number of minutes between adv frames
00553     Ticker              tlmTicker;
00554 
00555 public:
00556     /*
00557      *  Encode a human-readable URI into the binary format defined by URIBeacon spec (https://github.com/google/uribeacon/tree/master/specification).
00558      */
00559     static void encodeURL(const char *uriDataIn, UriData_t uriDataOut, uint8_t &sizeofURIDataOut) {
00560         DBG("Encode URL = %s", uriDataIn);
00561         const char  *prefixes[] = {
00562             "http://www.",
00563             "https://www.",
00564             "http://",
00565             "https://",
00566         };
00567         const size_t NUM_PREFIXES = sizeof(prefixes) / sizeof(char *);
00568         const char  *suffixes[]   = {
00569             ".com/",
00570             ".org/",
00571             ".edu/",
00572             ".net/",
00573             ".info/",
00574             ".biz/",
00575             ".gov/",
00576             ".com",
00577             ".org",
00578             ".edu",
00579             ".net",
00580             ".info",
00581             ".biz",
00582             ".gov"
00583         };
00584         const size_t NUM_SUFFIXES = sizeof(suffixes) / sizeof(char *);
00585 
00586         sizeofURIDataOut = 0;
00587         memset(uriDataOut, 0, sizeof(UriData_t));
00588 
00589         if ((uriDataIn == NULL) || (strlen(uriDataIn) == 0)) {
00590             return;
00591         }
00592 
00593         /*
00594          * handle prefix
00595          */
00596         for (unsigned i = 0; i < NUM_PREFIXES; i++) {
00597             size_t prefixLen = strlen(prefixes[i]);
00598             if (strncmp(uriDataIn, prefixes[i], prefixLen) == 0) {
00599                 uriDataOut[sizeofURIDataOut++]  = i;
00600                 uriDataIn                      += prefixLen;
00601                 break;
00602             }
00603         }
00604 
00605         /*
00606          * handle suffixes
00607          */
00608         while (*uriDataIn && (sizeofURIDataOut < URI_DATA_MAX)) {
00609             /* check for suffix match */
00610             unsigned i;
00611             for (i = 0; i < NUM_SUFFIXES; i++) {
00612                 size_t suffixLen = strlen(suffixes[i]);
00613                 if (strncmp(uriDataIn, suffixes[i], suffixLen) == 0) {
00614                     uriDataOut[sizeofURIDataOut++]  = i;
00615                     uriDataIn                      += suffixLen;
00616                     break; /* from the for loop for checking against suffixes */
00617                 }
00618             }
00619             /* This is the default case where we've got an ordinary character which doesn't match a suffix. */
00620             INFO("Encoding URI: No Suffix Found");
00621             if (i == NUM_SUFFIXES) {
00622                 uriDataOut[sizeofURIDataOut++] = *uriDataIn;
00623                 ++uriDataIn;
00624             }
00625         }
00626     }
00627 };
00628 
00629 #endif  // SERVICES_EDDYSTONEBEACON_H_