Add heart rate BLE service.

This commit is contained in:
Jean-François Milants 2021-01-17 16:34:14 +01:00
parent 3a3a14115a
commit 68674cec53
9 changed files with 152 additions and 7 deletions

View File

@ -375,6 +375,7 @@ list(APPEND SOURCE_FILES
components/ble/BatteryInformationService.cpp components/ble/BatteryInformationService.cpp
components/ble/ImmediateAlertService.cpp components/ble/ImmediateAlertService.cpp
components/ble/ServiceDiscovery.cpp components/ble/ServiceDiscovery.cpp
components/ble/HeartRateService.cpp
components/firmwarevalidator/FirmwareValidator.cpp components/firmwarevalidator/FirmwareValidator.cpp
drivers/Cst816s.cpp drivers/Cst816s.cpp
FreeRTOS/port.c FreeRTOS/port.c
@ -464,6 +465,7 @@ set(INCLUDE_FILES
components/ble/ImmediateAlertService.h components/ble/ImmediateAlertService.h
components/ble/ServiceDiscovery.h components/ble/ServiceDiscovery.h
components/ble/BleClient.h components/ble/BleClient.h
components/ble/HeartRateService.h.h
drivers/Cst816s.h drivers/Cst816s.h
FreeRTOS/portmacro.h FreeRTOS/portmacro.h
FreeRTOS/portmacro_cmsis.h FreeRTOS/portmacro_cmsis.h

View File

@ -0,0 +1,82 @@
#include "HeartRateService.h"
#include "components/heartrate/HeartRateController.h"
#include "systemtask/SystemTask.h"
using namespace Pinetime::Controllers;
constexpr ble_uuid16_t HeartRateService::heartRateServiceUuid;
constexpr ble_uuid16_t HeartRateService::heartRateMeasurementUuid;
namespace {
int HeartRateServiceServiceCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
auto* heartRateService = static_cast<HeartRateService*>(arg);
return heartRateService->OnHeartRateRequested(conn_handle, attr_handle, ctxt);
}
}
// TODO Refactoring - remove dependency to SystemTask
HeartRateService::HeartRateService(Pinetime::System::SystemTask &system, Controllers::HeartRateController& heartRateController) :
system{system},
heartRateController{heartRateController},
characteristicDefinition{
{
.uuid = (ble_uuid_t *) &heartRateMeasurementUuid,
.access_cb = HeartRateServiceServiceCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
.val_handle = &heartRateMeasurementHandle
},
{
0
}
},
serviceDefinition{
{
/* Device Information Service */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = (ble_uuid_t *) &heartRateServiceUuid,
.characteristics = characteristicDefinition
},
{
0
},
}{
// TODO refactor to prevent this loop dependency (service depends on controller and controller depends on service)
heartRateController.SetService(this);
}
void HeartRateService::Init() {
int res = 0;
res = ble_gatts_count_cfg(serviceDefinition);
ASSERT(res == 0);
res = ble_gatts_add_svcs(serviceDefinition);
ASSERT(res == 0);
}
int HeartRateService::OnHeartRateRequested(uint16_t connectionHandle, uint16_t attributeHandle,
ble_gatt_access_ctxt *context) {
if(attributeHandle == heartRateMeasurementHandle) {
NRF_LOG_INFO("BATTERY : handle = %d", heartRateMeasurementHandle);
static uint8_t batteryValue = heartRateController.HeartRate();
uint8_t buffer[2] = {0, heartRateController.HeartRate()}; // [0] = flags, [1] = hr value
int res = os_mbuf_append(context->om, buffer, 2);
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
return 0;
}
void HeartRateService::OnNewHeartRateValue(uint8_t heartRateValue) {
uint8_t buffer[2] = {0, heartRateController.HeartRate()}; // [0] = flags, [1] = hr value
auto *om = ble_hs_mbuf_from_flat(buffer, 2);
uint16_t connectionHandle = system.nimble().connHandle();
if (connectionHandle == 0 || connectionHandle == BLE_HS_CONN_HANDLE_NONE) {
return;
}
ble_gattc_notify_custom(connectionHandle, heartRateMeasurementHandle, om);
}

View File

@ -0,0 +1,44 @@
#pragma once
#define min // workaround: nimble's min/max macros conflict with libstdc++
#define max
#include <host/ble_gap.h>
#undef max
#undef min
namespace Pinetime {
namespace System {
class SystemTask;
}
namespace Controllers {
class HeartRateController;
class HeartRateService {
public:
HeartRateService(Pinetime::System::SystemTask &system, Controllers::HeartRateController& heartRateController);
void Init();
int OnHeartRateRequested(uint16_t connectionHandle, uint16_t attributeHandle, ble_gatt_access_ctxt *context);
void OnNewHeartRateValue(uint8_t hearRateValue);
private:
Pinetime::System::SystemTask &system;
Controllers::HeartRateController& heartRateController;
static constexpr uint16_t heartRateServiceId {0x180D};
static constexpr uint16_t heartRateMeasurementId {0x2A37};
static constexpr ble_uuid16_t heartRateServiceUuid {
.u {.type = BLE_UUID_TYPE_16},
.value = heartRateServiceId
};
static constexpr ble_uuid16_t heartRateMeasurementUuid {
.u {.type = BLE_UUID_TYPE_16},
.value = heartRateMeasurementId
};
struct ble_gatt_chr_def characteristicDefinition[3];
struct ble_gatt_svc_def serviceDefinition[2];
uint16_t heartRateMeasurementHandle;
};
}
}

View File

@ -22,7 +22,8 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
DateTime& dateTimeController, DateTime& dateTimeController,
Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::NotificationManager& notificationManager,
Controllers::Battery& batteryController, Controllers::Battery& batteryController,
Pinetime::Drivers::SpiNorFlash& spiNorFlash) : Pinetime::Drivers::SpiNorFlash& spiNorFlash,
Controllers::HeartRateController& heartRateController) :
systemTask{systemTask}, systemTask{systemTask},
bleController{bleController}, bleController{bleController},
dateTimeController{dateTimeController}, dateTimeController{dateTimeController},
@ -36,7 +37,8 @@ NimbleController::NimbleController(Pinetime::System::SystemTask& systemTask,
musicService{systemTask}, musicService{systemTask},
batteryInformationService{batteryController}, batteryInformationService{batteryController},
immediateAlertService{systemTask, notificationManager}, immediateAlertService{systemTask, notificationManager},
serviceDiscovery({&currentTimeClient, &alertNotificationClient}) { serviceDiscovery({&currentTimeClient, &alertNotificationClient}),
heartRateService{systemTask, heartRateController} {
} }
int GAPEventCallback(struct ble_gap_event *event, void *arg) { int GAPEventCallback(struct ble_gap_event *event, void *arg) {
@ -58,6 +60,7 @@ void NimbleController::Init() {
dfuService.Init(); dfuService.Init();
batteryInformationService.Init(); batteryInformationService.Init();
immediateAlertService.Init(); immediateAlertService.Init();
heartRateService.Init();
int res; int res;
res = ble_hs_util_ensure_addr(0); res = ble_hs_util_ensure_addr(0);
ASSERT(res == 0); ASSERT(res == 0);

View File

@ -17,6 +17,7 @@
#include "ImmediateAlertService.h" #include "ImmediateAlertService.h"
#include "MusicService.h" #include "MusicService.h"
#include "ServiceDiscovery.h" #include "ServiceDiscovery.h"
#include "HeartRateService.h"
namespace Pinetime { namespace Pinetime {
namespace Drivers { namespace Drivers {
@ -37,7 +38,8 @@ namespace Pinetime {
public: public:
NimbleController(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::Ble& bleController, NimbleController(Pinetime::System::SystemTask& systemTask, Pinetime::Controllers::Ble& bleController,
DateTime& dateTimeController, Pinetime::Controllers::NotificationManager& notificationManager, DateTime& dateTimeController, Pinetime::Controllers::NotificationManager& notificationManager,
Controllers::Battery& batteryController, Pinetime::Drivers::SpiNorFlash& spiNorFlash); Controllers::Battery& batteryController, Pinetime::Drivers::SpiNorFlash& spiNorFlash,
Controllers::HeartRateController& heartRateController);
void Init(); void Init();
void StartAdvertising(); void StartAdvertising();
int OnGAPEvent(ble_gap_event *event); int OnGAPEvent(ble_gap_event *event);
@ -74,6 +76,7 @@ namespace Pinetime {
MusicService musicService; MusicService musicService;
BatteryInformationService batteryInformationService; BatteryInformationService batteryInformationService;
ImmediateAlertService immediateAlertService; ImmediateAlertService immediateAlertService;
HeartRateService heartRateService;
uint8_t addrType; // 1 = Random, 0 = PUBLIC uint8_t addrType; // 1 = Random, 0 = PUBLIC
uint16_t connectionHandle = 0; uint16_t connectionHandle = 0;

View File

@ -11,7 +11,10 @@ HeartRateController::HeartRateController(Pinetime::System::SystemTask &systemTas
void HeartRateController::Update(HeartRateController::States newState, uint8_t heartRate) { void HeartRateController::Update(HeartRateController::States newState, uint8_t heartRate) {
this->state = newState; this->state = newState;
if(this->heartRate != heartRate) {
this->heartRate = heartRate; this->heartRate = heartRate;
service->OnNewHeartRateValue(heartRate);
}
} }
void HeartRateController::Start() { void HeartRateController::Start() {
@ -32,3 +35,7 @@ void HeartRateController::SetHeartRateTask(Pinetime::Applications::HeartRateTask
this->task = task; this->task = task;
} }
void HeartRateController::SetService(Pinetime::Controllers::HeartRateService *service) {
this->service = service;
}

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <components/ble/HeartRateService.h>
namespace Pinetime { namespace Pinetime {
namespace Applications { namespace Applications {
@ -24,11 +25,14 @@ namespace Pinetime {
States State() const { return state; } States State() const { return state; }
uint8_t HeartRate() const { return heartRate; } uint8_t HeartRate() const { return heartRate; }
void SetService(Pinetime::Controllers::HeartRateService *service);
private: private:
System::SystemTask& systemTask; System::SystemTask& systemTask;
Applications::HeartRateTask* task = nullptr; Applications::HeartRateTask* task = nullptr;
States state = States::Stopped; States state = States::Stopped;
uint8_t heartRate = 0; uint8_t heartRate = 0;
Pinetime::Controllers::HeartRateService* service = nullptr;
}; };
} }
} }

View File

@ -47,8 +47,8 @@ SystemTask::SystemTask(Drivers::SpiMaster &spi, Drivers::St7789 &lcd,
heartRateController{*this}, heartRateController{*this},
bleController{bleController}, dateTimeController{dateTimeController}, bleController{bleController}, dateTimeController{dateTimeController},
watchdog{}, watchdogView{watchdog}, notificationManager{notificationManager}, watchdog{}, watchdogView{watchdog}, notificationManager{notificationManager},
nimbleController(*this, bleController,dateTimeController, notificationManager, batteryController, spiNorFlash), heartRateSensor{heartRateSensor},
heartRateSensor{heartRateSensor}{ nimbleController(*this, bleController,dateTimeController, notificationManager, batteryController, spiNorFlash, heartRateController) {
systemTasksMsgQueue = xQueueCreate(10, 1); systemTasksMsgQueue = xQueueCreate(10, 1);
} }

View File

@ -74,8 +74,8 @@ namespace Pinetime {
Pinetime::Drivers::Watchdog watchdog; Pinetime::Drivers::Watchdog watchdog;
Pinetime::Drivers::WatchdogView watchdogView; Pinetime::Drivers::WatchdogView watchdogView;
Pinetime::Controllers::NotificationManager& notificationManager; Pinetime::Controllers::NotificationManager& notificationManager;
Pinetime::Controllers::NimbleController nimbleController;
Pinetime::Drivers::Hrs3300& heartRateSensor; Pinetime::Drivers::Hrs3300& heartRateSensor;
Pinetime::Controllers::NimbleController nimbleController;
static constexpr uint8_t pinSpiSck = 2; static constexpr uint8_t pinSpiSck = 2;
static constexpr uint8_t pinSpiMosi = 3; static constexpr uint8_t pinSpiMosi = 3;