Ble for smart sOlutions

Dependencies:   Adafruit_WS2801

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