Mbed BLE Example
// Leka - LekaOS | |
// Copyright 2020 APF France handicap | |
// SPDX-License-Identifier: Apache-2.0 | |
#include "events/mbed_events.h" | |
#include "ble/BLE.h" | |
#include "ble/Gap.h" | |
#include "ble/services/HeartRateService.h" | |
#include "pretty_printer.h" | |
const static char DEVICE_NAME[] = "Heartrate"; | |
static events::EventQueue event_queue(/* event count */ 32 * 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(500ms, this, &HeartrateDemo::blink); | |
_event_queue.call_every(1000ms, this, &HeartrateDemo::update_sensor_value); | |
// _event_queue.dispatch_forever(); | |
// printf("dispatch_forever\n"); | |
} | |
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; | |
} | |
print_mac_address(); | |
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; | |
}; |
// Leka - LekaOS | |
// Copyright 2020 APF France handicap | |
// SPDX-License-Identifier: Apache-2.0 | |
#include <mbed.h> | |
#include "LKBLE.h" | |
Thread thread_ble_event_queue; | |
void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context) | |
{ | |
event_queue.call(Callback<void()>(&context->ble, &BLE::processEvents)); | |
} | |
int main() | |
{ | |
BLE &ble = BLE::Instance(); | |
ble.onEventsToProcess(schedule_ble_events); | |
HeartrateDemo demo(ble, event_queue); | |
demo.start(); | |
thread_ble_event_queue.start(callback(&event_queue, &EventQueue::dispatch_forever)); | |
while (true) { | |
rtos::ThisThread::sleep_for(5s); | |
} | |
return 0; | |
} |
// Leka - LekaOS | |
// Copyright 2020 APF France handicap | |
// SPDX-License-Identifier: Apache-2.0 | |
#ifndef _LEKA_OS_LIB_BLE_PRETTY_PRINTER_H_ | |
#define _LEKA_OS_LIB_BLE_PRETTY_PRINTER_H_ | |
#include "ble/BLE.h" | |
inline void print_error(ble_error_t error, const char *msg) | |
{ | |
printf("%s: ", msg); | |
switch (error) { | |
case BLE_ERROR_NONE: | |
printf("BLE_ERROR_NONE: No error"); | |
break; | |
case BLE_ERROR_BUFFER_OVERFLOW: | |
printf( | |
"BLE_ERROR_BUFFER_OVERFLOW: The requested action would cause a buffer overflow and has been aborted"); | |
break; | |
case BLE_ERROR_NOT_IMPLEMENTED: | |
printf( | |
"BLE_ERROR_NOT_IMPLEMENTED: Requested a feature that isn't yet implement or isn't supported by the " | |
"target HW"); | |
break; | |
case BLE_ERROR_PARAM_OUT_OF_RANGE: | |
printf("BLE_ERROR_PARAM_OUT_OF_RANGE: One of the supplied parameters is outside the valid range"); | |
break; | |
case BLE_ERROR_INVALID_PARAM: | |
printf("BLE_ERROR_INVALID_PARAM: One of the supplied parameters is invalid"); | |
break; | |
case BLE_STACK_BUSY: | |
printf("BLE_STACK_BUSY: The stack is busy"); | |
break; | |
case BLE_ERROR_INVALID_STATE: | |
printf("BLE_ERROR_INVALID_STATE: Invalid state"); | |
break; | |
case BLE_ERROR_NO_MEM: | |
printf("BLE_ERROR_NO_MEM: Out of Memory"); | |
break; | |
case BLE_ERROR_OPERATION_NOT_PERMITTED: | |
printf("BLE_ERROR_OPERATION_NOT_PERMITTED"); | |
break; | |
case BLE_ERROR_INITIALIZATION_INCOMPLETE: | |
printf("BLE_ERROR_INITIALIZATION_INCOMPLETE"); | |
break; | |
case BLE_ERROR_ALREADY_INITIALIZED: | |
printf("BLE_ERROR_ALREADY_INITIALIZED"); | |
break; | |
case BLE_ERROR_UNSPECIFIED: | |
printf("BLE_ERROR_UNSPECIFIED: Unknown error"); | |
break; | |
case BLE_ERROR_INTERNAL_STACK_FAILURE: | |
printf("BLE_ERROR_INTERNAL_STACK_FAILURE: internal stack failure"); | |
break; | |
case BLE_ERROR_NOT_FOUND: | |
printf("BLE_ERROR_NOT_FOUND"); | |
break; | |
default: | |
printf("Unknown error"); | |
} | |
printf("\r\n"); | |
} | |
/** print device address to the terminal */ | |
inline void print_address(const ble::address_t &addr) | |
{ | |
printf("%02x:%02x:%02x:%02x:%02x:%02x\r\n", addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]); | |
} | |
inline void print_mac_address() | |
{ | |
/* Print out device MAC address to the console*/ | |
ble::own_address_type_t addr_type; | |
ble::address_t address; | |
BLE::Instance().gap().getAddress(addr_type, address); | |
printf("DEVICE MAC ADDRESS: "); | |
print_address(address); | |
} | |
inline const char *phy_to_string(ble::phy_t phy) | |
{ | |
switch (phy.value()) { | |
case ble::phy_t::LE_1M: | |
return "LE 1M"; | |
case ble::phy_t::LE_2M: | |
return "LE 2M"; | |
case ble::phy_t::LE_CODED: | |
return "LE coded"; | |
default: | |
return "invalid PHY"; | |
} | |
} | |
#endif // _LEKA_OS_LIB_BLE_PRETTY_PRINTER_H_ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment