mbed-os-examples
/
mbed-os-example-ble-GattClient
This application demonstrates detailed uses of the GattClient APIs.
Embed:
(wiki syntax)
Show/hide line numbers
main.cpp
00001 00002 /* mbed Microcontroller Library 00003 * Copyright (c) 2006-2018 ARM Limited 00004 * 00005 * Licensed under the Apache License, Version 2.0 (the "License"); 00006 * you may not use this file except in compliance with the License. 00007 * You may obtain a copy of the License at 00008 * 00009 * http://www.apache.org/licenses/LICENSE-2.0 00010 * 00011 * Unless required by applicable law or agreed to in writing, software 00012 * distributed under the License is distributed on an "AS IS" BASIS, 00013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00014 * See the License for the specific language governing permissions and 00015 * limitations under the License. 00016 */ 00017 00018 #include <memory> 00019 #include <new> 00020 #include <stdio.h> 00021 00022 #include "events/EventQueue.h" 00023 #include "platform/NonCopyable.h" 00024 00025 #include "ble/BLE.h" 00026 #include "ble/Gap.h" 00027 #include "ble/GattClient.h" 00028 #include "ble/GapAdvertisingParams.h" 00029 #include "ble/GapAdvertisingData.h" 00030 #include "ble/GattClient.h" 00031 #include "ble/DiscoveredService.h" 00032 #include "ble/DiscoveredCharacteristic.h" 00033 #include "ble/CharacteristicDescriptorDiscovery.h" 00034 00035 #include "BLEProcess.h" 00036 00037 /** 00038 * Handle discovery of the GATT server. 00039 * 00040 * First the GATT server is discovered in its entirety then each readable 00041 * characteristic is read and the client register to characteristic 00042 * notifications or indication when available. The client report server 00043 * indications and notification until the connection end. 00044 */ 00045 class GattClientProcess : private mbed::NonCopyable<GattClientProcess>, 00046 public ble::Gap::EventHandler, 00047 public GattClient::EventHandler { 00048 00049 // Internal typedef to this class type. 00050 // It is used as a shorthand to pass member function as callbacks. 00051 typedef GattClientProcess Self; 00052 00053 typedef CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t 00054 DiscoveryCallbackParams_t; 00055 00056 typedef CharacteristicDescriptorDiscovery::TerminationCallbackParams_t 00057 TerminationCallbackParams_t; 00058 00059 typedef DiscoveredCharacteristic::Properties_t Properties_t; 00060 00061 public: 00062 00063 /** 00064 * Construct an empty client process. 00065 * 00066 * The function start() shall be called to initiate the discovery process. 00067 */ 00068 GattClientProcess() : 00069 _client(NULL), 00070 _connection_handle(), 00071 _characteristics(NULL), 00072 _it(NULL), 00073 _descriptor_handle(0), 00074 _ble_interface(NULL), 00075 _event_queue(NULL) { 00076 } 00077 00078 ~GattClientProcess() 00079 { 00080 stop(); 00081 } 00082 00083 void init(BLE &ble_interface, events::EventQueue &event_queue) 00084 { 00085 _ble_interface = &ble_interface; 00086 _event_queue = &event_queue; 00087 _client = &_ble_interface->gattClient(); 00088 00089 _ble_interface->gap().setEventHandler(this); 00090 } 00091 00092 /** 00093 * Start the discovery process. 00094 * 00095 * @param[in] client The GattClient instance which will discover the distant 00096 * GATT server. 00097 * @param[in] connection_handle Reference of the connection to the GATT 00098 * server which will be discovered. 00099 */ 00100 void start() 00101 { 00102 // setup the event handlers called during the process 00103 _client->onDataWritten().add(as_cb(&Self::when_descriptor_written)); 00104 _client->onHVX().add(as_cb(&Self::when_characteristic_changed)); 00105 00106 // The discovery process will invoke when_service_discovered when a 00107 // service is discovered, when_characteristic_discovered when a 00108 // characteristic is discovered and when_service_discovery_ends once the 00109 // discovery process has ended. 00110 _client->onServiceDiscoveryTermination(as_cb(&Self::when_service_discovery_ends)); 00111 ble_error_t error = _client->launchServiceDiscovery( 00112 _connection_handle, 00113 as_cb(&Self::when_service_discovered), 00114 as_cb(&Self::when_characteristic_discovered) 00115 ); 00116 00117 if (error) { 00118 printf("Error %u returned by _client->launchServiceDiscovery.\r\n", error); 00119 return; 00120 } 00121 00122 // register as a handler for GattClient events 00123 _client->setEventHandler(this); 00124 00125 // this might not result in a new value but if it does we will be informed through 00126 // an call in the event handler we just registered 00127 _client->negotiateAttMtu(_connection_handle); 00128 00129 printf("Client process started: initiate service discovery.\r\n"); 00130 } 00131 00132 /** 00133 * Stop the discovery process and clean the instance. 00134 */ 00135 void stop() 00136 { 00137 if (!_client) { 00138 return; 00139 } 00140 00141 // unregister event handlers 00142 _client->onDataWritten().detach(as_cb(&Self::when_descriptor_written)); 00143 _client->onHVX().detach(as_cb(&Self::when_characteristic_changed)); 00144 _client->onServiceDiscoveryTermination(NULL); 00145 00146 // remove discovered characteristics 00147 clear_characteristics(); 00148 00149 // clean up the instance 00150 _connection_handle = 0; 00151 _characteristics = NULL; 00152 _it = NULL; 00153 _descriptor_handle = 0; 00154 00155 printf("Client process stopped.\r\n"); 00156 } 00157 00158 private: 00159 /** 00160 * Event handler invoked when a connection is established. 00161 * 00162 * This function setup the connection handle to operate on then start the 00163 * discovery process. 00164 */ 00165 virtual void onConnectionComplete(const ble::ConnectionCompleteEvent &event) 00166 { 00167 _connection_handle = event.getConnectionHandle(); 00168 _event_queue->call(mbed::callback(this, &Self::start)); 00169 } 00170 00171 /** 00172 * Stop the discovery process and clean the instance. 00173 */ 00174 virtual void onDisconnectionComplete(const ble::DisconnectionCompleteEvent &event) 00175 { 00176 if (_client && event.getConnectionHandle() == _connection_handle) { 00177 stop(); 00178 } 00179 } 00180 00181 /** 00182 * Implementation of GattClient::EventHandler::onAttMtuChange event 00183 */ 00184 virtual void onAttMtuChange( 00185 ble::connection_handle_t connectionHandle, 00186 uint16_t attMtuSize 00187 ) { 00188 printf( 00189 "ATT_MTU changed on the connection %d to a new value of %d.\r\n", 00190 connectionHandle, 00191 attMtuSize 00192 /* maximum size of an attribute written in a single operation is one less */ 00193 ); 00194 } 00195 00196 private: 00197 //////////////////////////////////////////////////////////////////////////////// 00198 // Service and characteristic discovery process. 00199 00200 /** 00201 * Handle services discovered. 00202 * 00203 * The GattClient invokes this function when a service has been discovered. 00204 * 00205 * @see GattClient::launchServiceDiscovery 00206 */ 00207 void when_service_discovered(const DiscoveredService *discovered_service) 00208 { 00209 // print information of the service discovered 00210 printf("Service discovered: value = "); 00211 print_uuid(discovered_service->getUUID()); 00212 printf(", start = %u, end = %u.\r\n", 00213 discovered_service->getStartHandle(), 00214 discovered_service->getEndHandle() 00215 ); 00216 } 00217 00218 /** 00219 * Handle characteristics discovered. 00220 * 00221 * The GattClient invoke this function when a characteristic has been 00222 * discovered. 00223 * 00224 * @see GattClient::launchServiceDiscovery 00225 */ 00226 void when_characteristic_discovered(const DiscoveredCharacteristic *discovered_characteristic) 00227 { 00228 // print characteristics properties 00229 printf("\tCharacteristic discovered: uuid = "); 00230 print_uuid(discovered_characteristic->getUUID()); 00231 printf(", properties = "); 00232 print_properties(discovered_characteristic->getProperties()); 00233 printf( 00234 ", decl handle = %u, value handle = %u, last handle = %u.\r\n", 00235 discovered_characteristic->getDeclHandle(), 00236 discovered_characteristic->getValueHandle(), 00237 discovered_characteristic->getLastHandle() 00238 ); 00239 00240 // add the characteristic into the list of discovered characteristics 00241 bool success = add_characteristic(discovered_characteristic); 00242 if (!success) { 00243 printf("Error: memory allocation failure while adding the discovered characteristic.\r\n"); 00244 _client->terminateServiceDiscovery(); 00245 stop(); 00246 return; 00247 } 00248 } 00249 00250 /** 00251 * Handle termination of the service and characteristic discovery process. 00252 * 00253 * The GattClient invokes this function when the service and characteristic 00254 * discovery process ends. 00255 * 00256 * @see GattClient::onServiceDiscoveryTermination 00257 */ 00258 void when_service_discovery_ends(Gap::Handle_t connection_handle) 00259 { 00260 if (!_characteristics) { 00261 printf("No characteristics discovered, end of the process.\r\n"); 00262 return; 00263 } 00264 00265 printf("All services and characteristics discovered, process them.\r\n"); 00266 00267 // reset iterator and start processing characteristics in order 00268 _it = NULL; 00269 _event_queue->call(mbed::callback(this, &Self::process_next_characteristic)); 00270 } 00271 00272 //////////////////////////////////////////////////////////////////////////////// 00273 // Processing of characteristics based on their properties. 00274 00275 /** 00276 * Process the characteristics discovered. 00277 * 00278 * - If the characteristic is readable then read its value and print it. Then 00279 * - If the characteristic can emit notification or indication then discover 00280 * the characteristic CCCD and subscribe to the server initiated event. 00281 * - Otherwise skip the characteristic processing. 00282 */ 00283 void process_next_characteristic(void) 00284 { 00285 if (!_it) { 00286 _it = _characteristics; 00287 } else { 00288 _it = _it->next; 00289 } 00290 00291 while (_it) { 00292 Properties_t properties = _it->value.getProperties(); 00293 00294 if (properties.read()) { 00295 read_characteristic(_it->value); 00296 return; 00297 } else if(properties.notify() || properties.indicate()) { 00298 discover_descriptors(_it->value); 00299 return; 00300 } else { 00301 printf( 00302 "Skip processing of characteristic %u\r\n", 00303 _it->value.getValueHandle() 00304 ); 00305 _it = _it->next; 00306 } 00307 } 00308 00309 printf("All characteristics discovered have been processed.\r\n"); 00310 } 00311 00312 /** 00313 * Initate the read of the characteristic in input. 00314 * 00315 * The completion of the operation will happens in when_characteristic_read() 00316 */ 00317 void read_characteristic(const DiscoveredCharacteristic &characteristic) 00318 { 00319 printf("Initiating read at %u.\r\n", characteristic.getValueHandle()); 00320 ble_error_t error = characteristic.read( 00321 0, as_cb(&Self::when_characteristic_read) 00322 ); 00323 00324 if (error) { 00325 printf( 00326 "Error: cannot initiate read at %u due to %u\r\n", 00327 characteristic.getValueHandle(), error 00328 ); 00329 stop(); 00330 } 00331 } 00332 00333 /** 00334 * Handle the reception of a read response. 00335 * 00336 * If the characteristic can emit notification or indication then start the 00337 * discovery of the the characteristic descriptors then subscribe to the 00338 * server initiated event by writing the CCCD discovered. Otherwise start 00339 * the processing of the next characteristic discovered in the server. 00340 */ 00341 void when_characteristic_read(const GattReadCallbackParams *read_event) 00342 { 00343 printf("\tCharacteristic value at %u equal to: ", read_event->handle); 00344 for (size_t i = 0; i < read_event->len; ++i) { 00345 printf("0x%02X ", read_event->data[i]); 00346 } 00347 printf(".\r\n"); 00348 00349 Properties_t properties = _it->value.getProperties(); 00350 00351 if(properties.notify() || properties.indicate()) { 00352 discover_descriptors(_it->value); 00353 } else { 00354 process_next_characteristic(); 00355 } 00356 } 00357 00358 /** 00359 * Initiate the discovery of the descriptors of the characteristic in input. 00360 * 00361 * When a descriptor is discovered, the function when_descriptor_discovered 00362 * is invoked. 00363 */ 00364 void discover_descriptors(const DiscoveredCharacteristic &characteristic) 00365 { 00366 printf("Initiating descriptor discovery of %u.\r\n", characteristic.getValueHandle()); 00367 00368 _descriptor_handle = 0; 00369 ble_error_t error = characteristic.discoverDescriptors( 00370 as_cb(&Self::when_descriptor_discovered), 00371 as_cb(&Self::when_descriptor_discovery_ends) 00372 ); 00373 00374 if (error) { 00375 printf( 00376 "Error: cannot initiate discovery of %04X due to %u.\r\n", 00377 characteristic.getValueHandle(), error 00378 ); 00379 stop(); 00380 } 00381 } 00382 00383 /** 00384 * Handle the discovery of the characteristic descriptors. 00385 * 00386 * If the descriptor found is a CCCD then stop the discovery. Once the 00387 * process has ended subscribe to server initiated events by writing the 00388 * value of the CCCD. 00389 */ 00390 void when_descriptor_discovered(const DiscoveryCallbackParams_t* event) 00391 { 00392 printf("\tDescriptor discovered at %u, UUID: ", event->descriptor.getAttributeHandle()); 00393 print_uuid(event->descriptor.getUUID()); 00394 printf(".\r\n"); 00395 00396 if (event->descriptor.getUUID() == BLE_UUID_DESCRIPTOR_CLIENT_CHAR_CONFIG) { 00397 _descriptor_handle = event->descriptor.getAttributeHandle(); 00398 _client->terminateCharacteristicDescriptorDiscovery( 00399 event->characteristic 00400 ); 00401 } 00402 } 00403 00404 /** 00405 * If a CCCD has been found subscribe to server initiated events by writing 00406 * its value. 00407 */ 00408 void when_descriptor_discovery_ends(const TerminationCallbackParams_t *event) { 00409 // shall never happen but happen with android devices ... 00410 // process the next charateristic 00411 if (!_descriptor_handle) { 00412 printf("\tWarning: characteristic with notify or indicate attribute without CCCD.\r\n"); 00413 process_next_characteristic(); 00414 return; 00415 } 00416 00417 Properties_t properties = _it->value.getProperties(); 00418 00419 uint16_t cccd_value = 00420 (properties.notify() << 0) | (properties.indicate() << 1); 00421 00422 ble_error_t error = _client->write( 00423 GattClient::GATT_OP_WRITE_REQ, 00424 _connection_handle, 00425 _descriptor_handle, 00426 sizeof(cccd_value), 00427 reinterpret_cast<uint8_t*>(&cccd_value) 00428 ); 00429 00430 if (error) { 00431 printf( 00432 "Error: cannot initiate write of CCCD %u due to %u.\r\n", 00433 _descriptor_handle, error 00434 ); 00435 stop(); 00436 } 00437 } 00438 00439 /** 00440 * Called when the CCCD has been written. 00441 */ 00442 void when_descriptor_written(const GattWriteCallbackParams* event) 00443 { 00444 // should never happen 00445 if (!_descriptor_handle) { 00446 printf("\tError: received write response to unsolicited request.\r\n"); 00447 stop(); 00448 return; 00449 } 00450 00451 printf("\tCCCD at %u written.\r\n", _descriptor_handle); 00452 _descriptor_handle = 0; 00453 process_next_characteristic(); 00454 } 00455 00456 /** 00457 * Print the updated value of the characteristic. 00458 * 00459 * This function is called when the server emits a notification or an 00460 * indication of a characteristic value the client has subscribed to. 00461 * 00462 * @see GattClient::onHVX() 00463 */ 00464 void when_characteristic_changed(const GattHVXCallbackParams* event) 00465 { 00466 printf("Change on attribute %u: new value = ", event->handle); 00467 for (size_t i = 0; i < event->len; ++i) { 00468 printf("0x%02X ", event->data[i]); 00469 } 00470 printf(".\r\n"); 00471 } 00472 00473 struct DiscoveredCharacteristicNode { 00474 DiscoveredCharacteristicNode(const DiscoveredCharacteristic &c) : 00475 value(c), next(NULL) { } 00476 00477 DiscoveredCharacteristic value; 00478 DiscoveredCharacteristicNode *next; 00479 }; 00480 00481 /** 00482 * Add a discovered characteristic into the list of discovered characteristics. 00483 */ 00484 bool add_characteristic(const DiscoveredCharacteristic *characteristic) 00485 { 00486 DiscoveredCharacteristicNode* new_node = 00487 new(std::nothrow) DiscoveredCharacteristicNode(*characteristic); 00488 00489 if (new_node == false) { 00490 printf("Error while allocating a new characteristic.\r\n"); 00491 return false; 00492 } 00493 00494 if (_characteristics == NULL) { 00495 _characteristics = new_node; 00496 } else { 00497 DiscoveredCharacteristicNode* c = _characteristics; 00498 while(c->next) { 00499 c = c->next; 00500 } 00501 c->next = new_node; 00502 } 00503 00504 return true; 00505 } 00506 00507 /** 00508 * Clear the list of discovered characteristics. 00509 */ 00510 void clear_characteristics(void) 00511 { 00512 DiscoveredCharacteristicNode *c= _characteristics; 00513 00514 while (c) { 00515 DiscoveredCharacteristicNode *n = c->next; 00516 delete c; 00517 c = n; 00518 } 00519 } 00520 00521 /** 00522 * Helper to construct an event handler from a member function of this 00523 * instance. 00524 */ 00525 template<typename ContextType> 00526 FunctionPointerWithContext<ContextType> as_cb( 00527 void (Self::*member)(ContextType context) 00528 ) { 00529 return makeFunctionPointer(this, member); 00530 } 00531 00532 /** 00533 * Print the value of a UUID. 00534 */ 00535 static void print_uuid(const UUID &uuid) 00536 { 00537 const uint8_t *uuid_value = uuid.getBaseUUID(); 00538 00539 // UUIDs are in little endian, print them in big endian 00540 for (size_t i = 0; i < uuid.getLen(); ++i) { 00541 printf("%02X", uuid_value[(uuid.getLen() - 1) - i]); 00542 } 00543 } 00544 00545 /** 00546 * Print the value of a characteristic properties. 00547 */ 00548 static void print_properties(const Properties_t &properties) 00549 { 00550 const struct { 00551 bool (Properties_t::*fn)() const; 00552 const char* str; 00553 } prop_to_str[] = { 00554 { &Properties_t::broadcast, "broadcast" }, 00555 { &Properties_t::read, "read" }, 00556 { &Properties_t::writeWoResp, "writeWoResp" }, 00557 { &Properties_t::write, "write" }, 00558 { &Properties_t::notify, "notify" }, 00559 { &Properties_t::indicate, "indicate" }, 00560 { &Properties_t::authSignedWrite, "authSignedWrite" } 00561 }; 00562 00563 printf("["); 00564 for (size_t i = 0; i < (sizeof(prop_to_str) / sizeof(prop_to_str[0])); ++i) { 00565 if ((properties.*(prop_to_str[i].fn))()) { 00566 printf(" %s", prop_to_str[i].str); 00567 } 00568 } 00569 printf(" ]"); 00570 } 00571 00572 GattClient *_client; 00573 Gap::Handle_t _connection_handle; 00574 DiscoveredCharacteristicNode *_characteristics; 00575 DiscoveredCharacteristicNode *_it; 00576 GattAttribute::Handle_t _descriptor_handle; 00577 BLE *_ble_interface; 00578 events::EventQueue *_event_queue; 00579 }; 00580 00581 00582 int main() { 00583 00584 BLE &ble_interface = BLE::Instance(); 00585 events::EventQueue event_queue; 00586 BLEProcess ble_process(event_queue, ble_interface); 00587 GattClientProcess gatt_client_process; 00588 00589 // Register GattClientProcess::init in the ble_process; this function will 00590 // be called once the ble_interface is initialized. 00591 ble_process.on_init( 00592 mbed::callback(&gatt_client_process, &GattClientProcess::init) 00593 ); 00594 00595 // bind the event queue to the ble interface, initialize the interface 00596 // and start advertising 00597 ble_process.start(); 00598 00599 // Process the event queue. 00600 event_queue.dispatch_forever(); 00601 00602 return 0; 00603 }
Generated on Tue Jul 12 2022 11:33:28 by 1.7.2