Compare commits

...

45 commits

Author SHA1 Message Date
Felipe Martínez 52061ad50f
Merge 21c3b1ebb0 into a2356f2f4a 2024-10-02 17:47:58 +00:00
NeroBurner a2356f2f4a
MusicService: add missing includes for TickType_t and xTaskGetTickCount (#2130)
Some checks failed
CI / build-firmware (push) Successful in 10m55s
CI / build-simulator (push) Failing after 3s
CI / get-base-ref-size (push) Has been skipped
CI / Compare build size (push) Has been skipped
Add `FreeRTOS.h` include for the directly used data type `TickType_t` in the header
and the function `xTaskGetTickCount` from FreeRTOS's `task.h`
2024-09-29 21:10:32 +02:00
NeroBurner 3db4e012ce
Remove unused pointer to DisplayApp member variables (#2125)
Some checks failed
CI / build-firmware (push) Successful in 10m10s
CI / build-simulator (push) Failing after 3s
CI / get-base-ref-size (push) Has been skipped
CI / Compare build size (push) Has been skipped
In the screens that use `DisplayApp *app` and pass it to a child item,
or use the reference just in the constructor. Afterwards the `app`
member is not used. So remove it from the private member variables.

Completely remove `app` parameter from `SettingDisplay` constructor as
it is unused.
2024-09-29 19:39:14 +02:00
NeroBurner a0cd439efc
Alarm persist to flash (#1367)
Some checks failed
CI / build-firmware (push) Successful in 13m37s
CI / build-simulator (push) Failing after 4s
CI / get-base-ref-size (push) Has been skipped
CI / Compare build size (push) Has been skipped
* AlarmController: Add saving alarm time to file

Save the set alarm time to the SPI NOR flash, so it does not reset to
the default value when the watch resets, e.g. due to watchdog timeout
or reflashing of a new version of InfiniTime.

Just like the `Settings.h` `LoadSettingsFromFile()` the previous alarm
at boot (if available) and `SaveSettingsToFile()` the current alarm when
the `Alarm.h` screen is closed (only if the settings have changed).

The alarm-settings file is stored in `.system/alarm.dat`. The `.system`
folder is created if it doesn't yet exist.

Fixes: https://github.com/InfiniTimeOrg/InfiniTime/issues/1330

* alarmController: close .system dir after usage

Close the `lfs_dir` object for the `.system` dir after usage. Otherwise
on the second changed alarm the system will lockup because the `.system`
dir is already open and was never closed.

---------

Co-authored-by: Galdor Takacs <g@ldor.de>
2024-09-28 08:14:08 +02:00
Reinhold Gschweicher 997e4cee8c Hrs3300: fix includes for std::begin/std::end
Some checks failed
CI / build-firmware (push) Successful in 5m35s
CI / build-simulator (push) Failing after 2s
CI / get-base-ref-size (push) Has been skipped
CI / Compare build size (push) Has been skipped
Fix for Hrs3300 PR about Atomic HRS reads:
https://github.com/InfiniTimeOrg/InfiniTime/pull/1845

We use `std::begin` and `std::end`, but we don't include one of the
headers that define those functions.
See https://en.cppreference.com/w/cpp/iterator/begin for a list of
headers that define `std::begin` and `std::end`.

Starting with GCC 14 this leads to a compilation error presumably
because they cleaned up their headers.

Fix code by inlcuding `<iterator>`
2024-09-22 16:15:48 +02:00
mark9064 ad3bf49c7b
Atomic HRS reads (#1845)
Some checks failed
CI / build-firmware (push) Successful in 5m25s
CI / build-simulator (push) Failing after 2s
CI / get-base-ref-size (push) Has been skipped
CI / Compare build size (push) Has been skipped
- Combine the reading of all `HRS3300` registers into one I2C read so data is not partial
- Downsizes both HRS and ALS to 16bit as the sensor does not generate larger than
  16bit values in its current configuration
  - Increasing the resolution by 1 bit doubles the sensor acquisition time,
    since we are already at 10Hz we are never going to use a higher resolution
  - The PPG algorithm buffers for ALS/HRS are already 16bit anyway
- Remove functions for setting gain / drive that are unused throughout the codebase
- Calculate constants with constexpr
2024-09-22 00:29:15 +02:00
mark9064 7ca0418c82 Refactor doNotGoToSleep to a wakelock counter
Some checks failed
CI / build-firmware (push) Successful in 6m20s
CI / build-simulator (push) Failing after 22s
CI / get-base-ref-size (push) Has been skipped
CI / Compare build size (push) Has been skipped
2024-09-21 22:45:57 +02:00
mark9064 c3d05901a0 Refactor SystemTask state handling for resilience
State transitions now happen immediately where possible
This simplifies state management in general,
and prevents bugs such as the chime issue from occurring in the first place
2024-09-21 21:08:07 +02:00
mark9064 b3756e45fa Remove unused method declarations 2024-09-21 21:08:07 +02:00
Felipe Martinez 21c3b1ebb0 Use stop variable instead of return 2024-07-09 23:03:43 +00:00
Felipe Martinez c59e29a5c8 Add conditionals 2024-07-09 23:02:14 +00:00
Felipe Martinez 23b1bfcb9a Remove logs 2024-07-09 23:01:55 +00:00
Felipe Martinez 86c8fd882d Get time components 2024-07-09 22:26:26 +00:00
Felipe Martinez 47522994f5 Add arcs; push object when setting property 2024-07-09 21:19:29 +00:00
Felipe Martinez 700e9a4f6d Store status icons in unique_ptr 2024-07-09 20:53:29 +00:00
Felipe Martinez 9bc6c820e9 Tweaks 2024-07-08 21:37:32 +00:00
Felipe Martinez ff4607c6da Add PushCurrentTicks 2024-07-08 21:37:23 +00:00
Felipe Martinez b55b59ea13 Set object parent 2024-07-08 20:06:33 +00:00
Felipe Martinez b7cdd48157 Add buttons and event handlers 2024-07-08 19:39:22 +00:00
Felipe Martinez 06fffa6df2 Destroy status icons on exit 2024-07-08 13:51:44 +00:00
Felipe Martinez c5183c0cba Find program size 2024-07-08 13:02:32 +00:00
Felipe Martinez 9bc75c5d3c Add FS::FileSeek method with whence 2024-07-08 13:01:27 +00:00
Felipe Martinez 3556687e2e Read program from external flash 2024-07-08 12:49:58 +00:00
Felipe Martinez 46c25e83bc Add asm_assert macro 2024-07-08 12:49:30 +00:00
Felipe Martinez c9f22468ce Use enum class 2024-07-08 11:25:30 +00:00
Felipe Martinez d6b51579e5 Add Equals and Negate 2024-07-08 11:14:14 +00:00
Felipe Martinez 408f4bd6dc Ensure division order 2024-07-08 02:11:40 +00:00
Felipe Martinez d3706722e0 Add status icons 2024-07-08 02:05:57 +00:00
Felipe Martinez 1751c4d5b4 Add fonts array 2024-07-08 00:59:00 +00:00
Felipe Martinez db53157c21 Cleanup 2024-07-08 00:53:17 +00:00
Felipe Martinez 2adeb39800 Add RealignObject 2024-07-08 00:52:42 +00:00
Felipe Martinez 562035dcab Add ASM app to apps list 2024-07-08 00:42:21 +00:00
Felipe Martinez e9db08e589 Include ASM screen 2024-07-07 22:14:17 +00:00
Felipe Martinez 29f1a93839 Add datetime formatting 2024-07-07 22:08:00 +00:00
Felipe Martinez bf13db05ce Use inheritance for values 2024-07-07 21:01:23 +00:00
Felipe Martinez 7ac66dc237 Rename obj to lvobj 2024-07-07 18:50:05 +00:00
Felipe Martinez 23729eff99 Use shared_ptrs 2024-07-07 18:49:19 +00:00
Felipe Martinez 8d5ac4f4c2 Add call 2024-07-07 16:45:53 +00:00
Felipe Martinez 0faece9e4b Add refreshing 2024-07-07 16:36:59 +00:00
Felipe Martinez e677d1cb42 Concat strings 2024-07-07 13:34:11 +00:00
Felipe Martinez c7d58fc97a Add arithmetic 2024-07-07 12:52:24 +00:00
Felipe Martinez 350567a6d2 Add asm_assert 2024-07-07 12:51:13 +00:00
Felipe Martinez f173f1b1d5 Add locals 2024-07-07 12:44:32 +00:00
Felipe Martinez c4bea51725 Add ASM screen 2024-07-06 01:57:04 +00:00
Felipe Martinez b6f4234517 Move motorController.Init call to DisplayApp::Start 2024-06-02 02:57:36 +02:00
45 changed files with 1263 additions and 233 deletions

View file

@ -397,6 +397,7 @@ list(APPEND SOURCE_FILES
displayapp/screens/Alarm.cpp
displayapp/screens/Styles.cpp
displayapp/screens/WeatherSymbols.cpp
displayapp/screens/ASM.cpp
displayapp/Colors.cpp
displayapp/widgets/Counter.cpp
displayapp/widgets/PageIndicator.cpp
@ -478,6 +479,7 @@ list(APPEND SOURCE_FILES
systemtask/SystemTask.cpp
systemtask/SystemMonitor.cpp
systemtask/WakeLock.cpp
drivers/TwiMaster.cpp
heartratetask/HeartRateTask.cpp
@ -542,6 +544,7 @@ list(APPEND RECOVERY_SOURCE_FILES
systemtask/SystemTask.cpp
systemtask/SystemMonitor.cpp
systemtask/WakeLock.cpp
drivers/TwiMaster.cpp
components/rle/RleDecoder.cpp
components/heartrate/HeartRateController.cpp
@ -660,6 +663,7 @@ set(INCLUDE_FILES
displayapp/InfiniTimeTheme.h
systemtask/SystemTask.h
systemtask/SystemMonitor.h
systemtask/WakeLock.h
displayapp/screens/Symbols.h
drivers/TwiMaster.h
heartratetask/HeartRateTask.h

View file

@ -19,11 +19,13 @@
#include "systemtask/SystemTask.h"
#include "task.h"
#include <chrono>
#include <libraries/log/nrf_log.h>
using namespace Pinetime::Controllers;
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 {
@ -36,11 +38,28 @@ namespace {
void AlarmController::Init(System::SystemTask* systemTask) {
this->systemTask = systemTask;
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) {
hours = alarmHr;
minutes = alarmMin;
if (alarm.hours == alarmHr && alarm.minutes == alarmMin) {
return;
}
alarm.hours = alarmHr;
alarm.minutes = alarmMin;
alarmChanged = true;
}
void AlarmController::ScheduleAlarm() {
@ -53,18 +72,19 @@ void AlarmController::ScheduleAlarm() {
tm* tmAlarmTime = std::localtime(&ttAlarmTime);
// 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;
// tm_wday doesn't update automatically
tmAlarmTime->tm_wday = (tmAlarmTime->tm_wday + 1) % 7;
}
tmAlarmTime->tm_hour = hours;
tmAlarmTime->tm_min = minutes;
tmAlarmTime->tm_hour = alarm.hours;
tmAlarmTime->tm_min = alarm.minutes;
tmAlarmTime->tm_sec = 0;
// 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
tmAlarmTime->tm_mday += 1;
} else if (tmAlarmTime->tm_wday == 6) { // Saturday, shift 2 days
@ -79,7 +99,10 @@ void AlarmController::ScheduleAlarm() {
xTimerChangePeriod(alarmTimer, secondsToAlarm * configTICK_RATE_HZ, 0);
xTimerStart(alarmTimer, 0);
state = AlarmState::Set;
if (!alarm.isEnabled) {
alarm.isEnabled = true;
alarmChanged = true;
}
}
uint32_t AlarmController::SecondsToAlarm() const {
@ -88,20 +111,72 @@ uint32_t AlarmController::SecondsToAlarm() const {
void AlarmController::DisableAlarm() {
xTimerStop(alarmTimer, 0);
state = AlarmState::Not_Set;
isAlerting = false;
if (alarm.isEnabled) {
alarm.isEnabled = false;
alarmChanged = true;
}
}
void AlarmController::SetOffAlarmNow() {
state = AlarmState::Alerting;
isAlerting = true;
systemTask->PushMessage(System::Messages::SetOffAlarm);
}
void AlarmController::StopAlerting() {
// Alarm state is off unless this is a recurring alarm
if (recurrence == RecurType::None) {
state = AlarmState::Not_Set;
isAlerting = false;
// Disable alarm unless it is recurring
if (alarm.recurrence == RecurType::None) {
alarm.isEnabled = false;
alarmChanged = true;
} else {
// set next instance
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);
}

View file

@ -30,47 +30,65 @@ namespace Pinetime {
namespace Controllers {
class AlarmController {
public:
AlarmController(Controllers::DateTime& dateTimeController);
AlarmController(Controllers::DateTime& dateTimeController, Controllers::FS& fs);
void Init(System::SystemTask* systemTask);
void SaveAlarm();
void SetAlarmTime(uint8_t alarmHr, uint8_t alarmMin);
void ScheduleAlarm();
void DisableAlarm();
void SetOffAlarmNow();
uint32_t SecondsToAlarm() const;
void StopAlerting();
enum class AlarmState { Not_Set, Set, Alerting };
enum class RecurType { None, Daily, Weekdays };
uint8_t Hours() const {
return hours;
return alarm.hours;
}
uint8_t Minutes() const {
return minutes;
return alarm.minutes;
}
AlarmState State() const {
return state;
bool IsAlerting() const {
return isAlerting;
}
bool IsEnabled() const {
return alarm.isEnabled;
}
RecurType Recurrence() const {
return recurrence;
return alarm.recurrence;
}
void SetRecurrence(RecurType recurType) {
recurrence = recurType;
}
void SetRecurrence(RecurType recurrence);
private:
Controllers::DateTime& dateTimeController;
System::SystemTask* systemTask = nullptr;
TimerHandle_t alarmTimer;
// Versions 255 is reserved for now, so the version field can be made
// bigger, should it ever be needed.
static constexpr uint8_t alarmFormatVersion = 1;
struct AlarmSettings {
uint8_t version = alarmFormatVersion;
uint8_t hours = 7;
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;
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;
};
}
}

View file

@ -124,9 +124,11 @@ int DfuService::WritePacketHandler(uint16_t connectionHandle, os_mbuf* om) {
bootloaderSize,
applicationSize);
// wait until SystemTask has finished waking up all devices
while (systemTask.IsSleeping()) {
vTaskDelay(50); // 50ms
// Wait until SystemTask has disabled sleeping
// This isn't quite correct, as we don't actually know
// if BleFirmwareUpdateStarted has been received yet
while (!systemTask.IsSleepDisabled()) {
vTaskDelay(pdMS_TO_TICKS(5));
}
dfuImage.Erase();

View file

@ -18,6 +18,8 @@
#include "components/ble/MusicService.h"
#include "components/ble/NimbleController.h"
#include <cstring>
#include <FreeRTOS.h>
#include <task.h>
namespace {
// 0000yyxx-78fc-48fe-8e23-433b3a1942d0

View file

@ -25,6 +25,7 @@
#include <host/ble_uuid.h>
#undef max
#undef min
#include <FreeRTOS.h>
namespace Pinetime {
namespace Controllers {

View file

@ -454,9 +454,15 @@ void NimbleController::PersistBond(struct ble_gap_conn_desc& desc) {
/* Wakeup Spi and SpiNorFlash before accessing the file system
* This should be fixed in the FS driver
*/
systemTask.PushMessage(Pinetime::System::Messages::GoToRunning);
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;

View file

@ -70,7 +70,11 @@ int FS::FileWrite(lfs_file_t* file_p, const uint8_t* buff, uint32_t size) {
}
int FS::FileSeek(lfs_file_t* file_p, uint32_t pos) {
return lfs_file_seek(&lfs, file_p, pos, LFS_SEEK_SET);
return FileSeek(file_p, pos, LFS_SEEK_SET);
}
int FS::FileSeek(lfs_file_t* file_p, uint32_t pos, int whence) {
return lfs_file_seek(&lfs, file_p, pos, whence);
}
int FS::FileDelete(const char* fileName) {

View file

@ -17,6 +17,7 @@ namespace Pinetime {
int FileRead(lfs_file_t* file_p, uint8_t* buff, uint32_t size);
int FileWrite(lfs_file_t* file_p, const uint8_t* buff, uint32_t size);
int FileSeek(lfs_file_t* file_p, uint32_t pos);
int FileSeek(lfs_file_t* file_p, uint32_t pos, int whence);
int FileDelete(const char* fileName);

View file

@ -142,7 +142,7 @@ Ppg::Ppg() {
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) {
dataHRS[dataIndex++] = hrs;
}

View file

@ -14,7 +14,7 @@ namespace Pinetime {
class Ppg {
public:
Ppg();
int8_t Preprocess(uint32_t hrs, uint32_t als);
int8_t Preprocess(uint16_t hrs, uint16_t als);
int HeartRate();
void Reset(bool resetDaqBuffer);
static constexpr int deltaTms = 100;

View file

@ -255,9 +255,20 @@ void DisplayApp::Refresh() {
isDimmed = true;
brightnessController.Set(Controllers::BrightnessController::Levels::Low);
}
if (IsPastSleepTime()) {
systemTask->PushMessage(System::Messages::GoToSleep);
state = States::Idle;
if (IsPastSleepTime() && uxQueueMessagesWaiting(msgQueue) == 0) {
PushMessageToSystemTask(System::Messages::GoToSleep);
// 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) {
isDimmed = false;
@ -273,6 +284,9 @@ void DisplayApp::Refresh() {
if (xQueueReceive(msgQueue, &msg, queueTimeout) == pdTRUE) {
switch (msg) {
case Messages::GoToSleep:
if (state != States::Running) {
break;
}
while (brightnessController.Level() != Controllers::BrightnessController::Levels::Low) {
brightnessController.Lower();
vTaskDelay(100);
@ -307,6 +321,9 @@ void DisplayApp::Refresh() {
lv_disp_trig_activity(nullptr);
break;
case Messages::GoToRunning:
if (state == States::Running) {
break;
}
if (settingsController.GetAlwaysOnDisplay()) {
lcd.LowPowerOff();
} else {
@ -565,7 +582,7 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio
currentScreen = std::make_unique<Screens::SettingWakeUp>(settingsController);
break;
case Apps::SettingDisplay:
currentScreen = std::make_unique<Screens::SettingDisplay>(this, settingsController);
currentScreen = std::make_unique<Screens::SettingDisplay>(settingsController);
break;
case Apps::SettingSteps:
currentScreen = std::make_unique<Screens::SettingSteps>(settingsController);

View file

@ -2,6 +2,7 @@
#include "displayapp/apps/Apps.h"
#include "Controllers.h"
#include "displayapp/screens/ASM.h"
#include "displayapp/screens/Alarm.h"
#include "displayapp/screens/Dice.h"
#include "displayapp/screens/Timer.h"

View file

@ -42,7 +42,8 @@ namespace Pinetime {
SettingChimes,
SettingShakeThreshold,
SettingBluetooth,
Error
Error,
ASM
};
enum class WatchFace : uint8_t {

View file

@ -2,6 +2,7 @@ if(DEFINED ENABLE_USERAPPS)
set(USERAPP_TYPES ${ENABLE_USERAPPS} CACHE STRING "List of user apps to build into the firmware")
else ()
set(DEFAULT_USER_APP_TYPES "Apps::StopWatch")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::ASM")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Alarm")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Timer")
set(DEFAULT_USER_APP_TYPES "${DEFAULT_USER_APP_TYPES}, Apps::Steps")

View file

@ -0,0 +1,564 @@
#include "ASM.h"
#include <libraries/log/nrf_log.h>
#include <assert.h>
#include <cstdio>
using namespace Pinetime::Applications::Screens;
constexpr lv_font_t* fonts[] = {
&fontawesome_weathericons,
&jetbrains_mono_42,
&jetbrains_mono_76,
&jetbrains_mono_bold_20,
&jetbrains_mono_extrabold_compressed,
&lv_font_sys_48,
&open_sans_light,
};
constexpr int num_fonts = sizeof(fonts) / sizeof(fonts[0]);
constexpr uint32_t handler_return_pc_mark = 1 << 31;
struct CallbackInfo {
ASM* instance;
uint32_t callback_pc;
};
static void event_handler(lv_obj_t* obj, lv_event_t event) {
CallbackInfo* cbInfo = static_cast<CallbackInfo*>(lv_obj_get_user_data(obj));
cbInfo->instance->OnObjectEvent(obj, event);
}
ASM::ASM(Controllers::DateTime& dateTimeController,
const Controllers::Battery& batteryController,
const Controllers::Ble& bleController,
Controllers::FS& fsController)
: dateTimeController(dateTimeController), batteryController(batteryController), bleController(bleController), fs(fsController) {
int result = fsController.FileOpen(&file, "program.bin", LFS_O_RDONLY);
asm_assert(result >= 0);
result = fsController.FileSeek(&file, 0, LFS_SEEK_END);
asm_assert(result >= 0);
program_size = result;
fsController.FileSeek(&file, 0, LFS_SEEK_SET);
populate_cache(0);
Refresh();
}
ASM::~ASM() {
if (taskRefresh != nullptr) {
lv_task_del(taskRefresh);
}
if (statusIcons) {
lv_obj_del(statusIcons->GetObject());
}
fs.FileClose(&file);
// We don't need to clean the screen since all objects are deleted when their shared_ptr is dropped
// lv_obj_clean(lv_scr_act());
}
void ASM::populate_cache(size_t pos) {
int result = fs.FileSeek(&file, pos);
asm_assert(result >= 0);
result = fs.FileRead(&file, cache, cache_size);
asm_assert(result >= 0);
cache_start = pos;
}
void ASM::run() {
bool stop = false;
while (!stop && pc < program_size) {
OpcodeShort opcode = static_cast<OpcodeShort>(read_byte(pc));
if (static_cast<uint8_t>(opcode) & (1 << 7)) {
// Long opcode
OpcodeLong opcode = static_cast<OpcodeLong>(read_u16(pc));
pc += 2;
switch (opcode) {
default:
NRF_LOG_ERROR("Unknown opcode: 0x%04X", opcode);
break;
}
} else {
pc++;
switch (opcode) {
case OpcodeShort::WaitRefresh:
stop = true;
break;
case OpcodeShort::Push0:
push(std::make_shared<ValueInteger>(0));
break;
case OpcodeShort::PushU8:
push(std::make_shared<ValueInteger>(read_byte(pc)));
pc++;
break;
case OpcodeShort::PushU16:
push(std::make_shared<ValueInteger>(read_u16(pc)));
pc += 2;
break;
case OpcodeShort::PushU24:
push(std::make_shared<ValueInteger>(read_u24(pc)));
pc += 3;
break;
case OpcodeShort::PushU32:
push(std::make_shared<ValueInteger>(read_u32(pc)));
pc += 4;
break;
case OpcodeShort::PushEmptyString:
push(std::make_shared<ValueString>(new char[1] {0}, 1));
break;
case OpcodeShort::Duplicate:
push(stack[stack_pointer - 1]);
break;
case OpcodeShort::Pop:
pop();
break;
case OpcodeShort::LoadString: {
uint32_t ptr = pop_uint32();
int length = read_byte(ptr);
char* text = new char[length + 1];
text[length] = '\0';
for (int i = 0; i < length; i++) {
text[i] = read_byte(ptr + 1 + i);
}
push(std::make_shared<ValueString>(text, length + 1));
break;
}
case OpcodeShort::StoreLocal:
locals[read_byte(pc++)] = pop();
break;
case OpcodeShort::LoadLocal:
push(locals[read_byte(pc++)]);
break;
case OpcodeShort::Branch: {
if (doBranch(pop_uint32()))
stop = true;
break;
}
case OpcodeShort::BranchIfTrue: {
uint32_t to = pop_uint32();
auto cond = pop();
if (cond->isTruthy() && doBranch(to))
stop = true;
break;
}
case OpcodeShort::Call: {
uint32_t next = pc;
pc = pop_uint32();
push(std::make_shared<ValueInteger>(next));
break;
}
case OpcodeShort::StartPeriodicRefresh:
if (taskRefresh == nullptr) {
taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this);
}
break;
case OpcodeShort::StopPeriodicRefresh:
if (taskRefresh != nullptr) {
lv_task_del(taskRefresh);
taskRefresh = nullptr;
}
break;
case OpcodeShort::SetLabelText: {
auto str = pop<ValueString>(String);
auto obj = pop<ValueLvglObject>(LvglObject);
lv_label_set_text(obj->obj, str->str);
push(obj);
break;
}
case OpcodeShort::SetArcRange: {
uint32_t max = pop_uint32();
uint32_t min = pop_uint32();
auto obj = pop<ValueLvglObject>(LvglObject);
lv_arc_set_range(obj->obj, min, max);
push(obj);
break;
}
case OpcodeShort::SetArcRotation: {
uint32_t rot = pop_uint32();
auto obj = pop<ValueLvglObject>(LvglObject);
lv_arc_set_rotation(obj->obj, rot);
push(obj);
break;
}
case OpcodeShort::SetArcBgAngles: {
uint32_t end = pop_uint32();
uint32_t start = pop_uint32();
auto obj = pop<ValueLvglObject>(LvglObject);
lv_arc_set_bg_angles(obj->obj, start, end);
push(obj);
break;
}
case OpcodeShort::SetArcAdjustable: {
auto val = pop();
auto obj = pop<ValueLvglObject>(LvglObject);
lv_arc_set_adjustable(obj->obj, val->isTruthy());
push(obj);
break;
}
case OpcodeShort::SetArcStartAngle: {
uint32_t angle = pop_uint32();
auto obj = pop<ValueLvglObject>(LvglObject);
lv_arc_set_start_angle(obj->obj, angle);
push(obj);
break;
}
case OpcodeShort::SetArcValue: {
uint32_t value = pop_uint32();
auto obj = pop<ValueLvglObject>(LvglObject);
lv_arc_set_value(obj->obj, value);
push(obj);
break;
}
case OpcodeShort::CreateLabel:
push(std::make_shared<ValueLvglObject>(lv_label_create(lv_scr_act(), NULL)));
break;
case OpcodeShort::CreateButton:
push(std::make_shared<ValueLvglObject>(lv_btn_create(lv_scr_act(), NULL)));
break;
case OpcodeShort::CreateArc:
push(std::make_shared<ValueLvglObject>(lv_arc_create(lv_scr_act(), NULL)));
break;
case OpcodeShort::SetObjectAlign: {
int16_t y = pop_uint32();
int16_t x = pop_uint32();
uint8_t align = pop_uint32();
auto obj = pop<ValueLvglObject>(LvglObject);
lv_obj_align(obj->obj, lv_scr_act(), align, x, y);
push(obj);
break;
}
case OpcodeShort::SetObjectSize: {
int16_t h = pop_uint32();
int16_t w = pop_uint32();
auto obj = pop<ValueLvglObject>(LvglObject);
lv_obj_set_size(obj->obj, w, h);
push(obj);
break;
}
case OpcodeShort::SetObjectParent: {
auto parent = pop<ValueLvglObject>(LvglObject);
auto child = pop<ValueLvglObject>(LvglObject);
lv_obj_set_parent(child->obj, parent->obj);
push(child);
break;
}
case OpcodeShort::SetStyleLocalInt:
case OpcodeShort::SetStyleLocalFont:
case OpcodeShort::SetStyleLocalColor: {
uint32_t value = pop_uint32();
uint32_t prop = pop_uint32();
uint32_t part = pop_uint32();
auto obj = pop<ValueLvglObject>(LvglObject);
switch (opcode) {
case OpcodeShort::SetStyleLocalInt:
_lv_obj_set_style_local_int(obj->obj, part, prop, value);
break;
case OpcodeShort::SetStyleLocalColor:
_lv_obj_set_style_local_color(obj->obj, part, prop, lv_color_hex(value));
break;
case OpcodeShort::SetStyleLocalFont: {
if (value < num_fonts) {
_lv_obj_set_style_local_ptr(obj->obj, part, prop, fonts[value]);
}
break;
}
default:
break;
}
push(obj);
break;
}
case OpcodeShort::SetEventHandler: {
uint32_t cb_pc = pop_uint32();
auto obj = pop<ValueLvglObject>(LvglObject);
CallbackInfo* cb = new CallbackInfo {this, cb_pc};
lv_obj_set_user_data(obj->obj, cb);
lv_obj_set_event_cb(obj->obj, event_handler);
push(obj);
break;
}
case OpcodeShort::Add:
push(std::make_shared<ValueInteger>(pop_uint32() + pop_uint32()));
break;
case OpcodeShort::Subtract: {
uint32_t b = pop_uint32();
uint32_t a = pop_uint32();
push(std::make_shared<ValueInteger>(a - b));
break;
}
case OpcodeShort::Multiply:
push(std::make_shared<ValueInteger>(pop_uint32() * pop_uint32()));
break;
case OpcodeShort::Divide: {
uint32_t b = pop_uint32();
uint32_t a = pop_uint32();
push(std::make_shared<ValueInteger>(a / b));
break;
}
case OpcodeShort::GrowString: {
auto len = pop_uint32();
auto str = pop<ValueString>(String);
size_t new_cap = len + str->capacity;
char* new_str = new char[new_cap];
memcpy(new_str, str->str, str->capacity);
push(std::make_shared<ValueString>(new_str, new_cap));
break;
}
case OpcodeShort::ClearString: {
auto str = pop<ValueString>(String);
if (str->capacity > 0)
str->str[0] = '\0';
push(str);
break;
}
case OpcodeShort::Concat: {
auto b = pop();
auto a = pop();
if (a->type() == String && b->type() == String) {
auto aString = static_cast<ValueString*>(a.get());
auto bString = static_cast<ValueString*>(b.get());
int len_a = strlen(aString->str);
int len_b = strlen(bString->str);
size_t new_len = len_a + len_b + 1;
if (aString->capacity >= new_len) {
strcat(aString->str, bString->str);
push(a);
} else {
char* s = new char[new_len + 1];
strcpy(s, aString->str);
strcat(s, bString->str);
push(std::make_shared<ValueString>(s, new_len + 1));
}
} else if (a->type() == String && b->type() == Integer) {
auto aString = static_cast<ValueString*>(a.get());
auto bInt = static_cast<ValueInteger*>(b.get());
size_t aLen = strlen(aString->str);
size_t need_cap = aLen + 12 + 1;
if (aString->capacity - aLen >= need_cap) {
snprintf(aString->str + aLen, aString->capacity - aLen, "%lu", bInt->i);
push(a);
} else {
char* s = new char[need_cap];
memcpy(s, aString->str, aLen);
snprintf(s + aLen, need_cap - aLen, "%lu", bInt->i);
push(std::make_shared<ValueString>(s, need_cap));
}
} else {
asm_assert(false);
}
break;
}
case OpcodeShort::PushCurrentDateTime: {
auto time = dateTimeController.CurrentDateTime();
std::tm tm {
.tm_sec = dateTimeController.Seconds(),
.tm_min = dateTimeController.Minutes(),
.tm_hour = dateTimeController.Hours(),
.tm_mday = dateTimeController.Day(),
.tm_mon = static_cast<int>(dateTimeController.Month()) - 1,
.tm_year = dateTimeController.Year() - 1900,
.tm_wday = static_cast<int>(dateTimeController.DayOfWeek()),
.tm_yday = dateTimeController.DayOfYear() - 1,
};
push(std::make_shared<ValueDateTime>(time, tm));
break;
}
case OpcodeShort::PushCurrentTicks:
push(std::make_shared<ValueInteger>((xTaskGetTickCount() * configTICK_RATE_HZ) / 1000));
break;
case OpcodeShort::FormatDateTime: {
auto fmt = pop<ValueString>(String);
auto time = pop<ValueDateTime>(DateTime);
constexpr int max_len = 16;
char* str = new char[max_len]; // TODO: Allow user to reuse string in stack
strftime(str, max_len, fmt->str, &time->tm);
push(std::make_shared<ValueString>(str, max_len));
break;
}
case OpcodeShort::RealignObject:
lv_obj_realign(pop<ValueLvglObject>(LvglObject)->obj);
break;
case OpcodeShort::ShowStatusIcons:
if (!statusIcons) {
statusIcons = std::make_unique<Widgets::StatusIcons>(batteryController, bleController);
statusIcons->Create();
}
break;
case OpcodeShort::Equals: {
auto b = pop();
auto a = pop();
push(std::make_shared<ValueInteger>(a.get()->compare(b.get()) == 0 ? 1 : 0));
break;
}
case OpcodeShort::Greater: {
auto b = pop();
auto a = pop();
push(std::make_shared<ValueInteger>(a.get()->compare(b.get()) > 0 ? 1 : 0));
break;
}
case OpcodeShort::Lesser: {
auto b = pop();
auto a = pop();
push(std::make_shared<ValueInteger>(a.get()->compare(b.get()) < 0 ? 1 : 0));
break;
}
case OpcodeShort::Negate:
push(std::make_shared<ValueInteger>(pop().get()->isTruthy() ? 0 : 1));
break;
case OpcodeShort::GetDateTimeHour: {
auto time = pop<ValueDateTime>(DateTime);
push(std::make_shared<ValueInteger>(time->tm.tm_hour));
break;
}
case OpcodeShort::GetDateTimeMinute: {
auto time = pop<ValueDateTime>(DateTime);
push(std::make_shared<ValueInteger>(time->tm.tm_min));
break;
}
case OpcodeShort::GetDateTimeSecond: {
auto time = pop<ValueDateTime>(DateTime);
push(std::make_shared<ValueInteger>(time->tm.tm_sec));
break;
}
default:
NRF_LOG_ERROR("Unknown opcode: 0x%02X", opcode);
break;
}
}
}
}
void ASM::Refresh() {
run();
if (statusIcons) {
statusIcons->Update();
}
}
void ASM::_asm_assert(bool condition, const char* msg) {
if (!condition) {
// TODO: Handle better
if (msg)
NRF_LOG_ERROR("Assertion failed: %s", msg);
for (;;) {
}
}
}
bool ASM::doBranch(uint32_t to) {
if ((to & handler_return_pc_mark) != 0) {
pc = to & ~handler_return_pc_mark;
return true;
}
pc = to;
return false;
}
void ASM::OnObjectEvent(lv_obj_t* obj, lv_event_t event) {
if (event != LV_EVENT_CLICKED)
return;
CallbackInfo* cb = static_cast<CallbackInfo*>(lv_obj_get_user_data(obj));
if (cb) {
push(std::make_shared<ValueInteger>(pc | handler_return_pc_mark));
pc = cb->callback_pc;
run();
}
}

View file

@ -0,0 +1,319 @@
#pragma once
#include "displayapp/screens/Screen.h"
#include "displayapp/apps/Apps.h"
#include "displayapp/Controllers.h"
#include "displayapp/widgets/StatusIcons.h"
#include "components/datetime/DateTimeController.h"
#include "Symbols.h"
#include <cassert>
#include <memory>
#include <chrono>
#if DEBUG
#define STRINGIZE_DETAIL(x) #x
#define STRINGIZE(x) STRINGIZE_DETAIL(x)
#define asm_assert(condition) _asm_assert(condition, __FILE__ ":" STRINGIZE(__LINE__) " " #condition)
#else
#define asm_assert(condition) _asm_assert(condition, NULL)
#endif
namespace Pinetime {
namespace Applications {
namespace Screens {
class ASM : public Screen {
public:
ASM(Controllers::DateTime&, const Controllers::Battery&, const Controllers::Ble&, Controllers::FS&);
~ASM();
void Refresh() override;
void OnObjectEvent(lv_obj_t* obj, lv_event_t event);
private:
static constexpr int num_slots = 16;
static constexpr int max_locals = 16;
static constexpr int stack_size = 32;
static constexpr int cache_size = 16;
enum DataType : uint8_t { Integer, String, LvglObject, DateTime };
// TODO: Use fancy C++ type stuff
struct Value {
virtual DataType type() = 0;
virtual int compare(Value* other) = 0;
virtual bool isTruthy() = 0;
};
struct ValueInteger : public Value {
uint32_t i;
ValueInteger(uint32_t i) : i(i) {
}
DataType type() override {
return Integer;
}
int compare(Value* other) override {
if (other->type() != Integer) {
return -1;
}
auto otherInt = static_cast<ValueInteger*>(other)->i;
if (i < otherInt) {
return -1;
} else if (i > otherInt) {
return 1;
}
return 0;
}
bool isTruthy() override {
return i != 0;
}
};
struct ValueString : public Value {
char* str;
uint16_t capacity;
ValueString(char* str, uint16_t cap) : str(str), capacity(cap) {
}
~ValueString() {
delete[] str;
}
DataType type() override {
return String;
}
int compare(Value* other) override {
if (other->type() != String) {
return -1;
}
return strcmp(str, static_cast<ValueString*>(other)->str);
}
bool isTruthy() override {
return capacity > 0 && str[0] != '\0';
}
};
struct ValueLvglObject : public Value {
lv_obj_t* obj;
ValueLvglObject(lv_obj_t* obj) : obj(obj) {
}
~ValueLvglObject() {
lv_obj_del(obj);
}
DataType type() override {
return LvglObject;
}
int compare(Value*) override {
return -1;
}
bool isTruthy() override {
return obj != nullptr;
}
};
struct ValueDateTime : public Value {
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> time;
std::tm tm;
ValueDateTime(std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> time, std::tm tm)
: time(time), tm(tm) {
}
DataType type() override {
return DateTime;
}
int compare(Value* other) override {
if (other->type() != DateTime) {
return -1;
}
auto otherTime = static_cast<ValueDateTime*>(other)->time;
if (time < otherTime) {
return -1;
} else if (time > otherTime) {
return 1;
}
return 0;
}
bool isTruthy() override {
return true;
}
};
enum class OpcodeShort : uint8_t {
StoreLocal,
LoadLocal,
Branch,
BranchIfTrue,
Call,
Push0,
PushU8,
PushU16,
PushU24,
PushU32,
PushEmptyString,
PushCurrentDateTime,
PushCurrentTicks,
Duplicate,
Pop,
LoadString,
StartPeriodicRefresh,
StopPeriodicRefresh,
ShowStatusIcons,
CreateLabel,
CreateButton,
CreateArc,
SetLabelText,
SetArcRange,
SetArcRotation,
SetArcBgAngles,
SetArcAdjustable,
SetArcStartAngle,
SetArcValue,
SetObjectAlign,
SetObjectSize,
SetObjectParent,
SetStyleLocalInt,
SetStyleLocalColor,
SetStyleLocalOpa,
SetStyleLocalFont,
SetEventHandler,
RealignObject,
WaitRefresh,
Add,
Subtract,
Multiply,
Divide,
Equals,
Greater,
Lesser,
Negate,
GrowString,
ClearString,
Concat,
FormatDateTime,
GetDateTimeHour,
GetDateTimeMinute,
GetDateTimeSecond
};
enum class OpcodeLong : uint16_t {};
void populate_cache(size_t pos);
uint8_t read_byte(size_t pos) {
if (pos < cache_start || pos >= cache_start + cache_size) {
populate_cache(pos);
}
return cache[pos - cache_start];
}
uint16_t read_u16(size_t pos) {
return static_cast<uint16_t>(read_byte(pos + 1) << 8 | read_byte(pos));
}
uint32_t read_u24(size_t pos) {
return static_cast<uint32_t>(read_byte(pos + 2) << 16 | read_byte(pos + 1) << 8 | read_byte(pos));
}
uint32_t read_u32(size_t pos) {
return static_cast<uint32_t>(read_byte(pos + 3) << 24 | read_byte(pos + 2) << 16 | read_byte(pos + 1) << 8 | read_byte(pos));
}
lfs_file_t file;
uint8_t cache[cache_size];
size_t cache_start;
size_t program_size;
size_t pc = 0;
std::shared_ptr<Value> locals[max_locals] = {};
std::shared_ptr<Value> stack[stack_size] = {};
uint8_t stack_pointer = 0;
lv_task_t* taskRefresh = nullptr;
Controllers::DateTime& dateTimeController;
const Controllers::Battery& batteryController;
const Controllers::Ble& bleController;
Controllers::FS& fs;
std::unique_ptr<Widgets::StatusIcons> statusIcons;
void run();
void _asm_assert(bool condition, const char* msg);
bool doBranch(uint32_t to);
std::shared_ptr<Value> pop() {
asm_assert(stack_pointer > 0);
stack_pointer--;
auto v = stack[stack_pointer];
stack[stack_pointer] = nullptr;
return v;
}
template <typename T>
std::shared_ptr<T> pop(DataType wantType)
requires(std::is_base_of_v<Value, T>)
{
auto v = pop();
asm_assert(v->type() == wantType);
return std::static_pointer_cast<T>(v);
}
uint32_t pop_uint32() {
return pop<ValueInteger>(Integer)->i;
}
void push(std::shared_ptr<Value> v) {
asm_assert(stack_pointer < stack_size);
stack[stack_pointer++] = v;
}
};
}
template <>
struct AppTraits<Apps::ASM> {
static constexpr Apps app = Apps::ASM;
static constexpr const char* icon = Screens::Symbols::eye;
static Screens::Screen* Create(AppControllers& controllers) {
return new Screens::ASM(controllers.dateTimeController,
controllers.batteryController,
controllers.bleController,
controllers.filesystem);
};
};
};
}

View file

@ -48,7 +48,7 @@ Alarm::Alarm(Controllers::AlarmController& alarmController,
Controllers::Settings::ClockType clockType,
System::SystemTask& systemTask,
Controllers::MotorController& motorController)
: alarmController {alarmController}, systemTask {systemTask}, motorController {motorController} {
: alarmController {alarmController}, wakeLock(systemTask), motorController {motorController} {
hourCounter.Create();
lv_obj_align(hourCounter.GetObject(), nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0);
@ -117,7 +117,7 @@ Alarm::Alarm(Controllers::AlarmController& alarmController,
UpdateAlarmTime();
if (alarmController.State() == Controllers::AlarmController::AlarmState::Alerting) {
if (alarmController.IsAlerting()) {
SetAlerting();
} else {
SetSwitchState(LV_ANIM_OFF);
@ -125,14 +125,15 @@ Alarm::Alarm(Controllers::AlarmController& alarmController,
}
Alarm::~Alarm() {
if (alarmController.State() == AlarmController::AlarmState::Alerting) {
if (alarmController.IsAlerting()) {
StopAlerting();
}
lv_obj_clean(lv_scr_act());
alarmController.SaveAlarm();
}
void Alarm::DisableAlarm() {
if (alarmController.State() == AlarmController::AlarmState::Set) {
if (alarmController.IsEnabled()) {
alarmController.DisableAlarm();
lv_switch_off(enableSwitch, LV_ANIM_ON);
}
@ -172,7 +173,7 @@ bool Alarm::OnButtonPushed() {
HideInfo();
return true;
}
if (alarmController.State() == AlarmController::AlarmState::Alerting) {
if (alarmController.IsAlerting()) {
StopAlerting();
return true;
}
@ -181,7 +182,7 @@ bool Alarm::OnButtonPushed() {
bool Alarm::OnTouchEvent(Pinetime::Applications::TouchEvents event) {
// 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() {
@ -205,7 +206,7 @@ void Alarm::SetAlerting() {
lv_obj_set_hidden(btnStop, false);
taskStopAlarm = lv_task_create(StopAlarmTaskCallback, pdMS_TO_TICKS(60 * 1000), LV_TASK_PRIO_MID, this);
motorController.StartRinging();
systemTask.PushMessage(System::Messages::DisableSleeping);
wakeLock.Lock();
}
void Alarm::StopAlerting() {
@ -216,21 +217,16 @@ void Alarm::StopAlerting() {
lv_task_del(taskStopAlarm);
taskStopAlarm = nullptr;
}
systemTask.PushMessage(System::Messages::EnableSleeping);
wakeLock.Release();
lv_obj_set_hidden(enableSwitch, false);
lv_obj_set_hidden(btnStop, true);
}
void Alarm::SetSwitchState(lv_anim_enable_t anim) {
switch (alarmController.State()) {
case AlarmController::AlarmState::Set:
if (alarmController.IsEnabled()) {
lv_switch_on(enableSwitch, anim);
break;
case AlarmController::AlarmState::Not_Set:
} else {
lv_switch_off(enableSwitch, anim);
break;
default:
break;
}
}
@ -247,7 +243,7 @@ void Alarm::ShowInfo() {
txtMessage = lv_label_create(btnMessage, nullptr);
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 daysToAlarm = timeToAlarm / 86400;

View file

@ -22,6 +22,7 @@
#include "displayapp/screens/Screen.h"
#include "displayapp/widgets/Counter.h"
#include "displayapp/Controllers.h"
#include "systemtask/WakeLock.h"
#include "Symbols.h"
namespace Pinetime {
@ -43,7 +44,7 @@ namespace Pinetime {
private:
Controllers::AlarmController& alarmController;
System::SystemTask& systemTask;
System::WakeLock wakeLock;
Controllers::MotorController& motorController;
lv_obj_t *btnStop, *txtStop, *btnRecur, *txtRecur, *btnInfo, *enableSwitch;

View file

@ -15,7 +15,7 @@ namespace {
}
FlashLight::FlashLight(System::SystemTask& systemTask, Controllers::BrightnessController& brightnessController)
: systemTask {systemTask}, brightnessController {brightnessController} {
: wakeLock(systemTask), brightnessController {brightnessController} {
previousBrightnessLevel = brightnessController.Level();
brightnessController.Set(Controllers::BrightnessController::Levels::Low);
@ -47,14 +47,13 @@ FlashLight::FlashLight(System::SystemTask& systemTask, Controllers::BrightnessCo
backgroundAction->user_data = this;
lv_obj_set_event_cb(backgroundAction, EventHandler);
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
wakeLock.Lock();
}
FlashLight::~FlashLight() {
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);
brightnessController.Set(previousBrightnessLevel);
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
}
void FlashLight::SetColors() {

View file

@ -3,6 +3,7 @@
#include "displayapp/screens/Screen.h"
#include "components/brightness/BrightnessController.h"
#include "systemtask/SystemTask.h"
#include "systemtask/WakeLock.h"
#include <cstdint>
#include <lvgl/lvgl.h>
@ -23,7 +24,7 @@ namespace Pinetime {
void SetIndicators();
void SetColors();
Pinetime::System::SystemTask& systemTask;
Pinetime::System::WakeLock wakeLock;
Controllers::BrightnessController& brightnessController;
Controllers::BrightnessController::Levels brightnessLevel = Controllers::BrightnessController::Levels::High;

View file

@ -29,7 +29,7 @@ namespace {
}
HeartRate::HeartRate(Controllers::HeartRateController& heartRateController, System::SystemTask& systemTask)
: heartRateController {heartRateController}, systemTask {systemTask} {
: heartRateController {heartRateController}, wakeLock(systemTask) {
bool isHrRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped;
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);
UpdateStartStopButton(isHrRunning);
if (isHrRunning) {
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
wakeLock.Lock();
}
taskRefresh = lv_task_create(RefreshTaskCallback, 100, LV_TASK_PRIO_MID, this);
@ -72,7 +72,6 @@ HeartRate::HeartRate(Controllers::HeartRateController& heartRateController, Syst
HeartRate::~HeartRate() {
lv_task_del(taskRefresh);
lv_obj_clean(lv_scr_act());
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
}
void HeartRate::Refresh() {
@ -101,12 +100,12 @@ void HeartRate::OnStartStopEvent(lv_event_t event) {
if (heartRateController.State() == Controllers::HeartRateController::States::Stopped) {
heartRateController.Start();
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);
} else {
heartRateController.Stop();
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);
}
}

View file

@ -4,6 +4,7 @@
#include <chrono>
#include "displayapp/screens/Screen.h"
#include "systemtask/SystemTask.h"
#include "systemtask/WakeLock.h"
#include "Symbols.h"
#include <lvgl/src/lv_core/lv_style.h>
#include <lvgl/src/lv_core/lv_obj.h>
@ -27,7 +28,7 @@ namespace Pinetime {
private:
Controllers::HeartRateController& heartRateController;
Pinetime::System::SystemTask& systemTask;
Pinetime::System::WakeLock wakeLock;
void UpdateStartStopButton(bool isRunning);
lv_obj_t* label_hr;
lv_obj_t* label_bpm;

View file

@ -22,7 +22,7 @@ namespace {
}
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->user_data = this;
@ -72,7 +72,6 @@ Metronome::Metronome(Controllers::MotorController& motorController, System::Syst
Metronome::~Metronome() {
lv_task_del(taskRefresh);
systemTask.PushMessage(System::Messages::EnableSleeping);
lv_obj_clean(lv_scr_act());
}
@ -128,12 +127,12 @@ void Metronome::OnEvent(lv_obj_t* obj, lv_event_t event) {
metronomeStarted = !metronomeStarted;
if (metronomeStarted) {
lv_label_set_text_static(lblPlayPause, Symbols::pause);
systemTask.PushMessage(System::Messages::DisableSleeping);
wakeLock.Lock();
startTime = xTaskGetTickCount();
counter = 1;
} else {
lv_label_set_text_static(lblPlayPause, Symbols::play);
systemTask.PushMessage(System::Messages::EnableSleeping);
wakeLock.Release();
}
}
break;

View file

@ -1,6 +1,7 @@
#pragma once
#include "systemtask/SystemTask.h"
#include "systemtask/WakeLock.h"
#include "components/motor/MotorController.h"
#include "displayapp/screens/Screen.h"
#include "Symbols.h"
@ -21,7 +22,7 @@ namespace Pinetime {
TickType_t startTime = 0;
TickType_t tappedTime = 0;
Controllers::MotorController& motorController;
System::SystemTask& systemTask;
System::WakeLock wakeLock;
int16_t bpm = 120;
uint8_t bpb = 4;
uint8_t counter = 1;

View file

@ -20,7 +20,7 @@ Notifications::Notifications(DisplayApp* app,
notificationManager {notificationManager},
alertNotificationService {alertNotificationService},
motorController {motorController},
systemTask {systemTask},
wakeLock(systemTask),
mode {mode} {
notificationManager.ClearNewNotificationFlag();
@ -40,7 +40,7 @@ Notifications::Notifications(DisplayApp* app,
validDisplay = false;
}
if (mode == Modes::Preview) {
systemTask.PushMessage(System::Messages::DisableSleeping);
wakeLock.Lock();
if (notification.category == Controllers::NotificationManager::Categories::IncomingCall) {
motorController.StartRinging();
} else {
@ -65,7 +65,6 @@ Notifications::~Notifications() {
lv_task_del(taskRefresh);
// make sure we stop any vibrations before exiting
motorController.StopRinging();
systemTask.PushMessage(System::Messages::EnableSleeping);
lv_obj_clean(lv_scr_act());
}
@ -120,7 +119,7 @@ void Notifications::Refresh() {
}
void Notifications::OnPreviewInteraction() {
systemTask.PushMessage(System::Messages::EnableSleeping);
wakeLock.Release();
motorController.StopRinging();
if (timeoutLine != nullptr) {
lv_obj_del(timeoutLine);

View file

@ -8,6 +8,7 @@
#include "components/ble/NotificationManager.h"
#include "components/motor/MotorController.h"
#include "systemtask/SystemTask.h"
#include "systemtask/WakeLock.h"
namespace Pinetime {
namespace Controllers {
@ -73,7 +74,7 @@ namespace Pinetime {
Pinetime::Controllers::NotificationManager& notificationManager;
Pinetime::Controllers::AlertNotificationService& alertNotificationService;
Pinetime::Controllers::MotorController& motorController;
System::SystemTask& systemTask;
System::WakeLock wakeLock;
Modes mode = Modes::Normal;
std::unique_ptr<NotificationItem> currentItem;
Pinetime::Controllers::NotificationManager::Notification::Id currentId;

View file

@ -34,7 +34,7 @@ namespace {
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 btnHeight = 80;
btnPlayPause = lv_btn_create(lv_scr_act(), nullptr);
@ -79,7 +79,6 @@ StopWatch::StopWatch(System::SystemTask& systemTask) : systemTask {systemTask} {
StopWatch::~StopWatch() {
lv_task_del(taskRefresh);
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
lv_obj_clean(lv_scr_act());
}
@ -135,7 +134,7 @@ void StopWatch::Start() {
SetInterfaceRunning();
startTime = xTaskGetTickCount();
currentState = States::Running;
systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping);
wakeLock.Lock();
}
void StopWatch::Pause() {
@ -145,7 +144,7 @@ void StopWatch::Pause() {
oldTimeElapsed = laps[lapsDone];
blinkTime = xTaskGetTickCount() + blinkInterval;
currentState = States::Halted;
systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping);
wakeLock.Release();
}
void StopWatch::Refresh() {

View file

@ -7,6 +7,7 @@
#include "portmacro_cmsis.h"
#include "systemtask/SystemTask.h"
#include "systemtask/WakeLock.h"
#include "displayapp/apps/Apps.h"
#include "displayapp/Controllers.h"
#include "Symbols.h"
@ -43,7 +44,7 @@ namespace Pinetime {
void Start();
void Pause();
Pinetime::System::SystemTask& systemTask;
Pinetime::System::WakeLock wakeLock;
States currentState = States::Init;
TickType_t startTime;
TickType_t oldTimeElapsed = 0;

View file

@ -40,8 +40,7 @@ SystemInfo::SystemInfo(Pinetime::Applications::DisplayApp* app,
Pinetime::Controllers::MotionController& motionController,
const Pinetime::Drivers::Cst816S& touchPanel,
const Pinetime::Drivers::SpiNorFlash& spiNorFlash)
: app {app},
dateTimeController {dateTimeController},
: dateTimeController {dateTimeController},
batteryController {batteryController},
brightnessController {brightnessController},
bleController {bleController},

View file

@ -35,7 +35,6 @@ namespace Pinetime {
bool OnTouchEvent(TouchEvents event) override;
private:
DisplayApp* app;
Pinetime::Controllers::DateTime& dateTimeController;
const Pinetime::Controllers::Battery& batteryController;
Pinetime::Controllers::BrightnessController& brightnessController;

View file

@ -24,8 +24,7 @@ namespace {
constexpr std::array<uint16_t, 6> SettingDisplay::options;
SettingDisplay::SettingDisplay(Pinetime::Applications::DisplayApp* app, Pinetime::Controllers::Settings& settingsController)
: app {app}, settingsController {settingsController} {
SettingDisplay::SettingDisplay(Pinetime::Controllers::Settings& settingsController) : settingsController {settingsController} {
lv_obj_t* container1 = lv_cont_create(lv_scr_act(), nullptr);

View file

@ -14,14 +14,13 @@ namespace Pinetime {
class SettingDisplay : public Screen {
public:
SettingDisplay(DisplayApp* app, Pinetime::Controllers::Settings& settingsController);
SettingDisplay(Pinetime::Controllers::Settings& settingsController);
~SettingDisplay() override;
void UpdateSelected(lv_obj_t* object, lv_event_t event);
void ToggleAlwaysOn();
private:
DisplayApp* app;
static constexpr std::array<uint16_t, 6> options = {5000, 7000, 10000, 15000, 20000, 30000};
Controllers::Settings& settingsController;

View file

@ -15,8 +15,7 @@ bool SettingSetDateTime::OnTouchEvent(Pinetime::Applications::TouchEvents event)
SettingSetDateTime::SettingSetDateTime(Pinetime::Applications::DisplayApp* app,
Pinetime::Controllers::DateTime& dateTimeController,
Pinetime::Controllers::Settings& settingsController)
: app {app},
dateTimeController {dateTimeController},
: dateTimeController {dateTimeController},
settingsController {settingsController},
screens {app,
0,

View file

@ -20,7 +20,6 @@ namespace Pinetime {
void Quit();
private:
DisplayApp* app;
Controllers::DateTime& dateTimeController;
Controllers::Settings& settingsController;

View file

@ -54,8 +54,7 @@ SettingWatchFace::SettingWatchFace(Pinetime::Applications::DisplayApp* app,
std::array<Screens::SettingWatchFace::Item, UserWatchFaceTypes::Count>&& watchfaceItems,
Pinetime::Controllers::Settings& settingsController,
Pinetime::Controllers::FS& filesystem)
: app {app},
watchfaceItems {std::move(watchfaceItems)},
: watchfaceItems {std::move(watchfaceItems)},
settingsController {settingsController},
filesystem {filesystem},
screens {app, 0, CreateScreenList(), Screens::ScreenListModes::UpDown} {

View file

@ -34,7 +34,6 @@ namespace Pinetime {
bool OnTouchEvent(TouchEvents event) override;
private:
DisplayApp* app;
auto CreateScreenList() const;
std::unique_ptr<Screen> CreateScreen(unsigned int screenNum) const;

View file

@ -6,6 +6,7 @@
#include "drivers/Hrs3300.h"
#include <algorithm>
#include <iterator>
#include <nrf_gpio.h>
#include <FreeRTOS.h>
@ -67,40 +68,37 @@ void Hrs3300::Disable() {
WriteRegister(static_cast<uint8_t>(Registers::PDriver), 0);
}
uint32_t Hrs3300::ReadHrs() {
auto m = ReadRegister(static_cast<uint8_t>(Registers::C0DataM));
auto h = ReadRegister(static_cast<uint8_t>(Registers::C0DataH));
auto l = ReadRegister(static_cast<uint8_t>(Registers::C0dataL));
return ((l & 0x30) << 12) | (m << 8) | ((h & 0x0f) << 4) | (l & 0x0f);
}
Hrs3300::PackedHrsAls Hrs3300::ReadHrsAls() {
constexpr Registers dataRegisters[] =
{Registers::C1dataM, Registers::C0DataM, Registers::C0DataH, Registers::C1dataH, Registers::C1dataL, Registers::C0dataL};
// Calculate smallest register address
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() {
auto m = ReadRegister(static_cast<uint8_t>(Registers::C1dataM));
auto h = ReadRegister(static_cast<uint8_t>(Registers::C1dataH));
auto l = ReadRegister(static_cast<uint8_t>(Registers::C1dataL));
return ((h & 0x3f) << 11) | (m << 3) | (l & 0x07);
}
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;
Hrs3300::PackedHrsAls res;
uint8_t buf[length];
auto ret = twiMaster.Read(twiAddress, baseOffset, buf, length);
if (ret != TwiMaster::ErrorCodes::NoError) {
NRF_LOG_INFO("READ ERROR");
}
// 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) {
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);
return res;
}
void Hrs3300::WriteRegister(uint8_t reg, uint8_t data) {

View file

@ -21,6 +21,11 @@ namespace Pinetime {
Hgain = 0x17
};
struct PackedHrsAls {
uint16_t hrs;
uint16_t als;
};
Hrs3300(TwiMaster& twiMaster, uint8_t twiAddress);
Hrs3300(const Hrs3300&) = delete;
Hrs3300& operator=(const Hrs3300&) = delete;
@ -30,10 +35,7 @@ namespace Pinetime {
void Init();
void Enable();
void Disable();
uint32_t ReadHrs();
uint32_t ReadAls();
void SetGain(uint8_t gain);
void SetDrive(uint8_t drive);
PackedHrsAls ReadHrsAls();
private:
TwiMaster& twiMaster;

View file

@ -70,7 +70,8 @@ void HeartRateTask::Work() {
}
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();
// If ambient light detected or a reset requested (bpm < 0)

View file

@ -104,7 +104,7 @@ Pinetime::Controllers::DateTime dateTimeController {settingsController};
Pinetime::Drivers::Watchdog watchdog;
Pinetime::Controllers::NotificationManager notificationManager;
Pinetime::Controllers::MotionController motionController;
Pinetime::Controllers::AlarmController alarmController {dateTimeController};
Pinetime::Controllers::AlarmController alarmController {dateTimeController, fs};
Pinetime::Controllers::TouchHandler touchHandler;
Pinetime::Controllers::ButtonHandler buttonHandler;
Pinetime::Controllers::BrightnessController brightnessController {};

View file

@ -189,36 +189,14 @@ void SystemTask::Work() {
if (xQueueReceive(systemTasksMsgQueue, &msg, 100) == pdTRUE) {
switch (msg) {
case Messages::EnableSleeping:
// Make sure that exiting an app doesn't enable sleeping,
// if the exiting was caused by a firmware update
if (!bleController.IsFirmwareUpdating()) {
doNotGoToSleep = false;
}
wakeLocksHeld--;
break;
case Messages::DisableSleeping:
doNotGoToSleep = true;
GoToRunning();
wakeLocksHeld++;
break;
case Messages::GoToRunning:
// SPI doesn't go to sleep for always on mode
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;
GoToRunning();
break;
case Messages::TouchWakeUp: {
if (touchHandler.ProcessTouchInfo(touchPanel.GetTouchInfo())) {
@ -235,31 +213,23 @@ void SystemTask::Work() {
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);
GoToSleep();
break;
case Messages::OnNewTime:
if (alarmController.State() == Controllers::AlarmController::AlarmState::Set) {
if (alarmController.IsEnabled()) {
alarmController.ScheduleAlarm();
}
break;
case Messages::OnNewNotification:
if (settingsController.GetNotificationStatus() == Pinetime::Controllers::Settings::Notification::On) {
if (state == SystemTaskState::Sleeping) {
if (IsSleeping()) {
GoToRunning();
}
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:
@ -268,29 +238,25 @@ void SystemTask::Work() {
bleDiscoveryTimer = 5;
break;
case Messages::BleFirmwareUpdateStarted:
doNotGoToSleep = true;
if (state == SystemTaskState::Sleeping) {
GoToRunning();
}
wakeLocksHeld++;
displayApp.PushMessage(Pinetime::Applications::Display::Messages::BleFirmwareUpdateStarted);
break;
case Messages::BleFirmwareUpdateFinished:
if (bleController.State() == Pinetime::Controllers::Ble::FirmwareUpdateStates::Validated) {
NVIC_SystemReset();
}
doNotGoToSleep = false;
wakeLocksHeld--;
break;
case Messages::StartFileTransfer:
NRF_LOG_INFO("[systemtask] FS Started");
doNotGoToSleep = true;
if (state == SystemTaskState::Sleeping) {
GoToRunning();
}
wakeLocksHeld++;
// TODO add intent of fs access icon or something
break;
case Messages::StopFileTransfer:
NRF_LOG_INFO("[systemtask] FS Stopped");
doNotGoToSleep = false;
wakeLocksHeld--;
// TODO add intent of fs access icon or something
break;
case Messages::OnTouchEvent:
@ -318,6 +284,13 @@ void SystemTask::Work() {
HandleButtonAction(action);
} break;
case Messages::OnDisplayTaskSleeping:
// The state was set to GoingToSleep when GoToSleep() was called
// If the state is no longer GoingToSleep, we have since transitioned back to Running
// In this case absorb the OnDisplayTaskSleeping
// as DisplayApp is about to receive GoToRunning
if (state != SystemTaskState::GoingToSleep) {
break;
}
if (BootloaderVersion::IsValid()) {
// 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.
@ -344,39 +317,23 @@ void SystemTask::Work() {
case Messages::OnNewHour:
using Pinetime::Controllers::AlarmController;
if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep &&
settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::Hours &&
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) {
settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::Hours && !alarmController.IsAlerting()) {
GoToRunning();
PushMessage(msg);
} else {
displayApp.PushMessage(Pinetime::Applications::Display::Messages::Chime);
}
}
break;
case Messages::OnNewHalfHour:
using Pinetime::Controllers::AlarmController;
if (settingsController.GetNotificationStatus() != Controllers::Settings::Notification::Sleep &&
settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::HalfHours &&
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) {
settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::HalfHours && !alarmController.IsAlerting()) {
GoToRunning();
PushMessage(msg);
} else {
displayApp.PushMessage(Pinetime::Applications::Display::Messages::Chime);
}
}
break;
case Messages::OnChargingEvent:
batteryController.ReadPowerState();
displayApp.PushMessage(Applications::Display::Messages::OnChargingEvent);
if (state == SystemTaskState::Sleeping) {
GoToRunning();
}
displayApp.PushMessage(Applications::Display::Messages::OnChargingEvent);
break;
case Messages::MeasureBatteryTimerExpired:
batteryController.MeasureVoltage();
@ -385,9 +342,7 @@ void SystemTask::Work() {
nimbleController.NotifyBatteryLevel(batteryController.PercentRemaining());
break;
case Messages::OnPairing:
if (state == SystemTaskState::Sleeping) {
GoToRunning();
}
displayApp.PushMessage(Pinetime::Applications::Display::Messages::ShowPairingKey);
break;
case Messages::BleRadioEnableToggle:
@ -422,12 +377,48 @@ void SystemTask::Work() {
#pragma clang diagnostic pop
}
void SystemTask::UpdateMotion() {
if (state == SystemTaskState::GoingToSleep || state == SystemTaskState::WakingUp) {
void SystemTask::GoToRunning() {
if (state == SystemTaskState::Running) {
return;
}
// SPI doesn't go to sleep for always on mode
if (!settingsController.GetAlwaysOnDisplay()) {
spi.Wakeup();
}
if (state == SystemTaskState::Sleeping && !(settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist) ||
// Double Tap needs the touch screen to be in normal mode
if (!settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) {
touchPanel.Wakeup();
}
spiNorFlash.Wakeup();
displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToRunning);
heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::WakeUp);
if (bleController.IsRadioEnabled() && !bleController.IsConnected()) {
nimbleController.RestartFastAdv();
}
state = SystemTaskState::Running;
};
void SystemTask::GoToSleep() {
if (IsSleeping()) {
return;
}
if (IsSleepDisabled()) {
return;
}
NRF_LOG_INFO("[systemtask] Going to sleep");
displayApp.PushMessage(Pinetime::Applications::Display::Messages::GoToSleep);
heartRateApp.PushMessage(Pinetime::Applications::HeartRateTask::Messages::GoToSleep);
state = SystemTaskState::GoingToSleep;
};
void SystemTask::UpdateMotion() {
if (IsSleeping() && !(settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::RaiseWrist) ||
settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::Shake) ||
motionController.GetService()->IsMotionNotificationSubscribed())) {
return;
@ -452,7 +443,7 @@ void SystemTask::UpdateMotion() {
}
if (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::LowerWrist) && state == SystemTaskState::Running &&
motionController.ShouldLowerSleep()) {
PushMessage(Messages::GoToSleep);
GoToSleep();
}
}
@ -468,7 +459,7 @@ void SystemTask::HandleButtonAction(Controllers::ButtonActions action) {
switch (action) {
case Actions::Click:
// 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);
}
break;
@ -488,17 +479,10 @@ void SystemTask::HandleButtonAction(Controllers::ButtonActions action) {
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) {
} else {
if (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::SingleTap) or
settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::DoubleTap)) {
PushMessage(Messages::TouchWakeUp);
@ -507,10 +491,6 @@ void SystemTask::OnTouchEvent() {
}
void SystemTask::PushMessage(System::Messages msg) {
if (msg == Messages::GoToSleep && !doNotGoToSleep) {
state = SystemTaskState::GoingToSleep;
}
if (in_isr()) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xQueueSendFromISR(systemTasksMsgQueue, &msg, &xHigherPriorityTaskWoken);

View file

@ -52,7 +52,7 @@ namespace Pinetime {
namespace System {
class SystemTask {
public:
enum class SystemTaskState { Sleeping, Running, GoingToSleep, WakingUp };
enum class SystemTaskState { Sleeping, Running, GoingToSleep };
SystemTask(Drivers::SpiMaster& spi,
Pinetime::Drivers::SpiNorFlash& spiNorFlash,
Drivers::TwiMaster& twiMaster,
@ -79,11 +79,8 @@ namespace Pinetime {
void OnTouchEvent();
void OnIdle();
void OnDim();
bool IsSleepDisabled() {
return doNotGoToSleep;
return wakeLocksHeld > 0;
}
Pinetime::Controllers::NimbleController& nimble() {
@ -91,7 +88,7 @@ namespace Pinetime {
};
bool IsSleeping() const {
return state == SystemTaskState::Sleeping || state == SystemTaskState::WakingUp;
return state != SystemTaskState::Running;
}
private:
@ -127,13 +124,14 @@ namespace Pinetime {
bool isBleDiscoveryTimerRunning = false;
uint8_t bleDiscoveryTimer = 0;
TimerHandle_t measureBatteryTimer;
bool doNotGoToSleep = false;
uint8_t wakeLocksHeld = 0;
SystemTaskState state = SystemTaskState::Running;
void HandleButtonAction(Controllers::ButtonActions action);
bool fastWakeUpDone = false;
void GoToRunning();
void GoToSleep();
void UpdateMotion();
bool stepCounterMustBeReset = false;
static constexpr TickType_t batteryMeasurementPeriod = pdMS_TO_TICKS(10 * 60 * 1000);

View 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
View 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;
};
}
}