GattServer
Note: Some functions, variables or types have been deprecated. Please see the class reference linked below for details.
You can use Generic Attribute Profile (GATT) to discover services, characteristics and descriptors and to perform operations on them. The interaction happens between two peers, one of which is the client (which initiates interactions) and the other is the server (which responds). You can use Attribute Protocol (ATT) to implement this interaction.
GattServer
is a collection of GattServices. These services contain characteristics that a GattClient
on the peer connected to the device may read or write. These characteristics may also emit updates to subscribed clients when their values change.
Server layout
Application code can add a GattService object to the server with the help of the function addService()
. That function registers all the GattCharacteristics enclosed in the service, as well as all the characteristic descriptors (see GattAttribute) that these characteristics contain. Service registration assigns a unique handle to the various attributes that are part of the service. The user must use this handle to read or write these components.
There are no defined primitives that remove a single service; however, a call to the function reset()
removes all services previously registered in the GattServer.
Characteristic and attributes access
You must access values of the characteristic and the characteristic descriptor present in the GattServer through the handle assigned to them when you registered the service. The GattServer class offers several types of read()
and write()
functions that retrieve or mutate an attribute value.
You can query the server by invoking the function areUpdatesEnabled()
to find out if a client has subscribed to a given characteristic's value update.
Attribute Protocol Maximum Transmission Unit (ATT_MTU)
The Attribute Protocol Maximum Transmission Unit (ATT_MTU
) is the maximum size of the attribute protocol packet. For details, please see the GattClient Documentation.
Events
You can register several event handlers with the GattServer that it will call to notify you of client (remote application connected to the server) and server activities:
onDataSent
: Register an event handler with the GattServer that it will call to notify you when it sends a characteristic value update to a client.onDataWriten
: Register an event handler with the GattServer that it will call to notify you when a client has written an attribute of the server.onDataRead
: Register an event handler with the GattServer that it will call to notify you when a client has read an attribute of the server.onUpdatesEnabled
: Register an event handler with the GattServer that it will call to notify you when a client subscribes to updates for a characteristic.onUpdatesDisabled
: Register an event handler with the GattServer that it will call to notify you when a client unsubscribes from updates for a characteristic.onConfimationReceived
: Register an event handler with the GattServer that it will call to notify you when a client acknowledges a characteristic value notification.
The term characteristic value update represents Characteristic Value Notification and Characteristic Value Indication when the nature of the server initiated is not relevant.
GattServer class reference
Data Structures | |
struct | EventHandler |
Definition of the general handler of GattServer related events. More... |
Public Types | |
typedef FunctionPointerWithContext< unsigned > | DataSentCallback_t |
Event handler invoked when the server has sent data to a client. More... | |
typedef CallChainOfFunctionPointersWithContext< unsigned > | DataSentCallbackChain_t |
Callchain of DataSentCallback_t objects. More... | |
typedef FunctionPointerWithContext< const GattWriteCallbackParams * > | DataWrittenCallback_t |
Event handler invoked when the client has written an attribute of the server. More... | |
typedef CallChainOfFunctionPointersWithContext< const GattWriteCallbackParams * > | DataWrittenCallbackChain_t |
Callchain of DataWrittenCallback_t objects. More... | |
typedef FunctionPointerWithContext< const GattReadCallbackParams * > | DataReadCallback_t |
Event handler invoked when the client has read an attribute of the server. More... | |
typedef CallChainOfFunctionPointersWithContext< const GattReadCallbackParams * > | DataReadCallbackChain_t |
Callchain of DataReadCallback_t. More... | |
typedef FunctionPointerWithContext< const GattServer * > | GattServerShutdownCallback_t |
Event handler invoked when the GattServer is reset. More... | |
typedef CallChainOfFunctionPointersWithContext< const GattServer * > | GattServerShutdownCallbackChain_t |
Callchain of GattServerShutdownCallback_t. More... | |
typedef FunctionPointerWithContext< GattAttribute::Handle_t > | EventCallback_t |
Event handler that handles subscription to characteristic updates, unsubscription from characteristic updates and notification confirmation. More... |
Public Member Functions | |
void | setEventHandler (EventHandler *handler) |
Assign the event handler implementation that will be used by the module to signal events back to the application. More... | |
ble_error_t | addService (GattService &service) |
Add a service declaration to the local attribute server table. More... | |
ble_error_t | read (GattAttribute::Handle_t attributeHandle, uint8_t buffer[], uint16_t *lengthP) |
Read the value of an attribute present in the local GATT server. More... | |
ble_error_t | read (ble::connection_handle_t connectionHandle, GattAttribute::Handle_t attributeHandle, uint8_t *buffer, uint16_t *lengthP) |
Read the value of an attribute present in the local GATT server. More... | |
ble_error_t | write (GattAttribute::Handle_t attributeHandle, const uint8_t *value, uint16_t size, bool localOnly=false) |
Update the value of an attribute present in the local GATT server. More... | |
ble_error_t | write (ble::connection_handle_t connectionHandle, GattAttribute::Handle_t attributeHandle, const uint8_t *value, uint16_t size, bool localOnly=false) |
Update the value of an attribute present in the local GATT server. More... | |
ble_error_t | areUpdatesEnabled (const GattCharacteristic &characteristic, bool *enabledP) |
Determine if one of the connected clients has subscribed to notifications or indications of the characteristic in input. More... | |
ble_error_t | areUpdatesEnabled (ble::connection_handle_t connectionHandle, const GattCharacteristic &characteristic, bool *enabledP) |
Determine if an identified client has subscribed to notifications or indications of a given characteristic. More... | |
bool | isOnDataReadAvailable () const |
Indicate if the underlying stack emit events when an attribute is read by a client. More... | |
void | onDataSent (const DataSentCallback_t &callback) |
Add an event handler that monitors emission of characteristic value updates. More... | |
template<typename T > | |
void | onDataSent (T *objPtr, void(T::*memberPtr)(unsigned count)) |
Add an event handler that monitors emission of characteristic value updates. More... | |
DataSentCallbackChain_t & | onDataSent () |
Access the callchain of data sent event handlers. More... | |
void | onDataWritten (const DataWrittenCallback_t &callback) |
Set an event handler that is called after a connected peer has written an attribute. More... | |
template<typename T > | |
void | onDataWritten (T *objPtr, void(T::*memberPtr)(const GattWriteCallbackParams *context)) |
Set an event handler that is called after a connected peer has written an attribute. More... | |
DataWrittenCallbackChain_t & | onDataWritten () |
Access the callchain of data written event handlers. More... | |
ble_error_t | onDataRead (const DataReadCallback_t &callback) |
Set an event handler that monitors attribute reads from connected clients. More... | |
template<typename T > | |
ble_error_t | onDataRead (T *objPtr, void(T::*memberPtr)(const GattReadCallbackParams *context)) |
Set an event handler that monitors attribute reads from connected clients. More... | |
DataReadCallbackChain_t & | onDataRead () |
Access the callchain of data read event handlers. More... | |
void | onShutdown (const GattServerShutdownCallback_t &callback) |
Set an event handler that monitors shutdown or reset of the GattServer. More... | |
template<typename T > | |
void | onShutdown (T *objPtr, void(T::*memberPtr)(const GattServer *)) |
Set an event handler that monitors shutdown or reset of the GattServer. More... | |
GattServerShutdownCallbackChain_t & | onShutdown () |
Access the callchain of shutdown event handlers. More... | |
void | onUpdatesEnabled (EventCallback_t callback) |
Set up an event handler that monitors subscription to characteristic updates. More... | |
void | onUpdatesDisabled (EventCallback_t callback) |
Set up an event handler that monitors unsubscription from characteristic updates. More... | |
void | onConfirmationReceived (EventCallback_t callback) |
Set up an event handler that monitors notification acknowledgment. More... | |
ble_error_t | reset (void) |
Shut down the GattServer instance. More... |
Protected Member Functions | |
GattServer () | |
Construct a GattServer instance. More... | |
void | handleDataWrittenEvent (const GattWriteCallbackParams *params) |
Helper function that notifies all registered handlers of an occurrence of a data written event. More... | |
void | handleDataReadEvent (const GattReadCallbackParams *params) |
Helper function that notifies all registered handlers of an occurrence of a data read event. More... | |
void | handleEvent (GattServerEvents::gattEvent_e type, GattAttribute::Handle_t attributeHandle) |
Helper function that notifies the registered handler of an occurrence of updates enabled, updates disabled or confirmation received events. More... | |
void | handleDataSentEvent (unsigned count) |
Helper function that notifies all registered handlers of an occurrence of a data sent event. More... |
Protected Attributes | |
EventHandler * | eventHandler |
Event handler provided by the application. More... | |
uint8_t | serviceCount |
The total number of services added to the ATT table. More... | |
uint8_t | characteristicCount |
The total number of characteristics added to the ATT table. More... |
GattServer example
/* mbed Microcontroller Library
* Copyright (c) 2017 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 <stdio.h>
#include "platform/Callback.h"
#include "events/EventQueue.h"
#include "platform/NonCopyable.h"
#include "ble/BLE.h"
#include "ble/Gap.h"
#include "ble/GattClient.h"
#include "ble/GapAdvertisingParams.h"
#include "ble/GapAdvertisingData.h"
#include "ble/GattServer.h"
#include "BLEProcess.h"
using mbed::callback;
/**
* A Clock service that demonstrate the GattServer features.
*
* The clock service host three characteristics that model the current hour,
* minute and second of the clock. The value of the second characteristic is
* incremented automatically by the system.
*
* A client can subscribe to updates of the clock characteristics and get
* notified when one of the value is changed. Clients can also change value of
* the second, minute and hour characteristric.
*/
class ClockService {
typedef ClockService Self;
public:
ClockService() :
_hour_char("485f4145-52b9-4644-af1f-7a6b9322490f", 0),
_minute_char("0a924ca7-87cd-4699-a3bd-abdcd9cf126a", 0),
_second_char("8dd6a1b7-bc75-4741-8a26-264af75807de", 0),
_clock_service(
/* uuid */ "51311102-030e-485f-b122-f8f381aa84ed",
/* characteristics */ _clock_characteristics,
/* numCharacteristics */ sizeof(_clock_characteristics) /
sizeof(_clock_characteristics[0])
),
_server(NULL),
_event_queue(NULL)
{
// update internal pointers (value, descriptors and characteristics array)
_clock_characteristics[0] = &_hour_char;
_clock_characteristics[1] = &_minute_char;
_clock_characteristics[2] = &_second_char;
// setup authorization handlers
_hour_char.setWriteAuthorizationCallback(this, &Self::authorize_client_write);
_minute_char.setWriteAuthorizationCallback(this, &Self::authorize_client_write);
_second_char.setWriteAuthorizationCallback(this, &Self::authorize_client_write);
}
void start(BLE &ble_interface, events::EventQueue &event_queue)
{
if (_event_queue) {
return;
}
_server = &ble_interface.gattServer();
_event_queue = &event_queue;
// register the service
printf("Adding demo service\r\n");
ble_error_t err = _server->addService(_clock_service);
if (err) {
printf("Error %u during demo service registration.\r\n", err);
return;
}
// read write handler
_server->onDataSent(as_cb(&Self::when_data_sent));
_server->onDataWritten(as_cb(&Self::when_data_written));
_server->onDataRead(as_cb(&Self::when_data_read));
// updates subscribtion handlers
_server->onUpdatesEnabled(as_cb(&Self::when_update_enabled));
_server->onUpdatesDisabled(as_cb(&Self::when_update_disabled));
_server->onConfirmationReceived(as_cb(&Self::when_confirmation_received));
// print the handles
printf("clock service registered\r\n");
printf("service handle: %u\r\n", _clock_service.getHandle());
printf("\thour characteristic value handle %u\r\n", _hour_char.getValueHandle());
printf("\tminute characteristic value handle %u\r\n", _minute_char.getValueHandle());
printf("\tsecond characteristic value handle %u\r\n", _second_char.getValueHandle());
_event_queue->call_every(1000 /* ms */, callback(this, &Self::increment_second));
}
private:
/**
* Handler called when a notification or an indication has been sent.
*/
void when_data_sent(unsigned count)
{
printf("sent %u updates\r\n", count);
}
/**
* Handler called after an attribute has been written.
*/
void when_data_written(const GattWriteCallbackParams *e)
{
printf("data written:\r\n");
printf("\tconnection handle: %u\r\n", e->connHandle);
printf("\tattribute handle: %u", e->handle);
if (e->handle == _hour_char.getValueHandle()) {
printf(" (hour characteristic)\r\n");
} else if (e->handle == _minute_char.getValueHandle()) {
printf(" (minute characteristic)\r\n");
} else if (e->handle == _second_char.getValueHandle()) {
printf(" (second characteristic)\r\n");
} else {
printf("\r\n");
}
printf("\twrite operation: %u\r\n", e->writeOp);
printf("\toffset: %u\r\n", e->offset);
printf("\tlength: %u\r\n", e->len);
printf("\t data: ");
for (size_t i = 0; i < e->len; ++i) {
printf("%02X", e->data[i]);
}
printf("\r\n");
}
/**
* Handler called after an attribute has been read.
*/
void when_data_read(const GattReadCallbackParams *e)
{
printf("data read:\r\n");
printf("\tconnection handle: %u\r\n", e->connHandle);
printf("\tattribute handle: %u", e->handle);
if (e->handle == _hour_char.getValueHandle()) {
printf(" (hour characteristic)\r\n");
} else if (e->handle == _minute_char.getValueHandle()) {
printf(" (minute characteristic)\r\n");
} else if (e->handle == _second_char.getValueHandle()) {
printf(" (second characteristic)\r\n");
} else {
printf("\r\n");
}
}
/**
* Handler called after a client has subscribed to notification or indication.
*
* @param handle Handle of the characteristic value affected by the change.
*/
void when_update_enabled(GattAttribute::Handle_t handle)
{
printf("update enabled on handle %d\r\n", handle);
}
/**
* Handler called after a client has cancelled his subscription from
* notification or indication.
*
* @param handle Handle of the characteristic value affected by the change.
*/
void when_update_disabled(GattAttribute::Handle_t handle)
{
printf("update disabled on handle %d\r\n", handle);
}
/**
* Handler called when an indication confirmation has been received.
*
* @param handle Handle of the characteristic value that has emitted the
* indication.
*/
void when_confirmation_received(GattAttribute::Handle_t handle)
{
printf("confirmation received on handle %d\r\n", handle);
}
/**
* Handler called when a write request is received.
*
* This handler verify that the value submitted by the client is valid before
* authorizing the operation.
*/
void authorize_client_write(GattWriteAuthCallbackParams *e)
{
printf("characteristic %u write authorization\r\n", e->handle);
if (e->offset != 0) {
printf("Error invalid offset\r\n");
e->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_OFFSET;
return;
}
if (e->len != 1) {
printf("Error invalid len\r\n");
e->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_INVALID_ATT_VAL_LENGTH;
return;
}
if ((e->data[0] >= 60) ||
((e->data[0] >= 24) && (e->handle == _hour_char.getValueHandle()))) {
printf("Error invalid data\r\n");
e->authorizationReply = AUTH_CALLBACK_REPLY_ATTERR_WRITE_NOT_PERMITTED;
return;
}
e->authorizationReply = AUTH_CALLBACK_REPLY_SUCCESS;
}
/**
* Increment the second counter.
*/
void increment_second(void)
{
uint8_t second = 0;
ble_error_t err = _second_char.get(*_server, second);
if (err) {
printf("read of the second value returned error %u\r\n", err);
return;
}
second = (second + 1) % 60;
err = _second_char.set(*_server, second);
if (err) {
printf("write of the second value returned error %u\r\n", err);
return;
}
if (second == 0) {
increment_minute();
}
}
/**
* Increment the minute counter.
*/
void increment_minute(void)
{
uint8_t minute = 0;
ble_error_t err = _minute_char.get(*_server, minute);
if (err) {
printf("read of the minute value returned error %u\r\n", err);
return;
}
minute = (minute + 1) % 60;
err = _minute_char.set(*_server, minute);
if (err) {
printf("write of the minute value returned error %u\r\n", err);
return;
}
if (minute == 0) {
increment_hour();
}
}
/**
* Increment the hour counter.
*/
void increment_hour(void)
{
uint8_t hour = 0;
ble_error_t err = _hour_char.get(*_server, hour);
if (err) {
printf("read of the hour value returned error %u\r\n", err);
return;
}
hour = (hour + 1) % 24;
err = _hour_char.set(*_server, hour);
if (err) {
printf("write of the hour value returned error %u\r\n", err);
return;
}
}
private:
/**
* Helper that construct an event handler from a member function of this
* instance.
*/
template<typename Arg>
FunctionPointerWithContext<Arg> as_cb(void (Self::*member)(Arg))
{
return makeFunctionPointer(this, member);
}
/**
* Read, Write, Notify, Indicate Characteristic declaration helper.
*
* @tparam T type of data held by the characteristic.
*/
template<typename T>
class ReadWriteNotifyIndicateCharacteristic : public GattCharacteristic {
public:
/**
* Construct a characteristic that can be read or written and emit
* notification or indication.
*
* @param[in] uuid The UUID of the characteristic.
* @param[in] initial_value Initial value contained by the characteristic.
*/
ReadWriteNotifyIndicateCharacteristic(const UUID & uuid, const T& initial_value) :
GattCharacteristic(
/* UUID */ uuid,
/* Initial value */ &_value,
/* Value size */ sizeof(_value),
/* Value capacity */ sizeof(_value),
/* Properties */ GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ |
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE |
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY |
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE,
/* Descriptors */ NULL,
/* Num descriptors */ 0,
/* variable len */ false
),
_value(initial_value) {
}
/**
* Get the value of this characteristic.
*
* @param[in] server GattServer instance that contain the characteristic
* value.
* @param[in] dst Variable that will receive the characteristic value.
*
* @return BLE_ERROR_NONE in case of success or an appropriate error code.
*/
ble_error_t get(GattServer &server, T& dst) const
{
uint16_t value_length = sizeof(dst);
return server.read(getValueHandle(), &dst, &value_length);
}
/**
* Assign a new value to this characteristic.
*
* @param[in] server GattServer instance that will receive the new value.
* @param[in] value The new value to set.
* @param[in] local_only Flag that determine if the change should be kept
* locally or forwarded to subscribed clients.
*/
ble_error_t set(
GattServer &server, const uint8_t &value, bool local_only = false
) const {
return server.write(getValueHandle(), &value, sizeof(value), local_only);
}
private:
uint8_t _value;
};
ReadWriteNotifyIndicateCharacteristic<uint8_t> _hour_char;
ReadWriteNotifyIndicateCharacteristic<uint8_t> _minute_char;
ReadWriteNotifyIndicateCharacteristic<uint8_t> _second_char;
// list of the characteristics of the clock service
GattCharacteristic* _clock_characteristics[3];
// demo service
GattService _clock_service;
GattServer* _server;
events::EventQueue *_event_queue;
};
int main() {
BLE &ble_interface = BLE::Instance();
events::EventQueue event_queue;
ClockService demo_service;
BLEProcess ble_process(event_queue, ble_interface);
ble_process.on_init(callback(&demo_service, &ClockService::start));
// bind the event queue to the ble interface, initialize the interface
// and start advertising
ble_process.start();
// Process the event queue.
event_queue.dispatch_forever();
return 0;
}