From 764a8fe33e740fa12245031df3df35a6eb2e3a78 Mon Sep 17 00:00:00 2001 From: Jan Hustak Date: Sun, 20 Oct 2024 12:01:36 +0200 Subject: [PATCH 01/22] StopWatch: add persistence --- src/CMakeLists.txt | 3 + .../stopwatch/StopWatchController.cpp | 101 ++++++++++ .../stopwatch/StopWatchController.h | 66 +++++++ src/displayapp/Controllers.h | 2 + src/displayapp/DisplayApp.cpp | 3 + src/displayapp/DisplayApp.h | 3 + src/displayapp/DisplayAppRecovery.cpp | 1 + src/displayapp/DisplayAppRecovery.h | 2 + src/displayapp/screens/StopWatch.cpp | 178 ++++++++++-------- src/displayapp/screens/StopWatch.h | 107 +++++------ src/main.cpp | 4 + src/systemtask/SystemTask.cpp | 2 + src/systemtask/SystemTask.h | 3 + 13 files changed, 333 insertions(+), 142 deletions(-) create mode 100644 src/components/stopwatch/StopWatchController.cpp create mode 100644 src/components/stopwatch/StopWatchController.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0a97a0158b..7d201166d8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -466,6 +466,7 @@ list(APPEND SOURCE_FILES components/motor/MotorController.cpp components/settings/Settings.cpp components/timer/Timer.cpp + components/stopwatch/StopWatchController.cpp components/alarm/AlarmController.cpp components/fs/FS.cpp drivers/Cst816s.cpp @@ -535,6 +536,7 @@ list(APPEND RECOVERY_SOURCE_FILES components/firmwarevalidator/FirmwareValidator.cpp components/settings/Settings.cpp components/timer/Timer.cpp + components/stopwatch/StopWatchController.cpp components/alarm/AlarmController.cpp drivers/Cst816s.cpp FreeRTOS/port.c @@ -654,6 +656,7 @@ set(INCLUDE_FILES components/ble/SimpleWeatherService.h components/settings/Settings.h components/timer/Timer.h + components/stopwatch/StopWatchController.h components/alarm/AlarmController.h drivers/Cst816s.h FreeRTOS/portmacro.h diff --git a/src/components/stopwatch/StopWatchController.cpp b/src/components/stopwatch/StopWatchController.cpp new file mode 100644 index 0000000000..200919f4dc --- /dev/null +++ b/src/components/stopwatch/StopWatchController.cpp @@ -0,0 +1,101 @@ +#include "components/stopwatch/StopWatchController.h" + +using namespace Pinetime::Controllers; + +namespace { + TickType_t CalculateDelta(const TickType_t startTime, const TickType_t currentTime) { + TickType_t delta = 0; + // Take care of overflow + if (startTime > currentTime) { + delta = 0xffffffff - startTime; + delta += (currentTime + 1); + } else { + delta = currentTime - startTime; + } + return delta; + } +} + +StopWatchController::StopWatchController() { + Clear(); +} + +// State Change + +void StopWatchController::Start() { + currentState = StopWatchStates::Running; + startTime = xTaskGetTickCount(); +} + +void StopWatchController::Pause() { + currentState = StopWatchStates::Paused; + timeElapsedPreviously += CalculateDelta(startTime, xTaskGetTickCount()); +} + +void StopWatchController::Clear() { + currentState = StopWatchStates::Cleared; + timeElapsedPreviously = 0; + + for (int i = 0; i < LAP_CAPACITY; i++) { + laps[i].count = 0; + laps[i].time = 0; + } + lapCount = 0; + lapHead = 0; +} + +// Lap + +void StopWatchController::PushLap() { + TickType_t lapEnd = GetElapsedTime(); + laps[lapHead].time = lapEnd; + laps[lapHead].count = lapCount + 1; + lapCount += 1; + lapHead = lapCount % LAP_CAPACITY; +} + +int StopWatchController::GetLapNum() { + if (lapCount < LAP_CAPACITY) + return lapCount; + else + return LAP_CAPACITY; +} + +int StopWatchController::GetLapCount() { + return lapCount; +} + +int Wrap(int index) { + return ((index % LAP_CAPACITY) + LAP_CAPACITY) % LAP_CAPACITY; +} + +LapInfo* StopWatchController::LastLap(int lap) { + if (lap >= LAP_CAPACITY || lap > lapCount || lapCount == 0) { + // Return "empty" LapInfo_t + return &emptyLapInfo; + } + // Index backwards + int index = Wrap(lapHead - lap); + return &laps[index]; +} + +// Data / State acess + +TickType_t StopWatchController::GetElapsedTime() { + if (!IsRunning()) { + return timeElapsedPreviously; + } + return timeElapsedPreviously + CalculateDelta(startTime, xTaskGetTickCount()); +} + +bool StopWatchController::IsRunning() { + return currentState == StopWatchStates::Running; +} + +bool StopWatchController::IsCleared() { + return currentState == StopWatchStates::Cleared; +} + +bool StopWatchController::IsPaused() { + return currentState == StopWatchStates::Paused; +} diff --git a/src/components/stopwatch/StopWatchController.h b/src/components/stopwatch/StopWatchController.h new file mode 100644 index 0000000000..0aaeb5ca48 --- /dev/null +++ b/src/components/stopwatch/StopWatchController.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include + +#define LAP_CAPACITY 2 + +namespace Pinetime { + namespace System { + class SystemTask; + } + namespace Controllers { + + enum class StopWatchStates { Cleared, Running, Paused }; + + struct LapInfo { + int count = 0; // Used to label the lap + TickType_t time = 0; // Delta time from beginning of stopwatch + }; + + class StopWatchController { + public: + StopWatchController(); + + // StopWatch functionality and data + void Start(); + void Pause(); + void Clear(); + + TickType_t GetElapsedTime(); + + // Lap functionality + + /// Only the latest laps are stored, the lap count is saved until reset + void PushLap(); + + /// Returns actual count of stored laps + int GetLapNum(); + + /// Returns lapCount + int GetLapCount(); + + /// Indexes into lap history, with 0 being the latest lap. + /// If the lap is unavailable, count and time will be 0. If there is a + /// real value, count should be above 0 + LapInfo* LastLap(int lap = 0); + + bool IsRunning(); + bool IsCleared(); + bool IsPaused(); + + private: + // Current state of stopwatch + StopWatchStates currentState = StopWatchStates::Cleared; + // Start time of current duration + TickType_t startTime = 0; + // How much time was elapsed before current duration + TickType_t timeElapsedPreviously = 0; + // Stores lap times + LapInfo laps[LAP_CAPACITY]; + LapInfo emptyLapInfo = {.count = 0, .time = 0}; + int lapCount = 0; + int lapHead = 0; + }; + } +} diff --git a/src/displayapp/Controllers.h b/src/displayapp/Controllers.h index 9992426c5d..223c7c699e 100644 --- a/src/displayapp/Controllers.h +++ b/src/displayapp/Controllers.h @@ -18,6 +18,7 @@ namespace Pinetime { class Settings; class MotorController; class MotionController; + class StopWatchController; class AlarmController; class BrightnessController; class SimpleWeatherService; @@ -41,6 +42,7 @@ namespace Pinetime { Pinetime::Controllers::Settings& settingsController; Pinetime::Controllers::MotorController& motorController; Pinetime::Controllers::MotionController& motionController; + Pinetime::Controllers::StopWatchController& stopWatchController; Pinetime::Controllers::AlarmController& alarmController; Pinetime::Controllers::BrightnessController& brightnessController; Pinetime::Controllers::SimpleWeatherService* weatherController; diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index b1594f197c..a709b9edaf 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -78,6 +78,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd, Controllers::Settings& settingsController, Pinetime::Controllers::MotorController& motorController, Pinetime::Controllers::MotionController& motionController, + Pinetime::Controllers::StopWatchController& stopWatchController, Pinetime::Controllers::AlarmController& alarmController, Pinetime::Controllers::BrightnessController& brightnessController, Pinetime::Controllers::TouchHandler& touchHandler, @@ -94,6 +95,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd, settingsController {settingsController}, motorController {motorController}, motionController {motionController}, + stopWatchController {stopWatchController}, alarmController {alarmController}, brightnessController {brightnessController}, touchHandler {touchHandler}, @@ -109,6 +111,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd, settingsController, motorController, motionController, + stopWatchController, alarmController, brightnessController, nullptr, diff --git a/src/displayapp/DisplayApp.h b/src/displayapp/DisplayApp.h index 2f276eaf9e..33dec1de88 100644 --- a/src/displayapp/DisplayApp.h +++ b/src/displayapp/DisplayApp.h @@ -13,6 +13,7 @@ #include "components/settings/Settings.h" #include "displayapp/screens/Screen.h" #include "components/timer/Timer.h" +#include "components/stopwatch/StopWatchController.h" #include "components/alarm/AlarmController.h" #include "touchhandler/TouchHandler.h" @@ -63,6 +64,7 @@ namespace Pinetime { Controllers::Settings& settingsController, Pinetime::Controllers::MotorController& motorController, Pinetime::Controllers::MotionController& motionController, + Pinetime::Controllers::StopWatchController& stopWatchController, Pinetime::Controllers::AlarmController& alarmController, Pinetime::Controllers::BrightnessController& brightnessController, Pinetime::Controllers::TouchHandler& touchHandler, @@ -93,6 +95,7 @@ namespace Pinetime { Pinetime::Controllers::Settings& settingsController; Pinetime::Controllers::MotorController& motorController; Pinetime::Controllers::MotionController& motionController; + Pinetime::Controllers::StopWatchController& stopWatchController; Pinetime::Controllers::AlarmController& alarmController; Pinetime::Controllers::BrightnessController& brightnessController; Pinetime::Controllers::TouchHandler& touchHandler; diff --git a/src/displayapp/DisplayAppRecovery.cpp b/src/displayapp/DisplayAppRecovery.cpp index bcb8db0e9d..cb358d601e 100644 --- a/src/displayapp/DisplayAppRecovery.cpp +++ b/src/displayapp/DisplayAppRecovery.cpp @@ -21,6 +21,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd, Controllers::Settings& /*settingsController*/, Pinetime::Controllers::MotorController& /*motorController*/, Pinetime::Controllers::MotionController& /*motionController*/, + Pinetime::Controllers::StopWatchController& /*stopWatchController*/, Pinetime::Controllers::AlarmController& /*alarmController*/, Pinetime::Controllers::BrightnessController& /*brightnessController*/, Pinetime::Controllers::TouchHandler& /*touchHandler*/, diff --git a/src/displayapp/DisplayAppRecovery.h b/src/displayapp/DisplayAppRecovery.h index 162ff2575e..ed387a8b08 100644 --- a/src/displayapp/DisplayAppRecovery.h +++ b/src/displayapp/DisplayAppRecovery.h @@ -31,6 +31,7 @@ namespace Pinetime { class MotionController; class TouchHandler; class MotorController; + class StopWatchController; class AlarmController; class BrightnessController; class FS; @@ -57,6 +58,7 @@ namespace Pinetime { Controllers::Settings& settingsController, Pinetime::Controllers::MotorController& motorController, Pinetime::Controllers::MotionController& motionController, + Pinetime::Controllers::StopWatchController& stopWatchController, Pinetime::Controllers::AlarmController& alarmController, Pinetime::Controllers::BrightnessController& brightnessController, Pinetime::Controllers::TouchHandler& touchHandler, diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index ff852beb69..397cd3655c 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -4,9 +4,10 @@ #include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Controllers; namespace { - TimeSeparated_t convertTicksToTimeSegments(const TickType_t timeElapsed) { + TimeSeparated convertTicksToTimeSegments(const TickType_t timeElapsed) { // Centiseconds const int timeElapsedCentis = timeElapsed * 100 / configTICK_RATE_HZ; @@ -14,27 +15,29 @@ namespace { const int secs = (timeElapsedCentis / 100) % 60; const int mins = ((timeElapsedCentis / 100) / 60) % 60; const int hours = ((timeElapsedCentis / 100) / 60) / 60; - return TimeSeparated_t {hours, mins, secs, hundredths}; + return TimeSeparated {hours, mins, secs, hundredths}; } void play_pause_event_handler(lv_obj_t* obj, lv_event_t event) { auto* stopWatch = static_cast(obj->user_data); if (event == LV_EVENT_CLICKED) { - stopWatch->playPauseBtnEventHandler(); + stopWatch->PlayPauseBtnEventHandler(); } } void stop_lap_event_handler(lv_obj_t* obj, lv_event_t event) { auto* stopWatch = static_cast(obj->user_data); if (event == LV_EVENT_CLICKED) { - stopWatch->stopLapBtnEventHandler(); + stopWatch->StopLapBtnEventHandler(); } } constexpr TickType_t blinkInterval = pdMS_TO_TICKS(1000); } -StopWatch::StopWatch(System::SystemTask& systemTask) : wakeLock(systemTask) { +StopWatch::StopWatch(System::SystemTask& systemTask, + StopWatchController& stopWatchController) + : wakeLock(systemTask), stopWatchController {stopWatchController} { static constexpr uint8_t btnWidth = 115; static constexpr uint8_t btnHeight = 80; btnPlayPause = lv_btn_create(lv_scr_act(), nullptr); @@ -72,9 +75,28 @@ StopWatch::StopWatch(System::SystemTask& systemTask) : wakeLock(systemTask) { lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DISABLED, Colors::lightGray); lv_obj_align(time, msecTime, LV_ALIGN_OUT_TOP_MID, 0, 0); - SetInterfaceStopped(); - taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); + + // Figure out what the current state of the stopwatch is and select the correct display + if (stopWatchController.IsCleared()) { + DisplayCleared(); + } else { + if (stopWatchController.GetLapCount() > 0) { + RenderLaps(); + } + RenderTime(); + + if (stopWatchController.IsRunning()) { + lv_obj_set_state(btnStopLap, LV_STATE_DISABLED); + lv_obj_set_state(txtStopLap, LV_STATE_DISABLED); + DisplayStarted(); + wakeLock.Lock(); + } else if (stopWatchController.IsPaused()) { + lv_obj_set_state(btnStopLap, LV_STATE_DEFAULT); + lv_obj_set_state(txtStopLap, LV_STATE_DEFAULT); + DisplayPaused(); + } + } } StopWatch::~StopWatch() { @@ -82,14 +104,14 @@ StopWatch::~StopWatch() { lv_obj_clean(lv_scr_act()); } -void StopWatch::SetInterfacePaused() { +void StopWatch::DisplayPaused() { lv_obj_set_style_local_bg_color(btnStopLap, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); lv_obj_set_style_local_bg_color(btnPlayPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Colors::blue); lv_label_set_text_static(txtPlayPause, Symbols::play); lv_label_set_text_static(txtStopLap, Symbols::stop); } -void StopWatch::SetInterfaceRunning() { +void StopWatch::DisplayStarted() { lv_obj_set_state(time, LV_STATE_DEFAULT); lv_obj_set_state(msecTime, LV_STATE_DEFAULT); lv_obj_set_style_local_bg_color(btnPlayPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt); @@ -102,7 +124,7 @@ void StopWatch::SetInterfaceRunning() { lv_obj_set_state(txtStopLap, LV_STATE_DEFAULT); } -void StopWatch::SetInterfaceStopped() { +void StopWatch::DisplayCleared() { lv_obj_set_state(time, LV_STATE_DISABLED); lv_obj_set_state(msecTime, LV_STATE_DISABLED); lv_obj_set_style_local_bg_color(btnPlayPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Colors::blue); @@ -123,96 +145,86 @@ void StopWatch::SetInterfaceStopped() { lv_obj_set_state(txtStopLap, LV_STATE_DISABLED); } -void StopWatch::Reset() { - SetInterfaceStopped(); - currentState = States::Init; - oldTimeElapsed = 0; - lapsDone = 0; +void StopWatch::RenderTime() { + TimeSeparated currentTimeSeparated = convertTicksToTimeSegments(stopWatchController.GetElapsedTime()); + if (currentTimeSeparated.hours == 0) { + lv_label_set_text_fmt(time, "%02d:%02d", currentTimeSeparated.mins, currentTimeSeparated.secs); + } else { + lv_label_set_text_fmt(time, "%02d:%02d:%02d", currentTimeSeparated.hours, currentTimeSeparated.mins, currentTimeSeparated.secs); + if (!isHoursLabelUpdated) { + lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42); + lv_obj_realign(time); + isHoursLabelUpdated = true; + } + } + lv_label_set_text_fmt(msecTime, "%02d", currentTimeSeparated.hundredths); } -void StopWatch::Start() { - SetInterfaceRunning(); - startTime = xTaskGetTickCount(); - currentState = States::Running; - wakeLock.Lock(); +void StopWatch::RenderPause() { + const TickType_t currentTime = xTaskGetTickCount(); + if (currentTime > blinkTime) { + blinkTime = currentTime + blinkInterval; + if (lv_obj_get_state(time, LV_LABEL_PART_MAIN) == LV_STATE_DEFAULT) { + lv_obj_set_state(time, LV_STATE_DISABLED); + lv_obj_set_state(msecTime, LV_STATE_DISABLED); + } else { + lv_obj_set_state(time, LV_STATE_DEFAULT); + lv_obj_set_state(msecTime, LV_STATE_DEFAULT); + } + } } -void StopWatch::Pause() { - SetInterfacePaused(); - startTime = 0; - // Store the current time elapsed in cache - oldTimeElapsed = laps[lapsDone]; - blinkTime = xTaskGetTickCount() + blinkInterval; - currentState = States::Halted; - wakeLock.Release(); +void StopWatch::RenderLaps() { + lv_label_set_text(lapText, ""); + for (int i = 0; i < displayedLaps; i++) { + LapInfo* lap = stopWatchController.LastLap(i); + + if (lap->count != 0) { + TimeSeparated laptime = convertTicksToTimeSegments(lap->time); + char buffer[16]; + sprintf(buffer, "#%2d %2d:%02d.%02d\n", lap->count, laptime.mins, laptime.secs, laptime.hundredths); + lv_label_ins_text(lapText, LV_LABEL_POS_LAST, buffer); + } + } } void StopWatch::Refresh() { - if (currentState == States::Running) { - laps[lapsDone] = oldTimeElapsed + xTaskGetTickCount() - startTime; - - TimeSeparated_t currentTimeSeparated = convertTicksToTimeSegments(laps[lapsDone]); - if (currentTimeSeparated.hours == 0) { - lv_label_set_text_fmt(time, "%02d:%02d", currentTimeSeparated.mins, currentTimeSeparated.secs); - } else { - lv_label_set_text_fmt(time, "%02d:%02d:%02d", currentTimeSeparated.hours, currentTimeSeparated.mins, currentTimeSeparated.secs); - if (!isHoursLabelUpdated) { - lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42); - lv_obj_realign(time); - isHoursLabelUpdated = true; - } - } - lv_label_set_text_fmt(msecTime, "%02d", currentTimeSeparated.hundredths); - } else if (currentState == States::Halted) { - const TickType_t currentTime = xTaskGetTickCount(); - if (currentTime > blinkTime) { - blinkTime = currentTime + blinkInterval; - if (lv_obj_get_state(time, LV_LABEL_PART_MAIN) == LV_STATE_DEFAULT) { - lv_obj_set_state(time, LV_STATE_DISABLED); - lv_obj_set_state(msecTime, LV_STATE_DISABLED); - } else { - lv_obj_set_state(time, LV_STATE_DEFAULT); - lv_obj_set_state(msecTime, LV_STATE_DEFAULT); - } - } + if (stopWatchController.IsRunning()) { + RenderTime(); + } else if (stopWatchController.IsPaused()) { + RenderPause(); } } -void StopWatch::playPauseBtnEventHandler() { - if (currentState == States::Init || currentState == States::Halted) { - Start(); - } else if (currentState == States::Running) { - Pause(); +void StopWatch::PlayPauseBtnEventHandler() { + if (stopWatchController.IsCleared() || stopWatchController.IsPaused()) { + stopWatchController.Start(); + DisplayStarted(); + wakeLock.Lock(); + } else if (stopWatchController.IsRunning()) { + stopWatchController.Pause(); + blinkTime = xTaskGetTickCount() + blinkInterval; + DisplayPaused(); + wakeLock.Release(); } } -void StopWatch::stopLapBtnEventHandler() { - // If running, then this button is used to save laps - if (currentState == States::Running) { - lv_label_set_text(lapText, ""); - lapsDone = std::min(lapsDone + 1, maxLapCount); - for (int i = lapsDone - displayedLaps; i < lapsDone; i++) { - if (i < 0) { - lv_label_ins_text(lapText, LV_LABEL_POS_LAST, "\n"); - continue; - } - TimeSeparated_t times = convertTicksToTimeSegments(laps[i]); - char buffer[17]; - if (times.hours == 0) { - snprintf(buffer, sizeof(buffer), "#%2d %2d:%02d.%02d\n", i + 1, times.mins, times.secs, times.hundredths); - } else { - snprintf(buffer, sizeof(buffer), "#%2d %2d:%02d:%02d.%02d\n", i + 1, times.hours, times.mins, times.secs, times.hundredths); - } - lv_label_ins_text(lapText, LV_LABEL_POS_LAST, buffer); - } - } else if (currentState == States::Halted) { - Reset(); +void StopWatch::StopLapBtnEventHandler() { + if (stopWatchController.IsRunning()) { + stopWatchController.PushLap(); + RenderLaps(); + } else if (stopWatchController.IsPaused()) { + stopWatchController.Clear(); + DisplayCleared(); + wakeLock.Release(); } } bool StopWatch::OnButtonPushed() { - if (currentState == States::Running) { - Pause(); + if (stopWatchController.IsRunning()) { + stopWatchController.Pause(); + DisplayPaused(); + wakeLock.Release(); return true; } return false; diff --git a/src/displayapp/screens/StopWatch.h b/src/displayapp/screens/StopWatch.h index 55a178dcbe..358e2859df 100644 --- a/src/displayapp/screens/StopWatch.h +++ b/src/displayapp/screens/StopWatch.h @@ -3,72 +3,61 @@ #include "displayapp/screens/Screen.h" #include -#include -#include "portmacro_cmsis.h" - +#include "components/stopwatch/StopWatchController.h" #include "systemtask/SystemTask.h" #include "systemtask/WakeLock.h" -#include "displayapp/apps/Apps.h" -#include "displayapp/Controllers.h" #include "Symbols.h" -namespace Pinetime { - namespace Applications { - namespace Screens { - - enum class States { Init, Running, Halted }; - - struct TimeSeparated_t { - int hours; - int mins; - int secs; - int hundredths; - }; - - class StopWatch : public Screen { - public: - explicit StopWatch(System::SystemTask& systemTask); - ~StopWatch() override; - void Refresh() override; - - void playPauseBtnEventHandler(); - void stopLapBtnEventHandler(); - bool OnButtonPushed() override; - - private: - void SetInterfacePaused(); - void SetInterfaceRunning(); - void SetInterfaceStopped(); +namespace Pinetime::Applications { + namespace Screens { - void Reset(); - void Start(); - void Pause(); - - Pinetime::System::WakeLock wakeLock; - States currentState = States::Init; - TickType_t startTime; - TickType_t oldTimeElapsed = 0; - TickType_t blinkTime = 0; - static constexpr int maxLapCount = 20; - TickType_t laps[maxLapCount + 1]; - static constexpr int displayedLaps = 2; - int lapsDone = 0; - lv_obj_t *time, *msecTime, *btnPlayPause, *btnStopLap, *txtPlayPause, *txtStopLap; - lv_obj_t* lapText; - bool isHoursLabelUpdated = false; + struct TimeSeparated { + int hours; + int mins; + int secs; + int hundredths; + }; - lv_task_t* taskRefresh; - }; - } + class StopWatch : public Screen { + public: + explicit StopWatch(System::SystemTask& systemTask, + Controllers::StopWatchController& stopWatchController); + ~StopWatch() override; + void Refresh() override; + + void PlayPauseBtnEventHandler(); + void StopLapBtnEventHandler(); + bool OnButtonPushed() override; + + private: + void DisplayPaused(); + void DisplayStarted(); + void DisplayCleared(); + + void RenderTime(); + void RenderPause(); + void RenderLaps(); + + Pinetime::System::WakeLock wakeLock; + Controllers::StopWatchController& stopWatchController; + TickType_t blinkTime = 0; + static constexpr int displayedLaps = 2; + lv_obj_t *time, *msecTime, *btnPlayPause, *btnStopLap, *txtPlayPause, *txtStopLap; + lv_obj_t* lapText; + bool isHoursLabelUpdated = false; + + lv_task_t* taskRefresh; + }; + } - template <> - struct AppTraits { - static constexpr Apps app = Apps::StopWatch; - static constexpr const char* icon = Screens::Symbols::stopWatch; + template <> + struct AppTraits { + static constexpr Apps app = Apps::StopWatch; + static constexpr const char* icon = Screens::Symbols::stopWatch; - static Screens::Screen* Create(AppControllers& controllers) { - return new Screens::StopWatch(*controllers.systemTask); - }; + static Screens::Screen* Create(AppControllers& controllers) { + return new Screens::StopWatch(*controllers.systemTask, + controllers.stopWatchController); }; - } + }; } diff --git a/src/main.cpp b/src/main.cpp index 24f13caddd..c573b482f6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -35,6 +35,7 @@ #include "components/motor/MotorController.h" #include "components/datetime/DateTimeController.h" #include "components/heartrate/HeartRateController.h" +#include "components/stopwatch/StopWatchController.h" #include "components/fs/FS.h" #include "drivers/Spi.h" #include "drivers/SpiMaster.h" @@ -104,6 +105,7 @@ Pinetime::Controllers::DateTime dateTimeController {settingsController}; Pinetime::Drivers::Watchdog watchdog; Pinetime::Controllers::NotificationManager notificationManager; Pinetime::Controllers::MotionController motionController; +Pinetime::Controllers::StopWatchController stopWatchController; Pinetime::Controllers::AlarmController alarmController {dateTimeController, fs}; Pinetime::Controllers::TouchHandler touchHandler; Pinetime::Controllers::ButtonHandler buttonHandler; @@ -120,6 +122,7 @@ Pinetime::Applications::DisplayApp displayApp(lcd, settingsController, motorController, motionController, + stopWatchController, alarmController, brightnessController, touchHandler, @@ -133,6 +136,7 @@ Pinetime::System::SystemTask systemTask(spi, batteryController, bleController, dateTimeController, + stopWatchController, alarmController, watchdog, notificationManager, diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index eb013d6d1a..976fcda25f 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -39,6 +39,7 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi, Controllers::Battery& batteryController, Controllers::Ble& bleController, Controllers::DateTime& dateTimeController, + Controllers::StopWatchController& stopWatchController, Controllers::AlarmController& alarmController, Drivers::Watchdog& watchdog, Pinetime::Controllers::NotificationManager& notificationManager, @@ -59,6 +60,7 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi, batteryController {batteryController}, bleController {bleController}, dateTimeController {dateTimeController}, + stopWatchController {stopWatchController}, alarmController {alarmController}, watchdog {watchdog}, notificationManager {notificationManager}, diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index 0060e36096..2f925aed31 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -15,6 +15,7 @@ #include "systemtask/SystemMonitor.h" #include "components/ble/NimbleController.h" #include "components/ble/NotificationManager.h" +#include "components/stopwatch/StopWatchController.h" #include "components/alarm/AlarmController.h" #include "components/fs/FS.h" #include "touchhandler/TouchHandler.h" @@ -60,6 +61,7 @@ namespace Pinetime { Controllers::Battery& batteryController, Controllers::Ble& bleController, Controllers::DateTime& dateTimeController, + Controllers::StopWatchController& stopWatchController, Controllers::AlarmController& alarmController, Drivers::Watchdog& watchdog, Pinetime::Controllers::NotificationManager& notificationManager, @@ -100,6 +102,7 @@ namespace Pinetime { Pinetime::Controllers::Ble& bleController; Pinetime::Controllers::DateTime& dateTimeController; + Pinetime::Controllers::StopWatchController& stopWatchController; Pinetime::Controllers::AlarmController& alarmController; QueueHandle_t systemTasksMsgQueue; Pinetime::Drivers::Watchdog& watchdog; From 4db6a09255734d4da5b6e1c0a5b7d598545d5051 Mon Sep 17 00:00:00 2001 From: codingjourney Date: Wed, 23 Oct 2024 23:29:00 +0200 Subject: [PATCH 02/22] minor fixes: * more consistent function names * lapCapacity as constexpr * LastLap returns std::optional * simplified handling of TickType_t values * removed unused methods * minor fix in lap rendering --- .../stopwatch/StopWatchController.cpp | 43 +++++-------------- .../stopwatch/StopWatchController.h | 19 ++++---- src/displayapp/screens/StopWatch.cpp | 22 +++++----- 3 files changed, 31 insertions(+), 53 deletions(-) diff --git a/src/components/stopwatch/StopWatchController.cpp b/src/components/stopwatch/StopWatchController.cpp index 200919f4dc..f9709e6b07 100644 --- a/src/components/stopwatch/StopWatchController.cpp +++ b/src/components/stopwatch/StopWatchController.cpp @@ -2,20 +2,6 @@ using namespace Pinetime::Controllers; -namespace { - TickType_t CalculateDelta(const TickType_t startTime, const TickType_t currentTime) { - TickType_t delta = 0; - // Take care of overflow - if (startTime > currentTime) { - delta = 0xffffffff - startTime; - delta += (currentTime + 1); - } else { - delta = currentTime - startTime; - } - return delta; - } -} - StopWatchController::StopWatchController() { Clear(); } @@ -29,14 +15,14 @@ void StopWatchController::Start() { void StopWatchController::Pause() { currentState = StopWatchStates::Paused; - timeElapsedPreviously += CalculateDelta(startTime, xTaskGetTickCount()); + timeElapsedPreviously += xTaskGetTickCount() - startTime; } void StopWatchController::Clear() { currentState = StopWatchStates::Cleared; timeElapsedPreviously = 0; - for (int i = 0; i < LAP_CAPACITY; i++) { + for (int i = 0; i < lapCapacity; i++) { laps[i].count = 0; laps[i].time = 0; } @@ -51,14 +37,7 @@ void StopWatchController::PushLap() { laps[lapHead].time = lapEnd; laps[lapHead].count = lapCount + 1; lapCount += 1; - lapHead = lapCount % LAP_CAPACITY; -} - -int StopWatchController::GetLapNum() { - if (lapCount < LAP_CAPACITY) - return lapCount; - else - return LAP_CAPACITY; + lapHead = lapCount % lapCapacity; } int StopWatchController::GetLapCount() { @@ -66,17 +45,17 @@ int StopWatchController::GetLapCount() { } int Wrap(int index) { - return ((index % LAP_CAPACITY) + LAP_CAPACITY) % LAP_CAPACITY; + return ((index % lapCapacity) + lapCapacity) % lapCapacity; } -LapInfo* StopWatchController::LastLap(int lap) { - if (lap >= LAP_CAPACITY || lap > lapCount || lapCount == 0) { - // Return "empty" LapInfo_t - return &emptyLapInfo; +std::optional StopWatchController::LastLap(int lap) { + if (lap >= lapCapacity || lap >= lapCount) { + return {}; } // Index backwards - int index = Wrap(lapHead - lap); - return &laps[index]; + int mostRecentLap = lapHead - 1; + int index = Wrap(mostRecentLap - lap); + return laps[index]; } // Data / State acess @@ -85,7 +64,7 @@ TickType_t StopWatchController::GetElapsedTime() { if (!IsRunning()) { return timeElapsedPreviously; } - return timeElapsedPreviously + CalculateDelta(startTime, xTaskGetTickCount()); + return timeElapsedPreviously + (xTaskGetTickCount() - startTime); } bool StopWatchController::IsRunning() { diff --git a/src/components/stopwatch/StopWatchController.h b/src/components/stopwatch/StopWatchController.h index 0aaeb5ca48..c549a71e69 100644 --- a/src/components/stopwatch/StopWatchController.h +++ b/src/components/stopwatch/StopWatchController.h @@ -1,10 +1,9 @@ #pragma once #include +#include #include -#define LAP_CAPACITY 2 - namespace Pinetime { namespace System { class SystemTask; @@ -18,6 +17,8 @@ namespace Pinetime { TickType_t time = 0; // Delta time from beginning of stopwatch }; + constexpr int lapCapacity = 2; + class StopWatchController { public: StopWatchController(); @@ -34,22 +35,17 @@ namespace Pinetime { /// Only the latest laps are stored, the lap count is saved until reset void PushLap(); - /// Returns actual count of stored laps - int GetLapNum(); - /// Returns lapCount int GetLapCount(); /// Indexes into lap history, with 0 being the latest lap. - /// If the lap is unavailable, count and time will be 0. If there is a - /// real value, count should be above 0 - LapInfo* LastLap(int lap = 0); + std::optional LastLap(int lap = 0); bool IsRunning(); bool IsCleared(); bool IsPaused(); - private: + // private: // Current state of stopwatch StopWatchStates currentState = StopWatchStates::Cleared; // Start time of current duration @@ -57,9 +53,10 @@ namespace Pinetime { // How much time was elapsed before current duration TickType_t timeElapsedPreviously = 0; // Stores lap times - LapInfo laps[LAP_CAPACITY]; - LapInfo emptyLapInfo = {.count = 0, .time = 0}; + LapInfo laps[lapCapacity]; + // Total lap count; may exceed lapCapacity int lapCount = 0; + // Index for next lap time; must be lower than lapCapacity int lapHead = 0; }; } diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index 397cd3655c..b17518d559 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -7,7 +7,7 @@ using namespace Pinetime::Applications::Screens; using namespace Pinetime::Controllers; namespace { - TimeSeparated convertTicksToTimeSegments(const TickType_t timeElapsed) { + TimeSeparated ConvertTicksToTimeSegments(const TickType_t timeElapsed) { // Centiseconds const int timeElapsedCentis = timeElapsed * 100 / configTICK_RATE_HZ; @@ -18,14 +18,14 @@ namespace { return TimeSeparated {hours, mins, secs, hundredths}; } - void play_pause_event_handler(lv_obj_t* obj, lv_event_t event) { + void PlayPauseEventHandler(lv_obj_t* obj, lv_event_t event) { auto* stopWatch = static_cast(obj->user_data); if (event == LV_EVENT_CLICKED) { stopWatch->PlayPauseBtnEventHandler(); } } - void stop_lap_event_handler(lv_obj_t* obj, lv_event_t event) { + void StopLapEventHandler(lv_obj_t* obj, lv_event_t event) { auto* stopWatch = static_cast(obj->user_data); if (event == LV_EVENT_CLICKED) { stopWatch->StopLapBtnEventHandler(); @@ -42,14 +42,14 @@ StopWatch::StopWatch(System::SystemTask& systemTask, static constexpr uint8_t btnHeight = 80; btnPlayPause = lv_btn_create(lv_scr_act(), nullptr); btnPlayPause->user_data = this; - lv_obj_set_event_cb(btnPlayPause, play_pause_event_handler); + lv_obj_set_event_cb(btnPlayPause, PlayPauseEventHandler); lv_obj_set_size(btnPlayPause, btnWidth, btnHeight); lv_obj_align(btnPlayPause, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); txtPlayPause = lv_label_create(btnPlayPause, nullptr); btnStopLap = lv_btn_create(lv_scr_act(), nullptr); btnStopLap->user_data = this; - lv_obj_set_event_cb(btnStopLap, stop_lap_event_handler); + lv_obj_set_event_cb(btnStopLap, StopLapEventHandler); lv_obj_set_size(btnStopLap, btnWidth, btnHeight); lv_obj_align(btnStopLap, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); txtStopLap = lv_label_create(btnStopLap, nullptr); @@ -146,7 +146,7 @@ void StopWatch::DisplayCleared() { } void StopWatch::RenderTime() { - TimeSeparated currentTimeSeparated = convertTicksToTimeSegments(stopWatchController.GetElapsedTime()); + TimeSeparated currentTimeSeparated = ConvertTicksToTimeSegments(stopWatchController.GetElapsedTime()); if (currentTimeSeparated.hours == 0) { lv_label_set_text_fmt(time, "%02d:%02d", currentTimeSeparated.mins, currentTimeSeparated.secs); } else { @@ -176,14 +176,16 @@ void StopWatch::RenderPause() { void StopWatch::RenderLaps() { lv_label_set_text(lapText, ""); - for (int i = 0; i < displayedLaps; i++) { - LapInfo* lap = stopWatchController.LastLap(i); + for (int i = displayedLaps - 1; i >= 0; i--) { + std::optional lap = stopWatchController.LastLap(i); - if (lap->count != 0) { - TimeSeparated laptime = convertTicksToTimeSegments(lap->time); + if (lap) { + TimeSeparated laptime = ConvertTicksToTimeSegments(lap->time); char buffer[16]; sprintf(buffer, "#%2d %2d:%02d.%02d\n", lap->count, laptime.mins, laptime.secs, laptime.hundredths); lv_label_ins_text(lapText, LV_LABEL_POS_LAST, buffer); + } else { + lv_label_ins_text(lapText, LV_LABEL_POS_LAST, "\n"); } } } From 0a730c0c8c5a8b53bf41fb2a5b7244c94c12139f Mon Sep 17 00:00:00 2001 From: codingjourney Date: Thu, 24 Oct 2024 21:49:42 +0200 Subject: [PATCH 03/22] lap storage as CircularBuffer, minor fixes --- .../stopwatch/StopWatchController.cpp | 19 +++++-------------- .../stopwatch/StopWatchController.h | 13 +++++++------ src/displayapp/screens/StopWatch.cpp | 2 +- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/src/components/stopwatch/StopWatchController.cpp b/src/components/stopwatch/StopWatchController.cpp index f9709e6b07..d8e4858860 100644 --- a/src/components/stopwatch/StopWatchController.cpp +++ b/src/components/stopwatch/StopWatchController.cpp @@ -27,35 +27,26 @@ void StopWatchController::Clear() { laps[i].time = 0; } lapCount = 0; - lapHead = 0; } // Lap void StopWatchController::PushLap() { TickType_t lapEnd = GetElapsedTime(); - laps[lapHead].time = lapEnd; - laps[lapHead].count = lapCount + 1; - lapCount += 1; - lapHead = lapCount % lapCapacity; + laps[0].time = lapEnd; + laps[0].count = ++lapCount; + laps--; } int StopWatchController::GetLapCount() { return lapCount; } -int Wrap(int index) { - return ((index % lapCapacity) + lapCapacity) % lapCapacity; -} - std::optional StopWatchController::LastLap(int lap) { - if (lap >= lapCapacity || lap >= lapCount) { + if (lap < 0 || lap >= lapCapacity || laps[lap].count == 0) { return {}; } - // Index backwards - int mostRecentLap = lapHead - 1; - int index = Wrap(mostRecentLap - lap); - return laps[index]; + return laps[lap]; } // Data / State acess diff --git a/src/components/stopwatch/StopWatchController.h b/src/components/stopwatch/StopWatchController.h index c549a71e69..626f214f8d 100644 --- a/src/components/stopwatch/StopWatchController.h +++ b/src/components/stopwatch/StopWatchController.h @@ -3,6 +3,7 @@ #include #include #include +#include "utility/CircularBuffer.h" namespace Pinetime { namespace System { @@ -17,7 +18,6 @@ namespace Pinetime { TickType_t time = 0; // Delta time from beginning of stopwatch }; - constexpr int lapCapacity = 2; class StopWatchController { public: @@ -45,19 +45,20 @@ namespace Pinetime { bool IsCleared(); bool IsPaused(); - // private: + private: // Current state of stopwatch StopWatchStates currentState = StopWatchStates::Cleared; // Start time of current duration TickType_t startTime = 0; // How much time was elapsed before current duration TickType_t timeElapsedPreviously = 0; - // Stores lap times - LapInfo laps[lapCapacity]; + + // Number of stored laps + static constexpr int lapCapacity = 2; + // Lap storage + Utility::CircularBuffer laps; // Total lap count; may exceed lapCapacity int lapCount = 0; - // Index for next lap time; must be lower than lapCapacity - int lapHead = 0; }; } } diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index b17518d559..cc11f885ea 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -176,7 +176,7 @@ void StopWatch::RenderPause() { void StopWatch::RenderLaps() { lv_label_set_text(lapText, ""); - for (int i = displayedLaps - 1; i >= 0; i--) { + for (int i = 0; i < displayedLaps; i++) { std::optional lap = stopWatchController.LastLap(i); if (lap) { From 277b32904cb679febfe895d1d9bb092bbff5cc8d Mon Sep 17 00:00:00 2001 From: codingjourney Date: Sat, 26 Oct 2024 20:36:39 +0200 Subject: [PATCH 04/22] improved naming of lap-related fields and methods --- .../stopwatch/StopWatchController.cpp | 26 +++++++++---------- .../stopwatch/StopWatchController.h | 24 ++++++++--------- src/displayapp/screens/StopWatch.cpp | 10 +++---- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/components/stopwatch/StopWatchController.cpp b/src/components/stopwatch/StopWatchController.cpp index d8e4858860..7af8036766 100644 --- a/src/components/stopwatch/StopWatchController.cpp +++ b/src/components/stopwatch/StopWatchController.cpp @@ -22,31 +22,31 @@ void StopWatchController::Clear() { currentState = StopWatchStates::Cleared; timeElapsedPreviously = 0; - for (int i = 0; i < lapCapacity; i++) { - laps[i].count = 0; - laps[i].time = 0; + for (int i = 0; i < histSize; i++) { + history[i].number = 0; + history[i].timeSinceStart = 0; } - lapCount = 0; + maxLapNumber = 0; } // Lap -void StopWatchController::PushLap() { +void StopWatchController::AddLapToHistory() { TickType_t lapEnd = GetElapsedTime(); - laps[0].time = lapEnd; - laps[0].count = ++lapCount; - laps--; + history[0].timeSinceStart = lapEnd; + history[0].number = ++maxLapNumber; + history--; } -int StopWatchController::GetLapCount() { - return lapCount; +int StopWatchController::GetMaxLapNumber() { + return maxLapNumber; } -std::optional StopWatchController::LastLap(int lap) { - if (lap < 0 || lap >= lapCapacity || laps[lap].count == 0) { +std::optional StopWatchController::GetLapFromHistory(int index) { + if (index < 0 || index >= histSize || history[index].number == 0) { return {}; } - return laps[lap]; + return history[index]; } // Data / State acess diff --git a/src/components/stopwatch/StopWatchController.h b/src/components/stopwatch/StopWatchController.h index 626f214f8d..9386e35158 100644 --- a/src/components/stopwatch/StopWatchController.h +++ b/src/components/stopwatch/StopWatchController.h @@ -14,8 +14,8 @@ namespace Pinetime { enum class StopWatchStates { Cleared, Running, Paused }; struct LapInfo { - int count = 0; // Used to label the lap - TickType_t time = 0; // Delta time from beginning of stopwatch + int number = 0; // Used to label the lap + TickType_t timeSinceStart = 0; // Excluding pauses }; @@ -32,14 +32,14 @@ namespace Pinetime { // Lap functionality - /// Only the latest laps are stored, the lap count is saved until reset - void PushLap(); + /// Only the latest histSize laps are stored + void AddLapToHistory(); - /// Returns lapCount - int GetLapCount(); + /// Returns maxLapNumber + int GetMaxLapNumber(); /// Indexes into lap history, with 0 being the latest lap. - std::optional LastLap(int lap = 0); + std::optional GetLapFromHistory(int index); bool IsRunning(); bool IsCleared(); @@ -53,12 +53,12 @@ namespace Pinetime { // How much time was elapsed before current duration TickType_t timeElapsedPreviously = 0; - // Number of stored laps - static constexpr int lapCapacity = 2; + // Maximum number of stored laps + static constexpr int histSize = 2; // Lap storage - Utility::CircularBuffer laps; - // Total lap count; may exceed lapCapacity - int lapCount = 0; + Utility::CircularBuffer history; + // Highest lap number; may exceed histSize + int maxLapNumber = 0; }; } } diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index cc11f885ea..5d3404cc14 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -81,7 +81,7 @@ StopWatch::StopWatch(System::SystemTask& systemTask, if (stopWatchController.IsCleared()) { DisplayCleared(); } else { - if (stopWatchController.GetLapCount() > 0) { + if (stopWatchController.GetMaxLapNumber() > 0) { RenderLaps(); } RenderTime(); @@ -177,12 +177,12 @@ void StopWatch::RenderPause() { void StopWatch::RenderLaps() { lv_label_set_text(lapText, ""); for (int i = 0; i < displayedLaps; i++) { - std::optional lap = stopWatchController.LastLap(i); + std::optional lap = stopWatchController.GetLapFromHistory(i); if (lap) { - TimeSeparated laptime = ConvertTicksToTimeSegments(lap->time); + TimeSeparated laptime = ConvertTicksToTimeSegments(lap->timeSinceStart); char buffer[16]; - sprintf(buffer, "#%2d %2d:%02d.%02d\n", lap->count, laptime.mins, laptime.secs, laptime.hundredths); + sprintf(buffer, "#%2d %2d:%02d.%02d\n", lap->number, laptime.mins, laptime.secs, laptime.hundredths); lv_label_ins_text(lapText, LV_LABEL_POS_LAST, buffer); } else { lv_label_ins_text(lapText, LV_LABEL_POS_LAST, "\n"); @@ -213,7 +213,7 @@ void StopWatch::PlayPauseBtnEventHandler() { void StopWatch::StopLapBtnEventHandler() { if (stopWatchController.IsRunning()) { - stopWatchController.PushLap(); + stopWatchController.AddLapToHistory(); RenderLaps(); } else if (stopWatchController.IsPaused()) { stopWatchController.Clear(); From 0fda70322505cb99e2234219515f5e55ba0f9046 Mon Sep 17 00:00:00 2001 From: codingjourney Date: Sat, 26 Oct 2024 20:45:53 +0200 Subject: [PATCH 05/22] removed superfluous default values in controller --- src/components/stopwatch/StopWatchController.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/stopwatch/StopWatchController.h b/src/components/stopwatch/StopWatchController.h index 9386e35158..1590581f5b 100644 --- a/src/components/stopwatch/StopWatchController.h +++ b/src/components/stopwatch/StopWatchController.h @@ -49,16 +49,16 @@ namespace Pinetime { // Current state of stopwatch StopWatchStates currentState = StopWatchStates::Cleared; // Start time of current duration - TickType_t startTime = 0; + TickType_t startTime; // How much time was elapsed before current duration - TickType_t timeElapsedPreviously = 0; + TickType_t timeElapsedPreviously; // Maximum number of stored laps static constexpr int histSize = 2; // Lap storage Utility::CircularBuffer history; // Highest lap number; may exceed histSize - int maxLapNumber = 0; + int maxLapNumber; }; } } From 9a30b1848cf29e7dcfbf54764a50ff95053aa362 Mon Sep 17 00:00:00 2001 From: codingjourney Date: Sun, 27 Oct 2024 09:20:10 +0100 Subject: [PATCH 06/22] render accurate time at pause --- src/displayapp/screens/StopWatch.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index 5d3404cc14..2ef5d84599 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -207,6 +207,7 @@ void StopWatch::PlayPauseBtnEventHandler() { stopWatchController.Pause(); blinkTime = xTaskGetTickCount() + blinkInterval; DisplayPaused(); + RenderTime(); wakeLock.Release(); } } From 81dc7d6619091d52c5ced220a6d539f869daf98d Mon Sep 17 00:00:00 2001 From: codingjourney Date: Mon, 28 Oct 2024 05:26:30 +0100 Subject: [PATCH 07/22] fixed issues found by the test-format CI job --- src/components/stopwatch/StopWatchController.h | 2 +- src/displayapp/screens/StopWatch.cpp | 5 ++--- src/displayapp/screens/StopWatch.h | 6 ++---- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/components/stopwatch/StopWatchController.h b/src/components/stopwatch/StopWatchController.h index 1590581f5b..800ca1b34e 100644 --- a/src/components/stopwatch/StopWatchController.h +++ b/src/components/stopwatch/StopWatchController.h @@ -9,6 +9,7 @@ namespace Pinetime { namespace System { class SystemTask; } + namespace Controllers { enum class StopWatchStates { Cleared, Running, Paused }; @@ -18,7 +19,6 @@ namespace Pinetime { TickType_t timeSinceStart = 0; // Excluding pauses }; - class StopWatchController { public: StopWatchController(); diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index 2ef5d84599..5265455bcb 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -35,8 +35,7 @@ namespace { constexpr TickType_t blinkInterval = pdMS_TO_TICKS(1000); } -StopWatch::StopWatch(System::SystemTask& systemTask, - StopWatchController& stopWatchController) +StopWatch::StopWatch(System::SystemTask& systemTask, StopWatchController& stopWatchController) : wakeLock(systemTask), stopWatchController {stopWatchController} { static constexpr uint8_t btnWidth = 115; static constexpr uint8_t btnHeight = 80; @@ -80,7 +79,7 @@ StopWatch::StopWatch(System::SystemTask& systemTask, // Figure out what the current state of the stopwatch is and select the correct display if (stopWatchController.IsCleared()) { DisplayCleared(); - } else { + } else { if (stopWatchController.GetMaxLapNumber() > 0) { RenderLaps(); } diff --git a/src/displayapp/screens/StopWatch.h b/src/displayapp/screens/StopWatch.h index 358e2859df..e649485693 100644 --- a/src/displayapp/screens/StopWatch.h +++ b/src/displayapp/screens/StopWatch.h @@ -20,8 +20,7 @@ namespace Pinetime::Applications { class StopWatch : public Screen { public: - explicit StopWatch(System::SystemTask& systemTask, - Controllers::StopWatchController& stopWatchController); + explicit StopWatch(System::SystemTask& systemTask, Controllers::StopWatchController& stopWatchController); ~StopWatch() override; void Refresh() override; @@ -56,8 +55,7 @@ namespace Pinetime::Applications { static constexpr const char* icon = Screens::Symbols::stopWatch; static Screens::Screen* Create(AppControllers& controllers) { - return new Screens::StopWatch(*controllers.systemTask, - controllers.stopWatchController); + return new Screens::StopWatch(*controllers.systemTask, controllers.stopWatchController); }; }; } From 8a643886a4113ec1a3d54e61f45a9d8224e8ac38 Mon Sep 17 00:00:00 2001 From: codingjourney Date: Wed, 30 Oct 2024 21:43:50 +0100 Subject: [PATCH 08/22] common method for entering the Paused state --- src/displayapp/screens/StopWatch.cpp | 18 ++++++++++-------- src/displayapp/screens/StopWatch.h | 2 ++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index 5265455bcb..6fa8165225 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -203,11 +203,7 @@ void StopWatch::PlayPauseBtnEventHandler() { DisplayStarted(); wakeLock.Lock(); } else if (stopWatchController.IsRunning()) { - stopWatchController.Pause(); - blinkTime = xTaskGetTickCount() + blinkInterval; - DisplayPaused(); - RenderTime(); - wakeLock.Release(); + OnPause(); } } @@ -224,10 +220,16 @@ void StopWatch::StopLapBtnEventHandler() { bool StopWatch::OnButtonPushed() { if (stopWatchController.IsRunning()) { - stopWatchController.Pause(); - DisplayPaused(); - wakeLock.Release(); + OnPause(); return true; } return false; } + +void StopWatch::OnPause() { + stopWatchController.Pause(); + blinkTime = xTaskGetTickCount() + blinkInterval; + RenderTime(); // make sure displayed time is not stale + DisplayPaused(); + wakeLock.Release(); +} \ No newline at end of file diff --git a/src/displayapp/screens/StopWatch.h b/src/displayapp/screens/StopWatch.h index e649485693..200f0192e2 100644 --- a/src/displayapp/screens/StopWatch.h +++ b/src/displayapp/screens/StopWatch.h @@ -29,6 +29,8 @@ namespace Pinetime::Applications { bool OnButtonPushed() override; private: + void OnPause(); + void DisplayPaused(); void DisplayStarted(); void DisplayCleared(); From 0dfae72b5de7784157a59180dd726ce5a3f223bb Mon Sep 17 00:00:00 2001 From: codingjourney Date: Wed, 30 Oct 2024 22:11:26 +0100 Subject: [PATCH 09/22] added missing newline --- src/displayapp/screens/StopWatch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index 6fa8165225..25b0ad77a5 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -232,4 +232,4 @@ void StopWatch::OnPause() { RenderTime(); // make sure displayed time is not stale DisplayPaused(); wakeLock.Release(); -} \ No newline at end of file +} From 0d0af6e57474f18973e9627132fa6ee822cb6830 Mon Sep 17 00:00:00 2001 From: codingjourney Date: Mon, 18 Nov 2024 18:26:52 +0100 Subject: [PATCH 10/22] fixed an integer overflow bug in time rendering --- src/displayapp/screens/StopWatch.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index 25b0ad77a5..710c4d443f 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -8,13 +8,13 @@ using namespace Pinetime::Controllers; namespace { TimeSeparated ConvertTicksToTimeSegments(const TickType_t timeElapsed) { - // Centiseconds - const int timeElapsedCentis = timeElapsed * 100 / configTICK_RATE_HZ; + const int timeElapsedSecs = timeElapsed / configTICK_RATE_HZ; + const int timeElapsedFraction = timeElapsed % configTICK_RATE_HZ; - const int hundredths = (timeElapsedCentis % 100); - const int secs = (timeElapsedCentis / 100) % 60; - const int mins = ((timeElapsedCentis / 100) / 60) % 60; - const int hours = ((timeElapsedCentis / 100) / 60) / 60; + const int hundredths = timeElapsedFraction * 100 / configTICK_RATE_HZ; + const int secs = (timeElapsedSecs) % 60; + const int mins = (timeElapsedSecs / 60) % 60; + const int hours = (timeElapsedSecs / 60) / 60; return TimeSeparated {hours, mins, secs, hundredths}; } From d3820f3f0c81ab3f93d66bc3e2479cdb710b6ad6 Mon Sep 17 00:00:00 2001 From: codingjourney Date: Thu, 28 Nov 2024 05:19:34 +0100 Subject: [PATCH 11/22] upper bound for lap numbers --- src/components/stopwatch/StopWatchController.cpp | 2 +- src/components/stopwatch/StopWatchController.h | 3 ++- src/displayapp/screens/StopWatch.cpp | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/stopwatch/StopWatchController.cpp b/src/components/stopwatch/StopWatchController.cpp index 7af8036766..311bccc717 100644 --- a/src/components/stopwatch/StopWatchController.cpp +++ b/src/components/stopwatch/StopWatchController.cpp @@ -34,7 +34,7 @@ void StopWatchController::Clear() { void StopWatchController::AddLapToHistory() { TickType_t lapEnd = GetElapsedTime(); history[0].timeSinceStart = lapEnd; - history[0].number = ++maxLapNumber; + history[0].number = ++maxLapNumber % lapNumberBoundary; history--; } diff --git a/src/components/stopwatch/StopWatchController.h b/src/components/stopwatch/StopWatchController.h index 800ca1b34e..0791701cc2 100644 --- a/src/components/stopwatch/StopWatchController.h +++ b/src/components/stopwatch/StopWatchController.h @@ -55,9 +55,10 @@ namespace Pinetime { // Maximum number of stored laps static constexpr int histSize = 2; + static constexpr int lapNumberBoundary = 1000; // Lap storage Utility::CircularBuffer history; - // Highest lap number; may exceed histSize + // Highest lap number; less than lapNumberBoundary, may exceed histSize int maxLapNumber; }; } diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index 710c4d443f..08bc5ddda2 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -181,7 +181,8 @@ void StopWatch::RenderLaps() { if (lap) { TimeSeparated laptime = ConvertTicksToTimeSegments(lap->timeSinceStart); char buffer[16]; - sprintf(buffer, "#%2d %2d:%02d.%02d\n", lap->number, laptime.mins, laptime.secs, laptime.hundredths); + snprintf(buffer, sizeof(buffer), "#%3d %2d:%02d.%02d\n", + lap->number, laptime.mins, laptime.secs, laptime.hundredths); lv_label_ins_text(lapText, LV_LABEL_POS_LAST, buffer); } else { lv_label_ins_text(lapText, LV_LABEL_POS_LAST, "\n"); From 41da56e81ce99761fe737220e8651e7805da1342 Mon Sep 17 00:00:00 2001 From: codingjourney Date: Thu, 28 Nov 2024 05:20:03 +0100 Subject: [PATCH 12/22] upper bound for elapsed time --- src/components/stopwatch/StopWatchController.cpp | 5 +++-- src/components/stopwatch/StopWatchController.h | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/stopwatch/StopWatchController.cpp b/src/components/stopwatch/StopWatchController.cpp index 311bccc717..64261055a0 100644 --- a/src/components/stopwatch/StopWatchController.cpp +++ b/src/components/stopwatch/StopWatchController.cpp @@ -14,8 +14,8 @@ void StopWatchController::Start() { } void StopWatchController::Pause() { + timeElapsedPreviously = GetElapsedTime(); currentState = StopWatchStates::Paused; - timeElapsedPreviously += xTaskGetTickCount() - startTime; } void StopWatchController::Clear() { @@ -55,7 +55,8 @@ TickType_t StopWatchController::GetElapsedTime() { if (!IsRunning()) { return timeElapsedPreviously; } - return timeElapsedPreviously + (xTaskGetTickCount() - startTime); + TickType_t delta = xTaskGetTickCount() - startTime; + return (timeElapsedPreviously + delta) % elapsedTimeBoundary; } bool StopWatchController::IsRunning() { diff --git a/src/components/stopwatch/StopWatchController.h b/src/components/stopwatch/StopWatchController.h index 0791701cc2..6aaec2b21d 100644 --- a/src/components/stopwatch/StopWatchController.h +++ b/src/components/stopwatch/StopWatchController.h @@ -46,6 +46,8 @@ namespace Pinetime { bool IsPaused(); private: + // Time at which stopwatch wraps around to zero (1000 hours) + TickType_t elapsedTimeBoundary = configTICK_RATE_HZ * 60 * 60 * 1000; // Current state of stopwatch StopWatchStates currentState = StopWatchStates::Cleared; // Start time of current duration @@ -55,6 +57,7 @@ namespace Pinetime { // Maximum number of stored laps static constexpr int histSize = 2; + // Value at which lap numbers wrap around to zero static constexpr int lapNumberBoundary = 1000; // Lap storage Utility::CircularBuffer history; From a715ff30ade5fb6af4ab57d92094720c63d2bc1f Mon Sep 17 00:00:00 2001 From: codingjourney Date: Thu, 28 Nov 2024 05:23:54 +0100 Subject: [PATCH 13/22] fixed layout of lap data --- src/displayapp/screens/StopWatch.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index 08bc5ddda2..9b4ed2129e 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -180,9 +180,14 @@ void StopWatch::RenderLaps() { if (lap) { TimeSeparated laptime = ConvertTicksToTimeSegments(lap->timeSinceStart); - char buffer[16]; - snprintf(buffer, sizeof(buffer), "#%3d %2d:%02d.%02d\n", - lap->number, laptime.mins, laptime.secs, laptime.hundredths); + char buffer[19]; + if (laptime.hours == 0) { + snprintf(buffer, sizeof(buffer), "#%-3d %2d:%02d.%02d\n", + lap->number, laptime.mins, laptime.secs, laptime.hundredths); + } else { + snprintf(buffer, sizeof(buffer), "#%-3d %3d:%02d:%02d.%02d\n", + lap->number, laptime.hours, laptime.mins, laptime.secs, laptime.hundredths); + } lv_label_ins_text(lapText, LV_LABEL_POS_LAST, buffer); } else { lv_label_ins_text(lapText, LV_LABEL_POS_LAST, "\n"); From a6122a289ff48c9b2bc177bf04e0097851c91b5d Mon Sep 17 00:00:00 2001 From: codingjourney Date: Sat, 30 Nov 2024 04:03:58 +0100 Subject: [PATCH 14/22] improved layout, improved re-alignment of time fields --- src/displayapp/screens/StopWatch.cpp | 49 +++++++++++++++------------- src/displayapp/screens/StopWatch.h | 4 ++- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index 9b4ed2129e..5d77fdcc76 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -63,16 +63,19 @@ StopWatch::StopWatch(System::SystemTask& systemTask, StopWatchController& stopWa lv_obj_set_width(lapText, LV_HOR_RES_MAX); lv_obj_align(lapText, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, -btnHeight); - msecTime = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text_static(msecTime, "00"); - lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DISABLED, Colors::lightGray); - lv_obj_align(msecTime, lapText, LV_ALIGN_OUT_TOP_MID, 0, 0); - time = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DISABLED, Colors::lightGray); lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76); lv_label_set_text_static(time, "00:00"); - lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DISABLED, Colors::lightGray); - lv_obj_align(time, msecTime, LV_ALIGN_OUT_TOP_MID, 0, 0); + lv_label_set_long_mode(time, LV_LABEL_LONG_CROP); + lv_label_set_align(time, LV_LABEL_ALIGN_CENTER); + lv_obj_set_width(time, LV_HOR_RES_MAX); + lv_obj_align(time, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 0, 0); + + msecTime = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DISABLED, Colors::lightGray); + lv_label_set_text_static(msecTime, "00"); + lv_obj_align(msecTime, time, LV_ALIGN_OUT_BOTTOM_MID, 0, 0); taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); @@ -131,11 +134,7 @@ void StopWatch::DisplayCleared() { lv_label_set_text_static(time, "00:00"); lv_label_set_text_static(msecTime, "00"); - if (isHoursLabelUpdated) { - lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76); - lv_obj_realign(time); - isHoursLabelUpdated = false; - } + SetHoursVisible(false); lv_label_set_text_static(lapText, ""); lv_label_set_text_static(txtPlayPause, Symbols::play); @@ -145,18 +144,14 @@ void StopWatch::DisplayCleared() { } void StopWatch::RenderTime() { - TimeSeparated currentTimeSeparated = ConvertTicksToTimeSegments(stopWatchController.GetElapsedTime()); - if (currentTimeSeparated.hours == 0) { - lv_label_set_text_fmt(time, "%02d:%02d", currentTimeSeparated.mins, currentTimeSeparated.secs); + TimeSeparated elapsedTime = ConvertTicksToTimeSegments(stopWatchController.GetElapsedTime()); + SetHoursVisible(elapsedTime.hours != 0); + if (!hoursVisible) { + lv_label_set_text_fmt(time, "%02d:%02d", elapsedTime.mins, elapsedTime.secs); } else { - lv_label_set_text_fmt(time, "%02d:%02d:%02d", currentTimeSeparated.hours, currentTimeSeparated.mins, currentTimeSeparated.secs); - if (!isHoursLabelUpdated) { - lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_42); - lv_obj_realign(time); - isHoursLabelUpdated = true; - } + lv_label_set_text_fmt(time, "%02d:%02d:%02d", elapsedTime.hours, elapsedTime.mins, elapsedTime.secs); } - lv_label_set_text_fmt(msecTime, "%02d", currentTimeSeparated.hundredths); + lv_label_set_text_fmt(msecTime, "%02d", elapsedTime.hundredths); } void StopWatch::RenderPause() { @@ -195,6 +190,16 @@ void StopWatch::RenderLaps() { } } +void StopWatch::SetHoursVisible(bool visible) { + if (hoursVisible != visible) { + lv_font_t *font = visible ? &jetbrains_mono_42 : &jetbrains_mono_76; + lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font); + lv_obj_set_height(time, font->line_height); + lv_obj_realign(msecTime); + hoursVisible = visible; + } +} + void StopWatch::Refresh() { if (stopWatchController.IsRunning()) { RenderTime(); diff --git a/src/displayapp/screens/StopWatch.h b/src/displayapp/screens/StopWatch.h index 200f0192e2..f797a49678 100644 --- a/src/displayapp/screens/StopWatch.h +++ b/src/displayapp/screens/StopWatch.h @@ -39,13 +39,15 @@ namespace Pinetime::Applications { void RenderPause(); void RenderLaps(); + void SetHoursVisible(bool visible); + Pinetime::System::WakeLock wakeLock; Controllers::StopWatchController& stopWatchController; TickType_t blinkTime = 0; static constexpr int displayedLaps = 2; lv_obj_t *time, *msecTime, *btnPlayPause, *btnStopLap, *txtPlayPause, *txtStopLap; lv_obj_t* lapText; - bool isHoursLabelUpdated = false; + bool hoursVisible = false; lv_task_t* taskRefresh; }; From 87be94e3fe0faadf28c6d627c691caaee925d894 Mon Sep 17 00:00:00 2001 From: codingjourney Date: Sat, 30 Nov 2024 22:23:31 +0100 Subject: [PATCH 15/22] length of lap list adapting to available space --- src/components/stopwatch/StopWatchController.cpp | 2 +- src/components/stopwatch/StopWatchController.h | 2 +- src/displayapp/screens/StopWatch.cpp | 15 +++++++++------ src/displayapp/screens/StopWatch.h | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/components/stopwatch/StopWatchController.cpp b/src/components/stopwatch/StopWatchController.cpp index 64261055a0..4979ee6844 100644 --- a/src/components/stopwatch/StopWatchController.cpp +++ b/src/components/stopwatch/StopWatchController.cpp @@ -33,9 +33,9 @@ void StopWatchController::Clear() { void StopWatchController::AddLapToHistory() { TickType_t lapEnd = GetElapsedTime(); + history--; history[0].timeSinceStart = lapEnd; history[0].number = ++maxLapNumber % lapNumberBoundary; - history--; } int StopWatchController::GetMaxLapNumber() { diff --git a/src/components/stopwatch/StopWatchController.h b/src/components/stopwatch/StopWatchController.h index 6aaec2b21d..210cbeaac0 100644 --- a/src/components/stopwatch/StopWatchController.h +++ b/src/components/stopwatch/StopWatchController.h @@ -56,7 +56,7 @@ namespace Pinetime { TickType_t timeElapsedPreviously; // Maximum number of stored laps - static constexpr int histSize = 2; + static constexpr int histSize = 4; // Value at which lap numbers wrap around to zero static constexpr int lapNumberBoundary = 1000; // Lap storage diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index 5d77fdcc76..ef5ee6f537 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -57,7 +57,7 @@ StopWatch::StopWatch(System::SystemTask& systemTask, StopWatchController& stopWa lapText = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_color(lapText, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); - lv_label_set_text_static(lapText, "\n"); + lv_label_set_text_static(lapText, ""); lv_label_set_long_mode(lapText, LV_LABEL_LONG_BREAK); lv_label_set_align(lapText, LV_LABEL_ALIGN_CENTER); lv_obj_set_width(lapText, LV_HOR_RES_MAX); @@ -170,24 +170,23 @@ void StopWatch::RenderPause() { void StopWatch::RenderLaps() { lv_label_set_text(lapText, ""); - for (int i = 0; i < displayedLaps; i++) { + for (int i = displayedLaps - 1; i >= 0; i--) { std::optional lap = stopWatchController.GetLapFromHistory(i); if (lap) { TimeSeparated laptime = ConvertTicksToTimeSegments(lap->timeSinceStart); char buffer[19]; if (laptime.hours == 0) { - snprintf(buffer, sizeof(buffer), "#%-3d %2d:%02d.%02d\n", + snprintf(buffer, sizeof(buffer), "\n#%-3d %2d:%02d.%02d", lap->number, laptime.mins, laptime.secs, laptime.hundredths); } else { - snprintf(buffer, sizeof(buffer), "#%-3d %3d:%02d:%02d.%02d\n", + snprintf(buffer, sizeof(buffer), "\n#%-3d %3d:%02d:%02d.%02d", lap->number, laptime.hours, laptime.mins, laptime.secs, laptime.hundredths); } lv_label_ins_text(lapText, LV_LABEL_POS_LAST, buffer); - } else { - lv_label_ins_text(lapText, LV_LABEL_POS_LAST, "\n"); } } + lv_obj_realign(lapText); } void StopWatch::SetHoursVisible(bool visible) { @@ -196,7 +195,11 @@ void StopWatch::SetHoursVisible(bool visible) { lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font); lv_obj_set_height(time, font->line_height); lv_obj_realign(msecTime); + displayedLaps = visible ? 4 : 3; hoursVisible = visible; + if (stopWatchController.GetLapFromHistory(0)) { + RenderLaps(); + } } } diff --git a/src/displayapp/screens/StopWatch.h b/src/displayapp/screens/StopWatch.h index f797a49678..0e27ab00d3 100644 --- a/src/displayapp/screens/StopWatch.h +++ b/src/displayapp/screens/StopWatch.h @@ -44,7 +44,7 @@ namespace Pinetime::Applications { Pinetime::System::WakeLock wakeLock; Controllers::StopWatchController& stopWatchController; TickType_t blinkTime = 0; - static constexpr int displayedLaps = 2; + int displayedLaps = 3; lv_obj_t *time, *msecTime, *btnPlayPause, *btnStopLap, *txtPlayPause, *txtStopLap; lv_obj_t* lapText; bool hoursVisible = false; From a6edd4103dcf934740e273abb57d098ca0e9798b Mon Sep 17 00:00:00 2001 From: codingjourney Date: Wed, 4 Dec 2024 05:29:31 +0100 Subject: [PATCH 16/22] tweaked some margins to improve aesthetics --- src/displayapp/screens/StopWatch.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index ef5ee6f537..fc96011316 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -61,7 +61,7 @@ StopWatch::StopWatch(System::SystemTask& systemTask, StopWatchController& stopWa lv_label_set_long_mode(lapText, LV_LABEL_LONG_BREAK); lv_label_set_align(lapText, LV_LABEL_ALIGN_CENTER); lv_obj_set_width(lapText, LV_HOR_RES_MAX); - lv_obj_align(lapText, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, -btnHeight); + lv_obj_align(lapText, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, -btnHeight - 2); time = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DISABLED, Colors::lightGray); @@ -75,7 +75,7 @@ StopWatch::StopWatch(System::SystemTask& systemTask, StopWatchController& stopWa msecTime = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DISABLED, Colors::lightGray); lv_label_set_text_static(msecTime, "00"); - lv_obj_align(msecTime, time, LV_ALIGN_OUT_BOTTOM_MID, 0, 0); + lv_obj_align(msecTime, time, LV_ALIGN_OUT_BOTTOM_MID, 0, -2); taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); @@ -194,7 +194,8 @@ void StopWatch::SetHoursVisible(bool visible) { lv_font_t *font = visible ? &jetbrains_mono_42 : &jetbrains_mono_76; lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font); lv_obj_set_height(time, font->line_height); - lv_obj_realign(msecTime); + lv_obj_align(time, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 0, visible ? 5 : 0); + lv_obj_align(msecTime, time, LV_ALIGN_OUT_BOTTOM_MID, 0, visible ? 5 : -2); displayedLaps = visible ? 4 : 3; hoursVisible = visible; if (stopWatchController.GetLapFromHistory(0)) { From b9def19ce33c1aa8c47d42321a032c05f69b7cce Mon Sep 17 00:00:00 2001 From: codingjourney Date: Sat, 7 Dec 2024 07:51:48 +0100 Subject: [PATCH 17/22] reduced heap size to fix a build error --- src/FreeRTOSConfig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FreeRTOSConfig.h b/src/FreeRTOSConfig.h index 67c33a34cc..4696e386f0 100644 --- a/src/FreeRTOSConfig.h +++ b/src/FreeRTOSConfig.h @@ -62,7 +62,7 @@ #define configTICK_RATE_HZ 1024 #define configMAX_PRIORITIES (3) #define configMINIMAL_STACK_SIZE (120) -#define configTOTAL_HEAP_SIZE (1024 * 40) +#define configTOTAL_HEAP_SIZE (1024 * 39) #define configMAX_TASK_NAME_LEN (4) #define configUSE_16_BIT_TICKS 0 #define configIDLE_SHOULD_YIELD 1 From 42c59132bac1c308b93af6c6c72569a44f0594eb Mon Sep 17 00:00:00 2001 From: codingjourney Date: Sat, 7 Dec 2024 07:52:32 +0100 Subject: [PATCH 18/22] fixed issues found by the test-format CI job --- src/displayapp/screens/StopWatch.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index fc96011316..506a317b76 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -177,11 +177,16 @@ void StopWatch::RenderLaps() { TimeSeparated laptime = ConvertTicksToTimeSegments(lap->timeSinceStart); char buffer[19]; if (laptime.hours == 0) { - snprintf(buffer, sizeof(buffer), "\n#%-3d %2d:%02d.%02d", - lap->number, laptime.mins, laptime.secs, laptime.hundredths); + snprintf(buffer, sizeof(buffer), "\n#%-3d %2d:%02d.%02d", lap->number, laptime.mins, laptime.secs, laptime.hundredths); } else { - snprintf(buffer, sizeof(buffer), "\n#%-3d %3d:%02d:%02d.%02d", - lap->number, laptime.hours, laptime.mins, laptime.secs, laptime.hundredths); + snprintf(buffer, + sizeof(buffer), + "\n#%-3d %3d:%02d:%02d.%02d", + lap->number, + laptime.hours, + laptime.mins, + laptime.secs, + laptime.hundredths); } lv_label_ins_text(lapText, LV_LABEL_POS_LAST, buffer); } @@ -191,7 +196,7 @@ void StopWatch::RenderLaps() { void StopWatch::SetHoursVisible(bool visible) { if (hoursVisible != visible) { - lv_font_t *font = visible ? &jetbrains_mono_42 : &jetbrains_mono_76; + lv_font_t* font = visible ? &jetbrains_mono_42 : &jetbrains_mono_76; lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, font); lv_obj_set_height(time, font->line_height); lv_obj_align(time, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 0, visible ? 5 : 0); From 1b665bf11479eda32bca5f0fd32c68656f3842f6 Mon Sep 17 00:00:00 2001 From: codingjourney Date: Thu, 12 Dec 2024 08:13:00 +0100 Subject: [PATCH 19/22] elapsedTimeBoundary as constexpr --- src/components/stopwatch/StopWatchController.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/stopwatch/StopWatchController.h b/src/components/stopwatch/StopWatchController.h index 210cbeaac0..bcc9b5515c 100644 --- a/src/components/stopwatch/StopWatchController.h +++ b/src/components/stopwatch/StopWatchController.h @@ -47,7 +47,7 @@ namespace Pinetime { private: // Time at which stopwatch wraps around to zero (1000 hours) - TickType_t elapsedTimeBoundary = configTICK_RATE_HZ * 60 * 60 * 1000; + static constexpr TickType_t elapsedTimeBoundary = (TickType_t) configTICK_RATE_HZ * 60 * 60 * 1000; // Current state of stopwatch StopWatchStates currentState = StopWatchStates::Cleared; // Start time of current duration From 42729f8dd99f67861c0a253d10327505dad20014 Mon Sep 17 00:00:00 2001 From: codingjourney Date: Thu, 12 Dec 2024 08:20:57 +0100 Subject: [PATCH 20/22] prevent unnecessary redrawing of the time label --- src/displayapp/screens/StopWatch.cpp | 15 +++++++++------ src/displayapp/screens/StopWatch.h | 3 +++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index 506a317b76..fffedcc3d5 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -15,7 +15,7 @@ namespace { const int secs = (timeElapsedSecs) % 60; const int mins = (timeElapsedSecs / 60) % 60; const int hours = (timeElapsedSecs / 60) / 60; - return TimeSeparated {hours, mins, secs, hundredths}; + return TimeSeparated {hours, mins, secs, hundredths, timeElapsedSecs}; } void PlayPauseEventHandler(lv_obj_t* obj, lv_event_t event) { @@ -145,11 +145,14 @@ void StopWatch::DisplayCleared() { void StopWatch::RenderTime() { TimeSeparated elapsedTime = ConvertTicksToTimeSegments(stopWatchController.GetElapsedTime()); - SetHoursVisible(elapsedTime.hours != 0); - if (!hoursVisible) { - lv_label_set_text_fmt(time, "%02d:%02d", elapsedTime.mins, elapsedTime.secs); - } else { - lv_label_set_text_fmt(time, "%02d:%02d:%02d", elapsedTime.hours, elapsedTime.mins, elapsedTime.secs); + renderedSeconds = elapsedTime.epochSecs; + if (renderedSeconds.IsUpdated()) { + SetHoursVisible(elapsedTime.hours != 0); + if (!hoursVisible) { + lv_label_set_text_fmt(time, "%02d:%02d", elapsedTime.mins, elapsedTime.secs); + } else { + lv_label_set_text_fmt(time, "%02d:%02d:%02d", elapsedTime.hours, elapsedTime.mins, elapsedTime.secs); + } } lv_label_set_text_fmt(msecTime, "%02d", elapsedTime.hundredths); } diff --git a/src/displayapp/screens/StopWatch.h b/src/displayapp/screens/StopWatch.h index 0e27ab00d3..64eb21966a 100644 --- a/src/displayapp/screens/StopWatch.h +++ b/src/displayapp/screens/StopWatch.h @@ -7,6 +7,7 @@ #include "systemtask/SystemTask.h" #include "systemtask/WakeLock.h" #include "Symbols.h" +#include "utility/DirtyValue.h" namespace Pinetime::Applications { namespace Screens { @@ -16,6 +17,7 @@ namespace Pinetime::Applications { int mins; int secs; int hundredths; + int epochSecs; }; class StopWatch : public Screen { @@ -47,6 +49,7 @@ namespace Pinetime::Applications { int displayedLaps = 3; lv_obj_t *time, *msecTime, *btnPlayPause, *btnStopLap, *txtPlayPause, *txtStopLap; lv_obj_t* lapText; + Utility::DirtyValue renderedSeconds; bool hoursVisible = false; lv_task_t* taskRefresh; From 135e5f877d4f5ccfec83dd261498acbda0761672 Mon Sep 17 00:00:00 2001 From: codingjourney Date: Sat, 14 Dec 2024 07:27:56 +0100 Subject: [PATCH 21/22] tightened declarations of integer fields --- src/components/stopwatch/StopWatchController.cpp | 8 ++++---- src/components/stopwatch/StopWatchController.h | 14 +++++++------- src/displayapp/screens/StopWatch.cpp | 12 ++++++------ src/displayapp/screens/StopWatch.h | 12 ++++++------ 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/components/stopwatch/StopWatchController.cpp b/src/components/stopwatch/StopWatchController.cpp index 4979ee6844..d21f650817 100644 --- a/src/components/stopwatch/StopWatchController.cpp +++ b/src/components/stopwatch/StopWatchController.cpp @@ -22,7 +22,7 @@ void StopWatchController::Clear() { currentState = StopWatchStates::Cleared; timeElapsedPreviously = 0; - for (int i = 0; i < histSize; i++) { + for (uint8_t i = 0; i < histSize; i++) { history[i].number = 0; history[i].timeSinceStart = 0; } @@ -38,12 +38,12 @@ void StopWatchController::AddLapToHistory() { history[0].number = ++maxLapNumber % lapNumberBoundary; } -int StopWatchController::GetMaxLapNumber() { +uint16_t StopWatchController::GetMaxLapNumber() { return maxLapNumber; } -std::optional StopWatchController::GetLapFromHistory(int index) { - if (index < 0 || index >= histSize || history[index].number == 0) { +std::optional StopWatchController::GetLapFromHistory(uint8_t index) { + if (index >= histSize || history[index].number == 0) { return {}; } return history[index]; diff --git a/src/components/stopwatch/StopWatchController.h b/src/components/stopwatch/StopWatchController.h index bcc9b5515c..5c52cf5ea5 100644 --- a/src/components/stopwatch/StopWatchController.h +++ b/src/components/stopwatch/StopWatchController.h @@ -15,7 +15,7 @@ namespace Pinetime { enum class StopWatchStates { Cleared, Running, Paused }; struct LapInfo { - int number = 0; // Used to label the lap + uint16_t number = 0; // Used to label the lap TickType_t timeSinceStart = 0; // Excluding pauses }; @@ -36,10 +36,10 @@ namespace Pinetime { void AddLapToHistory(); /// Returns maxLapNumber - int GetMaxLapNumber(); + uint16_t GetMaxLapNumber(); /// Indexes into lap history, with 0 being the latest lap. - std::optional GetLapFromHistory(int index); + std::optional GetLapFromHistory(uint8_t index); bool IsRunning(); bool IsCleared(); @@ -47,7 +47,7 @@ namespace Pinetime { private: // Time at which stopwatch wraps around to zero (1000 hours) - static constexpr TickType_t elapsedTimeBoundary = (TickType_t) configTICK_RATE_HZ * 60 * 60 * 1000; + static constexpr TickType_t elapsedTimeBoundary = static_cast(configTICK_RATE_HZ) * 60 * 60 * 1000; // Current state of stopwatch StopWatchStates currentState = StopWatchStates::Cleared; // Start time of current duration @@ -56,13 +56,13 @@ namespace Pinetime { TickType_t timeElapsedPreviously; // Maximum number of stored laps - static constexpr int histSize = 4; + static constexpr uint8_t histSize = 4; // Value at which lap numbers wrap around to zero - static constexpr int lapNumberBoundary = 1000; + static constexpr uint16_t lapNumberBoundary = 1000; // Lap storage Utility::CircularBuffer history; // Highest lap number; less than lapNumberBoundary, may exceed histSize - int maxLapNumber; + uint16_t maxLapNumber; }; } } diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index fffedcc3d5..d2f4047960 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -8,13 +8,13 @@ using namespace Pinetime::Controllers; namespace { TimeSeparated ConvertTicksToTimeSegments(const TickType_t timeElapsed) { - const int timeElapsedSecs = timeElapsed / configTICK_RATE_HZ; - const int timeElapsedFraction = timeElapsed % configTICK_RATE_HZ; + const uint32_t timeElapsedSecs = timeElapsed / configTICK_RATE_HZ; + const uint16_t timeElapsedFraction = timeElapsed % configTICK_RATE_HZ; - const int hundredths = timeElapsedFraction * 100 / configTICK_RATE_HZ; - const int secs = (timeElapsedSecs) % 60; - const int mins = (timeElapsedSecs / 60) % 60; - const int hours = (timeElapsedSecs / 60) / 60; + const uint8_t hundredths = timeElapsedFraction * 100 / configTICK_RATE_HZ; + const uint8_t secs = (timeElapsedSecs) % 60; + const uint8_t mins = (timeElapsedSecs / 60) % 60; + const uint16_t hours = (timeElapsedSecs / 60) / 60; return TimeSeparated {hours, mins, secs, hundredths, timeElapsedSecs}; } diff --git a/src/displayapp/screens/StopWatch.h b/src/displayapp/screens/StopWatch.h index 64eb21966a..2d8d67aec1 100644 --- a/src/displayapp/screens/StopWatch.h +++ b/src/displayapp/screens/StopWatch.h @@ -13,11 +13,11 @@ namespace Pinetime::Applications { namespace Screens { struct TimeSeparated { - int hours; - int mins; - int secs; - int hundredths; - int epochSecs; + uint16_t hours; + uint8_t mins; + uint8_t secs; + uint8_t hundredths; + uint32_t epochSecs; }; class StopWatch : public Screen { @@ -46,7 +46,7 @@ namespace Pinetime::Applications { Pinetime::System::WakeLock wakeLock; Controllers::StopWatchController& stopWatchController; TickType_t blinkTime = 0; - int displayedLaps = 3; + uint8_t displayedLaps = 3; lv_obj_t *time, *msecTime, *btnPlayPause, *btnStopLap, *txtPlayPause, *txtStopLap; lv_obj_t* lapText; Utility::DirtyValue renderedSeconds; From 5c2dba163660aaf9cf825103df53371d0007ce77 Mon Sep 17 00:00:00 2001 From: codingjourney Date: Sat, 14 Dec 2024 07:56:54 +0100 Subject: [PATCH 22/22] lap times without leading zeroes --- src/displayapp/screens/StopWatch.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp index d2f4047960..026260cbfa 100644 --- a/src/displayapp/screens/StopWatch.cpp +++ b/src/displayapp/screens/StopWatch.cpp @@ -179,17 +179,24 @@ void StopWatch::RenderLaps() { if (lap) { TimeSeparated laptime = ConvertTicksToTimeSegments(lap->timeSinceStart); char buffer[19]; - if (laptime.hours == 0) { - snprintf(buffer, sizeof(buffer), "\n#%-3d %2d:%02d.%02d", lap->number, laptime.mins, laptime.secs, laptime.hundredths); - } else { - snprintf(buffer, - sizeof(buffer), - "\n#%-3d %3d:%02d:%02d.%02d", + if (laptime.hours > 0) { + snprintf(buffer, sizeof(buffer), "\n#%-3d %3d:%02d:%02d.%02d", lap->number, laptime.hours, laptime.mins, laptime.secs, laptime.hundredths); + } else if (laptime.mins > 0) { + snprintf(buffer, sizeof(buffer), "\n#%-3d %2d:%02d.%02d", + lap->number, + laptime.mins, + laptime.secs, + laptime.hundredths); + } else { + snprintf(buffer, sizeof(buffer), "\n#%-3d %2d.%02d", + lap->number, + laptime.secs, + laptime.hundredths); } lv_label_ins_text(lapText, LV_LABEL_POS_LAST, buffer); }