Demonstration of possible usage of the GattServer

Dependents:   mbed-os-example-ble-GattServer_ECG

Files at this revision

API Documentation at this revision

Comitter:
mbed_official
Date:
Wed Jun 20 11:03:22 2018 +0100
Child:
1:16c9c60e0b61
Commit message:
Merge pull request #158 from adbridge/master

Updating mbed-os to mbed-os-5.8.6
.
Commit copied from https://github.com/ARMmbed/mbed-os-example-ble

Changed in this revision

.mbed Show annotated file Show diff for this revision Revisions of this file
BLEProcess.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-os.lib Show annotated file Show diff for this revision Revisions of this file
mbed_app.json Show annotated file Show diff for this revision Revisions of this file
shields/TARGET_ST_BLUENRG.lib Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.mbed	Wed Jun 20 11:03:22 2018 +0100
@@ -0,0 +1,3 @@
+ROOT=.
+TARGET=NRF52_DK
+TOOLCHAIN=GCC_ARM
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BLEProcess.h	Wed Jun 20 11:03:22 2018 +0100
@@ -0,0 +1,206 @@
+/* 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.
+ */
+
+#ifndef GATT_SERVER_EXAMPLE_BLE_PROCESS_H_
+#define GATT_SERVER_EXAMPLE_BLE_PROCESS_H_
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "events/EventQueue.h"
+#include "platform/Callback.h"
+#include "platform/NonCopyable.h"
+
+#include "ble/BLE.h"
+#include "ble/Gap.h"
+#include "ble/GapAdvertisingParams.h"
+#include "ble/GapAdvertisingData.h"
+#include "ble/FunctionPointerWithContext.h"
+
+/**
+ * Handle initialization adn shutdown of the BLE Instance.
+ *
+ * Setup advertising payload and manage advertising state.
+ * Delegate to GattClientProcess once the connection is established.
+ */
+class BLEProcess : private mbed::NonCopyable<BLEProcess> {
+public:
+    /**
+     * Construct a BLEProcess from an event queue and a ble interface.
+     *
+     * Call start() to initiate ble processing.
+     */
+    BLEProcess(events::EventQueue &event_queue, BLE &ble_interface) :
+        _event_queue(event_queue),
+        _ble_interface(ble_interface),
+        _post_init_cb() {
+    }
+
+    ~BLEProcess()
+    {
+        stop();
+    }
+
+   /**
+     * Subscription to the ble interface initialization event.
+     *
+     * @param[in] cb The callback object that will be called when the ble
+     * interface is initialized.
+     */
+    void on_init(mbed::Callback<void(BLE&, events::EventQueue&)> cb)
+    {
+        _post_init_cb = cb;
+    }
+
+    /**
+     * Initialize the ble interface, configure it and start advertising.
+     */
+    bool start()
+    {
+        printf("Ble process started.\r\n");
+
+        if (_ble_interface.hasInitialized()) {
+            printf("Error: the ble instance has already been initialized.\r\n");
+            return false;
+        }
+
+        _ble_interface.onEventsToProcess(
+            makeFunctionPointer(this, &BLEProcess::schedule_ble_events)
+        );
+
+        ble_error_t error = _ble_interface.init(
+            this, &BLEProcess::when_init_complete
+        );
+
+        if (error) {
+            printf("Error: %u returned by BLE::init.\r\n", error);
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Close existing connections and stop the process.
+     */
+    void stop()
+    {
+        if (_ble_interface.hasInitialized()) {
+            _ble_interface.shutdown();
+            printf("Ble process stopped.");
+        }
+    }
+
+private:
+
+    /**
+     * Schedule processing of events from the BLE middleware in the event queue.
+     */
+    void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *event)
+    {
+        _event_queue.call(mbed::callback(&event->ble, &BLE::processEvents));
+    }
+
+    /**
+     * Sets up adverting payload and start advertising.
+     *
+     * This function is invoked when the ble interface is initialized.
+     */
+    void when_init_complete(BLE::InitializationCompleteCallbackContext *event)
+    {
+        if (event->error) {
+            printf("Error %u during the initialization\r\n", event->error);
+            return;
+        }
+        printf("Ble instance initialized\r\n");
+
+        Gap &gap = _ble_interface.gap();
+        ble_error_t error = gap.setAdvertisingPayload(make_advertising_data());
+        if (error) {
+            printf("Error %u during gap.setAdvertisingPayload\r\n", error);
+            return;
+        }
+
+        gap.setAdvertisingParams(make_advertising_params());
+
+        gap.onConnection(this, &BLEProcess::when_connection);
+        gap.onDisconnection(this, &BLEProcess::when_disconnection);
+
+        start_advertising();
+
+       if (_post_init_cb) {
+            _post_init_cb(_ble_interface, _event_queue);
+        }
+    }
+
+    void when_connection(const Gap::ConnectionCallbackParams_t *connection_event)
+    {
+        printf("Connected.\r\n");
+    }
+
+    void when_disconnection(const Gap::DisconnectionCallbackParams_t *event)
+    {
+        printf("Disconnected.\r\n");
+        start_advertising();
+    }
+
+    void start_advertising(void)
+    {
+        ble_error_t error = _ble_interface.gap().startAdvertising();
+        if (error) {
+            printf("Error %u during gap.startAdvertising.\r\n", error);
+            return;
+        } else {
+            printf("Advertising started.\r\n");
+        }
+    }
+
+    static GapAdvertisingData make_advertising_data(void)
+    {
+        static const uint8_t device_name[] = "GattServer";
+        GapAdvertisingData advertising_data;
+
+        // add advertising flags
+        advertising_data.addFlags(
+            GapAdvertisingData::LE_GENERAL_DISCOVERABLE |
+            GapAdvertisingData::BREDR_NOT_SUPPORTED
+        );
+
+        // add device name
+        advertising_data.addData(
+            GapAdvertisingData::COMPLETE_LOCAL_NAME,
+            device_name,
+            sizeof(device_name)
+        );
+
+        return advertising_data;
+    }
+
+    static GapAdvertisingParams make_advertising_params(void)
+    {
+        return GapAdvertisingParams(
+            /* type */ GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED,
+            /* interval */ GapAdvertisingParams::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(500),
+            /* timeout */ 0
+        );
+    }
+
+    events::EventQueue &_event_queue;
+    BLE &_ble_interface;
+    mbed::Callback<void(BLE&, events::EventQueue&)> _post_init_cb;
+};
+
+#endif /* GATT_SERVER_EXAMPLE_BLE_PROCESS_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Jun 20 11:03:22 2018 +0100
@@ -0,0 +1,411 @@
+/* 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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-os.lib	Wed Jun 20 11:03:22 2018 +0100
@@ -0,0 +1,1 @@
+https://github.com/ARMmbed/mbed-os/#35fa909641fedcad9bbe0c7300d4ccdf15a2b71a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_app.json	Wed Jun 20 11:03:22 2018 +0100
@@ -0,0 +1,16 @@
+{
+    "target_overrides": {
+        "K64F": {
+            "target.features_add": ["BLE"],
+            "target.extra_labels_add": ["ST_BLUENRG"]
+        },
+        "NUCLEO_F401RE": {
+            "target.features_add": ["BLE"],
+            "target.extra_labels_add": ["ST_BLUENRG"]
+        },
+        "DISCO_L475VG_IOT01A": {
+            "target.features_add": ["BLE"],
+            "target.extra_labels_add": ["ST_BLUENRG"]
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/shields/TARGET_ST_BLUENRG.lib	Wed Jun 20 11:03:22 2018 +0100
@@ -0,0 +1,2 @@
+
+https://github.com/ARMmbed/ble-x-nucleo-idb0xa1/#b630517008bbe47592927cc8e5dfcd2e5b9de968
\ No newline at end of file