Fabio Dal Forno / Mbed OS mbed-os-CALR-ONE
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

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 }