From 8df49f434fd4b2fc9fea4e0ef8c8877e49c276d8 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Sat, 30 Aug 2025 04:22:25 +0200 Subject: [PATCH] Use std::optional for heart rate If no value is available, use null. Use this distinction to differentiate in the UI between missing data and zero value. Still sends value 0 over BLE. I'm unsure whether this should change or not --- src/components/ble/HeartRateService.cpp | 6 +++--- src/components/ble/HeartRateService.h | 3 ++- src/components/heartrate/HeartRateController.cpp | 3 ++- src/components/heartrate/HeartRateController.h | 9 +++++---- src/displayapp/screens/HeartRate.cpp | 5 +++-- src/displayapp/screens/WatchFaceCasioStyleG7710.cpp | 7 ++++++- src/displayapp/screens/WatchFaceCasioStyleG7710.h | 2 +- src/displayapp/screens/WatchFaceDigital.cpp | 7 ++++++- src/displayapp/screens/WatchFaceDigital.h | 2 +- src/displayapp/screens/WatchFaceTerminal.cpp | 5 +++-- src/displayapp/screens/WatchFaceTerminal.h | 2 +- src/heartratetask/HeartRateTask.cpp | 11 ++++++----- 12 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/components/ble/HeartRateService.cpp b/src/components/ble/HeartRateService.cpp index d34dbf83eb..d57e010901 100644 --- a/src/components/ble/HeartRateService.cpp +++ b/src/components/ble/HeartRateService.cpp @@ -48,7 +48,7 @@ void HeartRateService::Init() { int HeartRateService::OnHeartRateRequested(uint16_t attributeHandle, ble_gatt_access_ctxt* context) { if (attributeHandle == heartRateMeasurementHandle) { NRF_LOG_INFO("HEARTRATE : handle = %d", heartRateMeasurementHandle); - uint8_t buffer[2] = {0, heartRateController.HeartRate()}; // [0] = flags, [1] = hr value + uint8_t buffer[2] = {0, heartRateController.HeartRate().value_or(0)}; // [0] = flags, [1] = hr value int res = os_mbuf_append(context->om, buffer, 2); return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES; @@ -56,11 +56,11 @@ int HeartRateService::OnHeartRateRequested(uint16_t attributeHandle, ble_gatt_ac return 0; } -void HeartRateService::OnNewHeartRateValue(uint8_t heartRateValue) { +void HeartRateService::OnNewHeartRateValue(std::optional heartRateValue) { if (!heartRateMeasurementNotificationEnable) return; - uint8_t buffer[2] = {0, heartRateValue}; // [0] = flags, [1] = hr value + uint8_t buffer[2] = {0, heartRateValue.value_or(0)}; // [0] = flags, [1] = hr value auto* om = ble_hs_mbuf_from_flat(buffer, 2); uint16_t connectionHandle = nimble.connHandle(); diff --git a/src/components/ble/HeartRateService.h b/src/components/ble/HeartRateService.h index ca8f10fb8d..131e4be3aa 100644 --- a/src/components/ble/HeartRateService.h +++ b/src/components/ble/HeartRateService.h @@ -5,6 +5,7 @@ #undef max #undef min #include +#include namespace Pinetime { namespace Controllers { @@ -16,7 +17,7 @@ namespace Pinetime { HeartRateService(NimbleController& nimble, Controllers::HeartRateController& heartRateController); void Init(); int OnHeartRateRequested(uint16_t attributeHandle, ble_gatt_access_ctxt* context); - void OnNewHeartRateValue(uint8_t hearRateValue); + void OnNewHeartRateValue(std::optional hearRateValue); void SubscribeNotification(uint16_t attributeHandle); void UnsubscribeNotification(uint16_t attributeHandle); diff --git a/src/components/heartrate/HeartRateController.cpp b/src/components/heartrate/HeartRateController.cpp index e0d692729e..cfc4c8117c 100644 --- a/src/components/heartrate/HeartRateController.cpp +++ b/src/components/heartrate/HeartRateController.cpp @@ -1,10 +1,11 @@ #include "components/heartrate/HeartRateController.h" #include #include +#include using namespace Pinetime::Controllers; -void HeartRateController::Update(HeartRateController::States newState, uint8_t heartRate) { +void HeartRateController::Update(HeartRateController::States newState, std::optional heartRate) { this->state = newState; if (this->heartRate != heartRate) { this->heartRate = heartRate; diff --git a/src/components/heartrate/HeartRateController.h b/src/components/heartrate/HeartRateController.h index f66c79f830..8cee88f6f5 100644 --- a/src/components/heartrate/HeartRateController.h +++ b/src/components/heartrate/HeartRateController.h @@ -2,6 +2,7 @@ #include #include +#include namespace Pinetime { namespace Applications { @@ -20,7 +21,7 @@ namespace Pinetime { HeartRateController() = default; void Start(); void Stop(); - void Update(States newState, uint8_t heartRate); + void Update(States newState, std::optional heartRate); void SetHeartRateTask(Applications::HeartRateTask* task); @@ -28,7 +29,7 @@ namespace Pinetime { return state; } - uint8_t HeartRate() const { + std::optional HeartRate() const { return heartRate; } @@ -37,8 +38,8 @@ namespace Pinetime { private: Applications::HeartRateTask* task = nullptr; States state = States::Stopped; - uint8_t heartRate = 0; + std::optional heartRate = std::nullopt; Pinetime::Controllers::HeartRateService* service = nullptr; }; } -} \ No newline at end of file +} diff --git a/src/displayapp/screens/HeartRate.cpp b/src/displayapp/screens/HeartRate.cpp index 1a84d34928..044ec5794c 100644 --- a/src/displayapp/screens/HeartRate.cpp +++ b/src/displayapp/screens/HeartRate.cpp @@ -84,10 +84,11 @@ void HeartRate::Refresh() { lv_label_set_text_static(label_hr, "---"); break; default: - if (heartRateController.HeartRate() == 0) { + auto hb = heartRateController.HeartRate(); + if (!hb) { lv_label_set_text_static(label_hr, "---"); } else { - lv_label_set_text_fmt(label_hr, "%03d", heartRateController.HeartRate()); + lv_label_set_text_fmt(label_hr, "%03d", hb.value()); } } diff --git a/src/displayapp/screens/WatchFaceCasioStyleG7710.cpp b/src/displayapp/screens/WatchFaceCasioStyleG7710.cpp index c695f852fe..4609769a8b 100644 --- a/src/displayapp/screens/WatchFaceCasioStyleG7710.cpp +++ b/src/displayapp/screens/WatchFaceCasioStyleG7710.cpp @@ -294,7 +294,12 @@ void WatchFaceCasioStyleG7710::Refresh() { if (heartbeat.IsUpdated() || heartbeatRunning.IsUpdated()) { if (heartbeatRunning.Get()) { lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, color_text); - lv_label_set_text_fmt(heartbeatValue, "%d", heartbeat.Get()); + auto hb = heartbeat.Get(); + if (hb.has_value()) { + lv_label_set_text_fmt(heartbeatValue, "%d", hb.value()); + } else { + lv_label_set_text_static(heartbeatValue, "-"); + } } else { lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x1B1B1B)); lv_label_set_text_static(heartbeatValue, ""); diff --git a/src/displayapp/screens/WatchFaceCasioStyleG7710.h b/src/displayapp/screens/WatchFaceCasioStyleG7710.h index 0f46a69251..b92ca35c0c 100644 --- a/src/displayapp/screens/WatchFaceCasioStyleG7710.h +++ b/src/displayapp/screens/WatchFaceCasioStyleG7710.h @@ -48,7 +48,7 @@ namespace Pinetime { Utility::DirtyValue bleRadioEnabled {}; Utility::DirtyValue> currentDateTime {}; Utility::DirtyValue stepCount {}; - Utility::DirtyValue heartbeat {}; + Utility::DirtyValue> heartbeat {}; Utility::DirtyValue heartbeatRunning {}; Utility::DirtyValue notificationState {}; Utility::DirtyValue> currentDate; diff --git a/src/displayapp/screens/WatchFaceDigital.cpp b/src/displayapp/screens/WatchFaceDigital.cpp index 3163c6e750..b91d6059bc 100644 --- a/src/displayapp/screens/WatchFaceDigital.cpp +++ b/src/displayapp/screens/WatchFaceDigital.cpp @@ -155,7 +155,12 @@ void WatchFaceDigital::Refresh() { if (heartbeat.IsUpdated() || heartbeatRunning.IsUpdated()) { if (heartbeatRunning.Get()) { lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0xCE1B1B)); - lv_label_set_text_fmt(heartbeatValue, "%d", heartbeat.Get()); + auto hb = heartbeat.Get(); + if (hb.has_value()) { + lv_label_set_text_fmt(heartbeatValue, "%d", hb.value()); + } else { + lv_label_set_text_static(heartbeatValue, "-"); + } } else { lv_obj_set_style_local_text_color(heartbeatIcon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x1B1B1B)); lv_label_set_text_static(heartbeatValue, ""); diff --git a/src/displayapp/screens/WatchFaceDigital.h b/src/displayapp/screens/WatchFaceDigital.h index 3005cea56f..3870299fe3 100644 --- a/src/displayapp/screens/WatchFaceDigital.h +++ b/src/displayapp/screens/WatchFaceDigital.h @@ -47,7 +47,7 @@ namespace Pinetime { Utility::DirtyValue> currentDateTime {}; Utility::DirtyValue stepCount {}; - Utility::DirtyValue heartbeat {}; + Utility::DirtyValue> heartbeat {}; Utility::DirtyValue heartbeatRunning {}; Utility::DirtyValue notificationState {}; Utility::DirtyValue> currentWeather {}; diff --git a/src/displayapp/screens/WatchFaceTerminal.cpp b/src/displayapp/screens/WatchFaceTerminal.cpp index 96d77741fd..1a2bce38b9 100644 --- a/src/displayapp/screens/WatchFaceTerminal.cpp +++ b/src/displayapp/screens/WatchFaceTerminal.cpp @@ -137,8 +137,9 @@ void WatchFaceTerminal::Refresh() { heartbeat = heartRateController.HeartRate(); heartbeatRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped; if (heartbeat.IsUpdated() || heartbeatRunning.IsUpdated()) { - if (heartbeatRunning.Get()) { - lv_label_set_text_fmt(heartbeatValue, "[L_HR]#ee3311 %d bpm#", heartbeat.Get()); + auto hb = heartbeat.Get(); + if (hb.has_value()) { + lv_label_set_text_fmt(heartbeatValue, "[L_HR]#ee3311 %d bpm#", hb.value()); } else { lv_label_set_text_static(heartbeatValue, "[L_HR]#ee3311 ---#"); } diff --git a/src/displayapp/screens/WatchFaceTerminal.h b/src/displayapp/screens/WatchFaceTerminal.h index bf4608660f..13a719ad56 100644 --- a/src/displayapp/screens/WatchFaceTerminal.h +++ b/src/displayapp/screens/WatchFaceTerminal.h @@ -42,7 +42,7 @@ namespace Pinetime { Utility::DirtyValue bleRadioEnabled {}; Utility::DirtyValue> currentDateTime {}; Utility::DirtyValue stepCount {}; - Utility::DirtyValue heartbeat {}; + Utility::DirtyValue> heartbeat {}; Utility::DirtyValue heartbeatRunning {}; Utility::DirtyValue notificationState {}; Utility::DirtyValue> currentDate; diff --git a/src/heartratetask/HeartRateTask.cpp b/src/heartratetask/HeartRateTask.cpp index 8a5a871b41..327d70f684 100644 --- a/src/heartratetask/HeartRateTask.cpp +++ b/src/heartratetask/HeartRateTask.cpp @@ -2,6 +2,7 @@ #include #include #include +#include using namespace Pinetime::Applications; @@ -24,7 +25,7 @@ void HeartRateTask::Process(void* instance) { } void HeartRateTask::Work() { - int lastBpm = 0; + std::optional lastBpm = std::nullopt; while (true) { Messages msg; uint32_t delay; @@ -47,7 +48,7 @@ void HeartRateTask::Work() { case Messages::WakeUp: state = States::Running; if (measurementStarted) { - lastBpm = 0; + lastBpm = std::nullopt; StartMeasurement(); } break; @@ -55,7 +56,7 @@ void HeartRateTask::Work() { if (measurementStarted) { break; } - lastBpm = 0; + lastBpm = std::nullopt; StartMeasurement(); measurementStarted = true; break; @@ -79,7 +80,7 @@ void HeartRateTask::Work() { // Reset all DAQ buffers ppg.Reset(true); // Force state to NotEnoughData (below) - lastBpm = 0; + lastBpm = std::nullopt; bpm = 0; } else if (bpm < 0) { // Reset all DAQ buffers except HRS buffer @@ -94,7 +95,7 @@ void HeartRateTask::Work() { } if (bpm != 0) { - lastBpm = bpm; + lastBpm = std::make_optional(bpm); controller.Update(Controllers::HeartRateController::States::Running, lastBpm); } }