Kris Scholte Lubberink
/
SSS_Ble
Ble for smart sOlutions
source/BleDevice.h
- Committer:
- kris@kris-X682X
- Date:
- 2019-06-11
- Revision:
- 9:92d861703f96
- Parent:
- 8:369b80cef5ae
- Child:
- 10:d845189d146e
File content as of revision 9:92d861703f96:
// // Created by kris on 20-4-19. // #ifndef SSS_BLE_BLEDEVICE_H #define SSS_BLE_BLEDEVICE_H #include "pretty_printer.h" #include <mbed.h> #include "SecurityManager.h" #include "CustomUUIDs.h" #include "ble/BLE.h" #include "ble/DiscoveredCharacteristic.h" #include "ble/DiscoveredService.h" #include "ble/gap/Gap.h" #include "ble/gap/AdvertisingDataParser.h" #include <events/mbed_events.h> typedef ble_error_t (Gap::*disconnect_call_t)(ble::connection_handle_t, ble::local_disconnection_reason_t); const static disconnect_call_t disconnect_call = &Gap::disconnect; class BleDevice : private mbed::NonCopyable<BleDevice>, public SecurityManager::EventHandler, public ble::Gap::EventHandler { typedef BleDevice Self; typedef CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t DiscoveryCallbackParams_t; typedef CharacteristicDescriptorDiscovery::TerminationCallbackParams_t TerminationCallbackParams_t; typedef DiscoveredCharacteristic::Properties_t Properties_t; public: inline void print_error(ble_error_t error, const char* msg) { printf("%s: ", msg); switch(error) { case BLE_ERROR_NONE: printf("BLE_ERROR_NONE: No error"); break; case BLE_ERROR_BUFFER_OVERFLOW: printf("BLE_ERROR_BUFFER_OVERFLOW: The requested action would cause a buffer overflow and has been aborted"); break; case BLE_ERROR_NOT_IMPLEMENTED: printf("BLE_ERROR_NOT_IMPLEMENTED: Requested a feature that isn't yet implement or isn't supported by the target HW"); break; case BLE_ERROR_PARAM_OUT_OF_RANGE: printf("BLE_ERROR_PARAM_OUT_OF_RANGE: One of the supplied parameters is outside the valid range"); break; case BLE_ERROR_INVALID_PARAM: printf("BLE_ERROR_INVALID_PARAM: One of the supplied parameters is invalid"); break; case BLE_STACK_BUSY: printf("BLE_STACK_BUSY: The stack is busy"); break; case BLE_ERROR_INVALID_STATE: printf("BLE_ERROR_INVALID_STATE: Invalid state"); break; case BLE_ERROR_NO_MEM: printf("BLE_ERROR_NO_MEM: Out of Memory"); break; case BLE_ERROR_OPERATION_NOT_PERMITTED: printf("BLE_ERROR_OPERATION_NOT_PERMITTED"); break; case BLE_ERROR_INITIALIZATION_INCOMPLETE: printf("BLE_ERROR_INITIALIZATION_INCOMPLETE"); break; case BLE_ERROR_ALREADY_INITIALIZED: printf("BLE_ERROR_ALREADY_INITIALIZED"); break; case BLE_ERROR_UNSPECIFIED: printf("BLE_ERROR_UNSPECIFIED: Unknown error"); break; case BLE_ERROR_INTERNAL_STACK_FAILURE: printf("BLE_ERROR_INTERNAL_STACK_FAILURE: internal stack faillure"); break; case BLE_ERROR_NOT_FOUND: printf("BLE_ERROR_NOT_FOUND"); break; } printf("\r\n"); } /** print device address to the terminal */ void print_address(const uint8_t *addr) { printf("%02x:%02x:%02x:%02x:%02x:%02x\r\n", addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]); } inline void print_mac_address() { /* Print out device MAC address to the console*/ Gap::AddressType_t addr_type; Gap::Address_t address; BLE::Instance().gap().getAddress(&addr_type, address); printf("DEVICE MAC ADDRESS: "); print_address(address); } inline const char* phy_to_string(Gap::Phy_t phy) { switch(phy.value()) { case Gap::Phy_t::LE_1M: return "LE 1M"; case Gap::Phy_t::LE_2M: return "LE 2M"; case Gap::Phy_t::LE_CODED: return "LE coded"; default: return "invalid PHY"; } } BleDevice(const BLE& ble, events::EventQueue &event_queue) : _led1(LED1, 0), _ble(const_cast<BLE &>(ble)), _event_queue(event_queue), _handle(0), _is_connecting(false), hi(false){ }; virtual ~BleDevice() { printf("[DEVICE]\t Destructing the device\r\n"); if (_ble.hasInitialized()) { _ble.shutdown(); } }; /** Start BLE interface initialisation */ void run(int time) { ble_error_t error; /* to show we're running we'll blink every 500ms */ _event_queue.call_every(500, this, &BleDevice::blink); if (_ble.hasInitialized()) { printf("Ble instance already initialised.\r\n"); return; } /* this will inform us off all events so we can schedule their handling * using our event queue */ _ble.onEventsToProcess( makeFunctionPointer(this, &BleDevice::schedule_ble_events) ); /* handle gap events */ _ble.gap().setEventHandler(this); error = _ble.init(this, &BleDevice::on_init_complete); if (error) { printf("Error returned by BLE::init.\r\n"); return; } printf("Initialized the device! I should now wait for a bit maybe?"); /* this will not return until shutdown */ //BUT IT SHOULD! // _event_queue.dispatch_forever();// wait(2); // printf(" Waited? "); _event_queue.dispatch(time); }; private: /** Override to start chosen activity when initialisation completes */ virtual void start() = 0; /** This is called when BLE interface is initialised and starts the demonstration */ void on_init_complete(BLE::InitializationCompleteCallbackContext *event) { // ble_error_t error; if (event->error) { printf("Error during the initialisation\r\n"); return; } /* gap events also handled by this class */ _ble.gap().setEventHandler(this); /* print device address */ Gap::AddressType_t addr_type; Gap::Address_t addr; _ble.gap().getAddress(&addr_type, addr); print_address(addr); printf("\r\n.... Initialized the device! \r\n"); _event_queue.call_in(500, this, &BleDevice::start); }; /** Schedule processing of events from the BLE in the event queue. */ void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context) { _event_queue.call(mbed::callback(&context->ble, &BLE::processEvents)); }; /** Blink LED to show we're running */ void blink(void) { _led1 = !_led1; }; private: static void print_uuid(const UUID &uuid) { const uint8_t *uuid_value = uuid.getBaseUUID(); // UUIDs are in little endian, print them in big endian for (size_t i = 0; i < uuid.getLen(); ++i) { printf("%02X", uuid_value[(uuid.getLen() - 1) - i]); } } void when_service_discovered(const DiscoveredService *discovered_service) { // print information of the service discovered printf("Service discovered: value = "); print_uuid(discovered_service->getUUID()); printf(", start = %u, end = %u.\r\n", discovered_service->getStartHandle(), discovered_service->getEndHandle() ); } /** * Add a discovered characteristic into the list of discovered characteristics. */ // bool add_characteristic(const DiscoveredCharacteristic *characteristic) // { // //TODO: If char UUID == UUID, store char for reading // if // printf("Adding char"); // //// if (new_node == false) { //// printf("Error while allocating a new characteristic.\r\n"); //// return false; //// } // printf("Added char"); // // if (_characteristics == NULL) { // _characteristics = const_cast<DiscoveredCharacteristic *>(characteristic); // } else { // DiscoveredCharacteristic* c = _characteristics; // while() { // c = c->next; // } // c->next = new_node; // } // delete new_node; // return true; // } /** * Handle characteristics discovered. * * The GattClient invoke this function when a characteristic has been * discovered. * * @see GattClient::launchServiceDiscovery */ void when_characteristic_discovered(const DiscoveredCharacteristic *discovered_characteristic) { // print characteristics properties printf("\tCharacteristic discovered: uuid = "); print_uuid(discovered_characteristic->getUUID()); printf(", properties = "); // print_properties(discovered_characteristic->getProperties()); printf( ", decl handle = %u, value handle = %u, last handle = %u.\r\n", discovered_characteristic->getDeclHandle(), discovered_characteristic->getValueHandle(), discovered_characteristic->getLastHandle() ); if(discovered_characteristic->getUUID() == CustomUUIDs::UUID_INTEREST_CHAR){ printf("You have the interest char!"); hi = true; _interest_characteristic = discovered_characteristic; } } /** * Handle termination of the service and characteristic discovery process. * * The GattClient invokes this function when the service and characteristic * discovery process ends. * * @see GattClient::onServiceDiscoveryTermination */ void when_service_discovery_ends(Gap::Handle_t connection_handle) { if (!hi) { printf("No characteristics discovered, end of the process.\r\n"); return; } printf("All services and characteristics discovered, process them.\r\n"); process_next_characteristic(); // reset iterator and start processing characteristics in order // _event_queue.call(mbed::callback(this, &Self::process_next_characteristic)); } //////////////////////////////////////////////////////////////////////////////// // Processing of characteristics based on their properties. void process_next_characteristic(void) { printf("Test"); if(_interest_characteristic){ //Interest char can be null; check that Properties_t properties = _interest_characteristic->getProperties(); if (properties.read()) { read_characteristic(*_interest_characteristic); return; } else { // printf( // "Skip processing of characteristic %u\r\n", // _it->value.getValueHandle() // ); // _it = _it->next; } } printf("All characteristics discovered have been processed.\r\n"); } /** * Initate the read of the characteristic in input. * * The completion of the operation will happens in when_characteristic_read() */ void read_characteristic(const DiscoveredCharacteristic &characteristic) { printf("Initiating read at %u.\r\n", characteristic.getValueHandle()); ble_error_t error = characteristic.read( 0, makeFunctionPointer(this, &Self::when_characteristic_read) ); if (error) { printf( "Error: cannot initiate read at %u due to %u\r\n", characteristic.getValueHandle(), error ); } } /** * Handle the reception of a read response. * * If the characteristic can emit notification or indication then start the * discovery of the the characteristic descriptors then subscribe to the * server initiated event by writing the CCCD discovered. Otherwise start * the processing of the next characteristic discovered in the server. */ void when_characteristic_read(const GattReadCallbackParams *read_event) { printf("\tCharacteristic value at %u equal to: ", read_event->handle); for (size_t i = 0; i < read_event->len; ++i) { printf("0x%02X ", read_event->data[i]); } printf(".\r\n"); // Properties_t properties = _>getProperties(); // if(properties.notify() || properties.indicate()) { // discover_descriptors(_it->value); // } else { // process_next_characteristic(); // } } /* Event handler */ /** This is called by Gap to notify the application we disconnected, * in our case it ends the demonstration. */ virtual void onDisconnectionComplete(const ble::DisconnectionCompleteEvent &) { printf("Disconnected\r\n"); _event_queue.break_dispatch(); //And/or resume advertising? }; virtual void onAdvertisingEnd(const ble::AdvertisingEndEvent &) { printf("Advertising timed out - aborting advertisting\r\n"); // _event_queue.break_dispatch(); } virtual void onScanTimeout(const ble::ScanTimeoutEvent &) { printf("Scan timed out - aborting\r\n"); _event_queue.break_dispatch(); } /** This is called by Gap to notify the application we connected, * in our case it immediately request pairing */ virtual void onConnectionComplete(const ble::ConnectionCompleteEvent &event) { if (event.getStatus() == BLE_ERROR_NONE) { /* store the handle for future Security Manager requests */ _handle = event.getConnectionHandle(); printf("Connected\r\n"); /* in this example the local device is the master so we request pairing */ // ble_error_t error = _ble.securityManager().requestPairing(_handle); // if (error) { // printf("Error during SM::requestPairing %d\r\n", error); // return; // } _ble.gattClient().onServiceDiscoveryTermination(makeFunctionPointer(this, &Self::when_service_discovery_ends)); _ble.gattClient().launchServiceDiscovery(_handle, makeFunctionPointer(this, &Self::when_service_discovered), makeFunctionPointer(this, &Self::when_characteristic_discovered)); /* upon pairing success the application will disconnect */ } /* failed to connect - restart scan */ // ble_error_t error = _ble.gap().startScan(); // if (error) { // print_error(error, "Error in Gap::startScan %d\r\n"); // return; // } } private: DigitalOut _led1; protected: BLE &_ble; events::EventQueue &_event_queue; ble::connection_handle_t _handle; bool _is_connecting; bool hi; const DiscoveredCharacteristic *_interest_characteristic; }; #endif //SSS_BLE_BLEDEVICE_H