Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: microbit-dal microbit-dal microbit-ble-open microbit-dal ... more
Fork of BLE_API by
Diff: services/URIBeaconConfigService.h
- Revision:
- 286:898ff71b9502
- Parent:
- 285:984353e66610
- Child:
- 287:cca148e535b1
--- a/services/URIBeaconConfigService.h Fri Feb 13 13:00:41 2015 +0000
+++ b/services/URIBeaconConfigService.h Fri Feb 13 16:37:42 2015 +0000
@@ -14,282 +14,280 @@
* limitations under the License.
*/
-#ifndef SERVICES_URIBEACONCONFIGSERVICE_H_
-#define SERVICES_URIBEACONCONFIGSERVICE_H_
+#ifndef __BLE_URI_BEACON_CONFIG_SERVICE_H__
+#define __BLE_URI_BEACON_CONFIG_SERVICE_H__
-#define UUID_URI_BEACON(FIRST, SECOND) { \
+#include "BLEDevice.h"
+
+#define URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(FIRST, SECOND) { \
0xee, 0x0c, FIRST, SECOND, 0x87, 0x86, 0x40, 0xba, \
0xab, 0x96, 0x99, 0xb9, 0x1a, 0xc9, 0x81, 0xd8, \
}
-
-static const uint8_t UUID_URI_BEACON_SERVICE[] = UUID_URI_BEACON(0x20, 0x80);
-static const uint8_t UUID_LOCK_STATE_CHAR[] = UUID_URI_BEACON(0x20, 0x81);
-static const uint8_t UUID_LOCK_CHAR[] = UUID_URI_BEACON(0x20, 0x82);
-static const uint8_t UUID_UNLOCK_CHAR[] = UUID_URI_BEACON(0x20, 0x83);
-static const uint8_t UUID_URI_DATA_CHAR[] = UUID_URI_BEACON(0x20, 0x84);
-static const uint8_t UUID_FLAGS_CHAR[] = UUID_URI_BEACON(0x20, 0x85);
-static const uint8_t UUID_ADV_POWER_LEVELS_CHAR[] = UUID_URI_BEACON(0x20, 0x86);
-static const uint8_t UUID_TX_POWER_MODE_CHAR[] = UUID_URI_BEACON(0x20, 0x87);
-static const uint8_t UUID_BEACON_PERIOD_CHAR[] = UUID_URI_BEACON(0x20, 0x88);
-static const uint8_t UUID_RESET_CHAR[] = UUID_URI_BEACON(0x20, 0x89);
-static const uint8_t BEACON_UUID[] = {0xD8, 0xFE};
+static const uint8_t URIBeacon2ControlServiceUUID[] = URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(0x20, 0x80);
+static const uint8_t lockedStateCharUUID[] = URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(0x20, 0x81);
+static const uint8_t lockCharUUID[] = URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(0x20, 0x82);
+static const uint8_t unlockCharUUID[] = URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(0x20, 0x83);
+static const uint8_t uriDataCharUUID[] = URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(0x20, 0x84);
+static const uint8_t flagsCharUUID[] = URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(0x20, 0x85);
+static const uint8_t txPowerLevelsCharUUID[] = URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(0x20, 0x86);
+static const uint8_t txPowerModeCharUUID[] = URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(0x20, 0x87);
+static const uint8_t beaconPeriodCharUUID[] = URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(0x20, 0x88);
+static const uint8_t resetCharUUID[] = URI_BEACON_CONFIG_UUID_INITIALIZER_LIST(0x20, 0x89);
/**
* @class URIBeaconConfigService
* @brief UriBeacon Configuration Service. Can be used to set URL, adjust power levels, and set flags.
-* See http://uribeacon.org
-*
*/
class URIBeaconConfigService {
- public:
+public:
/**
+ * @enum TXPowerModes_t
* @brief Transmission Power Modes for UriBeacon
*/
- static const uint8_t TX_POWER_MODE_LOWEST = 0; /*!< Lowest TX power mode */
- static const uint8_t TX_POWER_MODE_LOW = 1; /*!< Low TX power mode */
- static const uint8_t TX_POWER_MODE_MEDIUM = 2; /*!< Medium TX power mode */
- static const uint8_t TX_POWER_MODE_HIGH = 3; /*!< High TX power mode */
- static const unsigned int NUM_POWER_MODES = 4; /*!< Number of Power Modes defined */
-
-
- typedef uint8_t Lock_t[16]; /* 128 bits */
- typedef int8_t PowerLevels_t[NUM_POWER_MODES];
-
- static const int URI_DATA_MAX = 18;
- typedef uint8_t UriData_t[URI_DATA_MAX];
-
- struct Params_t {
- Lock_t lock;
- uint8_t uriDataLength;
- UriData_t uriData;
- uint8_t flags;
- // Current value of AdvertisedPowerLevels
- PowerLevels_t advPowerLevels;
- // Firmware power levels used with setTxPower()
- uint8_t txPowerMode;
- uint16_t beaconPeriod;
+ enum TXPowerModes_t {
+ TX_POWER_MODE_LOWEST = 0, /*!< Lowest TX power mode */
+ TX_POWER_MODE_LOW = 1, /*!< Low TX power mode */
+ TX_POWER_MODE_MEDIUM = 2, /*!< Medium TX power mode */
+ TX_POWER_MODE_HIGH = 3, /*!< High TX power mode */
+ NUM_POWER_MODES /*!< Number of Power Modes defined */
};
+ static const size_t SIZEOF_LOCK_BITS = 16; /* uint128 */
+ typedef uint8_t LockBits_t[SIZEOF_LOCK_BITS];
/**
- * @param[ref] ble
- * BLEDevice object for the underlying controller.
- * @param[in/out] paramsIn
- * Reference to application-visible beacon state, loaded
- * from persistent storage at startup.
- * @param[in] resetToDefaultsFlag
- * reset params state to the defaults.
- * @param[in] defaultUriDataIn
- * Default encoded URIData; applies only if the resetToDefaultsFlag is true.
- * @param[in] defaultUriDataLengthIn
- * Length of the default encoded URIData (from above); applies only if the resetToDefaultsFlag is true.
- * @param[in] defaultAdvPowerLevelsIn
- * Default power-levels array; applies only if the resetToDefaultsFlag is true.
+ * @param[ref] ble
+ * BLEDevice object for the underlying controller.
+ * @param[in] uridata
+ * URI as a null-terminated string.
+ * @param[in] flagsIn
+ * UriBeacon Flags.
+ * @param[in] powerLevels[]
+ * Table of UriBeacon Tx Power Levels in dBm.
+ * @param[in] powerMode
+ * Currently effective power mode.
+ * @param[in] beaconPeriodIn
+ * The period in milliseconds that a UriBeacon packet is
+ * transmitted. A value of zero disables UriBeacon
+ * transmissions.
*/
- URIBeaconConfigService(BLEDevice &bleIn,
- Params_t ¶msIn,
- bool resetToDefaultsFlag,
- UriData_t &defaultUriDataIn,
- int defaultUriDataLengthIn,
- PowerLevels_t &defaultAdvPowerLevelsIn) :
+ URIBeaconConfigService(BLEDevice &bleIn,
+ const char *uriDataIn,
+ uint8_t flagsIn = 0,
+ const int8_t powerLevelsIn[NUM_POWER_MODES] = NULL,
+ TXPowerModes_t powerModeIn = TX_POWER_MODE_LOW,
+ uint16_t beaconPeriodIn = 1000) :
ble(bleIn),
- params(paramsIn),
- defaultUriDataLength(defaultUriDataLengthIn),
- defaultUriData(defaultUriDataIn),
- defaultAdvPowerLevels(defaultAdvPowerLevelsIn),
+ payloadIndex(0),
+ serviceDataPayload(),
initSucceeded(false),
- lockedStateChar(UUID_LOCK_STATE_CHAR, &lockedState),
- lockChar(UUID_LOCK_CHAR, ¶ms.lock),
- uriDataChar(UUID_URI_DATA_CHAR, params.uriData, URI_DATA_MAX, params.uriDataLength,
- GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE),
- unlockChar(UUID_UNLOCK_CHAR, ¶ms.lock),
- flagsChar(UUID_FLAGS_CHAR, ¶ms.flags),
- advPowerLevelsChar(UUID_ADV_POWER_LEVELS_CHAR, ¶ms.advPowerLevels),
- txPowerModeChar(UUID_TX_POWER_MODE_CHAR, ¶ms.txPowerMode),
- beaconPeriodChar(UUID_BEACON_PERIOD_CHAR, ¶ms.beaconPeriod),
- resetChar(UUID_RESET_CHAR, &resetFlag) {
- if (defaultUriDataLengthIn > URI_DATA_MAX) {
+ lockedState(false),
+ lockBits(),
+ uriDataLength(0),
+ uriData(),
+ flags(flagsIn),
+ powerLevels(),
+ beaconPeriod(beaconPeriodIn),
+ lockedStateChar(lockedStateCharUUID, &lockedState),
+ lockChar(lockCharUUID, lockBits),
+ unlockChar(unlockCharUUID, lockBits),
+ uriDataChar(uriDataCharUUID, uriData),
+ flagsChar(flagsCharUUID, &flags),
+ txPowerLevelsChar(txPowerLevelsCharUUID, powerLevels),
+ txPowerModeChar(txPowerModeCharUUID, &txPowerMode),
+ beaconPeriodChar(beaconPeriodCharUUID, &beaconPeriod),
+ resetChar(resetCharUUID, &resetFlag)
+ {
+ if ((uriDataIn == NULL) || ((uriDataLength = strlen(uriDataIn)) == 0) || (uriDataLength > MAX_SIZE_URI_DATA_CHAR_VALUE)) {
return;
}
+ strcpy(reinterpret_cast<char *>(uriData), uriDataIn);
- if (params.uriDataLength > URI_DATA_MAX) {
- resetToDefaultsFlag = true;
+ if (powerLevelsIn != NULL) {
+ memcpy(powerLevels, powerLevelsIn, sizeof(powerLevels));
+ updateTxPowerLevelsCharacteristic();
}
- lockedState = isLocked();
+ configureGAP();
- if (resetToDefaultsFlag) {
- resetToDefaults();
- } else {
- updateCharacteristicValues();
- }
+ // enable the following for debugging the state of the lock
+ // lockedStateChar.setReadAuthorizationCallback(this, &URIBeaconConfigService::lockedStateAuthorizationCallback);
lockChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::lockAuthorizationCallback);
unlockChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::unlockAuthorizationCallback);
uriDataChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::uriDataWriteAuthorizationCallback);
flagsChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::flagsAuthorizationCallback);
- advPowerLevelsChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::denyGATTWritesIfLocked);
- txPowerModeChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::denyGATTWritesIfLocked);
+ txPowerLevelsChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::denyGATTWritesIfLocked);
+ txPowerModeChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::powerModeAuthorizationCallback);
beaconPeriodChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::denyGATTWritesIfLocked);
resetChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::denyGATTWritesIfLocked);
- static GattCharacteristic *charTable[] = {
- &lockedStateChar, &lockChar, &unlockChar, &uriDataChar,
- &flagsChar, &advPowerLevelsChar, &txPowerModeChar, &beaconPeriodChar, &resetChar
- };
+ GattCharacteristic *charTable[] = {&lockedStateChar,
+ &lockChar,
+ &unlockChar,
+ &uriDataChar,
+ &flagsChar,
+ &txPowerLevelsChar,
+ &beaconPeriodChar,
+ &resetChar};
+ GattService beaconControlService(URIBeacon2ControlServiceUUID, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
+ ble.addService(beaconControlService);
- GattService configService(UUID_URI_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
-
- ble.addService(configService);
- ble.onDataWritten(this, &URIBeaconConfigService::onDataWrittenCallback);
-
- initSucceeded = true;
+ ble.onDataWritten(this, &URIBeaconConfigService::onDataWritten);
}
bool configuredSuccessfully(void) const {
return initSucceeded;
}
-
- private:
- // True if the lock bits are non-zero
- bool isLocked() {
- Lock_t testLock;
- memset(testLock, 0, sizeof(Lock_t));
- return memcmp(params.lock, testLock, sizeof(Lock_t));
+ /**
+ * Please note that the following public APIs are offered to allow modifying
+ * the service programmatically. It is also possible to do so over BLE GATT
+ * transactions.
+ */
+public:
+ /**
+ * Update flags of the URIBeacon dynamically.
+ *
+ * @param[in] flagsIn
+ * @verbatim
+ * ### UriBeacon Flags
+ * Bit | Description
+ * :---- | :----------
+ * 0 | Invisible Hint
+ * 1..7 | Reserved for future use. Must be zero.
+ * @endverbatim
+ * The `Invisible Hint` flag is a command for the user-agent that tells
+ * it not to access or display the UriBeacon. This is a guideline only,
+ * and is not a blocking method. User agents may, with user approval,
+ * display invisible beacons.
+ */
+ void setFlags(uint8_t flagsIn) {
+ flags = flagsIn;
+ configureGAP();
+ updateFlagsCharacteristic();
}
- /*
- * This callback is invoked when a GATT client attempts to modify any of the
- * characteristics of this service. Attempts to do so are also applied to
- * the internal state of this service object.
+ /**
+ * @brief Update the txPowerLevels table.
+ *
+ * @param[in] powerLevelsIn
+ * Array of power levels
*/
- void onDataWrittenCallback(const GattCharacteristicWriteCBParams *writeParams) {
- uint16_t handle = writeParams->charHandle;
+ void setTxPowerLevels(const int8_t powerLevelsIn[NUM_POWER_MODES]) {
+ memcpy(powerLevels, powerLevelsIn, sizeof(powerLevels));
+ configureGAP();
+ updateTxPowerLevelsCharacteristic();
+ }
- if (handle == lockChar.getValueHandle()) {
- // Validated earlier
- memcpy(params.lock, writeParams->data, sizeof(Lock_t));
- // use isLocked() in case bits are being set to all 0's
- lockedState = isLocked();
- } else if (handle == unlockChar.getValueHandle()) {
- // Validated earlier
- memset(params.lock, 0, sizeof(Lock_t));
- lockedState = false;
- } else if (handle == uriDataChar.getValueHandle()) {
- params.uriDataLength = writeParams->len;
- memcpy(params.uriData, writeParams->data, params.uriDataLength);
- } else if (handle == flagsChar.getValueHandle()) {
- params.flags = *(writeParams->data);
- } else if (handle == advPowerLevelsChar.getValueHandle()) {
- memcpy(params.advPowerLevels, writeParams->data, sizeof(PowerLevels_t));
- } else if (handle == txPowerModeChar.getValueHandle()) {
- params.txPowerMode = *(writeParams->data);
- } else if (handle == beaconPeriodChar.getValueHandle()) {
- params.beaconPeriod = *((uint16_t *)(writeParams->data));
- } else if (handle == resetChar.getValueHandle()) {
- resetToDefaults();
+ /**
+ * @brief Set the effective power mode from one of the values in the powerLevels tables.
+ *
+ * @param[in] mode
+ * Set the TX Power Mode.
+ */
+ void setTxPowerMode(TXPowerModes_t mode) {
+ txPowerMode = mode;
+ configureGAP();
+ updateTxPowerModeCharacteristic();
+ }
+
+ /**
+ * The period in milliseconds that a UriBeacon packet is transmitted.
+ *
+ * @note A value of zero disables UriBeacon transmissions.
+ *
+ * @param beaconPeriodIn
+ * Beacon advertising period in milliseconds
+ */
+ void setBeaconPeriod(uint16_t beaconPeriodIn) {
+ beaconPeriod = beaconPeriodIn;
+ configureGAP();
+ updateBeaconPeriodCharacteristic();
+ }
+
+protected:
+ void updateLockBits(const LockBits_t lockBitsIn) {
+ static const uint8_t allZeroes[SIZEOF_LOCK_BITS] = {0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0};
+
+ memcpy(lockBits, lockBitsIn, SIZEOF_LOCK_BITS);
+ if (memcmp(lockBits, allZeroes, SIZEOF_LOCK_BITS)) {
+ lockedState = true;
}
}
- /*
- * Reset the default values.
+ void copyLockBitsInto(LockBits_t lockBitsOut) const {
+ memcpy(lockBitsOut, lockBits, SIZEOF_LOCK_BITS);
+ }
+
+ void resetLockBits(void) {
+ lockedState = false;
+ memset(lockBits, 0, SIZEOF_LOCK_BITS);
+ storage_saveLockBits();
+ }
+
+ /**
+ * APIs around making lockBits persistent.
+ */
+private:
+ /**
+ * Have we previously saved lockedBits? Once set, this state is expected to persist.
+ * @return true if we've previously saved locked bits.
+ */
+ virtual bool storage_haveSavedLockBits() const {
+ /* Expecting to be overridden. Left empty to allow the default URIBeacon to be instantiated if persistence isn't required. */
+ return false;
+ }
+
+ /**
+ * Save the current value of lockBits into persistent storage; this value is then retrievable by lockLockBits() until a subsequent call to saveLockBits().
*/
- void resetToDefaults(void) {
- lockedState = false;
- memset(params.lock, 0, sizeof(Lock_t));
- memcpy(params.uriData, defaultUriData, URI_DATA_MAX);
- params.uriDataLength = defaultUriDataLength;
- params.flags = 0;
- memcpy(params.advPowerLevels, defaultAdvPowerLevels, sizeof(PowerLevels_t));
- params.txPowerMode = TX_POWER_MODE_LOW;
- params.beaconPeriod = 1000;
- updateCharacteristicValues();
+ virtual void storage_saveLockBits() {
+ /* Expecting to be overridden. Left empty to allow the default URIBeacon to be instantiated if persistence isn't required. */
+ }
+
+ /**
+ * Retrieve the saved lockBits from persistent storage and update the class member 'lockBits'.
+ */
+ virtual void storage_loadLockBits() {
+ /* Expecting to be overridden. Left empty to allow the default URIBeacon to be instantiated if persistence isn't required. */
+ }
+
+private:
+ /*
+ * Setup the advertisement payload and GAP settings.
+ */
+ void configureGAP(void) {
+ const uint8_t BEACON_UUID[] = {0xD8, 0xFE};
+
+ payloadIndex = 0;
+ serviceDataPayload[payloadIndex++] = BEACON_UUID[0];
+ serviceDataPayload[payloadIndex++] = BEACON_UUID[1];
+ serviceDataPayload[payloadIndex++] = flags;
+ serviceDataPayload[payloadIndex++] = powerLevels[txPowerMode];
+
+ const char *urlData = reinterpret_cast<char *>(uriData);
+ size_t sizeofURLData = uriDataLength;
+ size_t encodedBytes = encodeURISchemePrefix(urlData, sizeofURLData) + encodeURI(urlData, sizeofURLData);
+
+ ble.clearAdvertisingPayload();
+ ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, BEACON_UUID, sizeof(BEACON_UUID));
+ ble.accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, serviceDataPayload, encodedBytes + 4);
+
+ ble.setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(beaconPeriod));
+ ble.setTxPower(powerLevels[txPowerMode]);
}
/*
- * Internal helper function used to update the GATT database following any
- * change to the internal state of the service object.
+ * Encode the URI Prefix to a single byte if possible.
*/
- void updateCharacteristicValues(void) {
- ble.updateCharacteristicValue(lockedStateChar.getValueHandle(), &lockedState, 1);
- ble.updateCharacteristicValue(uriDataChar.getValueHandle(), params.uriData, params.uriDataLength);
- ble.updateCharacteristicValue(flagsChar.getValueHandle(), ¶ms.flags, 1);
- ble.updateCharacteristicValue(beaconPeriodChar.getValueHandle(),
- reinterpret_cast<uint8_t *>(¶ms.beaconPeriod), sizeof(uint16_t));
- ble.updateCharacteristicValue(txPowerModeChar.getValueHandle(), ¶ms.txPowerMode, 1);
- ble.updateCharacteristicValue(advPowerLevelsChar.getValueHandle(),
- reinterpret_cast<uint8_t *>(params.advPowerLevels), sizeof(PowerLevels_t));
- }
-
- private:
- void lockAuthorizationCallback(GattCharacteristicWriteAuthCBParams *authParams) {
- authParams->authorizationReply = (authParams->len == sizeof(Lock_t)) && !lockedState;
- }
-
-
- void unlockAuthorizationCallback(GattCharacteristicWriteAuthCBParams *authParams) {
- if (!lockedState || (authParams->len == sizeof(Lock_t) && (memcmp(authParams->data, params.lock, sizeof(Lock_t)) == 0))) {
- authParams->authorizationReply = true;
- } else {
- authParams->authorizationReply = false;
- }
- }
-
- void uriDataWriteAuthorizationCallback(GattCharacteristicWriteAuthCBParams *authParams) {
- if (lockedState || (authParams->offset != 0) || (authParams->len > URI_DATA_MAX)) {
- authParams->authorizationReply = false;
- }
- }
-
- void flagsAuthorizationCallback(GattCharacteristicWriteAuthCBParams *authParams) {
- if (lockedState || authParams->len != 1) {
- authParams->authorizationReply = false;
- }
- }
-
- void denyGATTWritesIfLocked(GattCharacteristicWriteAuthCBParams *authParams) {
- if (lockedState) {
- authParams->authorizationReply = false;
- }
- }
-
- BLEDevice &ble;
- Params_t ¶ms;
- // Default value that is restored on reset
- uint16_t defaultUriDataLength;
- UriData_t &defaultUriData;
- // Default value that is restored on reset
- PowerLevels_t &defaultAdvPowerLevels;
- uint8_t lockedState;
- bool initSucceeded;
- uint8_t resetFlag;
-
- ReadOnlyGattCharacteristic<uint8_t> lockedStateChar;
- WriteOnlyGattCharacteristic<Lock_t> lockChar;
- GattCharacteristic uriDataChar;
- WriteOnlyGattCharacteristic<Lock_t> unlockChar;
- ReadWriteGattCharacteristic<uint8_t> flagsChar;
- ReadWriteGattCharacteristic<PowerLevels_t> advPowerLevelsChar;
- ReadWriteGattCharacteristic<uint8_t> txPowerModeChar;
- ReadWriteGattCharacteristic<uint16_t> beaconPeriodChar;
- WriteOnlyGattCharacteristic<uint8_t> resetChar;
-
- public:
- /*
- * Encode a human-readable URI into the binary format defined by URIBeacon spec (https://github.com/google/uribeacon/tree/master/specification).
- */
- static void encodeURI(const char *uriDataIn, UriData_t uriDataOut, size_t &sizeofURIDataOut) {
- sizeofURIDataOut = 0;
- memset(uriDataOut, 0, sizeof(UriData_t));
-
- if ((uriDataIn == NULL) || (strlen(uriDataIn) == 0)) {
- return;
+ size_t encodeURISchemePrefix(const char *&urldata, size_t &sizeofURLData) {
+ if (!sizeofURLData) {
+ return 0;
}
- /*
- * handle prefix
- */
+ /* These are the URI Prefixes that can be abbreviated.*/
const char *prefixes[] = {
"http://www.",
"https://www.",
@@ -297,19 +295,29 @@
"https://",
"urn:uuid:"
};
- const size_t NUM_PREFIXES = sizeof(prefixes) / sizeof(char *);
+
+ size_t encodedBytes = 0;
+ const size_t NUM_PREFIXES = sizeof(prefixes) / sizeof(char *);
for (unsigned i = 0; i < NUM_PREFIXES; i++) {
size_t prefixLen = strlen(prefixes[i]);
- if (strncmp(uriDataIn, prefixes[i], prefixLen) == 0) {
- uriDataOut[sizeofURIDataOut++] = i;
- uriDataIn += prefixLen;
+ if (strncmp(urldata, prefixes[i], prefixLen) == 0) {
+ serviceDataPayload[payloadIndex++] = i;
+ encodedBytes = 1;
+
+ urldata += prefixLen;
+ sizeofURLData -= prefixLen;
break;
}
}
- /*
- * handle suffixes
- */
+ return encodedBytes;
+ }
+
+ /*
+ * Encode the URI Suffix to a single byte if possible.
+ */
+ size_t encodeURI(const char *urldata, size_t sizeofURLData) {
+ /* These are the URI suffixes that can be abbreviated. */
const char *suffixes[] = {
".com/",
".org/",
@@ -327,28 +335,212 @@
".gov"
};
const size_t NUM_SUFFIXES = sizeof(suffixes) / sizeof(char *);
- while (*uriDataIn && (sizeofURIDataOut < URI_DATA_MAX)) {
+
+ size_t encodedBytes = 0;
+ while (sizeofURLData && (payloadIndex < MAX_SIZEOF_SERVICE_DATA_PAYLOAD)) {
/* check for suffix match */
unsigned i;
for (i = 0; i < NUM_SUFFIXES; i++) {
size_t suffixLen = strlen(suffixes[i]);
- if (suffixLen == 0) {
+ if ((suffixLen == 0) || (sizeofURLData < suffixLen)) {
continue;
}
- if (strncmp(uriDataIn, suffixes[i], suffixLen) == 0) {
- uriDataOut[sizeofURIDataOut++] = i;
- uriDataIn += suffixLen;
+ if (strncmp(urldata, suffixes[i], suffixLen) == 0) {
+ serviceDataPayload[payloadIndex++] = i;
+ ++encodedBytes;
+ urldata += suffixLen;
+ sizeofURLData -= suffixLen;
break; /* from the for loop for checking against suffixes */
}
}
/* This is the default case where we've got an ordinary character which doesn't match a suffix. */
if (i == NUM_SUFFIXES) {
- uriDataOut[sizeofURIDataOut++] = *uriDataIn;
- ++uriDataIn;
+ serviceDataPayload[payloadIndex++] = *urldata;
+ ++encodedBytes;
+ ++urldata;
+ --sizeofURLData;
}
}
+ if (sizeofURLData == 0) {
+ initSucceeded = true;
+ }
+
+ return encodedBytes;
}
+
+ /*
+ * This callback is invoked when a GATT client attempts to modify any of the
+ * characteristics of this service. Attempts to do so are also applied to
+ * the internal state of this service object.
+ */
+ void onDataWritten(const GattCharacteristicWriteCBParams *params) {
+ uint16_t handle = params->charHandle;
+ if (handle == lockChar.getValueHandle()) {
+ updateLockBits(params->data);
+ storage_saveLockBits();
+ } else if (handle == unlockChar.getValueHandle()) {
+ memset(lockBits, 0, SIZEOF_LOCK_BITS);
+ lockedState = false;
+ storage_saveLockBits();
+ } else if (handle == uriDataChar.getValueHandle()) {
+ uriDataLength = params->len;
+ memcpy(uriData, params->data, uriDataLength);
+ } else if (handle == flagsChar.getValueHandle()) {
+ flags = *(params->data);
+ } else if (handle == txPowerLevelsChar.getValueHandle()) {
+ memcpy(powerLevels, params->data, NUM_POWER_MODES * sizeof(int8_t));
+ } else if (handle == txPowerModeChar.getValueHandle()) {
+ txPowerMode = *reinterpret_cast<const TXPowerModes_t *>(params->data);
+ } else if (handle == beaconPeriodChar.getValueHandle()) {
+ beaconPeriod = *((uint16_t *)(params->data));
+ } else if (handle == resetChar.getValueHandle()) {
+ resetDefaults();
+ }
+ configureGAP();
+ ble.setAdvertisingPayload();
+ }
+
+ /*
+ * Reset the default values.
+ */
+ void resetDefaults(void) {
+ uriDataLength = 0;
+ memset(uriData, 0, MAX_SIZE_URI_DATA_CHAR_VALUE);
+ flags = 0;
+ memset(powerLevels, 0, sizeof(powerLevels));
+ txPowerMode = TX_POWER_MODE_LOW;
+ beaconPeriod = 0;
+ resetLockBits();
+
+ updateGATT();
+ }
+
+ /*
+ * Internal helper function used to update the GATT database following any
+ * change to the internal state of the service object.
+ */
+ void updateGATT(void) {
+ updateLockedStateCharacteristic();
+ updateURIDataCharacteristic();
+ updateFlagsCharacteristic();
+ updateBeaconPeriodCharacteristic();
+ updateTxPowerLevelsCharacteristic();
+ updateTxPowerModeCharacteristic();
+ }
+
+ void updateLockedStateCharacteristic(void) {
+ ble.updateCharacteristicValue(lockedStateChar.getValueHandle(), reinterpret_cast<uint8_t *>(&lockedState), sizeof(lockedState));
+ }
+
+ void updateURIDataCharacteristic(void) {
+ ble.updateCharacteristicValue(uriDataChar.getValueHandle(), uriData, uriDataLength);
+ }
+
+ void updateFlagsCharacteristic(void) {
+ ble.updateCharacteristicValue(flagsChar.getValueHandle(), &flags, 1 /* size */);
+ }
+
+ void updateBeaconPeriodCharacteristic(void) {
+ ble.updateCharacteristicValue(beaconPeriodChar.getValueHandle(), reinterpret_cast<uint8_t *>(&beaconPeriod), sizeof(uint16_t));
+ }
+
+ void updateTxPowerModeCharacteristic(void) {
+ ble.updateCharacteristicValue(txPowerModeChar.getValueHandle(), reinterpret_cast<uint8_t *>(&txPowerMode), sizeof(uint8_t));
+ }
+
+ void updateTxPowerLevelsCharacteristic(void) {
+ ble.updateCharacteristicValue(txPowerLevelsChar.getValueHandle(), reinterpret_cast<uint8_t *>(powerLevels), NUM_POWER_MODES * sizeof(int8_t));
+ }
+
+private:
+ // enable the following for debugging the state of the lock
+ // void lockedStateAuthorizationCallback(GattCharacteristicReadAuthCBParams *params) {
+ // printf("read authorization callback: lockedState is %u\r\n", lockedState);
+ // params->authorizationReply = true;
+ // }
+
+ void lockAuthorizationCallback(GattCharacteristicWriteAuthCBParams *params) {
+ params->authorizationReply = !lockedState;
+ }
+
+ void unlockAuthorizationCallback(GattCharacteristicWriteAuthCBParams *params) {
+ if (lockedState && (memcmp(params->data, lockBits, SIZEOF_LOCK_BITS) == 0)) {
+ params->authorizationReply = true;
+ } else {
+ params->authorizationReply = false;
+ }
+ }
+
+ void uriDataWriteAuthorizationCallback(GattCharacteristicWriteAuthCBParams *params) {
+ if (lockedState || (params->offset != 0) || (params->len > MAX_SIZE_URI_DATA_CHAR_VALUE)) {
+ params->authorizationReply = false;
+ }
+ }
+
+ void flagsAuthorizationCallback(GattCharacteristicWriteAuthCBParams *params) {
+ if (lockedState || ((*(params->data) & 0xFE) != 0)) {
+ params->authorizationReply = false;
+ }
+ }
+
+ void powerModeAuthorizationCallback(GattCharacteristicWriteAuthCBParams *params) {
+ if (lockedState || (*(params->data) >= NUM_POWER_MODES)) {
+ params->authorizationReply = false;
+ }
+ }
+
+ void denyGATTWritesIfLocked(GattCharacteristicWriteAuthCBParams *params) {
+ if (lockedState) {
+ params->authorizationReply = false;
+ }
+ }
+
+private:
+ /**
+ * For debugging only. Print Hex representation of ServiceDataPayload to the console.
+ */
+ // void dumpEncodedSeviceData() const {
+ // printf("encoded: '");
+ // for (unsigned i = 0; i < payloadIndex; i++) {
+ // printf(" %02x", serviceDataPayload[i]);
+ // }
+ // printf("'\r\n");
+ // }
+
+private:
+ static const size_t MAX_SIZEOF_SERVICE_DATA_PAYLOAD = 22; /* Uri Data must be between 0 and 18 bytes in length; and
+ * together with the 4-byte header, the service data must
+ * fit within 22 bytes. */
+ static const size_t MAX_SIZE_URI_DATA_CHAR_VALUE = 48; /* This is chosen arbitrarily. It should be large enough
+ * to hold any reasonable uncompressed URI. */
+private:
+ BLEDevice &ble;
+
+ size_t payloadIndex;
+ uint8_t serviceDataPayload[MAX_SIZEOF_SERVICE_DATA_PAYLOAD];
+ bool initSucceeded;
+
+ bool lockedState;
+ uint8_t lockBits[SIZEOF_LOCK_BITS];
+
+ uint16_t uriDataLength;
+ uint8_t uriData[MAX_SIZE_URI_DATA_CHAR_VALUE];
+ uint8_t flags;
+ int8_t powerLevels[NUM_POWER_MODES];
+ TXPowerModes_t txPowerMode;
+ uint16_t beaconPeriod;
+ bool resetFlag;
+
+ ReadOnlyGattCharacteristic<bool> lockedStateChar;
+ WriteOnlyArrayGattCharacteristic<uint8_t, SIZEOF_LOCK_BITS> lockChar;
+ WriteOnlyArrayGattCharacteristic<uint8_t, SIZEOF_LOCK_BITS> unlockChar;
+ ReadWriteArrayGattCharacteristic<uint8_t, MAX_SIZE_URI_DATA_CHAR_VALUE> uriDataChar;
+ ReadWriteGattCharacteristic<uint8_t> flagsChar;
+ ReadWriteArrayGattCharacteristic<int8_t, NUM_POWER_MODES> txPowerLevelsChar;
+ ReadWriteGattCharacteristic<TXPowerModes_t> txPowerModeChar;
+ ReadWriteGattCharacteristic<uint16_t> beaconPeriodChar;
+ WriteOnlyGattCharacteristic<bool> resetChar;
};
-#endif // SERVICES_URIBEACONCONFIGSERVICE_H_
\ No newline at end of file
+#endif /* #ifndef __BLE_URI_BEACON_CONFIG_SERVICE_H__*/
\ No newline at end of file
