High level Bluetooth Low Energy API and radio abstraction layer
Fork of BLE_API by
Revision 118:620d28e7a1ba, committed 2014-09-22
- Comitter:
- Rohit Grover
- Date:
- Mon Sep 22 10:59:09 2014 +0100
- Parent:
- 117:0fb20195102b
- Child:
- 119:18684018b83e
- Commit message:
- Release 0.2.0
=============
Highlights:
Introducing standard services to simplify applications.
Add support for over-the-air firmware updates.
Features
~~~~~~~~
- This release introduces 'templates' for common services such as heart-rate,
battery-level, device-info, UART, device-firmware-update etc. These services
take the shape of class declarations within header files aggregated under a
new folder called 'services/'. These service-classes provide a high-level
API hopefully easing the burden of developing BLE applications. The
underlying APIs to work with characteristics and services are still
available to allow greater control if needed. We expect to grow the
supported services to include all SIG defined BLE profiles.
- WriteCallbackParams now includes the characteristic's value-attribute
handle; this changes the signature of onDataWritten().
- BLEDevice::onDataWritten() now allows chaining of callbacks--this means that
it is possible to chain together multiple onDataWritten callbacks
(potentially from different modules of an application) to receive updates to
characteristics. Many services, such as DFU and UART add their own
onDataWritten callbacks behind the scenes to trap interesting events. It is
also possible to chain callbacks to functions within objects.
- Added the following expectation for GattCharacteristic: If valuePtr ==
NULL, initialLength == 0, and properties == READ for the value attribute of
a characteristic, then that particular characteristic may be considered
optional and dropped while instantiating the service with the underlying BLE
stack.
- Introducing the typedef GattAttribute::Handle_t to capture Attribute handles.
Bugfixes
~~~~~~~~
None.
Compatibility
~~~~~~~~~~~~~
The signature of onDataWritten() has seen a change; so application programs
using this new version of the BLE API will need minor modifications. Please
refer to sample programs under BLE team page.
Changed in this revision
--- a/public/BLEDevice.h Mon Sep 08 17:11:58 2014 +0100 +++ b/public/BLEDevice.h Mon Sep 22 10:59:09 2014 +0100 @@ -213,8 +213,18 @@ /** * Setup a callback for when a characteristic has its value updated by a * client. + * + * @Note: it is possible to chain together multiple onDataWritten callbacks + * (potentially from different modules of an application) to receive updates + * to characteristics. Many services, such as DFU and UART add their own + * onDataWritten callbacks behind the scenes to trap interesting events. + * + * @Note: it is also possible to setup a callback into a member function of + * some object. */ - void onDataWritten(GattServer::WriteEventCallback_t callback); + void onDataWritten(void (*callback)(const GattCharacteristicWriteCBParams *eventDataP)); + template <typename T> void onDataWritten(T *objPtr, void (T::*memberPtr)(const GattCharacteristicWriteCBParams *context)); + void onUpdatesEnabled(GattServer::EventCallback_t callback); void onUpdatesDisabled(GattServer::EventCallback_t callback); void onConfirmationReceived(GattServer::EventCallback_t callback); @@ -227,7 +237,17 @@ Gap::GapState_t getGapState(void) const; + /** + * @param[in/out] lengthP + * input: Length in bytes to be read, + * output: Total length of attribute value upon successful return. + */ ble_error_t readCharacteristicValue(uint16_t handle, uint8_t *const buffer, uint16_t *const lengthP); + + /** + * @param localOnly + * Only update the characteristic locally regardless of notify/indicate flags in the CCCD. + */ ble_error_t updateCharacteristicValue(uint16_t handle, const uint8_t* value, uint16_t size, bool localOnly = false); /** @@ -473,11 +493,16 @@ } inline void -BLEDevice::onDataWritten(GattServer::WriteEventCallback_t callback) -{ +BLEDevice::onDataWritten(void (*callback)(const GattCharacteristicWriteCBParams *eventDataP)) { transport->getGattServer().setOnDataWritten(callback); } +template <typename T> inline void +BLEDevice::onDataWritten(T *objPtr, void (T::*memberPtr)(const GattCharacteristicWriteCBParams *context)) { + transport->getGattServer().setOnDataWritten(objPtr, memberPtr); +} + + inline void BLEDevice::onUpdatesEnabled(GattServer::EventCallback_t callback) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/public/CallChainOfFunctionPointersWithContext.h Mon Sep 22 10:59:09 2014 +0100 @@ -0,0 +1,153 @@ +/* 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 MBED_CALLCHAIN_OF_FUNCTION_POINTERS_WITH_CONTEXT_H +#define MBED_CALLCHAIN_OF_FUNCTION_POINTERS_WITH_CONTEXT_H + +#include <string.h> +#include "FunctionPointerWithContext.h" + +namespace mbed { + +/** Group one or more functions in an instance of a CallChainOfFunctionPointersWithContext, then call them in + * sequence using CallChainOfFunctionPointersWithContext::call(). Used mostly by the interrupt chaining code, + * but can be used for other purposes. + * + * Example: + * @code + * #include "mbed.h" + * + * CallChainOfFunctionPointersWithContext<void *> chain; + * + * void first(void *context) { + * printf("'first' function.\n"); + * } + * + * void second(void *context) { + * printf("'second' function.\n"); + * } + * + * class Test { + * public: + * void f(void *context) { + * printf("A::f (class member).\n"); + * } + * }; + * + * int main() { + * Test test; + * + * chain.add(second); + * chain.add_front(first); + * chain.add(&test, &Test::f); + * chain.call(); + * } + * @endcode + */ + +template <typename ContextType> +class CallChainOfFunctionPointersWithContext { +public: + typedef FunctionPointerWithContext<ContextType>* pFunctionPointerWithContext_t; + +public: + /** Create an empty chain + * + * @param size (optional) Initial size of the chain + */ + CallChainOfFunctionPointersWithContext() : chainHead(NULL) { + /* empty */ + } + + virtual ~CallChainOfFunctionPointersWithContext() { + clear(); + } + + /** Add a function at the front of the chain + * + * @param function A pointer to a void function + * + * @returns + * The function object created for 'function' + */ + pFunctionPointerWithContext_t add(void (*function)(ContextType context)) { + return common_add(new FunctionPointerWithContext<ContextType>(function)); + } + + /** Add a function at the front of the chain + * + * @param tptr pointer to the object to call the member function on + * @param mptr pointer to the member function to be called + * + * @returns + * The function object created for 'tptr' and 'mptr' + */ + template<typename T> + pFunctionPointerWithContext_t add(T *tptr, void (T::*mptr)(ContextType context)) { + return common_add(new FunctionPointerWithContext<ContextType>(tptr, mptr)); + } + + /** Clear the call chain (remove all functions in the chain). + */ + void clear(void) { + pFunctionPointerWithContext_t fptr = chainHead; + while (fptr) { + pFunctionPointerWithContext_t deadPtr = fptr; + fptr = deadPtr->getNext(); + delete deadPtr; + } + + chainHead = NULL; + } + + bool hasCallbacksAttached(void) const { + return (chainHead != NULL); + } + + /** 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) { + if (chainHead) + chainHead->call(context); + } + +private: + pFunctionPointerWithContext_t common_add(pFunctionPointerWithContext_t pf) { + if (chainHead == NULL) { + chainHead = pf; + } else { + pf->chainAsNext(chainHead); + chainHead = pf; + } + + return chainHead; + } + +private: + pFunctionPointerWithContext_t chainHead; + + /* disallow copy constructor and assignment operators */ +private: + CallChainOfFunctionPointersWithContext(const CallChainOfFunctionPointersWithContext &); + CallChainOfFunctionPointersWithContext & operator = (const CallChainOfFunctionPointersWithContext &); +}; + +} // namespace mbed + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/public/FunctionPointerWithContext.h Mon Sep 22 10:59:09 2014 +0100 @@ -0,0 +1,131 @@ +/* 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 MBED_FUNCTIONPOINTER_WITH_CONTEXT_H +#define MBED_FUNCTIONPOINTER_WITH_CONTEXT_H + +#include <string.h> + +namespace mbed { + +/** A class for storing and calling a pointer to a static or member void function + * which takes a context. + */ +template <typename ContextType> +class FunctionPointerWithContext { +public: + typedef FunctionPointerWithContext<ContextType> *pFunctionPointerWithContext_t; + typedef void (*pvoidfcontext_t)(ContextType context); + + /** Create a FunctionPointerWithContext, attaching a static function + * + * @param function The void static function to attach (default is none) + */ + FunctionPointerWithContext(void (*function)(ContextType context) = NULL) : + _function(NULL), _object(NULL), _member(), _membercaller(NULL), _next(NULL) { + attach(function); + } + + /** Create a FunctionPointerWithContext, attaching a member function + * + * @param object The object pointer to invoke the member function on (i.e. the this pointer) + * @param function The address of the void member function to attach + */ + template<typename T> + FunctionPointerWithContext(T *object, void (T::*member)(ContextType context)) : + _function(NULL), _object(NULL), _member(), _membercaller(NULL), _next(NULL) { + attach(object, member); + } + + /** Attach a static function + * + * @param function The void static function to attach (default is none) + */ + void attach(void (*function)(ContextType context) = NULL) { + _function = function; + } + + /** Attach a member function + * + * @param object The object pointer to invoke the member function on (i.e. the this pointer) + * @param function The address of the void member function to attach + */ + template<typename T> + void attach(T *object, void (T::*member)(ContextType context)) { + _object = static_cast<void *>(object); + memcpy(_member, (char *)&member, sizeof(member)); + _membercaller = &FunctionPointerWithContext::membercaller<T>; + } + + /** Call the attached static or member function; and if there are chained + * 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) { + if (_function) { + _function(context); + } else if (_object && _membercaller) { + _membercaller(_object, _member, context); + } + + /* Propagate the call to next in the chain. */ + if (_next) { + _next->call(context); + } + } + + /** + * Setup an external FunctionPointer as a next in the chain of related + * callbacks. Invoking call() on the head FunctionPointer will invoke all + * chained callbacks. + * + * Refer to 'CallChain' as an alternative. + */ + void chainAsNext(pFunctionPointerWithContext_t next) { + _next = next; + } + + pFunctionPointerWithContext_t getNext(void) const { + return _next; + } + + pvoidfcontext_t get_function() const { + return (pvoidfcontext_t)_function; + } + +private: + template<typename T> + static void membercaller(void *object, char *member, ContextType context) { + T *o = static_cast<T *>(object); + void (T::*m)(ContextType); + memcpy((char *)&m, member, sizeof(m)); + (o->*m)(context); + } + + void (*_function)(ContextType context); /**< static function pointer - NULL if none attached */ + void *_object; /**< object this pointer - NULL if none attached */ + char _member[16]; /**< raw member function pointer storage - converted back by + * registered _membercaller */ + void (*_membercaller)(void *, char *, ContextType); /**< registered membercaller function to convert back and call + * _member on _object passing the context. */ + pFunctionPointerWithContext_t _next; /**< Optional link to make a chain out of functionPointers; this + * allows chaining function pointers without requiring + * external memory to manage the chain. Also refer to + * 'CallChain' as an alternative. */ +}; +} // namespace mbed + +#endif // ifndef MBED_FUNCTIONPOINTER_WITH_CONTEXT_H
--- a/public/GattAttribute.h Mon Sep 08 17:11:58 2014 +0100 +++ b/public/GattAttribute.h Mon Sep 22 10:59:09 2014 +0100 @@ -29,7 +29,9 @@ class GattAttribute { public: + typedef uint16_t Handle_t; +public: /** * @brief Creates a new GattAttribute using the specified * UUID, value length, and inital value @@ -59,21 +61,26 @@ } public: - uint16_t getHandle(void) const { + Handle_t getHandle(void) const { return _handle; } - void setHandle(uint16_t id) { + + void setHandle(Handle_t id) { _handle = id; } + const UUID &getUUID(void) const { return _uuid; } + uint16_t getInitialLength(void) const { return _initialLen; } + uint16_t getMaxLength(void) const { return _lenMax; } + uint8_t *getValuePtr(void) { return _valuePtr; } @@ -83,7 +90,7 @@ uint8_t *_valuePtr; uint16_t _initialLen; /* Initial length of the value */ uint16_t _lenMax; /* Maximum length of the value */ - uint16_t _handle; + Handle_t _handle; }; #endif // ifndef __GATT_ATTRIBUTE_H__
--- a/public/GattCharacteristic.h Mon Sep 08 17:11:58 2014 +0100 +++ b/public/GattCharacteristic.h Mon Sep 22 10:59:09 2014 +0100 @@ -306,7 +306,9 @@ * @param[in] uuid * The UUID to use for this characteristic * @param[in] valuePtr - * The memory holding the initial value. + * The memory holding the initial value. The value is copied + * into the stack when the enclosing service is added; and + * thereafter maintained internally by the stack. * @param[in] initialLen * The min length in bytes of this characteristic's value * @param[in] maxLen @@ -317,13 +319,17 @@ * A pointer to an array of descriptors to be included within this characteristic * @param[in] numDescriptors * The number of descriptors + * + * @NOTE: If valuePtr == NULL, initialLength == 0, and properties == READ + * for the value attribute of a characteristic, then that particular + * characteristic may be considered optional and dropped while + * instantiating the service with the underlying BLE stack. */ /**************************************************************************/ GattCharacteristic(const UUID &uuid, uint8_t *valuePtr = NULL, uint16_t initialLen = 0, uint16_t maxLen = 0, uint8_t props = BLE_GATT_CHAR_PROPERTIES_NONE, GattAttribute *descriptors[] = NULL, unsigned numDescriptors = 0) : _valueAttribute(uuid, valuePtr, initialLen, maxLen), _properties(props), _descriptors(descriptors), _descriptorCount(numDescriptors) { - } public:
--- a/public/GattCharacteristicWriteCBParams.h Mon Sep 08 17:11:58 2014 +0100 +++ b/public/GattCharacteristicWriteCBParams.h Mon Sep 22 10:59:09 2014 +0100 @@ -18,6 +18,7 @@ #define __GATT_CHARACTERISTIC_WRITE_CB_PARAMS_H__ struct GattCharacteristicWriteCBParams { + GattAttribute::Handle_t charHandle; enum Type { GATTS_CHAR_OP_INVALID = 0x00, /**< Invalid Operation. */ GATTS_CHAR_OP_WRITE_REQ = 0x01, /**< Write Request. */
--- a/public/GattServer.h Mon Sep 08 17:11:58 2014 +0100 +++ b/public/GattServer.h Mon Sep 22 10:59:09 2014 +0100 @@ -22,6 +22,7 @@ #include "GattService.h" #include "GattServerEvents.h" #include "GattCharacteristicWriteCBParams.h" +#include "CallChainOfFunctionPointersWithContext.h" /**************************************************************************/ /*! @@ -45,14 +46,17 @@ /* Event callback handlers. */ typedef void (*EventCallback_t)(uint16_t attributeHandle); - typedef void (*WriteEventCallback_t)(uint16_t attributeHandle, const GattCharacteristicWriteCBParams *eventDataP); typedef void (*ServerEventCallback_t)(void); /**< independent of any particular attribute */ typedef void (*ServerEventCallbackWithCount_t)(unsigned count); /**< independent of any particular attribute */ void setOnDataSent(ServerEventCallbackWithCount_t callback) { onDataSent = callback; } - void setOnDataWritten(WriteEventCallback_t callback) { - onDataWritten = callback; + void setOnDataWritten(void (*callback)(const GattCharacteristicWriteCBParams *eventDataP)) { + onDataWritten.add(callback); + } + template <typename T> + void setOnDataWritten(T *objPtr, void (T::*memberPtr)(const GattCharacteristicWriteCBParams *context)) { + onDataWritten.add(objPtr, memberPtr); } void setOnUpdatesEnabled(EventCallback_t callback) { onUpdatesEnabled = callback; @@ -64,9 +68,9 @@ onConfirmationReceived = callback; } - void handleDataWrittenEvent(uint16_t charHandle, const GattCharacteristicWriteCBParams *params) { - if (onDataWritten) { - onDataWritten(charHandle, params); + void handleDataWrittenEvent(const GattCharacteristicWriteCBParams *params) { + if (onDataWritten.hasCallbacksAttached()) { + onDataWritten.call(params); } } @@ -97,7 +101,7 @@ } protected: - GattServer() : serviceCount(0), characteristicCount(0), onDataSent(NULL), onDataWritten(NULL), onUpdatesEnabled(NULL), onUpdatesDisabled(NULL), onConfirmationReceived(NULL) { + GattServer() : serviceCount(0), characteristicCount(0), onDataSent(NULL), onDataWritten(), onUpdatesEnabled(NULL), onUpdatesDisabled(NULL), onConfirmationReceived(NULL) { /* empty */ } @@ -108,7 +112,7 @@ private: ServerEventCallbackWithCount_t onDataSent; - WriteEventCallback_t onDataWritten; + CallChainOfFunctionPointersWithContext<const GattCharacteristicWriteCBParams *> onDataWritten; EventCallback_t onUpdatesEnabled; EventCallback_t onUpdatesDisabled; EventCallback_t onConfirmationReceived;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/services/BatteryService.h Mon Sep 22 10:59:09 2014 +0100 @@ -0,0 +1,61 @@ +/* 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_BATTERY_SERVICE_H__ +#define __BLE_BATTERY_SERVICE_H__ + +#include "BLEDevice.h" + +/* Battery Service */ +/* Service: https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.battery_service.xml */ +/* Battery Level Char: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.battery_level.xml */ +class BatteryService { +public: + BatteryService(BLEDevice &_ble, uint8_t level = 100) : + ble(_ble), + batteryLevel(level), + batteryLevelCharacteristic(GattCharacteristic::UUID_BATTERY_LEVEL_CHAR, &batteryLevel, sizeof(batteryLevel), sizeof(batteryLevel), + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY) { + + static bool serviceAdded = false; /* We should only ever need to add the heart rate service once. */ + if (serviceAdded) { + return; + } + + GattCharacteristic *charTable[] = {&batteryLevelCharacteristic}; + GattService batteryService(GattService::UUID_BATTERY_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *)); + + ble.addService(batteryService); + serviceAdded = true; + } + + /** + * Update the battery level with a new value. Valid values range from + * 0..100. Anything outside this range will be ignored. + * @param newLevel New level. + */ + void updateBatteryLevel(uint8_t newLevel) { + batteryLevel = newLevel; + ble.updateCharacteristicValue(batteryLevelCharacteristic.getValueAttribute().getHandle(), &batteryLevel, 1); + } + +private: + BLEDevice &ble; + uint8_t batteryLevel; + GattCharacteristic batteryLevelCharacteristic; +}; + +#endif /* #ifndef __BLE_BATTERY_SERVICE_H__*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/services/DFUService.cpp Mon Sep 22 10:59:09 2014 +0100 @@ -0,0 +1,35 @@ +/* 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. + */ + +#include "DFUService.h" + +const uint8_t DFUServiceBaseUUID[] = { + 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0xEF, 0xDE, + 0x15, 0x23, 0x78, 0x5F, 0xEA, 0xBC, 0xD1, 0x23, +}; +const uint16_t DFUServiceShortUUID = 0x1530; +const uint16_t DFUServiceControlCharacteristicShortUUID = 0x1531; + +const uint8_t DFUServiceUUID[] = { + 0x00, 0x00, (uint8_t)(DFUServiceShortUUID >> 8), (uint8_t)(DFUServiceShortUUID & 0xFF), 0x12, 0x12, 0xEF, 0xDE, + 0x15, 0x23, 0x78, 0x5F, 0xEA, 0xBC, 0xD1, 0x23, +}; +const uint8_t DFUServiceControlCharacteristicUUID[] = { + 0x00, 0x00, (uint8_t)(DFUServiceControlCharacteristicShortUUID >> 8), (uint8_t)(DFUServiceControlCharacteristicShortUUID & 0xFF), 0x12, 0x12, 0xEF, 0xDE, + 0x15, 0x23, 0x78, 0x5F, 0xEA, 0xBC, 0xD1, 0x23, +}; + +DFUService::ResetPrepare_t DFUService::handoverCallback = NULL;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/services/DFUService.h Mon Sep 22 10:59:09 2014 +0100 @@ -0,0 +1,92 @@ +/* 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_DFU_SERVICE_H__ +#define __BLE_DFU_SERVICE_H__ + +#include "BLEDevice.h" +#include "UUID.h" + +extern "C" void bootloader_start(void); + +extern const uint8_t DFUServiceBaseUUID[]; +extern const uint16_t DFUServiceShortUUID; +extern const uint16_t DFUServiceControlCharacteristicShortUUID; + +extern const uint8_t DFUServiceUUID[]; +extern const uint8_t DFUServiceControlCharacteristicUUID[]; + +class DFUService { +public: + /** + * Signature for the handover callback. The application may provide such a + * callback when setting up the DFU service, in which case it will be + * invoked before handing control over to the Bootloader. + */ + typedef void (*ResetPrepare_t)(void); + +public: + DFUService(BLEDevice &_ble, ResetPrepare_t _handoverCallback = NULL) : + ble(_ble), + controlBytes(), + controlPoint(DFUServiceControlCharacteristicUUID, controlBytes, SIZEOF_CONTROL_BYTES, SIZEOF_CONTROL_BYTES, + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY) { + static bool serviceAdded = false; /* We should only ever need to add the DFU service once. */ + if (serviceAdded) { + return; + } + + GattCharacteristic *dfuChars[] = {&controlPoint}; + GattService dfuService(DFUServiceUUID, dfuChars, sizeof(dfuChars) / sizeof(GattCharacteristic *)); + + ble.addService(dfuService); + handoverCallback = _handoverCallback; + serviceAdded = true; + + ble.onDataWritten(this, &DFUService::onDataWritten); + } + + uint16_t getControlHandle(void) { + return controlPoint.getValueAttribute().getHandle(); + } + + /** + * This callback allows the DFU service to receive the initial trigger to + * handover control to the bootloader; but first the application is given a + * chance to clean up. + */ + virtual void onDataWritten(const GattCharacteristicWriteCBParams *params) { + if (params->charHandle == controlPoint.getValueAttribute().getHandle()) { + if (handoverCallback) { + handoverCallback(); + } + + bootloader_start(); + } + } + +private: + static const unsigned SIZEOF_CONTROL_BYTES = 2; + + static ResetPrepare_t handoverCallback; /**< application specific handover callback. */ + +private: + BLEDevice &ble; + uint8_t controlBytes[SIZEOF_CONTROL_BYTES]; + GattCharacteristic controlPoint; +}; + +#endif /* #ifndef __BLE_DFU_SERVICE_H__*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/services/DeviceInformationService.h Mon Sep 22 10:59:09 2014 +0100 @@ -0,0 +1,124 @@ +/* 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_DEVICE_INFORMATION_SERVICE_H__ +#define __BLE_DEVICE_INFORMATION_SERVICE_H__ + +#include "BLEDevice.h" + +/* Device Information Service */ +/* Service: https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.device_information.xml */ +/* Manufacturer Name String Char: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.manufacturer_name_string.xml */ +class DeviceInformationService { +public: + /** + * Constructor. + * + * @param[in] _ble + * Reference to the BLEDevice. + * @param[in] manufacturersName + * This characteristic represents the name of the + * manufacturer of the device. The name is copied into the + * BLE stack during this constructor. + * @param[in] modelNumber + * This characteristic represents the model number that is + * assigned by the device vendor. The value is copied into + * the BLE stack during this constructor. + * @param[in] serialNumber + * This characteristic represents the serial number for a + * particular instance of the device. The value is copied + * into the BLE stack during this constructor. + * @param[in] hardwareRevision + * This characteristic represents the hardware revision for + * the hardware within the device. The value is copied + * into the BLE stack during this constructor. + * @param[in] firmwareRevision + * This characteristic represents the firmware revision for + * the firmware within the device. The value is copied + * into the BLE stack during this constructor. + * @param[in] softwareRevision + * This characteristic represents the software revision for + * the software within the device. The value is copied + * into the BLE stack during this constructor. + */ + DeviceInformationService(BLEDevice &_ble, + const char *manufacturersName = NULL, + const char *modelNumber = NULL, + const char *serialNumber = NULL, + const char *hardwareRevision = NULL, + const char *firmwareRevision = NULL, + const char *softwareRevision = NULL) : + ble(_ble), + manufacturersNameStringCharacteristic(GattCharacteristic::UUID_MANUFACTURER_NAME_STRING_CHAR, + (uint8_t *)manufacturersName, + (manufacturersName != NULL) ? strlen(manufacturersName) : 0, /* minLength */ + (manufacturersName != NULL) ? strlen(manufacturersName) : 0, /* maxLength */ + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ), + modelNumberStringCharacteristic(GattCharacteristic::UUID_MODEL_NUMBER_STRING_CHAR, + (uint8_t *)modelNumber, + (modelNumber != NULL) ? strlen(modelNumber) : 0, /* minLength */ + (modelNumber != NULL) ? strlen(modelNumber) : 0, /* maxLength */ + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ), + serialNumberStringCharacteristic(GattCharacteristic::UUID_SERIAL_NUMBER_STRING_CHAR, + (uint8_t *)serialNumber, + (serialNumber != NULL) ? strlen(serialNumber) : 0, /* minLength */ + (serialNumber != NULL) ? strlen(serialNumber) : 0, /* maxLength */ + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ), + hardwareRevisionStringCharacteristic(GattCharacteristic::UUID_HARDWARE_REVISION_STRING_CHAR, + (uint8_t *)hardwareRevision, + (hardwareRevision != NULL) ? strlen(hardwareRevision) : 0, /* minLength */ + (hardwareRevision != NULL) ? strlen(hardwareRevision) : 0, /* maxLength */ + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ), + firmwareRevisionStringCharacteristic(GattCharacteristic::UUID_FIRMWARE_REVISION_STRING_CHAR, + (uint8_t *)firmwareRevision, + (firmwareRevision != NULL) ? strlen(firmwareRevision) : 0, /* minLength */ + (firmwareRevision != NULL) ? strlen(firmwareRevision) : 0, /* maxLength */ + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ), + softwareRevisionStringCharacteristic(GattCharacteristic::UUID_SOFTWARE_REVISION_STRING_CHAR, + (uint8_t *)softwareRevision, + (softwareRevision != NULL) ? strlen(softwareRevision) : 0, /* minLength */ + (softwareRevision != NULL) ? strlen(softwareRevision) : 0, /* maxLength */ + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ) + { + static bool serviceAdded = false; /* We should only ever need to add the heart rate service once. */ + if (serviceAdded) { + return; + } + + GattCharacteristic *charTable[] = {&manufacturersNameStringCharacteristic, + &modelNumberStringCharacteristic, + &serialNumberStringCharacteristic, + &hardwareRevisionStringCharacteristic, + &firmwareRevisionStringCharacteristic, + &softwareRevisionStringCharacteristic}; + GattService deviceInformationService(GattService::UUID_DEVICE_INFORMATION_SERVICE, charTable, + sizeof(charTable) / sizeof(GattCharacteristic *)); + + ble.addService(deviceInformationService); + serviceAdded = true; + } + +private: + BLEDevice &ble; + GattCharacteristic manufacturersNameStringCharacteristic; + GattCharacteristic modelNumberStringCharacteristic; + GattCharacteristic serialNumberStringCharacteristic; + GattCharacteristic hardwareRevisionStringCharacteristic; + GattCharacteristic firmwareRevisionStringCharacteristic; + GattCharacteristic softwareRevisionStringCharacteristic; +}; + +#endif /* #ifndef __BLE_DEVICE_INFORMATION_SERVICE_H__*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/services/HealthThermometerService.h Mon Sep 22 10:59:09 2014 +0100 @@ -0,0 +1,131 @@ +/* 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_HEALTH_THERMOMETER_SERVICE_H__ +#define __BLE_HEALTH_THERMOMETER_SERVICE_H__ + +#include "BLEDevice.h" + +/* Health Thermometer Service */ +/* Service: https://developer.bluetooth.org/gatt/profiles/Pages/ProfileViewer.aspx?u=org.bluetooth.profile.health_thermometer.xml */ +/* Temperature Measurement: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.temperature_measurement.xml */ +/* Temperature Type: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.temperature_type.xml */ +class HealthThermometerService { +public: + enum { + LOCATION_ARMPIT = 1, + LOCATION_BODY, + LOCATION_EAR, + LOCATION_FINGER, + LOCATION_GI_TRACT, + LOCATION_MOUTH, + LOCATION_RECTUM, + LOCATION_TOE, + LOCATION_EAR_DRUM, + }; + +public: + + /** + * @param[in] _ble reference to the BLE device + * @param[in] initialTemp initial value in celsius + * @param[in] _location + */ + HealthThermometerService(BLEDevice &_ble, float initialTemp, uint8_t _location) : + ble(_ble), + valueBytes(initialTemp), + tempMeasurement(GattCharacteristic::UUID_TEMPERATURE_MEASUREMENT_CHAR, valueBytes.getPointer(), + sizeof(TemperatureValueBytes), sizeof(TemperatureValueBytes), + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY), + tempLocation(GattCharacteristic::UUID_TEMPERATURE_TYPE_CHAR, (uint8_t *)&_location, sizeof(_location), sizeof(_location), + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ) { + + GattCharacteristic *hrmChars[] = {&tempMeasurement, &tempLocation, }; + GattService hrmService(GattService::UUID_HEALTH_THERMOMETER_SERVICE, hrmChars, sizeof(hrmChars) / sizeof(GattCharacteristic *)); + + ble.addService(hrmService); + } + + void updateTemperature(float temperature) { + if (ble.getGapState().connected) { + valueBytes.updateTemperature(temperature); + ble.updateCharacteristicValue(tempMeasurement.getValueAttribute().getHandle(), valueBytes.getPointer(), sizeof(TemperatureValueBytes)); + } + } + +private: + /* Private internal representation for the bytes used to work with the vaulue of the heart-rate characteristic. */ + struct TemperatureValueBytes { + static const unsigned OFFSET_OF_FLAGS = 0; + static const unsigned OFFSET_OF_VALUE = OFFSET_OF_FLAGS + sizeof(uint8_t); + static const unsigned SIZEOF_VALUE_BYTES = sizeof(uint8_t) + sizeof(float); + + static const unsigned TEMPERATURE_UNITS_FLAG_POS = 0; + static const unsigned TIMESTAMP_FLAG_POS = 1; + static const unsigned TEMPERATURE_TYPE_FLAG_POS = 2; + + static const uint8_t TEMPERATURE_UNITS_CELSIUS = 0; + static const uint8_t TEMPERATURE_UNITS_FAHRENHEIT = 1; + + TemperatureValueBytes(float initialTemperature) : bytes() { + /* assumption: temperature values are expressed in Celsius */ + bytes[OFFSET_OF_FLAGS] = (TEMPERATURE_UNITS_CELSIUS << TEMPERATURE_UNITS_FLAG_POS) | + (false << TIMESTAMP_FLAG_POS) | + (false << TEMPERATURE_TYPE_FLAG_POS); + updateTemperature(initialTemperature); + } + + void updateTemperature(float temp) { + uint32_t temp_ieee11073 = quick_ieee11073_from_float(temp); + memcpy(&bytes[OFFSET_OF_VALUE], &temp_ieee11073, sizeof(float)); + } + + uint8_t *getPointer(void) { + return bytes; + } + + const uint8_t *getPointer(void) const { + return bytes; + } + + private: + /** + * @brief A very quick conversion between a float temperature and 11073-20601 FLOAT-Type. + * @param temperature The temperature as a float. + * @return The temperature in 11073-20601 FLOAT-Type format. + */ + uint32_t quick_ieee11073_from_float(float temperature) { + uint8_t exponent = 0xFE; //exponent is -2 + uint32_t mantissa = (uint32_t)(temperature * 100); + + return (((uint32_t)exponent) << 24) | mantissa; + } + + + private: + /* First byte = 8-bit flags, Second field is a float holding the temperature value. */ + /* See --> https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.temperature_measurement.xml */ + uint8_t bytes[SIZEOF_VALUE_BYTES]; + }; + +private: + BLEDevice &ble; + TemperatureValueBytes valueBytes; + GattCharacteristic tempMeasurement; + GattCharacteristic tempLocation; +}; + +#endif /* #ifndef __BLE_HEALTH_THERMOMETER_SERVICE_H__*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/services/HeartRateService.h Mon Sep 22 10:59:09 2014 +0100 @@ -0,0 +1,182 @@ +/* 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_HEART_RATE_SERVICE_H__ +#define __BLE_HEART_RATE_SERVICE_H__ + +#include "BLEDevice.h" + +/* Heart Rate Service */ +/* Service: https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.heart_rate.xml */ +/* HRM Char: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml */ +/* Location: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.body_sensor_location.xml */ +class HeartRateService { +public: + enum { + LOCATION_OTHER = 0, + LOCATION_CHEST, + LOCATION_WRIST, + LOCATION_FINGER, + LOCATION_HAND, + LOCATION_EAR_LOBE, + LOCATION_FOOT, + }; + +public: + /** + * Constructor. + * + * param[in] _ble + * Reference to the underlying BLEDevice. + * param[in] hrmCounter (8-bit) + * initial value for the hrm counter. + * param[in] location + * Sensor's location. + */ + HeartRateService(BLEDevice &_ble, uint8_t hrmCounter, uint8_t location) : + ble(_ble), + valueBytes(hrmCounter), + hrmRate(GattCharacteristic::UUID_HEART_RATE_MEASUREMENT_CHAR, valueBytes.getPointer(), + valueBytes.getNumValueBytes(), HeartRateValueBytes::MAX_VALUE_BYTES, + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY), + hrmLocation(GattCharacteristic::UUID_BODY_SENSOR_LOCATION_CHAR, (uint8_t *)&location, sizeof(location), sizeof(location), + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ), + controlPoint(GattCharacteristic::UUID_HEART_RATE_CONTROL_POINT_CHAR, (uint8_t *)&controlPointValue, + sizeof(controlPointValue), sizeof(controlPointValue), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE) { + setupService(); + } + + /** + * Same constructor as above, but with a 16-bit HRM Counter value. + */ + HeartRateService(BLEDevice &_ble, uint16_t hrmCounter, uint8_t location) : + ble(_ble), + valueBytes(hrmCounter), + hrmRate(GattCharacteristic::UUID_HEART_RATE_MEASUREMENT_CHAR, valueBytes.getPointer(), + valueBytes.getNumValueBytes(), HeartRateValueBytes::MAX_VALUE_BYTES, + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY), + hrmLocation(GattCharacteristic::UUID_BODY_SENSOR_LOCATION_CHAR, (uint8_t *)&location, sizeof(location), sizeof(location), + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ), + controlPoint(GattCharacteristic::UUID_HEART_RATE_CONTROL_POINT_CHAR, (uint8_t *)&controlPointValue, + sizeof(controlPointValue), sizeof(controlPointValue), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE) { + setupService(); + } + + /** + * Set a new 8-bit value for heart rate. + */ + void updateHeartRate(uint8_t hrmCounter) { + valueBytes.updateHeartRate(hrmCounter); + ble.updateCharacteristicValue(hrmRate.getValueAttribute().getHandle(), valueBytes.getPointer(), valueBytes.getNumValueBytes()); + } + + /** + * Set a new 16-bit value for heart rate. + */ + void updateHeartRate(uint16_t hrmCounter) { + valueBytes.updateHeartRate(hrmCounter); + ble.updateCharacteristicValue(hrmRate.getValueAttribute().getHandle(), valueBytes.getPointer(), valueBytes.getNumValueBytes()); + } + + /** + * This callback allows the UART service to receive updates to the + * txCharacteristic. The application should forward the call to this + * function from the global onDataWritten() callback handler; or if that's + * not used, this method can be used as a callback directly. + */ + virtual void onDataWritten(const GattCharacteristicWriteCBParams *params) { + if (params->charHandle == controlPoint.getValueAttribute().getHandle()) { + /* Do something here if the new value is 1; else you can override this method by + * extending this class. + * @NOTE: if you are extending this class, be sure to also call + * ble.onDataWritten(this, &ExtendedHRService::onDataWritten); in + * your constructor. + */ + } + } + +private: + void setupService(void) { + static bool serviceAdded = false; /* We should only ever need to add the heart rate service once. */ + if (serviceAdded) { + return; + } + + GattCharacteristic *charTable[] = {&hrmRate, &hrmLocation, &controlPoint}; + GattService hrmService(GattService::UUID_HEART_RATE_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *)); + + ble.addService(hrmService); + serviceAdded = true; + + ble.onDataWritten(this, &HeartRateService::onDataWritten); + } + +private: + /* Private internal representation for the bytes used to work with the vaulue of the heart-rate characteristic. */ + struct HeartRateValueBytes { + static const unsigned MAX_VALUE_BYTES = 3; /* FLAGS + up to two bytes for heart-rate */ + static const unsigned FLAGS_BYTE_INDEX = 0; + + static const unsigned VALUE_FORMAT_BITNUM = 0; + static const uint8_t VALUE_FORMAT_FLAG = (1 << VALUE_FORMAT_BITNUM); + + HeartRateValueBytes(uint8_t hrmCounter) : valueBytes() { + updateHeartRate(hrmCounter); + } + + HeartRateValueBytes(uint16_t hrmCounter) : valueBytes() { + updateHeartRate(hrmCounter); + } + + void updateHeartRate(uint8_t hrmCounter) { + valueBytes[FLAGS_BYTE_INDEX] &= ~VALUE_FORMAT_FLAG; + valueBytes[FLAGS_BYTE_INDEX + 1] = hrmCounter; + } + + void updateHeartRate(uint16_t hrmCounter) { + valueBytes[FLAGS_BYTE_INDEX] |= VALUE_FORMAT_FLAG; + valueBytes[FLAGS_BYTE_INDEX + 1] = (uint8_t)(hrmCounter & 0xFF); + valueBytes[FLAGS_BYTE_INDEX + 2] = (uint8_t)(hrmCounter >> 8); + } + + uint8_t *getPointer(void) { + return valueBytes; + } + + const uint8_t *getPointer(void) const { + return valueBytes; + } + + unsigned getNumValueBytes(void) const { + return 1 + ((valueBytes[FLAGS_BYTE_INDEX] & VALUE_FORMAT_FLAG) ? sizeof(uint16_t) : sizeof(uint8_t)); + } + + private: + /* First byte = 8-bit values, no extra info, Second byte = uint8_t HRM value */ + /* See --> https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml */ + uint8_t valueBytes[MAX_VALUE_BYTES]; + }; + +private: + BLEDevice &ble; + HeartRateValueBytes valueBytes; + uint8_t controlPointValue; + GattCharacteristic hrmRate; + GattCharacteristic hrmLocation; + GattCharacteristic controlPoint; +}; + +#endif /* #ifndef __BLE_HEART_RATE_SERVICE_H__*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/services/UARTService.cpp Mon Sep 22 10:59:09 2014 +0100 @@ -0,0 +1,41 @@ +/* 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. + */ + +#include "UARTService.h" + +const uint8_t UARTServiceBaseUUID[LENGTH_OF_LONG_UUID] = { + 0x6E, 0x40, 0x00, 0x00, 0xB5, 0xA3, 0xF3, 0x93, + 0xE0, 0xA9, 0xE5, 0x0E, 0x24, 0xDC, 0xCA, 0x9E, +}; +const uint16_t UARTServiceShortUUID = 0x0001; +const uint16_t UARTServiceTXCharacteristicShortUUID = 0x0002; +const uint16_t UARTServiceRXCharacteristicShortUUID = 0x0003; +const uint8_t UARTServiceUUID[LENGTH_OF_LONG_UUID] = { + 0x6E, 0x40, (uint8_t)(UARTServiceShortUUID >> 8), (uint8_t)(UARTServiceShortUUID & 0xFF), 0xB5, 0xA3, 0xF3, 0x93, + 0xE0, 0xA9, 0xE5, 0x0E, 0x24, 0xDC, 0xCA, 0x9E, +}; +const uint8_t UARTServiceUUID_reversed[LENGTH_OF_LONG_UUID] = { + 0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, + 0x93, 0xF3, 0xA3, 0xB5, (uint8_t)(UARTServiceShortUUID & 0xFF), (uint8_t)(UARTServiceShortUUID >> 8), 0x40, 0x6E +}; +const uint8_t UARTServiceTXCharacteristicUUID[LENGTH_OF_LONG_UUID] = { + 0x6E, 0x40, (uint8_t)(UARTServiceTXCharacteristicShortUUID >> 8), (uint8_t)(UARTServiceTXCharacteristicShortUUID & 0xFF), 0xB5, 0xA3, 0xF3, 0x93, + 0xE0, 0xA9, 0xE5, 0x0E, 0x24, 0xDC, 0xCA, 0x9E, +}; +const uint8_t UARTServiceRXCharacteristicUUID[LENGTH_OF_LONG_UUID] = { + 0x6E, 0x40, (uint8_t)(UARTServiceRXCharacteristicShortUUID >> 8), (uint8_t)(UARTServiceRXCharacteristicShortUUID & 0xFF), 0xB5, 0xA3, 0xF3, 0x93, + 0xE0, 0xA9, 0xE5, 0x0E, 0x24, 0xDC, 0xCA, 0x9E, +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/services/UARTService.h Mon Sep 22 10:59:09 2014 +0100 @@ -0,0 +1,203 @@ +/* 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_UART_SERVICE_H__ +#define __BLE_UART_SERVICE_H__ + +#include "Stream.h" + +#include "UUID.h" +#include "BLEDevice.h" + +extern const uint8_t UARTServiceBaseUUID[LENGTH_OF_LONG_UUID]; +extern const uint16_t UARTServiceShortUUID; +extern const uint16_t UARTServiceTXCharacteristicShortUUID; +extern const uint16_t UARTServiceRXCharacteristicShortUUID; + +extern const uint8_t UARTServiceUUID[LENGTH_OF_LONG_UUID]; +extern const uint8_t UARTServiceUUID_reversed[LENGTH_OF_LONG_UUID]; + +extern const uint8_t UARTServiceTXCharacteristicUUID[LENGTH_OF_LONG_UUID]; +extern const uint8_t UARTServiceRXCharacteristicUUID[LENGTH_OF_LONG_UUID]; + +class UARTService : public Stream { +public: + /**< Maximum length of data (in bytes) that can be transmitted by the UART service module to the peer. */ + static const unsigned GATT_MTU_SIZE_DEFAULT = 23; + static const unsigned BLE_UART_SERVICE_MAX_DATA_LEN = (GATT_MTU_SIZE_DEFAULT - 3); + +public: + UARTService(BLEDevice &_ble) : + Stream("blueart"), + ble(_ble), + receiveBuffer(), + sendBuffer(), + sendBufferIndex(0), + numBytesReceived(0), + receiveBufferIndex(0), + txCharacteristic(UARTServiceTXCharacteristicUUID, receiveBuffer, 1, BLE_UART_SERVICE_MAX_DATA_LEN, + GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE), + rxCharacteristic(UARTServiceRXCharacteristicUUID, sendBuffer, 1, BLE_UART_SERVICE_MAX_DATA_LEN, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY) { + GattCharacteristic *charTable[] = {&txCharacteristic, &rxCharacteristic}; + GattService uartService(UARTServiceUUID, charTable, sizeof(charTable) / sizeof(GattCharacteristic *)); + + ble.addService(uartService); + ble.onDataWritten(this, &UARTService::onDataWritten); + } + + /** + * Note: TX and RX characteristics are to be interpreted from the viewpoint of the GATT client using this service. + */ + uint16_t getTXCharacteristicHandle() { + return txCharacteristic.getValueAttribute().getHandle(); + } + + /** + * Note: TX and RX characteristics are to be interpreted from the viewpoint of the GATT client using this service. + */ + uint16_t getRXCharacteristicHandle() { + return rxCharacteristic.getValueAttribute().getHandle(); + } + + /** + * Following a call to this function, all writes to stdout (such as from + * printf) get redirected to the outbound characteristic of this service. + * This might be very useful when wanting to receive debug messages over BLE. + * + * @Note: debug messages originating from printf() like calls are buffered + * before being sent out. A '\n' in the printf() triggers the buffer update + * to the underlying characteristic. + * + * @Note: long messages need to be chopped up into 20-byte updates so that + * they flow out completely with notifications. The receiver should be + * prepared to stitch these messages back. + */ + void retargetStdout() { + freopen("/blueart", "w", stdout); + } + + /** + * This callback allows the UART service to receive updates to the + * txCharacteristic. The application should forward the call to this + * function from the global onDataWritten() callback handler; or if that's + * not used, this method can be used as a callback directly. + */ + virtual void onDataWritten(const GattCharacteristicWriteCBParams *params) { + if (params->charHandle == getTXCharacteristicHandle()) { + uint16_t bytesRead = params->len; + if (bytesRead <= BLE_UART_SERVICE_MAX_DATA_LEN) { + numBytesReceived = bytesRead; + receiveBufferIndex = 0; + memcpy(receiveBuffer, params->data, numBytesReceived); + } + } + } + +protected: + /** + * Override for Stream::write(). + * + * We attempt to collect bytes before pushing them to the UART RX + * characteristic--writing to the RX characteristic will then generate + * notifications for the client. Updates made in quick succession to a + * notification-generating characteristic will result in data being buffered + * in the bluetooth stack as notifications are sent out. The stack will have + * its limits for this buffering; typically a small number under 10. + * Collecting data into the sendBuffer buffer helps mitigate the rate of + * updates. But we shouldn't buffer a large amount of data before updating + * the characteristic otherwise the client will need to turn around and make + * a long read request; this is because notifications include only the first + * 20 bytes of the updated data. + * + * @param buffer The received update + * @param length Amount of characters to be appended. + * @return Amount of characters appended to the rxCharacteristic. + */ + virtual ssize_t write(const void* _buffer, size_t length) { + size_t origLength = length; + const uint8_t *buffer = static_cast<const uint8_t *>(_buffer); + + if (ble.getGapState().connected) { + unsigned bufferIndex = 0; + while (length) { + unsigned bytesRemainingInSendBuffer = BLE_UART_SERVICE_MAX_DATA_LEN - sendBufferIndex; + unsigned bytesToCopy = (length < bytesRemainingInSendBuffer) ? length : bytesRemainingInSendBuffer; + + /* copy bytes into sendBuffer */ + memcpy(&sendBuffer[sendBufferIndex], &buffer[bufferIndex], bytesToCopy); + length -= bytesToCopy; + sendBufferIndex += bytesToCopy; + bufferIndex += bytesToCopy; + + /* have we collected enough? */ + if ((sendBufferIndex == BLE_UART_SERVICE_MAX_DATA_LEN) || + // (sendBuffer[sendBufferIndex - 1] == '\r') || + (sendBuffer[sendBufferIndex - 1] == '\n')) { + ble.updateCharacteristicValue(getRXCharacteristicHandle(), static_cast<const uint8_t *>(sendBuffer), sendBufferIndex); + sendBufferIndex = 0; + } + } + } + + return origLength; + } + + /** + * Override for Stream::_putc() + * @param c + * This function writes the character c, cast to an unsigned char, to stream. + * @return + * The character written as an unsigned char cast to an int or EOF on error. + */ + virtual int _putc(int c) { + return (write(&c, 1) == 1) ? 1 : EOF; + } + + virtual int _getc() { + if (receiveBufferIndex == numBytesReceived) { + return EOF; + } + + return receiveBuffer[receiveBufferIndex++]; + } + + virtual int isatty() { + return 1; + } + +private: + BLEDevice &ble; + + uint8_t receiveBuffer[BLE_UART_SERVICE_MAX_DATA_LEN]; /**< The local buffer into which we receive + * inbound data before forwarding it to the + * application. */ + + uint8_t sendBuffer[BLE_UART_SERVICE_MAX_DATA_LEN]; /**< The local buffer into which outbound data is + * accumulated before being pushed to the + * rxCharacteristic. */ + uint8_t sendBufferIndex; + uint8_t numBytesReceived; + uint8_t receiveBufferIndex; + + GattCharacteristic txCharacteristic; /**< From the point of view of the external client, this is the characteristic + * they'd write into in order to communicate with this application. */ + GattCharacteristic rxCharacteristic; /**< From the point of view of the external client, this is the characteristic + * they'd read from in order to receive the bytes transmitted by this + * application. */ +}; + +#endif /* #ifndef __BLE_UART_SERVICE_H__*/ +