From 99321f0cd3181d4cd6a00f69cd9b554c63c52849 Mon Sep 17 00:00:00 2001 From: Michele Bini Date: Thu, 31 Mar 2022 14:52:38 +0200 Subject: Add back stopwatch and timer diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 20e0dcb..5f8da60 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -387,6 +387,7 @@ list(APPEND SOURCE_FILES displayapp/screens/Screen.cpp displayapp/screens/Clock.cpp displayapp/screens/Tile.cpp + displayapp/screens/StopWatch.h displayapp/screens/BatteryIcon.cpp displayapp/screens/BleIcon.cpp displayapp/screens/NotificationIcon.cpp @@ -398,6 +399,7 @@ list(APPEND SOURCE_FILES displayapp/screens/Notifications.cpp displayapp/screens/FlashLight.cpp displayapp/screens/List.cpp + displayapp/screens/Timer.cpp displayapp/screens/Error.cpp displayapp/screens/Alarm.cpp displayapp/screens/Styles.cpp @@ -447,6 +449,7 @@ list(APPEND SOURCE_FILES components/firmwarevalidator/FirmwareValidator.cpp components/motor/MotorController.cpp components/settings/Settings.cpp + components/timer/TimerController.cpp components/alarm/AlarmController.cpp drivers/Cst816s.cpp FreeRTOS/port.c @@ -500,6 +503,7 @@ list(APPEND RECOVERY_SOURCE_FILES components/ble/ServiceDiscovery.cpp components/firmwarevalidator/FirmwareValidator.cpp components/settings/Settings.cpp + components/timer/TimerController.cpp components/alarm/AlarmController.cpp drivers/Cst816s.cpp FreeRTOS/port.c @@ -550,6 +554,7 @@ set(INCLUDE_FILES displayapp/screens/Clock.h displayapp/screens/Tile.h displayapp/screens/Meter.h + displayapp/screens/StopWatch.h displayapp/screens/BatteryIcon.h displayapp/screens/BleIcon.h displayapp/screens/NotificationIcon.h @@ -561,6 +566,7 @@ set(INCLUDE_FILES displayapp/screens/ApplicationList.h displayapp/Apps.h displayapp/screens/Notifications.h + displayapp/screens/Timer.h displayapp/screens/Alarm.h displayapp/Colors.h drivers/St7789.h @@ -595,6 +601,7 @@ set(INCLUDE_FILES components/ble/ServiceDiscovery.h components/ble/BleClient.h components/settings/Settings.h + components/timer/TimerController.h components/alarm/AlarmController.h drivers/Cst816s.h FreeRTOS/portmacro.h diff --git a/src/components/motor/MotorController.cpp b/src/components/motor/MotorController.cpp index bee07fb..c794a02 100644 --- a/src/components/motor/MotorController.cpp +++ b/src/components/motor/MotorController.cpp @@ -12,6 +12,7 @@ using namespace Pinetime::Controllers; void MotorController::Init() { nrf_gpio_cfg_output(PinMap::Motor); nrf_gpio_pin_set(PinMap::Motor); + app_timer_init(); app_timer_create(&shortVibTimer, APP_TIMER_MODE_SINGLE_SHOT, StopMotor); app_timer_create(&longVibTimer, APP_TIMER_MODE_REPEATED, Ring); diff --git a/src/components/timer/TimerController.cpp b/src/components/timer/TimerController.cpp new file mode 100644 index 0000000..79e44d6 --- /dev/null +++ b/src/components/timer/TimerController.cpp @@ -0,0 +1,69 @@ +// +// Created by florian on 16.05.21. +// + +#include "components/timer/TimerController.h" +#include "systemtask/SystemTask.h" +#include "app_timer.h" +#include "task.h" + +using namespace Pinetime::Controllers; + + +APP_TIMER_DEF(timerAppTimer); + +namespace { + void TimerEnd(void* p_context) { + auto* controller = static_cast (p_context); + if(controller != nullptr) + controller->OnTimerEnd(); + } +} + + +void TimerController::Init() { + app_timer_create(&timerAppTimer, APP_TIMER_MODE_SINGLE_SHOT, TimerEnd); +} + +void TimerController::StartTimer(uint32_t duration) { + app_timer_stop(timerAppTimer); + auto currentTicks = xTaskGetTickCount(); + app_timer_start(timerAppTimer, APP_TIMER_TICKS(duration), this); + endTicks = currentTicks + APP_TIMER_TICKS(duration); + timerRunning = true; +} + +uint32_t TimerController::GetTimeRemaining() { + if (!timerRunning) { + return 0; + } + auto currentTicks = xTaskGetTickCount(); + + TickType_t deltaTicks = 0; + if (currentTicks > endTicks) { + deltaTicks = 0xffffffff - currentTicks; + deltaTicks += (endTicks + 1); + } else { + deltaTicks = endTicks - currentTicks; + } + + return (static_cast(deltaTicks) / static_cast(configTICK_RATE_HZ)) * 1000; +} + +void TimerController::StopTimer() { + app_timer_stop(timerAppTimer); + timerRunning = false; +} + +bool TimerController::IsRunning() { + return timerRunning; +} +void TimerController::OnTimerEnd() { + timerRunning = false; + if(systemTask != nullptr) + systemTask->PushMessage(System::Messages::OnTimerDone); +} + +void TimerController::Register(Pinetime::System::SystemTask* systemTask) { + this->systemTask = systemTask; +} diff --git a/src/components/timer/TimerController.h b/src/components/timer/TimerController.h new file mode 100644 index 0000000..fa7bc90 --- /dev/null +++ b/src/components/timer/TimerController.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include "app_timer.h" +#include "portmacro_cmsis.h" + +namespace Pinetime { + namespace System { + class SystemTask; + } + namespace Controllers { + + class TimerController { + public: + TimerController() = default; + + void Init(); + + void StartTimer(uint32_t duration); + + void StopTimer(); + + uint32_t GetTimeRemaining(); + + bool IsRunning(); + + void OnTimerEnd(); + + void Register(System::SystemTask* systemTask); + + private: + System::SystemTask* systemTask = nullptr; + TickType_t endTicks; + bool timerRunning = false; + }; + } +} \ No newline at end of file diff --git a/src/displayapp/Apps.h b/src/displayapp/Apps.h index 3b125d4..2ef5f13 100644 --- a/src/displayapp/Apps.h +++ b/src/displayapp/Apps.h @@ -10,8 +10,9 @@ namespace Pinetime { FirmwareValidation, NotificationsPreview, Notifications, - FlashLight, Alarm, + Timer, + FlashLight, QuickSettings, Settings, SettingTimeFormat, diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index a841f1e..5a250b5 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -1,5 +1,6 @@ #include "displayapp/DisplayApp.h" #include +#include "displayapp/screens/Timer.h" #include "displayapp/screens/Alarm.h" #include "components/battery/BatteryController.h" #include "components/ble/BleController.h" @@ -12,6 +13,7 @@ #include "displayapp/screens/Clock.h" #include "displayapp/screens/FirmwareUpdate.h" #include "displayapp/screens/FirmwareValidation.h" +#include "displayapp/screens/StopWatch.h" #include "displayapp/screens/Notifications.h" #include "displayapp/screens/Tile.h" #include "displayapp/screens/FlashLight.h" @@ -74,6 +76,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd, Controllers::Settings& settingsController, Pinetime::Controllers::MotorController& motorController, Pinetime::Controllers::MotionController& motionController, + Pinetime::Controllers::TimerController& timerController, Pinetime::Controllers::AlarmController& alarmController, Pinetime::Controllers::BrightnessController& brightnessController, Pinetime::Controllers::TouchHandler& touchHandler) @@ -88,6 +91,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd, settingsController {settingsController}, motorController {motorController}, motionController {motionController}, + timerController {timerController}, alarmController {alarmController}, brightnessController {brightnessController}, touchHandler {touchHandler} { @@ -179,6 +183,14 @@ void DisplayApp::Refresh() { case Messages::NewNotification: LoadApp(Apps::NotificationsPreview, DisplayApp::FullRefreshDirections::Down); break; + case Messages::TimerDone: + if (currentApp == Apps::Timer) { + auto* timer = static_cast(currentScreen.get()); + timer->setDone(); + } else { + LoadApp(Apps::Timer, DisplayApp::FullRefreshDirections::Down); + } + break; case Messages::AlarmTriggered: if (currentApp == Apps::Alarm) { auto* alarm = static_cast(currentScreen.get()); @@ -335,6 +347,9 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) this, notificationManager, systemTask->nimble().alertService(), motorController, *systemTask, Screens::Notifications::Modes::Preview); ReturnApp(Apps::Clock, FullRefreshDirections::Up, TouchEvents::SwipeUp); break; + case Apps::Timer: + currentScreen = std::make_unique(this, timerController); + break; case Apps::Alarm: currentScreen = std::make_unique(this, alarmController, settingsController, *systemTask); break; @@ -369,6 +384,9 @@ void DisplayApp::LoadApp(Apps app, DisplayApp::FullRefreshDirections direction) currentScreen = std::make_unique(this, *systemTask, brightnessController); ReturnApp(Apps::QuickSettings, FullRefreshDirections::Down, TouchEvents::SwipeDown); break; + case Apps::StopWatch: + currentScreen = std::make_unique(this, *systemTask); + break; } currentApp = app; } diff --git a/src/displayapp/DisplayApp.h b/src/displayapp/DisplayApp.h index b5186dd..14663b1 100644 --- a/src/displayapp/DisplayApp.h +++ b/src/displayapp/DisplayApp.h @@ -13,6 +13,7 @@ #include "components/firmwarevalidator/FirmwareValidator.h" #include "components/settings/Settings.h" #include "displayapp/screens/Screen.h" +#include "components/timer/TimerController.h" #include "components/alarm/AlarmController.h" #include "touchhandler/TouchHandler.h" @@ -56,6 +57,7 @@ namespace Pinetime { Controllers::Settings& settingsController, Pinetime::Controllers::MotorController& motorController, Pinetime::Controllers::MotionController& motionController, + Pinetime::Controllers::TimerController& timerController, Pinetime::Controllers::AlarmController& alarmController, Pinetime::Controllers::BrightnessController& brightnessController, Pinetime::Controllers::TouchHandler& touchHandler); @@ -81,6 +83,7 @@ namespace Pinetime { Pinetime::Controllers::Settings& settingsController; Pinetime::Controllers::MotorController& motorController; Pinetime::Controllers::MotionController& motionController; + Pinetime::Controllers::TimerController& timerController; 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 4a3ddf6..617cb38 100644 --- a/src/displayapp/DisplayAppRecovery.cpp +++ b/src/displayapp/DisplayAppRecovery.cpp @@ -20,6 +20,7 @@ DisplayApp::DisplayApp(Drivers::St7789& lcd, Controllers::Settings& settingsController, Pinetime::Controllers::MotorController& motorController, Pinetime::Controllers::MotionController& motionController, + Pinetime::Controllers::TimerController& timerController, 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 b358c11..b4393af 100644 --- a/src/displayapp/DisplayAppRecovery.h +++ b/src/displayapp/DisplayAppRecovery.h @@ -54,6 +54,7 @@ namespace Pinetime { Controllers::Settings& settingsController, Pinetime::Controllers::MotorController& motorController, Pinetime::Controllers::MotionController& motionController, + Pinetime::Controllers::TimerController& timerController, Pinetime::Controllers::AlarmController& alarmController, Pinetime::Controllers::BrightnessController& brightnessController, Pinetime::Controllers::TouchHandler& touchHandler); diff --git a/src/displayapp/Messages.h b/src/displayapp/Messages.h index 2891d90..a58f6e5 100644 --- a/src/displayapp/Messages.h +++ b/src/displayapp/Messages.h @@ -14,6 +14,7 @@ namespace Pinetime { ButtonLongerPressed, ButtonDoubleClicked, NewNotification, + TimerDone, BleFirmwareUpdateStarted, UpdateTimeOut, DimScreen, diff --git a/src/displayapp/screens/ApplicationList.cpp b/src/displayapp/screens/ApplicationList.cpp index 5daafd1..d49d493 100644 --- a/src/displayapp/screens/ApplicationList.cpp +++ b/src/displayapp/screens/ApplicationList.cpp @@ -36,6 +36,8 @@ bool ApplicationList::OnTouchEvent(Pinetime::Applications::TouchEvents event) { std::unique_ptr ApplicationList::CreateScreen1() { std::array applications {{ + {Symbols::stopWatch, Apps::StopWatch}, + {Symbols::hourGlass, Apps::Timer}, {Symbols::clock, Apps::Alarm}, }}; diff --git a/src/displayapp/screens/StopWatch.cpp b/src/displayapp/screens/StopWatch.cpp new file mode 100644 index 0000000..8749839 --- /dev/null +++ b/src/displayapp/screens/StopWatch.cpp @@ -0,0 +1,200 @@ +#include "displayapp/screens/StopWatch.h" + +#include "displayapp/screens/Screen.h" +#include "displayapp/screens/Symbols.h" +#include +#include +#include + +using namespace Pinetime::Applications::Screens; + +// Anonymous namespace for local functions +namespace { + TimeSeparated_t convertTicksToTimeSegments(const TickType_t timeElapsed) { + const int timeElapsedMillis = (static_cast(timeElapsed) / static_cast(configTICK_RATE_HZ)) * 1000; + + const int hundredths = (timeElapsedMillis % 1000) / 10; // Get only the first two digits and ignore the last + const int secs = (timeElapsedMillis / 1000) % 60; + const int mins = (timeElapsedMillis / 1000) / 60; + return TimeSeparated_t {mins, secs, hundredths}; + } + + 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; + } +} + +static void play_pause_event_handler(lv_obj_t* obj, lv_event_t event) { + auto stopWatch = static_cast(obj->user_data); + stopWatch->playPauseBtnEventHandler(event); +} + +static void stop_lap_event_handler(lv_obj_t* obj, lv_event_t event) { + auto stopWatch = static_cast(obj->user_data); + stopWatch->stopLapBtnEventHandler(event); +} + +StopWatch::StopWatch(DisplayApp* app, System::SystemTask& systemTask) + : Screen(app), + systemTask {systemTask}, + currentState {States::Init}, + startTime {}, + oldTimeElapsed {}, + currentTimeSeparated {}, + lapBuffer {}, + lapNr {} { + + time = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76); + lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); + lv_label_set_text(time, "00:00"); + lv_obj_align(time, lv_scr_act(), LV_ALIGN_CENTER, 0, -45); + + msecTime = lv_label_create(lv_scr_act(), nullptr); + // lv_obj_set_style_local_text_font(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_20); + lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); + lv_label_set_text(msecTime, "00"); + lv_obj_align(msecTime, lv_scr_act(), LV_ALIGN_CENTER, 0, 3); + + 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_height(btnPlayPause, 50); + lv_obj_set_width(btnPlayPause, 115); + lv_obj_align(btnPlayPause, lv_scr_act(), LV_ALIGN_IN_BOTTOM_RIGHT, 0, 0); + txtPlayPause = lv_label_create(btnPlayPause, nullptr); + lv_label_set_text(txtPlayPause, Symbols::play); + + 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_height(btnStopLap, 50); + lv_obj_set_width(btnStopLap, 115); + lv_obj_align(btnStopLap, lv_scr_act(), LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); + lv_obj_set_style_local_bg_color(btnStopLap, LV_BTN_PART_MAIN, LV_STATE_DISABLED, lv_color_hex(0x080808)); + txtStopLap = lv_label_create(btnStopLap, nullptr); + lv_obj_set_style_local_text_color(txtStopLap, LV_BTN_PART_MAIN, LV_STATE_DISABLED, lv_color_hex(0x888888)); + lv_label_set_text(txtStopLap, Symbols::stop); + lv_obj_set_state(btnStopLap, LV_STATE_DISABLED); + lv_obj_set_state(txtStopLap, LV_STATE_DISABLED); + + lapOneText = lv_label_create(lv_scr_act(), nullptr); + // lv_obj_set_style_local_text_font(lapOneText, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_20); + lv_obj_set_style_local_text_color(lapOneText, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_YELLOW); + lv_obj_align(lapOneText, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 50, 30); + lv_label_set_text(lapOneText, ""); + + lapTwoText = lv_label_create(lv_scr_act(), nullptr); + // lv_obj_set_style_local_text_font(lapTwoText, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_bold_20); + lv_obj_set_style_local_text_color(lapTwoText, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_YELLOW); + lv_obj_align(lapTwoText, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 50, 55); + lv_label_set_text(lapTwoText, ""); + + taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); +} + +StopWatch::~StopWatch() { + lv_task_del(taskRefresh); + systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping); + lv_obj_clean(lv_scr_act()); +} + +void StopWatch::reset() { + currentState = States::Init; + oldTimeElapsed = 0; + lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); + lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); + + lv_label_set_text(time, "00:00"); + lv_label_set_text(msecTime, "00"); + + lv_label_set_text(lapOneText, ""); + lv_label_set_text(lapTwoText, ""); + lapBuffer.clearBuffer(); + lapNr = 0; + lv_obj_set_state(btnStopLap, LV_STATE_DISABLED); + lv_obj_set_state(txtStopLap, LV_STATE_DISABLED); +} + +void StopWatch::start() { + lv_obj_set_state(btnStopLap, LV_STATE_DEFAULT); + lv_obj_set_state(txtStopLap, LV_STATE_DEFAULT); + lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN); + lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN); + lv_label_set_text(txtPlayPause, Symbols::pause); + lv_label_set_text(txtStopLap, Symbols::lapsFlag); + startTime = xTaskGetTickCount(); + currentState = States::Running; + systemTask.PushMessage(Pinetime::System::Messages::DisableSleeping); +} + +void StopWatch::pause() { + startTime = 0; + // Store the current time elapsed in cache + oldTimeElapsed += timeElapsed; + currentState = States::Halted; + lv_label_set_text(txtPlayPause, Symbols::play); + lv_label_set_text(txtStopLap, Symbols::stop); + lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_YELLOW); + lv_obj_set_style_local_text_color(msecTime, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_YELLOW); + systemTask.PushMessage(Pinetime::System::Messages::EnableSleeping); +} + +void StopWatch::Refresh() { + if (currentState == States::Running) { + timeElapsed = calculateDelta(startTime, xTaskGetTickCount()); + currentTimeSeparated = convertTicksToTimeSegments((oldTimeElapsed + timeElapsed)); + + lv_label_set_text_fmt(time, "%02d:%02d", currentTimeSeparated.mins, currentTimeSeparated.secs); + lv_label_set_text_fmt(msecTime, "%02d", currentTimeSeparated.hundredths); + } +} + +void StopWatch::playPauseBtnEventHandler(lv_event_t event) { + if (event != LV_EVENT_CLICKED) { + return; + } + if (currentState == States::Init) { + start(); + } else if (currentState == States::Running) { + pause(); + } else if (currentState == States::Halted) { + start(); + } +} + +void StopWatch::stopLapBtnEventHandler(lv_event_t event) { + if (event != LV_EVENT_CLICKED) { + return; + } + // If running, then this button is used to save laps + if (currentState == States::Running) { + lapBuffer.addLaps(currentTimeSeparated); + lapNr++; + if (lapBuffer[1]) { + lv_label_set_text_fmt( + lapOneText, "#%2d %2d:%02d.%02d", (lapNr - 1), lapBuffer[1]->mins, lapBuffer[1]->secs, lapBuffer[1]->hundredths); + } + if (lapBuffer[0]) { + lv_label_set_text_fmt(lapTwoText, "#%2d %2d:%02d.%02d", lapNr, lapBuffer[0]->mins, lapBuffer[0]->secs, lapBuffer[0]->hundredths); + } + } else if (currentState == States::Halted) { + reset(); + } +} + +bool StopWatch::OnButtonPushed() { + if (currentState == States::Running) { + pause(); + return true; + } + return false; +} diff --git a/src/displayapp/screens/StopWatch.h b/src/displayapp/screens/StopWatch.h new file mode 100644 index 0000000..06193f6 --- /dev/null +++ b/src/displayapp/screens/StopWatch.h @@ -0,0 +1,91 @@ +#pragma once + +#include "displayapp/screens/Screen.h" +#include "components/datetime/DateTimeController.h" +#include "displayapp/LittleVgl.h" + +#include +#include "portmacro_cmsis.h" + +#include +#include "systemtask/SystemTask.h" + +namespace Pinetime::Applications::Screens { + + enum class States { Init, Running, Halted }; + + struct TimeSeparated_t { + int mins; + int secs; + int hundredths; + }; + + // A simple buffer to hold the latest two laps + template struct LapTextBuffer_t { + LapTextBuffer_t() : buffer {}, currentSize {}, capacity {N}, head {-1} { + } + + void addLaps(const TimeSeparated_t& timeVal) { + head++; + head %= capacity; + buffer[head] = timeVal; + + if (currentSize < capacity) { + currentSize++; + } + } + + void clearBuffer() { + buffer = {}; + currentSize = 0; + head = -1; + } + + TimeSeparated_t* operator[](std::size_t idx) { + // Sanity check for out-of-bounds + if (idx >= 0 && idx < capacity) { + if (idx < currentSize) { + // This transformation is to ensure that head is always pointing to index 0. + const auto transformed_idx = (head - idx) % capacity; + return (&buffer[transformed_idx]); + } + } + return nullptr; + } + + private: + std::array buffer; + uint8_t currentSize; + uint8_t capacity; + int8_t head; + }; + + class StopWatch : public Screen { + public: + StopWatch(DisplayApp* app, System::SystemTask& systemTask); + ~StopWatch() override; + void Refresh() override; + + void playPauseBtnEventHandler(lv_event_t event); + void stopLapBtnEventHandler(lv_event_t event); + bool OnButtonPushed() override; + + void reset(); + void start(); + void pause(); + + private: + Pinetime::System::SystemTask& systemTask; + TickType_t timeElapsed; + States currentState; + TickType_t startTime; + TickType_t oldTimeElapsed; + TimeSeparated_t currentTimeSeparated; // Holds Mins, Secs, millisecs + LapTextBuffer_t<2> lapBuffer; + int lapNr = 0; + lv_obj_t *time, *msecTime, *btnPlayPause, *btnStopLap, *txtPlayPause, *txtStopLap; + lv_obj_t *lapOneText, *lapTwoText; + + lv_task_t* taskRefresh; + }; +} diff --git a/src/displayapp/screens/Timer.cpp b/src/displayapp/screens/Timer.cpp new file mode 100644 index 0000000..a5e4019 --- /dev/null +++ b/src/displayapp/screens/Timer.cpp @@ -0,0 +1,161 @@ +#include "displayapp/screens/Timer.h" + +#include "displayapp/screens/Screen.h" +#include "displayapp/screens/Symbols.h" +#include + +using namespace Pinetime::Applications::Screens; + +static void btnEventHandler(lv_obj_t* obj, lv_event_t event) { + Timer* screen = static_cast(obj->user_data); + screen->OnButtonEvent(obj, event); +} + +void Timer::createButtons() { + btnMinutesUp = lv_btn_create(lv_scr_act(), nullptr); + btnMinutesUp->user_data = this; + lv_obj_set_event_cb(btnMinutesUp, btnEventHandler); + lv_obj_align(btnMinutesUp, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 20, -80); + lv_obj_set_height(btnMinutesUp, 40); + lv_obj_set_width(btnMinutesUp, 60); + txtMUp = lv_label_create(btnMinutesUp, nullptr); + lv_label_set_text(txtMUp, "+"); + + btnMinutesDown = lv_btn_create(lv_scr_act(), nullptr); + btnMinutesDown->user_data = this; + lv_obj_set_event_cb(btnMinutesDown, btnEventHandler); + lv_obj_align(btnMinutesDown, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 20, +40); + lv_obj_set_height(btnMinutesDown, 40); + lv_obj_set_width(btnMinutesDown, 60); + txtMDown = lv_label_create(btnMinutesDown, nullptr); + lv_label_set_text(txtMDown, "-"); + + btnSecondsUp = lv_btn_create(lv_scr_act(), nullptr); + btnSecondsUp->user_data = this; + lv_obj_set_event_cb(btnSecondsUp, btnEventHandler); + lv_obj_align(btnSecondsUp, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 10, -80); + lv_obj_set_height(btnSecondsUp, 40); + lv_obj_set_width(btnSecondsUp, 60); + txtSUp = lv_label_create(btnSecondsUp, nullptr); + lv_label_set_text(txtSUp, "+"); + + btnSecondsDown = lv_btn_create(lv_scr_act(), nullptr); + btnSecondsDown->user_data = this; + lv_obj_set_event_cb(btnSecondsDown, btnEventHandler); + lv_obj_align(btnSecondsDown, lv_scr_act(), LV_ALIGN_IN_RIGHT_MID, 10, +40); + lv_obj_set_height(btnSecondsDown, 40); + lv_obj_set_width(btnSecondsDown, 60); + txtSDown = lv_label_create(btnSecondsDown, nullptr); + lv_label_set_text(txtSDown, "-"); +} + +Timer::Timer(DisplayApp* app, Controllers::TimerController& timerController) + : Screen(app), running {true}, timerController {timerController} { + + time = lv_label_create(lv_scr_act(), nullptr); + lv_obj_set_style_local_text_font(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76); + lv_obj_set_style_local_text_color(time, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); + + uint32_t seconds = timerController.GetTimeRemaining() / 1000; + lv_label_set_text_fmt(time, "%02lu:%02lu", seconds / 60, seconds % 60); + + lv_obj_align(time, lv_scr_act(), LV_ALIGN_IN_LEFT_MID, 0, -20); + + btnPlayPause = lv_btn_create(lv_scr_act(), nullptr); + btnPlayPause->user_data = this; + lv_obj_set_event_cb(btnPlayPause, btnEventHandler); + lv_obj_align(btnPlayPause, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, -10); + lv_obj_set_height(btnPlayPause, 40); + txtPlayPause = lv_label_create(btnPlayPause, nullptr); + if (timerController.IsRunning()) { + lv_label_set_text(txtPlayPause, Symbols::pause); + } else { + lv_label_set_text(txtPlayPause, Symbols::play); + createButtons(); + } + + taskRefresh = lv_task_create(RefreshTaskCallback, LV_DISP_DEF_REFR_PERIOD, LV_TASK_PRIO_MID, this); +} + +Timer::~Timer() { + lv_task_del(taskRefresh); + lv_obj_clean(lv_scr_act()); +} + +void Timer::Refresh() { + if (timerController.IsRunning()) { + uint32_t seconds = timerController.GetTimeRemaining() / 1000; + lv_label_set_text_fmt(time, "%02lu:%02lu", seconds / 60, seconds % 60); + } +} + +void Timer::OnButtonEvent(lv_obj_t* obj, lv_event_t event) { + if (event == LV_EVENT_CLICKED) { + if (obj == btnPlayPause) { + if (timerController.IsRunning()) { + lv_label_set_text(txtPlayPause, Symbols::play); + uint32_t seconds = timerController.GetTimeRemaining() / 1000; + minutesToSet = seconds / 60; + secondsToSet = seconds % 60; + timerController.StopTimer(); + createButtons(); + + } else if (secondsToSet + minutesToSet > 0) { + lv_label_set_text(txtPlayPause, Symbols::pause); + timerController.StartTimer((secondsToSet + minutesToSet * 60) * 1000); + + lv_obj_del(btnSecondsDown); + btnSecondsDown = nullptr; + lv_obj_del(btnSecondsUp); + btnSecondsUp = nullptr; + lv_obj_del(btnMinutesDown); + btnMinutesDown = nullptr; + lv_obj_del(btnMinutesUp); + btnMinutesUp = nullptr; + } + } else { + if (!timerController.IsRunning()) { + if (obj == btnMinutesUp) { + if (minutesToSet >= 59) { + minutesToSet = 0; + } else { + minutesToSet++; + } + lv_label_set_text_fmt(time, "%02d:%02d", minutesToSet, secondsToSet); + + } else if (obj == btnMinutesDown) { + if (minutesToSet == 0) { + minutesToSet = 59; + } else { + minutesToSet--; + } + lv_label_set_text_fmt(time, "%02d:%02d", minutesToSet, secondsToSet); + + } else if (obj == btnSecondsUp) { + if (secondsToSet >= 59) { + secondsToSet = 0; + } else { + secondsToSet++; + } + lv_label_set_text_fmt(time, "%02d:%02d", minutesToSet, secondsToSet); + + } else if (obj == btnSecondsDown) { + if (secondsToSet == 0) { + secondsToSet = 59; + } else { + secondsToSet--; + } + lv_label_set_text_fmt(time, "%02d:%02d", minutesToSet, secondsToSet); + } + } + } + } +} + +void Timer::setDone() { + lv_label_set_text(time, "00:00"); + lv_label_set_text(txtPlayPause, Symbols::play); + secondsToSet = 0; + minutesToSet = 0; + createButtons(); +} diff --git a/src/displayapp/screens/Timer.h b/src/displayapp/screens/Timer.h new file mode 100644 index 0000000..23c8734 --- /dev/null +++ b/src/displayapp/screens/Timer.h @@ -0,0 +1,39 @@ +#pragma once + +#include "displayapp/screens/Screen.h" +#include "components/datetime/DateTimeController.h" +#include "systemtask/SystemTask.h" +#include "displayapp/LittleVgl.h" + +#include "components/timer/TimerController.h" + +namespace Pinetime::Applications::Screens { + + class Timer : public Screen { + public: + enum class Modes { Normal, Done }; + + Timer(DisplayApp* app, Controllers::TimerController& timerController); + + ~Timer() override; + + void Refresh() override; + + void setDone(); + + void OnButtonEvent(lv_obj_t* obj, lv_event_t event); + + private: + bool running; + uint8_t secondsToSet = 0; + uint8_t minutesToSet = 0; + Controllers::TimerController& timerController; + + void createButtons(); + + lv_obj_t *time, *msecTime, *btnPlayPause, *txtPlayPause, *btnMinutesUp, *btnMinutesDown, *btnSecondsUp, *btnSecondsDown, *txtMUp, + *txtMDown, *txtSUp, *txtSDown; + + lv_task_t* taskRefresh; + }; +} diff --git a/src/displayapp/screens/settings/Settings.cpp b/src/displayapp/screens/settings/Settings.cpp index 9e19d20..af57578 100644 --- a/src/displayapp/screens/settings/Settings.cpp +++ b/src/displayapp/screens/settings/Settings.cpp @@ -48,7 +48,7 @@ std::unique_ptr Settings::CreateScreen2() { {Symbols::check, "Firmware", Apps::FirmwareValidation}, {Symbols::none, "None", Apps::None}, {Symbols::none, "None", Apps::None}, - {Symbols::none, "None", Apps::None} + {Symbols::none, "None", Apps::None}, }}; return std::make_unique(1, 2, app, settingsController, applications); diff --git a/src/main.cpp b/src/main.cpp index 062e64c..4695354 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -103,6 +103,7 @@ Pinetime::Drivers::Watchdog watchdog; Pinetime::Drivers::WatchdogView watchdogView(watchdog); Pinetime::Controllers::NotificationManager notificationManager; Pinetime::Controllers::MotionController motionController; +Pinetime::Controllers::TimerController timerController; Pinetime::Controllers::AlarmController alarmController {dateTimeController}; Pinetime::Controllers::TouchHandler touchHandler(touchPanel, lvgl); Pinetime::Controllers::ButtonHandler buttonHandler; @@ -119,6 +120,7 @@ Pinetime::Applications::DisplayApp displayApp(lcd, settingsController, motorController, motionController, + timerController, alarmController, brightnessController, touchHandler); @@ -132,6 +134,7 @@ Pinetime::System::SystemTask systemTask(spi, batteryController, bleController, dateTimeController, + timerController, alarmController, watchdog, notificationManager, diff --git a/src/systemtask/Messages.h b/src/systemtask/Messages.h index 1210bbc..760cb7d 100644 --- a/src/systemtask/Messages.h +++ b/src/systemtask/Messages.h @@ -8,6 +8,7 @@ namespace Pinetime { TouchWakeUp, OnNewTime, OnNewNotification, + OnTimerDone, OnNewCall, BleConnected, UpdateTimeOut, diff --git a/src/systemtask/SystemTask.cpp b/src/systemtask/SystemTask.cpp index 6f32843..8e6f257 100644 --- a/src/systemtask/SystemTask.cpp +++ b/src/systemtask/SystemTask.cpp @@ -56,6 +56,7 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi, Controllers::Battery& batteryController, Controllers::Ble& bleController, Controllers::DateTime& dateTimeController, + Controllers::TimerController& timerController, Controllers::AlarmController& alarmController, Drivers::Watchdog& watchdog, Pinetime::Controllers::NotificationManager& notificationManager, @@ -75,6 +76,7 @@ SystemTask::SystemTask(Drivers::SpiMaster& spi, batteryController {batteryController}, bleController {bleController}, dateTimeController {dateTimeController}, + timerController {timerController}, alarmController {alarmController}, watchdog {watchdog}, notificationManager {notificationManager}, @@ -114,6 +116,8 @@ void SystemTask::Work() { NRF_LOG_INFO("Last reset reason : %s", Pinetime::Drivers::Watchdog::ResetReasonToString(watchdog.ResetReason())); APP_GPIOTE_INIT(2); + app_timer_init(); + spi.Init(); spiNorFlash.Init(); spiNorFlash.Wakeup(); @@ -135,6 +139,8 @@ void SystemTask::Work() { batteryController.Register(this); motorController.Init(); motionSensor.SoftReset(); + timerController.Register(this); + timerController.Init(); alarmController.Init(this); // Reset the TWI device because the motion sensor chip most probably crashed it... @@ -278,6 +284,13 @@ void SystemTask::Work() { displayApp.PushMessage(Pinetime::Applications::Display::Messages::NewNotification); } break; + case Messages::OnTimerDone: + if (isSleeping && !isWakingUp) { + GoToRunning(); + } + motorController.RunForDuration(35); + displayApp.PushMessage(Pinetime::Applications::Display::Messages::TimerDone); + break; case Messages::SetOffAlarm: if (isSleeping && !isWakingUp) { GoToRunning(); diff --git a/src/systemtask/SystemTask.h b/src/systemtask/SystemTask.h index 8fc48f1..fa28626 100644 --- a/src/systemtask/SystemTask.h +++ b/src/systemtask/SystemTask.h @@ -15,6 +15,7 @@ #include "components/ble/NimbleController.h" #include "components/ble/NotificationManager.h" #include "components/motor/MotorController.h" +#include "components/timer/TimerController.h" #include "components/alarm/AlarmController.h" #include "touchhandler/TouchHandler.h" #include "buttonhandler/ButtonHandler.h" @@ -58,6 +59,7 @@ namespace Pinetime { Controllers::Battery& batteryController, Controllers::Ble& bleController, Controllers::DateTime& dateTimeController, + Controllers::TimerController& timerController, Controllers::AlarmController& alarmController, Drivers::Watchdog& watchdog, Pinetime::Controllers::NotificationManager& notificationManager, @@ -98,6 +100,7 @@ namespace Pinetime { Pinetime::Controllers::Ble& bleController; Pinetime::Controllers::DateTime& dateTimeController; + Pinetime::Controllers::TimerController& timerController; Pinetime::Controllers::AlarmController& alarmController; QueueHandle_t systemTasksMsgQueue; std::atomic isSleeping {false}; -- cgit v0.10.2