How To Read Characteristic

Hello!

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
FunctionPointerWithContext 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 found the solution but I’ll leave it here for the new member. I haven’t got a chance to clean it up yet.

/* mbed Microcontroller Library

  • Copyright (c) 2006-2018 ARM Limited
  • Licensed under the Apache License, Version 2.0 (the “License”);
  • you may not use this file except in compliance with the License.
  • You may obtain a copy of the License at
  • http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software
  • distributed under the License is distributed on an “AS IS” BASIS,
  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  • See the License for the specific language governing permissions and
  • limitations under the License.
    */

#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();

}
void Start_client() {

_client.onDataRead(as_cb(&Self::onDataReadCallBack));
//_client.onServiceDiscoveryTermination(as_cb(&Self::discoveryTerminationCallback));
_client.setEventHandler(this);
_client.negotiateAttMtu(_connection_handle);

}

private:
/** This is called when BLE interface is initialised and starts the first mode */
void on_init_complete(BLE::InitializationCompleteCallbackContext *event) {
//Serial.println(“Helloworld”);
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());
  }
}

}

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”);
_connection_handle = event.getConnectionHandle();
_event_queue.call(mbed::callback(this, &Self::Start_client));
_client.onServiceDiscoveryTermination(as_cb(&Self::when_service_discovery_ends));
ble_error_t error = _client.launchServiceDiscovery(
event.getConnectionHandle(),
as_cb(&Self::when_service_discovered),
as_cb(&Self::when_characteristic_discovered),
0x180a,
0x2a1c);
}

/** 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”);
}

//void discoveryTerminationCallback(const ble::connection_handle_t connectionHandle) {
// printf(“terminated SD for handle %u\r\n”, connectionHandle);
//}

/**
* 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());
/printf(“TEST”);
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()) {
  Serial.println(characteristicP->getProperties().read(), DEC);
}
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;
writable_characteristic_found = true;

}

void when_service_discovery_ends(ble::connection_handle_t connection_handle) {
Serial.println(“discovert end”);
if (writable_characteristic_found) {
writable_characteristic_found = false;
_event_queue.call([this]{ writable_characteristic.read(); });
}
}

void onDataReadCallBack(const GattReadCallbackParams *params) {
Serial.println("GattClient read call back ");
Serial.print("The handle : ");
Serial.println(params->handle, HEX);
Serial.print("The offset : ");
Serial.println(params->offset, DEC);
Serial.print("The len : ");
Serial.println(params->len, DEC);
Serial.print(“The data : “);
for (uint8_t index = 0; index < params->len; index++) {
if (params->data[index] == 0) {
Serial.print(“00”);
} else {
Serial.print(params->data[index], HEX);
}
}
Serial.println(””);
}

private:

private:
BLE &_ble;
ble::Gap &_gap;
events::EventQueue &_event_queue;
GattClient &_client; //
DiscoveredCharacteristic writable_characteristic;
ble::connection_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
FunctionPointerWithContext 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:
}