Compare commits
39 commits
2625ed39e5
...
2105a7b63d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2105a7b63d | ||
|
|
79ee886904 | ||
|
|
b1d70ae2ed | ||
|
|
a77a3dcb8b | ||
|
|
8aefa3b9a6 | ||
|
|
6c7eb6630e | ||
|
|
4dd0d60eeb | ||
|
|
a2ced5659d | ||
|
|
5ea9c5537e | ||
|
|
f7c87a700d | ||
|
|
e247bd7019 | ||
|
|
29ad09f4ef | ||
|
|
afeded0126 | ||
|
|
57b6db8b2a | ||
|
|
0076962588 | ||
|
|
e6ee548536 | ||
|
|
1808634f0e | ||
|
|
cfaad261dc | ||
|
|
f1651c8000 | ||
|
|
8a2ee437f5 | ||
|
|
06b721a71f | ||
|
|
771008495e | ||
|
|
f032847ae1 | ||
|
|
97ba39988b | ||
|
|
879bdccd92 | ||
|
|
8598142c27 | ||
|
|
a2356f2f4a | ||
|
|
3db4e012ce | ||
|
|
a0cd439efc | ||
|
|
997e4cee8c | ||
|
|
ad3bf49c7b | ||
|
|
7ca0418c82 | ||
|
|
c3d05901a0 | ||
|
|
b3756e45fa | ||
|
|
a266202831 | ||
|
|
c8236afbef | ||
|
|
5040733a97 | ||
|
|
fd019c7aad | ||
|
|
975bfc5420 |
64 changed files with 685 additions and 471 deletions
15
.github/workflows/main.yml
vendored
15
.github/workflows/main.yml
vendored
|
|
@ -46,25 +46,30 @@ jobs:
|
||||||
# Unzip the package because Upload Artifact will zip up the files
|
# Unzip the package because Upload Artifact will zip up the files
|
||||||
- name: Unzip DFU package
|
- name: Unzip DFU package
|
||||||
run: unzip ./build/output/pinetime-mcuboot-app-dfu-*.zip -d ./build/output/pinetime-mcuboot-app-dfu
|
run: unzip ./build/output/pinetime-mcuboot-app-dfu-*.zip -d ./build/output/pinetime-mcuboot-app-dfu
|
||||||
|
- name: Set ref_name, but replace slashes with dashes.
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
ref_name: ${{ github.head_ref || github.ref_name }}
|
||||||
|
run: echo "REF_NAME=${ref_name//\//-}" >> $GITHUB_ENV
|
||||||
- name: Upload DFU artifacts
|
- name: Upload DFU artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: InfiniTime DFU ${{ github.head_ref }}
|
name: InfiniTime DFU ${{ env.REF_NAME }}
|
||||||
path: ./build/output/pinetime-mcuboot-app-dfu/*
|
path: ./build/output/pinetime-mcuboot-app-dfu/*
|
||||||
- name: Upload MCUBoot image artifacts
|
- name: Upload MCUBoot image artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: InfiniTime MCUBoot image ${{ github.head_ref }}
|
name: InfiniTime MCUBoot image ${{ env.REF_NAME }}
|
||||||
path: ./build/output/pinetime-mcuboot-app-image-*.bin
|
path: ./build/output/pinetime-mcuboot-app-image-*.bin
|
||||||
- name: Upload standalone ELF artifacts
|
- name: Upload standalone ELF artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: InfiniTime image ${{ github.head_ref }}
|
name: InfiniTime image ${{ env.REF_NAME }}
|
||||||
path: ./build/output/src/pinetime-app-*.out
|
path: ./build/output/src/pinetime-app-*.out
|
||||||
- name: Upload resources artifacts
|
- name: Upload resources artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: InfiniTime resources ${{ github.head_ref }}
|
name: InfiniTime resources ${{ env.REF_NAME }}
|
||||||
path: ./build/output/infinitime-resources-*.zip
|
path: ./build/output/infinitime-resources-*.zip
|
||||||
|
|
||||||
build-simulator:
|
build-simulator:
|
||||||
|
|
@ -105,7 +110,7 @@ jobs:
|
||||||
- name: Upload simulator executable
|
- name: Upload simulator executable
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: infinisim-${{ github.head_ref }}
|
name: infinisim-${{ env.REF_NAME }}
|
||||||
path: build_lv_sim/infinisim
|
path: build_lv_sim/infinisim
|
||||||
|
|
||||||
get-base-ref-size:
|
get-base-ref-size:
|
||||||
|
|
|
||||||
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.0 LANGUAGES C CXX ASM)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 99)
|
set(CMAKE_C_STANDARD 99)
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
|
||||||
31
README.md
31
README.md
|
|
@ -1,8 +1,24 @@
|
||||||
# [InfiniTime](https://github.com/InfiniTimeOrg/InfiniTime)
|
<div align="center">
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Fast open-source firmware for the [PineTime smartwatch](https://pine64.org/devices/pinetime/) with many features, written in modern C++.
|
<br>
|
||||||
|
|
||||||
|
[](https://github.com/InfiniTimeOrg/InfiniTime/releases)
|
||||||
|
[](https://github.com/InfiniTimeOrg/InfiniLink/blob/main/LICENSE)
|
||||||
|
[](https://github.com/InfiniTimeOrg/InfiniTime/issues)
|
||||||
|
[](https://github.com/InfiniTimeOrg/InfiniTime/pulls)
|
||||||
|
[](https://github.com/InfiniTimeOrg/InfiniTime)
|
||||||
|
[](https://github.com/InfiniTimeOrg/InfiniTime/stargazers)
|
||||||
|
[](https://github.com/InfiniTimeOrg/InfiniTime/network/members)
|
||||||
|
|
||||||
|
# InfiniTime
|
||||||
|
|
||||||
|
*Fast open-source firmware for the [PineTime smartwatch](https://pine64.org/devices/pinetime/) with many features, written in modern C++.*
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
## New to InfiniTime?
|
## New to InfiniTime?
|
||||||
|
|
||||||
|
|
@ -17,13 +33,18 @@ Fast open-source firmware for the [PineTime smartwatch](https://pine64.org/devic
|
||||||
### Companion apps
|
### Companion apps
|
||||||
|
|
||||||
- [Gadgetbridge](https://gadgetbridge.org/) (Android)
|
- [Gadgetbridge](https://gadgetbridge.org/) (Android)
|
||||||
- [AmazFish](https://openrepos.net/content/piggz/amazfish/) (SailfishOS)
|
- [Amazfish](https://github.com/piggz/harbour-amazfish/) ([SailfishOS](https://sailfishos-chum.github.io/apps/harbour-amazfish/), [Ubuntu Touch](https://open-store.io/app/uk.co.piggz.amazfish), [Flatpak](https://flathub.org/apps/uk.co.piggz.amazfish))
|
||||||
- [Siglo](https://github.com/alexr4535/siglo) (Linux)
|
- [Siglo](https://github.com/alexr4535/siglo) (Linux)
|
||||||
- [InfiniLink](https://github.com/InfiniTimeOrg/InfiniLink) (iOS)
|
- [InfiniLink](https://github.com/InfiniTimeOrg/InfiniLink) (iOS)
|
||||||
- [ITD](https://gitea.elara.ws/Elara6331/itd) (Linux)
|
- [ITD](https://gitea.elara.ws/Elara6331/itd) (Linux)
|
||||||
- [WatchMate](https://github.com/azymohliad/watchmate) (Linux)
|
- [WatchMate](https://github.com/azymohliad/watchmate) (Linux)
|
||||||
|
- [InfiniTimeExplorer](https://infinitimeexplorer.netlify.app) (Web)
|
||||||
|
|
||||||
***Note**: We removed mentions to NRFConnect as this app is closed source and recent versions do not work anymore with InfiniTime (the last version known to work is 4.24.3). If you used NRFConnect in the past, we recommend you switch to [Gadgetbridge](https://gadgetbridge.org/).*
|
<br>
|
||||||
|
|
||||||
|
> *InfiniTimeExplorer is only compatible with web browsers that support Web BLE. Current fully supported browsers include Chrome and Microsoft Edge.*
|
||||||
|
>
|
||||||
|
> *We removed mentions to NRFConnect as this app is closed source and recent versions do not work anymore with InfiniTime (the last version known to work is 4.24.3). If you used NRFConnect in the past, we recommend you switch to [Gadgetbridge](https://gadgetbridge.org/).*
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
|
|
||||||
BIN
doc/logo/watchface_collage.png
Normal file
BIN
doc/logo/watchface_collage.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 846 KiB |
|
|
@ -65,5 +65,8 @@ RUN bash -c "source /opt/build.sh; GetMcuBoot;"
|
||||||
# Add the infinitime user for connecting devcontainer
|
# Add the infinitime user for connecting devcontainer
|
||||||
RUN adduser infinitime
|
RUN adduser infinitime
|
||||||
|
|
||||||
|
# Configure Git to accept the /sources directory as safe
|
||||||
|
RUN git config --global --add safe.directory /sources
|
||||||
|
|
||||||
ENV SOURCES_DIR /sources
|
ENV SOURCES_DIR /sources
|
||||||
CMD ["/opt/build.sh"]
|
CMD ["/opt/build.sh"]
|
||||||
|
|
|
||||||
|
|
@ -478,6 +478,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 +543,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 +662,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
|
||||||
|
|
@ -860,7 +863,6 @@ target_compile_options(nrf-sdk PRIVATE
|
||||||
$<$<CONFIG:RELEASE>: ${RELEASE_FLAGS}>
|
$<$<CONFIG:RELEASE>: ${RELEASE_FLAGS}>
|
||||||
$<$<COMPILE_LANGUAGE:CXX>: ${CXX_FLAGS}>
|
$<$<COMPILE_LANGUAGE:CXX>: ${CXX_FLAGS}>
|
||||||
$<$<COMPILE_LANGUAGE:ASM>: ${ASM_FLAGS}>
|
$<$<COMPILE_LANGUAGE:ASM>: ${ASM_FLAGS}>
|
||||||
-O3
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# NimBLE
|
# NimBLE
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,9 @@ namespace {
|
||||||
std::memcpy(cityName.data(), &dataBuffer[16], 32);
|
std::memcpy(cityName.data(), &dataBuffer[16], 32);
|
||||||
cityName[32] = '\0';
|
cityName[32] = '\0';
|
||||||
return SimpleWeatherService::CurrentWeather(ToUInt64(&dataBuffer[2]),
|
return SimpleWeatherService::CurrentWeather(ToUInt64(&dataBuffer[2]),
|
||||||
ToInt16(&dataBuffer[10]),
|
SimpleWeatherService::Temperature(ToInt16(&dataBuffer[10])),
|
||||||
ToInt16(&dataBuffer[12]),
|
SimpleWeatherService::Temperature(ToInt16(&dataBuffer[12])),
|
||||||
ToInt16(&dataBuffer[14]),
|
SimpleWeatherService::Temperature(ToInt16(&dataBuffer[14])),
|
||||||
SimpleWeatherService::Icons {dataBuffer[16 + 32]},
|
SimpleWeatherService::Icons {dataBuffer[16 + 32]},
|
||||||
std::move(cityName));
|
std::move(cityName));
|
||||||
}
|
}
|
||||||
|
|
@ -52,12 +52,12 @@ namespace {
|
||||||
SimpleWeatherService::Forecast CreateForecast(const uint8_t* dataBuffer) {
|
SimpleWeatherService::Forecast CreateForecast(const uint8_t* dataBuffer) {
|
||||||
auto timestamp = static_cast<uint64_t>(ToUInt64(&dataBuffer[2]));
|
auto timestamp = static_cast<uint64_t>(ToUInt64(&dataBuffer[2]));
|
||||||
|
|
||||||
std::array<SimpleWeatherService::Forecast::Day, SimpleWeatherService::MaxNbForecastDays> days;
|
std::array<std::optional<SimpleWeatherService::Forecast::Day>, SimpleWeatherService::MaxNbForecastDays> days;
|
||||||
const uint8_t nbDaysInBuffer = dataBuffer[10];
|
const uint8_t nbDaysInBuffer = dataBuffer[10];
|
||||||
const uint8_t nbDays = std::min(SimpleWeatherService::MaxNbForecastDays, nbDaysInBuffer);
|
const uint8_t nbDays = std::min(SimpleWeatherService::MaxNbForecastDays, nbDaysInBuffer);
|
||||||
for (int i = 0; i < nbDays; i++) {
|
for (int i = 0; i < nbDays; i++) {
|
||||||
days[i] = SimpleWeatherService::Forecast::Day {ToInt16(&dataBuffer[11 + (i * 5)]),
|
days[i] = SimpleWeatherService::Forecast::Day {SimpleWeatherService::Temperature(ToInt16(&dataBuffer[11 + (i * 5)])),
|
||||||
ToInt16(&dataBuffer[13 + (i * 5)]),
|
SimpleWeatherService::Temperature(ToInt16(&dataBuffer[13 + (i * 5)])),
|
||||||
SimpleWeatherService::Icons {dataBuffer[15 + (i * 5)]}};
|
SimpleWeatherService::Icons {dataBuffer[15 + (i * 5)]}};
|
||||||
}
|
}
|
||||||
return SimpleWeatherService::Forecast {timestamp, nbDays, days};
|
return SimpleWeatherService::Forecast {timestamp, nbDays, days};
|
||||||
|
|
@ -98,9 +98,9 @@ int SimpleWeatherService::OnCommand(struct ble_gatt_access_ctxt* ctxt) {
|
||||||
currentWeather = CreateCurrentWeather(dataBuffer);
|
currentWeather = CreateCurrentWeather(dataBuffer);
|
||||||
NRF_LOG_INFO("Current weather :\n\tTimestamp : %d\n\tTemperature:%d\n\tMin:%d\n\tMax:%d\n\tIcon:%d\n\tLocation:%s",
|
NRF_LOG_INFO("Current weather :\n\tTimestamp : %d\n\tTemperature:%d\n\tMin:%d\n\tMax:%d\n\tIcon:%d\n\tLocation:%s",
|
||||||
currentWeather->timestamp,
|
currentWeather->timestamp,
|
||||||
currentWeather->temperature,
|
currentWeather->temperature.PreciseCelsius(),
|
||||||
currentWeather->minTemperature,
|
currentWeather->minTemperature.PreciseCelsius(),
|
||||||
currentWeather->maxTemperature,
|
currentWeather->maxTemperature.PreciseCelsius(),
|
||||||
currentWeather->iconId,
|
currentWeather->iconId,
|
||||||
currentWeather->location.data());
|
currentWeather->location.data());
|
||||||
}
|
}
|
||||||
|
|
@ -112,9 +112,9 @@ int SimpleWeatherService::OnCommand(struct ble_gatt_access_ctxt* ctxt) {
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
NRF_LOG_INFO("\t[%d] Min: %d - Max : %d - Icon : %d",
|
NRF_LOG_INFO("\t[%d] Min: %d - Max : %d - Icon : %d",
|
||||||
i,
|
i,
|
||||||
forecast->days[i].minTemperature,
|
forecast->days[i]->minTemperature.PreciseCelsius(),
|
||||||
forecast->days[i].maxTemperature,
|
forecast->days[i]->maxTemperature.PreciseCelsius(),
|
||||||
forecast->days[i].iconId);
|
forecast->days[i]->iconId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#define min // workaround: nimble's min/max macros conflict with libstdc++
|
#define min // workaround: nimble's min/max macros conflict with libstdc++
|
||||||
|
|
@ -61,13 +61,42 @@ namespace Pinetime {
|
||||||
Unknown = 255
|
Unknown = 255
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Temperature {
|
||||||
|
public:
|
||||||
|
explicit Temperature(int16_t raw) : raw {raw} {
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int16_t PreciseCelsius() const {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int16_t PreciseFahrenheit() const {
|
||||||
|
return raw * 9 / 5 + 3200;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int16_t Celsius() const {
|
||||||
|
return (PreciseCelsius() + 50) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int16_t Fahrenheit() const {
|
||||||
|
return (PreciseFahrenheit() + 50) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Temperature& other) const {
|
||||||
|
return raw == other.raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int16_t raw;
|
||||||
|
};
|
||||||
|
|
||||||
using Location = std::array<char, 33>; // 32 char + \0 (end of string)
|
using Location = std::array<char, 33>; // 32 char + \0 (end of string)
|
||||||
|
|
||||||
struct CurrentWeather {
|
struct CurrentWeather {
|
||||||
CurrentWeather(uint64_t timestamp,
|
CurrentWeather(uint64_t timestamp,
|
||||||
int16_t temperature,
|
Temperature temperature,
|
||||||
int16_t minTemperature,
|
Temperature minTemperature,
|
||||||
int16_t maxTemperature,
|
Temperature maxTemperature,
|
||||||
Icons iconId,
|
Icons iconId,
|
||||||
Location&& location)
|
Location&& location)
|
||||||
: timestamp {timestamp},
|
: timestamp {timestamp},
|
||||||
|
|
@ -79,9 +108,9 @@ namespace Pinetime {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t timestamp;
|
uint64_t timestamp;
|
||||||
int16_t temperature;
|
Temperature temperature;
|
||||||
int16_t minTemperature;
|
Temperature minTemperature;
|
||||||
int16_t maxTemperature;
|
Temperature maxTemperature;
|
||||||
Icons iconId;
|
Icons iconId;
|
||||||
Location location;
|
Location location;
|
||||||
|
|
||||||
|
|
@ -93,14 +122,14 @@ namespace Pinetime {
|
||||||
uint8_t nbDays;
|
uint8_t nbDays;
|
||||||
|
|
||||||
struct Day {
|
struct Day {
|
||||||
int16_t minTemperature;
|
Temperature minTemperature;
|
||||||
int16_t maxTemperature;
|
Temperature maxTemperature;
|
||||||
Icons iconId;
|
Icons iconId;
|
||||||
|
|
||||||
bool operator==(const Day& other) const;
|
bool operator==(const Day& other) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::array<Day, MaxNbForecastDays> days;
|
std::array<std::optional<Day>, MaxNbForecastDays> days;
|
||||||
|
|
||||||
bool operator==(const Forecast& other) const;
|
bool operator==(const Forecast& other) const;
|
||||||
};
|
};
|
||||||
|
|
@ -108,10 +137,6 @@ namespace Pinetime {
|
||||||
std::optional<CurrentWeather> Current() const;
|
std::optional<CurrentWeather> Current() const;
|
||||||
std::optional<Forecast> GetForecast() const;
|
std::optional<Forecast> GetForecast() const;
|
||||||
|
|
||||||
static int16_t CelsiusToFahrenheit(int16_t celsius) {
|
|
||||||
return celsius * 9 / 5 + 3200;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// 00050000-78fc-48fe-8e23-433b3a1942d0
|
// 00050000-78fc-48fe-8e23-433b3a1942d0
|
||||||
static constexpr ble_uuid128_t BaseUuid() {
|
static constexpr ble_uuid128_t BaseUuid() {
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,29 @@
|
||||||
using namespace Pinetime::Controllers;
|
using namespace Pinetime::Controllers;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
char const* DaysStringShort[] = {"--", "MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
|
constexpr const char* const DaysStringShort[] = {"--", "MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};
|
||||||
char const* DaysStringShortLow[] = {"--", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
|
constexpr const char* const DaysStringShortLow[] = {"--", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
|
||||||
char const* MonthsString[] = {"--", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
|
constexpr const char* const MonthsString[] = {"--", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
|
||||||
char const* MonthsStringLow[] = {"--", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
constexpr const char* const MonthsStringLow[] =
|
||||||
|
{"--", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||||||
|
|
||||||
|
constexpr int compileTimeAtoi(const char* str) {
|
||||||
|
int result = 0;
|
||||||
|
while (*str >= '0' && *str <= '9') {
|
||||||
|
result = result * 10 + *str - '0';
|
||||||
|
str++;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DateTime::DateTime(Controllers::Settings& settingsController) : settingsController {settingsController} {
|
DateTime::DateTime(Controllers::Settings& settingsController) : settingsController {settingsController} {
|
||||||
mutex = xSemaphoreCreateMutex();
|
mutex = xSemaphoreCreateMutex();
|
||||||
ASSERT(mutex != nullptr);
|
ASSERT(mutex != nullptr);
|
||||||
xSemaphoreGive(mutex);
|
xSemaphoreGive(mutex);
|
||||||
|
|
||||||
|
// __DATE__ is a string of the format "MMM DD YYYY", so an offset of 7 gives the start of the year
|
||||||
|
SetTime(compileTimeAtoi(&__DATE__[7]), 1, 1, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DateTime::SetCurrentTime(std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> t) {
|
void DateTime::SetCurrentTime(std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> t) {
|
||||||
|
|
@ -46,7 +59,9 @@ void DateTime::SetTime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour,
|
||||||
UpdateTime(previousSystickCounter, true);
|
UpdateTime(previousSystickCounter, true);
|
||||||
xSemaphoreGive(mutex);
|
xSemaphoreGive(mutex);
|
||||||
|
|
||||||
|
if (systemTask != nullptr) {
|
||||||
systemTask->PushMessage(System::Messages::OnNewTime);
|
systemTask->PushMessage(System::Messages::OnNewTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DateTime::SetTimeZone(int8_t timezone, int8_t dst) {
|
void DateTime::SetTimeZone(int8_t timezone, int8_t dst) {
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ namespace Pinetime {
|
||||||
*
|
*
|
||||||
* used to update difference between utc and local time (see UtcOffset())
|
* used to update difference between utc and local time (see UtcOffset())
|
||||||
*
|
*
|
||||||
* parameters are in quarters of an our. Following the BLE CTS specification,
|
* parameters are in quarters of an hour. Following the BLE CTS specification,
|
||||||
* timezone is expected to be constant over DST which will be reported in
|
* timezone is expected to be constant over DST which will be reported in
|
||||||
* dst field.
|
* dst field.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -157,12 +157,20 @@ void DisplayApp::InitHw() {
|
||||||
}
|
}
|
||||||
|
|
||||||
TickType_t DisplayApp::CalculateSleepTime() {
|
TickType_t DisplayApp::CalculateSleepTime() {
|
||||||
|
// Calculates how many system ticks DisplayApp should sleep before rendering the next AOD frame
|
||||||
|
// Next frame time is frame count * refresh period (ms) * tick rate
|
||||||
|
|
||||||
|
auto RoundedDiv = [](uint32_t a, uint32_t b) {
|
||||||
|
return ((a + (b / 2)) / b);
|
||||||
|
};
|
||||||
|
// RoundedDiv overflows when numerator + (denominator floordiv 2) > uint32 max
|
||||||
|
// in this case around 9 hours (=overflow frame count / always on refresh period)
|
||||||
|
constexpr TickType_t overflowFrameCount = (UINT32_MAX - (1000 / 16)) / ((configTICK_RATE_HZ / 8) * alwaysOnRefreshPeriod);
|
||||||
|
|
||||||
TickType_t ticksElapsed = xTaskGetTickCount() - alwaysOnStartTime;
|
TickType_t ticksElapsed = xTaskGetTickCount() - alwaysOnStartTime;
|
||||||
// Divide both the numerator and denominator by 8 to increase the number of ticks (frames) before the overflow tick is reached
|
// Divide both the numerator and denominator by 8 (=GCD(1000,1024))
|
||||||
TickType_t elapsedTarget = ROUNDED_DIV((configTICK_RATE_HZ / 8) * alwaysOnTickCount * alwaysOnRefreshPeriod, 1000 / 8);
|
// to increase the number of ticks (frames) before the overflow tick is reached
|
||||||
// ROUNDED_DIV overflows when numerator + (denominator floordiv 2) > uint32 max
|
TickType_t targetRenderTick = RoundedDiv((configTICK_RATE_HZ / 8) * alwaysOnFrameCount * alwaysOnRefreshPeriod, 1000 / 8);
|
||||||
// in this case around 9 hours
|
|
||||||
constexpr TickType_t overflowTick = (UINT32_MAX - (1000 / 16)) / ((configTICK_RATE_HZ / 8) * alwaysOnRefreshPeriod);
|
|
||||||
|
|
||||||
// Assumptions
|
// Assumptions
|
||||||
|
|
||||||
|
|
@ -170,17 +178,17 @@ TickType_t DisplayApp::CalculateSleepTime() {
|
||||||
// Needed for division trick above
|
// Needed for division trick above
|
||||||
static_assert(configTICK_RATE_HZ % 8 == 0);
|
static_assert(configTICK_RATE_HZ % 8 == 0);
|
||||||
|
|
||||||
// Local tick count must always wraparound before the system tick count does
|
// Frame count must always wraparound more often than the system tick count does
|
||||||
// As a static assert we can use 64 bit ints and therefore dodge overflows
|
|
||||||
// Always on overflow time (ms) < system tick overflow time (ms)
|
// Always on overflow time (ms) < system tick overflow time (ms)
|
||||||
static_assert((uint64_t) overflowTick * (uint64_t) alwaysOnRefreshPeriod < (uint64_t) UINT32_MAX * 1000ULL / configTICK_RATE_HZ);
|
// Using 64bit ints here to avoid overflow
|
||||||
|
static_assert((uint64_t) overflowFrameCount * (uint64_t) alwaysOnRefreshPeriod < (uint64_t) UINT32_MAX * 1000ULL / configTICK_RATE_HZ);
|
||||||
|
|
||||||
if (alwaysOnTickCount == overflowTick) {
|
if (alwaysOnFrameCount == overflowFrameCount) {
|
||||||
alwaysOnTickCount = 0;
|
alwaysOnFrameCount = 0;
|
||||||
alwaysOnStartTime = xTaskGetTickCount();
|
alwaysOnStartTime = xTaskGetTickCount();
|
||||||
}
|
}
|
||||||
if (elapsedTarget > ticksElapsed) {
|
if (targetRenderTick > ticksElapsed) {
|
||||||
return elapsedTarget - ticksElapsed;
|
return targetRenderTick - ticksElapsed;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -209,21 +217,6 @@ void DisplayApp::Refresh() {
|
||||||
LoadScreen(returnAppStack.Pop(), returnDirection);
|
LoadScreen(returnAppStack.Pop(), returnDirection);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto DimScreen = [this]() {
|
|
||||||
if (brightnessController.Level() != Controllers::BrightnessController::Levels::Off) {
|
|
||||||
isDimmed = true;
|
|
||||||
brightnessController.Set(Controllers::BrightnessController::Levels::Low);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
auto RestoreBrightness = [this]() {
|
|
||||||
if (brightnessController.Level() != Controllers::BrightnessController::Levels::Off) {
|
|
||||||
isDimmed = false;
|
|
||||||
lv_disp_trig_activity(nullptr);
|
|
||||||
ApplyBrightness();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
auto IsPastDimTime = [this]() -> bool {
|
auto IsPastDimTime = [this]() -> bool {
|
||||||
return lv_disp_get_inactive_time(nullptr) >= pdMS_TO_TICKS(settingsController.GetScreenTimeOut() - 2000);
|
return lv_disp_get_inactive_time(nullptr) >= pdMS_TO_TICKS(settingsController.GetScreenTimeOut() - 2000);
|
||||||
};
|
};
|
||||||
|
|
@ -235,7 +228,9 @@ void DisplayApp::Refresh() {
|
||||||
TickType_t queueTimeout;
|
TickType_t queueTimeout;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case States::Idle:
|
case States::Idle:
|
||||||
if (settingsController.GetAlwaysOnDisplay()) {
|
queueTimeout = portMAX_DELAY;
|
||||||
|
break;
|
||||||
|
case States::AOD:
|
||||||
if (!currentScreen->IsRunning()) {
|
if (!currentScreen->IsRunning()) {
|
||||||
LoadPreviousScreen();
|
LoadPreviousScreen();
|
||||||
}
|
}
|
||||||
|
|
@ -250,13 +245,10 @@ void DisplayApp::Refresh() {
|
||||||
if (lv_task_handler() > 0) {
|
if (lv_task_handler() > 0) {
|
||||||
// Drop frames that we've missed if drawing/event handling took way longer than expected
|
// Drop frames that we've missed if drawing/event handling took way longer than expected
|
||||||
while (queueTimeout == 0) {
|
while (queueTimeout == 0) {
|
||||||
alwaysOnTickCount += 1;
|
alwaysOnFrameCount += 1;
|
||||||
queueTimeout = CalculateSleepTime();
|
queueTimeout = CalculateSleepTime();
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
queueTimeout = portMAX_DELAY;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case States::Running:
|
case States::Running:
|
||||||
|
|
@ -267,14 +259,27 @@ void DisplayApp::Refresh() {
|
||||||
|
|
||||||
if (!systemTask->IsSleepDisabled() && IsPastDimTime()) {
|
if (!systemTask->IsSleepDisabled() && IsPastDimTime()) {
|
||||||
if (!isDimmed) {
|
if (!isDimmed) {
|
||||||
DimScreen();
|
isDimmed = true;
|
||||||
|
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) {
|
||||||
RestoreBrightness();
|
isDimmed = false;
|
||||||
|
ApplyBrightness();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
@ -285,16 +290,23 @@ void DisplayApp::Refresh() {
|
||||||
Messages msg;
|
Messages msg;
|
||||||
if (xQueueReceive(msgQueue, &msg, queueTimeout) == pdTRUE) {
|
if (xQueueReceive(msgQueue, &msg, queueTimeout) == pdTRUE) {
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case Messages::DimScreen:
|
|
||||||
DimScreen();
|
|
||||||
break;
|
|
||||||
case Messages::GoToSleep:
|
case Messages::GoToSleep:
|
||||||
|
case Messages::GoToAOD:
|
||||||
|
// Checking if SystemTask is sleeping is purely an optimisation.
|
||||||
|
// If it's no longer sleeping since it sent GoToSleep, it has
|
||||||
|
// cancelled the sleep and transitioned directly from
|
||||||
|
// GoingToSleep->Running, so we are about to receive GoToRunning
|
||||||
|
// and can ignore this message. If it wasn't ignored, DisplayApp
|
||||||
|
// would go to sleep and then immediately re-wake
|
||||||
|
if (state != States::Running || !systemTask->IsSleeping()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
while (brightnessController.Level() != Controllers::BrightnessController::Levels::Low) {
|
while (brightnessController.Level() != Controllers::BrightnessController::Levels::Low) {
|
||||||
brightnessController.Lower();
|
brightnessController.Lower();
|
||||||
vTaskDelay(100);
|
vTaskDelay(100);
|
||||||
}
|
}
|
||||||
// Turn brightness down (or set to AlwaysOn mode)
|
// Turn brightness down (or set to AlwaysOn mode)
|
||||||
if (settingsController.GetAlwaysOnDisplay()) {
|
if (msg == Messages::GoToAOD) {
|
||||||
brightnessController.Set(Controllers::BrightnessController::Levels::AlwaysOn);
|
brightnessController.Set(Controllers::BrightnessController::Levels::AlwaysOn);
|
||||||
} else {
|
} else {
|
||||||
brightnessController.Set(Controllers::BrightnessController::Levels::Off);
|
brightnessController.Set(Controllers::BrightnessController::Levels::Off);
|
||||||
|
|
@ -307,23 +319,34 @@ void DisplayApp::Refresh() {
|
||||||
while (!lv_task_handler()) {
|
while (!lv_task_handler()) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Turn LCD display off (or set to low power for AlwaysOn mode)
|
// Clear any ongoing touch pressed events
|
||||||
if (settingsController.GetAlwaysOnDisplay()) {
|
// Without this LVGL gets stuck in the pressed state and will keep refreshing the
|
||||||
|
// display activity timer causing the screen to never sleep after timeout
|
||||||
|
lvgl.ClearTouchState();
|
||||||
|
if (msg == Messages::GoToAOD) {
|
||||||
lcd.LowPowerOn();
|
lcd.LowPowerOn();
|
||||||
// Record idle entry time
|
// Record idle entry time
|
||||||
alwaysOnTickCount = 0;
|
alwaysOnFrameCount = 0;
|
||||||
alwaysOnStartTime = xTaskGetTickCount();
|
alwaysOnStartTime = xTaskGetTickCount();
|
||||||
|
PushMessageToSystemTask(Pinetime::System::Messages::OnDisplayTaskAOD);
|
||||||
|
state = States::AOD;
|
||||||
} else {
|
} else {
|
||||||
lcd.Sleep();
|
lcd.Sleep();
|
||||||
}
|
|
||||||
PushMessageToSystemTask(Pinetime::System::Messages::OnDisplayTaskSleeping);
|
PushMessageToSystemTask(Pinetime::System::Messages::OnDisplayTaskSleeping);
|
||||||
state = States::Idle;
|
state = States::Idle;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Messages::NotifyDeviceActivity:
|
case Messages::NotifyDeviceActivity:
|
||||||
lv_disp_trig_activity(nullptr);
|
lv_disp_trig_activity(nullptr);
|
||||||
break;
|
break;
|
||||||
case Messages::GoToRunning:
|
case Messages::GoToRunning:
|
||||||
if (settingsController.GetAlwaysOnDisplay()) {
|
// If SystemTask is sleeping, the GoToRunning message is old
|
||||||
|
// and must be ignored. Otherwise DisplayApp will use SPI
|
||||||
|
// that is powered down and cause bad behaviour
|
||||||
|
if (state == States::Running || systemTask->IsSleeping()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (state == States::AOD) {
|
||||||
lcd.LowPowerOff();
|
lcd.LowPowerOff();
|
||||||
} else {
|
} else {
|
||||||
lcd.Wakeup();
|
lcd.Wakeup();
|
||||||
|
|
@ -333,8 +356,7 @@ void DisplayApp::Refresh() {
|
||||||
state = States::Running;
|
state = States::Running;
|
||||||
break;
|
break;
|
||||||
case Messages::UpdateBleConnection:
|
case Messages::UpdateBleConnection:
|
||||||
// clockScreen.SetBleConnectionState(bleController.IsConnected() ? Screens::Clock::BleConnectionStates::Connected :
|
// Only used for recovery firmware
|
||||||
// Screens::Clock::BleConnectionStates::NotConnected);
|
|
||||||
break;
|
break;
|
||||||
case Messages::NewNotification:
|
case Messages::NewNotification:
|
||||||
LoadNewScreen(Apps::NotificationsPreview, DisplayApp::FullRefreshDirections::Down);
|
LoadNewScreen(Apps::NotificationsPreview, DisplayApp::FullRefreshDirections::Down);
|
||||||
|
|
@ -449,22 +471,17 @@ void DisplayApp::Refresh() {
|
||||||
case Messages::BleRadioEnableToggle:
|
case Messages::BleRadioEnableToggle:
|
||||||
PushMessageToSystemTask(System::Messages::BleRadioEnableToggle);
|
PushMessageToSystemTask(System::Messages::BleRadioEnableToggle);
|
||||||
break;
|
break;
|
||||||
case Messages::UpdateDateTime:
|
|
||||||
// Added to remove warning
|
|
||||||
// What should happen here?
|
|
||||||
break;
|
|
||||||
case Messages::Chime:
|
case Messages::Chime:
|
||||||
LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::None);
|
LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::None);
|
||||||
motorController.RunForDuration(35);
|
motorController.RunForDuration(35);
|
||||||
break;
|
break;
|
||||||
case Messages::OnChargingEvent:
|
case Messages::OnChargingEvent:
|
||||||
RestoreBrightness();
|
|
||||||
motorController.RunForDuration(15);
|
motorController.RunForDuration(15);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (touchHandler.IsTouching()) {
|
if (state == States::Running && touchHandler.IsTouching()) {
|
||||||
currentScreen->OnTouchEvent(touchHandler.GetX(), touchHandler.GetY());
|
currentScreen->OnTouchEvent(touchHandler.GetX(), touchHandler.GetY());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -587,7 +604,7 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
|
||||||
currentScreen = std::make_unique<Screens::SettingWakeUp>(settingsController);
|
currentScreen = std::make_unique<Screens::SettingWakeUp>(settingsController);
|
||||||
break;
|
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);
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ namespace Pinetime {
|
||||||
namespace Applications {
|
namespace Applications {
|
||||||
class DisplayApp {
|
class DisplayApp {
|
||||||
public:
|
public:
|
||||||
enum class States { Idle, Running };
|
enum class States { Idle, Running, AOD };
|
||||||
enum class FullRefreshDirections { None, Up, Down, Left, Right, LeftAnim, RightAnim };
|
enum class FullRefreshDirections { None, Up, Down, Left, Right, LeftAnim, RightAnim };
|
||||||
|
|
||||||
DisplayApp(Drivers::St7789& lcd,
|
DisplayApp(Drivers::St7789& lcd,
|
||||||
|
|
@ -139,7 +139,7 @@ namespace Pinetime {
|
||||||
bool isDimmed = false;
|
bool isDimmed = false;
|
||||||
|
|
||||||
TickType_t CalculateSleepTime();
|
TickType_t CalculateSleepTime();
|
||||||
TickType_t alwaysOnTickCount;
|
TickType_t alwaysOnFrameCount;
|
||||||
TickType_t alwaysOnStartTime;
|
TickType_t alwaysOnStartTime;
|
||||||
// If this is to be changed, make sure the actual always on refresh rate is changed
|
// If this is to be changed, make sure the actual always on refresh rate is changed
|
||||||
// by configuring the LCD refresh timings
|
// by configuring the LCD refresh timings
|
||||||
|
|
|
||||||
|
|
@ -248,6 +248,8 @@ void LittleVgl::SetNewTouchPoint(int16_t x, int16_t y, bool contact) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cancel an ongoing tap
|
||||||
|
// Signifies that LVGL should not handle the current tap
|
||||||
void LittleVgl::CancelTap() {
|
void LittleVgl::CancelTap() {
|
||||||
if (tapped) {
|
if (tapped) {
|
||||||
isCancelled = true;
|
isCancelled = true;
|
||||||
|
|
@ -255,6 +257,13 @@ void LittleVgl::CancelTap() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear the current tapped state
|
||||||
|
// Signifies that touch input processing is suspended
|
||||||
|
void LittleVgl::ClearTouchState() {
|
||||||
|
touchPoint = {-1, -1};
|
||||||
|
tapped = false;
|
||||||
|
}
|
||||||
|
|
||||||
bool LittleVgl::GetTouchPadInfo(lv_indev_data_t* ptr) {
|
bool LittleVgl::GetTouchPadInfo(lv_indev_data_t* ptr) {
|
||||||
ptr->point.x = touchPoint.x;
|
ptr->point.x = touchPoint.x;
|
||||||
ptr->point.y = touchPoint.y;
|
ptr->point.y = touchPoint.y;
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ namespace Pinetime {
|
||||||
void SetFullRefresh(FullRefreshDirections direction);
|
void SetFullRefresh(FullRefreshDirections direction);
|
||||||
void SetNewTouchPoint(int16_t x, int16_t y, bool contact);
|
void SetNewTouchPoint(int16_t x, int16_t y, bool contact);
|
||||||
void CancelTap();
|
void CancelTap();
|
||||||
|
void ClearTouchState();
|
||||||
|
|
||||||
bool GetFullRefresh() {
|
bool GetFullRefresh() {
|
||||||
bool returnValue = fullRefresh;
|
bool returnValue = fullRefresh;
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ namespace Pinetime {
|
||||||
namespace Display {
|
namespace Display {
|
||||||
enum class Messages : uint8_t {
|
enum class Messages : uint8_t {
|
||||||
GoToSleep,
|
GoToSleep,
|
||||||
|
GoToAOD,
|
||||||
GoToRunning,
|
GoToRunning,
|
||||||
UpdateDateTime,
|
|
||||||
UpdateBleConnection,
|
UpdateBleConnection,
|
||||||
TouchEvent,
|
TouchEvent,
|
||||||
ButtonPushed,
|
ButtonPushed,
|
||||||
|
|
@ -17,7 +17,8 @@ namespace Pinetime {
|
||||||
NewNotification,
|
NewNotification,
|
||||||
TimerDone,
|
TimerDone,
|
||||||
BleFirmwareUpdateStarted,
|
BleFirmwareUpdateStarted,
|
||||||
DimScreen,
|
// Resets the screen timeout timer when awake
|
||||||
|
// Does nothing when asleep
|
||||||
NotifyDeviceActivity,
|
NotifyDeviceActivity,
|
||||||
ShowPairingKey,
|
ShowPairingKey,
|
||||||
AlarmTriggered,
|
AlarmTriggered,
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ configure_file(${CMAKE_CURRENT_LIST_DIR}/jetbrains_mono_bold_20.c_M.patch
|
||||||
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)
|
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)
|
||||||
# FindPython3 module introduces with CMake 3.12
|
# FindPython3 module introduces with CMake 3.12
|
||||||
# https://cmake.org/cmake/help/latest/module/FindPython3.html
|
# https://cmake.org/cmake/help/latest/module/FindPython3.html
|
||||||
|
set(Python3_FIND_STRATEGY LOCATION) # https://discourse.cmake.org/t/find-package-python3-is-not-finding-the-correct-python/10563
|
||||||
find_package(Python3 REQUIRED)
|
find_package(Python3 REQUIRED)
|
||||||
else()
|
else()
|
||||||
set(Python3_EXECUTABLE "python")
|
set(Python3_EXECUTABLE "python")
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -104,9 +104,7 @@ void Timer::UpdateMask() {
|
||||||
|
|
||||||
void Timer::Refresh() {
|
void Timer::Refresh() {
|
||||||
if (timer.IsRunning()) {
|
if (timer.IsRunning()) {
|
||||||
auto secondsRemaining = std::chrono::duration_cast<std::chrono::seconds>(timer.GetTimeRemaining());
|
DisplayTime();
|
||||||
minuteCounter.SetValue(secondsRemaining.count() / 60);
|
|
||||||
secondCounter.SetValue(secondsRemaining.count() % 60);
|
|
||||||
} else if (buttonPressing && xTaskGetTickCount() > pressTime + pdMS_TO_TICKS(150)) {
|
} else if (buttonPressing && xTaskGetTickCount() > pressTime + pdMS_TO_TICKS(150)) {
|
||||||
lv_label_set_text_static(txtPlayPause, "Reset");
|
lv_label_set_text_static(txtPlayPause, "Reset");
|
||||||
maskPosition += 15;
|
maskPosition += 15;
|
||||||
|
|
@ -119,6 +117,14 @@ void Timer::Refresh() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Timer::DisplayTime() {
|
||||||
|
displaySeconds = std::chrono::duration_cast<std::chrono::seconds>(timer.GetTimeRemaining());
|
||||||
|
if (displaySeconds.IsUpdated()) {
|
||||||
|
minuteCounter.SetValue(displaySeconds.Get().count() / 60);
|
||||||
|
secondCounter.SetValue(displaySeconds.Get().count() % 60);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Timer::SetTimerRunning() {
|
void Timer::SetTimerRunning() {
|
||||||
minuteCounter.HideControls();
|
minuteCounter.HideControls();
|
||||||
secondCounter.HideControls();
|
secondCounter.HideControls();
|
||||||
|
|
@ -133,9 +139,7 @@ void Timer::SetTimerStopped() {
|
||||||
|
|
||||||
void Timer::ToggleRunning() {
|
void Timer::ToggleRunning() {
|
||||||
if (timer.IsRunning()) {
|
if (timer.IsRunning()) {
|
||||||
auto secondsRemaining = std::chrono::duration_cast<std::chrono::seconds>(timer.GetTimeRemaining());
|
DisplayTime();
|
||||||
minuteCounter.SetValue(secondsRemaining.count() / 60);
|
|
||||||
secondCounter.SetValue(secondsRemaining.count() % 60);
|
|
||||||
timer.StopTimer();
|
timer.StopTimer();
|
||||||
SetTimerStopped();
|
SetTimerStopped();
|
||||||
} else if (secondCounter.GetValue() + minuteCounter.GetValue() > 0) {
|
} else if (secondCounter.GetValue() + minuteCounter.GetValue() > 0) {
|
||||||
|
|
@ -147,7 +151,6 @@ void Timer::ToggleRunning() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timer::Reset() {
|
void Timer::Reset() {
|
||||||
minuteCounter.SetValue(0);
|
DisplayTime();
|
||||||
secondCounter.SetValue(0);
|
|
||||||
SetTimerStopped();
|
SetTimerStopped();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "displayapp/screens/Screen.h"
|
#include "displayapp/screens/Screen.h"
|
||||||
#include "components/datetime/DateTimeController.h"
|
|
||||||
#include "systemtask/SystemTask.h"
|
#include "systemtask/SystemTask.h"
|
||||||
#include "displayapp/LittleVgl.h"
|
#include "displayapp/LittleVgl.h"
|
||||||
#include "displayapp/widgets/Counter.h"
|
#include "displayapp/widgets/Counter.h"
|
||||||
|
#include "utility/DirtyValue.h"
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
|
|
||||||
#include "components/timer/Timer.h"
|
#include "components/timer/Timer.h"
|
||||||
|
|
@ -26,6 +26,7 @@ namespace Pinetime::Applications {
|
||||||
void SetTimerRunning();
|
void SetTimerRunning();
|
||||||
void SetTimerStopped();
|
void SetTimerStopped();
|
||||||
void UpdateMask();
|
void UpdateMask();
|
||||||
|
void DisplayTime();
|
||||||
Pinetime::Controllers::Timer& timer;
|
Pinetime::Controllers::Timer& timer;
|
||||||
|
|
||||||
lv_obj_t* btnPlayPause;
|
lv_obj_t* btnPlayPause;
|
||||||
|
|
@ -43,6 +44,7 @@ namespace Pinetime::Applications {
|
||||||
bool buttonPressing = false;
|
bool buttonPressing = false;
|
||||||
lv_coord_t maskPosition = 0;
|
lv_coord_t maskPosition = 0;
|
||||||
TickType_t pressTime = 0;
|
TickType_t pressTime = 0;
|
||||||
|
Utility::DirtyValue<std::chrono::seconds> displaySeconds;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#include "displayapp/screens/NotificationIcon.h"
|
#include "displayapp/screens/NotificationIcon.h"
|
||||||
#include "displayapp/screens/Symbols.h"
|
#include "displayapp/screens/Symbols.h"
|
||||||
#include "displayapp/screens/WeatherSymbols.h"
|
#include "displayapp/screens/WeatherSymbols.h"
|
||||||
|
|
@ -174,13 +175,12 @@ void WatchFaceDigital::Refresh() {
|
||||||
if (currentWeather.IsUpdated()) {
|
if (currentWeather.IsUpdated()) {
|
||||||
auto optCurrentWeather = currentWeather.Get();
|
auto optCurrentWeather = currentWeather.Get();
|
||||||
if (optCurrentWeather) {
|
if (optCurrentWeather) {
|
||||||
int16_t temp = optCurrentWeather->temperature;
|
int16_t temp = optCurrentWeather->temperature.Celsius();
|
||||||
char tempUnit = 'C';
|
char tempUnit = 'C';
|
||||||
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
||||||
temp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(temp);
|
temp = optCurrentWeather->temperature.Fahrenheit();
|
||||||
tempUnit = 'F';
|
tempUnit = 'F';
|
||||||
}
|
}
|
||||||
temp = temp / 100 + (temp % 100 >= 50 ? 1 : 0);
|
|
||||||
lv_label_set_text_fmt(temperature, "%d°%c", temp, tempUnit);
|
lv_label_set_text_fmt(temperature, "%d°%c", temp, tempUnit);
|
||||||
lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId));
|
lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
#include "displayapp/screens/WatchFacePineTimeStyle.h"
|
#include "displayapp/screens/WatchFacePineTimeStyle.h"
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <displayapp/Colors.h>
|
#include "displayapp/Colors.h"
|
||||||
#include "displayapp/screens/BatteryIcon.h"
|
#include "displayapp/screens/BatteryIcon.h"
|
||||||
#include "displayapp/screens/BleIcon.h"
|
#include "displayapp/screens/BleIcon.h"
|
||||||
#include "displayapp/screens/NotificationIcon.h"
|
#include "displayapp/screens/NotificationIcon.h"
|
||||||
|
|
@ -543,11 +543,10 @@ void WatchFacePineTimeStyle::Refresh() {
|
||||||
if (currentWeather.IsUpdated()) {
|
if (currentWeather.IsUpdated()) {
|
||||||
auto optCurrentWeather = currentWeather.Get();
|
auto optCurrentWeather = currentWeather.Get();
|
||||||
if (optCurrentWeather) {
|
if (optCurrentWeather) {
|
||||||
int16_t temp = optCurrentWeather->temperature;
|
int16_t temp = optCurrentWeather->temperature.Celsius();
|
||||||
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
||||||
temp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(temp);
|
temp = optCurrentWeather->temperature.Fahrenheit();
|
||||||
}
|
}
|
||||||
temp = temp / 100 + (temp % 100 >= 50 ? 1 : 0);
|
|
||||||
lv_label_set_text_fmt(temperature, "%d°", temp);
|
lv_label_set_text_fmt(temperature, "%d°", temp);
|
||||||
lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId));
|
lv_label_set_text(weatherIcon, Symbols::GetSymbol(optCurrentWeather->iconId));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#include "displayapp/screens/Weather.h"
|
#include "displayapp/screens/Weather.h"
|
||||||
|
|
||||||
#include <lvgl/lvgl.h>
|
#include <lvgl/lvgl.h>
|
||||||
|
|
||||||
#include "components/ble/SimpleWeatherService.h"
|
#include "components/ble/SimpleWeatherService.h"
|
||||||
#include "components/datetime/DateTimeController.h"
|
#include "components/datetime/DateTimeController.h"
|
||||||
#include "components/settings/Settings.h"
|
#include "components/settings/Settings.h"
|
||||||
|
|
@ -10,31 +12,27 @@
|
||||||
using namespace Pinetime::Applications::Screens;
|
using namespace Pinetime::Applications::Screens;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
lv_color_t TemperatureColor(int16_t temperature) {
|
lv_color_t TemperatureColor(Pinetime::Controllers::SimpleWeatherService::Temperature temp) {
|
||||||
if (temperature <= 0) { // freezing
|
if (temp.Celsius() <= 0) { // freezing
|
||||||
return Colors::blue;
|
return Colors::blue;
|
||||||
} else if (temperature <= 400) { // ice
|
} else if (temp.Celsius() <= 4) { // ice
|
||||||
return LV_COLOR_CYAN;
|
return LV_COLOR_CYAN;
|
||||||
} else if (temperature >= 2700) { // hot
|
} else if (temp.Celsius() >= 27) { // hot
|
||||||
return Colors::deepOrange;
|
return Colors::deepOrange;
|
||||||
}
|
}
|
||||||
return Colors::orange; // normal
|
return Colors::orange; // normal
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t TemperatureStyle(int16_t temperature) {
|
uint8_t TemperatureStyle(Pinetime::Controllers::SimpleWeatherService::Temperature temp) {
|
||||||
if (temperature <= 0) { // freezing
|
if (temp.Celsius() <= 0) { // freezing
|
||||||
return LV_TABLE_PART_CELL3;
|
return LV_TABLE_PART_CELL3;
|
||||||
} else if (temperature <= 400) { // ice
|
} else if (temp.Celsius() <= 4) { // ice
|
||||||
return LV_TABLE_PART_CELL4;
|
return LV_TABLE_PART_CELL4;
|
||||||
} else if (temperature >= 2700) { // hot
|
} else if (temp.Celsius() >= 27) { // hot
|
||||||
return LV_TABLE_PART_CELL6;
|
return LV_TABLE_PART_CELL6;
|
||||||
}
|
}
|
||||||
return LV_TABLE_PART_CELL5; // normal
|
return LV_TABLE_PART_CELL5; // normal
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t RoundTemperature(int16_t temp) {
|
|
||||||
return temp = temp / 100 + (temp % 100 >= 50 ? 1 : 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Weather::Weather(Controllers::Settings& settingsController, Controllers::SimpleWeatherService& weatherService)
|
Weather::Weather(Controllers::Settings& settingsController, Controllers::SimpleWeatherService& weatherService)
|
||||||
|
|
@ -120,22 +118,25 @@ void Weather::Refresh() {
|
||||||
if (currentWeather.IsUpdated()) {
|
if (currentWeather.IsUpdated()) {
|
||||||
auto optCurrentWeather = currentWeather.Get();
|
auto optCurrentWeather = currentWeather.Get();
|
||||||
if (optCurrentWeather) {
|
if (optCurrentWeather) {
|
||||||
int16_t temp = optCurrentWeather->temperature;
|
int16_t temp = optCurrentWeather->temperature.Celsius();
|
||||||
int16_t minTemp = optCurrentWeather->minTemperature;
|
int16_t minTemp = optCurrentWeather->minTemperature.Celsius();
|
||||||
int16_t maxTemp = optCurrentWeather->maxTemperature;
|
int16_t maxTemp = optCurrentWeather->maxTemperature.Celsius();
|
||||||
lv_obj_set_style_local_text_color(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, TemperatureColor(temp));
|
|
||||||
char tempUnit = 'C';
|
char tempUnit = 'C';
|
||||||
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
||||||
temp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(temp);
|
temp = optCurrentWeather->temperature.Fahrenheit();
|
||||||
minTemp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(minTemp);
|
minTemp = optCurrentWeather->minTemperature.Fahrenheit();
|
||||||
maxTemp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(maxTemp);
|
maxTemp = optCurrentWeather->maxTemperature.Fahrenheit();
|
||||||
tempUnit = 'F';
|
tempUnit = 'F';
|
||||||
}
|
}
|
||||||
|
lv_obj_set_style_local_text_color(temperature,
|
||||||
|
LV_LABEL_PART_MAIN,
|
||||||
|
LV_STATE_DEFAULT,
|
||||||
|
TemperatureColor(optCurrentWeather->temperature));
|
||||||
lv_label_set_text(icon, Symbols::GetSymbol(optCurrentWeather->iconId));
|
lv_label_set_text(icon, Symbols::GetSymbol(optCurrentWeather->iconId));
|
||||||
lv_label_set_text(condition, Symbols::GetCondition(optCurrentWeather->iconId));
|
lv_label_set_text(condition, Symbols::GetCondition(optCurrentWeather->iconId));
|
||||||
lv_label_set_text_fmt(temperature, "%d°%c", RoundTemperature(temp), tempUnit);
|
lv_label_set_text_fmt(temperature, "%d°%c", temp, tempUnit);
|
||||||
lv_label_set_text_fmt(minTemperature, "%d°", RoundTemperature(minTemp));
|
lv_label_set_text_fmt(minTemperature, "%d°", minTemp);
|
||||||
lv_label_set_text_fmt(maxTemperature, "%d°", RoundTemperature(maxTemp));
|
lv_label_set_text_fmt(maxTemperature, "%d°", maxTemp);
|
||||||
} else {
|
} else {
|
||||||
lv_label_set_text(icon, "");
|
lv_label_set_text(icon, "");
|
||||||
lv_label_set_text(condition, "");
|
lv_label_set_text(condition, "");
|
||||||
|
|
@ -152,24 +153,22 @@ void Weather::Refresh() {
|
||||||
if (optCurrentForecast) {
|
if (optCurrentForecast) {
|
||||||
std::tm localTime = *std::localtime(reinterpret_cast<const time_t*>(&optCurrentForecast->timestamp));
|
std::tm localTime = *std::localtime(reinterpret_cast<const time_t*>(&optCurrentForecast->timestamp));
|
||||||
|
|
||||||
for (int i = 0; i < Controllers::SimpleWeatherService::MaxNbForecastDays; i++) {
|
for (int i = 0; i < optCurrentForecast->nbDays; i++) {
|
||||||
int16_t maxTemp = optCurrentForecast->days[i].maxTemperature;
|
int16_t minTemp = optCurrentForecast->days[i]->minTemperature.Celsius();
|
||||||
int16_t minTemp = optCurrentForecast->days[i].minTemperature;
|
int16_t maxTemp = optCurrentForecast->days[i]->maxTemperature.Celsius();
|
||||||
lv_table_set_cell_type(forecast, 2, i, TemperatureStyle(maxTemp));
|
|
||||||
lv_table_set_cell_type(forecast, 3, i, TemperatureStyle(minTemp));
|
|
||||||
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
if (settingsController.GetWeatherFormat() == Controllers::Settings::WeatherFormat::Imperial) {
|
||||||
maxTemp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(maxTemp);
|
minTemp = optCurrentForecast->days[i]->maxTemperature.Fahrenheit();
|
||||||
minTemp = Controllers::SimpleWeatherService::CelsiusToFahrenheit(minTemp);
|
maxTemp = optCurrentForecast->days[i]->minTemperature.Fahrenheit();
|
||||||
}
|
}
|
||||||
|
lv_table_set_cell_type(forecast, 2, i, TemperatureStyle(optCurrentForecast->days[i]->maxTemperature));
|
||||||
|
lv_table_set_cell_type(forecast, 3, i, TemperatureStyle(optCurrentForecast->days[i]->minTemperature));
|
||||||
uint8_t wday = localTime.tm_wday + i + 1;
|
uint8_t wday = localTime.tm_wday + i + 1;
|
||||||
if (wday > 7) {
|
if (wday > 7) {
|
||||||
wday -= 7;
|
wday -= 7;
|
||||||
}
|
}
|
||||||
maxTemp = RoundTemperature(maxTemp);
|
|
||||||
minTemp = RoundTemperature(minTemp);
|
|
||||||
const char* dayOfWeek = Controllers::DateTime::DayOfWeekShortToStringLow(static_cast<Controllers::DateTime::Days>(wday));
|
const char* dayOfWeek = Controllers::DateTime::DayOfWeekShortToStringLow(static_cast<Controllers::DateTime::Days>(wday));
|
||||||
lv_table_set_cell_value(forecast, 0, i, dayOfWeek);
|
lv_table_set_cell_value(forecast, 0, i, dayOfWeek);
|
||||||
lv_table_set_cell_value(forecast, 1, i, Symbols::GetSymbol(optCurrentForecast->days[i].iconId));
|
lv_table_set_cell_value(forecast, 1, i, Symbols::GetSymbol(optCurrentForecast->days[i]->iconId));
|
||||||
// Pad cells based on the largest number of digits on each column
|
// Pad cells based on the largest number of digits on each column
|
||||||
char maxPadding[3] = " ";
|
char maxPadding[3] = " ";
|
||||||
char minPadding[3] = " ";
|
char minPadding[3] = " ";
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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,7 +38,7 @@ 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::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},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -175,9 +175,8 @@ void St7789::IdleFrameRateOn() {
|
||||||
// According to the datasheet, these controls should apply only to partial/idle mode
|
// According to the datasheet, these controls should apply only to partial/idle mode
|
||||||
// However they appear to apply to normal mode, so we have to enable/disable
|
// However they appear to apply to normal mode, so we have to enable/disable
|
||||||
// every time we enter/exit always on
|
// every time we enter/exit always on
|
||||||
// In testing this divider appears to actually be 16x?
|
|
||||||
constexpr uint8_t args[] = {
|
constexpr uint8_t args[] = {
|
||||||
0x13, // Enable frame rate control for partial/idle mode, 8x frame divider
|
0x12, // Enable frame rate control for partial/idle mode, 4x frame divider
|
||||||
0x1e, // Idle mode frame rate
|
0x1e, // Idle mode frame rate
|
||||||
0x1e, // Partial mode frame rate (unused)
|
0x1e, // Partial mode frame rate (unused)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,8 @@ void HeartRateTask::Work() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (measurementStarted) {
|
if (measurementStarted) {
|
||||||
int8_t ambient = ppg.Preprocess(heartRateSensor.ReadHrs(), heartRateSensor.ReadAls());
|
auto sensorData = heartRateSensor.ReadHrsAls();
|
||||||
|
int8_t ambient = ppg.Preprocess(sensorData.hrs, sensorData.als);
|
||||||
int bpm = ppg.HeartRate();
|
int bpm = ppg.HeartRate();
|
||||||
|
|
||||||
// If ambient light detected or a reset requested (bpm < 0)
|
// If ambient light detected or a reset requested (bpm < 0)
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 56b17bf9f74096774944bcac0829adcd887d391e
|
|
||||||
|
|
@ -104,7 +104,7 @@ 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 {};
|
||||||
|
|
@ -168,7 +168,7 @@ std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> NoI
|
||||||
|
|
||||||
void nrfx_gpiote_evt_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
|
void nrfx_gpiote_evt_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
|
||||||
if (pin == Pinetime::PinMap::Cst816sIrq) {
|
if (pin == Pinetime::PinMap::Cst816sIrq) {
|
||||||
systemTask.OnTouchEvent();
|
systemTask.PushMessage(Pinetime::System::Messages::OnTouchEvent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ message(STATUS "Using ${LV_IMG_CONV} to generate font files")
|
||||||
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)
|
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)
|
||||||
# FindPython3 module introduces with CMake 3.12
|
# FindPython3 module introduces with CMake 3.12
|
||||||
# https://cmake.org/cmake/help/latest/module/FindPython3.html
|
# https://cmake.org/cmake/help/latest/module/FindPython3.html
|
||||||
|
set(Python3_FIND_STRATEGY LOCATION) # https://discourse.cmake.org/t/find-package-python3-is-not-finding-the-correct-python/10563
|
||||||
find_package(Python3 REQUIRED)
|
find_package(Python3 REQUIRED)
|
||||||
else()
|
else()
|
||||||
set(Python3_EXECUTABLE "python")
|
set(Python3_EXECUTABLE "python")
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ namespace Pinetime {
|
||||||
enum class Messages : uint8_t {
|
enum class Messages : uint8_t {
|
||||||
GoToSleep,
|
GoToSleep,
|
||||||
GoToRunning,
|
GoToRunning,
|
||||||
TouchWakeUp,
|
|
||||||
OnNewTime,
|
OnNewTime,
|
||||||
OnNewNotification,
|
OnNewNotification,
|
||||||
OnNewCall,
|
OnNewCall,
|
||||||
|
|
@ -17,6 +16,7 @@ namespace Pinetime {
|
||||||
HandleButtonEvent,
|
HandleButtonEvent,
|
||||||
HandleButtonTimerEvent,
|
HandleButtonTimerEvent,
|
||||||
OnDisplayTaskSleeping,
|
OnDisplayTaskSleeping,
|
||||||
|
OnDisplayTaskAOD,
|
||||||
EnableSleeping,
|
EnableSleeping,
|
||||||
DisableSleeping,
|
DisableSleeping,
|
||||||
OnNewDay,
|
OnNewDay,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#include "systemtask/SystemTask.h"
|
#include "systemtask/SystemTask.h"
|
||||||
#if configUSE_TRACE_FACILITY == 1
|
#if NRF_LOG_ENABLED
|
||||||
// FreeRtosMonitor
|
// FreeRtosMonitor
|
||||||
#include <FreeRTOS.h>
|
#include <FreeRTOS.h>
|
||||||
#include <task.h>
|
#include <task.h>
|
||||||
|
|
|
||||||
|
|
@ -189,40 +189,71 @@ 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;
|
|
||||||
}
|
|
||||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::NotifyDeviceActivity);
|
|
||||||
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::GoToSleep:
|
||||||
if (touchHandler.ProcessTouchInfo(touchPanel.GetTouchInfo())) {
|
GoToSleep();
|
||||||
|
break;
|
||||||
|
case Messages::OnNewTime:
|
||||||
|
if (alarmController.IsEnabled()) {
|
||||||
|
alarmController.ScheduleAlarm();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Messages::OnNewNotification:
|
||||||
|
if (settingsController.GetNotificationStatus() == Pinetime::Controllers::Settings::Notification::On) {
|
||||||
|
if (IsSleeping()) {
|
||||||
|
GoToRunning();
|
||||||
|
}
|
||||||
|
displayApp.PushMessage(Pinetime::Applications::Display::Messages::NewNotification);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Messages::SetOffAlarm:
|
||||||
|
GoToRunning();
|
||||||
|
displayApp.PushMessage(Pinetime::Applications::Display::Messages::AlarmTriggered);
|
||||||
|
break;
|
||||||
|
case Messages::BleConnected:
|
||||||
|
displayApp.PushMessage(Pinetime::Applications::Display::Messages::NotifyDeviceActivity);
|
||||||
|
isBleDiscoveryTimerRunning = true;
|
||||||
|
bleDiscoveryTimer = 5;
|
||||||
|
break;
|
||||||
|
case Messages::BleFirmwareUpdateStarted:
|
||||||
|
GoToRunning();
|
||||||
|
wakeLocksHeld++;
|
||||||
|
displayApp.PushMessage(Pinetime::Applications::Display::Messages::BleFirmwareUpdateStarted);
|
||||||
|
break;
|
||||||
|
case Messages::BleFirmwareUpdateFinished:
|
||||||
|
if (bleController.State() == Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated) {
|
||||||
|
NVIC_SystemReset();
|
||||||
|
}
|
||||||
|
wakeLocksHeld--;
|
||||||
|
break;
|
||||||
|
case Messages::StartFileTransfer:
|
||||||
|
NRF_LOG_INFO("[systemtask] FS Started");
|
||||||
|
GoToRunning();
|
||||||
|
wakeLocksHeld++;
|
||||||
|
// TODO add intent of fs access icon or something
|
||||||
|
break;
|
||||||
|
case Messages::StopFileTransfer:
|
||||||
|
NRF_LOG_INFO("[systemtask] FS Stopped");
|
||||||
|
wakeLocksHeld--;
|
||||||
|
// TODO add intent of fs access icon or something
|
||||||
|
break;
|
||||||
|
case Messages::OnTouchEvent:
|
||||||
|
// Finish immediately if no new events
|
||||||
|
if (!touchHandler.ProcessTouchInfo(touchPanel.GetTouchInfo())) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (state == SystemTaskState::Running) {
|
||||||
|
displayApp.PushMessage(Pinetime::Applications::Display::Messages::TouchEvent);
|
||||||
|
} else {
|
||||||
|
// If asleep, check for touch panel wake triggers
|
||||||
auto gesture = touchHandler.GestureGet();
|
auto gesture = touchHandler.GestureGet();
|
||||||
if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep &&
|
if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep &&
|
||||||
gesture != Pinetime::Applications::TouchEvents::None &&
|
gesture != Pinetime::Applications::TouchEvents::None &&
|
||||||
|
|
@ -234,75 +265,6 @@ void SystemTask::Work() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case Messages::GoToSleep:
|
|
||||||
if (doNotGoToSleep) {
|
|
||||||
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;
|
|
||||||
case Messages::OnNewTime:
|
|
||||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::NotifyDeviceActivity);
|
|
||||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::UpdateDateTime);
|
|
||||||
if (alarmController.State() == Controllers::AlarmController::AlarmState::Set) {
|
|
||||||
alarmController.ScheduleAlarm();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Messages::OnNewNotification:
|
|
||||||
if (settingsController.GetNotificationStatus() == Pinetime::Controllers::Settings::Notification::On) {
|
|
||||||
if (state == SystemTaskState::Sleeping) {
|
|
||||||
GoToRunning();
|
|
||||||
} else {
|
|
||||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::NotifyDeviceActivity);
|
|
||||||
}
|
|
||||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::NewNotification);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Messages::SetOffAlarm:
|
|
||||||
if (state == SystemTaskState::Sleeping) {
|
|
||||||
GoToRunning();
|
|
||||||
}
|
|
||||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::AlarmTriggered);
|
|
||||||
break;
|
|
||||||
case Messages::BleConnected:
|
|
||||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::NotifyDeviceActivity);
|
|
||||||
isBleDiscoveryTimerRunning = true;
|
|
||||||
bleDiscoveryTimer = 5;
|
|
||||||
break;
|
|
||||||
case Messages::BleFirmwareUpdateStarted:
|
|
||||||
doNotGoToSleep = true;
|
|
||||||
if (state == SystemTaskState::Sleeping) {
|
|
||||||
GoToRunning();
|
|
||||||
}
|
|
||||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::BleFirmwareUpdateStarted);
|
|
||||||
break;
|
|
||||||
case Messages::BleFirmwareUpdateFinished:
|
|
||||||
if (bleController.State() == Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated) {
|
|
||||||
NVIC_SystemReset();
|
|
||||||
}
|
|
||||||
doNotGoToSleep = false;
|
|
||||||
break;
|
|
||||||
case Messages::StartFileTransfer:
|
|
||||||
NRF_LOG_INFO("[systemtask] FS Started");
|
|
||||||
doNotGoToSleep = true;
|
|
||||||
if (state == SystemTaskState::Sleeping) {
|
|
||||||
GoToRunning();
|
|
||||||
}
|
|
||||||
// TODO add intent of fs access icon or something
|
|
||||||
break;
|
|
||||||
case Messages::StopFileTransfer:
|
|
||||||
NRF_LOG_INFO("[systemtask] FS Stopped");
|
|
||||||
doNotGoToSleep = false;
|
|
||||||
// TODO add intent of fs access icon or something
|
|
||||||
break;
|
|
||||||
case Messages::OnTouchEvent:
|
|
||||||
if (touchHandler.ProcessTouchInfo(touchPanel.GetTouchInfo())) {
|
|
||||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::TouchEvent);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Messages::HandleButtonEvent: {
|
case Messages::HandleButtonEvent: {
|
||||||
Controllers::ButtonActions action = Controllers::ButtonActions::None;
|
Controllers::ButtonActions action = Controllers::ButtonActions::None;
|
||||||
if (nrf_gpio_pin_read(Pinetime::PinMap::Button) == 0) {
|
if (nrf_gpio_pin_read(Pinetime::PinMap::Button) == 0) {
|
||||||
|
|
@ -323,6 +285,14 @@ void SystemTask::Work() {
|
||||||
HandleButtonAction(action);
|
HandleButtonAction(action);
|
||||||
} break;
|
} break;
|
||||||
case Messages::OnDisplayTaskSleeping:
|
case Messages::OnDisplayTaskSleeping:
|
||||||
|
case Messages::OnDisplayTaskAOD:
|
||||||
|
// 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/AOD
|
||||||
|
// 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.
|
||||||
|
|
@ -330,7 +300,7 @@ void SystemTask::Work() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must keep SPI awake when still updating the display for always on
|
// Must keep SPI awake when still updating the display for always on
|
||||||
if (!settingsController.GetAlwaysOnDisplay()) {
|
if (msg == Messages::OnDisplayTaskSleeping) {
|
||||||
spi.Sleep();
|
spi.Sleep();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -339,7 +309,11 @@ void SystemTask::Work() {
|
||||||
touchPanel.Sleep();
|
touchPanel.Sleep();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg == Messages::OnDisplayTaskSleeping) {
|
||||||
state = SystemTaskState::Sleeping;
|
state = SystemTaskState::Sleeping;
|
||||||
|
} else {
|
||||||
|
state = SystemTaskState::AODSleeping;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Messages::OnNewDay:
|
case Messages::OnNewDay:
|
||||||
// We might be sleeping (with TWI device disabled.
|
// We might be sleeping (with TWI device disabled.
|
||||||
|
|
@ -349,39 +323,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();
|
||||||
|
|
@ -390,9 +348,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:
|
||||||
|
|
@ -427,11 +383,55 @@ 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;
|
||||||
}
|
}
|
||||||
|
if (state == SystemTaskState::Sleeping || state == SystemTaskState::AODSleeping) {
|
||||||
|
// SPI only switched off when entering Sleeping, not AOD or GoingToSleep
|
||||||
|
if (state == SystemTaskState::Sleeping) {
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
void SystemTask::GoToSleep() {
|
||||||
|
if (IsSleeping()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (IsSleepDisabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NRF_LOG_INFO("[systemtask] Going to sleep");
|
||||||
|
if (settingsController.GetAlwaysOnDisplay()) {
|
||||||
|
displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToAOD);
|
||||||
|
} else {
|
||||||
|
displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToSleep);
|
||||||
|
}
|
||||||
|
heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::GoToSleep);
|
||||||
|
|
||||||
|
state = SystemTaskState::GoingToSleep;
|
||||||
|
};
|
||||||
|
|
||||||
|
void SystemTask::UpdateMotion() {
|
||||||
|
// Only consider disabling motion updates specifically in the Sleeping state
|
||||||
|
// AOD needs motion on to show up to date step counts
|
||||||
if (state == SystemTaskState::Sleeping && !(settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist) ||
|
if (state == SystemTaskState::Sleeping && !(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())) {
|
||||||
|
|
@ -457,7 +457,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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -473,7 +473,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;
|
||||||
|
|
@ -493,29 +493,7 @@ 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() {
|
|
||||||
if (state == SystemTaskState::Running) {
|
|
||||||
PushMessage(Messages::OnTouchEvent);
|
|
||||||
} else if (state == SystemTaskState::Sleeping) {
|
|
||||||
if (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::SingleTap) or
|
|
||||||
settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) {
|
|
||||||
PushMessage(Messages::TouchWakeUp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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, AODSleeping };
|
||||||
SystemTask(Drivers::SpiMaster& spi,
|
SystemTask(Drivers::SpiMaster& spi,
|
||||||
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
|
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
|
||||||
Drivers::TwiMaster& twiMaster,
|
Drivers::TwiMaster& twiMaster,
|
||||||
|
|
@ -77,13 +77,8 @@ namespace Pinetime {
|
||||||
void Start();
|
void Start();
|
||||||
void PushMessage(Messages msg);
|
void PushMessage(Messages msg);
|
||||||
|
|
||||||
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 +86,7 @@ namespace Pinetime {
|
||||||
};
|
};
|
||||||
|
|
||||||
bool IsSleeping() const {
|
bool IsSleeping() const {
|
||||||
return state == SystemTaskState::Sleeping || state == SystemTaskState::WakingUp;
|
return state != SystemTaskState::Running;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -127,13 +122,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…
Add table
Reference in a new issue