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