diff --git a/.gitmodules b/.gitmodules index 7a4e307b..a6b4d5fd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,9 +4,6 @@ [submodule "src/libs/littlefs"] path = src/libs/littlefs url = https://github.com/littlefs-project/littlefs.git -[submodule "src/libs/QCBOR"] - path = src/libs/QCBOR - url = https://github.com/laurencelundblade/QCBOR.git [submodule "src/libs/arduinoFFT"] path = src/libs/arduinoFFT url = https://github.com/kosme/arduinoFFT.git diff --git a/README.md b/README.md index cc667f98..e8bbf172 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,14 @@ Fast open-source firmware for the [PineTime smartwatch](https://pine64.org/devic Here are pictures with and without alarm set : -![Meow alarm set](doc/ui/meow_alarmset.png "Meow WatchFace, alarm set") ![Meow alarm not set](doc/ui/meow_alarmnotset.png "Meow WatchFace, alarm not set") Original repo : [InfiniTime](https://github.com/InfiniTimeOrg/InfiniTime) - [Getting started with InfiniTime](doc/gettingStarted/gettingStarted-1.0.md) - [Updating the software](doc/gettingStarted/updating-software.md) - [About the firmware and bootloader](doc/gettingStarted/about-software.md) +- [Available apps](doc/gettingStarted/Applications.md) +- [Available watch faces](/doc/gettingStarted/Watchfaces.md) - [PineTimeStyle Watch face](https://pine64.org/documentation/PineTime/Watchfaces/PineTimeStyle) - [Weather integration](https://pine64.org/documentation/PineTime/Software/InfiniTime_weather/) @@ -34,7 +35,6 @@ Branches : - my-custom-infinitime : branch were I put things that I want for myself, like a watchface with paw instead of shoe icon for steps counter : -![Meow watchface](doc/ui/meow_alarmset.png "Meow watchface") ## New to InfiniTime? @@ -120,3 +120,4 @@ Here are some people I would like to highlight: - [Atc1441](https://github.com/atc1441/): He works on an Arduino based firmware for the Pinetime and many other smartwatches based on similar hardware. He was of great help when I was implementing support for the BMA421 motion sensor and I²C driver. - [Koen](https://github.com/bosmoment): He’s working on a firmware based on RiotOS. He integrated similar libs as me: NimBLE, LittleVGL,… His help was invaluable too! - [Lup Yuen Lee](https://github.com/lupyuen): He is everywhere: he works on a Rust firmware, builds a MCUBoot based bootloader for the Pinetime, designs a Flutter based companion app for smartphones and writes a lot of articles about the Pinetime! +>>>>>>>>> Temporary merge branch 2 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 587e43d5..093d5491 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -479,6 +479,7 @@ list(APPEND SOURCE_FILES systemtask/SystemTask.cpp systemtask/SystemMonitor.cpp + systemtask/WakeLock.cpp drivers/TwiMaster.cpp heartratetask/HeartRateTask.cpp @@ -543,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 @@ -661,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 @@ -861,7 +864,6 @@ target_compile_options(nrf-sdk PRIVATE $<$: ${RELEASE_FLAGS}> $<$: ${CXX_FLAGS}> $<$: ${ASM_FLAGS}> - -O3 ) # NimBLE diff --git a/src/components/alarm/AlarmController.cpp b/src/components/alarm/AlarmController.cpp index c4eb8ed0..7dbfb0e3 100644 --- a/src/components/alarm/AlarmController.cpp +++ b/src/components/alarm/AlarmController.cpp @@ -19,11 +19,13 @@ #include "systemtask/SystemTask.h" #include "task.h" #include +#include 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(&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(&alarm), sizeof(alarm)); + fs.FileClose(&alarmFile); + NRF_LOG_INFO("[AlarmController] Saved alarm settings with format version %u to file", alarm.version); +} diff --git a/src/components/alarm/AlarmController.h b/src/components/alarm/AlarmController.h index 8ac0de9a..278e9cdb 100644 --- a/src/components/alarm/AlarmController.h +++ b/src/components/alarm/AlarmController.h @@ -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 alarmTime; - AlarmState state = AlarmState::Not_Set; - RecurType recurrence = RecurType::None; + + void LoadSettingsFromFile(); + void SaveSettingsToFile() const; }; } } diff --git a/src/components/ble/DfuService.cpp b/src/components/ble/DfuService.cpp index b3f2ff10..2427513d 100644 --- a/src/components/ble/DfuService.cpp +++ b/src/components/ble/DfuService.cpp @@ -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(); diff --git a/src/components/ble/MusicService.cpp b/src/components/ble/MusicService.cpp index 94d97f46..43cbec70 100644 --- a/src/components/ble/MusicService.cpp +++ b/src/components/ble/MusicService.cpp @@ -18,6 +18,8 @@ #include "components/ble/MusicService.h" #include "components/ble/NimbleController.h" #include +#include +#include namespace { // 0000yyxx-78fc-48fe-8e23-433b3a1942d0 diff --git a/src/components/ble/MusicService.h b/src/components/ble/MusicService.h index f4b902fe..93d94a34 100644 --- a/src/components/ble/MusicService.h +++ b/src/components/ble/MusicService.h @@ -25,6 +25,7 @@ #include #undef max #undef min +#include namespace Pinetime { namespace Controllers { diff --git a/src/components/ble/NimbleController.cpp b/src/components/ble/NimbleController.cpp index f1411a3e..5059007a 100644 --- a/src/components/ble/NimbleController.cpp +++ b/src/components/ble/NimbleController.cpp @@ -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; diff --git a/src/components/heartrate/Ppg.cpp b/src/components/heartrate/Ppg.cpp index 3a6988ae..efbed852 100644 --- a/src/components/heartrate/Ppg.cpp +++ b/src/components/heartrate/Ppg.cpp @@ -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; } diff --git a/src/components/heartrate/Ppg.h b/src/components/heartrate/Ppg.h index 4492b2c2..373e7985 100644 --- a/src/components/heartrate/Ppg.h +++ b/src/components/heartrate/Ppg.h @@ -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; diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index 3be7656d..ff43bb81 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -209,21 +209,6 @@ void DisplayApp::Refresh() { LoadScreen(returnAppStack.Pop(), returnDirection); }; - auto DimScreen = [this]() { - if (brightnessController.Level() != Controllers::BrightnessController::Levels::Off) { - isDimmed = true; - brightnessController.Set(Controllers::BrightnessController::Levels::Low); - } - }; - - auto RestoreBrightness = [this]() { - if (brightnessController.Level() != Controllers::BrightnessController::Levels::Off) { - isDimmed = false; - lv_disp_trig_activity(nullptr); - ApplyBrightness(); - } - }; - auto IsPastDimTime = [this]() -> bool { return lv_disp_get_inactive_time(nullptr) >= pdMS_TO_TICKS(settingsController.GetScreenTimeOut() - 2000); }; @@ -267,14 +252,27 @@ void DisplayApp::Refresh() { if (!systemTask->IsSleepDisabled() && IsPastDimTime()) { if (!isDimmed) { - DimScreen(); + 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) { - RestoreBrightness(); + isDimmed = false; + ApplyBrightness(); } break; default: @@ -285,10 +283,10 @@ void DisplayApp::Refresh() { Messages msg; if (xQueueReceive(msgQueue, &msg, queueTimeout) == pdTRUE) { switch (msg) { - case Messages::DimScreen: - DimScreen(); - break; case Messages::GoToSleep: + if (state != States::Running) { + break; + } while (brightnessController.Level() != Controllers::BrightnessController::Levels::Low) { brightnessController.Lower(); vTaskDelay(100); @@ -323,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 { @@ -333,8 +334,7 @@ void DisplayApp::Refresh() { state = States::Running; break; case Messages::UpdateBleConnection: - // clockScreen.SetBleConnectionState(bleController.IsConnected() ? Screens::Clock::BleConnectionStates::Connected : - // Screens::Clock::BleConnectionStates::NotConnected); + // Only used for recovery firmware break; case Messages::NewNotification: LoadNewScreen(Apps::NotificationsPreview, DisplayApp::FullRefreshDirections::Down); @@ -449,16 +449,11 @@ void DisplayApp::Refresh() { case Messages::BleRadioEnableToggle: PushMessageToSystemTask(System::Messages::BleRadioEnableToggle); break; - case Messages::UpdateDateTime: - // Added to remove warning - // What should happen here? - break; case Messages::Chime: LoadNewScreen(Apps::Clock, DisplayApp::FullRefreshDirections::None); motorController.RunForDuration(35); break; case Messages::OnChargingEvent: - RestoreBrightness(); motorController.RunForDuration(15); break; } @@ -587,7 +582,7 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio currentScreen = std::make_unique(settingsController); break; case Apps::SettingDisplay: - currentScreen = std::make_unique(this, settingsController); + currentScreen = std::make_unique(settingsController); break; case Apps::SettingSteps: currentScreen = std::make_unique(settingsController); diff --git a/src/displayapp/Messages.h b/src/displayapp/Messages.h index 1418f6be..dcfff4c2 100644 --- a/src/displayapp/Messages.h +++ b/src/displayapp/Messages.h @@ -7,7 +7,6 @@ namespace Pinetime { enum class Messages : uint8_t { GoToSleep, GoToRunning, - UpdateDateTime, UpdateBleConnection, TouchEvent, ButtonPushed, @@ -17,7 +16,8 @@ namespace Pinetime { NewNotification, TimerDone, BleFirmwareUpdateStarted, - DimScreen, + // Resets the screen timeout timer when awake + // Does nothing when asleep NotifyDeviceActivity, ShowPairingKey, AlarmTriggered, diff --git a/src/displayapp/screens/Alarm.cpp b/src/displayapp/screens/Alarm.cpp index cbc702f3..b1e67363 100644 --- a/src/displayapp/screens/Alarm.cpp +++ b/src/displayapp/screens/Alarm.cpp @@ -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; diff --git a/src/displayapp/screens/Alarm.h b/src/displayapp/screens/Alarm.h index 444102cb..a875b275 100644 --- a/src/displayapp/screens/Alarm.h +++ b/src/displayapp/screens/Alarm.h @@ -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; diff --git a/src/displayapp/screens/FlashLight.cpp b/src/displayapp/screens/FlashLight.cpp index f169fac1..7e0caff1 100644 --- a/src/displayapp/screens/FlashLight.cpp +++ b/src/displayapp/screens/FlashLight.cpp @@ -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() { diff --git a/src/displayapp/screens/FlashLight.h b/src/displayapp/screens/FlashLight.h index c5404e93..00ef4a7e 100644 --- a/src/displayapp/screens/FlashLight.h +++ b/src/displayapp/screens/FlashLight.h @@ -3,6 +3,7 @@ #include "displayapp/screens/Screen.h" #include "components/brightness/BrightnessController.h" #include "systemtask/SystemTask.h" +#include "systemtask/WakeLock.h" #include #include @@ -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; diff --git a/src/displayapp/screens/HeartRate.cpp b/src/displayapp/screens/HeartRate.cpp index 9677be3b..1a84d349 100644 --- a/src/displayapp/screens/HeartRate.cpp +++ b/src/displayapp/screens/HeartRate.cpp @@ -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); } } diff --git a/src/displayapp/screens/HeartRate.h b/src/displayapp/screens/HeartRate.h index bf39209a..88b4918c 100644 --- a/src/displayapp/screens/HeartRate.h +++ b/src/displayapp/screens/HeartRate.h @@ -4,6 +4,7 @@ #include #include "displayapp/screens/Screen.h" #include "systemtask/SystemTask.h" +#include "systemtask/WakeLock.h" #include "Symbols.h" #include #include @@ -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; diff --git a/src/displayapp/screens/Metronome.cpp b/src/displayapp/screens/Metronome.cpp index 314fde73..6b758470 100644 --- a/src/displayapp/screens/Metronome.cpp +++ b/src/displayapp/screens/Metronome.cpp @@ -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; diff --git a/src/displayapp/screens/Metronome.h b/src/displayapp/screens/Metronome.h index c498048e..fab7ff87 100644 --- a/src/displayapp/screens/Metronome.h +++ b/src/displayapp/screens/Metronome.h @@ -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; diff --git a/src/displayapp/screens/Notifications.cpp b/src/displayapp/screens/Notifications.cpp index 037c43a7..45f72f2e 100644 --- a/src/displayapp/screens/Notifications.cpp +++ b/src/displayapp/screens/Notifications.cpp @@ -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()); } @@ -82,7 +81,6 @@ void Notifications::Refresh() { } else if (mode == Modes::Preview && dismissingNotification) { running = false; - currentItem = std::make_unique(alertNotificationService, motorController); } else if (dismissingNotification) { dismissingNotification = false; @@ -113,15 +111,15 @@ void Notifications::Refresh() { alertNotificationService, motorController); } else { - currentItem = std::make_unique(alertNotificationService, motorController); + running = false; } } - running = currentItem->IsRunning() && running; + running = running && currentItem->IsRunning(); } void Notifications::OnPreviewInteraction() { - systemTask.PushMessage(System::Messages::EnableSleeping); + wakeLock.Release(); motorController.StopRinging(); if (timeoutLine != nullptr) { lv_obj_del(timeoutLine); @@ -173,7 +171,9 @@ bool Notifications::OnTouchEvent(Pinetime::Applications::TouchEvents event) { } else if (nextMessage.valid) { currentId = nextMessage.id; } else { - // don't update id, won't be found be refresh and try to load latest message or no message box + // don't update id, notification manager will try to fetch + // but not find it. Refresh will try to load latest message + // or dismiss to watchface } DismissToBlack(); return true; diff --git a/src/displayapp/screens/Notifications.h b/src/displayapp/screens/Notifications.h index 114316b3..8488dc5b 100644 --- a/src/displayapp/screens/Notifications.h +++ b/src/displayapp/screens/Notifications.h @@ -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 currentItem; Pinetime::Controllers::NotificationManager::Notification::Id currentId; diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index f0359da4..ff852beb 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -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() { diff --git a/src/displayapp/screens/StopWatch.h b/src/displayapp/screens/StopWatch.h index 3386d042..55a178dc 100644 --- a/src/displayapp/screens/StopWatch.h +++ b/src/displayapp/screens/StopWatch.h @@ -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; diff --git a/src/displayapp/screens/SystemInfo.cpp b/src/displayapp/screens/SystemInfo.cpp index dd39f88a..d265fddd 100644 --- a/src/displayapp/screens/SystemInfo.cpp +++ b/src/displayapp/screens/SystemInfo.cpp @@ -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}, diff --git a/src/displayapp/screens/SystemInfo.h b/src/displayapp/screens/SystemInfo.h index 3129c463..301662dc 100644 --- a/src/displayapp/screens/SystemInfo.h +++ b/src/displayapp/screens/SystemInfo.h @@ -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; diff --git a/src/displayapp/screens/Timer.cpp b/src/displayapp/screens/Timer.cpp index a1ede6be..f6d5e73b 100644 --- a/src/displayapp/screens/Timer.cpp +++ b/src/displayapp/screens/Timer.cpp @@ -104,9 +104,7 @@ void Timer::UpdateMask() { void Timer::Refresh() { if (timer.IsRunning()) { - auto secondsRemaining = std::chrono::duration_cast(timer.GetTimeRemaining()); - minuteCounter.SetValue(secondsRemaining.count() / 60); - secondCounter.SetValue(secondsRemaining.count() % 60); + DisplayTime(); } else if (buttonPressing && xTaskGetTickCount() > pressTime + pdMS_TO_TICKS(150)) { lv_label_set_text_static(txtPlayPause, "Reset"); maskPosition += 15; @@ -119,6 +117,14 @@ void Timer::Refresh() { } } +void Timer::DisplayTime() { + displaySeconds = std::chrono::duration_cast(timer.GetTimeRemaining()); + if (displaySeconds.IsUpdated()) { + minuteCounter.SetValue(displaySeconds.Get().count() / 60); + secondCounter.SetValue(displaySeconds.Get().count() % 60); + } +} + void Timer::SetTimerRunning() { minuteCounter.HideControls(); secondCounter.HideControls(); @@ -133,9 +139,7 @@ void Timer::SetTimerStopped() { void Timer::ToggleRunning() { if (timer.IsRunning()) { - auto secondsRemaining = std::chrono::duration_cast(timer.GetTimeRemaining()); - minuteCounter.SetValue(secondsRemaining.count() / 60); - secondCounter.SetValue(secondsRemaining.count() % 60); + DisplayTime(); timer.StopTimer(); SetTimerStopped(); } else if (secondCounter.GetValue() + minuteCounter.GetValue() > 0) { @@ -147,7 +151,6 @@ void Timer::ToggleRunning() { } void Timer::Reset() { - minuteCounter.SetValue(0); - secondCounter.SetValue(0); + DisplayTime(); SetTimerStopped(); } diff --git a/src/displayapp/screens/Timer.h b/src/displayapp/screens/Timer.h index 409cae1c..0cb2bb08 100644 --- a/src/displayapp/screens/Timer.h +++ b/src/displayapp/screens/Timer.h @@ -5,6 +5,7 @@ #include "systemtask/SystemTask.h" #include "displayapp/LittleVgl.h" #include "displayapp/widgets/Counter.h" +#include "utility/DirtyValue.h" #include #include "components/timer/Timer.h" @@ -26,6 +27,7 @@ namespace Pinetime::Applications { void SetTimerRunning(); void SetTimerStopped(); void UpdateMask(); + void DisplayTime(); Pinetime::Controllers::Timer& timer; lv_obj_t* btnPlayPause; @@ -43,6 +45,7 @@ namespace Pinetime::Applications { bool buttonPressing = false; lv_coord_t maskPosition = 0; TickType_t pressTime = 0; + Utility::DirtyValue displaySeconds; }; } diff --git a/src/displayapp/screens/settings/SettingDisplay.cpp b/src/displayapp/screens/settings/SettingDisplay.cpp index 57a64d7f..bbc188a9 100644 --- a/src/displayapp/screens/settings/SettingDisplay.cpp +++ b/src/displayapp/screens/settings/SettingDisplay.cpp @@ -24,8 +24,7 @@ namespace { constexpr std::array 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); diff --git a/src/displayapp/screens/settings/SettingDisplay.h b/src/displayapp/screens/settings/SettingDisplay.h index b6d147c8..3bd10a62 100644 --- a/src/displayapp/screens/settings/SettingDisplay.h +++ b/src/displayapp/screens/settings/SettingDisplay.h @@ -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 options = {5000, 7000, 10000, 15000, 20000, 30000}; Controllers::Settings& settingsController; diff --git a/src/displayapp/screens/settings/SettingSetDateTime.cpp b/src/displayapp/screens/settings/SettingSetDateTime.cpp index cf9b0638..8926ff31 100644 --- a/src/displayapp/screens/settings/SettingSetDateTime.cpp +++ b/src/displayapp/screens/settings/SettingSetDateTime.cpp @@ -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, diff --git a/src/displayapp/screens/settings/SettingSetDateTime.h b/src/displayapp/screens/settings/SettingSetDateTime.h index 051b1abe..dea283f8 100644 --- a/src/displayapp/screens/settings/SettingSetDateTime.h +++ b/src/displayapp/screens/settings/SettingSetDateTime.h @@ -20,7 +20,6 @@ namespace Pinetime { void Quit(); private: - DisplayApp* app; Controllers::DateTime& dateTimeController; Controllers::Settings& settingsController; diff --git a/src/displayapp/screens/settings/SettingWatchFace.cpp b/src/displayapp/screens/settings/SettingWatchFace.cpp index e01e9f84..0d5168d2 100644 --- a/src/displayapp/screens/settings/SettingWatchFace.cpp +++ b/src/displayapp/screens/settings/SettingWatchFace.cpp @@ -54,8 +54,7 @@ SettingWatchFace::SettingWatchFace(Pinetime::Applications::DisplayApp* app, std::array&& 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} { diff --git a/src/displayapp/screens/settings/SettingWatchFace.h b/src/displayapp/screens/settings/SettingWatchFace.h index 2d0554fa..8d9a6906 100644 --- a/src/displayapp/screens/settings/SettingWatchFace.h +++ b/src/displayapp/screens/settings/SettingWatchFace.h @@ -36,7 +36,6 @@ namespace Pinetime { bool OnTouchEvent(TouchEvents event) override; private: - DisplayApp* app; auto CreateScreenList() const; std::unique_ptr CreateScreen(unsigned int screenNum) const; diff --git a/src/drivers/Hrs3300.cpp b/src/drivers/Hrs3300.cpp index 33889b6f..a4b72479 100644 --- a/src/drivers/Hrs3300.cpp +++ b/src/drivers/Hrs3300.cpp @@ -6,6 +6,7 @@ #include "drivers/Hrs3300.h" #include +#include #include #include @@ -67,40 +68,37 @@ void Hrs3300::Disable() { WriteRegister(static_cast(Registers::PDriver), 0); } -uint32_t Hrs3300::ReadHrs() { - auto m = ReadRegister(static_cast(Registers::C0DataM)); - auto h = ReadRegister(static_cast(Registers::C0DataH)); - auto l = ReadRegister(static_cast(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(*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(*std::max_element(std::begin(dataRegisters), std::end(dataRegisters))) - baseOffset + 1; -uint32_t Hrs3300::ReadAls() { - auto m = ReadRegister(static_cast(Registers::C1dataM)); - auto h = ReadRegister(static_cast(Registers::C1dataH)); - auto l = ReadRegister(static_cast(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(Registers::C0DataM) - baseOffset; + uint8_t h = static_cast(Registers::C0DataH) - baseOffset; + uint8_t l = static_cast(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(Registers::Hgain), hgain << 2); -} + // als + m = static_cast(Registers::C1dataM) - baseOffset; + h = static_cast(Registers::C1dataH) - baseOffset; + l = static_cast(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(Registers::Enable)); - auto pd = ReadRegister(static_cast(Registers::PDriver)); - - en = (en & 0xf7) | ((drive & 2) << 2); - pd = (pd & 0xbf) | ((drive & 1) << 6); - - WriteRegister(static_cast(Registers::Enable), en); - WriteRegister(static_cast(Registers::PDriver), pd); + return res; } void Hrs3300::WriteRegister(uint8_t reg, uint8_t data) { diff --git a/src/drivers/Hrs3300.h b/src/drivers/Hrs3300.h index 8bbdc69a..6f721448 100644 --- a/src/drivers/Hrs3300.h +++ b/src/drivers/Hrs3300.h @@ -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; diff --git a/src/heartratetask/HeartRateTask.cpp b/src/heartratetask/HeartRateTask.cpp index 9d82d11e..8a5a871b 100644 --- a/src/heartratetask/HeartRateTask.cpp +++ b/src/heartratetask/HeartRateTask.cpp @@ -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) diff --git a/src/libs/QCBOR b/src/libs/QCBOR deleted file mode 160000 index 56b17bf9..00000000 --- a/src/libs/QCBOR +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 56b17bf9f74096774944bcac0829adcd887d391e diff --git a/src/main.cpp b/src/main.cpp index ab50fa74..84f30eef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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 {}; diff --git a/src/systemtask/SystemMonitor.cpp b/src/systemtask/SystemMonitor.cpp index 2edee7bf..8696c8ce 100644 --- a/src/systemtask/SystemMonitor.cpp +++ b/src/systemtask/SystemMonitor.cpp @@ -1,5 +1,5 @@ #include "systemtask/SystemTask.h" -#if configUSE_TRACE_FACILITY == 1 +#if NRF_LOG_ENABLED // FreeRtosMonitor #include #include diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 211e19ec..fc4e8f7e 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -189,37 +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; - } - displayApp.PushMessage(Pinetime::Applications::Display::Messages::NotifyDeviceActivity); + 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())) { @@ -236,35 +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: - displayApp.PushMessage(Pinetime::Applications::Display::Messages::NotifyDeviceActivity); - displayApp.PushMessage(Pinetime::Applications::Display::Messages::UpdateDateTime); - 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(); - } else { - displayApp.PushMessage(Pinetime::Applications::Display::Messages::NotifyDeviceActivity); } 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: @@ -273,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: @@ -323,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. @@ -349,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(); @@ -390,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: @@ -427,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; } @@ -457,7 +443,7 @@ void SystemTask::UpdateMotion() { } if (settingsController.isWakeUpModeOn(Pinetime::Controllers::Settings::WakeUpMode::LowerWrist) && state == SystemTaskState::Running && motionController.ShouldLowerSleep()) { - PushMessage(Messages::GoToSleep); + GoToSleep(); } } @@ -473,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; @@ -493,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); @@ -512,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); diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index 79f1cf44..8a4e5954 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -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); diff --git a/src/systemtask/WakeLock.cpp b/src/systemtask/WakeLock.cpp new file mode 100644 index 00000000..2953f7ee --- /dev/null +++ b/src/systemtask/WakeLock.cpp @@ -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; +} diff --git a/src/systemtask/WakeLock.h b/src/systemtask/WakeLock.h new file mode 100644 index 00000000..5424c009 --- /dev/null +++ b/src/systemtask/WakeLock.h @@ -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; + }; + } +}