Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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