After a few minor modifications, the examples compile and upload without a problem. I can see the services being advertised in nRF Connect on my mobile and the LED is blinking showing that the events are going off as scheduled. But for some reason I am unable to connect to either BLE peripheral using nRF Connect.
Has anyone else tried this? Any idea what could be going wrong?
Can you please post your code? Use code tags so your source is displayed like this. Click on Preview or Modify to get the toolbar for formatting the post.
Here is the Heartrate peripheral. From the original mbed code, I have removed pretty_print.h and the call to pretty_print the address, added "using namespace mbed;" and moved the contents of main() in the original mbed code to setup().
#include <events/mbed_events.h>
#include <mbed.h>
#include "ble/BLE.h"
#include "ble/gap/Gap.h"
#include "ble/services/HeartRateService.h"
using namespace mbed;
const static char DEVICE_NAME[] = "Heartrate";
static events::EventQueue event_queue(/* event count */ 16 * EVENTS_EVENT_SIZE);
class HeartrateDemo : ble::Gap::EventHandler {
public:
HeartrateDemo(BLE &ble, events::EventQueue &event_queue) :
_ble(ble),
_event_queue(event_queue),
_led1(LED1, 1),
_connected(false),
_hr_uuid(GattService::UUID_HEART_RATE_SERVICE),
_hr_counter(100),
_hr_service(ble, _hr_counter, HeartRateService::LOCATION_FINGER),
_adv_data_builder(_adv_buffer) { }
void start() {
_ble.gap().setEventHandler(this);
_ble.init(this, &HeartrateDemo::on_init_complete);
_event_queue.call_every(500, this, &HeartrateDemo::blink);
_event_queue.call_every(1000, this, &HeartrateDemo::update_sensor_value);
_event_queue.dispatch_forever();
}
private:
/** Callback triggered when the ble initialization process has finished */
void on_init_complete(BLE::InitializationCompleteCallbackContext *params) {
if (params->error != BLE_ERROR_NONE) {
printf("Ble initialization failed.");
return;
}
start_advertising();
}
void start_advertising() {
/* Create advertising parameters and payload */
ble::AdvertisingParameters adv_parameters(
ble::advertising_type_t::CONNECTABLE_UNDIRECTED,
ble::adv_interval_t(ble::millisecond_t(1000))
);
_adv_data_builder.setFlags();
_adv_data_builder.setAppearance(ble::adv_data_appearance_t::GENERIC_HEART_RATE_SENSOR);
_adv_data_builder.setLocalServiceList(mbed::make_Span(&_hr_uuid, 1));
_adv_data_builder.setName(DEVICE_NAME);
/* Setup advertising */
ble_error_t error = _ble.gap().setAdvertisingParameters(
ble::LEGACY_ADVERTISING_HANDLE,
adv_parameters
);
if (error) {
printf("_ble.gap().setAdvertisingParameters() failed\r\n");
return;
}
error = _ble.gap().setAdvertisingPayload(
ble::LEGACY_ADVERTISING_HANDLE,
_adv_data_builder.getAdvertisingData()
);
if (error) {
printf("_ble.gap().setAdvertisingPayload() failed\r\n");
return;
}
/* Start advertising */
error = _ble.gap().startAdvertising(ble::LEGACY_ADVERTISING_HANDLE);
if (error) {
printf("_ble.gap().startAdvertising() failed\r\n");
return;
}
}
void update_sensor_value() {
if (_connected) {
// Do blocking calls or whatever is necessary for sensor polling.
// In our case, we simply update the HRM measurement.
_hr_counter++;
// 100 <= HRM bps <=175
if (_hr_counter == 175) {
_hr_counter = 100;
}
_hr_service.updateHeartRate(_hr_counter);
}
}
void blink(void) {
_led1 = !_led1;
}
private:
/* Event handler */
void onDisconnectionComplete(const ble::DisconnectionCompleteEvent&) {
_ble.gap().startAdvertising(ble::LEGACY_ADVERTISING_HANDLE);
_connected = false;
}
virtual void onConnectionComplete(const ble::ConnectionCompleteEvent &event) {
if (event.getStatus() == BLE_ERROR_NONE) {
_connected = true;
}
}
private:
BLE &_ble;
events::EventQueue &_event_queue;
DigitalOut _led1;
bool _connected;
UUID _hr_uuid;
uint8_t _hr_counter;
HeartRateService _hr_service;
uint8_t _adv_buffer[ble::LEGACY_ADVERTISING_MAX_SIZE];
ble::AdvertisingDataBuilder _adv_data_builder;
};
/** Schedule processing of events from the BLE middleware in the event queue. */
void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context) {
// either explicit mbed::Callback or use "using namespace mbed;"
event_queue.call(Callback<void()>(&context->ble, &BLE::processEvents));
}
void setup() {
BLE& ble = BLE::Instance();
ble.onEventsToProcess(schedule_ble_events);
HeartrateDemo demo(ble, event_queue);
demo.start();
}
void loop() {
}
I have just tried to connect to the Heartrate peripheral using an nRF52840 dongle connect to another port on my PC and the nRF Connect desktop app and it works!
So why want my phone connect? My phone connects with when running peripheral sketches built using the Arduino BLE library without a problem.
I just downloaded the sketch to my Arduino Nano 33 BLE. I was able to connect with my iPhone using multiple apps BLE Scanner, nRF Connect and nRF Toolbox which shows a disturbing heart rate graph. I might need to see a doctor.
I uploaded the sketch and then connected my Android phone using nRFconnect without any problems. Woweeeee!!! This is so cool (i.e. using MBED) as gives plenty of scope for new applications.
I have tested on 2 moto phones, 1 huawei tablet, 3 samsung phones and nRF Connect desktop with the nRF52840 dongle. 2 Samsung phones don't work (Galaxy S5 and Note 4), all others are fine.
Yet the Arduino BatteryMonitor example works fine on everything. I tried reducing the advertising interval, but it made no difference.
Very mysterious. I may dig into the androidBLE APIs as I assume they are calling the mbed BLE API to see if I can resolve this.
By the way, for those interested in using mbed code, the mbed printf() statements can be made to work by simply changing to Serial.printf(), but does anyone know how to automatically redirect output so I don't have to change printf() statements? Redirecting stderr to Serial would be good as well.