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:
}