Mistake on this page?
Report an issue in GitHub or email us

GattServer

A GattServer is a collection of GattServices. These services contain characteristics that a 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.

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

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_tEventCallback_t
 Event handler that handles subscription to characteristic updates, unsubscription from characteristic updates and notification confirmation. More...
Public Member Functions
virtual ble_error_t addService (GattService &service)
 Add a service declaration to the local attribute server table. More...
virtual 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...
virtual ble_error_t read (Gap::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...
virtual 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...
virtual ble_error_t write (Gap::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...
virtual 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...
virtual ble_error_t areUpdatesEnabled (Gap::Handle_t connectionHandle, const GattCharacteristic &characteristic, bool *enabledP)
 Determine if an identified client has subscribed to notifications or indications of a given characteristic. More...
virtual 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_tonDataSent ()
 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_tonDataWritten ()
 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_tonDataRead ()
 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_tonShutdown ()
 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...
virtual 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
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;
}

Important Information for this Arm website

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies. If you are not happy with the use of these cookies, please review our Cookie Policy to learn how they can be disabled. By disabling cookies, some features of the site will not work.