mirror of
https://github.com/InfiniTimeOrg/InfiniTime.git
synced 2024-10-22 15:11:51 +02:00
Compare commits
45 commits
a0c8c49c09
...
52061ad50f
Author | SHA1 | Date | |
---|---|---|---|
52061ad50f | |||
a2356f2f4a | |||
3db4e012ce | |||
a0cd439efc | |||
997e4cee8c | |||
ad3bf49c7b | |||
7ca0418c82 | |||
c3d05901a0 | |||
b3756e45fa | |||
21c3b1ebb0 | |||
c59e29a5c8 | |||
23b1bfcb9a | |||
86c8fd882d | |||
47522994f5 | |||
700e9a4f6d | |||
9bc6c820e9 | |||
ff4607c6da | |||
b55b59ea13 | |||
b7cdd48157 | |||
06fffa6df2 | |||
c5183c0cba | |||
9bc75c5d3c | |||
3556687e2e | |||
46c25e83bc | |||
c9f22468ce | |||
d6b51579e5 | |||
408f4bd6dc | |||
d3706722e0 | |||
1751c4d5b4 | |||
db53157c21 | |||
2adeb39800 | |||
562035dcab | |||
e9db08e589 | |||
29f1a93839 | |||
bf13db05ce | |||
7ac66dc237 | |||
23729eff99 | |||
8d5ac4f4c2 | |||
0faece9e4b | |||
e677d1cb42 | |||
c7d58fc97a | |||
350567a6d2 | |||
f173f1b1d5 | |||
c4bea51725 | |||
b6f4234517 |
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
// 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;
|
||||
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;
|
||||
uint8_t hours = 7;
|
||||
uint8_t minutes = 0;
|
||||
AlarmSettings alarm;
|
||||
std::chrono::time_point<std::chrono::system_clock, std::chrono::nanoseconds> alarmTime;
|
||||
AlarmState state = AlarmState::Not_Set;
|
||||
RecurType recurrence = RecurType::None;
|
||||
|
||||
void LoadSettingsFromFile();
|
||||
void SaveSettingsToFile() const;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <host/ble_uuid.h>
|
||||
#undef max
|
||||
#undef min
|
||||
#include <FreeRTOS.h>
|
||||
|
||||
namespace Pinetime {
|
||||
namespace Controllers {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -42,7 +42,8 @@ namespace Pinetime {
|
|||
SettingChimes,
|
||||
SettingShakeThreshold,
|
||||
SettingBluetooth,
|
||||
Error
|
||||
Error,
|
||||
ASM
|
||||
};
|
||||
|
||||
enum class WatchFace : uint8_t {
|
||||
|
|
|
@ -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")
|
||||
|
|
564
src/displayapp/screens/ASM.cpp
Normal file
564
src/displayapp/screens/ASM.cpp
Normal 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();
|
||||
}
|
||||
}
|
319
src/displayapp/screens/ASM.h
Normal file
319
src/displayapp/screens/ASM.h
Normal 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);
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -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:
|
||||
lv_switch_on(enableSwitch, anim);
|
||||
break;
|
||||
case AlarmController::AlarmState::Not_Set:
|
||||
lv_switch_off(enableSwitch, anim);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (alarmController.IsEnabled()) {
|
||||
lv_switch_on(enableSwitch, anim);
|
||||
} else {
|
||||
lv_switch_off(enableSwitch, anim);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -20,7 +20,6 @@ namespace Pinetime {
|
|||
void Quit();
|
||||
|
||||
private:
|
||||
DisplayApp* app;
|
||||
Controllers::DateTime& dateTimeController;
|
||||
Controllers::Settings& settingsController;
|
||||
|
||||
|
|
|
@ -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} {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
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) {
|
||||
GoToRunning();
|
||||
PushMessage(msg);
|
||||
} else {
|
||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::Chime);
|
||||
}
|
||||
settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::Hours && !alarmController.IsAlerting()) {
|
||||
GoToRunning();
|
||||
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) {
|
||||
GoToRunning();
|
||||
PushMessage(msg);
|
||||
} else {
|
||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::Chime);
|
||||
}
|
||||
settingsController.GetChimeOption() == Controllers::Settings::ChimesOption::HalfHours && !alarmController.IsAlerting()) {
|
||||
GoToRunning();
|
||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::Chime);
|
||||
}
|
||||
break;
|
||||
case Messages::OnChargingEvent:
|
||||
batteryController.ReadPowerState();
|
||||
GoToRunning();
|
||||
displayApp.PushMessage(Applications::Display::Messages::OnChargingEvent);
|
||||
if (state == SystemTaskState::Sleeping) {
|
||||
GoToRunning();
|
||||
}
|
||||
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();
|
||||
}
|
||||
GoToRunning();
|
||||
displayApp.PushMessage(Pinetime::Applications::Display::Messages::ShowPairingKey);
|
||||
break;
|
||||
case Messages::BleRadioEnableToggle:
|
||||
|
@ -422,14 +377,50 @@ 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) ||
|
||||
settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::Shake) ||
|
||||
motionController.GetService()->IsMotionNotificationSubscribed())) {
|
||||
// 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);
|
||||
|
|
|
@ -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);
|
||||
|
|
27
src/systemtask/WakeLock.cpp
Normal file
27
src/systemtask/WakeLock.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include "systemtask/WakeLock.h"
|
||||
|
||||
using namespace Pinetime::System;
|
||||
|
||||
WakeLock::WakeLock(SystemTask& systemTask) : systemTask {systemTask} {
|
||||
lockHeld = false;
|
||||
}
|
||||
|
||||
WakeLock::~WakeLock() {
|
||||
Release();
|
||||
}
|
||||
|
||||
void WakeLock::Lock() {
|
||||
if (lockHeld) {
|
||||
return;
|
||||
}
|
||||
systemTask.PushMessage(Messages::DisableSleeping);
|
||||
lockHeld = true;
|
||||
}
|
||||
|
||||
void WakeLock::Release() {
|
||||
if (!lockHeld) {
|
||||
return;
|
||||
}
|
||||
systemTask.PushMessage(Messages::EnableSleeping);
|
||||
lockHeld = false;
|
||||
}
|
19
src/systemtask/WakeLock.h
Normal file
19
src/systemtask/WakeLock.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "systemtask/SystemTask.h"
|
||||
|
||||
namespace Pinetime {
|
||||
namespace System {
|
||||
class WakeLock {
|
||||
public:
|
||||
WakeLock(SystemTask& systemTask);
|
||||
~WakeLock();
|
||||
void Lock();
|
||||
void Release();
|
||||
|
||||
private:
|
||||
bool lockHeld;
|
||||
SystemTask& systemTask;
|
||||
};
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue