Compare commits
48 commits
Author | SHA1 | Date | |
---|---|---|---|
D. Scott Boggs | 423dd489ef | ||
D. Scott Boggs | d93d809255 | ||
D. Scott Boggs | 6e57b178e8 | ||
D. Scott Boggs | a2a2b6a64d | ||
c613bc7afd | |||
8598142c27 | |||
a2356f2f4a | |||
3db4e012ce | |||
a0cd439efc | |||
462ea11bd4 | |||
997e4cee8c | |||
ad3bf49c7b | |||
7ca0418c82 | |||
c3d05901a0 | |||
b3756e45fa | |||
a266202831 | |||
7df39994ab | |||
9501d36060 | |||
b846547f2f | |||
71b31c78fb | |||
0978964b7d | |||
cedca795e2 | |||
78af44eafe | |||
e6f0a89202 | |||
616926345e | |||
6a0276f164 | |||
7cf4f6e1ec | |||
ffc5f96d9a | |||
3b432cd310 | |||
d78f26201b | |||
4ed4d2cfcd | |||
50d88bbe84 | |||
520e50901a | |||
04ed068ff9 | |||
69578a679a | |||
eeaf5374d4 | |||
f94c074064 | |||
a2edd931ec | |||
d376a856b7 | |||
be1a519098 | |||
27ee1eb2c8 | |||
5dbe1f77b5 | |||
7ae790bcdb | |||
a5db54af27 | |||
0370e3cd65 | |||
58c507ee45 | |||
f7b1111e05 | |||
98455aa54d |
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -4,9 +4,6 @@
|
||||||
[submodule "src/libs/littlefs"]
|
[submodule "src/libs/littlefs"]
|
||||||
path = src/libs/littlefs
|
path = src/libs/littlefs
|
||||||
url = https://github.com/littlefs-project/littlefs.git
|
url = https://github.com/littlefs-project/littlefs.git
|
||||||
[submodule "src/libs/QCBOR"]
|
|
||||||
path = src/libs/QCBOR
|
|
||||||
url = https://github.com/laurencelundblade/QCBOR.git
|
|
||||||
[submodule "src/libs/arduinoFFT"]
|
[submodule "src/libs/arduinoFFT"]
|
||||||
path = src/libs/arduinoFFT
|
path = src/libs/arduinoFFT
|
||||||
url = https://github.com/kosme/arduinoFFT.git
|
url = https://github.com/kosme/arduinoFFT.git
|
||||||
|
|
|
@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose Debug or Release")
|
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose Debug or Release")
|
||||||
|
|
||||||
project(pinetime VERSION 1.14.0 LANGUAGES C CXX ASM)
|
project(pinetime VERSION 1.15.3 LANGUAGES C CXX ASM)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 99)
|
set(CMAKE_C_STANDARD 99)
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
10
default.nix
Normal file
10
default.nix
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
(import
|
||||||
|
(
|
||||||
|
let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in
|
||||||
|
fetchTarball {
|
||||||
|
url = lock.nodes.flake-compat.locked.url or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
|
||||||
|
sha256 = lock.nodes.flake-compat.locked.narHash;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
{ src = ./.; }
|
||||||
|
).defaultNix
|
42
flake.lock
Normal file
42
flake.lock
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-compat": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1696426674,
|
||||||
|
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||||
|
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||||
|
"revCount": 57,
|
||||||
|
"type": "tarball",
|
||||||
|
"url": "https://api.flakehub.com/f/pinned/edolstra/flake-compat/1.0.1/018afb31-abd1-7bff-a5e4-cff7e18efb7a/source.tar.gz"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "tarball",
|
||||||
|
"url": "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1728492678,
|
||||||
|
"narHash": "sha256-9UTxR8eukdg+XZeHgxW5hQA9fIKHsKCdOIUycTryeVw=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "5633bcff0c6162b9e4b5f1264264611e950c8ec7",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-compat": "flake-compat",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
102
flake.nix
Normal file
102
flake.nix
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
{
|
||||||
|
description = "A very basic flake";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||||
|
flake-compat.url = "https://flakehub.com/f/edolstra/flake-compat/1.tar.gz";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, ... }@inputs:
|
||||||
|
let
|
||||||
|
forAllSystems = function:
|
||||||
|
inputs.nixpkgs.lib.genAttrs [
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
]
|
||||||
|
(system: function (import inputs.nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
config.allowUnfree = true;
|
||||||
|
}));
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages = forAllSystems (pkgs:
|
||||||
|
let
|
||||||
|
infinitime-nrf5-sdk = pkgs.nrf5-sdk.overrideAttrs (old: {
|
||||||
|
version = "15.3.0";
|
||||||
|
src = pkgs.fetchzip {
|
||||||
|
url = "https://nsscprodmedia.blob.core.windows.net/prod/software-and-other-downloads/sdks/nrf5/binaries/nrf5sdk153059ac345.zip";
|
||||||
|
sha256 = "sha256-pfmhbpgVv5x2ju489XcivguwpnofHbgVA7bFUJRTj08=";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
in
|
||||||
|
with pkgs; {
|
||||||
|
default = stdenv.mkDerivation rec {
|
||||||
|
name = "infinitime";
|
||||||
|
version = "1.14.1";
|
||||||
|
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "InfiniTimeOrg";
|
||||||
|
repo = "InfiniTime";
|
||||||
|
rev = "refs/tags/${version}";
|
||||||
|
hash = "sha256-IrsN+9LgEjgfoRR6H7FhsdLMK+GLxc41IBnSbdpwv/E=";
|
||||||
|
fetchSubmodules = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
nativeBuildInputs = [
|
||||||
|
adafruit-nrfutil
|
||||||
|
nodePackages.lv_font_conv
|
||||||
|
patch
|
||||||
|
python3
|
||||||
|
python3.pkgs.cbor
|
||||||
|
python3.pkgs.click
|
||||||
|
python3.pkgs.cryptography
|
||||||
|
python3.pkgs.intelhex
|
||||||
|
python3.pkgs.pillow
|
||||||
|
];
|
||||||
|
|
||||||
|
postPatch = ''
|
||||||
|
# /usr/bin/env is not available in the build sandbox
|
||||||
|
substituteInPlace src/displayapp/fonts/generate.py --replace "'/usr/bin/env', 'patch'" "'patch'"
|
||||||
|
substituteInPlace tools/mcuboot/imgtool.py --replace "/usr/bin/env python3" "${python3}/bin/python3"
|
||||||
|
'';
|
||||||
|
|
||||||
|
cmakeFlags = [
|
||||||
|
''-DARM_NONE_EABI_TOOLCHAIN_PATH=${gcc-arm-embedded-10}''
|
||||||
|
''-DNRF5_SDK_PATH=${infinitime-nrf5-sdk}/share/nRF5_SDK''
|
||||||
|
''-DBUILD_DFU=1''
|
||||||
|
''-DBUILD_RESOURCES=1''
|
||||||
|
''-DCMAKE_SOURCE_DIR=${src}''
|
||||||
|
];
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
SOURCES_DIR=${src} BUILD_DIR=. OUTPUT_DIR=$out ./post_build.sh
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
devShells = forAllSystems (pkgs:
|
||||||
|
let
|
||||||
|
infinitime-nrf5-sdk = pkgs.nrf5-sdk.overrideAttrs (old: {
|
||||||
|
version = "15.3.0";
|
||||||
|
src = pkgs.fetchzip {
|
||||||
|
url = "https://nsscprodmedia.blob.core.windows.net/prod/software-and-other-downloads/sdks/nrf5/binaries/nrf5sdk153059ac345.zip";
|
||||||
|
sha256 = "sha256-pfmhbpgVv5x2ju489XcivguwpnofHbgVA7bFUJRTj08=";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
in
|
||||||
|
with pkgs; {
|
||||||
|
default =
|
||||||
|
mkShell {
|
||||||
|
packages = [
|
||||||
|
(writeShellScriptBin "cmake_infinitime" ''
|
||||||
|
${cmake}/bin/cmake -DARM_NONE_EABI_TOOLCHAIN_PATH="${gcc-arm-embedded-10}" \
|
||||||
|
-DNRF5_SDK_PATH="${infinitime-nrf5-sdk}/share/nRF5_SDK" \
|
||||||
|
"$@"
|
||||||
|
'')
|
||||||
|
ninja
|
||||||
|
] ++ self.packages.${pkgs.system}.default.nativeBuildInputs;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
10
shell.nix
Normal file
10
shell.nix
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
(import
|
||||||
|
(
|
||||||
|
let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in
|
||||||
|
fetchTarball {
|
||||||
|
url = lock.nodes.flake-compat.locked.url or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz";
|
||||||
|
sha256 = lock.nodes.flake-compat.locked.narHash;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
{ src = ./.; }
|
||||||
|
).shellNix
|
|
@ -411,6 +411,7 @@ list(APPEND SOURCE_FILES
|
||||||
displayapp/screens/settings/SettingWeatherFormat.cpp
|
displayapp/screens/settings/SettingWeatherFormat.cpp
|
||||||
displayapp/screens/settings/SettingWakeUp.cpp
|
displayapp/screens/settings/SettingWakeUp.cpp
|
||||||
displayapp/screens/settings/SettingDisplay.cpp
|
displayapp/screens/settings/SettingDisplay.cpp
|
||||||
|
displayapp/screens/settings/SettingHeartRate.cpp
|
||||||
displayapp/screens/settings/SettingSteps.cpp
|
displayapp/screens/settings/SettingSteps.cpp
|
||||||
displayapp/screens/settings/SettingSetDateTime.cpp
|
displayapp/screens/settings/SettingSetDateTime.cpp
|
||||||
displayapp/screens/settings/SettingSetDate.cpp
|
displayapp/screens/settings/SettingSetDate.cpp
|
||||||
|
@ -478,6 +479,7 @@ list(APPEND SOURCE_FILES
|
||||||
|
|
||||||
systemtask/SystemTask.cpp
|
systemtask/SystemTask.cpp
|
||||||
systemtask/SystemMonitor.cpp
|
systemtask/SystemMonitor.cpp
|
||||||
|
systemtask/WakeLock.cpp
|
||||||
drivers/TwiMaster.cpp
|
drivers/TwiMaster.cpp
|
||||||
|
|
||||||
heartratetask/HeartRateTask.cpp
|
heartratetask/HeartRateTask.cpp
|
||||||
|
@ -542,6 +544,7 @@ list(APPEND RECOVERY_SOURCE_FILES
|
||||||
|
|
||||||
systemtask/SystemTask.cpp
|
systemtask/SystemTask.cpp
|
||||||
systemtask/SystemMonitor.cpp
|
systemtask/SystemMonitor.cpp
|
||||||
|
systemtask/WakeLock.cpp
|
||||||
drivers/TwiMaster.cpp
|
drivers/TwiMaster.cpp
|
||||||
components/rle/RleDecoder.cpp
|
components/rle/RleDecoder.cpp
|
||||||
components/heartrate/HeartRateController.cpp
|
components/heartrate/HeartRateController.cpp
|
||||||
|
@ -660,6 +663,7 @@ set(INCLUDE_FILES
|
||||||
displayapp/InfiniTimeTheme.h
|
displayapp/InfiniTimeTheme.h
|
||||||
systemtask/SystemTask.h
|
systemtask/SystemTask.h
|
||||||
systemtask/SystemMonitor.h
|
systemtask/SystemMonitor.h
|
||||||
|
systemtask/WakeLock.h
|
||||||
displayapp/screens/Symbols.h
|
displayapp/screens/Symbols.h
|
||||||
drivers/TwiMaster.h
|
drivers/TwiMaster.h
|
||||||
heartratetask/HeartRateTask.h
|
heartratetask/HeartRateTask.h
|
||||||
|
|
|
@ -19,11 +19,13 @@
|
||||||
#include "systemtask/SystemTask.h"
|
#include "systemtask/SystemTask.h"
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <libraries/log/nrf_log.h>
|
||||||
|
|
||||||
using namespace Pinetime::Controllers;
|
using namespace Pinetime::Controllers;
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
AlarmController::AlarmController(Controllers::DateTime& dateTimeController) : dateTimeController {dateTimeController} {
|
AlarmController::AlarmController(Controllers::DateTime& dateTimeController, Controllers::FS& fs)
|
||||||
|
: dateTimeController {dateTimeController}, fs {fs} {
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -36,11 +38,28 @@ namespace {
|
||||||
void AlarmController::Init(System::SystemTask* systemTask) {
|
void AlarmController::Init(System::SystemTask* systemTask) {
|
||||||
this->systemTask = systemTask;
|
this->systemTask = systemTask;
|
||||||
alarmTimer = xTimerCreate("Alarm", 1, pdFALSE, this, SetOffAlarm);
|
alarmTimer = xTimerCreate("Alarm", 1, pdFALSE, this, SetOffAlarm);
|
||||||
|
LoadSettingsFromFile();
|
||||||
|
if (alarm.isEnabled) {
|
||||||
|
NRF_LOG_INFO("[AlarmController] Loaded alarm was enabled, scheduling");
|
||||||
|
ScheduleAlarm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmController::SaveAlarm() {
|
||||||
|
// verify if it is necessary to save
|
||||||
|
if (alarmChanged) {
|
||||||
|
SaveSettingsToFile();
|
||||||
|
}
|
||||||
|
alarmChanged = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlarmController::SetAlarmTime(uint8_t alarmHr, uint8_t alarmMin) {
|
void AlarmController::SetAlarmTime(uint8_t alarmHr, uint8_t alarmMin) {
|
||||||
hours = alarmHr;
|
if (alarm.hours == alarmHr && alarm.minutes == alarmMin) {
|
||||||
minutes = alarmMin;
|
return;
|
||||||
|
}
|
||||||
|
alarm.hours = alarmHr;
|
||||||
|
alarm.minutes = alarmMin;
|
||||||
|
alarmChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlarmController::ScheduleAlarm() {
|
void AlarmController::ScheduleAlarm() {
|
||||||
|
@ -53,18 +72,19 @@ void AlarmController::ScheduleAlarm() {
|
||||||
tm* tmAlarmTime = std::localtime(&ttAlarmTime);
|
tm* tmAlarmTime = std::localtime(&ttAlarmTime);
|
||||||
|
|
||||||
// If the time being set has already passed today,the alarm should be set for tomorrow
|
// If the time being set has already passed today,the alarm should be set for tomorrow
|
||||||
if (hours < dateTimeController.Hours() || (hours == dateTimeController.Hours() && minutes <= dateTimeController.Minutes())) {
|
if (alarm.hours < dateTimeController.Hours() ||
|
||||||
|
(alarm.hours == dateTimeController.Hours() && alarm.minutes <= dateTimeController.Minutes())) {
|
||||||
tmAlarmTime->tm_mday += 1;
|
tmAlarmTime->tm_mday += 1;
|
||||||
// tm_wday doesn't update automatically
|
// tm_wday doesn't update automatically
|
||||||
tmAlarmTime->tm_wday = (tmAlarmTime->tm_wday + 1) % 7;
|
tmAlarmTime->tm_wday = (tmAlarmTime->tm_wday + 1) % 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmAlarmTime->tm_hour = hours;
|
tmAlarmTime->tm_hour = alarm.hours;
|
||||||
tmAlarmTime->tm_min = minutes;
|
tmAlarmTime->tm_min = alarm.minutes;
|
||||||
tmAlarmTime->tm_sec = 0;
|
tmAlarmTime->tm_sec = 0;
|
||||||
|
|
||||||
// if alarm is in weekday-only mode, make sure it shifts to the next weekday
|
// if alarm is in weekday-only mode, make sure it shifts to the next weekday
|
||||||
if (recurrence == RecurType::Weekdays) {
|
if (alarm.recurrence == RecurType::Weekdays) {
|
||||||
if (tmAlarmTime->tm_wday == 0) { // Sunday, shift 1 day
|
if (tmAlarmTime->tm_wday == 0) { // Sunday, shift 1 day
|
||||||
tmAlarmTime->tm_mday += 1;
|
tmAlarmTime->tm_mday += 1;
|
||||||
} else if (tmAlarmTime->tm_wday == 6) { // Saturday, shift 2 days
|
} else if (tmAlarmTime->tm_wday == 6) { // Saturday, shift 2 days
|
||||||
|
@ -79,7 +99,10 @@ void AlarmController::ScheduleAlarm() {
|
||||||
xTimerChangePeriod(alarmTimer, secondsToAlarm * configTICK_RATE_HZ, 0);
|
xTimerChangePeriod(alarmTimer, secondsToAlarm * configTICK_RATE_HZ, 0);
|
||||||
xTimerStart(alarmTimer, 0);
|
xTimerStart(alarmTimer, 0);
|
||||||
|
|
||||||
state = AlarmState::Set;
|
if (!alarm.isEnabled) {
|
||||||
|
alarm.isEnabled = true;
|
||||||
|
alarmChanged = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t AlarmController::SecondsToAlarm() const {
|
uint32_t AlarmController::SecondsToAlarm() const {
|
||||||
|
@ -88,20 +111,72 @@ uint32_t AlarmController::SecondsToAlarm() const {
|
||||||
|
|
||||||
void AlarmController::DisableAlarm() {
|
void AlarmController::DisableAlarm() {
|
||||||
xTimerStop(alarmTimer, 0);
|
xTimerStop(alarmTimer, 0);
|
||||||
state = AlarmState::Not_Set;
|
isAlerting = false;
|
||||||
|
if (alarm.isEnabled) {
|
||||||
|
alarm.isEnabled = false;
|
||||||
|
alarmChanged = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlarmController::SetOffAlarmNow() {
|
void AlarmController::SetOffAlarmNow() {
|
||||||
state = AlarmState::Alerting;
|
isAlerting = true;
|
||||||
systemTask->PushMessage(System::Messages::SetOffAlarm);
|
systemTask->PushMessage(System::Messages::SetOffAlarm);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlarmController::StopAlerting() {
|
void AlarmController::StopAlerting() {
|
||||||
// Alarm state is off unless this is a recurring alarm
|
isAlerting = false;
|
||||||
if (recurrence == RecurType::None) {
|
// Disable alarm unless it is recurring
|
||||||
state = AlarmState::Not_Set;
|
if (alarm.recurrence == RecurType::None) {
|
||||||
|
alarm.isEnabled = false;
|
||||||
|
alarmChanged = true;
|
||||||
} else {
|
} else {
|
||||||
// set next instance
|
// set next instance
|
||||||
ScheduleAlarm();
|
ScheduleAlarm();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AlarmController::SetRecurrence(RecurType recurrence) {
|
||||||
|
if (alarm.recurrence != recurrence) {
|
||||||
|
alarm.recurrence = recurrence;
|
||||||
|
alarmChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmController::LoadSettingsFromFile() {
|
||||||
|
lfs_file_t alarmFile;
|
||||||
|
AlarmSettings alarmBuffer;
|
||||||
|
|
||||||
|
if (fs.FileOpen(&alarmFile, "/.system/alarm.dat", LFS_O_RDONLY) != LFS_ERR_OK) {
|
||||||
|
NRF_LOG_WARNING("[AlarmController] Failed to open alarm data file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.FileRead(&alarmFile, reinterpret_cast<uint8_t*>(&alarmBuffer), sizeof(alarmBuffer));
|
||||||
|
fs.FileClose(&alarmFile);
|
||||||
|
if (alarmBuffer.version != alarmFormatVersion) {
|
||||||
|
NRF_LOG_WARNING("[AlarmController] Loaded alarm settings has version %u instead of %u, discarding",
|
||||||
|
alarmBuffer.version,
|
||||||
|
alarmFormatVersion);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
alarm = alarmBuffer;
|
||||||
|
NRF_LOG_INFO("[AlarmController] Loaded alarm settings from file");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlarmController::SaveSettingsToFile() const {
|
||||||
|
lfs_dir systemDir;
|
||||||
|
if (fs.DirOpen("/.system", &systemDir) != LFS_ERR_OK) {
|
||||||
|
fs.DirCreate("/.system");
|
||||||
|
}
|
||||||
|
fs.DirClose(&systemDir);
|
||||||
|
lfs_file_t alarmFile;
|
||||||
|
if (fs.FileOpen(&alarmFile, "/.system/alarm.dat", LFS_O_WRONLY | LFS_O_CREAT) != LFS_ERR_OK) {
|
||||||
|
NRF_LOG_WARNING("[AlarmController] Failed to open alarm data file for saving");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.FileWrite(&alarmFile, reinterpret_cast<const uint8_t*>(&alarm), sizeof(alarm));
|
||||||
|
fs.FileClose(&alarmFile);
|
||||||
|
NRF_LOG_INFO("[AlarmController] Saved alarm settings with format version %u to file", alarm.version);
|
||||||
|
}
|
||||||
|
|
|
@ -30,47 +30,65 @@ namespace Pinetime {
|
||||||
namespace Controllers {
|
namespace Controllers {
|
||||||
class AlarmController {
|
class AlarmController {
|
||||||
public:
|
public:
|
||||||
AlarmController(Controllers::DateTime& dateTimeController);
|
AlarmController(Controllers::DateTime& dateTimeController, Controllers::FS& fs);
|
||||||
|
|
||||||
void Init(System::SystemTask* systemTask);
|
void Init(System::SystemTask* systemTask);
|
||||||
|
void SaveAlarm();
|
||||||
void SetAlarmTime(uint8_t alarmHr, uint8_t alarmMin);
|
void SetAlarmTime(uint8_t alarmHr, uint8_t alarmMin);
|
||||||
void ScheduleAlarm();
|
void ScheduleAlarm();
|
||||||
void DisableAlarm();
|
void DisableAlarm();
|
||||||
void SetOffAlarmNow();
|
void SetOffAlarmNow();
|
||||||
uint32_t SecondsToAlarm() const;
|
uint32_t SecondsToAlarm() const;
|
||||||
void StopAlerting();
|
void StopAlerting();
|
||||||
enum class AlarmState { Not_Set, Set, Alerting };
|
|
||||||
enum class RecurType { None, Daily, Weekdays };
|
enum class RecurType { None, Daily, Weekdays };
|
||||||
|
|
||||||
uint8_t Hours() const {
|
uint8_t Hours() const {
|
||||||
return hours;
|
return alarm.hours;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t Minutes() const {
|
uint8_t Minutes() const {
|
||||||
return minutes;
|
return alarm.minutes;
|
||||||
}
|
}
|
||||||
|
|
||||||
AlarmState State() const {
|
bool IsAlerting() const {
|
||||||
return state;
|
return isAlerting;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsEnabled() const {
|
||||||
|
return alarm.isEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
RecurType Recurrence() const {
|
RecurType Recurrence() const {
|
||||||
return recurrence;
|
return alarm.recurrence;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetRecurrence(RecurType recurType) {
|
void SetRecurrence(RecurType recurrence);
|
||||||
recurrence = recurType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Controllers::DateTime& dateTimeController;
|
// Versions 255 is reserved for now, so the version field can be made
|
||||||
System::SystemTask* systemTask = nullptr;
|
// bigger, should it ever be needed.
|
||||||
TimerHandle_t alarmTimer;
|
static constexpr uint8_t alarmFormatVersion = 1;
|
||||||
|
|
||||||
|
struct AlarmSettings {
|
||||||
|
uint8_t version = alarmFormatVersion;
|
||||||
uint8_t hours = 7;
|
uint8_t hours = 7;
|
||||||
uint8_t minutes = 0;
|
uint8_t minutes = 0;
|
||||||
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> alarmTime;
|
|
||||||
AlarmState state = AlarmState::Not_Set;
|
|
||||||
RecurType recurrence = RecurType::None;
|
RecurType recurrence = RecurType::None;
|
||||||
|
bool isEnabled = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool isAlerting = false;
|
||||||
|
bool alarmChanged = false;
|
||||||
|
|
||||||
|
Controllers::DateTime& dateTimeController;
|
||||||
|
Controllers::FS& fs;
|
||||||
|
System::SystemTask* systemTask = nullptr;
|
||||||
|
TimerHandle_t alarmTimer;
|
||||||
|
AlarmSettings alarm;
|
||||||
|
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> alarmTime;
|
||||||
|
|
||||||
|
void LoadSettingsFromFile();
|
||||||
|
void SaveSettingsToFile() const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,9 +124,11 @@ int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf* om) {
|
||||||
bootloaderSize,
|
bootloaderSize,
|
||||||
applicationSize);
|
applicationSize);
|
||||||
|
|
||||||
// wait until SystemTask has finished waking up all devices
|
// Wait until SystemTask has disabled sleeping
|
||||||
while (systemTask.IsSleeping()) {
|
// This isn't quite correct, as we don't actually know
|
||||||
vTaskDelay(50); // 50ms
|
// if BleFirmwareUpdateStarted has been received yet
|
||||||
|
while (!systemTask.IsSleepDisabled()) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
dfuImage.Erase();
|
dfuImage.Erase();
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
#include "components/ble/MusicService.h"
|
#include "components/ble/MusicService.h"
|
||||||
#include "components/ble/NimbleController.h"
|
#include "components/ble/NimbleController.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <FreeRTOS.h>
|
||||||
|
#include <task.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// 0000yyxx-78fc-48fe-8e23-433b3a1942d0
|
// 0000yyxx-78fc-48fe-8e23-433b3a1942d0
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include <host/ble_uuid.h>
|
#include <host/ble_uuid.h>
|
||||||
#undef max
|
#undef max
|
||||||
#undef min
|
#undef min
|
||||||
|
#include <FreeRTOS.h>
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Controllers {
|
namespace Controllers {
|
||||||
|
|
|
@ -454,9 +454,15 @@ void NimbleController::PersistBond(struct ble_gap_conn_desc& desc) {
|
||||||
/* Wakeup Spi and SpiNorFlash before accessing the file system
|
/* Wakeup Spi and SpiNorFlash before accessing the file system
|
||||||
* This should be fixed in the FS driver
|
* This should be fixed in the FS driver
|
||||||
*/
|
*/
|
||||||
systemTask.PushMessage(Pinetime::System::Messages::GoToRunning);
|
|
||||||
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
|
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
|
||||||
vTaskDelay(10);
|
|
||||||
|
// This isn't quite correct
|
||||||
|
// SystemTask could receive EnableSleeping right after passing this check
|
||||||
|
// We need some guarantee that the SystemTask has processed the above message
|
||||||
|
// before we can continue
|
||||||
|
while (!systemTask.IsSleepDisabled()) {
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(5));
|
||||||
|
}
|
||||||
|
|
||||||
lfs_file_t file_p;
|
lfs_file_t file_p;
|
||||||
|
|
||||||
|
|
|
@ -142,7 +142,7 @@ Ppg::Ppg() {
|
||||||
spectrum.fill(0.0f);
|
spectrum.fill(0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
int8_t Ppg::Preprocess(uint32_t hrs, uint32_t als) {
|
int8_t Ppg::Preprocess(uint16_t hrs, uint16_t als) {
|
||||||
if (dataIndex < dataLength) {
|
if (dataIndex < dataLength) {
|
||||||
dataHRS[dataIndex++] = hrs;
|
dataHRS[dataIndex++] = hrs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace Pinetime {
|
||||||
class Ppg {
|
class Ppg {
|
||||||
public:
|
public:
|
||||||
Ppg();
|
Ppg();
|
||||||
int8_t Preprocess(uint32_t hrs, uint32_t als);
|
int8_t Preprocess(uint16_t hrs, uint16_t als);
|
||||||
int HeartRate();
|
int HeartRate();
|
||||||
void Reset(bool resetDaqBuffer);
|
void Reset(bool resetDaqBuffer);
|
||||||
static constexpr int deltaTms = 100;
|
static constexpr int deltaTms = 100;
|
||||||
|
|
|
@ -8,13 +8,11 @@ Settings::Settings(Pinetime::Controllers::FS& fs) : fs {fs} {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::Init() {
|
void Settings::Init() {
|
||||||
|
|
||||||
// Load default settings from Flash
|
// Load default settings from Flash
|
||||||
LoadSettingsFromFile();
|
LoadSettingsFromFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Settings::SaveSettings() {
|
void Settings::SaveSettings() {
|
||||||
|
|
||||||
// verify if is necessary to save
|
// verify if is necessary to save
|
||||||
if (settingsChanged) {
|
if (settingsChanged) {
|
||||||
SaveSettingsToFile();
|
SaveSettingsToFile();
|
||||||
|
|
|
@ -50,6 +50,17 @@ namespace Pinetime {
|
||||||
int colorIndex = 0;
|
int colorIndex = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class HeartRateBackgroundMeasurementInterval : uint8_t {
|
||||||
|
Off,
|
||||||
|
Continuous,
|
||||||
|
FifteenSeconds,
|
||||||
|
ThirtySeconds,
|
||||||
|
OneMinute,
|
||||||
|
FiveMinutes,
|
||||||
|
TenMinutes,
|
||||||
|
ThirtyMinutes,
|
||||||
|
};
|
||||||
|
|
||||||
Settings(Pinetime::Controllers::FS& fs);
|
Settings(Pinetime::Controllers::FS& fs);
|
||||||
|
|
||||||
Settings(const Settings&) = delete;
|
Settings(const Settings&) = delete;
|
||||||
|
@ -298,10 +309,21 @@ namespace Pinetime {
|
||||||
return bleRadioEnabled;
|
return bleRadioEnabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
HeartRateBackgroundMeasurementInterval GetHeartRateBackgroundMeasurementInterval() const {
|
||||||
|
return settings.heartRateBackgroundMeasurementInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetHeartRateBackgroundMeasurementInterval(HeartRateBackgroundMeasurementInterval newHeartRateBackgroundMeasurementInterval) {
|
||||||
|
if (newHeartRateBackgroundMeasurementInterval != settings.heartRateBackgroundMeasurementInterval) {
|
||||||
|
settingsChanged = true;
|
||||||
|
}
|
||||||
|
settings.heartRateBackgroundMeasurementInterval = newHeartRateBackgroundMeasurementInterval;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Pinetime::Controllers::FS& fs;
|
Pinetime::Controllers::FS& fs;
|
||||||
|
|
||||||
static constexpr uint32_t settingsVersion = 0x0008;
|
static constexpr uint32_t settingsVersion = 0x0009;
|
||||||
|
|
||||||
struct SettingsData {
|
struct SettingsData {
|
||||||
uint32_t version = settingsVersion;
|
uint32_t version = settingsVersion;
|
||||||
|
@ -325,6 +347,8 @@ namespace Pinetime {
|
||||||
uint16_t shakeWakeThreshold = 150;
|
uint16_t shakeWakeThreshold = 150;
|
||||||
|
|
||||||
Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium;
|
Controllers::BrightnessController::Levels brightLevel = Controllers::BrightnessController::Levels::Medium;
|
||||||
|
|
||||||
|
HeartRateBackgroundMeasurementInterval heartRateBackgroundMeasurementInterval = HeartRateBackgroundMeasurementInterval::Off;
|
||||||
};
|
};
|
||||||
|
|
||||||
SettingsData settings;
|
SettingsData settings;
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
#include "displayapp/screens/settings/SettingSteps.h"
|
#include "displayapp/screens/settings/SettingSteps.h"
|
||||||
#include "displayapp/screens/settings/SettingSetDateTime.h"
|
#include "displayapp/screens/settings/SettingSetDateTime.h"
|
||||||
#include "displayapp/screens/settings/SettingChimes.h"
|
#include "displayapp/screens/settings/SettingChimes.h"
|
||||||
|
#include "displayapp/screens/settings/SettingHeartRate.h"
|
||||||
#include "displayapp/screens/settings/SettingShakeThreshold.h"
|
#include "displayapp/screens/settings/SettingShakeThreshold.h"
|
||||||
#include "displayapp/screens/settings/SettingBluetooth.h"
|
#include "displayapp/screens/settings/SettingBluetooth.h"
|
||||||
|
|
||||||
|
@ -255,9 +256,20 @@ void DisplayApp::Refresh() {
|
||||||
isDimmed = true;
|
isDimmed = true;
|
||||||
brightnessController.Set(Controllers::BrightnessController::Levels::Low);
|
brightnessController.Set(Controllers::BrightnessController::Levels::Low);
|
||||||
}
|
}
|
||||||
if (IsPastSleepTime()) {
|
if (IsPastSleepTime() && uxQueueMessagesWaiting(msgQueue) == 0) {
|
||||||
systemTask->PushMessage(System::Messages::GoToSleep);
|
PushMessageToSystemTask(System::Messages::GoToSleep);
|
||||||
state = States::Idle;
|
// Can't set state to Idle here, something may send
|
||||||
|
// DisableSleeping before this GoToSleep arrives
|
||||||
|
// Instead we check we have no messages queued before sending GoToSleep
|
||||||
|
// This works as the SystemTask is higher priority than DisplayApp
|
||||||
|
// As soon as we send GoToSleep, SystemTask pre-empts DisplayApp
|
||||||
|
// Whenever DisplayApp is running again, it is guaranteed that
|
||||||
|
// SystemTask has handled the message
|
||||||
|
// If it responded, we will have a GoToSleep waiting in the queue
|
||||||
|
// By checking that there are no messages in the queue, we avoid
|
||||||
|
// resending GoToSleep when we already have a response
|
||||||
|
// SystemTask is resilient to duplicate messages, this is an
|
||||||
|
// optimisation to reduce pressure on the message queues
|
||||||
}
|
}
|
||||||
} else if (isDimmed) {
|
} else if (isDimmed) {
|
||||||
isDimmed = false;
|
isDimmed = false;
|
||||||
|
@ -273,6 +285,9 @@ void DisplayApp::Refresh() {
|
||||||
if (xQueueReceive(msgQueue, &msg, queueTimeout) == pdTRUE) {
|
if (xQueueReceive(msgQueue, &msg, queueTimeout) == pdTRUE) {
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case Messages::GoToSleep:
|
case Messages::GoToSleep:
|
||||||
|
if (state != States::Running) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
while (brightnessController.Level() != Controllers::BrightnessController::Levels::Low) {
|
while (brightnessController.Level() != Controllers::BrightnessController::Levels::Low) {
|
||||||
brightnessController.Lower();
|
brightnessController.Lower();
|
||||||
vTaskDelay(100);
|
vTaskDelay(100);
|
||||||
|
@ -307,6 +322,9 @@ void DisplayApp::Refresh() {
|
||||||
lv_disp_trig_activity(nullptr);
|
lv_disp_trig_activity(nullptr);
|
||||||
break;
|
break;
|
||||||
case Messages::GoToRunning:
|
case Messages::GoToRunning:
|
||||||
|
if (state == States::Running) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (settingsController.GetAlwaysOnDisplay()) {
|
if (settingsController.GetAlwaysOnDisplay()) {
|
||||||
lcd.LowPowerOff();
|
lcd.LowPowerOff();
|
||||||
} else {
|
} else {
|
||||||
|
@ -333,7 +351,7 @@ void DisplayApp::Refresh() {
|
||||||
} else {
|
} else {
|
||||||
LoadNewScreen(Apps::Timer, DisplayApp::FullRefreshDirections::Up);
|
LoadNewScreen(Apps::Timer, DisplayApp::FullRefreshDirections::Up);
|
||||||
}
|
}
|
||||||
motorController.RunForDuration(35);
|
motorController.StartRinging();
|
||||||
break;
|
break;
|
||||||
case Messages::AlarmTriggered:
|
case Messages::AlarmTriggered:
|
||||||
if (currentApp == Apps::Alarm) {
|
if (currentApp == Apps::Alarm) {
|
||||||
|
@ -564,8 +582,11 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
|
||||||
case Apps::SettingWakeUp:
|
case Apps::SettingWakeUp:
|
||||||
currentScreen = std::make_unique<Screens::SettingWakeUp>(settingsController);
|
currentScreen = std::make_unique<Screens::SettingWakeUp>(settingsController);
|
||||||
break;
|
break;
|
||||||
|
case Apps::SettingHeartRate:
|
||||||
|
currentScreen = std::make_unique<Screens::SettingHeartRate>(settingsController);
|
||||||
|
break;
|
||||||
case Apps::SettingDisplay:
|
case Apps::SettingDisplay:
|
||||||
currentScreen = std::make_unique<Screens::SettingDisplay>(this, settingsController);
|
currentScreen = std::make_unique<Screens::SettingDisplay>(settingsController);
|
||||||
break;
|
break;
|
||||||
case Apps::SettingSteps:
|
case Apps::SettingSteps:
|
||||||
currentScreen = std::make_unique<Screens::SettingSteps>(settingsController);
|
currentScreen = std::make_unique<Screens::SettingSteps>(settingsController);
|
||||||
|
|
|
@ -35,6 +35,7 @@ namespace Pinetime {
|
||||||
SettingWatchFace,
|
SettingWatchFace,
|
||||||
SettingTimeFormat,
|
SettingTimeFormat,
|
||||||
SettingWeatherFormat,
|
SettingWeatherFormat,
|
||||||
|
SettingHeartRate,
|
||||||
SettingDisplay,
|
SettingDisplay,
|
||||||
SettingWakeUp,
|
SettingWakeUp,
|
||||||
SettingSteps,
|
SettingSteps,
|
||||||
|
|
|
@ -48,7 +48,7 @@ Alarm::Alarm(Controllers::AlarmController& alarmController,
|
||||||
Controllers::Settings::ClockType clockType,
|
Controllers::Settings::ClockType clockType,
|
||||||
System::SystemTask& systemTask,
|
System::SystemTask& systemTask,
|
||||||
Controllers::MotorController& motorController)
|
Controllers::MotorController& motorController)
|
||||||
: alarmController {alarmController}, systemTask {systemTask}, motorController {motorController} {
|
: alarmController {alarmController}, wakeLock(systemTask), motorController {motorController} {
|
||||||
|
|
||||||
hourCounter.Create();
|
hourCounter.Create();
|
||||||
lv_obj_align(hourCounter.GetObject(), nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
|
lv_obj_align(hourCounter.GetObject(), nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
|
||||||
|
@ -117,7 +117,7 @@ Alarm::Alarm(Controllers::AlarmController& alarmController,
|
||||||
|
|
||||||
UpdateAlarmTime();
|
UpdateAlarmTime();
|
||||||
|
|
||||||
if (alarmController.State() == Controllers::AlarmController::AlarmState::Alerting) {
|
if (alarmController.IsAlerting()) {
|
||||||
SetAlerting();
|
SetAlerting();
|
||||||
} else {
|
} else {
|
||||||
SetSwitchState(LV_ANIM_OFF);
|
SetSwitchState(LV_ANIM_OFF);
|
||||||
|
@ -125,14 +125,15 @@ Alarm::Alarm(Controllers::AlarmController& alarmController,
|
||||||
}
|
}
|
||||||
|
|
||||||
Alarm::~Alarm() {
|
Alarm::~Alarm() {
|
||||||
if (alarmController.State() == AlarmController::AlarmState::Alerting) {
|
if (alarmController.IsAlerting()) {
|
||||||
StopAlerting();
|
StopAlerting();
|
||||||
}
|
}
|
||||||
lv_obj_clean(lv_scr_act());
|
lv_obj_clean(lv_scr_act());
|
||||||
|
alarmController.SaveAlarm();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Alarm::DisableAlarm() {
|
void Alarm::DisableAlarm() {
|
||||||
if (alarmController.State() == AlarmController::AlarmState::Set) {
|
if (alarmController.IsEnabled()) {
|
||||||
alarmController.DisableAlarm();
|
alarmController.DisableAlarm();
|
||||||
lv_switch_off(enableSwitch, LV_ANIM_ON);
|
lv_switch_off(enableSwitch, LV_ANIM_ON);
|
||||||
}
|
}
|
||||||
|
@ -172,7 +173,7 @@ bool Alarm::OnButtonPushed() {
|
||||||
HideInfo();
|
HideInfo();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (alarmController.State() == AlarmController::AlarmState::Alerting) {
|
if (alarmController.IsAlerting()) {
|
||||||
StopAlerting();
|
StopAlerting();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -181,7 +182,7 @@ bool Alarm::OnButtonPushed() {
|
||||||
|
|
||||||
bool Alarm::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
|
bool Alarm::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
|
||||||
// Don't allow closing the screen by swiping while the alarm is alerting
|
// Don't allow closing the screen by swiping while the alarm is alerting
|
||||||
return alarmController.State() == AlarmController::AlarmState::Alerting && event == TouchEvents::SwipeDown;
|
return alarmController.IsAlerting() && event == TouchEvents::SwipeDown;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Alarm::OnValueChanged() {
|
void Alarm::OnValueChanged() {
|
||||||
|
@ -205,7 +206,7 @@ void Alarm::SetAlerting() {
|
||||||
lv_obj_set_hidden(btnStop, false);
|
lv_obj_set_hidden(btnStop, false);
|
||||||
taskStopAlarm = lv_task_create(StopAlarmTaskCallback, pdMS_TO_TICKS(60 * 1000), LV_TASK_PRIO_MID, this);
|
taskStopAlarm = lv_task_create(StopAlarmTaskCallback, pdMS_TO_TICKS(60 * 1000), LV_TASK_PRIO_MID, this);
|
||||||
motorController.StartRinging();
|
motorController.StartRinging();
|
||||||
systemTask.PushMessage(System::Messages::DisableSleeping);
|
wakeLock.Lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Alarm::StopAlerting() {
|
void Alarm::StopAlerting() {
|
||||||
|
@ -216,21 +217,16 @@ void Alarm::StopAlerting() {
|
||||||
lv_task_del(taskStopAlarm);
|
lv_task_del(taskStopAlarm);
|
||||||
taskStopAlarm = nullptr;
|
taskStopAlarm = nullptr;
|
||||||
}
|
}
|
||||||
systemTask.PushMessage(System::Messages::EnableSleeping);
|
wakeLock.Release();
|
||||||
lv_obj_set_hidden(enableSwitch, false);
|
lv_obj_set_hidden(enableSwitch, false);
|
||||||
lv_obj_set_hidden(btnStop, true);
|
lv_obj_set_hidden(btnStop, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Alarm::SetSwitchState(lv_anim_enable_t anim) {
|
void Alarm::SetSwitchState(lv_anim_enable_t anim) {
|
||||||
switch (alarmController.State()) {
|
if (alarmController.IsEnabled()) {
|
||||||
case AlarmController::AlarmState::Set:
|
|
||||||
lv_switch_on(enableSwitch, anim);
|
lv_switch_on(enableSwitch, anim);
|
||||||
break;
|
} else {
|
||||||
case AlarmController::AlarmState::Not_Set:
|
|
||||||
lv_switch_off(enableSwitch, anim);
|
lv_switch_off(enableSwitch, anim);
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +243,7 @@ void Alarm::ShowInfo() {
|
||||||
txtMessage = lv_label_create(btnMessage, nullptr);
|
txtMessage = lv_label_create(btnMessage, nullptr);
|
||||||
lv_obj_set_style_local_bg_color(btnMessage, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_NAVY);
|
lv_obj_set_style_local_bg_color(btnMessage, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_NAVY);
|
||||||
|
|
||||||
if (alarmController.State() == AlarmController::AlarmState::Set) {
|
if (alarmController.IsEnabled()) {
|
||||||
auto timeToAlarm = alarmController.SecondsToAlarm();
|
auto timeToAlarm = alarmController.SecondsToAlarm();
|
||||||
|
|
||||||
auto daysToAlarm = timeToAlarm / 86400;
|
auto daysToAlarm = timeToAlarm / 86400;
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
#include "displayapp/widgets/Counter.h"
|
#include "displayapp/widgets/Counter.h"
|
||||||
#include "displayapp/Controllers.h"
|
#include "displayapp/Controllers.h"
|
||||||
|
#include "systemtask/WakeLock.h"
|
||||||
#include "Symbols.h"
|
#include "Symbols.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
|
@ -43,7 +44,7 @@ namespace Pinetime {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Controllers::AlarmController& alarmController;
|
Controllers::AlarmController& alarmController;
|
||||||
System::SystemTask& systemTask;
|
System::WakeLock wakeLock;
|
||||||
Controllers::MotorController& motorController;
|
Controllers::MotorController& motorController;
|
||||||
|
|
||||||
lv_obj_t *btnStop, *txtStop, *btnRecur, *txtRecur, *btnInfo, *enableSwitch;
|
lv_obj_t *btnStop, *txtStop, *btnRecur, *txtRecur, *btnInfo, *enableSwitch;
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
FlashLight::FlashLight(System::SystemTask& systemTask, Controllers::BrightnessController& brightnessController)
|
FlashLight::FlashLight(System::SystemTask& systemTask, Controllers::BrightnessController& brightnessController)
|
||||||
: systemTask {systemTask}, brightnessController {brightnessController} {
|
: wakeLock(systemTask), brightnessController {brightnessController} {
|
||||||
|
|
||||||
previousBrightnessLevel = brightnessController.Level();
|
previousBrightnessLevel = brightnessController.Level();
|
||||||
brightnessController.Set(Controllers::BrightnessController::Levels::Low);
|
brightnessController.Set(Controllers::BrightnessController::Levels::Low);
|
||||||
|
@ -47,14 +47,13 @@ FlashLight::FlashLight(System::SystemTask& systemTask, Controllers::BrightnessCo
|
||||||
backgroundAction->user_data = this;
|
backgroundAction->user_data = this;
|
||||||
lv_obj_set_event_cb(backgroundAction, EventHandler);
|
lv_obj_set_event_cb(backgroundAction, EventHandler);
|
||||||
|
|
||||||
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
|
wakeLock.Lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
FlashLight::~FlashLight() {
|
FlashLight::~FlashLight() {
|
||||||
lv_obj_clean(lv_scr_act());
|
lv_obj_clean(lv_scr_act());
|
||||||
lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
|
lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
|
||||||
brightnessController.Set(previousBrightnessLevel);
|
brightnessController.Set(previousBrightnessLevel);
|
||||||
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlashLight::SetColors() {
|
void FlashLight::SetColors() {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
#include "components/brightness/BrightnessController.h"
|
#include "components/brightness/BrightnessController.h"
|
||||||
#include "systemtask/SystemTask.h"
|
#include "systemtask/SystemTask.h"
|
||||||
|
#include "systemtask/WakeLock.h"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
|
|
||||||
|
@ -23,7 +24,7 @@ namespace Pinetime {
|
||||||
void SetIndicators();
|
void SetIndicators();
|
||||||
void SetColors();
|
void SetColors();
|
||||||
|
|
||||||
Pinetime::System::SystemTask& systemTask;
|
Pinetime::System::WakeLock wakeLock;
|
||||||
Controllers::BrightnessController& brightnessController;
|
Controllers::BrightnessController& brightnessController;
|
||||||
|
|
||||||
Controllers::BrightnessController::Levels brightnessLevel = Controllers::BrightnessController::Levels::High;
|
Controllers::BrightnessController::Levels brightnessLevel = Controllers::BrightnessController::Levels::High;
|
||||||
|
|
|
@ -29,7 +29,7 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
HeartRate::HeartRate(Controllers::HeartRateController& heartRateController, System::SystemTask& systemTask)
|
HeartRate::HeartRate(Controllers::HeartRateController& heartRateController, System::SystemTask& systemTask)
|
||||||
: heartRateController {heartRateController}, systemTask {systemTask} {
|
: heartRateController {heartRateController}, wakeLock(systemTask) {
|
||||||
bool isHrRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped;
|
bool isHrRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped;
|
||||||
label_hr = lv_label_create(lv_scr_act(), nullptr);
|
label_hr = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ HeartRate::HeartRate(Controllers::HeartRateController& heartRateController, Syst
|
||||||
label_startStop = lv_label_create(btn_startStop, nullptr);
|
label_startStop = lv_label_create(btn_startStop, nullptr);
|
||||||
UpdateStartStopButton(isHrRunning);
|
UpdateStartStopButton(isHrRunning);
|
||||||
if (isHrRunning) {
|
if (isHrRunning) {
|
||||||
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
|
wakeLock.Lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
taskRefresh = lv_task_create(RefreshTaskCallback, 100, LV_TASK_PRIO_MID, this);
|
taskRefresh = lv_task_create(RefreshTaskCallback, 100, LV_TASK_PRIO_MID, this);
|
||||||
|
@ -72,7 +72,6 @@ HeartRate::HeartRate(Controllers::HeartRateController& heartRateController, Syst
|
||||||
HeartRate::~HeartRate() {
|
HeartRate::~HeartRate() {
|
||||||
lv_task_del(taskRefresh);
|
lv_task_del(taskRefresh);
|
||||||
lv_obj_clean(lv_scr_act());
|
lv_obj_clean(lv_scr_act());
|
||||||
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeartRate::Refresh() {
|
void HeartRate::Refresh() {
|
||||||
|
@ -101,12 +100,12 @@ void HeartRate::OnStartStopEvent(lv_event_t event) {
|
||||||
if (heartRateController.State() == Controllers::HeartRateController::States::Stopped) {
|
if (heartRateController.State() == Controllers::HeartRateController::States::Stopped) {
|
||||||
heartRateController.Start();
|
heartRateController.Start();
|
||||||
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
|
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
|
||||||
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
|
wakeLock.Lock();
|
||||||
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight);
|
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight);
|
||||||
} else {
|
} else {
|
||||||
heartRateController.Stop();
|
heartRateController.Stop();
|
||||||
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
|
UpdateStartStopButton(heartRateController.State() != Controllers::HeartRateController::States::Stopped);
|
||||||
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
|
wakeLock.Release();
|
||||||
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray);
|
lv_obj_set_style_local_text_color(label_hr, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
#include "systemtask/SystemTask.h"
|
#include "systemtask/SystemTask.h"
|
||||||
|
#include "systemtask/WakeLock.h"
|
||||||
#include "Symbols.h"
|
#include "Symbols.h"
|
||||||
#include <lvgl/src/lv_core/lv_style.h>
|
#include <lvgl/src/lv_core/lv_style.h>
|
||||||
#include <lvgl/src/lv_core/lv_obj.h>
|
#include <lvgl/src/lv_core/lv_obj.h>
|
||||||
|
@ -27,7 +28,7 @@ namespace Pinetime {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Controllers::HeartRateController& heartRateController;
|
Controllers::HeartRateController& heartRateController;
|
||||||
Pinetime::System::SystemTask& systemTask;
|
Pinetime::System::WakeLock wakeLock;
|
||||||
void UpdateStartStopButton(bool isRunning);
|
void UpdateStartStopButton(bool isRunning);
|
||||||
lv_obj_t* label_hr;
|
lv_obj_t* label_hr;
|
||||||
lv_obj_t* label_bpm;
|
lv_obj_t* label_bpm;
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
Metronome::Metronome(Controllers::MotorController& motorController, System::SystemTask& systemTask)
|
Metronome::Metronome(Controllers::MotorController& motorController, System::SystemTask& systemTask)
|
||||||
: motorController {motorController}, systemTask {systemTask} {
|
: motorController {motorController}, wakeLock(systemTask) {
|
||||||
|
|
||||||
bpmArc = lv_arc_create(lv_scr_act(), nullptr);
|
bpmArc = lv_arc_create(lv_scr_act(), nullptr);
|
||||||
bpmArc->user_data = this;
|
bpmArc->user_data = this;
|
||||||
|
@ -72,7 +72,6 @@ Metronome::Metronome(Controllers::MotorController& motorController, System::Syst
|
||||||
|
|
||||||
Metronome::~Metronome() {
|
Metronome::~Metronome() {
|
||||||
lv_task_del(taskRefresh);
|
lv_task_del(taskRefresh);
|
||||||
systemTask.PushMessage(System::Messages::EnableSleeping);
|
|
||||||
lv_obj_clean(lv_scr_act());
|
lv_obj_clean(lv_scr_act());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,12 +127,12 @@ void Metronome::OnEvent(lv_obj_t* obj, lv_event_t event) {
|
||||||
metronomeStarted = !metronomeStarted;
|
metronomeStarted = !metronomeStarted;
|
||||||
if (metronomeStarted) {
|
if (metronomeStarted) {
|
||||||
lv_label_set_text_static(lblPlayPause, Symbols::pause);
|
lv_label_set_text_static(lblPlayPause, Symbols::pause);
|
||||||
systemTask.PushMessage(System::Messages::DisableSleeping);
|
wakeLock.Lock();
|
||||||
startTime = xTaskGetTickCount();
|
startTime = xTaskGetTickCount();
|
||||||
counter = 1;
|
counter = 1;
|
||||||
} else {
|
} else {
|
||||||
lv_label_set_text_static(lblPlayPause, Symbols::play);
|
lv_label_set_text_static(lblPlayPause, Symbols::play);
|
||||||
systemTask.PushMessage(System::Messages::EnableSleeping);
|
wakeLock.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "systemtask/SystemTask.h"
|
#include "systemtask/SystemTask.h"
|
||||||
|
#include "systemtask/WakeLock.h"
|
||||||
#include "components/motor/MotorController.h"
|
#include "components/motor/MotorController.h"
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
#include "Symbols.h"
|
#include "Symbols.h"
|
||||||
|
@ -21,7 +22,7 @@ namespace Pinetime {
|
||||||
TickType_t startTime = 0;
|
TickType_t startTime = 0;
|
||||||
TickType_t tappedTime = 0;
|
TickType_t tappedTime = 0;
|
||||||
Controllers::MotorController& motorController;
|
Controllers::MotorController& motorController;
|
||||||
System::SystemTask& systemTask;
|
System::WakeLock wakeLock;
|
||||||
int16_t bpm = 120;
|
int16_t bpm = 120;
|
||||||
uint8_t bpb = 4;
|
uint8_t bpb = 4;
|
||||||
uint8_t counter = 1;
|
uint8_t counter = 1;
|
||||||
|
|
|
@ -20,7 +20,7 @@ Notifications::Notifications(DisplayApp* app,
|
||||||
notificationManager {notificationManager},
|
notificationManager {notificationManager},
|
||||||
alertNotificationService {alertNotificationService},
|
alertNotificationService {alertNotificationService},
|
||||||
motorController {motorController},
|
motorController {motorController},
|
||||||
systemTask {systemTask},
|
wakeLock(systemTask),
|
||||||
mode {mode} {
|
mode {mode} {
|
||||||
|
|
||||||
notificationManager.ClearNewNotificationFlag();
|
notificationManager.ClearNewNotificationFlag();
|
||||||
|
@ -40,7 +40,7 @@ Notifications::Notifications(DisplayApp* app,
|
||||||
validDisplay = false;
|
validDisplay = false;
|
||||||
}
|
}
|
||||||
if (mode == Modes::Preview) {
|
if (mode == Modes::Preview) {
|
||||||
systemTask.PushMessage(System::Messages::DisableSleeping);
|
wakeLock.Lock();
|
||||||
if (notification.category == Controllers::NotificationManager::Categories::IncomingCall) {
|
if (notification.category == Controllers::NotificationManager::Categories::IncomingCall) {
|
||||||
motorController.StartRinging();
|
motorController.StartRinging();
|
||||||
} else {
|
} else {
|
||||||
|
@ -65,7 +65,6 @@ Notifications::~Notifications() {
|
||||||
lv_task_del(taskRefresh);
|
lv_task_del(taskRefresh);
|
||||||
// make sure we stop any vibrations before exiting
|
// make sure we stop any vibrations before exiting
|
||||||
motorController.StopRinging();
|
motorController.StopRinging();
|
||||||
systemTask.PushMessage(System::Messages::EnableSleeping);
|
|
||||||
lv_obj_clean(lv_scr_act());
|
lv_obj_clean(lv_scr_act());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +81,6 @@ void Notifications::Refresh() {
|
||||||
|
|
||||||
} else if (mode == Modes::Preview && dismissingNotification) {
|
} else if (mode == Modes::Preview && dismissingNotification) {
|
||||||
running = false;
|
running = false;
|
||||||
currentItem = std::make_unique<NotificationItem>(alertNotificationService, motorController);
|
|
||||||
|
|
||||||
} else if (dismissingNotification) {
|
} else if (dismissingNotification) {
|
||||||
dismissingNotification = false;
|
dismissingNotification = false;
|
||||||
|
@ -113,15 +111,15 @@ void Notifications::Refresh() {
|
||||||
alertNotificationService,
|
alertNotificationService,
|
||||||
motorController);
|
motorController);
|
||||||
} else {
|
} else {
|
||||||
currentItem = std::make_unique<NotificationItem>(alertNotificationService, motorController);
|
running = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
running = currentItem->IsRunning() && running;
|
running = running && currentItem->IsRunning();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Notifications::OnPreviewInteraction() {
|
void Notifications::OnPreviewInteraction() {
|
||||||
systemTask.PushMessage(System::Messages::EnableSleeping);
|
wakeLock.Release();
|
||||||
motorController.StopRinging();
|
motorController.StopRinging();
|
||||||
if (timeoutLine != nullptr) {
|
if (timeoutLine != nullptr) {
|
||||||
lv_obj_del(timeoutLine);
|
lv_obj_del(timeoutLine);
|
||||||
|
@ -173,7 +171,9 @@ bool Notifications::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
|
||||||
} else if (nextMessage.valid) {
|
} else if (nextMessage.valid) {
|
||||||
currentId = nextMessage.id;
|
currentId = nextMessage.id;
|
||||||
} else {
|
} else {
|
||||||
// don't update id, won't be found be refresh and try to load latest message or no message box
|
// don't update id, notification manager will try to fetch
|
||||||
|
// but not find it. Refresh will try to load latest message
|
||||||
|
// or dismiss to watchface
|
||||||
}
|
}
|
||||||
DismissToBlack();
|
DismissToBlack();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "components/ble/NotificationManager.h"
|
#include "components/ble/NotificationManager.h"
|
||||||
#include "components/motor/MotorController.h"
|
#include "components/motor/MotorController.h"
|
||||||
#include "systemtask/SystemTask.h"
|
#include "systemtask/SystemTask.h"
|
||||||
|
#include "systemtask/WakeLock.h"
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Controllers {
|
namespace Controllers {
|
||||||
|
@ -73,7 +74,7 @@ namespace Pinetime {
|
||||||
Pinetime::Controllers::NotificationManager& notificationManager;
|
Pinetime::Controllers::NotificationManager& notificationManager;
|
||||||
Pinetime::Controllers::AlertNotificationService& alertNotificationService;
|
Pinetime::Controllers::AlertNotificationService& alertNotificationService;
|
||||||
Pinetime::Controllers::MotorController& motorController;
|
Pinetime::Controllers::MotorController& motorController;
|
||||||
System::SystemTask& systemTask;
|
System::WakeLock wakeLock;
|
||||||
Modes mode = Modes::Normal;
|
Modes mode = Modes::Normal;
|
||||||
std::unique_ptr<NotificationItem> currentItem;
|
std::unique_ptr<NotificationItem> currentItem;
|
||||||
Pinetime::Controllers::NotificationManager::Notification::Id currentId;
|
Pinetime::Controllers::NotificationManager::Notification::Id currentId;
|
||||||
|
|
|
@ -34,7 +34,7 @@ namespace {
|
||||||
constexpr TickType_t blinkInterval = pdMS_TO_TICKS(1000);
|
constexpr TickType_t blinkInterval = pdMS_TO_TICKS(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
StopWatch::StopWatch(System::SystemTask& systemTask) : systemTask {systemTask} {
|
StopWatch::StopWatch(System::SystemTask& systemTask) : wakeLock(systemTask) {
|
||||||
static constexpr uint8_t btnWidth = 115;
|
static constexpr uint8_t btnWidth = 115;
|
||||||
static constexpr uint8_t btnHeight = 80;
|
static constexpr uint8_t btnHeight = 80;
|
||||||
btnPlayPause = lv_btn_create(lv_scr_act(), nullptr);
|
btnPlayPause = lv_btn_create(lv_scr_act(), nullptr);
|
||||||
|
@ -79,7 +79,6 @@ StopWatch::StopWatch(System::SystemTask& systemTask) : systemTask {systemTask} {
|
||||||
|
|
||||||
StopWatch::~StopWatch() {
|
StopWatch::~StopWatch() {
|
||||||
lv_task_del(taskRefresh);
|
lv_task_del(taskRefresh);
|
||||||
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
|
|
||||||
lv_obj_clean(lv_scr_act());
|
lv_obj_clean(lv_scr_act());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +134,7 @@ void StopWatch::Start() {
|
||||||
SetInterfaceRunning();
|
SetInterfaceRunning();
|
||||||
startTime = xTaskGetTickCount();
|
startTime = xTaskGetTickCount();
|
||||||
currentState = States::Running;
|
currentState = States::Running;
|
||||||
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
|
wakeLock.Lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StopWatch::Pause() {
|
void StopWatch::Pause() {
|
||||||
|
@ -145,7 +144,7 @@ void StopWatch::Pause() {
|
||||||
oldTimeElapsed = laps[lapsDone];
|
oldTimeElapsed = laps[lapsDone];
|
||||||
blinkTime = xTaskGetTickCount() + blinkInterval;
|
blinkTime = xTaskGetTickCount() + blinkInterval;
|
||||||
currentState = States::Halted;
|
currentState = States::Halted;
|
||||||
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
|
wakeLock.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StopWatch::Refresh() {
|
void StopWatch::Refresh() {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "portmacro_cmsis.h"
|
#include "portmacro_cmsis.h"
|
||||||
|
|
||||||
#include "systemtask/SystemTask.h"
|
#include "systemtask/SystemTask.h"
|
||||||
|
#include "systemtask/WakeLock.h"
|
||||||
#include "displayapp/apps/Apps.h"
|
#include "displayapp/apps/Apps.h"
|
||||||
#include "displayapp/Controllers.h"
|
#include "displayapp/Controllers.h"
|
||||||
#include "Symbols.h"
|
#include "Symbols.h"
|
||||||
|
@ -43,7 +44,7 @@ namespace Pinetime {
|
||||||
void Start();
|
void Start();
|
||||||
void Pause();
|
void Pause();
|
||||||
|
|
||||||
Pinetime::System::SystemTask& systemTask;
|
Pinetime::System::WakeLock wakeLock;
|
||||||
States currentState = States::Init;
|
States currentState = States::Init;
|
||||||
TickType_t startTime;
|
TickType_t startTime;
|
||||||
TickType_t oldTimeElapsed = 0;
|
TickType_t oldTimeElapsed = 0;
|
||||||
|
|
|
@ -40,8 +40,7 @@ SystemInfo::SystemInfo(Pinetime::Applications::DisplayApp* app,
|
||||||
Pinetime::Controllers::MotionController& motionController,
|
Pinetime::Controllers::MotionController& motionController,
|
||||||
const Pinetime::Drivers::Cst816S& touchPanel,
|
const Pinetime::Drivers::Cst816S& touchPanel,
|
||||||
const Pinetime::Drivers::SpiNorFlash& spiNorFlash)
|
const Pinetime::Drivers::SpiNorFlash& spiNorFlash)
|
||||||
: app {app},
|
: dateTimeController {dateTimeController},
|
||||||
dateTimeController {dateTimeController},
|
|
||||||
batteryController {batteryController},
|
batteryController {batteryController},
|
||||||
brightnessController {brightnessController},
|
brightnessController {brightnessController},
|
||||||
bleController {bleController},
|
bleController {bleController},
|
||||||
|
|
|
@ -35,7 +35,6 @@ namespace Pinetime {
|
||||||
bool OnTouchEvent(TouchEvents event) override;
|
bool OnTouchEvent(TouchEvents event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DisplayApp* app;
|
|
||||||
Pinetime::Controllers::DateTime& dateTimeController;
|
Pinetime::Controllers::DateTime& dateTimeController;
|
||||||
const Pinetime::Controllers::Battery& batteryController;
|
const Pinetime::Controllers::Battery& batteryController;
|
||||||
Pinetime::Controllers::BrightnessController& brightnessController;
|
Pinetime::Controllers::BrightnessController& brightnessController;
|
||||||
|
|
|
@ -24,8 +24,7 @@ namespace {
|
||||||
|
|
||||||
constexpr std::array<uint16_t, 6> SettingDisplay::options;
|
constexpr std::array<uint16_t, 6> SettingDisplay::options;
|
||||||
|
|
||||||
SettingDisplay::SettingDisplay(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Settings& settingsController)
|
SettingDisplay::SettingDisplay(Pinetime::Controllers::Settings& settingsController) : settingsController {settingsController} {
|
||||||
: app {app}, settingsController {settingsController} {
|
|
||||||
|
|
||||||
lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr);
|
lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr);
|
||||||
|
|
||||||
|
|
|
@ -14,14 +14,13 @@ namespace Pinetime {
|
||||||
|
|
||||||
class SettingDisplay : public Screen {
|
class SettingDisplay : public Screen {
|
||||||
public:
|
public:
|
||||||
SettingDisplay(DisplayApp* app, Pinetime::Controllers::Settings& settingsController);
|
SettingDisplay(Pinetime::Controllers::Settings& settingsController);
|
||||||
~SettingDisplay() override;
|
~SettingDisplay() override;
|
||||||
|
|
||||||
void UpdateSelected(lv_obj_t* object, lv_event_t event);
|
void UpdateSelected(lv_obj_t* object, lv_event_t event);
|
||||||
void ToggleAlwaysOn();
|
void ToggleAlwaysOn();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DisplayApp* app;
|
|
||||||
static constexpr std::array<uint16_t, 6> options = {5000, 7000, 10000, 15000, 20000, 30000};
|
static constexpr std::array<uint16_t, 6> options = {5000, 7000, 10000, 15000, 20000, 30000};
|
||||||
|
|
||||||
Controllers::Settings& settingsController;
|
Controllers::Settings& settingsController;
|
||||||
|
|
75
src/displayapp/screens/settings/SettingHeartRate.cpp
Normal file
75
src/displayapp/screens/settings/SettingHeartRate.cpp
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#include "displayapp/screens/settings/SettingHeartRate.h"
|
||||||
|
#include <lvgl/lvgl.h>
|
||||||
|
#include "displayapp/screens/Styles.h"
|
||||||
|
#include "displayapp/screens/Screen.h"
|
||||||
|
#include "displayapp/screens/Symbols.h"
|
||||||
|
#include <array>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using namespace Pinetime::Applications::Screens;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void event_handler(lv_obj_t* obj, lv_event_t event) {
|
||||||
|
auto* screen = static_cast<SettingHeartRate*>(obj->user_data);
|
||||||
|
screen->UpdateSelected(obj, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr std::array<Option, 8> SettingHeartRate::options;
|
||||||
|
|
||||||
|
SettingHeartRate::SettingHeartRate(Pinetime::Controllers::Settings& settingsController) : settingsController {settingsController} {
|
||||||
|
|
||||||
|
lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr);
|
||||||
|
|
||||||
|
lv_obj_set_style_local_bg_opa(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_TRANSP);
|
||||||
|
lv_obj_set_style_local_pad_all(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5);
|
||||||
|
lv_obj_set_style_local_pad_inner(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 5);
|
||||||
|
lv_obj_set_style_local_border_width(container1, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 0);
|
||||||
|
|
||||||
|
lv_obj_set_pos(container1, 10, 60);
|
||||||
|
lv_obj_set_width(container1, LV_HOR_RES - 20);
|
||||||
|
lv_obj_set_height(container1, LV_VER_RES - 50);
|
||||||
|
lv_cont_set_layout(container1, LV_LAYOUT_PRETTY_TOP);
|
||||||
|
|
||||||
|
lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_label_set_text_static(title, "Backg. Interval");
|
||||||
|
lv_label_set_text(title, "Backg. Interval");
|
||||||
|
lv_label_set_align(title, LV_LABEL_ALIGN_CENTER);
|
||||||
|
lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 10, 15);
|
||||||
|
|
||||||
|
lv_obj_t* icon = lv_label_create(lv_scr_act(), nullptr);
|
||||||
|
lv_obj_set_style_local_text_color(icon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED);
|
||||||
|
lv_label_set_text_static(icon, Symbols::heartBeat);
|
||||||
|
lv_label_set_align(icon, LV_LABEL_ALIGN_CENTER);
|
||||||
|
lv_obj_align(icon, title, LV_ALIGN_OUT_LEFT_MID, -10, 0);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < options.size(); i++) {
|
||||||
|
cbOption[i] = lv_checkbox_create(container1, nullptr);
|
||||||
|
lv_checkbox_set_text(cbOption[i], options[i].name);
|
||||||
|
cbOption[i]->user_data = this;
|
||||||
|
lv_obj_set_event_cb(cbOption[i], event_handler);
|
||||||
|
SetRadioButtonStyle(cbOption[i]);
|
||||||
|
|
||||||
|
if (settingsController.GetHeartRateBackgroundMeasurementInterval() == options[i].interval) {
|
||||||
|
lv_checkbox_set_checked(cbOption[i], true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingHeartRate::~SettingHeartRate() {
|
||||||
|
lv_obj_clean(lv_scr_act());
|
||||||
|
settingsController.SaveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingHeartRate::UpdateSelected(lv_obj_t* object, lv_event_t event) {
|
||||||
|
if (event == LV_EVENT_CLICKED) {
|
||||||
|
for (unsigned int i = 0; i < options.size(); i++) {
|
||||||
|
if (object == cbOption[i]) {
|
||||||
|
lv_checkbox_set_checked(cbOption[i], true);
|
||||||
|
settingsController.SetHeartRateBackgroundMeasurementInterval(options[i].interval);
|
||||||
|
} else {
|
||||||
|
lv_checkbox_set_checked(cbOption[i], false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
src/displayapp/screens/settings/SettingHeartRate.h
Normal file
47
src/displayapp/screens/settings/SettingHeartRate.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <lvgl/lvgl.h>
|
||||||
|
|
||||||
|
#include "components/settings/Settings.h"
|
||||||
|
#include "displayapp/screens/ScreenList.h"
|
||||||
|
#include "displayapp/screens/Screen.h"
|
||||||
|
#include "displayapp/screens/Symbols.h"
|
||||||
|
#include "displayapp/screens/CheckboxList.h"
|
||||||
|
|
||||||
|
namespace Pinetime {
|
||||||
|
|
||||||
|
namespace Applications {
|
||||||
|
namespace Screens {
|
||||||
|
|
||||||
|
struct Option {
|
||||||
|
const Pinetime::Controllers::Settings::HeartRateBackgroundMeasurementInterval interval;
|
||||||
|
const char* name;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SettingHeartRate : public Screen {
|
||||||
|
public:
|
||||||
|
SettingHeartRate(Pinetime::Controllers::Settings& settings);
|
||||||
|
~SettingHeartRate() override;
|
||||||
|
|
||||||
|
void UpdateSelected(lv_obj_t* object, lv_event_t event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Pinetime::Controllers::Settings& settingsController;
|
||||||
|
|
||||||
|
static constexpr std::array<Option, 8> options = {{
|
||||||
|
{Pinetime::Controllers::Settings::HeartRateBackgroundMeasurementInterval::Off, " Off"},
|
||||||
|
{Pinetime::Controllers::Settings::HeartRateBackgroundMeasurementInterval::Continuous, "Cont"},
|
||||||
|
{Pinetime::Controllers::Settings::HeartRateBackgroundMeasurementInterval::FifteenSeconds, " 15s"},
|
||||||
|
{Pinetime::Controllers::Settings::HeartRateBackgroundMeasurementInterval::ThirtySeconds, " 30s"},
|
||||||
|
{Pinetime::Controllers::Settings::HeartRateBackgroundMeasurementInterval::OneMinute, " 1m"},
|
||||||
|
{Pinetime::Controllers::Settings::HeartRateBackgroundMeasurementInterval::FiveMinutes, " 5m"},
|
||||||
|
{Pinetime::Controllers::Settings::HeartRateBackgroundMeasurementInterval::TenMinutes, " 10m"},
|
||||||
|
{Pinetime::Controllers::Settings::HeartRateBackgroundMeasurementInterval::ThirtyMinutes, " 30m"},
|
||||||
|
}};
|
||||||
|
|
||||||
|
lv_obj_t* cbOption[options.size()];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,8 +15,7 @@ bool SettingSetDateTime::OnTouchEvent(Pinetime::Applications::TouchEvents event)
|
||||||
SettingSetDateTime::SettingSetDateTime(Pinetime::Applications::DisplayApp* app,
|
SettingSetDateTime::SettingSetDateTime(Pinetime::Applications::DisplayApp* app,
|
||||||
Pinetime::Controllers::DateTime& dateTimeController,
|
Pinetime::Controllers::DateTime& dateTimeController,
|
||||||
Pinetime::Controllers::Settings& settingsController)
|
Pinetime::Controllers::Settings& settingsController)
|
||||||
: app {app},
|
: dateTimeController {dateTimeController},
|
||||||
dateTimeController {dateTimeController},
|
|
||||||
settingsController {settingsController},
|
settingsController {settingsController},
|
||||||
screens {app,
|
screens {app,
|
||||||
0,
|
0,
|
||||||
|
|
|
@ -20,7 +20,6 @@ namespace Pinetime {
|
||||||
void Quit();
|
void Quit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DisplayApp* app;
|
|
||||||
Controllers::DateTime& dateTimeController;
|
Controllers::DateTime& dateTimeController;
|
||||||
Controllers::Settings& settingsController;
|
Controllers::Settings& settingsController;
|
||||||
|
|
||||||
|
|
|
@ -54,8 +54,7 @@ SettingWatchFace::SettingWatchFace(Pinetime::Applications::DisplayApp* app,
|
||||||
std::array<Screens::SettingWatchFace::Item, UserWatchFaceTypes::Count>&& watchfaceItems,
|
std::array<Screens::SettingWatchFace::Item, UserWatchFaceTypes::Count>&& watchfaceItems,
|
||||||
Pinetime::Controllers::Settings& settingsController,
|
Pinetime::Controllers::Settings& settingsController,
|
||||||
Pinetime::Controllers::FS& filesystem)
|
Pinetime::Controllers::FS& filesystem)
|
||||||
: app {app},
|
: watchfaceItems {std::move(watchfaceItems)},
|
||||||
watchfaceItems {std::move(watchfaceItems)},
|
|
||||||
settingsController {settingsController},
|
settingsController {settingsController},
|
||||||
filesystem {filesystem},
|
filesystem {filesystem},
|
||||||
screens {app, 0, CreateScreenList(), Screens::ScreenListModes::UpDown} {
|
screens {app, 0, CreateScreenList(), Screens::ScreenListModes::UpDown} {
|
||||||
|
|
|
@ -34,7 +34,6 @@ namespace Pinetime {
|
||||||
bool OnTouchEvent(TouchEvents event) override;
|
bool OnTouchEvent(TouchEvents event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DisplayApp* app;
|
|
||||||
auto CreateScreenList() const;
|
auto CreateScreenList() const;
|
||||||
std::unique_ptr<Screen> CreateScreen(unsigned int screenNum) const;
|
std::unique_ptr<Screen> CreateScreen(unsigned int screenNum) const;
|
||||||
|
|
||||||
|
|
|
@ -38,15 +38,16 @@ namespace Pinetime {
|
||||||
{Symbols::home, "Watch face", Apps::SettingWatchFace},
|
{Symbols::home, "Watch face", Apps::SettingWatchFace},
|
||||||
|
|
||||||
{Symbols::shoe, "Steps", Apps::SettingSteps},
|
{Symbols::shoe, "Steps", Apps::SettingSteps},
|
||||||
|
{Symbols::heartBeat, "Heartrate", Apps::SettingHeartRate},
|
||||||
{Symbols::clock, "Date&Time", Apps::SettingSetDateTime},
|
{Symbols::clock, "Date&Time", Apps::SettingSetDateTime},
|
||||||
{Symbols::cloudSunRain, "Weather", Apps::SettingWeatherFormat},
|
{Symbols::cloudSunRain, "Weather", Apps::SettingWeatherFormat},
|
||||||
{Symbols::batteryHalf, "Battery", Apps::BatteryInfo},
|
|
||||||
|
|
||||||
|
{Symbols::batteryHalf, "Battery", Apps::BatteryInfo},
|
||||||
{Symbols::clock, "Chimes", Apps::SettingChimes},
|
{Symbols::clock, "Chimes", Apps::SettingChimes},
|
||||||
{Symbols::tachometer, "Shake Calib.", Apps::SettingShakeThreshold},
|
{Symbols::tachometer, "Shake Calib.", Apps::SettingShakeThreshold},
|
||||||
{Symbols::check, "Firmware", Apps::FirmwareValidation},
|
{Symbols::check, "Firmware", Apps::FirmwareValidation},
|
||||||
{Symbols::bluetooth, "Bluetooth", Apps::SettingBluetooth},
|
|
||||||
|
|
||||||
|
{Symbols::bluetooth, "Bluetooth", Apps::SettingBluetooth},
|
||||||
{Symbols::list, "About", Apps::SysInfo},
|
{Symbols::list, "About", Apps::SysInfo},
|
||||||
|
|
||||||
// {Symbols::none, "None", Apps::None},
|
// {Symbols::none, "None", Apps::None},
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "drivers/Hrs3300.h"
|
#include "drivers/Hrs3300.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
#include <nrf_gpio.h>
|
#include <nrf_gpio.h>
|
||||||
|
|
||||||
#include <FreeRTOS.h>
|
#include <FreeRTOS.h>
|
||||||
|
@ -67,40 +68,37 @@ void Hrs3300::Disable() {
|
||||||
WriteRegister(static_cast<uint8_t>(Registers::PDriver), 0);
|
WriteRegister(static_cast<uint8_t>(Registers::PDriver), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Hrs3300::ReadHrs() {
|
Hrs3300::PackedHrsAls Hrs3300::ReadHrsAls() {
|
||||||
auto m = ReadRegister(static_cast<uint8_t>(Registers::C0DataM));
|
constexpr Registers dataRegisters[] =
|
||||||
auto h = ReadRegister(static_cast<uint8_t>(Registers::C0DataH));
|
{Registers::C1dataM, Registers::C0DataM, Registers::C0DataH, Registers::C1dataH, Registers::C1dataL, Registers::C0dataL};
|
||||||
auto l = ReadRegister(static_cast<uint8_t>(Registers::C0dataL));
|
// Calculate smallest register address
|
||||||
return ((l & 0x30) << 12) | (m << 8) | ((h & 0x0f) << 4) | (l & 0x0f);
|
constexpr uint8_t baseOffset = static_cast<uint8_t>(*std::min_element(std::begin(dataRegisters), std::end(dataRegisters)));
|
||||||
}
|
// Calculate largest address to determine length of read needed
|
||||||
|
// Add one to largest relative index to find the length
|
||||||
|
constexpr uint8_t length = static_cast<uint8_t>(*std::max_element(std::begin(dataRegisters), std::end(dataRegisters))) - baseOffset + 1;
|
||||||
|
|
||||||
uint32_t Hrs3300::ReadAls() {
|
Hrs3300::PackedHrsAls res;
|
||||||
auto m = ReadRegister(static_cast<uint8_t>(Registers::C1dataM));
|
uint8_t buf[length];
|
||||||
auto h = ReadRegister(static_cast<uint8_t>(Registers::C1dataH));
|
auto ret = twiMaster.Read(twiAddress, baseOffset, buf, length);
|
||||||
auto l = ReadRegister(static_cast<uint8_t>(Registers::C1dataL));
|
if (ret != TwiMaster::ErrorCodes::NoError) {
|
||||||
return ((h & 0x3f) << 11) | (m << 3) | (l & 0x07);
|
NRF_LOG_INFO("READ ERROR");
|
||||||
}
|
|
||||||
|
|
||||||
void Hrs3300::SetGain(uint8_t gain) {
|
|
||||||
constexpr uint8_t maxGain = 64U;
|
|
||||||
gain = std::min(gain, maxGain);
|
|
||||||
uint8_t hgain = 0;
|
|
||||||
while ((1 << hgain) < gain) {
|
|
||||||
++hgain;
|
|
||||||
}
|
}
|
||||||
|
// hrs
|
||||||
|
uint8_t m = static_cast<uint8_t>(Registers::C0DataM) - baseOffset;
|
||||||
|
uint8_t h = static_cast<uint8_t>(Registers::C0DataH) - baseOffset;
|
||||||
|
uint8_t l = static_cast<uint8_t>(Registers::C0dataL) - baseOffset;
|
||||||
|
// There are two extra bits (17 and 18) but they are not read here
|
||||||
|
// as resolutions >16bit aren't practically useful (too slow) and
|
||||||
|
// all hrs values throughout InfiniTime are 16bit
|
||||||
|
res.hrs = (buf[m] << 8) | ((buf[h] & 0x0f) << 4) | (buf[l] & 0x0f);
|
||||||
|
|
||||||
WriteRegister(static_cast<uint8_t>(Registers::Hgain), hgain << 2);
|
// als
|
||||||
}
|
m = static_cast<uint8_t>(Registers::C1dataM) - baseOffset;
|
||||||
|
h = static_cast<uint8_t>(Registers::C1dataH) - baseOffset;
|
||||||
|
l = static_cast<uint8_t>(Registers::C1dataL) - baseOffset;
|
||||||
|
res.als = ((buf[h] & 0x3f) << 11) | (buf[m] << 3) | (buf[l] & 0x07);
|
||||||
|
|
||||||
void Hrs3300::SetDrive(uint8_t drive) {
|
return res;
|
||||||
auto en = ReadRegister(static_cast<uint8_t>(Registers::Enable));
|
|
||||||
auto pd = ReadRegister(static_cast<uint8_t>(Registers::PDriver));
|
|
||||||
|
|
||||||
en = (en & 0xf7) | ((drive & 2) << 2);
|
|
||||||
pd = (pd & 0xbf) | ((drive & 1) << 6);
|
|
||||||
|
|
||||||
WriteRegister(static_cast<uint8_t>(Registers::Enable), en);
|
|
||||||
WriteRegister(static_cast<uint8_t>(Registers::PDriver), pd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hrs3300::WriteRegister(uint8_t reg, uint8_t data) {
|
void Hrs3300::WriteRegister(uint8_t reg, uint8_t data) {
|
||||||
|
|
|
@ -21,6 +21,11 @@ namespace Pinetime {
|
||||||
Hgain = 0x17
|
Hgain = 0x17
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PackedHrsAls {
|
||||||
|
uint16_t hrs;
|
||||||
|
uint16_t als;
|
||||||
|
};
|
||||||
|
|
||||||
Hrs3300(TwiMaster& twiMaster, uint8_t twiAddress);
|
Hrs3300(TwiMaster& twiMaster, uint8_t twiAddress);
|
||||||
Hrs3300(const Hrs3300&) = delete;
|
Hrs3300(const Hrs3300&) = delete;
|
||||||
Hrs3300& operator=(const Hrs3300&) = delete;
|
Hrs3300& operator=(const Hrs3300&) = delete;
|
||||||
|
@ -30,10 +35,7 @@ namespace Pinetime {
|
||||||
void Init();
|
void Init();
|
||||||
void Enable();
|
void Enable();
|
||||||
void Disable();
|
void Disable();
|
||||||
uint32_t ReadHrs();
|
PackedHrsAls ReadHrsAls();
|
||||||
uint32_t ReadAls();
|
|
||||||
void SetGain(uint8_t gain);
|
|
||||||
void SetDrive(uint8_t drive);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TwiMaster& twiMaster;
|
TwiMaster& twiMaster;
|
||||||
|
|
|
@ -5,8 +5,23 @@
|
||||||
|
|
||||||
using namespace Pinetime::Applications;
|
using namespace Pinetime::Applications;
|
||||||
|
|
||||||
HeartRateTask::HeartRateTask(Drivers::Hrs3300& heartRateSensor, Controllers::HeartRateController& controller)
|
TickType_t CurrentTaskDelay(HeartRateTask::States state, TickType_t ppgDeltaTms) {
|
||||||
: heartRateSensor {heartRateSensor}, controller {controller} {
|
switch (state) {
|
||||||
|
case HeartRateTask::States::ScreenOnAndMeasuring:
|
||||||
|
case HeartRateTask::States::ScreenOffAndMeasuring:
|
||||||
|
return ppgDeltaTms;
|
||||||
|
case HeartRateTask::States::ScreenOffAndWaiting:
|
||||||
|
return pdMS_TO_TICKS(1000);
|
||||||
|
default:
|
||||||
|
return portMAX_DELAY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HeartRateTask::HeartRateTask(Drivers::Hrs3300& heartRateSensor,
|
||||||
|
Controllers::HeartRateController& controller,
|
||||||
|
Controllers::Settings& settings)
|
||||||
|
: heartRateSensor {heartRateSensor}, controller {controller}, settings {settings} {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeartRateTask::Start() {
|
void HeartRateTask::Start() {
|
||||||
|
@ -25,77 +40,40 @@ void HeartRateTask::Process(void* instance) {
|
||||||
|
|
||||||
void HeartRateTask::Work() {
|
void HeartRateTask::Work() {
|
||||||
int lastBpm = 0;
|
int lastBpm = 0;
|
||||||
while (true) {
|
|
||||||
Messages msg;
|
|
||||||
uint32_t delay;
|
|
||||||
if (state == States::Running) {
|
|
||||||
if (measurementStarted) {
|
|
||||||
delay = ppg.deltaTms;
|
|
||||||
} else {
|
|
||||||
delay = 100;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
delay = portMAX_DELAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xQueueReceive(messageQueue, &msg, delay)) {
|
while (true) {
|
||||||
|
TickType_t delay = CurrentTaskDelay(state, ppg.deltaTms);
|
||||||
|
Messages msg;
|
||||||
|
|
||||||
|
if (xQueueReceive(messageQueue, &msg, delay) == pdTRUE) {
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case Messages::GoToSleep:
|
case Messages::GoToSleep:
|
||||||
StopMeasurement();
|
HandleGoToSleep();
|
||||||
state = States::Idle;
|
|
||||||
break;
|
break;
|
||||||
case Messages::WakeUp:
|
case Messages::WakeUp:
|
||||||
state = States::Running;
|
HandleWakeUp();
|
||||||
if (measurementStarted) {
|
|
||||||
lastBpm = 0;
|
|
||||||
StartMeasurement();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case Messages::StartMeasurement:
|
case Messages::StartMeasurement:
|
||||||
if (measurementStarted) {
|
HandleStartMeasurement(&lastBpm);
|
||||||
break;
|
|
||||||
}
|
|
||||||
lastBpm = 0;
|
|
||||||
StartMeasurement();
|
|
||||||
measurementStarted = true;
|
|
||||||
break;
|
break;
|
||||||
case Messages::StopMeasurement:
|
case Messages::StopMeasurement:
|
||||||
if (!measurementStarted) {
|
HandleStopMeasurement();
|
||||||
break;
|
|
||||||
}
|
|
||||||
StopMeasurement();
|
|
||||||
measurementStarted = false;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (measurementStarted) {
|
switch (state) {
|
||||||
int8_t ambient = ppg.Preprocess(heartRateSensor.ReadHrs(), heartRateSensor.ReadAls());
|
case States::ScreenOffAndWaiting:
|
||||||
int bpm = ppg.HeartRate();
|
HandleBackgroundWaiting();
|
||||||
|
break;
|
||||||
// If ambient light detected or a reset requested (bpm < 0)
|
case States::ScreenOffAndMeasuring:
|
||||||
if (ambient > 0) {
|
case States::ScreenOnAndMeasuring:
|
||||||
// Reset all DAQ buffers
|
HandleSensorData(&lastBpm);
|
||||||
ppg.Reset(true);
|
break;
|
||||||
// Force state to NotEnoughData (below)
|
case States::ScreenOffAndStopped:
|
||||||
lastBpm = 0;
|
case States::ScreenOnAndStopped:
|
||||||
bpm = 0;
|
// nothing to do -> ignore
|
||||||
} else if (bpm < 0) {
|
break;
|
||||||
// Reset all DAQ buffers except HRS buffer
|
|
||||||
ppg.Reset(false);
|
|
||||||
// Set HR to zero and update
|
|
||||||
bpm = 0;
|
|
||||||
controller.Update(Controllers::HeartRateController::States::Running, bpm);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastBpm == 0 && bpm == 0) {
|
|
||||||
controller.Update(Controllers::HeartRateController::States::NotEnoughData, bpm);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bpm != 0) {
|
|
||||||
lastBpm = bpm;
|
|
||||||
controller.Update(Controllers::HeartRateController::States::Running, lastBpm);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,6 +88,7 @@ void HeartRateTask::StartMeasurement() {
|
||||||
heartRateSensor.Enable();
|
heartRateSensor.Enable();
|
||||||
ppg.Reset(true);
|
ppg.Reset(true);
|
||||||
vTaskDelay(100);
|
vTaskDelay(100);
|
||||||
|
measurementStart = xTaskGetTickCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeartRateTask::StopMeasurement() {
|
void HeartRateTask::StopMeasurement() {
|
||||||
|
@ -117,3 +96,182 @@ void HeartRateTask::StopMeasurement() {
|
||||||
ppg.Reset(true);
|
ppg.Reset(true);
|
||||||
vTaskDelay(100);
|
vTaskDelay(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HeartRateTask::HandleGoToSleep() {
|
||||||
|
switch (state) {
|
||||||
|
case States::ScreenOnAndStopped:
|
||||||
|
state = States::ScreenOffAndStopped;
|
||||||
|
break;
|
||||||
|
case States::ScreenOnAndMeasuring:
|
||||||
|
state = States::ScreenOffAndMeasuring;
|
||||||
|
break;
|
||||||
|
case States::ScreenOffAndStopped:
|
||||||
|
case States::ScreenOffAndWaiting:
|
||||||
|
case States::ScreenOffAndMeasuring:
|
||||||
|
// shouldn't happen -> ignore
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HeartRateTask::HandleWakeUp() {
|
||||||
|
switch (state) {
|
||||||
|
case States::ScreenOffAndStopped:
|
||||||
|
state = States::ScreenOnAndStopped;
|
||||||
|
break;
|
||||||
|
case States::ScreenOffAndMeasuring:
|
||||||
|
state = States::ScreenOnAndMeasuring;
|
||||||
|
break;
|
||||||
|
case States::ScreenOffAndWaiting:
|
||||||
|
state = States::ScreenOnAndMeasuring;
|
||||||
|
StartMeasurement();
|
||||||
|
break;
|
||||||
|
case States::ScreenOnAndStopped:
|
||||||
|
case States::ScreenOnAndMeasuring:
|
||||||
|
// shouldn't happen -> ignore
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HeartRateTask::HandleStartMeasurement(int* lastBpm) {
|
||||||
|
switch (state) {
|
||||||
|
case States::ScreenOffAndStopped:
|
||||||
|
case States::ScreenOnAndStopped:
|
||||||
|
state = States::ScreenOnAndMeasuring;
|
||||||
|
*lastBpm = 0;
|
||||||
|
StartMeasurement();
|
||||||
|
break;
|
||||||
|
case States::ScreenOnAndMeasuring:
|
||||||
|
case States::ScreenOffAndMeasuring:
|
||||||
|
case States::ScreenOffAndWaiting:
|
||||||
|
// shouldn't happen -> ignore
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HeartRateTask::HandleStopMeasurement() {
|
||||||
|
switch (state) {
|
||||||
|
case States::ScreenOnAndMeasuring:
|
||||||
|
state = States::ScreenOnAndStopped;
|
||||||
|
StopMeasurement();
|
||||||
|
break;
|
||||||
|
case States::ScreenOffAndMeasuring:
|
||||||
|
case States::ScreenOffAndWaiting:
|
||||||
|
state = States::ScreenOffAndStopped;
|
||||||
|
StopMeasurement();
|
||||||
|
break;
|
||||||
|
case States::ScreenOnAndStopped:
|
||||||
|
case States::ScreenOffAndStopped:
|
||||||
|
// shouldn't happen -> ignore
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HeartRateTask::HandleBackgroundWaiting() {
|
||||||
|
if (!IsBackgroundMeasurementActivated()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ShouldStartBackgroundMeasuring()) {
|
||||||
|
state = States::ScreenOffAndMeasuring;
|
||||||
|
StartMeasurement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HeartRateTask::HandleSensorData(int* lastBpm) {
|
||||||
|
int8_t ambient = ppg.Preprocess(heartRateSensor.ReadHrs(), heartRateSensor.ReadAls());
|
||||||
|
int bpm = ppg.HeartRate();
|
||||||
|
|
||||||
|
// If ambient light detected or a reset requested (bpm < 0)
|
||||||
|
if (ambient > 0) {
|
||||||
|
// Reset all DAQ buffers
|
||||||
|
ppg.Reset(true);
|
||||||
|
} else if (bpm < 0) {
|
||||||
|
// Reset all DAQ buffers except HRS buffer
|
||||||
|
ppg.Reset(false);
|
||||||
|
// Set HR to zero and update
|
||||||
|
bpm = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool notEnoughData = *lastBpm == 0 && bpm == 0;
|
||||||
|
if (notEnoughData) {
|
||||||
|
controller.Update(Controllers::HeartRateController::States::NotEnoughData, bpm);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bpm != 0) {
|
||||||
|
*lastBpm = bpm;
|
||||||
|
controller.Update(Controllers::HeartRateController::States::Running, bpm);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == States::ScreenOnAndMeasuring || IsContinuousModeActivated()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// state == States::ScreenOffAndMeasuring
|
||||||
|
// (because state != ScreenOnAndMeasuring and the only state that enables measuring is ScreenOffAndMeasuring)
|
||||||
|
// !IsContinuousModeActivated()
|
||||||
|
|
||||||
|
if (ShouldStartBackgroundMeasuring()) {
|
||||||
|
// This doesn't change the state but resets the measurment timer, which basically starts the next measurment without resetting the sensor.
|
||||||
|
// This is basically a fall back to continuous mode, when measurments take too long.
|
||||||
|
measurementStart = xTaskGetTickCount();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool noDataWithinTimeLimit = bpm == 0 && ShoudStopTryingToGetData();
|
||||||
|
bool dataWithinTimeLimit = bpm != 0;
|
||||||
|
if (dataWithinTimeLimit || noDataWithinTimeLimit) {
|
||||||
|
state = States::ScreenOffAndWaiting;
|
||||||
|
StopMeasurement();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TickType_t HeartRateTask::GetHeartRateBackgroundMeasurementIntervalInTicks() {
|
||||||
|
int ms;
|
||||||
|
switch (settings.GetHeartRateBackgroundMeasurementInterval()) {
|
||||||
|
case Pinetime::Controllers::Settings::HeartRateBackgroundMeasurementInterval::FifteenSeconds:
|
||||||
|
ms = 15 * 1000;
|
||||||
|
break;
|
||||||
|
case Pinetime::Controllers::Settings::HeartRateBackgroundMeasurementInterval::ThirtySeconds:
|
||||||
|
ms = 30 * 1000;
|
||||||
|
break;
|
||||||
|
case Pinetime::Controllers::Settings::HeartRateBackgroundMeasurementInterval::OneMinute:
|
||||||
|
ms = 60 * 1000;
|
||||||
|
break;
|
||||||
|
case Pinetime::Controllers::Settings::HeartRateBackgroundMeasurementInterval::FiveMinutes:
|
||||||
|
ms = 5 * 60 * 1000;
|
||||||
|
break;
|
||||||
|
case Pinetime::Controllers::Settings::HeartRateBackgroundMeasurementInterval::TenMinutes:
|
||||||
|
ms = 10 * 60 * 1000;
|
||||||
|
break;
|
||||||
|
case Pinetime::Controllers::Settings::HeartRateBackgroundMeasurementInterval::ThirtyMinutes:
|
||||||
|
ms = 30 * 60 * 1000;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ms = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return pdMS_TO_TICKS(ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HeartRateTask::IsContinuousModeActivated() {
|
||||||
|
return settings.GetHeartRateBackgroundMeasurementInterval() ==
|
||||||
|
Pinetime::Controllers::Settings::HeartRateBackgroundMeasurementInterval::Continuous;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HeartRateTask::IsBackgroundMeasurementActivated() {
|
||||||
|
return settings.GetHeartRateBackgroundMeasurementInterval() !=
|
||||||
|
Pinetime::Controllers::Settings::HeartRateBackgroundMeasurementInterval::Off;
|
||||||
|
}
|
||||||
|
|
||||||
|
TickType_t HeartRateTask::GetTicksSinceLastMeasurementStarted() {
|
||||||
|
return xTaskGetTickCount() - measurementStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HeartRateTask::ShoudStopTryingToGetData() {
|
||||||
|
return GetTicksSinceLastMeasurementStarted() >= DURATION_UNTIL_BACKGROUND_MEASUREMENT_IS_STOPPED;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HeartRateTask::ShouldStartBackgroundMeasuring() {
|
||||||
|
return GetTicksSinceLastMeasurementStarted() >= GetHeartRateBackgroundMeasurementIntervalInTicks();
|
||||||
|
}
|
|
@ -3,6 +3,9 @@
|
||||||
#include <task.h>
|
#include <task.h>
|
||||||
#include <queue.h>
|
#include <queue.h>
|
||||||
#include <components/heartrate/Ppg.h>
|
#include <components/heartrate/Ppg.h>
|
||||||
|
#include "components/settings/Settings.h"
|
||||||
|
|
||||||
|
#define DURATION_UNTIL_BACKGROUND_MEASUREMENT_IS_STOPPED pdMS_TO_TICKS(30 * 1000)
|
||||||
|
|
||||||
namespace Pinetime {
|
namespace Pinetime {
|
||||||
namespace Drivers {
|
namespace Drivers {
|
||||||
|
@ -16,10 +19,24 @@ namespace Pinetime {
|
||||||
namespace Applications {
|
namespace Applications {
|
||||||
class HeartRateTask {
|
class HeartRateTask {
|
||||||
public:
|
public:
|
||||||
enum class Messages : uint8_t { GoToSleep, WakeUp, StartMeasurement, StopMeasurement };
|
enum class Messages : uint8_t {
|
||||||
enum class States { Idle, Running };
|
GoToSleep,
|
||||||
|
WakeUp,
|
||||||
|
StartMeasurement,
|
||||||
|
StopMeasurement
|
||||||
|
};
|
||||||
|
|
||||||
explicit HeartRateTask(Drivers::Hrs3300& heartRateSensor, Controllers::HeartRateController& controller);
|
enum class States {
|
||||||
|
ScreenOnAndStopped,
|
||||||
|
ScreenOnAndMeasuring,
|
||||||
|
ScreenOffAndStopped,
|
||||||
|
ScreenOffAndWaiting,
|
||||||
|
ScreenOffAndMeasuring
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit HeartRateTask(Drivers::Hrs3300& heartRateSensor,
|
||||||
|
Controllers::HeartRateController& controller,
|
||||||
|
Controllers::Settings& settings);
|
||||||
void Start();
|
void Start();
|
||||||
void Work();
|
void Work();
|
||||||
void PushMessage(Messages msg);
|
void PushMessage(Messages msg);
|
||||||
|
@ -29,13 +46,30 @@ namespace Pinetime {
|
||||||
void StartMeasurement();
|
void StartMeasurement();
|
||||||
void StopMeasurement();
|
void StopMeasurement();
|
||||||
|
|
||||||
|
void HandleGoToSleep();
|
||||||
|
void HandleWakeUp();
|
||||||
|
void HandleStartMeasurement(int* lastBpm);
|
||||||
|
void HandleStopMeasurement();
|
||||||
|
|
||||||
|
void HandleBackgroundWaiting();
|
||||||
|
void HandleSensorData(int* lastBpm);
|
||||||
|
|
||||||
|
TickType_t GetHeartRateBackgroundMeasurementIntervalInTicks();
|
||||||
|
bool IsContinuousModeActivated();
|
||||||
|
bool IsBackgroundMeasurementActivated();
|
||||||
|
|
||||||
|
TickType_t GetTicksSinceLastMeasurementStarted();
|
||||||
|
bool ShoudStopTryingToGetData();
|
||||||
|
bool ShouldStartBackgroundMeasuring();
|
||||||
|
|
||||||
TaskHandle_t taskHandle;
|
TaskHandle_t taskHandle;
|
||||||
QueueHandle_t messageQueue;
|
QueueHandle_t messageQueue;
|
||||||
States state = States::Running;
|
States state = States::ScreenOnAndStopped;
|
||||||
Drivers::Hrs3300& heartRateSensor;
|
Drivers::Hrs3300& heartRateSensor;
|
||||||
Controllers::HeartRateController& controller;
|
Controllers::HeartRateController& controller;
|
||||||
|
Controllers::Settings& settings;
|
||||||
Controllers::Ppg ppg;
|
Controllers::Ppg ppg;
|
||||||
bool measurementStarted = false;
|
TickType_t measurementStart = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 56b17bf9f74096774944bcac0829adcd887d391e
|
|
|
@ -93,18 +93,18 @@ TimerHandle_t debounceChargeTimer;
|
||||||
Pinetime::Controllers::Battery batteryController;
|
Pinetime::Controllers::Battery batteryController;
|
||||||
Pinetime::Controllers::Ble bleController;
|
Pinetime::Controllers::Ble bleController;
|
||||||
|
|
||||||
Pinetime::Controllers::HeartRateController heartRateController;
|
|
||||||
Pinetime::Applications::HeartRateTask heartRateApp(heartRateSensor, heartRateController);
|
|
||||||
|
|
||||||
Pinetime::Controllers::FS fs {spiNorFlash};
|
Pinetime::Controllers::FS fs {spiNorFlash};
|
||||||
Pinetime::Controllers::Settings settingsController {fs};
|
Pinetime::Controllers::Settings settingsController {fs};
|
||||||
Pinetime::Controllers::MotorController motorController {};
|
Pinetime::Controllers::MotorController motorController {};
|
||||||
|
|
||||||
|
Pinetime::Controllers::HeartRateController heartRateController;
|
||||||
|
Pinetime::Applications::HeartRateTask heartRateApp(heartRateSensor, heartRateController, settingsController);
|
||||||
|
|
||||||
Pinetime::Controllers::DateTime dateTimeController {settingsController};
|
Pinetime::Controllers::DateTime dateTimeController {settingsController};
|
||||||
Pinetime::Drivers::Watchdog watchdog;
|
Pinetime::Drivers::Watchdog watchdog;
|
||||||
Pinetime::Controllers::NotificationManager notificationManager;
|
Pinetime::Controllers::NotificationManager notificationManager;
|
||||||
Pinetime::Controllers::MotionController motionController;
|
Pinetime::Controllers::MotionController motionController;
|
||||||
Pinetime::Controllers::AlarmController alarmController {dateTimeController};
|
Pinetime::Controllers::AlarmController alarmController {dateTimeController, fs};
|
||||||
Pinetime::Controllers::TouchHandler touchHandler;
|
Pinetime::Controllers::TouchHandler touchHandler;
|
||||||
Pinetime::Controllers::ButtonHandler buttonHandler;
|
Pinetime::Controllers::ButtonHandler buttonHandler;
|
||||||
Pinetime::Controllers::BrightnessController brightnessController {};
|
Pinetime::Controllers::BrightnessController brightnessController {};
|
||||||
|
|
|
@ -189,36 +189,14 @@ void SystemTask::Work() {
|
||||||
if (xQueueReceive(systemTasksMsgQueue, &msg, 100) == pdTRUE) {
|
if (xQueueReceive(systemTasksMsgQueue, &msg, 100) == pdTRUE) {
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case Messages::EnableSleeping:
|
case Messages::EnableSleeping:
|
||||||
// Make sure that exiting an app doesn't enable sleeping,
|
wakeLocksHeld--;
|
||||||
// if the exiting was caused by a firmware update
|
|
||||||
if (!bleController.IsFirmwareUpdating()) {
|
|
||||||
doNotGoToSleep = false;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case Messages::DisableSleeping:
|
case Messages::DisableSleeping:
|
||||||
doNotGoToSleep = true;
|
GoToRunning();
|
||||||
|
wakeLocksHeld++;
|
||||||
break;
|
break;
|
||||||
case Messages::GoToRunning:
|
case Messages::GoToRunning:
|
||||||
// SPI doesn't go to sleep for always on mode
|
GoToRunning();
|
||||||
if (!settingsController.GetAlwaysOnDisplay()) {
|
|
||||||
spi.Wakeup();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Double Tap needs the touch screen to be in normal mode
|
|
||||||
if (!settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) {
|
|
||||||
touchPanel.Wakeup();
|
|
||||||
}
|
|
||||||
|
|
||||||
spiNorFlash.Wakeup();
|
|
||||||
|
|
||||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToRunning);
|
|
||||||
heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::WakeUp);
|
|
||||||
|
|
||||||
if (bleController.IsRadioEnabled() && !bleController.IsConnected()) {
|
|
||||||
nimbleController.RestartFastAdv();
|
|
||||||
}
|
|
||||||
|
|
||||||
state = SystemTaskState::Running;
|
|
||||||
break;
|
break;
|
||||||
case Messages::TouchWakeUp: {
|
case Messages::TouchWakeUp: {
|
||||||
if (touchHandler.ProcessTouchInfo(touchPanel.GetTouchInfo())) {
|
if (touchHandler.ProcessTouchInfo(touchPanel.GetTouchInfo())) {
|
||||||
|
@ -235,31 +213,23 @@ void SystemTask::Work() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Messages::GoToSleep:
|
case Messages::GoToSleep:
|
||||||
if (doNotGoToSleep) {
|
GoToSleep();
|
||||||
break;
|
|
||||||
}
|
|
||||||
state = SystemTaskState::GoingToSleep; // Already set in PushMessage()
|
|
||||||
NRF_LOG_INFO("[systemtask] Going to sleep");
|
|
||||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToSleep);
|
|
||||||
heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::GoToSleep);
|
|
||||||
break;
|
break;
|
||||||
case Messages::OnNewTime:
|
case Messages::OnNewTime:
|
||||||
if (alarmController.State() == Controllers::AlarmController::AlarmState::Set) {
|
if (alarmController.IsEnabled()) {
|
||||||
alarmController.ScheduleAlarm();
|
alarmController.ScheduleAlarm();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Messages::OnNewNotification:
|
case Messages::OnNewNotification:
|
||||||
if (settingsController.GetNotificationStatus() == Pinetime::Controllers::Settings::Notification::On) {
|
if (settingsController.GetNotificationStatus() == Pinetime::Controllers::Settings::Notification::On) {
|
||||||
if (state == SystemTaskState::Sleeping) {
|
if (IsSleeping()) {
|
||||||
GoToRunning();
|
GoToRunning();
|
||||||
}
|
}
|
||||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::NewNotification);
|
displayApp.PushMessage(Pinetime::Applications::Display::Messages::NewNotification);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Messages::SetOffAlarm:
|
case Messages::SetOffAlarm:
|
||||||
if (state == SystemTaskState::Sleeping) {
|
|
||||||
GoToRunning();
|
GoToRunning();
|
||||||
}
|
|
||||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::AlarmTriggered);
|
displayApp.PushMessage(Pinetime::Applications::Display::Messages::AlarmTriggered);
|
||||||
break;
|
break;
|
||||||
case Messages::BleConnected:
|
case Messages::BleConnected:
|
||||||
|
@ -268,29 +238,25 @@ void SystemTask::Work() {
|
||||||
bleDiscoveryTimer = 5;
|
bleDiscoveryTimer = 5;
|
||||||
break;
|
break;
|
||||||
case Messages::BleFirmwareUpdateStarted:
|
case Messages::BleFirmwareUpdateStarted:
|
||||||
doNotGoToSleep = true;
|
|
||||||
if (state == SystemTaskState::Sleeping) {
|
|
||||||
GoToRunning();
|
GoToRunning();
|
||||||
}
|
wakeLocksHeld++;
|
||||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::BleFirmwareUpdateStarted);
|
displayApp.PushMessage(Pinetime::Applications::Display::Messages::BleFirmwareUpdateStarted);
|
||||||
break;
|
break;
|
||||||
case Messages::BleFirmwareUpdateFinished:
|
case Messages::BleFirmwareUpdateFinished:
|
||||||
if (bleController.State() == Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated) {
|
if (bleController.State() == Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated) {
|
||||||
NVIC_SystemReset();
|
NVIC_SystemReset();
|
||||||
}
|
}
|
||||||
doNotGoToSleep = false;
|
wakeLocksHeld--;
|
||||||
break;
|
break;
|
||||||
case Messages::StartFileTransfer:
|
case Messages::StartFileTransfer:
|
||||||
NRF_LOG_INFO("[systemtask] FS Started");
|
NRF_LOG_INFO("[systemtask] FS Started");
|
||||||
doNotGoToSleep = true;
|
|
||||||
if (state == SystemTaskState::Sleeping) {
|
|
||||||
GoToRunning();
|
GoToRunning();
|
||||||
}
|
wakeLocksHeld++;
|
||||||
// TODO add intent of fs access icon or something
|
// TODO add intent of fs access icon or something
|
||||||
break;
|
break;
|
||||||
case Messages::StopFileTransfer:
|
case Messages::StopFileTransfer:
|
||||||
NRF_LOG_INFO("[systemtask] FS Stopped");
|
NRF_LOG_INFO("[systemtask] FS Stopped");
|
||||||
doNotGoToSleep = false;
|
wakeLocksHeld--;
|
||||||
// TODO add intent of fs access icon or something
|
// TODO add intent of fs access icon or something
|
||||||
break;
|
break;
|
||||||
case Messages::OnTouchEvent:
|
case Messages::OnTouchEvent:
|
||||||
|
@ -318,6 +284,13 @@ void SystemTask::Work() {
|
||||||
HandleButtonAction(action);
|
HandleButtonAction(action);
|
||||||
} break;
|
} break;
|
||||||
case Messages::OnDisplayTaskSleeping:
|
case Messages::OnDisplayTaskSleeping:
|
||||||
|
// The state was set to GoingToSleep when GoToSleep() was called
|
||||||
|
// If the state is no longer GoingToSleep, we have since transitioned back to Running
|
||||||
|
// In this case absorb the OnDisplayTaskSleeping
|
||||||
|
// as DisplayApp is about to receive GoToRunning
|
||||||
|
if (state != SystemTaskState::GoingToSleep) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (BootloaderVersion::IsValid()) {
|
if (BootloaderVersion::IsValid()) {
|
||||||
// First versions of the bootloader do not expose their version and cannot initialize the SPI NOR FLASH
|
// First versions of the bootloader do not expose their version and cannot initialize the SPI NOR FLASH
|
||||||
// if it's in sleep mode. Avoid bricked device by disabling sleep mode on these versions.
|
// if it's in sleep mode. Avoid bricked device by disabling sleep mode on these versions.
|
||||||
|
@ -344,39 +317,23 @@ void SystemTask::Work() {
|
||||||
case Messages::OnNewHour:
|
case Messages::OnNewHour:
|
||||||
using Pinetime::Controllers::AlarmController;
|
using Pinetime::Controllers::AlarmController;
|
||||||
if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep &&
|
if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep &&
|
||||||
settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::Hours &&
|
settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::Hours && !alarmController.IsAlerting()) {
|
||||||
alarmController.State() != AlarmController::AlarmState::Alerting) {
|
|
||||||
// if sleeping, we can't send a chime to displayApp yet (SPI flash switched off)
|
|
||||||
// request running first and repush the chime message
|
|
||||||
if (state == SystemTaskState::Sleeping) {
|
|
||||||
GoToRunning();
|
GoToRunning();
|
||||||
PushMessage(msg);
|
|
||||||
} else {
|
|
||||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::Chime);
|
displayApp.PushMessage(Pinetime::Applications::Display::Messages::Chime);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case Messages::OnNewHalfHour:
|
case Messages::OnNewHalfHour:
|
||||||
using Pinetime::Controllers::AlarmController;
|
using Pinetime::Controllers::AlarmController;
|
||||||
if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep &&
|
if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep &&
|
||||||
settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::HalfHours &&
|
settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::HalfHours && !alarmController.IsAlerting()) {
|
||||||
alarmController.State() != AlarmController::AlarmState::Alerting) {
|
|
||||||
// if sleeping, we can't send a chime to displayApp yet (SPI flash switched off)
|
|
||||||
// request running first and repush the chime message
|
|
||||||
if (state == SystemTaskState::Sleeping) {
|
|
||||||
GoToRunning();
|
GoToRunning();
|
||||||
PushMessage(msg);
|
|
||||||
} else {
|
|
||||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::Chime);
|
displayApp.PushMessage(Pinetime::Applications::Display::Messages::Chime);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case Messages::OnChargingEvent:
|
case Messages::OnChargingEvent:
|
||||||
batteryController.ReadPowerState();
|
batteryController.ReadPowerState();
|
||||||
displayApp.PushMessage(Applications::Display::Messages::OnChargingEvent);
|
|
||||||
if (state == SystemTaskState::Sleeping) {
|
|
||||||
GoToRunning();
|
GoToRunning();
|
||||||
}
|
displayApp.PushMessage(Applications::Display::Messages::OnChargingEvent);
|
||||||
break;
|
break;
|
||||||
case Messages::MeasureBatteryTimerExpired:
|
case Messages::MeasureBatteryTimerExpired:
|
||||||
batteryController.MeasureVoltage();
|
batteryController.MeasureVoltage();
|
||||||
|
@ -385,9 +342,7 @@ void SystemTask::Work() {
|
||||||
nimbleController.NotifyBatteryLevel(batteryController.PercentRemaining());
|
nimbleController.NotifyBatteryLevel(batteryController.PercentRemaining());
|
||||||
break;
|
break;
|
||||||
case Messages::OnPairing:
|
case Messages::OnPairing:
|
||||||
if (state == SystemTaskState::Sleeping) {
|
|
||||||
GoToRunning();
|
GoToRunning();
|
||||||
}
|
|
||||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::ShowPairingKey);
|
displayApp.PushMessage(Pinetime::Applications::Display::Messages::ShowPairingKey);
|
||||||
break;
|
break;
|
||||||
case Messages::BleRadioEnableToggle:
|
case Messages::BleRadioEnableToggle:
|
||||||
|
@ -422,12 +377,48 @@ void SystemTask::Work() {
|
||||||
#pragma clang diagnostic pop
|
#pragma clang diagnostic pop
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemTask::UpdateMotion() {
|
void SystemTask::GoToRunning() {
|
||||||
if (state == SystemTaskState::GoingToSleep || state == SystemTaskState::WakingUp) {
|
if (state == SystemTaskState::Running) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// SPI doesn't go to sleep for always on mode
|
||||||
|
if (!settingsController.GetAlwaysOnDisplay()) {
|
||||||
|
spi.Wakeup();
|
||||||
|
}
|
||||||
|
|
||||||
if (state == SystemTaskState::Sleeping && !(settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist) ||
|
// Double Tap needs the touch screen to be in normal mode
|
||||||
|
if (!settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) {
|
||||||
|
touchPanel.Wakeup();
|
||||||
|
}
|
||||||
|
|
||||||
|
spiNorFlash.Wakeup();
|
||||||
|
|
||||||
|
displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToRunning);
|
||||||
|
heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::WakeUp);
|
||||||
|
|
||||||
|
if (bleController.IsRadioEnabled() && !bleController.IsConnected()) {
|
||||||
|
nimbleController.RestartFastAdv();
|
||||||
|
}
|
||||||
|
|
||||||
|
state = SystemTaskState::Running;
|
||||||
|
};
|
||||||
|
|
||||||
|
void SystemTask::GoToSleep() {
|
||||||
|
if (IsSleeping()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (IsSleepDisabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NRF_LOG_INFO("[systemtask] Going to sleep");
|
||||||
|
displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToSleep);
|
||||||
|
heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::GoToSleep);
|
||||||
|
|
||||||
|
state = SystemTaskState::GoingToSleep;
|
||||||
|
};
|
||||||
|
|
||||||
|
void SystemTask::UpdateMotion() {
|
||||||
|
if (IsSleeping() && !(settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist) ||
|
||||||
settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::Shake) ||
|
settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::Shake) ||
|
||||||
motionController.GetService()->IsMotionNotificationSubscribed())) {
|
motionController.GetService()->IsMotionNotificationSubscribed())) {
|
||||||
return;
|
return;
|
||||||
|
@ -452,7 +443,7 @@ void SystemTask::UpdateMotion() {
|
||||||
}
|
}
|
||||||
if (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::LowerWrist) && state == SystemTaskState::Running &&
|
if (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::LowerWrist) && state == SystemTaskState::Running &&
|
||||||
motionController.ShouldLowerSleep()) {
|
motionController.ShouldLowerSleep()) {
|
||||||
PushMessage(Messages::GoToSleep);
|
GoToSleep();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,7 +459,7 @@ void SystemTask::HandleButtonAction(Controllers::ButtonActions action) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case Actions::Click:
|
case Actions::Click:
|
||||||
// If the first action after fast wakeup is a click, it should be ignored.
|
// If the first action after fast wakeup is a click, it should be ignored.
|
||||||
if (!fastWakeUpDone && state != SystemTaskState::GoingToSleep) {
|
if (!fastWakeUpDone) {
|
||||||
displayApp.PushMessage(Applications::Display::Messages::ButtonPushed);
|
displayApp.PushMessage(Applications::Display::Messages::ButtonPushed);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -488,17 +479,10 @@ void SystemTask::HandleButtonAction(Controllers::ButtonActions action) {
|
||||||
fastWakeUpDone = false;
|
fastWakeUpDone = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemTask::GoToRunning() {
|
|
||||||
if (state == SystemTaskState::Sleeping) {
|
|
||||||
state = SystemTaskState::WakingUp;
|
|
||||||
PushMessage(Messages::GoToRunning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SystemTask::OnTouchEvent() {
|
void SystemTask::OnTouchEvent() {
|
||||||
if (state == SystemTaskState::Running) {
|
if (state == SystemTaskState::Running) {
|
||||||
PushMessage(Messages::OnTouchEvent);
|
PushMessage(Messages::OnTouchEvent);
|
||||||
} else if (state == SystemTaskState::Sleeping) {
|
} else {
|
||||||
if (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::SingleTap) or
|
if (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::SingleTap) or
|
||||||
settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) {
|
settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) {
|
||||||
PushMessage(Messages::TouchWakeUp);
|
PushMessage(Messages::TouchWakeUp);
|
||||||
|
@ -507,10 +491,6 @@ void SystemTask::OnTouchEvent() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SystemTask::PushMessage(System::Messages msg) {
|
void SystemTask::PushMessage(System::Messages msg) {
|
||||||
if (msg == Messages::GoToSleep && !doNotGoToSleep) {
|
|
||||||
state = SystemTaskState::GoingToSleep;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_isr()) {
|
if (in_isr()) {
|
||||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||||
xQueueSendFromISR(systemTasksMsgQueue, &msg, &xHigherPriorityTaskWoken);
|
xQueueSendFromISR(systemTasksMsgQueue, &msg, &xHigherPriorityTaskWoken);
|
||||||
|
|
|
@ -52,7 +52,7 @@ namespace Pinetime {
|
||||||
namespace System {
|
namespace System {
|
||||||
class SystemTask {
|
class SystemTask {
|
||||||
public:
|
public:
|
||||||
enum class SystemTaskState { Sleeping, Running, GoingToSleep, WakingUp };
|
enum class SystemTaskState { Sleeping, Running, GoingToSleep };
|
||||||
SystemTask(Drivers::SpiMaster& spi,
|
SystemTask(Drivers::SpiMaster& spi,
|
||||||
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
|
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
|
||||||
Drivers::TwiMaster& twiMaster,
|
Drivers::TwiMaster& twiMaster,
|
||||||
|
@ -79,11 +79,8 @@ namespace Pinetime {
|
||||||
|
|
||||||
void OnTouchEvent();
|
void OnTouchEvent();
|
||||||
|
|
||||||
void OnIdle();
|
|
||||||
void OnDim();
|
|
||||||
|
|
||||||
bool IsSleepDisabled() {
|
bool IsSleepDisabled() {
|
||||||
return doNotGoToSleep;
|
return wakeLocksHeld > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pinetime::Controllers::NimbleController& nimble() {
|
Pinetime::Controllers::NimbleController& nimble() {
|
||||||
|
@ -91,7 +88,7 @@ namespace Pinetime {
|
||||||
};
|
};
|
||||||
|
|
||||||
bool IsSleeping() const {
|
bool IsSleeping() const {
|
||||||
return state == SystemTaskState::Sleeping || state == SystemTaskState::WakingUp;
|
return state != SystemTaskState::Running;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -127,13 +124,14 @@ namespace Pinetime {
|
||||||
bool isBleDiscoveryTimerRunning = false;
|
bool isBleDiscoveryTimerRunning = false;
|
||||||
uint8_t bleDiscoveryTimer = 0;
|
uint8_t bleDiscoveryTimer = 0;
|
||||||
TimerHandle_t measureBatteryTimer;
|
TimerHandle_t measureBatteryTimer;
|
||||||
bool doNotGoToSleep = false;
|
uint8_t wakeLocksHeld = 0;
|
||||||
SystemTaskState state = SystemTaskState::Running;
|
SystemTaskState state = SystemTaskState::Running;
|
||||||
|
|
||||||
void HandleButtonAction(Controllers::ButtonActions action);
|
void HandleButtonAction(Controllers::ButtonActions action);
|
||||||
bool fastWakeUpDone = false;
|
bool fastWakeUpDone = false;
|
||||||
|
|
||||||
void GoToRunning();
|
void GoToRunning();
|
||||||
|
void GoToSleep();
|
||||||
void UpdateMotion();
|
void UpdateMotion();
|
||||||
bool stepCounterMustBeReset = false;
|
bool stepCounterMustBeReset = false;
|
||||||
static constexpr TickType_t batteryMeasurementPeriod = pdMS_TO_TICKS(10 * 60 * 1000);
|
static constexpr TickType_t batteryMeasurementPeriod = pdMS_TO_TICKS(10 * 60 * 1000);
|
||||||
|
|
27
src/systemtask/WakeLock.cpp
Normal file
27
src/systemtask/WakeLock.cpp
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#include "systemtask/WakeLock.h"
|
||||||
|
|
||||||
|
using namespace Pinetime::System;
|
||||||
|
|
||||||
|
WakeLock::WakeLock(SystemTask& systemTask) : systemTask {systemTask} {
|
||||||
|
lockHeld = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WakeLock::~WakeLock() {
|
||||||
|
Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WakeLock::Lock() {
|
||||||
|
if (lockHeld) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
systemTask.PushMessage(Messages::DisableSleeping);
|
||||||
|
lockHeld = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WakeLock::Release() {
|
||||||
|
if (!lockHeld) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
systemTask.PushMessage(Messages::EnableSleeping);
|
||||||
|
lockHeld = false;
|
||||||
|
}
|
19
src/systemtask/WakeLock.h
Normal file
19
src/systemtask/WakeLock.h
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "systemtask/SystemTask.h"
|
||||||
|
|
||||||
|
namespace Pinetime {
|
||||||
|
namespace System {
|
||||||
|
class WakeLock {
|
||||||
|
public:
|
||||||
|
WakeLock(SystemTask& systemTask);
|
||||||
|
~WakeLock();
|
||||||
|
void Lock();
|
||||||
|
void Release();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool lockHeld;
|
||||||
|
SystemTask& systemTask;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue