Skip to content

Commit e5b9230

Browse files
committed
Add heart rate zone screen
1 parent 599fd01 commit e5b9230

12 files changed

Lines changed: 45 additions & 62 deletions

File tree

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ list(APPEND SOURCE_FILES
403403
displayapp/widgets/PageIndicator.cpp
404404
displayapp/widgets/DotIndicator.cpp
405405
displayapp/widgets/StatusIcons.cpp
406+
displayapp/screens/HeartRateZone.cpp
406407

407408
## Settings
408409
displayapp/screens/settings/QuickSettings.cpp

src/components/heartrate/HeartRateController.cpp

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,34 @@
44

55
using namespace Pinetime::Controllers;
66

7+
HeartRateZoneSettings::HeartRateZoneSettings() {
8+
version = 0;
9+
adjustMsDelay = 300000;
10+
age = 25;
11+
maxHeartRate = maxHeartRateEstimate(age);
12+
percentTarget = {50, 60, 70, 80, 90};
13+
bpmTarget = bpmZones(percentTarget, maxHeartRate);
14+
allowCalibration = true;
15+
};
16+
17+
HeartRateController::HeartRateController(Pinetime::Controllers::FS& fs) : fs {fs} {};
18+
719
void HeartRateController::Update(HeartRateController::States newState, uint8_t heartRate) {
820
this->state = newState;
921
if (this->heartRate != heartRate) {
1022
uint32_t ts = xTaskGetTickCount();
1123
uint32_t z;
1224
zone = 0;
13-
auto adjustMax = pdMS_TO_TICKS(zoneSettings.adjustDelay);
14-
for (z = zoneSettings.bpmTarget.size() - 1; i < zoneSettings.bpmTarget.size(); --i) {
15-
if (this->heartRate >= zoneSettings.bpmTarget[i]) {
25+
auto adjustMax = pdMS_TO_TICKS(this->zSettings.adjustMsDelay);
26+
for (z = this->zSettings.bpmTarget.size() - 1; z < this->zSettings.bpmTarget.size(); --z) {
27+
if (this->heartRate >= this->zSettings.bpmTarget[z]) {
1628
uint32_t dt = ts - lastActiveTime;
17-
currentActivity.zoneTime[i] += dt;
29+
currentActivity.zoneTime[z] += dt;
1830
zone = z + 1;
1931
// don't make increases unless this is consistantly higher than normal (zone 5 is max)
20-
if (zoneSettings.allowCalibration && zone >= 5 && dt > adjustMax) {
21-
zoneSettings.maxHeartRate = zoneSettings.maxHeartRate >= this->heartRate ? zoneSettings.maxHeartRate : this->heartRate;
22-
zoneSettings.bpmTarget = bpmZones(zoneSettings.percentTarget, zoneSettings.maxHeartRate);
32+
if (this->zSettings.allowCalibration && zone >= 5 && dt > adjustMax) {
33+
this->zSettings.maxHeartRate = this->zSettings.maxHeartRate >= this->heartRate ? this->zSettings.maxHeartRate : this->heartRate;
34+
this->zSettings.bpmTarget = bpmZones(this->zSettings.percentTarget, this->zSettings.maxHeartRate);
2335
}
2436
break;
2537
}
@@ -36,8 +48,9 @@ void HeartRateController::AdvanceDay() {
3648
HeartRateZones<uint16_t> convertedActivity {};
3749
auto ticksPerSecond = pdMS_TO_TICKS(1000);
3850

51+
auto totalTime = currentActivity.totalTime();
3952
for (uint32_t i = 0; i < convertedActivity.zoneTime.size(); i++) {
40-
convertedActivity.zoneTime[i] = fixed_rounding(currentActivity.totalTime, ticksPerSecond);
53+
convertedActivity.zoneTime[i] = fixed_rounding(totalTime, ticksPerSecond);
4154
}
4255

4356
activity[0] = convertedActivity;
@@ -56,9 +69,9 @@ void HeartRateController::SaveSettingsToFile() const {
5669
return;
5770
}
5871

59-
fs.FileWrite(&heartRateZoneFile, reinterpret_cast<const uint8_t*>(&zoneSettings), sizeof(zoneSettings));
72+
fs.FileWrite(&heartRateZoneFile, reinterpret_cast<const uint8_t*>(&(this->zSettings)), sizeof(this->zSettings));
6073
fs.FileClose(&heartRateZoneFile);
61-
NRF_LOG_INFO("[HeartRateController] Saved heart rate zone settings with format version %u to file", zoneSettings.version);
74+
NRF_LOG_INFO("[HeartRateController] Saved heart rate zone settings with format version %u to file", this->zSettings.version);
6275

6376
lfs_file_t zoneDataFile;
6477
if (fs.FileOpen(&zoneDataFile, "/.system/hrz.dat", LFS_O_WRONLY | LFS_O_CREAT) != LFS_ERR_OK) {
@@ -68,8 +81,7 @@ void HeartRateController::SaveSettingsToFile() const {
6881

6982
fs.FileWrite(&zoneDataFile, reinterpret_cast<const uint8_t*>(&activity), sizeof(activity));
7083
fs.FileClose(&zoneDataFile);
71-
NRF_LOG_INFO("[HeartRateController] Saved heart rate zone data with format version %u to file", zoneSettings.version);
72-
84+
NRF_LOG_INFO("[HeartRateController] Saved heart rate zone data with format version %u to file", this->zSettings.version);
7385
}
7486

7587
void HeartRateController::Enable() {

src/components/heartrate/HeartRateController.h

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <cstdint>
44
#include <components/ble/HeartRateService.h>
55
#include "utility/CircularBuffer.h"
6+
#include "components/fs/FS.h"
67
#include <array>
78

89
namespace Pinetime {
@@ -18,7 +19,7 @@ namespace Pinetime {
1819
template <typename T>
1920
struct HeartRateZones {
2021
// 1440 minutes in a day (11 bits), 86400 seconds (17 bits)
21-
T zoneTime[5] = {};
22+
std::array<T,5> zoneTime = {};
2223

2324
T totalTime() const {
2425
return zoneTime[0] + zoneTime[1] + zoneTime[2] + zoneTime[3] + zoneTime[4];
@@ -48,21 +49,21 @@ namespace Pinetime {
4849

4950
struct HeartRateZoneSettings {
5051
uint32_t version = 0;
52+
uint32_t adjustMsDelay = 300000;
5153
uint8_t age = 25;
52-
uint8_t maxHeartRate = maxHeartRateEstimate(age);
54+
uint8_t maxHeartRate = 195;
5355
std::array<uint8_t, 5> percentTarget = {50, 60, 70, 80, 90};
54-
std::array<uint8_t, 5> bpmTarget = bpmZones(percentTarget, maxHeartRate);
55-
uint32_t adjustMsDelay = 300000;
56+
std::array<uint8_t, 5> bpmTarget = {98, 117, 137, 156, 176};
5657
bool allowCalibration = true;
57-
};
5858

59-
HeartRateZoneSettings zoneSettings {};
59+
HeartRateZoneSettings();
60+
};
6061

6162
class HeartRateController {
6263
public:
6364
enum class States : uint8_t { Stopped, NotEnoughData, NoTouch, Running };
6465

65-
HeartRateController() = default;
66+
HeartRateController(Pinetime::Controllers::FS& fs);
6667
void Enable();
6768
void Disable();
6869
void Update(States newState, uint8_t heartRate);
@@ -88,14 +89,16 @@ namespace Pinetime {
8889
void SetService(Pinetime::Controllers::HeartRateService* service);
8990

9091
void AdvanceDay();
91-
void SaveSettingsToFile();
92+
void SaveSettingsToFile() const;
9293

9394
private:
9495
Applications::HeartRateTask* task = nullptr;
9596
States state = States::Stopped;
9697
uint8_t heartRate = 0;
9798
Pinetime::Controllers::HeartRateService* service = nullptr;
99+
Pinetime::Controllers::FS& fs;
98100

101+
HeartRateZoneSettings zSettings = {};
99102
uint32_t lastActiveTime = 0;
100103
// Heart Rate Zone Storage
101104
HeartRateZones<uint32_t> currentActivity = {};

src/displayapp/DisplayApp.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "displayapp/screens/PassKey.h"
3232
#include "displayapp/screens/Error.h"
3333
#include "displayapp/screens/Calculator.h"
34+
#include "displayapp/screens/HeartRateZone.h"
3435

3536
#include "drivers/Cst816s.h"
3637
#include "drivers/St7789.h"

src/displayapp/UserApps.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "displayapp/screens/Timer.h"
88
#include "displayapp/screens/Twos.h"
99
#include "displayapp/screens/Tile.h"
10+
#include "displayapp/screens/HeartRateZone.h"
1011
#include "displayapp/screens/ApplicationList.h"
1112
#include "displayapp/screens/WatchFaceDigital.h"
1213
#include "displayapp/screens/WatchFaceAnalog.h"

src/displayapp/apps/Apps.h.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ namespace Pinetime {
2222
Paddle,
2323
Twos,
2424
HeartRate,
25+
HeartRateZone,
2526
Navigation,
2627
StopWatch,
2728
Metronome,

src/displayapp/apps/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ else ()
66
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Timer")
77
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Steps")
88
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::HeartRate")
9+
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::HeartRateZone")
910
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Music")
1011
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Paint")
1112
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Paddle")

src/displayapp/screens/HeartRateZone.cpp

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,14 @@
11
#include "displayapp/screens/HeartRateZone.h"
22
#include <lvgl/lvgl.h>
3-
#include <lvgl/lv_obj_style.h>
43
#include <components/heartrate/HeartRateController.h>
54

65
#include "displayapp/DisplayApp.h"
76
#include "displayapp/InfiniTimeTheme.h"
87

98
using namespace Pinetime::Applications::Screens;
109

11-
namespace {
12-
const char* ToString(Pinetime::Controllers::HeartRateController::States s) {
13-
switch (s) {
14-
case Pinetime::Controllers::HeartRateController::States::NotEnoughData:
15-
return "Not enough data,\nplease wait...";
16-
case Pinetime::Controllers::HeartRateController::States::NoTouch:
17-
return "No touch detected";
18-
case Pinetime::Controllers::HeartRateController::States::Running:
19-
return "Measuring...";
20-
case Pinetime::Controllers::HeartRateController::States::Stopped:
21-
return "Stopped";
22-
}
23-
return "";
24-
}
25-
26-
void btnStartStopEventHandler(lv_obj_t* obj, lv_event_t event) {
27-
auto* screen = static_cast<HeartRate*>(obj->user_data);
28-
screen->OnStartStopEvent(event);
29-
}
30-
}
31-
3210
HeartRateZone::HeartRateZone(Controllers::HeartRateController& heartRateController, System::SystemTask& systemTask)
3311
: heartRateController {heartRateController}, wakeLock(systemTask) {
34-
bool isHrRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped;
35-
3612
auto activity = heartRateController.Activity();
3713
uint32_t total = 0;
3814

@@ -42,13 +18,12 @@ HeartRateZone::HeartRateZone(Controllers::HeartRateController& heartRateControll
4218
for (uint8_t i = 0; i < zone_bar.size(); i++) {
4319
zone_bar[i] = lv_bar_create(screen, nullptr);
4420

45-
//lv_bar_set_orientation(zone_bar[i], LV_BAR_ORIENTATION_HORIZONTAL);
4621
total += activity.zoneTime[i];
4722
lv_obj_set_style_local_line_color(zone_bar[i], LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt);
4823
lv_obj_set_size(zone_bar[i], 240, 20);
4924
lv_obj_align(zone_bar[i], nullptr, LV_ALIGN_IN_TOP_LEFT, 10, 25 * i);
5025

51-
label_time[i] = lv_label_create(zone_bar[i]);
26+
label_time[i] = lv_label_create(zone_bar[i],nullptr);
5227
lv_obj_align(zone_bar[i], nullptr, LV_ALIGN_CENTER, 0, 0);
5328
}
5429

src/displayapp/screens/HeartRateZone.h

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,13 @@ namespace Pinetime {
2525

2626
void Refresh() override;
2727

28-
void OnStartStopEvent(lv_event_t event);
29-
3028
private:
3129
Controllers::HeartRateController& heartRateController;
3230
Pinetime::System::WakeLock wakeLock;
33-
void UpdateStartStopButton(bool isRunning);
3431

3532
std::array<lv_obj_t*,5> zone_bar;
3633
std::array<lv_obj_t*,5> label_time;
37-
/*
38-
lv_obj_t* label_hr;
39-
lv_obj_t* label_bpm;
40-
lv_obj_t* label_status;
41-
lv_obj_t* btn_startStop;
42-
lv_obj_t* label_startStop;
43-
*/
34+
4435
lv_task_t* taskRefresh;
4536
};
4637
}

src/heartratetask/HeartRateTask.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#include "heartratetask/HeartRateTask.h"
22
#include <drivers/Hrs3300.h>
3-
#include <components/heartrate/HeartRateController.h>
3+
44
#include <limits>
55

66
using namespace Pinetime::Applications;

0 commit comments

Comments
 (0)