This application demonstrates detailed uses of the GattClient APIs.

Committer:
donatien
Date:
Tue Aug 14 14:21:00 2018 +0000
Revision:
1:71d7cec222eb
Parent:
0:ad9793cc5249
Fix casing issues in includes

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Donatien Garnier 0:ad9793cc5249 1
Donatien Garnier 0:ad9793cc5249 2 /* mbed Microcontroller Library
Donatien Garnier 0:ad9793cc5249 3 * Copyright (c) 2006-2017 ARM Limited
Donatien Garnier 0:ad9793cc5249 4 *
Donatien Garnier 0:ad9793cc5249 5 * Licensed under the Apache License, Version 2.0 (the "License");
Donatien Garnier 0:ad9793cc5249 6 * you may not use this file except in compliance with the License.
Donatien Garnier 0:ad9793cc5249 7 * You may obtain a copy of the License at
Donatien Garnier 0:ad9793cc5249 8 *
Donatien Garnier 0:ad9793cc5249 9 * http://www.apache.org/licenses/LICENSE-2.0
Donatien Garnier 0:ad9793cc5249 10 *
Donatien Garnier 0:ad9793cc5249 11 * Unless required by applicable law or agreed to in writing, software
Donatien Garnier 0:ad9793cc5249 12 * distributed under the License is distributed on an "AS IS" BASIS,
Donatien Garnier 0:ad9793cc5249 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Donatien Garnier 0:ad9793cc5249 14 * See the License for the specific language governing permissions and
Donatien Garnier 0:ad9793cc5249 15 * limitations under the License.
Donatien Garnier 0:ad9793cc5249 16 */
Donatien Garnier 0:ad9793cc5249 17
Donatien Garnier 0:ad9793cc5249 18 #include <memory>
Donatien Garnier 0:ad9793cc5249 19 #include <new>
Donatien Garnier 0:ad9793cc5249 20 #include <stdio.h>
Donatien Garnier 0:ad9793cc5249 21
donatien 1:71d7cec222eb 22 #include "events/EventQueue.h"
Donatien Garnier 0:ad9793cc5249 23 #include "platform/NonCopyable.h"
Donatien Garnier 0:ad9793cc5249 24
Donatien Garnier 0:ad9793cc5249 25 #include "ble/BLE.h"
Donatien Garnier 0:ad9793cc5249 26 #include "ble/Gap.h"
Donatien Garnier 0:ad9793cc5249 27 #include "ble/GattClient.h"
Donatien Garnier 0:ad9793cc5249 28 #include "ble/GapAdvertisingParams.h"
Donatien Garnier 0:ad9793cc5249 29 #include "ble/GapAdvertisingData.h"
Donatien Garnier 0:ad9793cc5249 30 #include "ble/GattClient.h"
Donatien Garnier 0:ad9793cc5249 31 #include "ble/DiscoveredService.h"
Donatien Garnier 0:ad9793cc5249 32 #include "ble/DiscoveredCharacteristic.h"
Donatien Garnier 0:ad9793cc5249 33 #include "ble/CharacteristicDescriptorDiscovery.h"
Donatien Garnier 0:ad9793cc5249 34
Donatien Garnier 0:ad9793cc5249 35 #include "BLEProcess.h"
Donatien Garnier 0:ad9793cc5249 36
Donatien Garnier 0:ad9793cc5249 37 /**
Donatien Garnier 0:ad9793cc5249 38 * Handle discovery of the GATT server.
Donatien Garnier 0:ad9793cc5249 39 *
Donatien Garnier 0:ad9793cc5249 40 * First the GATT server is discovered in its entirety then each readable
Donatien Garnier 0:ad9793cc5249 41 * characteristic is read and the client register to characteristic
Donatien Garnier 0:ad9793cc5249 42 * notifications or indication when available. The client report server
Donatien Garnier 0:ad9793cc5249 43 * indications and notification until the connection end.
Donatien Garnier 0:ad9793cc5249 44 */
Donatien Garnier 0:ad9793cc5249 45 class GattClientProcess : private mbed::NonCopyable<GattClientProcess> {
Donatien Garnier 0:ad9793cc5249 46
Donatien Garnier 0:ad9793cc5249 47 // Internal typedef to this class type.
Donatien Garnier 0:ad9793cc5249 48 // It is used as a shorthand to pass member function as callbacks.
Donatien Garnier 0:ad9793cc5249 49 typedef GattClientProcess Self;
Donatien Garnier 0:ad9793cc5249 50
Donatien Garnier 0:ad9793cc5249 51 typedef CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t
Donatien Garnier 0:ad9793cc5249 52 DiscoveryCallbackParams_t;
Donatien Garnier 0:ad9793cc5249 53
Donatien Garnier 0:ad9793cc5249 54 typedef CharacteristicDescriptorDiscovery::TerminationCallbackParams_t
Donatien Garnier 0:ad9793cc5249 55 TerminationCallbackParams_t;
Donatien Garnier 0:ad9793cc5249 56
Donatien Garnier 0:ad9793cc5249 57 typedef DiscoveredCharacteristic::Properties_t Properties_t;
Donatien Garnier 0:ad9793cc5249 58
Donatien Garnier 0:ad9793cc5249 59 public:
Donatien Garnier 0:ad9793cc5249 60
Donatien Garnier 0:ad9793cc5249 61 /**
Donatien Garnier 0:ad9793cc5249 62 * Construct an empty client process.
Donatien Garnier 0:ad9793cc5249 63 *
Donatien Garnier 0:ad9793cc5249 64 * The function start() shall be called to initiate the discovery process.
Donatien Garnier 0:ad9793cc5249 65 */
Donatien Garnier 0:ad9793cc5249 66 GattClientProcess() :
Donatien Garnier 0:ad9793cc5249 67 _client(NULL),
Donatien Garnier 0:ad9793cc5249 68 _connection_handle(),
Donatien Garnier 0:ad9793cc5249 69 _characteristics(NULL),
Donatien Garnier 0:ad9793cc5249 70 _it(NULL),
Donatien Garnier 0:ad9793cc5249 71 _descriptor_handle(0),
Donatien Garnier 0:ad9793cc5249 72 _ble_interface(NULL),
Donatien Garnier 0:ad9793cc5249 73 _event_queue(NULL) {
Donatien Garnier 0:ad9793cc5249 74 }
Donatien Garnier 0:ad9793cc5249 75
Donatien Garnier 0:ad9793cc5249 76 ~GattClientProcess()
Donatien Garnier 0:ad9793cc5249 77 {
Donatien Garnier 0:ad9793cc5249 78 stop();
Donatien Garnier 0:ad9793cc5249 79 }
Donatien Garnier 0:ad9793cc5249 80
Donatien Garnier 0:ad9793cc5249 81 void init(BLE &ble_interface, events::EventQueue &event_queue)
Donatien Garnier 0:ad9793cc5249 82 {
Donatien Garnier 0:ad9793cc5249 83 _ble_interface = &ble_interface;
Donatien Garnier 0:ad9793cc5249 84 _event_queue = &event_queue;
Donatien Garnier 0:ad9793cc5249 85 _client = &_ble_interface->gattClient();
Donatien Garnier 0:ad9793cc5249 86
Donatien Garnier 0:ad9793cc5249 87 _ble_interface->gap().onConnection(as_cb(&Self::on_connection));
Donatien Garnier 0:ad9793cc5249 88 _ble_interface->gap().onDisconnection(as_cb(&Self::on_disconnection));
Donatien Garnier 0:ad9793cc5249 89 }
Donatien Garnier 0:ad9793cc5249 90
Donatien Garnier 0:ad9793cc5249 91 /**
Donatien Garnier 0:ad9793cc5249 92 * Event handler invoked when a connection is established.
Donatien Garnier 0:ad9793cc5249 93 *
Donatien Garnier 0:ad9793cc5249 94 * This function setup the connection handle to operate on then start the
Donatien Garnier 0:ad9793cc5249 95 * discovery process.
Donatien Garnier 0:ad9793cc5249 96 */
Donatien Garnier 0:ad9793cc5249 97 void on_connection(const Gap::ConnectionCallbackParams_t* connection_event)
Donatien Garnier 0:ad9793cc5249 98 {
Donatien Garnier 0:ad9793cc5249 99 _connection_handle = connection_event->handle;
Donatien Garnier 0:ad9793cc5249 100 _event_queue->call(mbed::callback(this, &Self::start));
Donatien Garnier 0:ad9793cc5249 101 }
Donatien Garnier 0:ad9793cc5249 102
Donatien Garnier 0:ad9793cc5249 103 /**
Donatien Garnier 0:ad9793cc5249 104 * Start the discovery process.
Donatien Garnier 0:ad9793cc5249 105 *
Donatien Garnier 0:ad9793cc5249 106 * @param[in] client The GattClient instance which will discover the distant
Donatien Garnier 0:ad9793cc5249 107 * GATT server.
Donatien Garnier 0:ad9793cc5249 108 * @param[in] connection_handle Reference of the connection to the GATT
Donatien Garnier 0:ad9793cc5249 109 * server which will be discovered.
Donatien Garnier 0:ad9793cc5249 110 */
Donatien Garnier 0:ad9793cc5249 111 void start()
Donatien Garnier 0:ad9793cc5249 112 {
Donatien Garnier 0:ad9793cc5249 113 // setup the event handlers called during the process
Donatien Garnier 0:ad9793cc5249 114 _client->onDataWritten().add(as_cb(&Self::when_descriptor_written));
Donatien Garnier 0:ad9793cc5249 115 _client->onHVX().add(as_cb(&Self::when_characteristic_changed));
Donatien Garnier 0:ad9793cc5249 116
Donatien Garnier 0:ad9793cc5249 117 // The discovery process will invoke when_service_discovered when a
Donatien Garnier 0:ad9793cc5249 118 // service is discovered, when_characteristic_discovered when a
Donatien Garnier 0:ad9793cc5249 119 // characteristic is discovered and when_service_discovery_ends once the
Donatien Garnier 0:ad9793cc5249 120 // discovery process has ended.
Donatien Garnier 0:ad9793cc5249 121 _client->onServiceDiscoveryTermination(as_cb(&Self::when_service_discovery_ends));
Donatien Garnier 0:ad9793cc5249 122 ble_error_t error = _client->launchServiceDiscovery(
Donatien Garnier 0:ad9793cc5249 123 _connection_handle,
Donatien Garnier 0:ad9793cc5249 124 as_cb(&Self::when_service_discovered),
Donatien Garnier 0:ad9793cc5249 125 as_cb(&Self::when_characteristic_discovered)
Donatien Garnier 0:ad9793cc5249 126 );
Donatien Garnier 0:ad9793cc5249 127
Donatien Garnier 0:ad9793cc5249 128 if (error) {
Donatien Garnier 0:ad9793cc5249 129 printf("Error %u returned by _client->launchServiceDiscovery.\r\n", error);
Donatien Garnier 0:ad9793cc5249 130 return;
Donatien Garnier 0:ad9793cc5249 131 }
Donatien Garnier 0:ad9793cc5249 132
Donatien Garnier 0:ad9793cc5249 133 printf("Client process started: initiate service discovery.\r\n");
Donatien Garnier 0:ad9793cc5249 134 }
Donatien Garnier 0:ad9793cc5249 135
Donatien Garnier 0:ad9793cc5249 136 /**
Donatien Garnier 0:ad9793cc5249 137 * Stop the discovery process and clean the instance.
Donatien Garnier 0:ad9793cc5249 138 */
Donatien Garnier 0:ad9793cc5249 139 void on_disconnection(const Gap::DisconnectionCallbackParams_t* disconnection_event)
Donatien Garnier 0:ad9793cc5249 140 {
Donatien Garnier 0:ad9793cc5249 141 if (!_client || disconnection_event->handle != _connection_handle) {
Donatien Garnier 0:ad9793cc5249 142 return;
Donatien Garnier 0:ad9793cc5249 143 }
Donatien Garnier 0:ad9793cc5249 144 stop();
Donatien Garnier 0:ad9793cc5249 145 }
Donatien Garnier 0:ad9793cc5249 146
Donatien Garnier 0:ad9793cc5249 147 /**
Donatien Garnier 0:ad9793cc5249 148 * Stop the discovery process and clean the instance.
Donatien Garnier 0:ad9793cc5249 149 */
Donatien Garnier 0:ad9793cc5249 150 void stop()
Donatien Garnier 0:ad9793cc5249 151 {
Donatien Garnier 0:ad9793cc5249 152 if (!_client) {
Donatien Garnier 0:ad9793cc5249 153 return;
Donatien Garnier 0:ad9793cc5249 154 }
Donatien Garnier 0:ad9793cc5249 155
Donatien Garnier 0:ad9793cc5249 156 // unregister event handlers
Donatien Garnier 0:ad9793cc5249 157 _client->onDataWritten().detach(as_cb(&Self::when_descriptor_written));
Donatien Garnier 0:ad9793cc5249 158 _client->onHVX().detach(as_cb(&Self::when_characteristic_changed));
Donatien Garnier 0:ad9793cc5249 159 _client->onServiceDiscoveryTermination(NULL);
Donatien Garnier 0:ad9793cc5249 160
Donatien Garnier 0:ad9793cc5249 161 // remove discovered characteristics
Donatien Garnier 0:ad9793cc5249 162 clear_characteristics();
Donatien Garnier 0:ad9793cc5249 163
Donatien Garnier 0:ad9793cc5249 164 // clean up the instance
Donatien Garnier 0:ad9793cc5249 165 _connection_handle = 0;
Donatien Garnier 0:ad9793cc5249 166 _characteristics = NULL;
Donatien Garnier 0:ad9793cc5249 167 _it = NULL;
Donatien Garnier 0:ad9793cc5249 168 _descriptor_handle = 0;
Donatien Garnier 0:ad9793cc5249 169
Donatien Garnier 0:ad9793cc5249 170 printf("Client process stopped.\r\n");
Donatien Garnier 0:ad9793cc5249 171 }
Donatien Garnier 0:ad9793cc5249 172
Donatien Garnier 0:ad9793cc5249 173 private:
Donatien Garnier 0:ad9793cc5249 174 ////////////////////////////////////////////////////////////////////////////////
Donatien Garnier 0:ad9793cc5249 175 // Service and characteristic discovery process.
Donatien Garnier 0:ad9793cc5249 176
Donatien Garnier 0:ad9793cc5249 177 /**
Donatien Garnier 0:ad9793cc5249 178 * Handle services discovered.
Donatien Garnier 0:ad9793cc5249 179 *
Donatien Garnier 0:ad9793cc5249 180 * The GattClient invokes this function when a service has been discovered.
Donatien Garnier 0:ad9793cc5249 181 *
Donatien Garnier 0:ad9793cc5249 182 * @see GattClient::launchServiceDiscovery
Donatien Garnier 0:ad9793cc5249 183 */
Donatien Garnier 0:ad9793cc5249 184 void when_service_discovered(const DiscoveredService *discovered_service)
Donatien Garnier 0:ad9793cc5249 185 {
Donatien Garnier 0:ad9793cc5249 186 // print information of the service discovered
Donatien Garnier 0:ad9793cc5249 187 printf("Service discovered: value = ");
Donatien Garnier 0:ad9793cc5249 188 print_uuid(discovered_service->getUUID());
Donatien Garnier 0:ad9793cc5249 189 printf(", start = %u, end = %u.\r\n",
Donatien Garnier 0:ad9793cc5249 190 discovered_service->getStartHandle(),
Donatien Garnier 0:ad9793cc5249 191 discovered_service->getEndHandle()
Donatien Garnier 0:ad9793cc5249 192 );
Donatien Garnier 0:ad9793cc5249 193 }
Donatien Garnier 0:ad9793cc5249 194
Donatien Garnier 0:ad9793cc5249 195 /**
Donatien Garnier 0:ad9793cc5249 196 * Handle characteristics discovered.
Donatien Garnier 0:ad9793cc5249 197 *
Donatien Garnier 0:ad9793cc5249 198 * The GattClient invoke this function when a characteristic has been
Donatien Garnier 0:ad9793cc5249 199 * discovered.
Donatien Garnier 0:ad9793cc5249 200 *
Donatien Garnier 0:ad9793cc5249 201 * @see GattClient::launchServiceDiscovery
Donatien Garnier 0:ad9793cc5249 202 */
Donatien Garnier 0:ad9793cc5249 203 void when_characteristic_discovered(const DiscoveredCharacteristic *discovered_characteristic)
Donatien Garnier 0:ad9793cc5249 204 {
Donatien Garnier 0:ad9793cc5249 205 // print characteristics properties
Donatien Garnier 0:ad9793cc5249 206 printf("\tCharacteristic discovered: uuid = ");
Donatien Garnier 0:ad9793cc5249 207 print_uuid(discovered_characteristic->getUUID());
Donatien Garnier 0:ad9793cc5249 208 printf(", properties = ");
Donatien Garnier 0:ad9793cc5249 209 print_properties(discovered_characteristic->getProperties());
Donatien Garnier 0:ad9793cc5249 210 printf(
Donatien Garnier 0:ad9793cc5249 211 ", decl handle = %u, value handle = %u, last handle = %u.\r\n",
Donatien Garnier 0:ad9793cc5249 212 discovered_characteristic->getDeclHandle(),
Donatien Garnier 0:ad9793cc5249 213 discovered_characteristic->getValueHandle(),
Donatien Garnier 0:ad9793cc5249 214 discovered_characteristic->getLastHandle()
Donatien Garnier 0:ad9793cc5249 215 );
Donatien Garnier 0:ad9793cc5249 216
Donatien Garnier 0:ad9793cc5249 217 // add the characteristic into the list of discovered characteristics
Donatien Garnier 0:ad9793cc5249 218 bool success = add_characteristic(discovered_characteristic);
Donatien Garnier 0:ad9793cc5249 219 if (!success) {
Donatien Garnier 0:ad9793cc5249 220 printf("Error: memory allocation failure while adding the discovered characteristic.\r\n");
Donatien Garnier 0:ad9793cc5249 221 _client->terminateServiceDiscovery();
Donatien Garnier 0:ad9793cc5249 222 stop();
Donatien Garnier 0:ad9793cc5249 223 return;
Donatien Garnier 0:ad9793cc5249 224 }
Donatien Garnier 0:ad9793cc5249 225 }
Donatien Garnier 0:ad9793cc5249 226
Donatien Garnier 0:ad9793cc5249 227 /**
Donatien Garnier 0:ad9793cc5249 228 * Handle termination of the service and characteristic discovery process.
Donatien Garnier 0:ad9793cc5249 229 *
Donatien Garnier 0:ad9793cc5249 230 * The GattClient invokes this function when the service and characteristic
Donatien Garnier 0:ad9793cc5249 231 * discovery process ends.
Donatien Garnier 0:ad9793cc5249 232 *
Donatien Garnier 0:ad9793cc5249 233 * @see GattClient::onServiceDiscoveryTermination
Donatien Garnier 0:ad9793cc5249 234 */
Donatien Garnier 0:ad9793cc5249 235 void when_service_discovery_ends(Gap::Handle_t connection_handle)
Donatien Garnier 0:ad9793cc5249 236 {
Donatien Garnier 0:ad9793cc5249 237 if (!_characteristics) {
Donatien Garnier 0:ad9793cc5249 238 printf("No characteristics discovered, end of the process.\r\n");
Donatien Garnier 0:ad9793cc5249 239 return;
Donatien Garnier 0:ad9793cc5249 240 }
Donatien Garnier 0:ad9793cc5249 241
Donatien Garnier 0:ad9793cc5249 242 printf("All services and characteristics discovered, process them.\r\n");
Donatien Garnier 0:ad9793cc5249 243
Donatien Garnier 0:ad9793cc5249 244 // reset iterator and start processing characteristics in order
Donatien Garnier 0:ad9793cc5249 245 _it = NULL;
Donatien Garnier 0:ad9793cc5249 246 _event_queue->call(mbed::callback(this, &Self::process_next_characteristic));
Donatien Garnier 0:ad9793cc5249 247 }
Donatien Garnier 0:ad9793cc5249 248
Donatien Garnier 0:ad9793cc5249 249 ////////////////////////////////////////////////////////////////////////////////
Donatien Garnier 0:ad9793cc5249 250 // Processing of characteristics based on their properties.
Donatien Garnier 0:ad9793cc5249 251
Donatien Garnier 0:ad9793cc5249 252 /**
Donatien Garnier 0:ad9793cc5249 253 * Process the characteristics discovered.
Donatien Garnier 0:ad9793cc5249 254 *
Donatien Garnier 0:ad9793cc5249 255 * - If the characteristic is readable then read its value and print it. Then
Donatien Garnier 0:ad9793cc5249 256 * - If the characteristic can emit notification or indication then discover
Donatien Garnier 0:ad9793cc5249 257 * the characteristic CCCD and subscribe to the server initiated event.
Donatien Garnier 0:ad9793cc5249 258 * - Otherwise skip the characteristic processing.
Donatien Garnier 0:ad9793cc5249 259 */
Donatien Garnier 0:ad9793cc5249 260 void process_next_characteristic(void)
Donatien Garnier 0:ad9793cc5249 261 {
Donatien Garnier 0:ad9793cc5249 262 if (!_it) {
Donatien Garnier 0:ad9793cc5249 263 _it = _characteristics;
Donatien Garnier 0:ad9793cc5249 264 } else {
Donatien Garnier 0:ad9793cc5249 265 _it = _it->next;
Donatien Garnier 0:ad9793cc5249 266 }
Donatien Garnier 0:ad9793cc5249 267
Donatien Garnier 0:ad9793cc5249 268 while (_it) {
Donatien Garnier 0:ad9793cc5249 269 Properties_t properties = _it->value.getProperties();
Donatien Garnier 0:ad9793cc5249 270
Donatien Garnier 0:ad9793cc5249 271 if (properties.read()) {
Donatien Garnier 0:ad9793cc5249 272 read_characteristic(_it->value);
Donatien Garnier 0:ad9793cc5249 273 return;
Donatien Garnier 0:ad9793cc5249 274 } else if(properties.notify() || properties.indicate()) {
Donatien Garnier 0:ad9793cc5249 275 discover_descriptors(_it->value);
Donatien Garnier 0:ad9793cc5249 276 return;
Donatien Garnier 0:ad9793cc5249 277 } else {
Donatien Garnier 0:ad9793cc5249 278 printf(
Donatien Garnier 0:ad9793cc5249 279 "Skip processing of characteristic %u\r\n",
Donatien Garnier 0:ad9793cc5249 280 _it->value.getValueHandle()
Donatien Garnier 0:ad9793cc5249 281 );
Donatien Garnier 0:ad9793cc5249 282 _it = _it->next;
Donatien Garnier 0:ad9793cc5249 283 }
Donatien Garnier 0:ad9793cc5249 284 }
Donatien Garnier 0:ad9793cc5249 285
Donatien Garnier 0:ad9793cc5249 286 printf("All characteristics discovered have been processed.\r\n");
Donatien Garnier 0:ad9793cc5249 287 }
Donatien Garnier 0:ad9793cc5249 288
Donatien Garnier 0:ad9793cc5249 289 /**
Donatien Garnier 0:ad9793cc5249 290 * Initate the read of the characteristic in input.
Donatien Garnier 0:ad9793cc5249 291 *
Donatien Garnier 0:ad9793cc5249 292 * The completion of the operation will happens in when_characteristic_read()
Donatien Garnier 0:ad9793cc5249 293 */
Donatien Garnier 0:ad9793cc5249 294 void read_characteristic(const DiscoveredCharacteristic &characteristic)
Donatien Garnier 0:ad9793cc5249 295 {
Donatien Garnier 0:ad9793cc5249 296 printf("Initiating read at %u.\r\n", characteristic.getValueHandle());
Donatien Garnier 0:ad9793cc5249 297 ble_error_t error = characteristic.read(
Donatien Garnier 0:ad9793cc5249 298 0, as_cb(&Self::when_characteristic_read)
Donatien Garnier 0:ad9793cc5249 299 );
Donatien Garnier 0:ad9793cc5249 300
Donatien Garnier 0:ad9793cc5249 301 if (error) {
Donatien Garnier 0:ad9793cc5249 302 printf(
Donatien Garnier 0:ad9793cc5249 303 "Error: cannot initiate read at %u due to %u\r\n",
Donatien Garnier 0:ad9793cc5249 304 characteristic.getValueHandle(), error
Donatien Garnier 0:ad9793cc5249 305 );
Donatien Garnier 0:ad9793cc5249 306 stop();
Donatien Garnier 0:ad9793cc5249 307 }
Donatien Garnier 0:ad9793cc5249 308 }
Donatien Garnier 0:ad9793cc5249 309
Donatien Garnier 0:ad9793cc5249 310 /**
Donatien Garnier 0:ad9793cc5249 311 * Handle the reception of a read response.
Donatien Garnier 0:ad9793cc5249 312 *
Donatien Garnier 0:ad9793cc5249 313 * If the characteristic can emit notification or indication then start the
Donatien Garnier 0:ad9793cc5249 314 * discovery of the the characteristic descriptors then subscribe to the
Donatien Garnier 0:ad9793cc5249 315 * server initiated event by writing the CCCD discovered. Otherwise start
Donatien Garnier 0:ad9793cc5249 316 * the processing of the next characteristic discovered in the server.
Donatien Garnier 0:ad9793cc5249 317 */
Donatien Garnier 0:ad9793cc5249 318 void when_characteristic_read(const GattReadCallbackParams *read_event)
Donatien Garnier 0:ad9793cc5249 319 {
Donatien Garnier 0:ad9793cc5249 320 printf("\tCharacteristic value at %u equal to: ", read_event->handle);
Donatien Garnier 0:ad9793cc5249 321 for (size_t i = 0; i < read_event->len; ++i) {
Donatien Garnier 0:ad9793cc5249 322 printf("0x%02X ", read_event->data[i]);
Donatien Garnier 0:ad9793cc5249 323 }
Donatien Garnier 0:ad9793cc5249 324 printf(".\r\n");
Donatien Garnier 0:ad9793cc5249 325
Donatien Garnier 0:ad9793cc5249 326 Properties_t properties = _it->value.getProperties();
Donatien Garnier 0:ad9793cc5249 327
Donatien Garnier 0:ad9793cc5249 328 if(properties.notify() || properties.indicate()) {
Donatien Garnier 0:ad9793cc5249 329 discover_descriptors(_it->value);
Donatien Garnier 0:ad9793cc5249 330 } else {
Donatien Garnier 0:ad9793cc5249 331 process_next_characteristic();
Donatien Garnier 0:ad9793cc5249 332 }
Donatien Garnier 0:ad9793cc5249 333 }
Donatien Garnier 0:ad9793cc5249 334
Donatien Garnier 0:ad9793cc5249 335 /**
Donatien Garnier 0:ad9793cc5249 336 * Initiate the discovery of the descriptors of the characteristic in input.
Donatien Garnier 0:ad9793cc5249 337 *
Donatien Garnier 0:ad9793cc5249 338 * When a descriptor is discovered, the function when_descriptor_discovered
Donatien Garnier 0:ad9793cc5249 339 * is invoked.
Donatien Garnier 0:ad9793cc5249 340 */
Donatien Garnier 0:ad9793cc5249 341 void discover_descriptors(const DiscoveredCharacteristic &characteristic)
Donatien Garnier 0:ad9793cc5249 342 {
Donatien Garnier 0:ad9793cc5249 343 printf("Initiating descriptor discovery of %u.\r\n", characteristic.getValueHandle());
Donatien Garnier 0:ad9793cc5249 344
Donatien Garnier 0:ad9793cc5249 345 _descriptor_handle = 0;
Donatien Garnier 0:ad9793cc5249 346 ble_error_t error = characteristic.discoverDescriptors(
Donatien Garnier 0:ad9793cc5249 347 as_cb(&Self::when_descriptor_discovered),
Donatien Garnier 0:ad9793cc5249 348 as_cb(&Self::when_descriptor_discovery_ends)
Donatien Garnier 0:ad9793cc5249 349 );
Donatien Garnier 0:ad9793cc5249 350
Donatien Garnier 0:ad9793cc5249 351 if (error) {
Donatien Garnier 0:ad9793cc5249 352 printf(
Donatien Garnier 0:ad9793cc5249 353 "Error: cannot initiate discovery of %04X due to %u.\r\n",
Donatien Garnier 0:ad9793cc5249 354 characteristic.getValueHandle(), error
Donatien Garnier 0:ad9793cc5249 355 );
Donatien Garnier 0:ad9793cc5249 356 stop();
Donatien Garnier 0:ad9793cc5249 357 }
Donatien Garnier 0:ad9793cc5249 358 }
Donatien Garnier 0:ad9793cc5249 359
Donatien Garnier 0:ad9793cc5249 360 /**
Donatien Garnier 0:ad9793cc5249 361 * Handle the discovery of the characteristic descriptors.
Donatien Garnier 0:ad9793cc5249 362 *
Donatien Garnier 0:ad9793cc5249 363 * If the descriptor found is a CCCD then stop the discovery. Once the
Donatien Garnier 0:ad9793cc5249 364 * process has ended subscribe to server initiated events by writing the
Donatien Garnier 0:ad9793cc5249 365 * value of the CCCD.
Donatien Garnier 0:ad9793cc5249 366 */
Donatien Garnier 0:ad9793cc5249 367 void when_descriptor_discovered(const DiscoveryCallbackParams_t* event)
Donatien Garnier 0:ad9793cc5249 368 {
Donatien Garnier 0:ad9793cc5249 369 printf("\tDescriptor discovered at %u, UUID: ", event->descriptor.getAttributeHandle());
Donatien Garnier 0:ad9793cc5249 370 print_uuid(event->descriptor.getUUID());
Donatien Garnier 0:ad9793cc5249 371 printf(".\r\n");
Donatien Garnier 0:ad9793cc5249 372
Donatien Garnier 0:ad9793cc5249 373 if (event->descriptor.getUUID() == BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG) {
Donatien Garnier 0:ad9793cc5249 374 _descriptor_handle = event->descriptor.getAttributeHandle();
Donatien Garnier 0:ad9793cc5249 375 _client->terminateCharacteristicDescriptorDiscovery(
Donatien Garnier 0:ad9793cc5249 376 event->characteristic
Donatien Garnier 0:ad9793cc5249 377 );
Donatien Garnier 0:ad9793cc5249 378 }
Donatien Garnier 0:ad9793cc5249 379 }
Donatien Garnier 0:ad9793cc5249 380
Donatien Garnier 0:ad9793cc5249 381 /**
Donatien Garnier 0:ad9793cc5249 382 * If a CCCD has been found subscribe to server initiated events by writing
Donatien Garnier 0:ad9793cc5249 383 * its value.
Donatien Garnier 0:ad9793cc5249 384 */
Donatien Garnier 0:ad9793cc5249 385 void when_descriptor_discovery_ends(const TerminationCallbackParams_t *event) {
Donatien Garnier 0:ad9793cc5249 386 // shall never happen but happen with android devices ...
Donatien Garnier 0:ad9793cc5249 387 // process the next charateristic
Donatien Garnier 0:ad9793cc5249 388 if (!_descriptor_handle) {
Donatien Garnier 0:ad9793cc5249 389 printf("\tWarning: characteristic with notify or indicate attribute without CCCD.\r\n");
Donatien Garnier 0:ad9793cc5249 390 process_next_characteristic();
Donatien Garnier 0:ad9793cc5249 391 return;
Donatien Garnier 0:ad9793cc5249 392 }
Donatien Garnier 0:ad9793cc5249 393
Donatien Garnier 0:ad9793cc5249 394 Properties_t properties = _it->value.getProperties();
Donatien Garnier 0:ad9793cc5249 395
Donatien Garnier 0:ad9793cc5249 396 uint16_t cccd_value =
Donatien Garnier 0:ad9793cc5249 397 (properties.notify() << 0) | (properties.indicate() << 1);
Donatien Garnier 0:ad9793cc5249 398
Donatien Garnier 0:ad9793cc5249 399 ble_error_t error = _client->write(
Donatien Garnier 0:ad9793cc5249 400 GattClient::GATT_OP_WRITE_REQ,
Donatien Garnier 0:ad9793cc5249 401 _connection_handle,
Donatien Garnier 0:ad9793cc5249 402 _descriptor_handle,
Donatien Garnier 0:ad9793cc5249 403 sizeof(cccd_value),
Donatien Garnier 0:ad9793cc5249 404 reinterpret_cast<uint8_t*>(&cccd_value)
Donatien Garnier 0:ad9793cc5249 405 );
Donatien Garnier 0:ad9793cc5249 406
Donatien Garnier 0:ad9793cc5249 407 if (error) {
Donatien Garnier 0:ad9793cc5249 408 printf(
Donatien Garnier 0:ad9793cc5249 409 "Error: cannot initiate write of CCCD %u due to %u.\r\n",
Donatien Garnier 0:ad9793cc5249 410 _descriptor_handle, error
Donatien Garnier 0:ad9793cc5249 411 );
Donatien Garnier 0:ad9793cc5249 412 stop();
Donatien Garnier 0:ad9793cc5249 413 }
Donatien Garnier 0:ad9793cc5249 414 }
Donatien Garnier 0:ad9793cc5249 415
Donatien Garnier 0:ad9793cc5249 416 /**
Donatien Garnier 0:ad9793cc5249 417 * Called when the CCCD has been written.
Donatien Garnier 0:ad9793cc5249 418 */
Donatien Garnier 0:ad9793cc5249 419 void when_descriptor_written(const GattWriteCallbackParams* event)
Donatien Garnier 0:ad9793cc5249 420 {
Donatien Garnier 0:ad9793cc5249 421 // should never happen
Donatien Garnier 0:ad9793cc5249 422 if (!_descriptor_handle) {
Donatien Garnier 0:ad9793cc5249 423 printf("\tError: received write response to unsolicited request.\r\n");
Donatien Garnier 0:ad9793cc5249 424 stop();
Donatien Garnier 0:ad9793cc5249 425 return;
Donatien Garnier 0:ad9793cc5249 426 }
Donatien Garnier 0:ad9793cc5249 427
Donatien Garnier 0:ad9793cc5249 428 printf("\tCCCD at %u written.\r\n", _descriptor_handle);
Donatien Garnier 0:ad9793cc5249 429 _descriptor_handle = 0;
Donatien Garnier 0:ad9793cc5249 430 process_next_characteristic();
Donatien Garnier 0:ad9793cc5249 431 }
Donatien Garnier 0:ad9793cc5249 432
Donatien Garnier 0:ad9793cc5249 433 /**
Donatien Garnier 0:ad9793cc5249 434 * Print the updated value of the characteristic.
Donatien Garnier 0:ad9793cc5249 435 *
Donatien Garnier 0:ad9793cc5249 436 * This function is called when the server emits a notification or an
Donatien Garnier 0:ad9793cc5249 437 * indication of a characteristic value the client has subscribed to.
Donatien Garnier 0:ad9793cc5249 438 *
Donatien Garnier 0:ad9793cc5249 439 * @see GattClient::onHVX()
Donatien Garnier 0:ad9793cc5249 440 */
Donatien Garnier 0:ad9793cc5249 441 void when_characteristic_changed(const GattHVXCallbackParams* event)
Donatien Garnier 0:ad9793cc5249 442 {
Donatien Garnier 0:ad9793cc5249 443 printf("Change on attribute %u: new value = ", event->handle);
Donatien Garnier 0:ad9793cc5249 444 for (size_t i = 0; i < event->len; ++i) {
Donatien Garnier 0:ad9793cc5249 445 printf("0x%02X ", event->data[i]);
Donatien Garnier 0:ad9793cc5249 446 }
Donatien Garnier 0:ad9793cc5249 447 printf(".\r\n");
Donatien Garnier 0:ad9793cc5249 448 }
Donatien Garnier 0:ad9793cc5249 449
Donatien Garnier 0:ad9793cc5249 450 struct DiscoveredCharacteristicNode {
Donatien Garnier 0:ad9793cc5249 451 DiscoveredCharacteristicNode(const DiscoveredCharacteristic &c) :
Donatien Garnier 0:ad9793cc5249 452 value(c), next(NULL) { }
Donatien Garnier 0:ad9793cc5249 453
Donatien Garnier 0:ad9793cc5249 454 DiscoveredCharacteristic value;
Donatien Garnier 0:ad9793cc5249 455 DiscoveredCharacteristicNode *next;
Donatien Garnier 0:ad9793cc5249 456 };
Donatien Garnier 0:ad9793cc5249 457
Donatien Garnier 0:ad9793cc5249 458 /**
Donatien Garnier 0:ad9793cc5249 459 * Add a discovered characteristic into the list of discovered characteristics.
Donatien Garnier 0:ad9793cc5249 460 */
Donatien Garnier 0:ad9793cc5249 461 bool add_characteristic(const DiscoveredCharacteristic *characteristic)
Donatien Garnier 0:ad9793cc5249 462 {
Donatien Garnier 0:ad9793cc5249 463 DiscoveredCharacteristicNode* new_node =
Donatien Garnier 0:ad9793cc5249 464 new(std::nothrow) DiscoveredCharacteristicNode(*characteristic);
Donatien Garnier 0:ad9793cc5249 465
Donatien Garnier 0:ad9793cc5249 466 if (new_node == false) {
Donatien Garnier 0:ad9793cc5249 467 printf("Error while allocating a new characteristic.\r\n");
Donatien Garnier 0:ad9793cc5249 468 return false;
Donatien Garnier 0:ad9793cc5249 469 }
Donatien Garnier 0:ad9793cc5249 470
Donatien Garnier 0:ad9793cc5249 471 if (_characteristics == NULL) {
Donatien Garnier 0:ad9793cc5249 472 _characteristics = new_node;
Donatien Garnier 0:ad9793cc5249 473 } else {
Donatien Garnier 0:ad9793cc5249 474 DiscoveredCharacteristicNode* c = _characteristics;
Donatien Garnier 0:ad9793cc5249 475 while(c->next) {
Donatien Garnier 0:ad9793cc5249 476 c = c->next;
Donatien Garnier 0:ad9793cc5249 477 }
Donatien Garnier 0:ad9793cc5249 478 c->next = new_node;
Donatien Garnier 0:ad9793cc5249 479 }
Donatien Garnier 0:ad9793cc5249 480
Donatien Garnier 0:ad9793cc5249 481 return true;
Donatien Garnier 0:ad9793cc5249 482 }
Donatien Garnier 0:ad9793cc5249 483
Donatien Garnier 0:ad9793cc5249 484 /**
Donatien Garnier 0:ad9793cc5249 485 * Clear the list of discovered characteristics.
Donatien Garnier 0:ad9793cc5249 486 */
Donatien Garnier 0:ad9793cc5249 487 void clear_characteristics(void)
Donatien Garnier 0:ad9793cc5249 488 {
Donatien Garnier 0:ad9793cc5249 489 DiscoveredCharacteristicNode *c= _characteristics;
Donatien Garnier 0:ad9793cc5249 490
Donatien Garnier 0:ad9793cc5249 491 while (c) {
Donatien Garnier 0:ad9793cc5249 492 DiscoveredCharacteristicNode *n = c->next;
Donatien Garnier 0:ad9793cc5249 493 delete c;
Donatien Garnier 0:ad9793cc5249 494 c = n;
Donatien Garnier 0:ad9793cc5249 495 }
Donatien Garnier 0:ad9793cc5249 496 }
Donatien Garnier 0:ad9793cc5249 497
Donatien Garnier 0:ad9793cc5249 498 /**
Donatien Garnier 0:ad9793cc5249 499 * Helper to construct an event handler from a member function of this
Donatien Garnier 0:ad9793cc5249 500 * instance.
Donatien Garnier 0:ad9793cc5249 501 */
Donatien Garnier 0:ad9793cc5249 502 template<typename ContextType>
Donatien Garnier 0:ad9793cc5249 503 FunctionPointerWithContext<ContextType> as_cb(
Donatien Garnier 0:ad9793cc5249 504 void (Self::*member)(ContextType context)
Donatien Garnier 0:ad9793cc5249 505 ) {
Donatien Garnier 0:ad9793cc5249 506 return makeFunctionPointer(this, member);
Donatien Garnier 0:ad9793cc5249 507 }
Donatien Garnier 0:ad9793cc5249 508
Donatien Garnier 0:ad9793cc5249 509 /**
Donatien Garnier 0:ad9793cc5249 510 * Print the value of a UUID.
Donatien Garnier 0:ad9793cc5249 511 */
Donatien Garnier 0:ad9793cc5249 512 static void print_uuid(const UUID &uuid)
Donatien Garnier 0:ad9793cc5249 513 {
Donatien Garnier 0:ad9793cc5249 514 const uint8_t *uuid_value = uuid.getBaseUUID();
Donatien Garnier 0:ad9793cc5249 515
Donatien Garnier 0:ad9793cc5249 516 // UUIDs are in little endian, print them in big endian
Donatien Garnier 0:ad9793cc5249 517 for (size_t i = 0; i < uuid.getLen(); ++i) {
Donatien Garnier 0:ad9793cc5249 518 printf("%02X", uuid_value[(uuid.getLen() - 1) - i]);
Donatien Garnier 0:ad9793cc5249 519 }
Donatien Garnier 0:ad9793cc5249 520 }
Donatien Garnier 0:ad9793cc5249 521
Donatien Garnier 0:ad9793cc5249 522 /**
Donatien Garnier 0:ad9793cc5249 523 * Print the value of a characteristic properties.
Donatien Garnier 0:ad9793cc5249 524 */
Donatien Garnier 0:ad9793cc5249 525 static void print_properties(const Properties_t &properties)
Donatien Garnier 0:ad9793cc5249 526 {
Donatien Garnier 0:ad9793cc5249 527 const struct {
Donatien Garnier 0:ad9793cc5249 528 bool (Properties_t::*fn)() const;
Donatien Garnier 0:ad9793cc5249 529 const char* str;
Donatien Garnier 0:ad9793cc5249 530 } prop_to_str[] = {
Donatien Garnier 0:ad9793cc5249 531 { &Properties_t::broadcast, "broadcast" },
Donatien Garnier 0:ad9793cc5249 532 { &Properties_t::read, "read" },
Donatien Garnier 0:ad9793cc5249 533 { &Properties_t::writeWoResp, "writeWoResp" },
Donatien Garnier 0:ad9793cc5249 534 { &Properties_t::write, "write" },
Donatien Garnier 0:ad9793cc5249 535 { &Properties_t::notify, "notify" },
Donatien Garnier 0:ad9793cc5249 536 { &Properties_t::indicate, "indicate" },
Donatien Garnier 0:ad9793cc5249 537 { &Properties_t::authSignedWrite, "authSignedWrite" }
Donatien Garnier 0:ad9793cc5249 538 };
Donatien Garnier 0:ad9793cc5249 539
Donatien Garnier 0:ad9793cc5249 540 printf("[");
Donatien Garnier 0:ad9793cc5249 541 for (size_t i = 0; i < (sizeof(prop_to_str) / sizeof(prop_to_str[0])); ++i) {
Donatien Garnier 0:ad9793cc5249 542 if ((properties.*(prop_to_str[i].fn))()) {
Donatien Garnier 0:ad9793cc5249 543 printf(" %s", prop_to_str[i].str);
Donatien Garnier 0:ad9793cc5249 544 }
Donatien Garnier 0:ad9793cc5249 545 }
Donatien Garnier 0:ad9793cc5249 546 printf(" ]");
Donatien Garnier 0:ad9793cc5249 547 }
Donatien Garnier 0:ad9793cc5249 548
Donatien Garnier 0:ad9793cc5249 549 GattClient *_client;
Donatien Garnier 0:ad9793cc5249 550 Gap::Handle_t _connection_handle;
Donatien Garnier 0:ad9793cc5249 551 DiscoveredCharacteristicNode *_characteristics;
Donatien Garnier 0:ad9793cc5249 552 DiscoveredCharacteristicNode *_it;
Donatien Garnier 0:ad9793cc5249 553 GattAttribute::Handle_t _descriptor_handle;
Donatien Garnier 0:ad9793cc5249 554 BLE *_ble_interface;
Donatien Garnier 0:ad9793cc5249 555 events::EventQueue *_event_queue;
Donatien Garnier 0:ad9793cc5249 556 };
Donatien Garnier 0:ad9793cc5249 557
Donatien Garnier 0:ad9793cc5249 558
Donatien Garnier 0:ad9793cc5249 559 int main() {
Donatien Garnier 0:ad9793cc5249 560
Donatien Garnier 0:ad9793cc5249 561 BLE &ble_interface = BLE::Instance();
Donatien Garnier 0:ad9793cc5249 562 events::EventQueue event_queue;
Donatien Garnier 0:ad9793cc5249 563 BLEProcess ble_process(event_queue, ble_interface);
Donatien Garnier 0:ad9793cc5249 564 GattClientProcess gatt_client_process;
Donatien Garnier 0:ad9793cc5249 565
Donatien Garnier 0:ad9793cc5249 566 // Register GattClientProcess::init in the ble_process; this function will
Donatien Garnier 0:ad9793cc5249 567 // be called once the ble_interface is initialized.
Donatien Garnier 0:ad9793cc5249 568 ble_process.on_init(
Donatien Garnier 0:ad9793cc5249 569 mbed::callback(&gatt_client_process, &GattClientProcess::init)
Donatien Garnier 0:ad9793cc5249 570 );
Donatien Garnier 0:ad9793cc5249 571
Donatien Garnier 0:ad9793cc5249 572 // bind the event queue to the ble interface, initialize the interface
Donatien Garnier 0:ad9793cc5249 573 // and start advertising
Donatien Garnier 0:ad9793cc5249 574 ble_process.start();
Donatien Garnier 0:ad9793cc5249 575
Donatien Garnier 0:ad9793cc5249 576 // Process the event queue.
Donatien Garnier 0:ad9793cc5249 577 event_queue.dispatch_forever();
Donatien Garnier 0:ad9793cc5249 578
Donatien Garnier 0:ad9793cc5249 579 return 0;
Donatien Garnier 0:ad9793cc5249 580 }