-
-
Notifications
You must be signed in to change notification settings - Fork 960
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
timer: Add ringing and counter #1971
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -365,14 +365,17 @@ void DisplayApp::Refresh() { | |
if (state != States::Running) { | ||
PushMessageToSystemTask(System::Messages::GoToRunning); | ||
} | ||
// Load timer app if not loaded | ||
if (currentApp != Apps::Timer) { | ||
LoadNewScreen(Apps::Timer, DisplayApp::FullRefreshDirections::Up); | ||
} | ||
// Once loaded, set the timer to ringing mode | ||
if (currentApp == Apps::Timer) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this check redundant as timer is now loaded before? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this is redundant, but the builder fails when initializing the |
||
lv_disp_trig_activity(nullptr); | ||
auto* timer = static_cast<Screens::Timer*>(currentScreen.get()); | ||
timer->Reset(); | ||
} else { | ||
LoadNewScreen(Apps::Timer, DisplayApp::FullRefreshDirections::Up); | ||
timer->SetTimerRinging(); | ||
} | ||
motorController.RunForDuration(35); | ||
motorController.StartRinging(); | ||
break; | ||
case Messages::AlarmTriggered: | ||
if (currentApp == Apps::Alarm) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,8 @@ static void btnEventHandler(lv_obj_t* obj, lv_event_t event) { | |
} | ||
} | ||
|
||
Timer::Timer(Controllers::Timer& timerController) : timer {timerController} { | ||
Timer::Timer(Controllers::Timer& timerController, Controllers::MotorController& motorController) | ||
: timer {timerController}, motorController {motorController} { | ||
|
||
lv_obj_t* colonLabel = lv_label_create(lv_scr_act(), nullptr); | ||
lv_obj_set_style_local_text_font(colonLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76); | ||
|
@@ -62,7 +63,9 @@ Timer::Timer(Controllers::Timer& timerController) : timer {timerController} { | |
txtPlayPause = lv_label_create(lv_scr_act(), nullptr); | ||
lv_obj_align(txtPlayPause, btnPlayPause, LV_ALIGN_CENTER, 0, 0); | ||
|
||
if (timer.IsRunning()) { | ||
if (motorController.IsRinging()) { | ||
SetTimerRinging(); | ||
} else if (timer.IsRunning()) { | ||
SetTimerRunning(); | ||
} else { | ||
SetTimerStopped(); | ||
|
@@ -103,7 +106,17 @@ void Timer::UpdateMask() { | |
} | ||
|
||
void Timer::Refresh() { | ||
if (timer.IsRunning()) { | ||
if (isRinging) { | ||
DisplayTime(); | ||
// Stop buzzing after 10 seconds, but continue the counter | ||
if (motorController.IsRinging() && displaySeconds.Get().count() > 10) { | ||
motorController.StopRinging(); | ||
} | ||
// Reset timer after 1 minute | ||
if (displaySeconds.Get().count() > 60) { | ||
Reset(); | ||
} | ||
} else if (timer.IsRunning()) { | ||
DisplayTime(); | ||
} else if (buttonPressing && xTaskGetTickCount() > pressTime + pdMS_TO_TICKS(150)) { | ||
lv_label_set_text_static(txtPlayPause, "Reset"); | ||
|
@@ -129,16 +142,31 @@ void Timer::SetTimerRunning() { | |
minuteCounter.HideControls(); | ||
secondCounter.HideControls(); | ||
lv_label_set_text_static(txtPlayPause, "Pause"); | ||
lv_obj_set_style_local_bg_color(btnPlayPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt); | ||
} | ||
|
||
void Timer::SetTimerStopped() { | ||
isRinging = false; | ||
minuteCounter.ShowControls(); | ||
secondCounter.ShowControls(); | ||
lv_label_set_text_static(txtPlayPause, "Start"); | ||
lv_obj_set_style_local_bg_color(btnPlayPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN); | ||
} | ||
|
||
void Timer::SetTimerRinging() { | ||
isRinging = true; | ||
minuteCounter.HideControls(); | ||
secondCounter.HideControls(); | ||
lv_label_set_text_static(txtPlayPause, "Reset"); | ||
lv_obj_set_style_local_bg_color(btnPlayPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); | ||
timer.SetExpiredTime(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately I think this is still UB as the timer is no longer running at this point There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Without calling this, there's no count-up in the timer: it just stays at |
||
} | ||
|
||
void Timer::ToggleRunning() { | ||
if (timer.IsRunning()) { | ||
if (isRinging) { | ||
motorController.StopRinging(); | ||
Reset(); | ||
} else if (timer.IsRunning()) { | ||
DisplayTime(); | ||
timer.StopTimer(); | ||
SetTimerStopped(); | ||
|
@@ -151,6 +179,7 @@ void Timer::ToggleRunning() { | |
} | ||
|
||
void Timer::Reset() { | ||
timer.ResetExpiredTime(); | ||
DisplayTime(); | ||
SetTimerStopped(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On further reflection, we shouldn't change the semantics of this method
The Timer component is meant to be a generic component that multiple applications can be use (it should be just a thin wrapper around FreeRTOS timers)
One idea:
The only part of this I don't really like is the semantics of GetTimeSinceExpired when it hasn't expired yet or has been stopped, and also GetTimeRemaining when stopped or expired
Another idea:
Method GetTimerStatus which returns a variant of
This way semantics are always clear
Curious to know what you're thinking, any of these make sense? Not sure I've got any perfect solutions here, though I quite like the second one so far (haven't thought about either for too long)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it, though? That's what the
xTimer
interface is meant to do. This wrapper into aTimer
component is only used for the Timer app. That said, I think thisTimer
class could still work elsewhere in the code as is, if there was a use case for it. If a new use case shows up, and a change is needed, we can do it then - not sure creating a super generic class is useful without knowing what use cases it's meant to address.That's just my opinion though, and I may be entirely wrong (and I'm just having a hard time finding time to work on this these days - which is why I'm hesitant to get started on a change I probably won't finish).