High level Bluetooth Low Energy API and radio abstraction layer
Fork of BLE_API by
Revision 970:b3e45745026d, committed 2015-11-26
- Comitter:
- rgrover1
- Date:
- Thu Nov 26 12:52:35 2015 +0000
- Parent:
- 969:61f13bc8edbf
- Child:
- 971:acf895ac7ae4
- Commit message:
- Synchronized with git rev 183a572f
Author: Liyou Zhou
Styling changes.
Changed in this revision
--- a/ble/CallChainOfFunctionPointersWithContext.h Thu Nov 26 12:52:35 2015 +0000 +++ b/ble/CallChainOfFunctionPointersWithContext.h Thu Nov 26 12:52:35 2015 +0000 @@ -18,7 +18,6 @@ #include <string.h> #include "FunctionPointerWithContext.h" -#include "SafeBool.h" /** Group one or more functions in an instance of a CallChainOfFunctionPointersWithContext, then call them in @@ -57,7 +56,7 @@ */ template <typename ContextType> -class CallChainOfFunctionPointersWithContext : public SafeBool<CallChainOfFunctionPointersWithContext<ContextType> > { +class CallChainOfFunctionPointersWithContext { public: typedef FunctionPointerWithContext<ContextType> *pFunctionPointerWithContext_t; @@ -98,49 +97,6 @@ return common_add(new FunctionPointerWithContext<ContextType>(tptr, mptr)); } - /** Add a function at the front of the chain. - * - * @param func The FunctionPointerWithContext to add. - */ - pFunctionPointerWithContext_t add(const FunctionPointerWithContext<ContextType>& func) { - return common_add(new FunctionPointerWithContext<ContextType>(func)); - } - - /** - * Detach a function pointer from a callchain - * - * @oaram toDetach FunctionPointerWithContext to detach from this callchain - * - * @return true if a function pointer has been detached and false otherwise - */ - bool detach(const FunctionPointerWithContext<ContextType>& toDetach) { - pFunctionPointerWithContext_t current = chainHead; - pFunctionPointerWithContext_t previous = NULL; - - while (current) { - if(*current == toDetach) { - if(previous == NULL) { - if(currentCalled == current) { - currentCalled = NULL; - } - chainHead = current->getNext(); - } else { - if(currentCalled == current) { - currentCalled = previous; - } - previous->chainAsNext(current->getNext()); - } - delete current; - return true; - } - - previous = current; - current = current->getNext(); - } - - return false; - } - /** Clear the call chain (remove all functions in the chain). */ void clear(void) { @@ -159,56 +115,16 @@ } /** Call all the functions in the chain in sequence + * @Note: The stack frames of all the callbacks within the chained + * FunctionPointers will stack up. Hopefully there won't be too many + * chained FunctionPointers. */ void call(ContextType context) { - ((const CallChainOfFunctionPointersWithContext*) this)->call(context); - } - - /** - * @brief same as above but const - */ - void call(ContextType context) const { - currentCalled = chainHead; - - while(currentCalled) { - currentCalled->call(context); - // if this was the head and the call removed the head - if(currentCalled == NULL) { - currentCalled = chainHead; - } else { - currentCalled = currentCalled->getNext(); - } + if (chainHead) { + chainHead->call(context); } } - /** - * @brief same as above but with function call operator - * \code - * - * void first(bool); - * void second(bool); - * - * CallChainOfFunctionPointerWithContext<bool> foo; - * - * foo.attach(first); - * foo.attach(second); - * - * // call the callchain like a function - * foo(true); - * - * \endcode - */ - void operator()(ContextType context) const { - call(context); - } - - /** - * @brief bool conversion operation - */ - bool toBool() const { - return chainHead != NULL; - } - private: pFunctionPointerWithContext_t common_add(pFunctionPointerWithContext_t pf) { if (chainHead == NULL) { @@ -223,11 +139,6 @@ private: pFunctionPointerWithContext_t chainHead; - // iterator during a function call, this has to be mutable because the call function is const. - // Note: mutable is the correct behaviour here, the iterator never leak outside the object. - // So the object can still be seen as logically const even if it change its internal state - mutable pFunctionPointerWithContext_t currentCalled; - /* Disallow copy constructor and assignment operators. */ private:
--- a/ble/DiscoveredCharacteristic.h Thu Nov 26 12:52:35 2015 +0000 +++ b/ble/DiscoveredCharacteristic.h Thu Nov 26 12:52:35 2015 +0000 @@ -83,8 +83,6 @@ */ ble_error_t read(uint16_t offset = 0) const; - ble_error_t read(uint16_t offset, const GattClient::ReadCallback_t& onRead) const; - /** * Perform a write without response procedure. * @@ -137,11 +135,6 @@ */ ble_error_t write(uint16_t length, const uint8_t *value) const; - /** - * Same as above but register the callback wich will be called once the data has been written - */ - ble_error_t write(uint16_t length, const uint8_t *value, const GattClient::WriteCallback_t& onRead) const; - void setupLongUUID(UUID::LongUUIDBytes_t longUUID) { uuid.setupLong(longUUID); }
--- a/ble/FunctionPointerWithContext.h Thu Nov 26 12:52:35 2015 +0000 +++ b/ble/FunctionPointerWithContext.h Thu Nov 26 12:52:35 2015 +0000 @@ -18,16 +18,14 @@ #define MBED_FUNCTIONPOINTER_WITH_CONTEXT_H #include <string.h> -#include "SafeBool.h" /** A class for storing and calling a pointer to a static or member void function * that takes a context. */ template <typename ContextType> -class FunctionPointerWithContext : public SafeBool<FunctionPointerWithContext<ContextType> > { +class FunctionPointerWithContext { public: typedef FunctionPointerWithContext<ContextType> *pFunctionPointerWithContext_t; - typedef const FunctionPointerWithContext<ContextType> *cpFunctionPointerWithContext_t; typedef void (*pvoidfcontext_t)(ContextType context); /** Create a FunctionPointerWithContext, attaching a static function. @@ -35,7 +33,7 @@ * @param function The void static function to attach (default is none). */ FunctionPointerWithContext(void (*function)(ContextType context) = NULL) : - _memberFunctionAndPointer(), _caller(NULL), _next(NULL) { + _function(NULL), _caller(NULL), _next(NULL) { attach(function); } @@ -50,17 +48,6 @@ attach(object, member); } - FunctionPointerWithContext(const FunctionPointerWithContext& that) : - _memberFunctionAndPointer(that._memberFunctionAndPointer), _caller(that._caller), _next(NULL) { - } - - FunctionPointerWithContext& operator=(const FunctionPointerWithContext& that) { - _memberFunctionAndPointer = that._memberFunctionAndPointer; - _caller = that._caller; - _next = NULL; - return *this; - } - /** Attach a static function. * * @param function The void static function to attach (default is none). @@ -86,29 +73,13 @@ * FunctionPointers their callbacks are invoked as well. * @Note: All chained callbacks stack up, so hopefully there won't be too * many FunctionPointers in a chain. */ - void call(ContextType context) const { + void call(ContextType context) { _caller(this, context); - } - - /** - * @brief Same as above - */ - void operator()(ContextType context) const { - call(context); - } - /** Same as above, workaround for mbed os FunctionPointer implementation. */ - void call(ContextType context) { - ((const FunctionPointerWithContext*) this)->call(context); - } - - typedef void (FunctionPointerWithContext::*bool_type)() const; - - /** - * implementation of safe bool operator - */ - bool toBool() const { - return (_function || _memberFunctionAndPointer._object); + /* Propagate the call to next in the chain. */ + if (_next) { + _next->call(context); + } } /** @@ -130,18 +101,9 @@ return (pvoidfcontext_t)_function; } - friend bool operator==(const FunctionPointerWithContext& lhs, const FunctionPointerWithContext& rhs) { - return rhs._caller == lhs._caller && - memcmp( - &rhs._memberFunctionAndPointer, - &lhs._memberFunctionAndPointer, - sizeof(rhs._memberFunctionAndPointer) - ) == 0; - } - private: template<typename T> - static void membercaller(cpFunctionPointerWithContext_t self, ContextType context) { + static void membercaller(pFunctionPointerWithContext_t self, ContextType context) { if (self->_memberFunctionAndPointer._object) { T *o = static_cast<T *>(self->_memberFunctionAndPointer._object); void (T::*m)(ContextType); @@ -150,7 +112,7 @@ } } - static void functioncaller(cpFunctionPointerWithContext_t self, ContextType context) { + static void functioncaller(pFunctionPointerWithContext_t self, ContextType context) { if (self->_function) { self->_function(context); } @@ -179,10 +141,10 @@ * object this pointer and pointer to member - * _memberFunctionAndPointer._object will be NULL if none attached */ - mutable MemberFunctionAndPtr _memberFunctionAndPointer; + MemberFunctionAndPtr _memberFunctionAndPointer; }; - void (*_caller)(const FunctionPointerWithContext*, ContextType); + void (*_caller)(FunctionPointerWithContext*, ContextType); pFunctionPointerWithContext_t _next; /**< Optional link to make a chain out of functionPointers. This * allows chaining function pointers without requiring @@ -190,23 +152,4 @@ * 'CallChain' as an alternative. */ }; -/** - * @brief Create a new FunctionPointerWithContext which bind an instance and a - * a member function together. - * @details This little helper is a just here to eliminate the need to write the - * FunctionPointerWithContext type each time you want to create one by kicking - * automatic type deduction of function templates. With this function, it is easy - * to write only one entry point for functions which expect a FunctionPointer - * in parameters. - * - * @param object to bound with member function - * @param member The member function called - * @return a new FunctionPointerWithContext - */ -template<typename T, typename ContextType> -FunctionPointerWithContext<ContextType> makeFunctionPointer(T *object, void (T::*member)(ContextType context)) -{ - return FunctionPointerWithContext<ContextType>(object, member); -} - #endif // ifndef MBED_FUNCTIONPOINTER_WITH_CONTEXT_H \ No newline at end of file
--- a/ble/Gap.h Thu Nov 26 12:52:35 2015 +0000 +++ b/ble/Gap.h Thu Nov 26 12:52:35 2015 +0000 @@ -140,15 +140,10 @@ return (durationInMillis * 1000) / UNIT_1_25_MS; } - typedef FunctionPointerWithContext<TimeoutSource_t> TimeoutEventCallback_t; - typedef CallChainOfFunctionPointersWithContext<TimeoutSource_t> TimeoutEventCallbackChain_t; - typedef FunctionPointerWithContext<const ConnectionCallbackParams_t *> ConnectionEventCallback_t; - typedef CallChainOfFunctionPointersWithContext<const ConnectionCallbackParams_t *> ConnectionEventCallbackChain_t; - - typedef FunctionPointerWithContext<const DisconnectionCallbackParams_t*> DisconnectionEventCallback_t; - typedef CallChainOfFunctionPointersWithContext<const DisconnectionCallbackParams_t*> DisconnectionEventCallbackChain_t; - + typedef void (*TimeoutEventCallback_t)(TimeoutSource_t source); + typedef void (*ConnectionEventCallback_t)(const ConnectionCallbackParams_t *params); + typedef void (*DisconnectionEventCallback_t)(const DisconnectionCallbackParams_t *params); typedef FunctionPointerWithContext<bool> RadioNotificationEventCallback_t; /* @@ -897,25 +892,11 @@ /** * Set up a callback for timeout events. Refer to TimeoutSource_t for * possible event types. - * @note It is possible to unregister callbacks using onTimeout().detach(callback) */ - void onTimeout(TimeoutEventCallback_t callback) { - timeoutCallbackChain.add(callback); - } - - /** - * @brief provide access to the callchain of timeout event callbacks - * It is possible to register callbacks using onTimeout().add(callback); - * It is possible to unregister callbacks using onTimeout().detach(callback) - * @return The timeout event callbacks chain - */ - TimeoutEventCallbackChain_t& onTimeout() { - return timeoutCallbackChain; - } + void onTimeout(TimeoutEventCallback_t callback) {timeoutCallback = callback;} /** * Append to a chain of callbacks to be invoked upon GAP connection. - * @note It is possible to unregister callbacks using onConnection().detach(callback) */ void onConnection(ConnectionEventCallback_t callback) {connectionCallChain.add(callback);} @@ -923,18 +904,7 @@ void onConnection(T *tptr, void (T::*mptr)(const ConnectionCallbackParams_t*)) {connectionCallChain.add(tptr, mptr);} /** - * @brief provide access to the callchain of connection event callbacks - * It is possible to register callbacks using onConnection().add(callback); - * It is possible to unregister callbacks using onConnection().detach(callback) - * @return The connection event callbacks chain - */ - ConnectionEventCallbackChain_t& onconnection() { - return connectionCallChain; - } - - /** * Append to a chain of callbacks to be invoked upon GAP disconnection. - * @note It is possible to unregister callbacks using onDisconnection().detach(callback) */ void onDisconnection(DisconnectionEventCallback_t callback) {disconnectionCallChain.add(callback);} @@ -942,16 +912,6 @@ void onDisconnection(T *tptr, void (T::*mptr)(const DisconnectionCallbackParams_t*)) {disconnectionCallChain.add(tptr, mptr);} /** - * @brief provide access to the callchain of disconnection event callbacks - * It is possible to register callbacks using onDisconnection().add(callback); - * It is possible to unregister callbacks using onDisconnection().detach(callback) - * @return The disconnection event callbacks chain - */ - DisconnectionEventCallbackChain_t& onDisconnection() { - return disconnectionCallChain; - } - - /** * Set the application callback for radio-notification events. * * Radio Notification is a feature that enables ACTIVE and INACTIVE @@ -980,10 +940,12 @@ */ void onRadioNotification(void (*callback)(bool param)) { radioNotificationCallback.attach(callback); + initRadioNotification(); } template <typename T> void onRadioNotification(T *tptr, void (T::*mptr)(bool)) { radioNotificationCallback.attach(tptr, mptr); + initRadioNotification(); } protected: @@ -994,7 +956,7 @@ _scanResponse(), state(), scanningActive(false), - timeoutCallbackChain(), + timeoutCallback(NULL), radioNotificationCallback(), onAdvertisementReport(), connectionCallChain(), @@ -1040,8 +1002,8 @@ } void processTimeoutEvent(TimeoutSource_t source) { - if (timeoutCallbackChain) { - timeoutCallbackChain(source); + if (timeoutCallback) { + timeoutCallback(source); } } @@ -1055,11 +1017,11 @@ bool scanningActive; protected: - TimeoutEventCallbackChain_t timeoutCallbackChain; - RadioNotificationEventCallback_t radioNotificationCallback; - AdvertisementReportCallback_t onAdvertisementReport; - ConnectionEventCallbackChain_t connectionCallChain; - DisconnectionEventCallbackChain_t disconnectionCallChain; + TimeoutEventCallback_t timeoutCallback; + RadioNotificationEventCallback_t radioNotificationCallback; + AdvertisementReportCallback_t onAdvertisementReport; + CallChainOfFunctionPointersWithContext<const ConnectionCallbackParams_t*> connectionCallChain; + CallChainOfFunctionPointersWithContext<const DisconnectionCallbackParams_t*> disconnectionCallChain; private: /* Disallow copy and assignment. */
--- a/ble/GapAdvertisingData.h Thu Nov 26 12:52:35 2015 +0000 +++ b/ble/GapAdvertisingData.h Thu Nov 26 12:52:35 2015 +0000 @@ -89,22 +89,23 @@ */ /**********************************************************************/ enum DataType_t { - FLAGS = 0x01, /**< \ref *Flags. */ - INCOMPLETE_LIST_16BIT_SERVICE_IDS = 0x02, /**< Incomplete list of 16-bit service IDs. */ - COMPLETE_LIST_16BIT_SERVICE_IDS = 0x03, /**< Complete list of 16-bit service IDs. */ - INCOMPLETE_LIST_32BIT_SERVICE_IDS = 0x04, /**< Incomplete list of 32-bit service IDs (not relevant for Bluetooth 4.0). */ - COMPLETE_LIST_32BIT_SERVICE_IDS = 0x05, /**< Complete list of 32-bit service IDs (not relevant for Bluetooth 4.0). */ - INCOMPLETE_LIST_128BIT_SERVICE_IDS = 0x06, /**< Incomplete list of 128-bit service IDs. */ - COMPLETE_LIST_128BIT_SERVICE_IDS = 0x07, /**< Complete list of 128-bit service IDs. */ - SHORTENED_LOCAL_NAME = 0x08, /**< Shortened local name. */ - COMPLETE_LOCAL_NAME = 0x09, /**< Complete local name. */ - TX_POWER_LEVEL = 0x0A, /**< TX power level (in dBm). */ - DEVICE_ID = 0x10, /**< Device ID. */ - SLAVE_CONNECTION_INTERVAL_RANGE = 0x12, /**< Slave connection interval range. */ - SERVICE_DATA = 0x16, /**< Service data. */ - APPEARANCE = 0x19, /**< \ref Appearance. */ - ADVERTISING_INTERVAL = 0x1A, /**< Advertising interval. */ - MANUFACTURER_SPECIFIC_DATA = 0xFF /**< Manufacturer specific data. */ + FLAGS = 0x01, /**< \ref *Flags */ + INCOMPLETE_LIST_16BIT_SERVICE_IDS = 0x02, /**< Incomplete list of 16-bit Service IDs */ + COMPLETE_LIST_16BIT_SERVICE_IDS = 0x03, /**< Complete list of 16-bit Service IDs */ + INCOMPLETE_LIST_32BIT_SERVICE_IDS = 0x04, /**< Incomplete list of 32-bit Service IDs (not relevant for Bluetooth 4.0) */ + COMPLETE_LIST_32BIT_SERVICE_IDS = 0x05, /**< Complete list of 32-bit Service IDs (not relevant for Bluetooth 4.0) */ + INCOMPLETE_LIST_128BIT_SERVICE_IDS = 0x06, /**< Incomplete list of 128-bit Service IDs */ + COMPLETE_LIST_128BIT_SERVICE_IDS = 0x07, /**< Complete list of 128-bit Service IDs */ + SHORTENED_LOCAL_NAME = 0x08, /**< Shortened Local Name */ + COMPLETE_LOCAL_NAME = 0x09, /**< Complete Local Name */ + TX_POWER_LEVEL = 0x0A, /**< TX Power Level (in dBm) */ + DEVICE_ID = 0x10, /**< Device ID */ + SLAVE_CONNECTION_INTERVAL_RANGE = 0x12, /**< Slave Connection Interval Range */ + LIST_128BIT_SOLICITATION_IDS = 0x15, /**< List of 128 bit service UUIDs the device is looking for */ + SERVICE_DATA = 0x16, /**< Service Data */ + APPEARANCE = 0x19, /**< \ref Appearance */ + ADVERTISING_INTERVAL = 0x1A, /**< Advertising Interval */ + MANUFACTURER_SPECIFIC_DATA = 0xFF /**< Manufacturer Specific Data */ }; typedef enum DataType_t DataType; /* Deprecated type alias. This may be dropped in a future release. */ @@ -210,27 +211,98 @@ */ ble_error_t addData(DataType advDataType, const uint8_t *payload, uint8_t len) { - /* To Do: Check if an AD type already exists and if the existing */ - /* value is exclusive or not (flags and so on). */ + ble_error_t result = BLE_ERROR_BUFFER_OVERFLOW; + + // find field + uint8_t* field = findField(advDataType); + + // Field type already exist, either add to field or replace + if (field) { + switch(advDataType) { + // These fields will be overwritten with the new value + case FLAGS: + case SHORTENED_LOCAL_NAME: + case COMPLETE_LOCAL_NAME: + case TX_POWER_LEVEL: + case DEVICE_ID: + case SLAVE_CONNECTION_INTERVAL_RANGE: + case SERVICE_DATA: + case APPEARANCE: + case ADVERTISING_INTERVAL: + case MANUFACTURER_SPECIFIC_DATA: { + // current field length, with the type subtracted + uint8_t dataLength = field[0] - 1; + + // new data has same length, do in-order replacement + if (len == dataLength) { + for (uint8_t idx = 0; idx < dataLength; idx++) { + field[2 + idx] = payload[idx]; + } + } else { + // check if data fits + if ((_payloadLen - dataLength + len) <= GAP_ADVERTISING_DATA_MAX_PAYLOAD) { + + // remove old field + while ((field + dataLength + 2) < &_payload[_payloadLen]) { + *field = field[dataLength + 2]; + field++; + } + + // reduce length + _payloadLen -= dataLength + 2; + + // add new field + result = appendField(advDataType, payload, len); + } + } - /* Make sure we don't exceed the 31 byte payload limit. */ - if (_payloadLen + len + 2 > GAP_ADVERTISING_DATA_MAX_PAYLOAD) { - return BLE_ERROR_BUFFER_OVERFLOW; + break; + } + // These fields will have the new data appended if there is sufficient space + case INCOMPLETE_LIST_16BIT_SERVICE_IDS: + case COMPLETE_LIST_16BIT_SERVICE_IDS: + case INCOMPLETE_LIST_32BIT_SERVICE_IDS: + case COMPLETE_LIST_32BIT_SERVICE_IDS: + case INCOMPLETE_LIST_128BIT_SERVICE_IDS: + case COMPLETE_LIST_128BIT_SERVICE_IDS: + case LIST_128BIT_SOLICITATION_IDS: { + // check if data fits + if ((_payloadLen + len) <= GAP_ADVERTISING_DATA_MAX_PAYLOAD) { + // make room for new field by moving the remainder of the + // advertisement payload "to the right" starting after the + // TYPE field. + uint8_t* end = &_payload[_payloadLen]; + + while (&field[1] < end) { + end[len] = *end; + end--; + } + + // insert new data + for (uint8_t idx = 0; idx < len; idx++) { + field[2 + idx] = payload[idx]; + } + + // increment lengths + field[0] += len; + _payloadLen += len; + + result = BLE_ERROR_NONE; + } + + break; + } + // Field exists but updating it is not supported. Abort operation. + default: + result = BLE_ERROR_NOT_IMPLEMENTED; + break; + } + } else { + // field doesn't exists, insert new + result = appendField(advDataType, payload, len); } - /* Field length. */ - memset(&_payload[_payloadLen], len + 1, 1); - _payloadLen++; - - /* Field ID. */ - memset(&_payload[_payloadLen], (uint8_t)advDataType, 1); - _payloadLen++; - - /* Payload. */ - memcpy(&_payload[_payloadLen], payload, len); - _payloadLen += len; - - return BLE_ERROR_NONE; + return result; } /** @@ -345,7 +417,63 @@ return (uint16_t)_appearance; } + /** + * Search advertisement data for field. + * Returns pointer to the first element in the field if found, NULL otherwise. + * Where the first element is the length of the field. + */ + const uint8_t* findField(DataType_t type) const { + return findField(type); + } + private: + /** + * Append advertising data based on the specified AD type (see DataType) + */ + ble_error_t appendField(DataType advDataType, const uint8_t *payload, uint8_t len) + { + /* Make sure we don't exceed the 31 byte payload limit */ + if (_payloadLen + len + 2 > GAP_ADVERTISING_DATA_MAX_PAYLOAD) { + return BLE_ERROR_BUFFER_OVERFLOW; + } + + /* Field length. */ + memset(&_payload[_payloadLen], len + 1, 1); + _payloadLen++; + + /* Field ID. */ + memset(&_payload[_payloadLen], (uint8_t)advDataType, 1); + _payloadLen++; + + /* Payload. */ + memcpy(&_payload[_payloadLen], payload, len); + _payloadLen += len; + + return BLE_ERROR_NONE; + } + + /** + * Search advertisement data for field. + * Returns pointer to the first element in the field if found, NULL otherwise. + * Where the first element is the length of the field. + */ + uint8_t* findField(DataType_t type) { + // scan through advertisement data + for (uint8_t idx = 0; idx < _payloadLen; ) { + uint8_t fieldType = _payload[idx + 1]; + + if (fieldType == type) { + return &_payload[idx]; + } + + // advance to next field + idx += _payload[idx] + 1; + } + + // field not found + return NULL; + } + uint8_t _payload[GAP_ADVERTISING_DATA_MAX_PAYLOAD]; uint8_t _payloadLen; uint16_t _appearance;
--- a/ble/GattClient.h Thu Nov 26 12:52:35 2015 +0000 +++ b/ble/GattClient.h Thu Nov 26 12:52:35 2015 +0000 @@ -23,23 +23,18 @@ #include "GattCallbackParamTypes.h" -#include "CallChainOfFunctionPointersWithContext.h" - class GattClient { public: - typedef FunctionPointerWithContext<const GattReadCallbackParams*> ReadCallback_t; - typedef CallChainOfFunctionPointersWithContext<const GattReadCallbackParams*> ReadCallbackChain_t; + typedef void (*ReadCallback_t)(const GattReadCallbackParams *params); enum WriteOp_t { GATT_OP_WRITE_REQ = 0x01, /**< Write request. */ GATT_OP_WRITE_CMD = 0x02, /**< Write command. */ }; - typedef FunctionPointerWithContext<const GattWriteCallbackParams*> WriteCallback_t; - typedef CallChainOfFunctionPointersWithContext<const GattWriteCallbackParams*> WriteCallbackChain_t; + typedef void (*WriteCallback_t)(const GattWriteCallbackParams *params); - typedef FunctionPointerWithContext<const GattHVXCallbackParams*> HVXCallback_t; - typedef CallChainOfFunctionPointersWithContext<const GattHVXCallbackParams*> HVXCallbackChain_t; + typedef void (*HVXCallback_t)(const GattHVXCallbackParams *params); /* * The following functions are meant to be overridden in the platform-specific sub-class. @@ -246,42 +241,18 @@ /* Event callback handlers. */ public: /** - * Set up a callback for read response events. - * It is possible to remove registered callbacks using - * onDataRead().detach(callbackToRemove) + * Set up a callback for read response events. */ void onDataRead(ReadCallback_t callback) { - onDataReadCallbackChain.add(callback); - } - - /** - * @brief provide access to the callchain of read callbacks - * It is possible to register callbacks using onDataRead().add(callback); - * It is possible to unregister callbacks using onDataRead().detach(callback) - * @return The read callbacks chain - */ - ReadCallbackChain_t& onDataRead() { - return onDataReadCallbackChain; + onDataReadCallback = callback; } /** * Set up a callback for write response events. - * It is possible to remove registered callbacks using - * onDataWritten().detach(callbackToRemove). * @Note: Write commands (issued using writeWoResponse) don't generate a response. */ void onDataWritten(WriteCallback_t callback) { - onDataWriteCallbackChain.add(callback); - } - - /** - * @brief provide access to the callchain of data written callbacks - * It is possible to register callbacks using onDataWritten().add(callback); - * It is possible to unregister callbacks using onDataWritten().detach(callback) - * @return The data written callbacks chain - */ - WriteCallbackChain_t& onDataWritten() { - return onDataWriteCallbackChain; + onDataWriteCallback = callback; } /** @@ -308,21 +279,9 @@ * Set up a callback for when the GATT client receives an update event * corresponding to a change in the value of a characteristic on the remote * GATT server. - * It is possible to remove registered callbacks using onHVX().detach(callbackToRemove). */ void onHVX(HVXCallback_t callback) { - onHVXCallbackChain.add(callback); - } - - - /** - * @brief provide access to the callchain of HVX callbacks - * It is possible to register callbacks using onHVX().add(callback); - * It is possible to unregister callbacks using onHVX().detach(callback) - * @return The HVX callbacks chain - */ - HVXCallbackChain_t& onHVX() { - return onHVXCallbackChain; + onHVXCallback = callback; } protected: @@ -333,23 +292,27 @@ /* Entry points for the underlying stack to report events back to the user. */ public: void processReadResponse(const GattReadCallbackParams *params) { - onDataReadCallbackChain(params); + if (onDataReadCallback) { + onDataReadCallback(params); + } } void processWriteResponse(const GattWriteCallbackParams *params) { - onDataWriteCallbackChain(params); + if (onDataWriteCallback) { + onDataWriteCallback(params); + } } void processHVXEvent(const GattHVXCallbackParams *params) { - if (onHVXCallbackChain) { - onHVXCallbackChain(params); + if (onHVXCallback) { + onHVXCallback(params); } } protected: - ReadCallbackChain_t onDataReadCallbackChain; - WriteCallbackChain_t onDataWriteCallbackChain; - HVXCallbackChain_t onHVXCallbackChain; + ReadCallback_t onDataReadCallback; + WriteCallback_t onDataWriteCallback; + HVXCallback_t onHVXCallback; private: /* Disallow copy and assignment. */
--- a/ble/GattServer.h Thu Nov 26 12:52:35 2015 +0000 +++ b/ble/GattServer.h Thu Nov 26 12:52:35 2015 +0000 @@ -26,18 +26,9 @@ class GattServer { public: - /* Event callback handlers. */ - typedef FunctionPointerWithContext<unsigned> DataSentCallback_t; - typedef CallChainOfFunctionPointersWithContext<unsigned> DataSentCallbackChain_t; - - typedef FunctionPointerWithContext<const GattWriteCallbackParams*> DataWrittenCallback_t; - typedef CallChainOfFunctionPointersWithContext<const GattWriteCallbackParams*> DataWrittenCallbackChain_t; - - typedef FunctionPointerWithContext<const GattReadCallbackParams*> DataReadCallback_t; - typedef CallChainOfFunctionPointersWithContext<const GattReadCallbackParams *> DataReadCallbackChain_t; - - typedef FunctionPointerWithContext<GattAttribute::Handle_t> EventCallback_t; + typedef void (*EventCallback_t)(GattAttribute::Handle_t attributeHandle); + typedef void (*ServerEventCallback_t)(void); /**< Independent of any particular attribute. */ protected: GattServer() : @@ -247,20 +238,13 @@ * @Note: It is also possible to set up a callback into a member function of * some object. */ - void onDataSent(const DataSentCallback_t& callback) {dataSentCallChain.add(callback);} + void onDataSent(void (*callback)(unsigned count)) {dataSentCallChain.add(callback);} template <typename T> void onDataSent(T *objPtr, void (T::*memberPtr)(unsigned count)) { dataSentCallChain.add(objPtr, memberPtr); } /** - * @brief get the callback chain called when the event DATA_EVENT is triggered. - */ - DataSentCallbackChain_t& onDataSent() { - return dataSentCallChain; - } - - /** * Set up a callback for when an attribute has its value updated by or at the * connected peer. For a peripheral, this callback is triggered when the local * GATT server has an attribute updated by a write command from the peer. @@ -274,26 +258,14 @@ * * @Note: It is also possible to set up a callback into a member function of * some object. - * - * @Note It is possible to unregister a callback using onDataWritten().detach(callback) */ - void onDataWritten(const DataWrittenCallback_t& callback) {dataWrittenCallChain.add(callback);} + void onDataWritten(void (*callback)(const GattWriteCallbackParams *eventDataP)) {dataWrittenCallChain.add(callback);} template <typename T> void onDataWritten(T *objPtr, void (T::*memberPtr)(const GattWriteCallbackParams *context)) { dataWrittenCallChain.add(objPtr, memberPtr); } /** - * @brief provide access to the callchain of data written event callbacks - * It is possible to register callbacks using onDataWritten().add(callback); - * It is possible to unregister callbacks using onDataWritten().detach(callback) - * @return The data written event callbacks chain - */ - DataWrittenCallbackChain_t& onDataWritten() { - return dataWrittenCallChain; - } - - /** * Setup a callback to be invoked on the peripheral when an attribute is * being read by a remote client. * @@ -309,12 +281,10 @@ * @Note: It is also possible to set up a callback into a member function of * some object. * - * @Note It is possible to unregister a callback using onDataRead().detach(callback) - * * @return BLE_ERROR_NOT_IMPLEMENTED if this functionality isn't available; * else BLE_ERROR_NONE. */ - ble_error_t onDataRead(const DataReadCallback_t& callback) { + ble_error_t onDataRead(void (*callback)(const GattReadCallbackParams *eventDataP)) { if (!isOnDataReadAvailable()) { return BLE_ERROR_NOT_IMPLEMENTED; } @@ -333,16 +303,6 @@ } /** - * @brief provide access to the callchain of data read event callbacks - * It is possible to register callbacks using onDataRead().add(callback); - * It is possible to unregister callbacks using onDataRead().detach(callback) - * @return The data read event callbacks chain - */ - DataReadCallbackChain_t& onDataRead() { - return dataReadCallChain; - } - - /** * Set up a callback for when notifications or indications are enabled for a * characteristic on the local GATT server. */ @@ -363,11 +323,15 @@ /* Entry points for the underlying stack to report events back to the user. */ protected: void handleDataWrittenEvent(const GattWriteCallbackParams *params) { - dataWrittenCallChain.call(params); + if (dataWrittenCallChain.hasCallbacksAttached()) { + dataWrittenCallChain.call(params); + } } void handleDataReadEvent(const GattReadCallbackParams *params) { - dataReadCallChain.call(params); + if (dataReadCallChain.hasCallbacksAttached()) { + dataReadCallChain.call(params); + } } void handleEvent(GattServerEvents::gattEvent_e type, GattAttribute::Handle_t attributeHandle) { @@ -393,7 +357,9 @@ } void handleDataSentEvent(unsigned count) { - dataSentCallChain.call(count); + if (dataSentCallChain.hasCallbacksAttached()) { + dataSentCallChain.call(count); + } } protected: @@ -401,12 +367,12 @@ uint8_t characteristicCount; private: - DataSentCallbackChain_t dataSentCallChain; - DataWrittenCallbackChain_t dataWrittenCallChain; - DataReadCallbackChain_t dataReadCallChain; - EventCallback_t updatesEnabledCallback; - EventCallback_t updatesDisabledCallback; - EventCallback_t confirmationReceivedCallback; + CallChainOfFunctionPointersWithContext<unsigned> dataSentCallChain; + CallChainOfFunctionPointersWithContext<const GattWriteCallbackParams *> dataWrittenCallChain; + CallChainOfFunctionPointersWithContext<const GattReadCallbackParams *> dataReadCallChain; + EventCallback_t updatesEnabledCallback; + EventCallback_t updatesDisabledCallback; + EventCallback_t confirmationReceivedCallback; private: /* Disallow copy and assignment. */
--- a/ble/SafeBool.h Thu Nov 26 12:52:35 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,114 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2013 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef BLE_API_SAFE_BOOL_H_ -#define BLE_API_SAFE_BOOL_H_ - -//safe bool idiom, see : http://www.artima.com/cppsource/safebool.html - -namespace SafeBool_ { -/** - * @brief Base class for all intances of SafeBool, - * This base class reduce instantiation of trueTag function - */ -class base { - template<typename> - friend class SafeBool; - -protected: - //the bool type is a pointer to method which can be used in boolean context - typedef void (base::*BoolType_t)() const; - - // non implemented call, use to disallow conversion between unrelated types - void invalidTag() const; - - // member function which indicate true value - void trueTag() const {} -}; - - -} - -/** - * @brief template class SafeBool use CRTP to made boolean conversion easy and correct. - * Derived class should implement the function bool toBool() const to make this work. Inheritance - * should be public. - * - * @tparam T Type of the derived class - * - * \code - * - * class A : public SafeBool<A> { - * public: - * - * // boolean conversion - * bool toBool() { - * - * } - * }; - * - * class B : public SafeBool<B> { - * public: - * - * // boolean conversion - * bool toBool() const { - * - * } - * }; - * - * A a; - * B b; - * - * // will compile - * if(a) { - * - * } - * - * // compilation error - * if(a == b) { - * - * } - * - * - * \endcode - */ -template <typename T> -class SafeBool : public SafeBool_::base { -public: - /** - * bool operator implementation, derived class has to provide bool toBool() const function. - */ - operator BoolType_t() const { - return (static_cast<const T*>(this))->toBool() - ? &SafeBool<T>::trueTag : 0; - } -}; - -//Avoid conversion to bool between different classes -template <typename T, typename U> -void operator==(const SafeBool<T>& lhs,const SafeBool<U>& rhs) { - lhs.invalidTag(); -// return false; -} - -//Avoid conversion to bool between different classes -template <typename T,typename U> -void operator!=(const SafeBool<T>& lhs,const SafeBool<U>& rhs) { - lhs.invalidTag(); -// return false; -} - -#endif /* BLE_API_SAFE_BOOL_H_ */ \ No newline at end of file
--- a/ble/ServiceDiscovery.h Thu Nov 26 12:52:35 2015 +0000 +++ b/ble/ServiceDiscovery.h Thu Nov 26 12:52:35 2015 +0000 @@ -38,7 +38,7 @@ * framework. The application can safely make a persistent shallow-copy of * this object to work with the service beyond the callback. */ - typedef FunctionPointerWithContext<const DiscoveredService *> ServiceCallback_t; + typedef void (*ServiceCallback_t)(const DiscoveredService *); /** * Callback type for when a matching characteristic is found during service- @@ -48,12 +48,12 @@ * framework. The application can safely make a persistent shallow-copy of * this object to work with the characteristic beyond the callback. */ - typedef FunctionPointerWithContext<const DiscoveredCharacteristic *> CharacteristicCallback_t; + typedef void (*CharacteristicCallback_t)(const DiscoveredCharacteristic *); /** * Callback type for when serviceDiscovery terminates. */ - typedef FunctionPointerWithContext<Gap::Handle_t> TerminationCallback_t; + typedef void (*TerminationCallback_t)(Gap::Handle_t connectionHandle); public: /**
--- a/ble/services/DFUService.h Thu Nov 26 12:52:35 2015 +0000 +++ b/ble/services/DFUService.h Thu Nov 26 12:52:35 2015 +0000 @@ -20,7 +20,9 @@ #include "ble/BLE.h" #include "ble/UUID.h" -extern "C" void bootloader_start(void); +extern "C" { +#include "dfu_app_handler.h" +} extern const uint8_t DFUServiceBaseUUID[]; extern const uint16_t DFUServiceShortUUID; @@ -101,7 +103,15 @@ handoverCallback(); } - bootloader_start(); + // Call bootloader_start implicitly trough a event handler call + // it is a work around for bootloader_start not being public in sdk 8.1 + ble_dfu_t p_dfu; + ble_dfu_evt_t p_evt; + + p_dfu.conn_handle = params->connHandle; + p_evt.ble_dfu_evt_type = BLE_DFU_START; + + dfu_app_on_dfu_evt(&p_dfu, &p_evt); } }
--- a/ble/services/EddystoneConfigService.h Thu Nov 26 12:52:35 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,540 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2013 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SERVICES_EDDYSTONE_BEACON_CONFIG_SERVICE_H_ -#define SERVICES_EDDYSTONE_BEACON_CONFIG_SERVICE_H_ - -#include "mbed.h" -#include "ble/BLE.h" -#include "ble/services/EddystoneService.h" - -#define UUID_URI_BEACON(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); -extern const uint8_t BEACON_EDDYSTONE[2]; - -/** -* @class EddystoneConfigService -* @brief Eddystone Configuration Service. Can be used to set URL, adjust power levels, and set flags. -* See https://github.com/google/eddystone -* -*/ -class EddystoneConfigService -{ -public: - /** - * @brief Transmission Power Modes for UriBeacon - */ - enum { - TX_POWER_MODE_LOWEST, - TX_POWER_MODE_LOW, - TX_POWER_MODE_MEDIUM, - TX_POWER_MODE_HIGH, - NUM_POWER_MODES - }; - - static const unsigned ADVERTISING_INTERVAL_MSEC = 1000; // Advertising interval for config service. - static const unsigned SERVICE_DATA_MAX = 31; // Maximum size of service data in ADV packets - - typedef uint8_t Lock_t[16]; /* 128 bits */ - typedef int8_t PowerLevels_t[NUM_POWER_MODES]; - - // There are currently 3 subframes defined, URI, UID, and TLM -#define EDDYSTONE_MAX_FRAMETYPE 3 - static const unsigned URI_DATA_MAX = 18; - typedef uint8_t UriData_t[URI_DATA_MAX]; - - // UID Frame Type subfields - static const size_t UID_NAMESPACEID_SIZE = 10; - typedef uint8_t UIDNamespaceID_t[UID_NAMESPACEID_SIZE]; - static const size_t UID_INSTANCEID_SIZE = 6; - typedef uint8_t UIDInstanceID_t[UID_INSTANCEID_SIZE]; - - // Eddystone Frame Type ID - static const uint8_t FRAME_TYPE_UID = 0x00; - static const uint8_t FRAME_TYPE_URL = 0x10; - static const uint8_t FRAME_TYPE_TLM = 0x20; - - static const uint8_t FRAME_SIZE_TLM = 14; // TLM frame is a constant 14Bytes - static const uint8_t FRAME_SIZE_UID = 20; // includes RFU bytes - - struct Params_t { - // Config Data - bool isConfigured; // Flag for configuration being complete, - // True = configured, false = not configured. Reset at instantiation, used for external callbacks. - uint8_t lockedState; - Lock_t lock; - uint8_t flags; - PowerLevels_t advPowerLevels; // Current value of AdvertisedPowerLevels - uint8_t txPowerMode; // Firmware power levels used with setTxPower() - uint16_t beaconPeriod; - // TLM Frame Data - uint8_t tlmVersion; // version of TLM packet - bool tlmEnabled; - float tlmBeaconPeriod; // how often to broadcat TLM frame, in seconds. - // URI Frame Data - uint8_t uriDataLength; - UriData_t uriData; - bool uriEnabled; - float uriBeaconPeriod; // how often to broadcast URIFrame, in seconds. - // UID Frame Data - UIDNamespaceID_t uidNamespaceID; // UUID type, Namespace ID, 10B - UIDInstanceID_t uidInstanceID; // UUID type, Instance ID, 6B - bool uidEnabled; - float uidBeaconPeriod; // how often to broadcast UID Frame, in seconds. - }; - - /** - * @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] defaultAdvPowerLevelsIn - * Default power-levels array; applies only if the resetToDefaultsFlag is true. - */ - EddystoneConfigService(BLEDevice &bleIn, - Params_t ¶msIn, - PowerLevels_t &defaultAdvPowerLevelsIn, - PowerLevels_t &radioPowerLevelsIn) : - ble(bleIn), - params(paramsIn), // Initialize URL Data - defaultAdvPowerLevels(defaultAdvPowerLevelsIn), - radioPowerLevels(radioPowerLevelsIn), - initSucceeded(false), - resetFlag(), - defaultUidNamespaceID(), // Initialize UID Data - defaultUidInstanceID(), - defaultUidPower(defaultAdvPowerLevelsIn[params.txPowerMode]), - uidIsSet(false), - defaultUriDataLength(), - defaultUriData(), - defaultUrlPower(defaultAdvPowerLevelsIn[params.txPowerMode]), - urlIsSet(false), - tlmIsSet(false), - lockedStateChar(UUID_LOCK_STATE_CHAR, ¶ms.lockedState), - lockChar(UUID_LOCK_CHAR, ¶ms.lock), - uriDataChar(UUID_URI_DATA_CHAR, params.uriData, 0, URI_DATA_MAX, - 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) { - // set eddystone as not configured yet. Used to exit config before timeout if GATT services are written to. - params.isConfigured = false; - - lockChar.setWriteAuthorizationCallback(this, &EddystoneConfigService::lockAuthorizationCallback); - unlockChar.setWriteAuthorizationCallback(this, &EddystoneConfigService::unlockAuthorizationCallback); - uriDataChar.setWriteAuthorizationCallback(this, &EddystoneConfigService::uriDataWriteAuthorizationCallback); - flagsChar.setWriteAuthorizationCallback(this, &EddystoneConfigService::basicAuthorizationCallback<uint8_t>); - advPowerLevelsChar.setWriteAuthorizationCallback(this, &EddystoneConfigService::basicAuthorizationCallback<PowerLevels_t>); - txPowerModeChar.setWriteAuthorizationCallback(this, &EddystoneConfigService::powerModeAuthorizationCallback); - beaconPeriodChar.setWriteAuthorizationCallback(this, &EddystoneConfigService::basicAuthorizationCallback<uint16_t>); - resetChar.setWriteAuthorizationCallback(this, &EddystoneConfigService::basicAuthorizationCallback<uint8_t>); - - static GattCharacteristic *charTable[] = { - &lockedStateChar, &lockChar, &unlockChar, &uriDataChar, - &flagsChar, &advPowerLevelsChar, &txPowerModeChar, &beaconPeriodChar, &resetChar - }; - - GattService configService(UUID_URI_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *)); - - ble.addService(configService); - ble.onDataWritten(this, &EddystoneConfigService::onDataWrittenCallback); - } - - /** - * @brief Start EddystoneConfig advertising. This function should be called - * after the EddystoneConfig constructor and after all the frames have been added. - * - * @paramsP[in] resetToDefaultsFlag - * Applies to the state of the 'paramsIn' parameter. - * If true, it indicates that paramsIn is potentially - * un-initialized, and default values should be used - * instead. Otherwise, paramsIn overrides the defaults. - */ - void start(bool resetToDefaultsFlag){ - INFO("reset to defaults flag = %d", resetToDefaultsFlag); - if (!resetToDefaultsFlag && (params.uriDataLength > URI_DATA_MAX)) { - INFO("Reset to Defaults triggered"); - resetToDefaultsFlag = true; - } - - if (resetToDefaultsFlag) { - resetToDefaults(); - } else { - updateCharacteristicValues(); - } - - setupEddystoneConfigAdvertisements(); /* Setup advertising for the configService. */ - initSucceeded = true; - } - - /* - * Check if eddystone initialized successfully - */ - bool initSuccessfully(void) const { - return initSucceeded; - } - - /* - * @brief Function to update the default values for the TLM frame. Only applied if Reset Defaults is applied. - * - * @param[in] tlmVersionIn Version of the TLM frame being used - * @param[in] advPeriodInMin how long between TLM frames being advertised, this is measured in minutes. - * - */ - void setDefaultTLMFrameData(uint8_t tlmVersionIn = 0, float advPeriodInSec = 60){ - DBG("Setting Default TLM Data, version = %d, advPeriodInMind= %f", tlmVersionIn, advPeriodInSec); - defaultTlmVersion = tlmVersionIn; - TlmBatteryVoltage = 0; - TlmBeaconTemp = 0x8000; - TlmPduCount = 0; - TlmTimeSinceBoot = 0; - defaultTlmAdvPeriod = advPeriodInSec; - tlmIsSet = true; // flag to add this to eddystone service when config is done - } - - /* - * @brief Function to update the default values for the URI frame. Only applied if Reset Defaults is applied. - * - * @param[in] uriIn url to advertise - * @param[in] advPeriod how long to advertise the url for, measured in number of ADV frames. - * - */ - void setDefaultURIFrameData(const char *uriIn, float advPeriod = 1){ - DBG("Setting Default URI Data"); - // Set URL Frame - EddystoneService::encodeURL(uriIn, defaultUriData, defaultUriDataLength); // encode URL to URL Formatting - if (defaultUriDataLength > URI_DATA_MAX) { - return; - } - INFO("\t URI input = %s : %d", uriIn, defaultUriDataLength); - INFO("\t default URI = %s : %d ", defaultUriData, defaultUriDataLength ); - defaultUriAdvPeriod = advPeriod; - urlIsSet = true; // flag to add this to eddystone service when config is done - } - - /* - * @brief Function to update the default values for the UID frame. Only applied if Reset Defaults is applied. - * - * @param[in] namespaceID 10Byte Namespace ID - * @param[in] instanceID 6Byte Instance ID - * @param[in] advPeriod how long to advertise the URL for, measured in the number of adv frames. - * - */ - void setDefaultUIDFrameData(UIDNamespaceID_t *namespaceID, UIDInstanceID_t *instanceID, float advPeriod = 10){ - //Set UID frame - DBG("Setting default UID Data"); - memcpy(defaultUidNamespaceID, namespaceID, UID_NAMESPACEID_SIZE); - memcpy(defaultUidInstanceID, instanceID, UID_INSTANCEID_SIZE); - defaultUidAdvPeriod = advPeriod; - uidIsSet = true; // flag to add this to eddystone service when config is done - } - - /* Start out by advertising the configService for a limited time after - * startup; and switch to the normal non-connectible beacon functionality - * afterwards. */ - void setupEddystoneConfigAdvertisements() { - const char DEVICE_NAME[] = "eddystone Config"; - - ble.clearAdvertisingPayload(); - - ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); - - // UUID is in different order in the ADV frame (!) - uint8_t reversedServiceUUID[sizeof(UUID_URI_BEACON_SERVICE)]; - for (unsigned int i = 0; i < sizeof(UUID_URI_BEACON_SERVICE); i++) { - reversedServiceUUID[i] = UUID_URI_BEACON_SERVICE[sizeof(UUID_URI_BEACON_SERVICE) - i - 1]; - } - ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, reversedServiceUUID, sizeof(reversedServiceUUID)); - ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG); - ble.accumulateScanResponse(GapAdvertisingData::COMPLETE_LOCAL_NAME, reinterpret_cast<const uint8_t *>(&DEVICE_NAME), sizeof(DEVICE_NAME)); - ble.accumulateScanResponse( - GapAdvertisingData::TX_POWER_LEVEL, - reinterpret_cast<uint8_t *>(&defaultAdvPowerLevels[EddystoneConfigService::TX_POWER_MODE_LOW]), - sizeof(uint8_t)); - - ble.setTxPower(radioPowerLevels[params.txPowerMode]); - ble.setDeviceName(reinterpret_cast<const uint8_t *>(&DEVICE_NAME)); - ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); - ble.setAdvertisingInterval(ADVERTISING_INTERVAL_MSEC); - } - - /* - * This function actually impliments the Eddystone Beacon service. It can be called with the help of the wrapper function - * to load saved config params, or it can be called explicitly to reset the eddystone beacon to hardcoded values on each reset. - * - */ - void setupEddystoneAdvertisements() { - DBG("Switching Config -> adv"); - // Save params to storage - extern void saveURIBeaconConfigParams(const Params_t *paramsP); /* forward declaration; necessary to avoid a circular dependency. */ - saveURIBeaconConfigParams(¶ms); - INFO("Saved Params to Memory.") - // Setup Eddystone Service - static EddystoneService eddyServ(ble, params.beaconPeriod, radioPowerLevels[params.txPowerMode]); - // Set configured frames (TLM,UID,URI...etc) - if (params.tlmEnabled) { - eddyServ.setTLMFrameData(params.tlmVersion, params.tlmBeaconPeriod); - } - if (params.uriEnabled) { - eddyServ.setURLFrameEncodedData(params.advPowerLevels[params.txPowerMode], (const char *) params.uriData, params.uriDataLength, params.uriBeaconPeriod); - } - if (params.uidEnabled) { - eddyServ.setUIDFrameData(params.advPowerLevels[params.txPowerMode], - (uint8_t *)params.uidNamespaceID, - (uint8_t *)params.uidInstanceID, - params.uidBeaconPeriod); - } - // Start Advertising the eddystone service. - eddyServ.start(); - } - -private: - /* - * 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 onDataWrittenCallback(const GattWriteCallbackParams *writeParams) { - uint16_t handle = writeParams->handle; - - if (handle == lockChar.getValueHandle()) { - // Validated earlier - memcpy(params.lock, writeParams->data, sizeof(Lock_t)); - // Set the state to be locked by the lock code (note: zeros are a valid lock) - params.lockedState = true; - INFO("Device Locked"); - } else if (handle == unlockChar.getValueHandle()) { - // Validated earlier - params.lockedState = false; - INFO("Device Unlocked"); - } else if (handle == uriDataChar.getValueHandle()) { - params.uriDataLength = writeParams->len; - memset(params.uriData, 0x00, URI_DATA_MAX); // clear URI string - memcpy(params.uriData, writeParams->data, writeParams->len); // set URI string - params.uriEnabled = true; - INFO("URI = %s, URILen = %d", writeParams->data, writeParams->len); - } else if (handle == flagsChar.getValueHandle()) { - params.flags = *(writeParams->data); - INFO("flagsChar = 0x%x", params.flags); - } else if (handle == advPowerLevelsChar.getValueHandle()) { - memcpy(params.advPowerLevels, writeParams->data, sizeof(PowerLevels_t)); - INFO("PowerLevelsChar = %4x", params.advPowerLevels); - } else if (handle == txPowerModeChar.getValueHandle()) { - params.txPowerMode = *(writeParams->data); - INFO("TxPowerModeChar = %d", params.txPowerMode); - } else if (handle == beaconPeriodChar.getValueHandle()) { - params.beaconPeriod = *((uint16_t *)(writeParams->data)); - INFO("BeaconPeriod = %d", params.beaconPeriod); - - /* Re-map beaconPeriod to within permissible bounds if necessary. */ - if (params.beaconPeriod != 0) { - bool paramsUpdated = false; - if (params.beaconPeriod < ble.getMinAdvertisingInterval()) { - params.beaconPeriod = ble.getMinAdvertisingInterval(); - paramsUpdated = true; - } else if (params.beaconPeriod > ble.getMaxAdvertisingInterval()) { - params.beaconPeriod = ble.getMaxAdvertisingInterval(); - paramsUpdated = true; - } - if (paramsUpdated) { - ble.updateCharacteristicValue(beaconPeriodChar.getValueHandle(), reinterpret_cast<uint8_t *>(¶ms.beaconPeriod), sizeof(uint16_t)); - } - } - } else if (handle == resetChar.getValueHandle()) { - INFO("Reset triggered from Config Service, resetting to defaults"); - resetToDefaults(); - } - updateCharacteristicValues(); - params.isConfigured = true; // some configuration data has been passed, on disconnect switch to advertising mode. - } - - /* - * Reset the default values. - */ - void resetToDefaults(void) { - INFO("Resetting to defaults"); - // General - params.lockedState = false; - memset(params.lock, 0, sizeof(Lock_t)); - params.flags = 0x10; - memcpy(params.advPowerLevels, defaultAdvPowerLevels, sizeof(PowerLevels_t)); - params.txPowerMode = TX_POWER_MODE_LOW; - params.beaconPeriod = (uint16_t) defaultUriAdvPeriod * 1000; - - // TLM Frame - params.tlmVersion = defaultTlmVersion; - params.tlmBeaconPeriod = defaultTlmAdvPeriod; - params.tlmEnabled = tlmIsSet; - - // URL Frame - memcpy(params.uriData, defaultUriData, URI_DATA_MAX); - params.uriDataLength = defaultUriDataLength; - params.uriBeaconPeriod = defaultUriAdvPeriod; - params.uriEnabled = urlIsSet; - - // UID Frame - memcpy(params.uidNamespaceID, defaultUidNamespaceID, UID_NAMESPACEID_SIZE); - memcpy(params.uidInstanceID, defaultUidInstanceID, UID_INSTANCEID_SIZE); - params.uidBeaconPeriod = defaultUidAdvPeriod; - params.uidEnabled = uidIsSet; - - updateCharacteristicValues(); - } - - /* - * Internal helper function used to update the GATT database following any - * change to the internal state of the service object. - */ - void updateCharacteristicValues(void) { - ble.updateCharacteristicValue(lockedStateChar.getValueHandle(), ¶ms.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(GattWriteAuthCallbackParams *authParams) { - if (params.lockedState) { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION; - } else if (authParams->len != sizeof(Lock_t)) { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; - } else if (authParams->offset != 0) { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; - } else { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; - } - } - - void unlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams) { - if ((!params.lockedState) && (authParams->len == sizeof(Lock_t))) { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; - } else if (authParams->len != sizeof(Lock_t)) { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; - } else if (authParams->offset != 0) { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; - } else if (memcmp(authParams->data, params.lock, sizeof(Lock_t)) != 0) { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION; - } else { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; - } - } - - void uriDataWriteAuthorizationCallback(GattWriteAuthCallbackParams *authParams) { - if (params.lockedState) { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION; - } else if (authParams->offset != 0) { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; - } else { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; - } - } - - void powerModeAuthorizationCallback(GattWriteAuthCallbackParams *authParams) { - if (params.lockedState) { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION; - } else if (authParams->len != sizeof(uint8_t)) { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; - } else if (authParams->offset != 0) { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; - } else if (*((uint8_t *)authParams->data) >= NUM_POWER_MODES) { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED; - } else { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; - } - } - - template <typename T> - void basicAuthorizationCallback(GattWriteAuthCallbackParams *authParams) { - if (params.lockedState) { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INSUF_AUTHORIZATION; - } else if (authParams->len != sizeof(T)) { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH; - } else if (authParams->offset != 0) { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET; - } else { - authParams->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS; - } - } - - BLEDevice &ble; - Params_t ¶ms; - Ticker timeSinceBootTick; - Timeout switchFrame; - // Default value that is restored on reset - PowerLevels_t &defaultAdvPowerLevels; // this goes into the advertising frames (radio power measured at 1m from device) - PowerLevels_t &radioPowerLevels; // this configures the power levels of the radio - uint8_t lockedState; - bool initSucceeded; - uint8_t resetFlag; - bool switchFlag; - - //UID Default value that is restored on reset - UIDNamespaceID_t defaultUidNamespaceID; - UIDInstanceID_t defaultUidInstanceID; - float defaultUidAdvPeriod; - int8_t defaultUidPower; - uint16_t uidRFU; - bool uidIsSet; - - //URI Default value that is restored on reset - uint8_t defaultUriDataLength; - UriData_t defaultUriData; - int8_t defaultUrlPower; - float defaultUriAdvPeriod; - bool urlIsSet; - - //TLM Default value that is restored on reset - uint8_t defaultTlmVersion; - float defaultTlmAdvPeriod; - volatile uint16_t TlmBatteryVoltage; - volatile uint16_t TlmBeaconTemp; - volatile uint32_t TlmPduCount; - volatile uint32_t TlmTimeSinceBoot; - bool tlmIsSet; - - 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; -}; - -#endif // SERVICES_EDDYSTONE_BEACON_CONFIG_SERVICE_H_ \ No newline at end of file
--- a/ble/services/EddystoneService.h Thu Nov 26 12:52:35 2015 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,651 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2006-2015 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SERVICES_EDDYSTONEBEACON_H_ -#define SERVICES_EDDYSTONEBEACON_H_ - -#include "ble/BLE.h" -#include "mbed.h" -#include "CircularBuffer.h" -static const uint8_t BEACON_EDDYSTONE[] = {0xAA, 0xFE}; - -//Debug is disabled by default -#if 0 -#define DBG(MSG, ...) printf("[EddyStone: DBG]" MSG " \t[%s,%d]\r\n", \ - ## __VA_ARGS__, \ - __FILE__, \ - __LINE__); -#define WARN(MSG, ...) printf("[EddyStone: WARN]" MSG " \t[%s,%d]\r\n", \ - ## __VA_ARGS__, \ - __FILE__, \ - __LINE__); -#define ERR(MSG, ...) printf("[EddyStone: ERR]" MSG " \t[%s,%d]\r\n", \ - ## __VA_ARGS__, \ - __FILE__, \ - __LINE__); -#else // if 0 -#define DBG(x, ...) //wait_us(10); -#define WARN(x, ...) //wait_us(10); -#define ERR(x, ...) -#endif // if 0 - -#if 0 -#define INFO(x, ...) printf("[EddyStone: INFO]"x " \t[%s,%d]\r\n", \ - ## __VA_ARGS__, \ - __FILE__, \ - __LINE__); -#else // if 0 -#define INFO(x, ...) -#endif // if 0 - -/** -* @class Eddystone -* @brief Eddystone Configuration Service. Can be used to set URL, adjust power levels, and set flags. -* See https://github.com/google/eddystone -* -*/ -class EddystoneService -{ -public: - enum FrameTypes { - NONE, - url, - uid, - tlm - }; - - static const int SERVICE_DATA_MAX = 31; // Maximum size of service data in ADV packets - - // There are currently 3 subframes defined, URI, UID, and TLM -#define EDDYSTONE_MAX_FRAMETYPE 3 - void (*frames[EDDYSTONE_MAX_FRAMETYPE])(uint8_t *, uint32_t); - static const int URI_DATA_MAX = 18; - typedef uint8_t UriData_t[URI_DATA_MAX]; - CircularBuffer<FrameTypes, EDDYSTONE_MAX_FRAMETYPE> overflow; - - // UID Frame Type subfields - static const int UID_NAMESPACEID_SIZE = 10; - typedef uint8_t UIDNamespaceID_t[UID_NAMESPACEID_SIZE]; - static const int UID_INSTANCEID_SIZE = 6; - typedef uint8_t UIDInstanceID_t[UID_INSTANCEID_SIZE]; - - // Eddystone Frame Type ID - static const uint8_t FRAME_TYPE_UID = 0x00; - static const uint8_t FRAME_TYPE_URL = 0x10; - static const uint8_t FRAME_TYPE_TLM = 0x20; - - static const uint8_t FRAME_SIZE_TLM = 14; // TLM frame is a constant 14Bytes - static const uint8_t FRAME_SIZE_UID = 20; // includes RFU bytes - - /** - * Set Eddystone UID Frame information. - * - * @param[in] power TX Power in dB measured at 0 meters from the device. Range of -100 to +20 dB. - * @param[in] namespaceID 10B namespace ID - * @param[in] instanceID 6B instance ID - * @param[in] RFU 2B of RFU, initialized to 0x0000 and not broadcast, included for future reference. - */ - void setUIDFrameData(int8_t power, - UIDNamespaceID_t namespaceID, - UIDInstanceID_t instanceID, - float uidAdvPeriodIn, - uint16_t RFU = 0x0000) { - if (0.0f == uidAdvPeriodIn) { - uidIsSet = false; - return; - } - if (power > 20) { - power = 20; - } - if (power < -100) { - power = -100; - } - - defaultUidPower = power; - memcpy(defaultUidNamespaceID, namespaceID, UID_NAMESPACEID_SIZE); - memcpy(defaultUidInstanceID, instanceID, UID_INSTANCEID_SIZE); - uidRFU = (uint16_t)RFU; // this is probably bad form, but it doesn't really matter yet. - uidAdvPeriod = uidAdvPeriodIn; - uidIsSet = true; // set toggle to advertise UID frames - } - - /* - * Construct UID frame from private variables - * @param[in/out] Data pointer to array to store constructed frame in - * @param[in] maxSize number of bytes left in array, effectively how much empty space is available to write to - * @return number of bytes used. negative number indicates error message. - */ - unsigned constructUIDFrame(uint8_t *Data, uint8_t maxSize) { - unsigned index = 0; - - Data[index++] = FRAME_TYPE_UID; // 1B Type - - if (defaultUidPower > 20) { - defaultUidPower = 20; // enforce range of vaild values. - } - if (defaultUidPower < -100) { - defaultUidPower = -100; - } - Data[index++] = defaultUidPower; // 1B Power @ 0meter - - DBG("UID NamespaceID = '0x"); - for (size_t x = 0; x < UID_NAMESPACEID_SIZE; x++) { // 10B Namespace ID - Data[index++] = defaultUidNamespaceID[x]; - DBG("%x,", defaultUidNamespaceID[x]); - } - DBG("'\r\n"); - - DBG("UID InstanceID = '0x"); - for (size_t x = 0; x< UID_INSTANCEID_SIZE; x++) { // 6B Instance ID - Data[index++] = defaultUidInstanceID[x]; - DBG("%x,", defaultUidInstanceID[x]); - } - DBG("'\r\n"); - - if (0 != uidRFU) { // 2B RFU, include if non-zero, otherwise ignore - Data[index++] = (uint8_t)(uidRFU >> 0); - Data[index++] = (uint8_t)(uidRFU >> 8); - } - DBG("construcUIDFrame %d, %d", maxSize, index); - return index; - } - - /** - * Set Eddystone URL Frame information. - * @param[in] power TX Power in dB measured at 0 meters from the device. - * @param[in] url URL to encode - * @param[in] urlAdvPeriodIn How long to advertise the URL frame (measured in # of adv periods) - * @return false on success, true on failure. - */ - bool setURLFrameData(int8_t power, const char *urlIn, float urlAdvPeriodIn) { - if (0.0f == urlAdvPeriodIn) { - urlIsSet = false; - return false; - } - encodeURL(urlIn, defaultUriData, defaultUriDataLength); // encode URL to URL Formatting - if (defaultUriDataLength > URI_DATA_MAX) { - return true; // error, URL is too big - } - defaultUrlPower = power; - urlAdvPeriod = urlAdvPeriodIn; - urlIsSet = true; - return false; - } - - /** - * Set Eddystone URL Frame information. - * @param[in] power TX Power in dB measured at 0 meters from the device. - * @param[in] encodedUrlIn Encoded URL - * @param[in] encodedUrlInLength Length of the encoded URL - * @param[in] urlAdvPeriodIn How long to advertise the URL frame (measured in # of adv periods) - * @return false on success, true on failure. - */ - bool setURLFrameEncodedData(int8_t power, const char *encodedUrlIn, uint8_t encodedUrlInLength, float urlAdvPeriodIn) { - if (0.0f == urlAdvPeriodIn) { - urlIsSet = false; - return false; - } - memcpy(defaultUriData, encodedUrlIn, encodedUrlInLength); - if (defaultUriDataLength > URI_DATA_MAX) { - return true; // error, URL is too big - } - defaultUrlPower = power; - defaultUriDataLength = encodedUrlInLength; - urlAdvPeriod = urlAdvPeriodIn; - urlIsSet = true; - return false; - } - - /* - * Construct URL frame from private variables - * @param[in/out] Data pointer to array to store constructed frame in - * @param[in] maxSize number of bytes left in array, effectively how much emtpy space is available to write to - * @return number of bytes used. negative number indicates error message. - */ - int constructURLFrame(uint8_t *Data, uint8_t maxSize) { - int index = 0; - Data[index++] = FRAME_TYPE_URL; // 1B Type - Data[index++] = defaultUrlPower; // 1B TX Power - for (int x = 0; x < defaultUriDataLength; x++) { // 18B of URL Prefix + encoded URL - Data[index++] = defaultUriData[x]; - } - DBG("constructURLFrame: %d, %d", maxSize, index); - return index; - } - - /* - * Set Eddystone TLM Frame information. - * @param[in] Version of the TLM beacon data format - * @param[in] advPeriod how often to advertise the TLM frame for (in minutes) - * @param batteryVoltage in milivolts - * @param beaconTemp in 8.8 floating point notation - * - */ - void setTLMFrameData(uint8_t version = 0, - float advPeriod = 60.0f, - uint16_t batteryVoltage = 0, - uint16_t beaconTemp = 0x8000, - uint32_t pduCount = 0, - uint32_t timeSinceBoot = 0) { - if (0.0f == advPeriod) { - tlmIsSet = false; - return; - } - TlmVersion = version; - TlmBatteryVoltage = batteryVoltage; - TlmBeaconTemp = beaconTemp; - TlmPduCount = pduCount; // reset - TlmTimeSinceBoot = timeSinceBoot; // reset - TlmAdvPeriod = advPeriod; - tlmIsSet = true; // TLM Data has been enabled - } - - /* - * Construct TLM frame from private variables - * @param[in/out] Data pointer to array to store constructed frame in - * @param[in] maxSize number of bytes left in array, effectively how much emtpy space is available to write to - * @return number of bytes used. negative number indicates error message. - */ - int constructTLMFrame(uint8_t *Data, uint8_t maxSize) { - uint32_t now = timeSinceBootTimer.read_ms(); - TlmTimeSinceBoot += (now - lastBootTimerRead) / 100; - lastBootTimerRead = now; - - int index = 0; - Data[index++] = FRAME_TYPE_TLM; // Eddystone frame type = Telemetry - Data[index++] = TlmVersion; // TLM Version Number - Data[index++] = (uint8_t)(TlmBatteryVoltage >> 8); // Battery Voltage[0] - Data[index++] = (uint8_t)(TlmBatteryVoltage >> 0); // Battery Voltage[1] - Data[index++] = (uint8_t)(TlmBeaconTemp >> 8); // Beacon Temp[0] - Data[index++] = (uint8_t)(TlmBeaconTemp >> 0); // Beacon Temp[1] - Data[index++] = (uint8_t)(TlmPduCount >> 24); // PDU Count [0] - Data[index++] = (uint8_t)(TlmPduCount >> 16); // PDU Count [1] - Data[index++] = (uint8_t)(TlmPduCount >> 8); // PDU Count [2] - Data[index++] = (uint8_t)(TlmPduCount >> 0); // PDU Count [3] - Data[index++] = (uint8_t)(TlmTimeSinceBoot >> 24); // Time Since Boot [0] - Data[index++] = (uint8_t)(TlmTimeSinceBoot >> 16); // Time Since Boot [1] - Data[index++] = (uint8_t)(TlmTimeSinceBoot >> 8); // Time Since Boot [2] - Data[index++] = (uint8_t)(TlmTimeSinceBoot >> 0); // Time Since Boot [3] - DBG("constructURLFrame: %d, %d", maxSize, index); - return index; - } - - /* - * Update the TLM frame battery voltage value - * @param[in] voltagemv Voltage to update the TLM field battery voltage with (in mV) - * @return nothing - */ - void updateTlmBatteryVoltage(uint16_t voltagemv) { - TlmBatteryVoltage = voltagemv; - } - - /* - * Update the TLM frame beacon temperature - * @param[in] temp Temperature of beacon (in 8.8fpn) - * @return nothing - */ - void updateTlmBeaconTemp(uint16_t temp) { - TlmBeaconTemp = temp; - } - - /* - * Update the TLM frame PDU Count field - * @param[in] pduCount Number of Advertisiting frames sent since powerup - * @return nothing - */ - void updateTlmPduCount(uint32_t pduCount) { - TlmPduCount = pduCount; - } - - /* - * Update the TLM frame Time since boot in 0.1s incriments - * @param[in] timeSinceBoot Time since boot in 0.1s incriments - * @return nothing - */ - void updateTlmTimeSinceBoot(uint32_t timeSinceBoot) { - TlmTimeSinceBoot = timeSinceBoot; - } - - /* - * Update advertising data - * @return true on success, false on failure - */ - bool updateAdvPacket(uint8_t serviceData[], unsigned serviceDataLen) { - // Fields from the Service - DBG("Updating AdvFrame: %d", serviceDataLen); - - ble.clearAdvertisingPayload(); - ble.setAdvertisingType(GapAdvertisingParams::ADV_NON_CONNECTABLE_UNDIRECTED); - ble.setAdvertisingInterval(100); - ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); - ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, BEACON_EDDYSTONE, sizeof(BEACON_EDDYSTONE)); - ble.accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, serviceData, serviceDataLen); - - - return true; - } - - /* - * State machine for switching out frames. - * This function is called by the radioNotificationCallback when a frame needs to get swapped out. - * This function exists because of time constraints in the radioNotificationCallback, so it is effectively - * broken up into two functions. - */ - void swapOutFrames(FrameTypes frameType) { - uint8_t serviceData[SERVICE_DATA_MAX]; - unsigned serviceDataLen = 0; - //hard code in the eddystone UUID - serviceData[serviceDataLen++] = BEACON_EDDYSTONE[0]; - serviceData[serviceDataLen++] = BEACON_EDDYSTONE[1]; - - // if certain frames are not enabled, then skip them. Worst case TLM is always enabled - switch (frameType) { - case tlm: - // TLM frame - if (tlmIsSet) { - DBG("Swapping in TLM Frame: version=%x, Batt=%d, Temp = %d, PDUCnt = %d, TimeSinceBoot=%d", - TlmVersion, - TlmBatteryVoltage, - TlmBeaconTemp, - TlmPduCount, - TlmTimeSinceBoot); - serviceDataLen += constructTLMFrame(serviceData + serviceDataLen, 20); - DBG("\t Swapping in TLM Frame: len=%d", serviceDataLen); - updateAdvPacket(serviceData, serviceDataLen); - } - break; - case url: - // URL Frame - if (urlIsSet) { - DBG("Swapping in URL Frame: Power: %d", defaultUrlPower); - serviceDataLen += constructURLFrame(serviceData + serviceDataLen, 20); - DBG("\t Swapping in URL Frame: len=%d ", serviceDataLen); - updateAdvPacket(serviceData, serviceDataLen); - //switchFlag = false; - } - break; - case uid: - // UID Frame - if (uidIsSet) { - DBG("Swapping in UID Frame: Power: %d", defaultUidPower); - serviceDataLen += constructUIDFrame(serviceData + serviceDataLen, 20); - DBG("\t Swapping in UID Frame: len=%d", serviceDataLen); - updateAdvPacket(serviceData, serviceDataLen); - //switchFlag = false; - } - break; - default: - ERR("You have not initialized a Frame yet, please initialize one before starting a beacon"); - ERR("uidIsSet = %d, urlIsSet = %d, tlmIsSet = %d", uidIsSet, urlIsSet, tlmIsSet); - } - } - - /* - * Callback to swap in URL frame - */ - void urlCallback(void) { - DBG("urlCallback"); - if (false == advLock) { - advLock = true; - DBG("advLock = url") - frameIndex = url; - swapOutFrames(frameIndex); - ble.startAdvertising(); - } else { - // Someone else is broadcasting, toss it into the overflow buffer to retransmit when free - INFO("URI(%d) cannot complete, %d is currently broadcasting", url, frameIndex); - FrameTypes x = url; - overflow.push(x); - } - } - - /* - * Callback to swap in UID frame - */ - void uidCallback(void) { - DBG("uidCallback"); - if (false == advLock) { - advLock = true; - DBG("advLock = uid") - frameIndex = uid; - swapOutFrames(frameIndex); - ble.startAdvertising(); - } else { - // Someone else is broadcasting, toss it into the overflow buffer to retransmit when free - INFO("UID(%d) cannot complete, %d is currently broadcasting", uid, frameIndex); - FrameTypes x = uid; // have to do this to satisfy cont vs volatile keywords... sigh... - overflow.push(x); - } - } - - /* - * Callback to swap in TLM frame - */ - void tlmCallback(void) { - DBG("tlmCallback"); - if (false == advLock) { - // OK to broadcast - advLock = true; - DBG("advLock = tlm") - frameIndex = tlm; - swapOutFrames(frameIndex); - ble.startAdvertising(); - } else { - // Someone else is broadcasting, toss it into the overflow buffer to retransmit when free - INFO("TLM(%d) cannot complete, %d is currently broadcasting", tlm, frameIndex); - FrameTypes x = tlm; - overflow.push(x); - } - } - - void stopAdvCallback(void) { - if (overflow.empty()) { - // if nothing left to transmit, stop - ble.stopAdvertising(); - advLock = false; // unlock lock - } else { - // transmit other packets at current time index - FrameTypes x = NONE; - overflow.pop(x); - INFO("Re-Transmitting %d", x); - swapOutFrames(x); - } - } - - /* - * Callback from onRadioNotification(), used to update the PDUCounter and process next state. - */ -#define EDDYSTONE_SWAPFRAME_DELAYMS 1 - void radioNotificationCallback(bool radioActive) { - // Update PDUCount - TlmPduCount++; - // True just before an frame is sent, false just after a frame is sent - if (radioActive) { - // Do Nothing - } else { - // Packet has been sent, disable advertising - stopAdv.attach_us(this, &EddystoneService::stopAdvCallback, 1); - } - } - - /* - * This function explicityly sets the parameters used by the Eddystone beacon. - * this function should be used in leu of the config service. - * - * @param bleIn ble object used to broadcast eddystone information - * @param beaconPeriodus is how often ble broadcasts are mde, in mili seconds - * @param txPowerLevel sets the broadcasting power level. - * - */ - EddystoneService(BLEDevice &bleIn, - uint16_t beaconPeriodus = 100, - uint8_t txPowerIn = 0) : - ble(bleIn), - advPeriodus(beaconPeriodus), - txPower(txPowerIn), - advLock(false), - frameIndex(NONE) { - } - - /* - * @breif this function starts eddystone advertising based on configured frames. - */ - void start(void) { - // Initialize Frame transition, start with URL to pass eddystone validator app on first try - if (urlIsSet) { - frameIndex = url; - urlTicker.attach(this, &EddystoneService::urlCallback, (float) advPeriodus / 1000.0f); - DBG("attached urlCallback every %d seconds", urlAdvPeriod); - } - if (uidIsSet) { - frameIndex = uid; - uidTicker.attach(this, &EddystoneService::uidCallback, uidAdvPeriod); - DBG("attached uidCallback every %d seconds", uidAdvPeriod); - } - if (tlmIsSet) { - frameIndex = tlm; - // Make double sure the PDUCount and TimeSinceBoot fields are set to zero at reset - updateTlmPduCount(0); - updateTlmTimeSinceBoot(0); - lastBootTimerRead = 0; - timeSinceBootTimer.start(); - tlmTicker.attach(this, &EddystoneService::tlmCallback, TlmAdvPeriod); - DBG("attached tlmCallback every %d seconds", TlmAdvPeriod); - } - if (NONE == frameIndex) { - error("No Frames were Initialized! Please initialize a frame before starting an eddystone beacon."); - } - //uidRFU = 0; - - ble.setTxPower(txPower); - ble.gap().onRadioNotification(this, &EddystoneService::radioNotificationCallback); - } - -private: - - // Eddystone Variables - BLEDevice &ble; - uint16_t advPeriodus; - uint8_t txPower; - Timer timeSinceBootTimer; - volatile uint32_t lastBootTimerRead; - volatile bool advLock; - volatile FrameTypes frameIndex; - Timeout stopAdv; - - - // URI Frame Variables - uint8_t defaultUriDataLength; - UriData_t defaultUriData; - int8_t defaultUrlPower; - bool urlIsSet; // flag that enables / disable URI Frames - float urlAdvPeriod; // how long the url frame will be advertised for - Ticker urlTicker; - - // UID Frame Variables - UIDNamespaceID_t defaultUidNamespaceID; - UIDInstanceID_t defaultUidInstanceID; - int8_t defaultUidPower; - uint16_t uidRFU; - bool uidIsSet; // flag that enables / disable UID Frames - float uidAdvPeriod; // how long the uid frame will be advertised for - Ticker uidTicker; - - // TLM Frame Variables - uint8_t TlmVersion; - volatile uint16_t TlmBatteryVoltage; - volatile uint16_t TlmBeaconTemp; - volatile uint32_t TlmPduCount; - volatile uint32_t TlmTimeSinceBoot; - bool tlmIsSet; // flag that enables / disables TLM frames - float TlmAdvPeriod; // number of minutes between adv frames - Ticker tlmTicker; - -public: - /* - * Encode a human-readable URI into the binary format defined by URIBeacon spec (https://github.com/google/uribeacon/tree/master/specification). - */ - static void encodeURL(const char *uriDataIn, UriData_t uriDataOut, uint8_t &sizeofURIDataOut) { - DBG("Encode URL = %s", uriDataIn); - const char *prefixes[] = { - "http://www.", - "https://www.", - "http://", - "https://", - }; - const size_t NUM_PREFIXES = sizeof(prefixes) / sizeof(char *); - const char *suffixes[] = { - ".com/", - ".org/", - ".edu/", - ".net/", - ".info/", - ".biz/", - ".gov/", - ".com", - ".org", - ".edu", - ".net", - ".info", - ".biz", - ".gov" - }; - const size_t NUM_SUFFIXES = sizeof(suffixes) / sizeof(char *); - - sizeofURIDataOut = 0; - memset(uriDataOut, 0, sizeof(UriData_t)); - - if ((uriDataIn == NULL) || (strlen(uriDataIn) == 0)) { - return; - } - - /* - * handle prefix - */ - 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; - break; - } - } - - /* - * handle suffixes - */ - while (*uriDataIn && (sizeofURIDataOut < URI_DATA_MAX)) { - /* check for suffix match */ - unsigned i; - for (i = 0; i < NUM_SUFFIXES; i++) { - size_t suffixLen = strlen(suffixes[i]); - if (strncmp(uriDataIn, suffixes[i], suffixLen) == 0) { - uriDataOut[sizeofURIDataOut++] = i; - uriDataIn += 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. */ - INFO("Encoding URI: No Suffix Found"); - if (i == NUM_SUFFIXES) { - uriDataOut[sizeofURIDataOut++] = *uriDataIn; - ++uriDataIn; - } - } - } -}; - -#endif // SERVICES_EDDYSTONEBEACON_H_ \ No newline at end of file
--- a/source/DiscoveredCharacteristic.cpp Thu Nov 26 12:52:35 2015 +0000 +++ b/source/DiscoveredCharacteristic.cpp Thu Nov 26 12:52:35 2015 +0000 @@ -31,52 +31,6 @@ return gattc->read(connHandle, valueHandle, offset); } -struct OneShotReadCallback { - static void launch(GattClient* client, Gap::Handle_t connHandle, - GattAttribute::Handle_t handle, const GattClient::ReadCallback_t& cb) { - OneShotReadCallback* oneShot = new OneShotReadCallback(client, connHandle, handle, cb); - oneShot->attach(); - // delete will be made when this callback is called - } - -private: - OneShotReadCallback(GattClient* client, Gap::Handle_t connHandle, - GattAttribute::Handle_t handle, const GattClient::ReadCallback_t& cb) : - _client(client), - _connHandle(connHandle), - _handle(handle), - _callback(cb) { } - - void attach() { - _client->onDataRead(makeFunctionPointer(this, &OneShotReadCallback::call)); - } - - void call(const GattReadCallbackParams* params) { - // verifiy that it is the right characteristic on the right connection - if (params->connHandle == _connHandle && params->handle == _handle) { - _callback(params); - _client->onDataRead().detach(makeFunctionPointer(this, &OneShotReadCallback::call)); - delete this; - } - } - - GattClient* _client; - Gap::Handle_t _connHandle; - GattAttribute::Handle_t _handle; - GattClient::ReadCallback_t _callback; -}; - -ble_error_t DiscoveredCharacteristic::read(uint16_t offset, const GattClient::ReadCallback_t& onRead) const { - ble_error_t error = read(offset); - if (error) { - return error; - } - - OneShotReadCallback::launch(gattc, connHandle, valueHandle, onRead); - - return error; -} - ble_error_t DiscoveredCharacteristic::write(uint16_t length, const uint8_t *value) const { @@ -105,52 +59,6 @@ return gattc->write(GattClient::GATT_OP_WRITE_CMD, connHandle, valueHandle, length, value); } -struct OneShotWriteCallback { - static void launch(GattClient* client, Gap::Handle_t connHandle, - GattAttribute::Handle_t handle, const GattClient::WriteCallback_t& cb) { - OneShotWriteCallback* oneShot = new OneShotWriteCallback(client, connHandle, handle, cb); - oneShot->attach(); - // delete will be made when this callback is called - } - -private: - OneShotWriteCallback(GattClient* client, Gap::Handle_t connHandle, - GattAttribute::Handle_t handle, const GattClient::WriteCallback_t& cb) : - _client(client), - _connHandle(connHandle), - _handle(handle), - _callback(cb) { } - - void attach() { - _client->onDataWritten(makeFunctionPointer(this, &OneShotWriteCallback::call)); - } - - void call(const GattWriteCallbackParams* params) { - // verifiy that it is the right characteristic on the right connection - if (params->connHandle == _connHandle && params->handle == _handle) { - _callback(params); - _client->onDataWritten().detach(makeFunctionPointer(this, &OneShotWriteCallback::call)); - delete this; - } - } - - GattClient* _client; - Gap::Handle_t _connHandle; - GattAttribute::Handle_t _handle; - GattClient::WriteCallback_t _callback; -}; - -ble_error_t DiscoveredCharacteristic::write(uint16_t length, const uint8_t *value, const GattClient::WriteCallback_t& onRead) const { - ble_error_t error = write(length, value); - if (error) { - return error; - } - - OneShotWriteCallback::launch(gattc, connHandle, valueHandle, onRead); - - return error; -} - ble_error_t DiscoveredCharacteristic::discoverDescriptors(DescriptorCallback_t callback, const UUID &matchingUUID) const {