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.