Skip to content

Commit f2b1383

Browse files
mark9064patgruber
andcommitted
Background heartrate measurement
Co-Authored-By: Patric Gruber <me@patric-gruber.at>
1 parent c9a9e72 commit f2b1383

File tree

15 files changed

+343
-87
lines changed

15 files changed

+343
-87
lines changed

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,7 @@ list(APPEND SOURCE_FILES
412412
displayapp/screens/settings/SettingWeatherFormat.cpp
413413
displayapp/screens/settings/SettingWakeUp.cpp
414414
displayapp/screens/settings/SettingDisplay.cpp
415+
displayapp/screens/settings/SettingHeartRate.cpp
415416
displayapp/screens/settings/SettingSteps.cpp
416417
displayapp/screens/settings/SettingSetDateTime.cpp
417418
displayapp/screens/settings/SettingSetDate.cpp

src/components/heartrate/HeartRateController.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,17 @@ void HeartRateController::Update(HeartRateController::States newState, uint8_t h
1212
}
1313
}
1414

15-
void HeartRateController::Start() {
15+
void HeartRateController::Enable() {
1616
if (task != nullptr) {
1717
state = States::NotEnoughData;
18-
task->PushMessage(Pinetime::Applications::HeartRateTask::Messages::StartMeasurement);
18+
task->PushMessage(Pinetime::Applications::HeartRateTask::Messages::Enable);
1919
}
2020
}
2121

22-
void HeartRateController::Stop() {
22+
void HeartRateController::Disable() {
2323
if (task != nullptr) {
2424
state = States::Stopped;
25-
task->PushMessage(Pinetime::Applications::HeartRateTask::Messages::StopMeasurement);
25+
task->PushMessage(Pinetime::Applications::HeartRateTask::Messages::Disable);
2626
}
2727
}
2828

src/components/heartrate/HeartRateController.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ namespace Pinetime {
1515
namespace Controllers {
1616
class HeartRateController {
1717
public:
18-
enum class States { Stopped, NotEnoughData, NoTouch, Running };
18+
enum class States : uint8_t { Stopped, NotEnoughData, NoTouch, Running };
1919

2020
HeartRateController() = default;
21-
void Start();
22-
void Stop();
21+
void Enable();
22+
void Disable();
2323
void Update(States newState, uint8_t heartRate);
2424

2525
void SetHeartRateTask(Applications::HeartRateTask* task);

src/components/heartrate/Ppg.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,12 @@ int8_t Ppg::Preprocess(uint16_t hrs, uint16_t als) {
155155

156156
int Ppg::HeartRate() {
157157
if (dataIndex < dataLength) {
158+
if (!enoughData) {
159+
return -2;
160+
}
158161
return 0;
159162
}
163+
enoughData = true;
160164
int hr = 0;
161165
hr = ProcessHeartRate(resetSpectralAvg);
162166
resetSpectralAvg = false;
@@ -171,6 +175,7 @@ int Ppg::HeartRate() {
171175
void Ppg::Reset(bool resetDaqBuffer) {
172176
if (resetDaqBuffer) {
173177
dataIndex = 0;
178+
enoughData = false;
174179
}
175180
avgIndex = 0;
176181
dataAverage.fill(0.0f);

src/components/heartrate/Ppg.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ namespace Pinetime {
7171
uint16_t dataIndex = 0;
7272
float peakLocation;
7373
bool resetSpectralAvg = true;
74+
bool enoughData = false;
7475

7576
int ProcessHeartRate(bool init);
7677
float HeartRateAverage(float hr);

src/components/settings/Settings.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#pragma once
22
#include <cstdint>
33
#include <bitset>
4+
#include <limits>
5+
#include <optional>
46
#include "components/brightness/BrightnessController.h"
57
#include "components/fs/FS.h"
68
#include "displayapp/apps/Apps.h"
@@ -334,10 +336,25 @@ namespace Pinetime {
334336
return (settings.dfuAndFsEnabledOnBoot ? DfuAndFsMode::Enabled : DfuAndFsMode::Disabled);
335337
};
336338

339+
std::optional<uint16_t> GetHeartRateBackgroundMeasurementInterval() const {
340+
if (settings.heartRateBackgroundPeriod == std::numeric_limits<uint16_t>::max()) {
341+
return std::nullopt;
342+
}
343+
return settings.heartRateBackgroundPeriod;
344+
}
345+
346+
void SetHeartRateBackgroundMeasurementInterval(std::optional<uint16_t> newIntervalInSeconds) {
347+
newIntervalInSeconds = newIntervalInSeconds.value_or(std::numeric_limits<uint16_t>::max());
348+
if (newIntervalInSeconds != settings.heartRateBackgroundPeriod) {
349+
settingsChanged = true;
350+
}
351+
settings.heartRateBackgroundPeriod = newIntervalInSeconds.value();
352+
}
353+
337354
private:
338355
Pinetime::Controllers::FS& fs;
339356

340-
static constexpr uint32_t settingsVersion = 0x0009;
357+
static constexpr uint32_t settingsVersion = 0x000a;
341358

342359
struct SettingsData {
343360
uint32_t version = settingsVersion;
@@ -365,6 +382,7 @@ namespace Pinetime {
365382
Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium;
366383

367384
bool dfuAndFsEnabledOnBoot = false;
385+
uint16_t heartRateBackgroundPeriod = std::numeric_limits<uint16_t>::max(); // Disabled by default
368386
};
369387

370388
SettingsData settings;

src/displayapp/DisplayApp.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#include "displayapp/screens/settings/SettingSteps.h"
4949
#include "displayapp/screens/settings/SettingSetDateTime.h"
5050
#include "displayapp/screens/settings/SettingChimes.h"
51+
#include "displayapp/screens/settings/SettingHeartRate.h"
5152
#include "displayapp/screens/settings/SettingShakeThreshold.h"
5253
#include "displayapp/screens/settings/SettingBluetooth.h"
5354
#include "displayapp/screens/settings/SettingOTA.h"
@@ -603,6 +604,9 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
603604
case Apps::SettingWakeUp:
604605
currentScreen = std::make_unique<Screens::SettingWakeUp>(settingsController);
605606
break;
607+
case Apps::SettingHeartRate:
608+
currentScreen = std::make_unique<Screens::SettingHeartRate>(settingsController);
609+
break;
606610
case Apps::SettingDisplay:
607611
currentScreen = std::make_unique<Screens::SettingDisplay>(settingsController);
608612
break;

src/displayapp/apps/Apps.h.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ namespace Pinetime {
3636
SettingWatchFace,
3737
SettingTimeFormat,
3838
SettingWeatherFormat,
39+
SettingHeartRate,
3940
SettingDisplay,
4041
SettingWakeUp,
4142
SettingSteps,

src/displayapp/screens/HeartRate.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,12 @@ void HeartRate::Refresh() {
9898
void HeartRate::OnStartStopEvent(lv_event_t event) {
9999
if (event == LV_EVENT_CLICKED) {
100100
if (heartRateController.State() == Controllers::HeartRateController::States::Stopped) {
101-
heartRateController.Start();
101+
heartRateController.Enable();
102102
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
103103
wakeLock.Lock();
104104
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight);
105105
} else {
106-
heartRateController.Stop();
106+
heartRateController.Disable();
107107
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
108108
wakeLock.Release();
109109
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray);
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#include "displayapp/screens/settings/SettingHeartRate.h"
2+
#include <lvgl/lvgl.h>
3+
#include "displayapp/screens/Styles.h"
4+
#include "displayapp/screens/Symbols.h"
5+
6+
using namespace Pinetime::Applications::Screens;
7+
8+
namespace {
9+
void EventHandler(lv_obj_t* obj, lv_event_t event) {
10+
auto* screen = static_cast<SettingHeartRate*>(obj->user_data);
11+
screen->UpdateSelected(obj, event);
12+
}
13+
}
14+
15+
SettingHeartRate::SettingHeartRate(Pinetime::Controllers::Settings& settingsController) : settingsController {settingsController} {
16+
lv_obj_t* container = lv_cont_create(lv_scr_act(), nullptr);
17+
18+
lv_obj_set_style_local_bg_opa(container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
19+
lv_obj_set_style_local_pad_all(container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5);
20+
lv_obj_set_style_local_pad_inner(container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5);
21+
lv_obj_set_style_local_border_width(container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0);
22+
23+
lv_obj_set_pos(container, 10, 60);
24+
lv_obj_set_width(container, LV_HOR_RES - 20);
25+
lv_obj_set_height(container, LV_VER_RES - 50);
26+
lv_cont_set_layout(container, LV_LAYOUT_PRETTY_TOP);
27+
28+
lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr);
29+
lv_label_set_text_static(title, "Backg. Interval");
30+
lv_label_set_text(title, "Backg. Interval");
31+
lv_label_set_align(title, LV_LABEL_ALIGN_CENTER);
32+
lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 10, 15);
33+
34+
lv_obj_t* icon = lv_label_create(lv_scr_act(), nullptr);
35+
lv_obj_set_style_local_text_color(icon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED);
36+
lv_label_set_text_static(icon, Symbols::heartBeat);
37+
lv_label_set_align(icon, LV_LABEL_ALIGN_CENTER);
38+
lv_obj_align(icon, title, LV_ALIGN_OUT_LEFT_MID, -10, 0);
39+
40+
std::optional<uint16_t> currentInterval = settingsController.GetHeartRateBackgroundMeasurementInterval();
41+
42+
for (std::size_t i = 0; i < options.size(); i++) {
43+
cbOption[i] = lv_checkbox_create(container, nullptr);
44+
lv_checkbox_set_text(cbOption[i], options[i].name);
45+
cbOption[i]->user_data = this;
46+
lv_obj_set_event_cb(cbOption[i], EventHandler);
47+
SetRadioButtonStyle(cbOption[i]);
48+
49+
if (options[i].intervalInSeconds == currentInterval) {
50+
lv_checkbox_set_checked(cbOption[i], true);
51+
}
52+
}
53+
}
54+
55+
SettingHeartRate::~SettingHeartRate() {
56+
lv_obj_clean(lv_scr_act());
57+
settingsController.SaveSettings();
58+
}
59+
60+
void SettingHeartRate::UpdateSelected(lv_obj_t* object, lv_event_t event) {
61+
if (event == LV_EVENT_CLICKED) {
62+
for (std::size_t i = 0; i < options.size(); i++) {
63+
if (object == cbOption[i]) {
64+
lv_checkbox_set_checked(cbOption[i], true);
65+
settingsController.SetHeartRateBackgroundMeasurementInterval(options[i].intervalInSeconds);
66+
} else {
67+
lv_checkbox_set_checked(cbOption[i], false);
68+
}
69+
}
70+
}
71+
}

0 commit comments

Comments
 (0)