Example program for the Eddystone Beacon service.

Dependencies:   BLE_API mbed nRF51822 X_NUCLEO_IDB0XA1

Fork of BLE_EddystoneBeacon by URIBeacon

This example demonstrates how to set up and initialize a Eddystone Beacon. For more details on the Eddystone specification please see the Eddystone Github Page.

The Basics

An Eddystone Beacon is a Bluetooth Low Energy beacon, that means it does all of its data transfer in GAP advertising packets. Eddystone beacons, unlike other beacons, broadcast multiple types of data. Currently Eddystone beacons have 3 frame types (UID, URL, TLM) that will get swapped out periodically. This swapping of frame data allows Eddystone beacons to send many different types of data, thus increasing their usefulness over traditional beacons. Note that the UID frame type provides the same 16Bytes of UUID namespace that iBeacons do and the URL frame type provides the same functionality as a URIBeacon.

For more details see the Eddystone Specification.

Smartphone Apps

nRF Master Control Panel - this program recognizes Eddystone beacons and will display the data for all frame types.

iPhone Physical Web app

Android App

Walkthrough of Physical Web application

Technical Details

The Eddystone Specification looks like the following image. Please note that this may change over time and for up to date information the official spec should be referenced. /media/uploads/mbedAustin/scratch-1-.png

The Eddystone Frames get swapped in and out depending on what frames you have enabled. The only required frame type is the TLM frame, all others are optional and you can have any number enabled. To disable the UID or URL frames give their values a 'NULL' in the Eddystone constructor. The Eddystone spec recommends broadcasting 10 frames a second.

Note

  • The current Eddystone mbed example does not allow for combining the eddystone service with other services, this will be changes in a future update.
  • There is an Eddystone Config service that allows for updating beacons in the field. We are working on an example for this, so keep your eyes pealed for a future update.
Committer:
mbedAustin
Date:
Fri Jul 17 19:02:54 2015 +0000
Revision:
10:b5d19bcf23cf
Parent:
7:e9800c45e065
Child:
11:73ea4ef7f5a4
added private variables and set functions for 3 types of beacon frames.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
screamer 0:c04d932e96c9 1 /* mbed Microcontroller Library
screamer 0:c04d932e96c9 2 * Copyright (c) 2006-2013 ARM Limited
screamer 0:c04d932e96c9 3 *
screamer 0:c04d932e96c9 4 * Licensed under the Apache License, Version 2.0 (the "License");
screamer 0:c04d932e96c9 5 * you may not use this file except in compliance with the License.
screamer 0:c04d932e96c9 6 * You may obtain a copy of the License at
screamer 0:c04d932e96c9 7 *
screamer 0:c04d932e96c9 8 * http://www.apache.org/licenses/LICENSE-2.0
screamer 0:c04d932e96c9 9 *
screamer 0:c04d932e96c9 10 * Unless required by applicable law or agreed to in writing, software
screamer 0:c04d932e96c9 11 * distributed under the License is distributed on an "AS IS" BASIS,
screamer 0:c04d932e96c9 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
screamer 0:c04d932e96c9 13 * See the License for the specific language governing permissions and
screamer 0:c04d932e96c9 14 * limitations under the License.
screamer 0:c04d932e96c9 15 */
screamer 0:c04d932e96c9 16
screamer 0:c04d932e96c9 17 #ifndef SERVICES_ZIPBEACONCONFIGSERVICE_H_
screamer 0:c04d932e96c9 18 #define SERVICES_ZIPBEACONCONFIGSERVICE_H_
screamer 0:c04d932e96c9 19
rgrover1 7:e9800c45e065 20 #include "BLE.h"
screamer 0:c04d932e96c9 21 #include "mbed.h"
screamer 0:c04d932e96c9 22
screamer 0:c04d932e96c9 23 #define UUID_URI_BEACON(FIRST, SECOND) { \
screamer 0:c04d932e96c9 24 0xee, 0x0c, FIRST, SECOND, 0x87, 0x86, 0x40, 0xba, \
screamer 0:c04d932e96c9 25 0xab, 0x96, 0x99, 0xb9, 0x1a, 0xc9, 0x81, 0xd8, \
screamer 0:c04d932e96c9 26 }
screamer 0:c04d932e96c9 27
screamer 0:c04d932e96c9 28 static const uint8_t UUID_URI_BEACON_SERVICE[] = UUID_URI_BEACON(0x20, 0x80);
screamer 0:c04d932e96c9 29 static const uint8_t UUID_LOCK_STATE_CHAR[] = UUID_URI_BEACON(0x20, 0x81);
screamer 0:c04d932e96c9 30 static const uint8_t UUID_LOCK_CHAR[] = UUID_URI_BEACON(0x20, 0x82);
screamer 0:c04d932e96c9 31 static const uint8_t UUID_UNLOCK_CHAR[] = UUID_URI_BEACON(0x20, 0x83);
screamer 0:c04d932e96c9 32 static const uint8_t UUID_URI_DATA_CHAR[] = UUID_URI_BEACON(0x20, 0x84);
screamer 0:c04d932e96c9 33 static const uint8_t UUID_FLAGS_CHAR[] = UUID_URI_BEACON(0x20, 0x85);
screamer 0:c04d932e96c9 34 static const uint8_t UUID_ADV_POWER_LEVELS_CHAR[] = UUID_URI_BEACON(0x20, 0x86);
screamer 0:c04d932e96c9 35 static const uint8_t UUID_TX_POWER_MODE_CHAR[] = UUID_URI_BEACON(0x20, 0x87);
screamer 0:c04d932e96c9 36 static const uint8_t UUID_BEACON_PERIOD_CHAR[] = UUID_URI_BEACON(0x20, 0x88);
screamer 0:c04d932e96c9 37 static const uint8_t UUID_RESET_CHAR[] = UUID_URI_BEACON(0x20, 0x89);
screamer 0:c04d932e96c9 38 static const uint8_t BEACON_UUID[] = {0xAA, 0xFE};
screamer 0:c04d932e96c9 39
screamer 0:c04d932e96c9 40 /**
screamer 0:c04d932e96c9 41 * @class ZipBeaconConfigService
screamer 0:c04d932e96c9 42 * @brief ZipBeacon Configuration Service. Can be used to set URL, adjust power levels, and set flags.
screamer 0:c04d932e96c9 43 * See http://uribeacon.org
screamer 0:c04d932e96c9 44 *
screamer 0:c04d932e96c9 45 */
screamer 0:c04d932e96c9 46 class ZipBeaconConfigService {
screamer 0:c04d932e96c9 47 public:
screamer 0:c04d932e96c9 48 /**
screamer 0:c04d932e96c9 49 * @brief Transmission Power Modes for UriBeacon
screamer 0:c04d932e96c9 50 */
screamer 0:c04d932e96c9 51 static const uint8_t TX_POWER_MODE_LOWEST = 0; /*!< Lowest TX power mode */
screamer 0:c04d932e96c9 52 static const uint8_t TX_POWER_MODE_LOW = 1; /*!< Low TX power mode */
screamer 0:c04d932e96c9 53 static const uint8_t TX_POWER_MODE_MEDIUM = 2; /*!< Medium TX power mode */
screamer 0:c04d932e96c9 54 static const uint8_t TX_POWER_MODE_HIGH = 3; /*!< High TX power mode */
screamer 0:c04d932e96c9 55 static const unsigned int NUM_POWER_MODES = 4; /*!< Number of Power Modes defined */
screamer 0:c04d932e96c9 56
screamer 0:c04d932e96c9 57 static const int ADVERTISING_INTERVAL_MSEC = 1000; // Advertising interval for config service.
screamer 0:c04d932e96c9 58 static const int SERVICE_DATA_MAX = 31; // Maximum size of service data in ADV packets
screamer 0:c04d932e96c9 59
screamer 0:c04d932e96c9 60 typedef uint8_t Lock_t[16]; /* 128 bits */
screamer 0:c04d932e96c9 61 typedef int8_t PowerLevels_t[NUM_POWER_MODES];
screamer 0:c04d932e96c9 62
screamer 0:c04d932e96c9 63 static const int URI_DATA_MAX = 18;
screamer 0:c04d932e96c9 64 typedef uint8_t UriData_t[URI_DATA_MAX];
mbedAustin 10:b5d19bcf23cf 65
mbedAustin 10:b5d19bcf23cf 66 static const int UID_NAMESPACEID_SIZE = 10;
mbedAustin 10:b5d19bcf23cf 67 typedef uint8_t UIDNamespaceID_t[UUID_NAMESPACEID_SIZE];
mbedAustin 10:b5d19bcf23cf 68
mbedAustin 10:b5d19bcf23cf 69 static const int UID_INSTANCEID_SIZE = 6;
mbedAustin 10:b5d19bcf23cf 70 typedef uint8_t UIDInstanceID_t[UUID_INSTANCEID_SIZE];
screamer 0:c04d932e96c9 71
screamer 0:c04d932e96c9 72 static const uint8_t FRAME_TYPE_UID = 0x00;
screamer 0:c04d932e96c9 73 static const uint8_t FRAME_TYPE_URL = 0x10;
screamer 0:c04d932e96c9 74 static const uint8_t FRAME_TYPE_TLM = 0x20;
screamer 0:c04d932e96c9 75
screamer 0:c04d932e96c9 76 struct Params_t {
screamer 0:c04d932e96c9 77 Lock_t lock;
screamer 0:c04d932e96c9 78 uint8_t uriDataLength;
screamer 0:c04d932e96c9 79 UriData_t uriData;
screamer 0:c04d932e96c9 80 uint8_t flags;
screamer 0:c04d932e96c9 81 PowerLevels_t advPowerLevels; // Current value of AdvertisedPowerLevels
screamer 0:c04d932e96c9 82 uint8_t txPowerMode; // Firmware power levels used with setTxPower()
screamer 0:c04d932e96c9 83 uint16_t beaconPeriod;
mbedAustin 10:b5d19bcf23cf 84 uint8_t tlmVersion; // version of TLM packet
mbedAustin 10:b5d19bcf23cf 85 UUIDNamespaceID_t uidNamespaceID; // UUID type, Namespace ID, 10B
mbedAustin 10:b5d19bcf23cf 86 UUIDInstanceID_t uidInstanceID; // UUID type, Instance ID, 6B
screamer 0:c04d932e96c9 87 };
screamer 0:c04d932e96c9 88
screamer 0:c04d932e96c9 89 /**
screamer 0:c04d932e96c9 90 * @param[ref] ble
screamer 0:c04d932e96c9 91 * BLEDevice object for the underlying controller.
screamer 0:c04d932e96c9 92 * @param[in/out] paramsIn
screamer 0:c04d932e96c9 93 * Reference to application-visible beacon state, loaded
screamer 0:c04d932e96c9 94 * from persistent storage at startup.
screamer 0:c04d932e96c9 95 * @paramsP[in] resetToDefaultsFlag
screamer 0:c04d932e96c9 96 * Applies to the state of the 'paramsIn' parameter.
screamer 0:c04d932e96c9 97 * If true, it indicates that paramsIn is potentially
screamer 0:c04d932e96c9 98 * un-initialized, and default values should be used
screamer 0:c04d932e96c9 99 * instead. Otherwise, paramsIn overrides the defaults.
screamer 0:c04d932e96c9 100 * @param[in] defaultUriDataIn
screamer 0:c04d932e96c9 101 * Default un-encoded URI; applies only if the resetToDefaultsFlag is true.
screamer 0:c04d932e96c9 102 * @param[in] defaultAdvPowerLevelsIn
screamer 0:c04d932e96c9 103 * Default power-levels array; applies only if the resetToDefaultsFlag is true.
screamer 0:c04d932e96c9 104 */
screamer 0:c04d932e96c9 105 ZipBeaconConfigService(BLEDevice &bleIn,
screamer 0:c04d932e96c9 106 Params_t &paramsIn,
screamer 0:c04d932e96c9 107 bool resetToDefaultsFlag,
screamer 0:c04d932e96c9 108 const char *defaultURIDataIn,
screamer 0:c04d932e96c9 109 PowerLevels_t &defaultAdvPowerLevelsIn) :
screamer 0:c04d932e96c9 110 ble(bleIn),
screamer 0:c04d932e96c9 111 params(paramsIn),
screamer 0:c04d932e96c9 112 defaultUriDataLength(),
screamer 0:c04d932e96c9 113 defaultUriData(),
screamer 0:c04d932e96c9 114 defaultAdvPowerLevels(defaultAdvPowerLevelsIn),
screamer 0:c04d932e96c9 115 initSucceeded(false),
screamer 0:c04d932e96c9 116 resetFlag(),
screamer 0:c04d932e96c9 117 lockedStateChar(UUID_LOCK_STATE_CHAR, &lockedState),
screamer 0:c04d932e96c9 118 lockChar(UUID_LOCK_CHAR, &params.lock),
screamer 0:c04d932e96c9 119 uriDataChar(UUID_URI_DATA_CHAR, params.uriData, 0, URI_DATA_MAX,
screamer 0:c04d932e96c9 120 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE),
screamer 0:c04d932e96c9 121 unlockChar(UUID_UNLOCK_CHAR, &params.lock),
screamer 0:c04d932e96c9 122 flagsChar(UUID_FLAGS_CHAR, &params.flags),
screamer 0:c04d932e96c9 123 advPowerLevelsChar(UUID_ADV_POWER_LEVELS_CHAR, &params.advPowerLevels),
screamer 0:c04d932e96c9 124 txPowerModeChar(UUID_TX_POWER_MODE_CHAR, &params.txPowerMode),
screamer 0:c04d932e96c9 125 beaconPeriodChar(UUID_BEACON_PERIOD_CHAR, &params.beaconPeriod),
screamer 0:c04d932e96c9 126 resetChar(UUID_RESET_CHAR, &resetFlag) {
screamer 0:c04d932e96c9 127
mbedAustin 10:b5d19bcf23cf 128 encodeURI(defaultURIDataIn, defaultUriData, defaultUriDataLength); // encode URL to URL Formatting
screamer 0:c04d932e96c9 129 if (defaultUriDataLength > URI_DATA_MAX) {
screamer 0:c04d932e96c9 130 return;
screamer 0:c04d932e96c9 131 }
screamer 0:c04d932e96c9 132
screamer 0:c04d932e96c9 133 if (!resetToDefaultsFlag && (params.uriDataLength > URI_DATA_MAX)) {
screamer 0:c04d932e96c9 134 resetToDefaultsFlag = true;
screamer 0:c04d932e96c9 135 }
screamer 0:c04d932e96c9 136 if (resetToDefaultsFlag) {
screamer 0:c04d932e96c9 137 resetToDefaults();
screamer 0:c04d932e96c9 138 } else {
screamer 0:c04d932e96c9 139 updateCharacteristicValues();
screamer 0:c04d932e96c9 140 }
screamer 0:c04d932e96c9 141
screamer 0:c04d932e96c9 142 lockedState = isLocked();
screamer 0:c04d932e96c9 143
screamer 0:c04d932e96c9 144 lockChar.setWriteAuthorizationCallback(this, &ZipBeaconConfigService::lockAuthorizationCallback);
screamer 0:c04d932e96c9 145 unlockChar.setWriteAuthorizationCallback(this, &ZipBeaconConfigService::unlockAuthorizationCallback);
screamer 0:c04d932e96c9 146 uriDataChar.setWriteAuthorizationCallback(this, &ZipBeaconConfigService::uriDataWriteAuthorizationCallback);
screamer 0:c04d932e96c9 147 flagsChar.setWriteAuthorizationCallback(this, &ZipBeaconConfigService::basicAuthorizationCallback<uint8_t>);
screamer 0:c04d932e96c9 148 advPowerLevelsChar.setWriteAuthorizationCallback(this, &ZipBeaconConfigService::basicAuthorizationCallback<PowerLevels_t>);
screamer 0:c04d932e96c9 149 txPowerModeChar.setWriteAuthorizationCallback(this, &ZipBeaconConfigService::powerModeAuthorizationCallback);
screamer 0:c04d932e96c9 150 beaconPeriodChar.setWriteAuthorizationCallback(this, &ZipBeaconConfigService::basicAuthorizationCallback<uint16_t>);
screamer 0:c04d932e96c9 151 resetChar.setWriteAuthorizationCallback(this, &ZipBeaconConfigService::basicAuthorizationCallback<uint8_t>);
screamer 0:c04d932e96c9 152
screamer 0:c04d932e96c9 153 static GattCharacteristic *charTable[] = {
screamer 0:c04d932e96c9 154 &lockedStateChar, &lockChar, &unlockChar, &uriDataChar,
screamer 0:c04d932e96c9 155 &flagsChar, &advPowerLevelsChar, &txPowerModeChar, &beaconPeriodChar, &resetChar
screamer 0:c04d932e96c9 156 };
screamer 0:c04d932e96c9 157
screamer 0:c04d932e96c9 158 GattService configService(UUID_URI_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
screamer 0:c04d932e96c9 159
screamer 0:c04d932e96c9 160 ble.addService(configService);
screamer 0:c04d932e96c9 161 ble.onDataWritten(this, &ZipBeaconConfigService::onDataWrittenCallback);
screamer 0:c04d932e96c9 162
screamer 0:c04d932e96c9 163 setupZipBeaconConfigAdvertisements(); /* Setup advertising for the configService. */
screamer 0:c04d932e96c9 164
screamer 0:c04d932e96c9 165 initSucceeded = true;
screamer 0:c04d932e96c9 166 }
screamer 0:c04d932e96c9 167
screamer 0:c04d932e96c9 168 bool configuredSuccessfully(void) const {
screamer 0:c04d932e96c9 169 return initSucceeded;
screamer 0:c04d932e96c9 170 }
screamer 0:c04d932e96c9 171
screamer 0:c04d932e96c9 172 /* Start out by advertising the configService for a limited time after
screamer 0:c04d932e96c9 173 * startup; and switch to the normal non-connectible beacon functionality
screamer 0:c04d932e96c9 174 * afterwards. */
screamer 0:c04d932e96c9 175 void setupZipBeaconConfigAdvertisements()
screamer 0:c04d932e96c9 176 {
mbedAustin 10:b5d19bcf23cf 177 const char DEVICE_NAME[] = "eddystone Config";
screamer 0:c04d932e96c9 178
screamer 0:c04d932e96c9 179 ble.clearAdvertisingPayload();
screamer 0:c04d932e96c9 180
screamer 0:c04d932e96c9 181 ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
screamer 0:c04d932e96c9 182
screamer 0:c04d932e96c9 183 // UUID is in different order in the ADV frame (!)
screamer 0:c04d932e96c9 184 uint8_t reversedServiceUUID[sizeof(UUID_URI_BEACON_SERVICE)];
screamer 0:c04d932e96c9 185 for (unsigned int i = 0; i < sizeof(UUID_URI_BEACON_SERVICE); i++) {
screamer 0:c04d932e96c9 186 reversedServiceUUID[i] = UUID_URI_BEACON_SERVICE[sizeof(UUID_URI_BEACON_SERVICE) - i - 1];
screamer 0:c04d932e96c9 187 }
screamer 0:c04d932e96c9 188 ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, reversedServiceUUID, sizeof(reversedServiceUUID));
screamer 0:c04d932e96c9 189 ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG);
screamer 0:c04d932e96c9 190 ble.accumulateScanResponse(GapAdvertisingData::COMPLETE_LOCAL_NAME, reinterpret_cast<const uint8_t *>(&DEVICE_NAME), sizeof(DEVICE_NAME));
screamer 0:c04d932e96c9 191 ble.accumulateScanResponse(
screamer 0:c04d932e96c9 192 GapAdvertisingData::TX_POWER_LEVEL,
screamer 0:c04d932e96c9 193 reinterpret_cast<uint8_t *>(&defaultAdvPowerLevels[ZipBeaconConfigService::TX_POWER_MODE_LOW]),
screamer 0:c04d932e96c9 194 sizeof(uint8_t));
screamer 0:c04d932e96c9 195
screamer 0:c04d932e96c9 196 ble.setTxPower(params.advPowerLevels[params.txPowerMode]);
screamer 0:c04d932e96c9 197 ble.setDeviceName(reinterpret_cast<const uint8_t *>(&DEVICE_NAME));
screamer 0:c04d932e96c9 198 ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
screamer 0:c04d932e96c9 199 ble.setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(ADVERTISING_INTERVAL_MSEC));
screamer 0:c04d932e96c9 200 }
mbedAustin 10:b5d19bcf23cf 201
mbedAustin 10:b5d19bcf23cf 202 /*
mbedAustin 10:b5d19bcf23cf 203 * Set Eddystone UID Frame information.
mbedAustin 10:b5d19bcf23cf 204 * @param[in] power TX Power in dB measured at 0 meters from the device. Range of -100 to +20 dB.
mbedAustin 10:b5d19bcf23cf 205 * @param namespaceID 10B namespace ID
mbedAustin 10:b5d19bcf23cf 206 * @param instanceID 6B instance ID
mbedAustin 10:b5d19bcf23cf 207 * @param RFU 2B of RFU, initialized to 0x0000 and not broadcast, included for future reference.
mbedAustin 10:b5d19bcf23cf 208 *
mbedAustin 10:b5d19bcf23cf 209 */
mbedAustin 10:b5d19bcf23cf 210 void setUIDFrameData(int8_t power, UIDNamespaceID_t namespaceID, UIDInstanceID_t instanceID, uint8_t RFU[2] = [0x00,0x00])
mbedAustin 10:b5d19bcf23cf 211 {
mbedAustin 10:b5d19bcf23cf 212 defaultUidNamespaceID = namespaceID;
mbedAustin 10:b5d19bcf23cf 213 defaultUidInstanceID = instanceID;
mbedAustin 10:b5d19bcf23cf 214 uidRFU = (uint16_t)RFU;
mbedAustin 10:b5d19bcf23cf 215 return;
mbedAustin 10:b5d19bcf23cf 216 }
mbedAustin 10:b5d19bcf23cf 217
mbedAustin 10:b5d19bcf23cf 218 /*
mbedAustin 10:b5d19bcf23cf 219 * Set Eddystone URL Frame information.
mbedAustin 10:b5d19bcf23cf 220 * @param[in] power TX Power in dB measured at 0 meters from the device.
mbedAustin 10:b5d19bcf23cf 221 * @param url URL to encode
mbedAustin 10:b5d19bcf23cf 222 * @return false on success, true on failure.
mbedAustin 10:b5d19bcf23cf 223 */
mbedAustin 10:b5d19bcf23cf 224 bool setURLFrameData(int8_t power, const char * url)
mbedAustin 10:b5d19bcf23cf 225 {
mbedAustin 10:b5d19bcf23cf 226 encodeURI(defaultURIDataIn, defaultUriData, defaultUriDataLength); // encode URL to URL Formatting
mbedAustin 10:b5d19bcf23cf 227 if (defaultUriDataLength > URI_DATA_MAX) {
mbedAustin 10:b5d19bcf23cf 228 return true; // error, URL is too big
mbedAustin 10:b5d19bcf23cf 229 }
mbedAustin 10:b5d19bcf23cf 230
mbedAustin 10:b5d19bcf23cf 231 }
mbedAustin 10:b5d19bcf23cf 232
mbedAustin 10:b5d19bcf23cf 233 /*
mbedAustin 10:b5d19bcf23cf 234 * Set Eddystone TLM Frame information.
mbedAustin 10:b5d19bcf23cf 235 * @param[in] Version of the TLM beacon data format
mbedAustin 10:b5d19bcf23cf 236 * @param batteryVoltage in milivolts
mbedAustin 10:b5d19bcf23cf 237 * @param beaconTemp in 8.8 floating point notation
mbedAustin 10:b5d19bcf23cf 238 *
mbedAustin 10:b5d19bcf23cf 239 */
mbedAustin 10:b5d19bcf23cf 240 void setTLMFrameData(uint8_t version, uint16_t batteryVoltage, uint16_t beaconTemp, uint32_t pduCount = 0, uint32_t timeSinceBoot = 0)
mbedAustin 10:b5d19bcf23cf 241 {
mbedAustin 10:b5d19bcf23cf 242 TlmVersion = version;
mbedAustin 10:b5d19bcf23cf 243 TlmBatteryVoltage = batteryVoltage;
mbedAustin 10:b5d19bcf23cf 244 TlmBeaconTemp = beaconTemp;
mbedAustin 10:b5d19bcf23cf 245 TlmPduCount = pduCount; // reset
mbedAustin 10:b5d19bcf23cf 246 TlmTimeSinceBoot = timeSinceBoot; // reset
mbedAustin 10:b5d19bcf23cf 247 return;
mbedAustin 10:b5d19bcf23cf 248 }
screamer 0:c04d932e96c9 249
screamer 0:c04d932e96c9 250 /* Helper function to switch to the non-connectible normal mode for ZipBeacon. This gets called after a timeout. */
screamer 0:c04d932e96c9 251 void setupZipBeaconAdvertisements()
screamer 0:c04d932e96c9 252 {
screamer 0:c04d932e96c9 253 uint8_t serviceData[SERVICE_DATA_MAX];
screamer 0:c04d932e96c9 254 unsigned serviceDataLen = 0;
screamer 0:c04d932e96c9 255
screamer 0:c04d932e96c9 256 /* Reinitialize the BLE stack. This will clear away the existing services and advertising state. */
screamer 0:c04d932e96c9 257 ble.shutdown();
screamer 0:c04d932e96c9 258 ble.init();
screamer 0:c04d932e96c9 259
screamer 0:c04d932e96c9 260 // Fields from the Service
screamer 0:c04d932e96c9 261 unsigned beaconPeriod = params.beaconPeriod;
screamer 0:c04d932e96c9 262 unsigned txPowerMode = params.txPowerMode;
screamer 0:c04d932e96c9 263 unsigned uriDataLength = params.uriDataLength;
screamer 0:c04d932e96c9 264 ZipBeaconConfigService::UriData_t &uriData = params.uriData;
screamer 0:c04d932e96c9 265 ZipBeaconConfigService::PowerLevels_t &advPowerLevels = params.advPowerLevels;
screamer 0:c04d932e96c9 266 uint8_t flags = params.flags;
screamer 0:c04d932e96c9 267
rgrover1 6:e90c398b03e0 268 extern void saveURIBeaconConfigParams(const Params_t *paramsP); /* forward declaration; necessary to avoid a circular dependency. */
rgrover1 6:e90c398b03e0 269 saveURIBeaconConfigParams(&params);
rgrover1 6:e90c398b03e0 270
screamer 0:c04d932e96c9 271 ble.clearAdvertisingPayload();
screamer 0:c04d932e96c9 272 ble.setTxPower(params.advPowerLevels[params.txPowerMode]);
screamer 0:c04d932e96c9 273 ble.setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED);
screamer 0:c04d932e96c9 274 ble.setAdvertisingInterval(beaconPeriod);
screamer 0:c04d932e96c9 275 ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
screamer 0:c04d932e96c9 276 ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, BEACON_UUID, sizeof(BEACON_UUID));
screamer 0:c04d932e96c9 277
screamer 0:c04d932e96c9 278 serviceData[serviceDataLen++] = BEACON_UUID[0];
screamer 0:c04d932e96c9 279 serviceData[serviceDataLen++] = BEACON_UUID[1];
screamer 0:c04d932e96c9 280 serviceData[serviceDataLen++] = FRAME_TYPE_URL | flags;
screamer 0:c04d932e96c9 281 serviceData[serviceDataLen++] = advPowerLevels[txPowerMode];
screamer 0:c04d932e96c9 282 for (unsigned j = 0; j < uriDataLength; j++) {
screamer 0:c04d932e96c9 283 serviceData[serviceDataLen++] = uriData[j];
screamer 0:c04d932e96c9 284 }
screamer 0:c04d932e96c9 285 ble.accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, serviceData, serviceDataLen);
screamer 0:c04d932e96c9 286 }
screamer 0:c04d932e96c9 287
screamer 0:c04d932e96c9 288 private:
screamer 0:c04d932e96c9 289 // True if the lock bits are non-zero
screamer 0:c04d932e96c9 290 bool isLocked() {
screamer 0:c04d932e96c9 291 Lock_t testLock;
screamer 0:c04d932e96c9 292 memset(testLock, 0, sizeof(Lock_t));
screamer 0:c04d932e96c9 293 return memcmp(params.lock, testLock, sizeof(Lock_t));
screamer 0:c04d932e96c9 294 }
screamer 0:c04d932e96c9 295
screamer 0:c04d932e96c9 296 /*
screamer 0:c04d932e96c9 297 * This callback is invoked when a GATT client attempts to modify any of the
screamer 0:c04d932e96c9 298 * characteristics of this service. Attempts to do so are also applied to
screamer 0:c04d932e96c9 299 * the internal state of this service object.
screamer 0:c04d932e96c9 300 */
rgrover1 7:e9800c45e065 301 void onDataWrittenCallback(const GattWriteCallbackParams *writeParams) {
rgrover1 7:e9800c45e065 302 uint16_t handle = writeParams->handle;
screamer 0:c04d932e96c9 303
screamer 0:c04d932e96c9 304 if (handle == lockChar.getValueHandle()) {
screamer 0:c04d932e96c9 305 // Validated earlier
screamer 0:c04d932e96c9 306 memcpy(params.lock, writeParams->data, sizeof(Lock_t));
screamer 0:c04d932e96c9 307 // use isLocked() in case bits are being set to all 0's
screamer 0:c04d932e96c9 308 lockedState = isLocked();
screamer 0:c04d932e96c9 309 } else if (handle == unlockChar.getValueHandle()) {
screamer 0:c04d932e96c9 310 // Validated earlier
screamer 0:c04d932e96c9 311 memset(params.lock, 0, sizeof(Lock_t));
screamer 0:c04d932e96c9 312 lockedState = false;
screamer 0:c04d932e96c9 313 } else if (handle == uriDataChar.getValueHandle()) {
screamer 0:c04d932e96c9 314 params.uriDataLength = writeParams->len;
screamer 0:c04d932e96c9 315 memcpy(params.uriData, writeParams->data, params.uriDataLength);
screamer 0:c04d932e96c9 316 } else if (handle == flagsChar.getValueHandle()) {
screamer 0:c04d932e96c9 317 params.flags = *(writeParams->data);
screamer 0:c04d932e96c9 318 } else if (handle == advPowerLevelsChar.getValueHandle()) {
screamer 0:c04d932e96c9 319 memcpy(params.advPowerLevels, writeParams->data, sizeof(PowerLevels_t));
screamer 0:c04d932e96c9 320 } else if (handle == txPowerModeChar.getValueHandle()) {
screamer 0:c04d932e96c9 321 params.txPowerMode = *(writeParams->data);
screamer 0:c04d932e96c9 322 } else if (handle == beaconPeriodChar.getValueHandle()) {
screamer 0:c04d932e96c9 323 params.beaconPeriod = *((uint16_t *)(writeParams->data));
rgrover1 4:4440953bde10 324
rgrover1 4:4440953bde10 325 /* Re-map beaconPeriod to within permissible bounds if necessary. */
rgrover1 4:4440953bde10 326 if (params.beaconPeriod != 0) {
rgrover1 4:4440953bde10 327 bool paramsUpdated = false;
rgrover1 4:4440953bde10 328 if (params.beaconPeriod < ble.getMinAdvertisingInterval()) {
rgrover1 4:4440953bde10 329 params.beaconPeriod = ble.getMinAdvertisingInterval();
rgrover1 4:4440953bde10 330 paramsUpdated = true;
rgrover1 4:4440953bde10 331 } else if (params.beaconPeriod > ble.getMaxAdvertisingInterval()) {
rgrover1 4:4440953bde10 332 params.beaconPeriod = ble.getMaxAdvertisingInterval();
rgrover1 4:4440953bde10 333 paramsUpdated = true;
rgrover1 4:4440953bde10 334 }
rgrover1 4:4440953bde10 335 if (paramsUpdated) {
rgrover1 4:4440953bde10 336 ble.updateCharacteristicValue(beaconPeriodChar.getValueHandle(), reinterpret_cast<uint8_t *>(&params.beaconPeriod), sizeof(uint16_t));
rgrover1 4:4440953bde10 337 }
rgrover1 4:4440953bde10 338 }
screamer 0:c04d932e96c9 339 } else if (handle == resetChar.getValueHandle()) {
screamer 0:c04d932e96c9 340 resetToDefaults();
screamer 0:c04d932e96c9 341 }
screamer 0:c04d932e96c9 342 }
screamer 0:c04d932e96c9 343
screamer 0:c04d932e96c9 344 /*
screamer 0:c04d932e96c9 345 * Reset the default values.
screamer 0:c04d932e96c9 346 */
screamer 0:c04d932e96c9 347 void resetToDefaults(void) {
screamer 0:c04d932e96c9 348 lockedState = false;
screamer 0:c04d932e96c9 349 memset(params.lock, 0, sizeof(Lock_t));
screamer 0:c04d932e96c9 350 memcpy(params.uriData, defaultUriData, URI_DATA_MAX);
screamer 0:c04d932e96c9 351 params.uriDataLength = defaultUriDataLength;
screamer 0:c04d932e96c9 352 params.flags = 0;
screamer 0:c04d932e96c9 353 memcpy(params.advPowerLevels, defaultAdvPowerLevels, sizeof(PowerLevels_t));
screamer 0:c04d932e96c9 354 params.txPowerMode = TX_POWER_MODE_LOW;
screamer 0:c04d932e96c9 355 params.beaconPeriod = 1000;
mbedAustin 10:b5d19bcf23cf 356 memcpy(params.uidNamespaceID, defaultUidNamespaceID, UID_NAMESPACEID_SIZE);
mbedAustin 10:b5d19bcf23cf 357 memcpy(params.uidInstanceID, defaultUidInstanceID, UID_INSTANCEID_SIZE);
screamer 0:c04d932e96c9 358 updateCharacteristicValues();
screamer 0:c04d932e96c9 359 }
screamer 0:c04d932e96c9 360
screamer 0:c04d932e96c9 361 /*
screamer 0:c04d932e96c9 362 * Internal helper function used to update the GATT database following any
screamer 0:c04d932e96c9 363 * change to the internal state of the service object.
screamer 0:c04d932e96c9 364 */
screamer 0:c04d932e96c9 365 void updateCharacteristicValues(void) {
screamer 0:c04d932e96c9 366 ble.updateCharacteristicValue(lockedStateChar.getValueHandle(), &lockedState, 1);
screamer 0:c04d932e96c9 367 ble.updateCharacteristicValue(uriDataChar.getValueHandle(), params.uriData, params.uriDataLength);
screamer 0:c04d932e96c9 368 ble.updateCharacteristicValue(flagsChar.getValueHandle(), &params.flags, 1);
screamer 0:c04d932e96c9 369 ble.updateCharacteristicValue(beaconPeriodChar.getValueHandle(),
screamer 0:c04d932e96c9 370 reinterpret_cast<uint8_t *>(&params.beaconPeriod), sizeof(uint16_t));
screamer 0:c04d932e96c9 371 ble.updateCharacteristicValue(txPowerModeChar.getValueHandle(), &params.txPowerMode, 1);
screamer 0:c04d932e96c9 372 ble.updateCharacteristicValue(advPowerLevelsChar.getValueHandle(),
screamer 0:c04d932e96c9 373 reinterpret_cast<uint8_t *>(params.advPowerLevels), sizeof(PowerLevels_t));
screamer 0:c04d932e96c9 374 }
screamer 0:c04d932e96c9 375
screamer 0:c04d932e96c9 376 private:
rgrover1 7:e9800c45e065 377 void lockAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
screamer 0:c04d932e96c9 378 if (lockedState) {
screamer 0:c04d932e96c9 379 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
screamer 0:c04d932e96c9 380 } else if (authParams->len != sizeof(Lock_t)) {
screamer 0:c04d932e96c9 381 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
screamer 0:c04d932e96c9 382 } else if (authParams->offset != 0) {
screamer 0:c04d932e96c9 383 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
screamer 0:c04d932e96c9 384 } else {
screamer 0:c04d932e96c9 385 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
screamer 0:c04d932e96c9 386 }
screamer 0:c04d932e96c9 387 }
screamer 0:c04d932e96c9 388
screamer 0:c04d932e96c9 389
rgrover1 7:e9800c45e065 390 void unlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
screamer 0:c04d932e96c9 391 if (!lockedState) {
screamer 0:c04d932e96c9 392 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
screamer 0:c04d932e96c9 393 } else if (authParams->len != sizeof(Lock_t)) {
screamer 0:c04d932e96c9 394 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
screamer 0:c04d932e96c9 395 } else if (authParams->offset != 0) {
screamer 0:c04d932e96c9 396 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
screamer 0:c04d932e96c9 397 } else if (memcmp(authParams->data, params.lock, sizeof(Lock_t)) != 0) {
screamer 0:c04d932e96c9 398 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
screamer 0:c04d932e96c9 399 } else {
screamer 0:c04d932e96c9 400 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
screamer 0:c04d932e96c9 401 }
screamer 0:c04d932e96c9 402 }
screamer 0:c04d932e96c9 403
rgrover1 7:e9800c45e065 404 void uriDataWriteAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
screamer 0:c04d932e96c9 405 if (lockedState) {
screamer 0:c04d932e96c9 406 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
screamer 0:c04d932e96c9 407 } else if (authParams->offset != 0) {
screamer 0:c04d932e96c9 408 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
screamer 0:c04d932e96c9 409 } else {
screamer 0:c04d932e96c9 410 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
screamer 0:c04d932e96c9 411 }
screamer 0:c04d932e96c9 412 }
screamer 0:c04d932e96c9 413
rgrover1 7:e9800c45e065 414 void powerModeAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
screamer 0:c04d932e96c9 415 if (lockedState) {
screamer 0:c04d932e96c9 416 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
screamer 0:c04d932e96c9 417 } else if (authParams->len != sizeof(uint8_t)) {
screamer 0:c04d932e96c9 418 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
screamer 0:c04d932e96c9 419 } else if (authParams->offset != 0) {
screamer 0:c04d932e96c9 420 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
screamer 0:c04d932e96c9 421 } else if (*((uint8_t *)authParams->data) >= NUM_POWER_MODES) {
screamer 0:c04d932e96c9 422 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
screamer 0:c04d932e96c9 423 } else {
screamer 0:c04d932e96c9 424 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
screamer 0:c04d932e96c9 425 }
screamer 0:c04d932e96c9 426 }
screamer 0:c04d932e96c9 427
screamer 0:c04d932e96c9 428 template <typename T>
rgrover1 7:e9800c45e065 429 void basicAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
screamer 0:c04d932e96c9 430 if (lockedState) {
screamer 0:c04d932e96c9 431 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION;
screamer 0:c04d932e96c9 432 } else if (authParams->len != sizeof(T)) {
screamer 0:c04d932e96c9 433 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
screamer 0:c04d932e96c9 434 } else if (authParams->offset != 0) {
screamer 0:c04d932e96c9 435 authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
screamer 0:c04d932e96c9 436 } else {
screamer 0:c04d932e96c9 437 authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
screamer 0:c04d932e96c9 438 }
screamer 0:c04d932e96c9 439 }
screamer 0:c04d932e96c9 440
mbedAustin 10:b5d19bcf23cf 441 BLEDevice &ble;
mbedAustin 10:b5d19bcf23cf 442 Params_t &params;
mbedAustin 10:b5d19bcf23cf 443 // Default value that is restored on reset
mbedAustin 10:b5d19bcf23cf 444 size_t defaultUriDataLength;
mbedAustin 10:b5d19bcf23cf 445 UriData_t defaultUriData;
mbedAustin 10:b5d19bcf23cf 446 UIDNamespaceID_t defaultUidNamespaceID;
mbedAustin 10:b5d19bcf23cf 447 UIDInstanceID_t defaultUidInstanceID;
mbedAustin 10:b5d19bcf23cf 448 uint16_t uidRFU;
screamer 0:c04d932e96c9 449 // Default value that is restored on reset
mbedAustin 10:b5d19bcf23cf 450 PowerLevels_t &defaultAdvPowerLevels;
mbedAustin 10:b5d19bcf23cf 451 uint8_t lockedState;
mbedAustin 10:b5d19bcf23cf 452 bool initSucceeded;
mbedAustin 10:b5d19bcf23cf 453 uint8_t resetFlag;
mbedAustin 10:b5d19bcf23cf 454
mbedAustin 10:b5d19bcf23cf 455 // Private Variables for Telemetry Data
mbedAustin 10:b5d19bcf23cf 456 uint8_t TlmVersion;
mbedAustin 10:b5d19bcf23cf 457 uint16_t TlmBatteryVoltage;
mbedAustin 10:b5d19bcf23cf 458 uint16_t TlmBeaconTemp;
mbedAustin 10:b5d19bcf23cf 459 uint32_t TlmPduCount;
mbedAustin 10:b5d19bcf23cf 460 uint32_t TlmTimeSinceBoot;
screamer 0:c04d932e96c9 461
screamer 0:c04d932e96c9 462 ReadOnlyGattCharacteristic<uint8_t> lockedStateChar;
screamer 0:c04d932e96c9 463 WriteOnlyGattCharacteristic<Lock_t> lockChar;
screamer 0:c04d932e96c9 464 GattCharacteristic uriDataChar;
screamer 0:c04d932e96c9 465 WriteOnlyGattCharacteristic<Lock_t> unlockChar;
screamer 0:c04d932e96c9 466 ReadWriteGattCharacteristic<uint8_t> flagsChar;
screamer 0:c04d932e96c9 467 ReadWriteGattCharacteristic<PowerLevels_t> advPowerLevelsChar;
screamer 0:c04d932e96c9 468 ReadWriteGattCharacteristic<uint8_t> txPowerModeChar;
screamer 0:c04d932e96c9 469 ReadWriteGattCharacteristic<uint16_t> beaconPeriodChar;
screamer 0:c04d932e96c9 470 WriteOnlyGattCharacteristic<uint8_t> resetChar;
screamer 0:c04d932e96c9 471
screamer 0:c04d932e96c9 472 public:
screamer 0:c04d932e96c9 473 /*
screamer 0:c04d932e96c9 474 * Encode a human-readable URI into the binary format defined by URIBeacon spec (https://github.com/google/uribeacon/tree/master/specification).
screamer 0:c04d932e96c9 475 */
screamer 0:c04d932e96c9 476 static void encodeURI(const char *uriDataIn, UriData_t uriDataOut, size_t &sizeofURIDataOut) {
screamer 0:c04d932e96c9 477 const char *prefixes[] = {
screamer 0:c04d932e96c9 478 "http://www.",
screamer 0:c04d932e96c9 479 "https://www.",
screamer 0:c04d932e96c9 480 "http://",
screamer 0:c04d932e96c9 481 "https://",
screamer 0:c04d932e96c9 482 };
screamer 0:c04d932e96c9 483 const size_t NUM_PREFIXES = sizeof(prefixes) / sizeof(char *);
screamer 0:c04d932e96c9 484 const char *suffixes[] = {
screamer 0:c04d932e96c9 485 ".com/",
screamer 0:c04d932e96c9 486 ".org/",
screamer 0:c04d932e96c9 487 ".edu/",
screamer 0:c04d932e96c9 488 ".net/",
screamer 0:c04d932e96c9 489 ".info/",
screamer 0:c04d932e96c9 490 ".biz/",
screamer 0:c04d932e96c9 491 ".gov/",
screamer 0:c04d932e96c9 492 ".com",
screamer 0:c04d932e96c9 493 ".org",
screamer 0:c04d932e96c9 494 ".edu",
screamer 0:c04d932e96c9 495 ".net",
screamer 0:c04d932e96c9 496 ".info",
screamer 0:c04d932e96c9 497 ".biz",
screamer 0:c04d932e96c9 498 ".gov"
screamer 0:c04d932e96c9 499 };
screamer 0:c04d932e96c9 500 const size_t NUM_SUFFIXES = sizeof(suffixes) / sizeof(char *);
screamer 0:c04d932e96c9 501
screamer 0:c04d932e96c9 502 sizeofURIDataOut = 0;
screamer 0:c04d932e96c9 503 memset(uriDataOut, 0, sizeof(UriData_t));
screamer 0:c04d932e96c9 504
screamer 0:c04d932e96c9 505 if ((uriDataIn == NULL) || (strlen(uriDataIn) == 0)) {
screamer 0:c04d932e96c9 506 return;
screamer 0:c04d932e96c9 507 }
screamer 0:c04d932e96c9 508
screamer 0:c04d932e96c9 509 /*
screamer 0:c04d932e96c9 510 * handle prefix
screamer 0:c04d932e96c9 511 */
screamer 0:c04d932e96c9 512 for (unsigned i = 0; i < NUM_PREFIXES; i++) {
screamer 0:c04d932e96c9 513 size_t prefixLen = strlen(prefixes[i]);
screamer 0:c04d932e96c9 514 if (strncmp(uriDataIn, prefixes[i], prefixLen) == 0) {
screamer 0:c04d932e96c9 515 uriDataOut[sizeofURIDataOut++] = i;
screamer 0:c04d932e96c9 516 uriDataIn += prefixLen;
screamer 0:c04d932e96c9 517 break;
screamer 0:c04d932e96c9 518 }
screamer 0:c04d932e96c9 519 }
screamer 0:c04d932e96c9 520
screamer 0:c04d932e96c9 521 /*
screamer 0:c04d932e96c9 522 * handle suffixes
screamer 0:c04d932e96c9 523 */
screamer 0:c04d932e96c9 524 while (*uriDataIn && (sizeofURIDataOut < URI_DATA_MAX)) {
screamer 0:c04d932e96c9 525 /* check for suffix match */
screamer 0:c04d932e96c9 526 unsigned i;
screamer 0:c04d932e96c9 527 for (i = 0; i < NUM_SUFFIXES; i++) {
screamer 0:c04d932e96c9 528 size_t suffixLen = strlen(suffixes[i]);
screamer 0:c04d932e96c9 529 if (strncmp(uriDataIn, suffixes[i], suffixLen) == 0) {
screamer 0:c04d932e96c9 530 uriDataOut[sizeofURIDataOut++] = i;
screamer 0:c04d932e96c9 531 uriDataIn += suffixLen;
screamer 0:c04d932e96c9 532 break; /* from the for loop for checking against suffixes */
screamer 0:c04d932e96c9 533 }
screamer 0:c04d932e96c9 534 }
screamer 0:c04d932e96c9 535 /* This is the default case where we've got an ordinary character which doesn't match a suffix. */
screamer 0:c04d932e96c9 536 if (i == NUM_SUFFIXES) {
screamer 0:c04d932e96c9 537 uriDataOut[sizeofURIDataOut++] = *uriDataIn;
screamer 0:c04d932e96c9 538 ++uriDataIn;
screamer 0:c04d932e96c9 539 }
screamer 0:c04d932e96c9 540 }
screamer 0:c04d932e96c9 541 }
screamer 0:c04d932e96c9 542 };
screamer 0:c04d932e96c9 543
screamer 0:c04d932e96c9 544 #endif // SERVICES_ZIPBEACONCONFIGSERVICE_H_