Gestione Bluetooth per il progetto Tarallo
Revision 82:443323e7d4c9, committed 2020-01-16
- Comitter:
- fdalforno
- Date:
- Thu Jan 16 14:48:30 2020 +0000
- Parent:
- 81:796f36bfa718
- Commit message:
- Gestione client Bluetooth per scheda F401RE
Changed in this revision
| source/BLEProcess.h | Show annotated file Show diff for this revision Revisions of this file |
| source/main.cpp | Show annotated file Show diff for this revision Revisions of this file |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/BLEProcess.h Thu Jan 16 14:48:30 2020 +0000
@@ -0,0 +1,231 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2015 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GATT_EXAMPLE_BLE_PROCESS_H_
+#define GATT_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 and 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();
+ }
+
+ /**
+ * 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.");
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+private:
+ /**
+ * 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);
+ }
+ }
+
+ /**
+ * Start the gatt client process when a connection event is received.
+ */
+ void when_connection(const Gap::ConnectionCallbackParams_t *connection_event)
+ {
+ printf("Connected.\r\n");
+ }
+
+ /**
+ * Stop the gatt client process when the device is disconnected then restart
+ * advertising.
+ */
+ void when_disconnection(const Gap::DisconnectionCallbackParams_t *event)
+ {
+ printf("Disconnected.\r\n");
+ start_advertising();
+ }
+
+ /**
+ * Setup the advertising parameters.
+ */
+ void setup_advertising()
+ {
+ Gap &gap = _ble_interface.gap();
+ gap.setAdvertisingPayload(make_advertising_data());
+ gap.setAdvertisingParams(make_advertising_params());
+ }
+
+ /**
+ * Start the advertising process; it ends when a device connects.
+ */
+ void start_advertising()
+ {
+ ble_error_t error = _ble_interface.gap().startAdvertising();
+ if (error) {
+ printf("Error %u during gap.startAdvertising.\r\n", error);
+ } else {
+ printf("Advertising started.\r\n");
+ }
+ }
+
+ /**
+ * 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));
+ }
+
+ /**
+ * Build data advertised by the BLE interface.
+ */
+ static GapAdvertisingData make_advertising_data(void)
+ {
+ static const uint8_t device_name[] = "GattClient";
+ 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;
+ }
+
+ /**
+ * Build advertising parameters used by the BLE interface.
+ */
+ 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_EXAMPLE_BLE_PROCESS_H_ */
\ No newline at end of file
--- a/source/main.cpp Thu Aug 15 17:00:47 2019 +0100
+++ b/source/main.cpp Thu Jan 16 14:48:30 2020 +0000
@@ -1,5 +1,5 @@
/* mbed Microcontroller Library
- * Copyright (c) 2006-2015 ARM Limited
+ * Copyright (c) 2006-2018 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,221 +14,566 @@
* limitations under the License.
*/
-#include <events/mbed_events.h>
-#include <mbed.h>
+#include <memory>
+#include <new>
+#include <stdio.h>
+
+#include "events/EventQueue.h"
+#include "platform/NonCopyable.h"
+
#include "ble/BLE.h"
-#include "pretty_printer.h"
+#include "ble/Gap.h"
+#include "ble/GattClient.h"
+#include "ble/GapAdvertisingParams.h"
+#include "ble/GapAdvertisingData.h"
+#include "ble/GattClient.h"
+#include "ble/DiscoveredService.h"
+#include "ble/DiscoveredCharacteristic.h"
+#include "ble/CharacteristicDescriptorDiscovery.h"
-static events::EventQueue event_queue(/* event count */ 16 * EVENTS_EVENT_SIZE);
+#include "BLEProcess.h"
-/** @deprecated This demo is deprecated and no replacement is currently available.
+/**
+ * Handle discovery of the GATT server.
+ *
+ * First the GATT server is discovered in its entirety then each readable
+ * characteristic is read and the client register to characteristic
+ * notifications or indication when available. The client report server
+ * indications and notification until the connection end.
*/
-MBED_DEPRECATED_SINCE(
- "mbed-os-5.11",
- "This demo is deprecated and no replacement is currently available."
-)
-class BeaconDemo : ble::Gap::EventHandler {
+class GattClientProcess : private mbed::NonCopyable<GattClientProcess>,
+ public ble::Gap::EventHandler {
+
+ // Internal typedef to this class type.
+ // It is used as a shorthand to pass member function as callbacks.
+ typedef GattClientProcess Self;
+
+ typedef CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t
+ DiscoveryCallbackParams_t;
+
+ typedef CharacteristicDescriptorDiscovery::TerminationCallbackParams_t
+ TerminationCallbackParams_t;
+
+ typedef DiscoveredCharacteristic::Properties_t Properties_t;
+
public:
- BeaconDemo(BLE &ble, events::EventQueue &event_queue) :
- _ble(ble),
- _event_queue(event_queue),
- _adv_data_builder(_adv_buffer) { }
+
+ /**
+ * Construct an empty client process.
+ *
+ * The function start() shall be called to initiate the discovery process.
+ */
+ GattClientProcess() :
+ _client(NULL),
+ _connection_handle(),
+ _characteristics(NULL),
+ _it(NULL),
+ _descriptor_handle(0),
+ _ble_interface(NULL),
+ _event_queue(NULL) {
+ }
+
+ ~GattClientProcess()
+ {
+ stop();
+ }
+
+ void init(BLE &ble_interface, events::EventQueue &event_queue)
+ {
+ _ble_interface = &ble_interface;
+ _event_queue = &event_queue;
+ _client = &_ble_interface->gattClient();
+
+ _ble_interface->gap().setEventHandler(this);
+ }
+
+ /**
+ * Start the discovery process.
+ *
+ * @param[in] client The GattClient instance which will discover the distant
+ * GATT server.
+ * @param[in] connection_handle Reference of the connection to the GATT
+ * server which will be discovered.
+ */
+ void start()
+ {
+ // setup the event handlers called during the process
+ _client->onDataWritten().add(as_cb(&Self::when_descriptor_written));
+ _client->onHVX().add(as_cb(&Self::when_characteristic_changed));
- void start() {
- _ble.gap().setEventHandler(this);
+ // The discovery process will invoke when_service_discovered when a
+ // service is discovered, when_characteristic_discovered when a
+ // characteristic is discovered and when_service_discovery_ends once the
+ // discovery process has ended.
+ _client->onServiceDiscoveryTermination(as_cb(&Self::when_service_discovery_ends));
+ ble_error_t error = _client->launchServiceDiscovery(
+ _connection_handle,
+ as_cb(&Self::when_service_discovered),
+ as_cb(&Self::when_characteristic_discovered)
+ );
+
+ if (error) {
+ printf("Error %u returned by _client->launchServiceDiscovery.\r\n", error);
+ return;
+ }
+
+ printf("Client process started: initiate service discovery.\r\n");
+ }
- _ble.init(this, &BeaconDemo::on_init_complete);
+ /**
+ * Stop the discovery process and clean the instance.
+ */
+ void stop()
+ {
+ if (!_client) {
+ return;
+ }
- _event_queue.dispatch_forever();
+ // unregister event handlers
+ _client->onDataWritten().detach(as_cb(&Self::when_descriptor_written));
+ _client->onHVX().detach(as_cb(&Self::when_characteristic_changed));
+ _client->onServiceDiscoveryTermination(NULL);
+
+ // remove discovered characteristics
+ clear_characteristics();
+
+ // clean up the instance
+ _connection_handle = 0;
+ _characteristics = NULL;
+ _it = NULL;
+ _descriptor_handle = 0;
+
+ printf("Client process stopped.\r\n");
}
private:
/**
- * iBeacon payload builder.
+ * Event handler invoked when a connection is established.
*
- * This data structure contains the payload of an iBeacon. The payload is
- * built at construction time and application code can set up an iBeacon by
- * injecting the raw field into the GAP advertising payload as a
- * GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA.
+ * This function setup the connection handle to operate on then start the
+ * discovery process.
*/
- union Payload {
- /**
- * Raw data of the payload.
- */
- uint8_t raw[25];
- struct {
- /**
- * Beacon manufacturer identifier.
- */
- uint16_t companyID;
-
- /**
- * Packet ID; Equal to 2 for an iBeacon.
- */
- uint8_t ID;
-
- /**
- * Length of the remaining data presents in the payload.
- */
- uint8_t len;
-
- /**
- * Beacon UUID.
- */
- uint8_t proximityUUID[16];
-
- /**
- * Beacon Major group ID.
- */
- uint16_t majorNumber;
-
- /**
- * Beacon minor ID.
- */
- uint16_t minorNumber;
+ virtual void onConnectionComplete(const ble::ConnectionCompleteEvent &event)
+ {
+ _connection_handle = event.getConnectionHandle();
+ _event_queue->call(mbed::callback(this, &Self::start));
+ }
- /**
- * Tx power received at 1 meter; in dBm.
- */
- uint8_t txPower;
- };
-
- /**
- * Assemble an iBeacon payload.
- *
- * @param[in] uuid Beacon network ID. iBeacon operators use this value
- * to group their iBeacons into a single network, a single region and
- * identify their organization among others.
- *
- * @param[in] majNum Beacon major group ID. iBeacon exploitants may use
- * this field to divide the region into subregions, their network into
- * subnetworks.
- *
- * @param[in] minNum Identifier of the Beacon in its subregion.
- *
- * @param[in] transmitPower Measured transmit power of the beacon at 1
- * meter. Scanners use this parameter to approximate the distance
- * to the beacon.
- *
- * @param[in] companyIDIn ID of the beacon manufacturer.
- */
- Payload(
- const uint8_t *uuid,
- uint16_t majNum,
- uint16_t minNum,
- uint8_t transmitPower,
- uint16_t companyIDIn
- ) : companyID(companyIDIn),
- ID(0x02),
- len(0x15),
- majorNumber(__REV16(majNum)),
- minorNumber(__REV16(minNum)),
- txPower(transmitPower)
- {
- memcpy(proximityUUID, uuid, 16);
+ /**
+ * Stop the discovery process and clean the instance.
+ */
+ virtual void onDisconnectionComplete(const ble::DisconnectionCompleteEvent &event)
+ {
+ if (_client && event.getConnectionHandle() == _connection_handle) {
+ stop();
}
- };
-
- /** Callback triggered when the ble initialization process has finished */
- void on_init_complete(BLE::InitializationCompleteCallbackContext *params) {
- if (params->error != BLE_ERROR_NONE) {
- printf("Ble initialization failed.");
- return;
- }
-
- print_mac_address();
-
- start_advertising();
}
- void start_advertising() {
- /* Create advertising parameters and payload */
+private:
+////////////////////////////////////////////////////////////////////////////////
+// Service and characteristic discovery process.
- ble::AdvertisingParameters adv_parameters(
- ble::advertising_type_t::CONNECTABLE_UNDIRECTED,
- ble::adv_interval_t(ble::millisecond_t(1000))
+ /**
+ * Handle services discovered.
+ *
+ * The GattClient invokes this function when a service has been discovered.
+ *
+ * @see GattClient::launchServiceDiscovery
+ */
+ void when_service_discovered(const DiscoveredService *discovered_service)
+ {
+ // print information of the service discovered
+ printf("Service discovered: value = ");
+ print_uuid(discovered_service->getUUID());
+ printf(", start = %u, end = %u.\r\n",
+ discovered_service->getStartHandle(),
+ discovered_service->getEndHandle()
+ );
+ }
+
+ /**
+ * Handle characteristics discovered.
+ *
+ * The GattClient invoke this function when a characteristic has been
+ * discovered.
+ *
+ * @see GattClient::launchServiceDiscovery
+ */
+ void when_characteristic_discovered(const DiscoveredCharacteristic *discovered_characteristic)
+ {
+ // print characteristics properties
+ printf("\tCharacteristic discovered: uuid = ");
+ print_uuid(discovered_characteristic->getUUID());
+ printf(", properties = ");
+ print_properties(discovered_characteristic->getProperties());
+ printf(
+ ", decl handle = %u, value handle = %u, last handle = %u.\r\n",
+ discovered_characteristic->getDeclHandle(),
+ discovered_characteristic->getValueHandle(),
+ discovered_characteristic->getLastHandle()
);
- _adv_data_builder.setFlags();
-
- /**
- * The Beacon payload has the following composition:
- * 128-Bit / 16byte UUID = E2 0A 39 F4 73 F5 4B C4 A1 2F 17 D1 AD 07 A9 61
- * Major/Minor = 0x1122 / 0x3344
- * Tx Power = 0xC8 = 200, 2's compliment is 256-200 = (-56dB)
- *
- * Note: please remember to calibrate your beacons TX Power for more accurate results.
- */
- static const uint8_t uuid[] = { 0xE2, 0x0A, 0x39, 0xF4, 0x73, 0xF5, 0x4B, 0xC4,
- 0xA1, 0x2F, 0x17, 0xD1, 0xAD, 0x07, 0xA9, 0x61 };
- uint16_t major_number = 1122;
- uint16_t minor_number = 3344;
- uint16_t tx_power = 0xC8;
- uint16_t comp_id = 0x004C;
-
- Payload ibeacon(uuid, major_number, minor_number, tx_power, comp_id);
-
- _adv_data_builder.setManufacturerSpecificData(ibeacon.raw);
-
- /* Setup advertising */
-
- ble_error_t error = _ble.gap().setAdvertisingParameters(
- ble::LEGACY_ADVERTISING_HANDLE,
- adv_parameters
- );
-
- if (error) {
- print_error(error, "_ble.gap().setAdvertisingParameters() failed");
- return;
- }
-
- error = _ble.gap().setAdvertisingPayload(
- ble::LEGACY_ADVERTISING_HANDLE,
- _adv_data_builder.getAdvertisingData()
- );
-
- if (error) {
- print_error(error, "_ble.gap().setAdvertisingPayload() failed");
- return;
- }
-
- /* Start advertising */
-
- error = _ble.gap().startAdvertising(ble::LEGACY_ADVERTISING_HANDLE);
-
- if (error) {
- print_error(error, "_ble.gap().startAdvertising() failed");
+ // add the characteristic into the list of discovered characteristics
+ bool success = add_characteristic(discovered_characteristic);
+ if (!success) {
+ printf("Error: memory allocation failure while adding the discovered characteristic.\r\n");
+ _client->terminateServiceDiscovery();
+ stop();
return;
}
}
-private:
- /* Event handler */
+ /**
+ * Handle termination of the service and characteristic discovery process.
+ *
+ * The GattClient invokes this function when the service and characteristic
+ * discovery process ends.
+ *
+ * @see GattClient::onServiceDiscoveryTermination
+ */
+ void when_service_discovery_ends(Gap::Handle_t connection_handle)
+ {
+ if (!_characteristics) {
+ printf("No characteristics discovered, end of the process.\r\n");
+ return;
+ }
+
+ printf("All services and characteristics discovered, process them.\r\n");
+
+ // reset iterator and start processing characteristics in order
+ _it = NULL;
+ _event_queue->call(mbed::callback(this, &Self::process_next_characteristic));
+ }
+
+////////////////////////////////////////////////////////////////////////////////
+// Processing of characteristics based on their properties.
+
+ /**
+ * Process the characteristics discovered.
+ *
+ * - If the characteristic is readable then read its value and print it. Then
+ * - If the characteristic can emit notification or indication then discover
+ * the characteristic CCCD and subscribe to the server initiated event.
+ * - Otherwise skip the characteristic processing.
+ */
+ void process_next_characteristic(void)
+ {
+ if (!_it) {
+ _it = _characteristics;
+ } else {
+ _it = _it->next;
+ }
+
+ while (_it) {
+ Properties_t properties = _it->value.getProperties();
+
+ if (properties.read()) {
+ read_characteristic(_it->value);
+ return;
+ } else if(properties.notify() || properties.indicate()) {
+ discover_descriptors(_it->value);
+ return;
+ } else {
+ printf(
+ "Skip processing of characteristic %u\r\n",
+ _it->value.getValueHandle()
+ );
+ _it = _it->next;
+ }
+ }
+
+ printf("All characteristics discovered have been processed.\r\n");
+ }
+
+ /**
+ * Initate the read of the characteristic in input.
+ *
+ * The completion of the operation will happens in when_characteristic_read()
+ */
+ void read_characteristic(const DiscoveredCharacteristic &characteristic)
+ {
+ printf("Initiating read at %u.\r\n", characteristic.getValueHandle());
+ ble_error_t error = characteristic.read(
+ 0, as_cb(&Self::when_characteristic_read)
+ );
- void onDisconnectionComplete(const ble::DisconnectionCompleteEvent&) {
- _ble.gap().startAdvertising(ble::LEGACY_ADVERTISING_HANDLE);
+ if (error) {
+ printf(
+ "Error: cannot initiate read at %u due to %u\r\n",
+ characteristic.getValueHandle(), error
+ );
+ stop();
+ }
+ }
+
+ /**
+ * Handle the reception of a read response.
+ *
+ * If the characteristic can emit notification or indication then start the
+ * discovery of the the characteristic descriptors then subscribe to the
+ * server initiated event by writing the CCCD discovered. Otherwise start
+ * the processing of the next characteristic discovered in the server.
+ */
+ void when_characteristic_read(const GattReadCallbackParams *read_event)
+ {
+ printf("\tCharacteristic value at %u equal to: ", read_event->handle);
+ for (size_t i = 0; i < read_event->len; ++i) {
+ printf("0x%02X ", read_event->data[i]);
+ }
+ printf(".\r\n");
+
+ Properties_t properties = _it->value.getProperties();
+
+ if(properties.notify() || properties.indicate()) {
+ discover_descriptors(_it->value);
+ } else {
+ process_next_characteristic();
+ }
+ }
+
+ /**
+ * Initiate the discovery of the descriptors of the characteristic in input.
+ *
+ * When a descriptor is discovered, the function when_descriptor_discovered
+ * is invoked.
+ */
+ void discover_descriptors(const DiscoveredCharacteristic &characteristic)
+ {
+ printf("Initiating descriptor discovery of %u.\r\n", characteristic.getValueHandle());
+
+ _descriptor_handle = 0;
+ ble_error_t error = characteristic.discoverDescriptors(
+ as_cb(&Self::when_descriptor_discovered),
+ as_cb(&Self::when_descriptor_discovery_ends)
+ );
+
+ if (error) {
+ printf(
+ "Error: cannot initiate discovery of %04X due to %u.\r\n",
+ characteristic.getValueHandle(), error
+ );
+ stop();
+ }
+ }
+
+ /**
+ * Handle the discovery of the characteristic descriptors.
+ *
+ * If the descriptor found is a CCCD then stop the discovery. Once the
+ * process has ended subscribe to server initiated events by writing the
+ * value of the CCCD.
+ */
+ void when_descriptor_discovered(const DiscoveryCallbackParams_t* event)
+ {
+ printf("\tDescriptor discovered at %u, UUID: ", event->descriptor.getAttributeHandle());
+ print_uuid(event->descriptor.getUUID());
+ printf(".\r\n");
+
+ if (event->descriptor.getUUID() == BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG) {
+ _descriptor_handle = event->descriptor.getAttributeHandle();
+ _client->terminateCharacteristicDescriptorDiscovery(
+ event->characteristic
+ );
+ }
}
-private:
- BLE &_ble;
- events::EventQueue &_event_queue;
+ /**
+ * If a CCCD has been found subscribe to server initiated events by writing
+ * its value.
+ */
+ void when_descriptor_discovery_ends(const TerminationCallbackParams_t *event) {
+ // shall never happen but happen with android devices ...
+ // process the next charateristic
+ if (!_descriptor_handle) {
+ printf("\tWarning: characteristic with notify or indicate attribute without CCCD.\r\n");
+ process_next_characteristic();
+ return;
+ }
+
+ Properties_t properties = _it->value.getProperties();
+
+ uint16_t cccd_value =
+ (properties.notify() << 0) | (properties.indicate() << 1);
+
+ ble_error_t error = _client->write(
+ GattClient::GATT_OP_WRITE_REQ,
+ _connection_handle,
+ _descriptor_handle,
+ sizeof(cccd_value),
+ reinterpret_cast<uint8_t*>(&cccd_value)
+ );
+
+ if (error) {
+ printf(
+ "Error: cannot initiate write of CCCD %u due to %u.\r\n",
+ _descriptor_handle, error
+ );
+ stop();
+ }
+ }
+
+ /**
+ * Called when the CCCD has been written.
+ */
+ void when_descriptor_written(const GattWriteCallbackParams* event)
+ {
+ // should never happen
+ if (!_descriptor_handle) {
+ printf("\tError: received write response to unsolicited request.\r\n");
+ stop();
+ return;
+ }
+
+ printf("\tCCCD at %u written.\r\n", _descriptor_handle);
+ _descriptor_handle = 0;
+ process_next_characteristic();
+ }
+
+ /**
+ * Print the updated value of the characteristic.
+ *
+ * This function is called when the server emits a notification or an
+ * indication of a characteristic value the client has subscribed to.
+ *
+ * @see GattClient::onHVX()
+ */
+ void when_characteristic_changed(const GattHVXCallbackParams* event)
+ {
+ printf("Change on attribute %u: new value = ", event->handle);
+ for (size_t i = 0; i < event->len; ++i) {
+ printf("0x%02X ", event->data[i]);
+ }
+ printf(".\r\n");
+ }
+
+ struct DiscoveredCharacteristicNode {
+ DiscoveredCharacteristicNode(const DiscoveredCharacteristic &c) :
+ value(c), next(NULL) { }
+
+ DiscoveredCharacteristic value;
+ DiscoveredCharacteristicNode *next;
+ };
+
+ /**
+ * Add a discovered characteristic into the list of discovered characteristics.
+ */
+ bool add_characteristic(const DiscoveredCharacteristic *characteristic)
+ {
+ DiscoveredCharacteristicNode* new_node =
+ new(std::nothrow) DiscoveredCharacteristicNode(*characteristic);
- uint8_t _adv_buffer[ble::LEGACY_ADVERTISING_MAX_SIZE];
- ble::AdvertisingDataBuilder _adv_data_builder;
+ if (new_node == NULL) {
+ printf("Error while allocating a new characteristic.\r\n");
+ return false;
+ }
+
+ if (_characteristics == NULL) {
+ _characteristics = new_node;
+ } else {
+ DiscoveredCharacteristicNode* c = _characteristics;
+ while(c->next) {
+ c = c->next;
+ }
+ c->next = new_node;
+ }
+
+ return true;
+ }
+
+ /**
+ * Clear the list of discovered characteristics.
+ */
+ void clear_characteristics(void)
+ {
+ DiscoveredCharacteristicNode *c= _characteristics;
+
+ while (c) {
+ DiscoveredCharacteristicNode *n = c->next;
+ delete c;
+ c = n;
+ }
+ }
+
+ /**
+ * Helper to construct an event handler from a member function of this
+ * instance.
+ */
+ template<typename ContextType>
+ FunctionPointerWithContext<ContextType> as_cb(
+ void (Self::*member)(ContextType context)
+ ) {
+ return makeFunctionPointer(this, member);
+ }
+
+ /**
+ * Print the value of a UUID.
+ */
+ static void print_uuid(const UUID &uuid)
+ {
+ const uint8_t *uuid_value = uuid.getBaseUUID();
+
+ // UUIDs are in little endian, print them in big endian
+ for (size_t i = 0; i < uuid.getLen(); ++i) {
+ printf("%02X", uuid_value[(uuid.getLen() - 1) - i]);
+ }
+ }
+
+ /**
+ * Print the value of a characteristic properties.
+ */
+ static void print_properties(const Properties_t &properties)
+ {
+ const struct {
+ bool (Properties_t::*fn)() const;
+ const char* str;
+ } prop_to_str[] = {
+ { &Properties_t::broadcast, "broadcast" },
+ { &Properties_t::read, "read" },
+ { &Properties_t::writeWoResp, "writeWoResp" },
+ { &Properties_t::write, "write" },
+ { &Properties_t::notify, "notify" },
+ { &Properties_t::indicate, "indicate" },
+ { &Properties_t::authSignedWrite, "authSignedWrite" }
+ };
+
+ printf("[");
+ for (size_t i = 0; i < (sizeof(prop_to_str) / sizeof(prop_to_str[0])); ++i) {
+ if ((properties.*(prop_to_str[i].fn))()) {
+ printf(" %s", prop_to_str[i].str);
+ }
+ }
+ printf(" ]");
+ }
+
+ GattClient *_client;
+ Gap::Handle_t _connection_handle;
+ DiscoveredCharacteristicNode *_characteristics;
+ DiscoveredCharacteristicNode *_it;
+ GattAttribute::Handle_t _descriptor_handle;
+ BLE *_ble_interface;
+ events::EventQueue *_event_queue;
};
-/** Schedule processing of events from the BLE middleware in the event queue. */
-void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context) {
- event_queue.call(Callback<void()>(&context->ble, &BLE::processEvents));
-}
+
+int main() {
+
+ BLE &ble_interface = BLE::Instance();
+ events::EventQueue event_queue;
+ BLEProcess ble_process(event_queue, ble_interface);
+ GattClientProcess gatt_client_process;
-int main()
-{
- BLE &ble = BLE::Instance();
- ble.onEventsToProcess(schedule_ble_events);
+ // Register GattClientProcess::init in the ble_process; this function will
+ // be called once the ble_interface is initialized.
+ ble_process.on_init(
+ mbed::callback(&gatt_client_process, &GattClientProcess::init)
+ );
- BeaconDemo demo(ble, event_queue);
- demo.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;
-}
+}
\ No newline at end of file