Add heart rate BLE service.
This commit is contained in:
parent
3a3a14115a
commit
68674cec53
|
@ -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
|
||||||
|
|
82
src/components/ble/HeartRateService.cpp
Normal file
82
src/components/ble/HeartRateService.cpp
Normal 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);
|
||||||
|
}
|
44
src/components/ble/HeartRateService.h
Normal file
44
src/components/ble/HeartRateService.h
Normal 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;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -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({¤tTimeClient, &alertNotificationClient}) {
|
serviceDiscovery({¤tTimeClient, &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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
this->heartRate = heartRate;
|
if(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue