Read Characteristic with MBED OS BLE GattClient

29 views Asked by At

I'm working to read a characteristic from gatt server using mbed os but not very successful. I'm able to make it scan and connect to the gatt server but I'm stuck there. I read the mbed ble example but some api seem like outdated. Any thoughts would help!


#include <events/mbed_events.h>
#include <mbed.h>
#include "ble/BLE.h"
#include "ble/DiscoveredCharacteristic.h"
#include "ble/DiscoveredService.h"
#include "ble/Gap.h"
#include "ble/gap/AdvertisingDataParser.h"
#include "pretty_printer.h"

using namespace std::chrono;
using std::milli;
using namespace std::literals::chrono_literals;

//void on_read(const GattReadCallbackParams *response);

/* Scanning happens repeatedly and is defined by:
 *  - The scan interval which is the time (in 0.625us) between each scan cycle.
 *  - The scan window which is the scanning time (in 0.625us) during a cycle.
 * If the scanning process is active, the local device sends scan requests
 * to discovered peer to get additional data.
 */
static DiscoveredCharacteristic led_characteristic[3];
static bool trigger_led_characteristic = false;
void on_read(const GattReadCallbackParams *response);
//DiscoveredCharacteristicNode *_characteristics = nullptr;
//DiscoveredCharacteristicNode *_it = nullptr;

static const ble::ScanParameters scan_params(
  ble::phy_t::LE_CODED,
  ble::scan_interval_t(80),
  ble::scan_window_t(60),
  true /* active scanning */
);

/* config end */

events::EventQueue event_queue;
const ble::phy_set_t CodedPHY(ble::phy_t::LE_CODED);
void on_read(const GattReadCallbackParams *response);
typedef CharacteristicDescriptorDiscovery::DiscoveryCallbackParams_t DiscoveryCallbackParams_t;
typedef CharacteristicDescriptorDiscovery::TerminationCallbackParams_t TerminationCallbackParams_t;
//typedef DiscoveredCharacteristicNode *_it = nullptr;

void when_descriptor_discovery_ends(const TerminationCallbackParams_t *event) {

  printf("   - when_descriptor_discovery_ends\r\n");
}

// This gets triggered once connected




void when_DataRead(const GattReadCallbackParams *response) {

  printf("handle %u, offset %u, len %u\r\n", response->handle, response->offset, response->len);
  // --------------------uint8_t toggledValue = response->data[0] ^ 0x1;

  // --------------------led_characteristic.write(1, &toggledValue);
}



REDIRECT_STDOUT_TO(Serial);

/** Demonstrate advertising, scanning and connecting */
class GapDemo : public ble::Gap::EventHandler,
                public GattClient::EventHandler  {
  typedef GapDemo Self;
  friend void on_read(const GattReadCallbackParams *response);
public:

  GapDemo(BLE &ble, events::EventQueue &event_queue)
    : _ble(ble),
      _gap(ble.gap()),
      _client(ble.gattClient()),

      _event_queue(event_queue) {
  }

  ~GapDemo() {
    if (_ble.hasInitialized()) {
      _ble.shutdown();
    }
  }
  static GapDemo &get_instance(BLE &ble, events::EventQueue &event_queue) {
    static GapDemo instance(ble, event_queue);
    return instance;
  }

  /** Start BLE interface initialisation */
  void run() {
    /* handle gap events */
    _gap.setEventHandler(this);

    ble_error_t error = _ble.init(this, &GapDemo::on_init_complete);
    if (error) {
      print_error(error, "Error returned by BLE::init");
      return;
    }

    /* this will not return until shutdown */
    _event_queue.dispatch_forever();
  }



private:
  /** This is called when BLE interface is initialised and starts the first mode */
  void on_init_complete(BLE::InitializationCompleteCallbackContext *event) {
    //Serial.println("Helloworld");
    _ble.gattClient().onDataRead(when_DataRead);
    if (event->error) {
      print_error(event->error, "Error during the initialisation");
      return;
    }

    print_mac_address();

    /* setup the default phy used in connection to 2M to reduce power consumption*/
    if (_gap.isFeatureSupported(ble::controller_supported_features_t::LE_CODED_PHY)) {
      //ble::phy_set_t phys(/* 1M */ false, /* 2M */ false, /* coded */ true);
      ble_error_t error = _gap.setPreferredPhys(/* tx */ &CodedPHY, /* rx */ &CodedPHY);

      /* PHY 2M communication will only take place if both peers support it*/
      if (error) {
        print_error(error, "GAP::setPreferedPhys failed");
      } else {
        printf("Upgrade PHY to 2M\n");
      }


    } else {
      printf("No Supported");
      /* otherwise it will use 1M by default */
    }

    /* all calls are serialised on the user thread through the event queue*/
    //_client.onDataRead(trigger_toggled_write);

    _event_queue.call(this, &GapDemo::scan);
  }


  /** Set up and start scanning */
  void scan() {

    // Build the scan parameters

    ble_error_t error = _gap.setScanParameters(
      ble::ScanParameters()
        .setPhys(true, true)
        .setCodedPhyConfiguration(ble::scan_interval_t(80), ble::scan_window_t(60), false)
        .set1mPhyConfiguration(ble::scan_interval_t(100), ble::scan_window_t(40), false));
    if (error) {
      print_error(error, "Error caused by Gap::setScanParameters");
      return;
    }

    /* start scanning and attach a callback that will handle advertisements
         * and scan requests responses */
    error = _gap.startScan();
    if (error) {
      print_error(error, "Error caused by Gap::startScan");
      return;
    }

    printf(
      "\r\nScanning started (interval: %dms, window: %dms).\r\n",
      scan_params.get1mPhyConfiguration().getInterval().valueInMs(),
      scan_params.get1mPhyConfiguration().getWindow().valueInMs());
  }

private:
  /* Gap::EventHandler */

  /** Report on found devices */


  void onAdvertisingReport(const ble::AdvertisingReportEvent &event) override {
    /*printf("Scan found: Phy = %s, Tx Power = %d, RSSI = %d\n",
          phy_to_string(event.getPrimaryPhy()), event.getTxPower(), event.getRssi());*/

    ble::AdvertisingDataParser adv_parser(event.getPayload());

    /* parse the advertising payload*/
    while (adv_parser.hasNext()) {
      ble::AdvertisingDataParser::element_t field = adv_parser.next();
      if (field.type == ble::adv_data_type_t::COMPLETE_LOCAL_NAME) {
        char localname[128];
        memset(localname, 0, 128);
        memcpy(localname, field.value.data(), field.value.size());
        if (strcmp(localname, "LR Demo") == 0) {
          _gap.stopScan();
          if (_is_connecting) {
            return;
          }
          printf("\tComplete Name = %s\n", localname);
          ble_error_t error = _gap.connect(
            event.getPeerAddressType(),
            event.getPeerAddress(),
            ble::ConnectionParameters()  // use the default connection parameters
          );
          if (error) {
            print_error(error, "Error caused by Gap::connect");
            return;
          }
          _is_connecting = true;
          return;
        }

        printf("Scan found: Phy = %s, Tx Power = %d, RSSI = %d\n", phy_to_string(event.getPrimaryPhy()), event.getTxPower(), event.getRssi());
      }
      //else if (field.type == ble::adv_data_type_t::TX_POWER_LEVEL) {
      //    printf("\tAdvertised TX Power Level = %d\n", field.value.data()[0]);
    }
  }

  void onScanTimeout(const ble::ScanTimeoutEvent &) override {
    printf("Stopped scanning due to timeout parameter\r\n");
    _event_queue.call(this, &GapDemo::scan);
  }


  void onConnectionComplete(const ble::ConnectionCompleteEvent &event) override {
    Serial.println("Connected");
    ble_error_t error = _client.launchServiceDiscovery(
      event.getConnectionHandle(),
      as_cb(&Self::when_service_discovered),
      as_cb(&Self::when_characteristic_discovered),
      0x180a,
      0x2a1c);
    Serial.println("Returned");
    //_event_queue.call(this, &GapDemo::process_next_characteristic);
  }


  /** This is called by Gap to notify the application we disconnected,
     *  in our case it calls next_demo_mode() to progress the demo */
  void onDisconnectionComplete(const ble::DisconnectionCompleteEvent &event) override {
    printf("Disconnected\r\n");
  }

  /**
     * Implementation of Gap::EventHandler::onReadPhy
     */
  void onReadPhy(
    ble_error_t error,
    ble::connection_handle_t connectionHandle,
    ble::phy_t txPhy,
    ble::phy_t rxPhy) override {
    if (error) {
      printf(
        "Phy read on connection %d failed with error code %s\r\n",
        connectionHandle, BLE::errorToString(error));
    } else {
      printf(
        "Phy read on connection %d - Tx Phy: %s, Rx Phy: %s\r\n",
        connectionHandle, phy_to_string(txPhy), phy_to_string(rxPhy));
    }
  }

  /**
     * Implementation of Gap::EventHandler::onPhyUpdateComplete
     */
  void onPhyUpdateComplete(
    ble_error_t error,
    ble::connection_handle_t connectionHandle,
    ble::phy_t txPhy,
    ble::phy_t rxPhy) override {
    if (error) {
      printf(
        "Phy update on connection: %d failed with error code %s\r\n",
        connectionHandle, BLE::errorToString(error));
    } else {
      printf(
        "Phy update on connection %d - Tx Phy: %s, Rx Phy: %s\r\n",
        connectionHandle, phy_to_string(txPhy), phy_to_string(rxPhy));
    }
  }


  /**
     * Implementation of Gap::EventHandler::onDataLengthChange
     */
  void onDataLengthChange(
    ble::connection_handle_t connectionHandle,
    uint16_t txSize,
    uint16_t rxSize) override {
    printf(
      "Data length changed on the connection %d.\r\n"
      "Maximum sizes for over the air packets are:\r\n"
      "%d octets for transmit and %d octets for receive.\r\n",
      connectionHandle, txSize, rxSize);
  }

  void read_characteristic(const DiscoveredCharacteristic &characteristic) {
    printf("Initiating read at %u.\r\n", characteristic.getValueHandle());
    ble_error_t error = characteristic.read(0, as_cb(&Self::when_characteristic_read));

    if (error) {
      printf(
        "Error: cannot initiate read at %u due to %u\r\n",
        characteristic.getValueHandle(), error);
    }
  }

  void when_characteristic_read(const GattReadCallbackParams *read_event) {
    printf("\tCharacteristic value at %u equal to: ", read_event->handle);
  }


  void when_service_discovered(const DiscoveredService *service) {

    if (service->getUUID().shortOrLong() == UUID::UUID_TYPE_SHORT) {

      printf("Service UUID-%x attrs[%u - %u]\r\n", service->getUUID().getShortUUID(), service->getStartHandle(), service->getEndHandle());

    } else {

      printf("S UUID-");

      const uint8_t *longUUIDBytes = service->getUUID().getBaseUUID();

      for (unsigned i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) {

        printf("%02x", longUUIDBytes[i]);
      }

      printf(" attrs[%u %u]\r\n", service->getStartHandle(), service->getEndHandle());
    }
  }

  void when_characteristic_discovered(const DiscoveredCharacteristic *characteristicP) {

    printf("  Char UUID-%x valueHandle[%u] declHandle[%u] connHandle[%u]\r\n",

           characteristicP->getUUID().getShortUUID(),

           characteristicP->getValueHandle(),

           characteristicP->getDeclHandle(),

           characteristicP->getConnectionHandle());

    if (characteristicP->getProperties().broadcast()) printf("  Char has Broadcast property\r\n");

    if (characteristicP->getProperties().indicate()) printf("  Char has Indicate property\r\n");

    if (characteristicP->getProperties().notify()) {

      printf("  Char has Notify property\r\n");

      printf("   - Initiating descriptor discovery of %u.\r\n", characteristicP->getValueHandle());

      // ble_error_t error = characteristicP->discoverDescriptors(&when_descriptor_discovered, &when_descriptor_discovery_ends);

      //if (error) print_error(error, "Error caused by discoverDescriptors");
    }
    if (characteristicP->getProperties().read()) printf("  Char has Read property\r\n");

    if (characteristicP->getProperties().write()) printf("  Char has Write property\r\n");

    if (characteristicP->getProperties().writeWoResp()) printf("  Char has Write NoResp property\r\n");
    writable_characteristic = *characteristicP;

    //return;
    //if (cIndex == sizeof(led_characteristic)/sizeof(led_characteristic[0])-1) trigger_led_characteristic = true;

    // if (cIndex < sizeof(led_characteristic)/sizeof(led_characteristic[0])) cIndex++;
    //event_queue.call(&GapDemo::read_characteristic());
  }
  void on_read(const GattReadCallbackParams *response) {
    if (response->handle == writable_characteristic.getValueHandle()) {
      /* increment the value we just read */
      uint8_t value = response->data[0];
      Serial.println(value);
    }
  }





private:

private:
  BLE &_ble;
  ble::Gap &_gap;
  events::EventQueue &_event_queue;
  GattClient &_client;  //
  DiscoveredCharacteristic writable_characteristic;
  //Gap::Handle_t _connection_handle;
  //ReadWriteGattCharacteristic<uint8_t> *_writable_characteristic = new ReadWriteGattCharacteristic<uint8_t>(uuid, &_characteristic_value),

  bool _is_connecting = false;
  bool writable_characteristic_found = false;
  //DiscoveredCharacteristicNode *_it = nullptr;
  //ServiceDiscovery::ServiceCallback_t serviceCallback = makeFunctionPointer(when_service_discovered);
  template<typename ContextType>
  FunctionPointerWithContext<ContextType> as_cb(void (Self::*member)(ContextType context)) {
    return makeFunctionPointer(this, member);
  }

#if BLE_FEATURE_EXTENDED_ADVERTISING
  ble::advertising_handle_t _extended_adv_handle = ble::INVALID_ADVERTISING_HANDLE;
#endif  // BLE_FEATURE_EXTENDED_ADVERTISING
};



/** Schedule processing of events from the BLE middleware in the event queue. */
void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context) {
  event_queue.call(mbed::Callback<void()>(&context->ble, &BLE::processEvents));
}





void setup() {
  delay(2000);

  BLE &ble = BLE::Instance();

  /* this will inform us off all events so we can schedule their handling
     * using our event queue */
  ble.onEventsToProcess(schedule_ble_events);

  GapDemo demo(ble, event_queue);

  demo.run();
}

void loop() {
  // put your main code here, to run repeatedly:
} 

I have tried follow example on ble mbed os repository but seem like some API has been outdated.

0

There are 0 answers