Skip to content

Commit c8f896a

Browse files
committed
wip meeting sign ui
1 parent 20b6cc9 commit c8f896a

File tree

2 files changed

+207
-13
lines changed

2 files changed

+207
-13
lines changed

src/bin/control_panel.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use embassy_executor::Spawner;
1616
use embassy_futures::select::select;
1717
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
1818
use embassy_sync::mutex::Mutex;
19-
use embassy_time::{Duration, Ticker, Timer};
19+
use embassy_time::{Duration, Instant, Ticker, Timer};
2020
use embedded_graphics::primitives::{Line, Polyline};
2121
use embedded_graphics::{
2222
pixelcolor::BinaryColor,
@@ -114,6 +114,7 @@ async fn main(spawner: Spawner) {
114114
ui_selection_mode: UISelectionMode::Menu,
115115
ui_section: UISection::MeetingSign,
116116
display,
117+
meeting_sign_completion: None,
117118
}));
118119
control_panel_state.lock().await.draw_entire_ui().unwrap();
119120

@@ -169,6 +170,10 @@ async fn main(spawner: Spawner) {
169170
control_panel_state,
170171
))
171172
.ok();
173+
174+
spawner
175+
.spawn(monitor_meeting_sign_timer(control_panel_state))
176+
.ok();
172177
}
173178

174179
#[embassy_executor::task]
@@ -271,6 +276,34 @@ async fn monitor_usb_switch_leds(
271276
}
272277
}
273278

279+
#[embassy_executor::task]
280+
async fn monitor_meeting_sign_timer(control_panel_state: &'static StateMutex) {
281+
debug!("Starting monitor_meeting_sign_timer task");
282+
let mut ticker = Ticker::every(Duration::from_secs(1));
283+
loop {
284+
// let timer_completion = { control_panel_state.lock().await.meeting_sign_completion };
285+
// match timer_completion {
286+
// Some(instant) => {
287+
// let now = Instant::now();
288+
// if now < instant {
289+
// info!("Meeting Sign timer is running");
290+
// } else {
291+
// info!("Meeting Sign timer has completed");
292+
// control_panel_state
293+
// .lock()
294+
// .await
295+
// .update_meeting_sign_completion(None);
296+
// }
297+
// }
298+
// None => {
299+
// info!("Meeting Sign timer is not running");
300+
// }
301+
// }
302+
303+
ticker.next().await;
304+
}
305+
}
306+
274307
#[embassy_executor::task]
275308
async fn writer(mut uart: Uart<'static, Async>) {
276309
debug!("Starting UART writer task");

src/control_panel/state.rs

Lines changed: 173 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
use embassy_time::{Duration, Instant};
12
use embedded_graphics::{
23
mono_font::{ascii, MonoFont, MonoTextStyle},
34
pixelcolor::BinaryColor,
45
prelude::{DrawTarget, Point, Primitive, Size},
5-
primitives::{Line, Polyline, PrimitiveStyle, Rectangle, RoundedRectangle, StyledDrawable},
6+
primitives::{
7+
Line, Polyline, PrimitiveStyle, PrimitiveStyleBuilder, Rectangle, RoundedRectangle,
8+
StrokeAlignment, StyledDrawable,
9+
},
610
text::{Alignment, Baseline, Text, TextStyle, TextStyleBuilder},
711
Drawable,
812
};
@@ -11,10 +15,13 @@ use esp_hal::{
1115
i2c::master::I2c,
1216
Blocking,
1317
};
18+
use log::{error, info};
1419
use ssd1306::{
1520
mode::BufferedGraphicsMode, prelude::I2CInterface, size::DisplaySize128x64, Ssd1306,
1621
};
1722

23+
use crate::meeting_instruction::ProgressRatio;
24+
1825
type DisplayType = Ssd1306<
1926
I2CInterface<I2c<'static, Blocking>>,
2027
DisplaySize128x64,
@@ -25,6 +32,7 @@ pub struct ControlPanelState {
2532
pub usb_power_1: Output<'static>,
2633
pub usb_power_2: Output<'static>,
2734
pub meeting_sign_power: Output<'static>,
35+
pub meeting_sign_completion: Option<Instant>,
2836
pub ui_selection_mode: UISelectionMode,
2937
pub ui_section: UISection,
3038
pub display: DisplayType,
@@ -35,6 +43,9 @@ pub enum MovementDirection {
3543
CounterClockwise,
3644
}
3745
impl ControlPanelState {
46+
const MEETING_SIGN_INTERVAL: Duration = Duration::from_secs(60 * 5);
47+
const MEETING_SIGN_MAX_DURATION: Duration = Duration::from_secs(60 * 120);
48+
3849
pub fn rotary_encoder_rotate(&mut self, direction: MovementDirection) {
3950
match self.ui_selection_mode {
4051
UISelectionMode::Menu => {
@@ -58,8 +69,91 @@ impl ControlPanelState {
5869
.unwrap();
5970
}
6071
UISection::MeetingSign => match direction {
61-
MovementDirection::Clockwise => todo!(),
62-
MovementDirection::CounterClockwise => todo!(),
72+
MovementDirection::Clockwise => {
73+
match self.meeting_sign_power.output_level() {
74+
Level::High => {
75+
info!("Meeting Sign is already running");
76+
match self.meeting_sign_completion {
77+
None => {
78+
self.meeting_sign_power.set_high();
79+
self.meeting_sign_completion =
80+
Some(Instant::now() + Self::MEETING_SIGN_INTERVAL);
81+
error!(
82+
"Meeting sign completion is None, but power is high."
83+
);
84+
}
85+
Some(instant) => {
86+
if instant < Instant::now() {
87+
self.meeting_sign_power.set_high();
88+
self.meeting_sign_completion =
89+
Some(Instant::now() + Self::MEETING_SIGN_INTERVAL);
90+
error!("Meeting sign completion was before NOW.");
91+
} else if instant + Self::MEETING_SIGN_INTERVAL
92+
< Instant::now() + Self::MEETING_SIGN_MAX_DURATION
93+
{
94+
self.meeting_sign_power.set_high();
95+
self.meeting_sign_completion =
96+
Some(instant + Self::MEETING_SIGN_INTERVAL);
97+
info!(
98+
"Meeting sign timer increased by {}s.",
99+
Self::MEETING_SIGN_INTERVAL.as_secs()
100+
);
101+
} else {
102+
info!("Meeting sign timer would exceed max duration, not increasing.");
103+
}
104+
}
105+
}
106+
}
107+
Level::Low => {
108+
self.meeting_sign_power.set_high();
109+
self.meeting_sign_completion =
110+
Some(Instant::now() + Self::MEETING_SIGN_INTERVAL);
111+
info!("Meeting Sign turned on");
112+
info!("Meeting Sign timer started");
113+
}
114+
};
115+
self.check_meeting_sign_timer().unwrap();
116+
}
117+
MovementDirection::CounterClockwise => {
118+
match self.meeting_sign_power.output_level() {
119+
Level::High => {
120+
info!("Meeting Sign is already running");
121+
match self.meeting_sign_completion {
122+
None => {
123+
self.meeting_sign_power.set_low();
124+
error!(
125+
"Meeting sign completion is None, but power is high."
126+
);
127+
}
128+
Some(instant) => {
129+
if instant < Instant::now() {
130+
self.meeting_sign_power.set_low();
131+
error!("Meeting sign completion was before NOW, but power is high.");
132+
} else if instant - Self::MEETING_SIGN_INTERVAL
133+
< Instant::now()
134+
{
135+
self.meeting_sign_power.set_low();
136+
self.meeting_sign_completion = None;
137+
info!(
138+
"Meeting sign turned off and completion set to None."
139+
);
140+
} else {
141+
self.meeting_sign_completion =
142+
Some(instant - Self::MEETING_SIGN_INTERVAL);
143+
info!(
144+
"Decreasing Meeting Sign timer by {}s.",
145+
Self::MEETING_SIGN_INTERVAL.as_secs()
146+
);
147+
}
148+
}
149+
}
150+
self.check_meeting_sign_timer().unwrap();
151+
}
152+
Level::Low => {
153+
info!("Meeting sign is already off");
154+
}
155+
};
156+
}
63157
},
64158
},
65159
};
@@ -138,6 +232,42 @@ impl ControlPanelState {
138232
Ok(())
139233
}
140234

235+
pub fn check_meeting_sign_timer(&mut self) -> Result<(), <DisplayType as DrawTarget>::Error> {
236+
match (
237+
self.meeting_sign_power.output_level(),
238+
self.meeting_sign_completion,
239+
) {
240+
(Level::Low, None) => {}
241+
(Level::Low, Some(_)) => {
242+
error!("Meeting Sign is not on, but completion is set to something");
243+
}
244+
(Level::High, None) => {
245+
error!("Meeting Sign is on, but completion is None");
246+
// This should not happen, but if it does, we can reset the state
247+
self.meeting_sign_power.set_low();
248+
self.meeting_sign_completion = None;
249+
}
250+
(Level::High, Some(instant)) => {
251+
if instant < Instant::now() {
252+
self.meeting_sign_power.set_low();
253+
self.meeting_sign_completion = None;
254+
info!("Meeting Sign timer has completed");
255+
MeetingSignUI.draw_progress(&mut self.display, ProgressRatio(0))?;
256+
} else {
257+
let ratio = ProgressRatio::from_durations(
258+
&(instant - Instant::now()),
259+
&Self::MEETING_SIGN_MAX_DURATION,
260+
)
261+
.unwrap();
262+
info!("Meeting Sign timer is running with ratio={:?}", ratio);
263+
MeetingSignUI.draw_progress(&mut self.display, ratio)?;
264+
}
265+
}
266+
};
267+
268+
Ok(())
269+
}
270+
141271
pub fn draw_entire_ui(&mut self) -> Result<(), <DisplayType as DrawTarget>::Error> {
142272
self.draw_border_ui()?;
143273
self.usb_switch_state.draw_entire_ui(&mut self.display)?;
@@ -481,18 +611,28 @@ impl MeetingSignUI {
481611
Self::CENTER_ALIGNED,
482612
);
483613

484-
pub const BORDER_ON_STYLE: PrimitiveStyle<BinaryColor> =
485-
PrimitiveStyle::with_stroke(BinaryColor::On, 1);
486-
pub const BORDER_OFF_STYLE: PrimitiveStyle<BinaryColor> =
487-
PrimitiveStyle::with_stroke(BinaryColor::Off, 1);
614+
pub const BORDER_ON_STYLE: PrimitiveStyle<BinaryColor> = PrimitiveStyleBuilder::new()
615+
.stroke_color(BinaryColor::On)
616+
.stroke_width(1)
617+
.stroke_alignment(StrokeAlignment::Outside)
618+
.build();
619+
pub const BORDER_OFF_STYLE: PrimitiveStyle<BinaryColor> = PrimitiveStyleBuilder::new()
620+
.stroke_color(BinaryColor::Off)
621+
.stroke_width(1)
622+
.stroke_alignment(StrokeAlignment::Outside)
623+
.build();
624+
488625
const PROGRESS_SIZE: Size = Size::new(UISection::MEETING_SIGN_SIZE.width - 20, 12);
626+
const PROGRESS_X: i32 = Self::MIDDLE_X - (Self::PROGRESS_SIZE.width as i32 / 2);
627+
const PROGRESS_Y: i32 = 25;
628+
const PROGRESS_PT: Point = Point::new(Self::PROGRESS_X, Self::PROGRESS_Y);
629+
const PROGRESS_CORNER_RADIUS: Size = Size::new(3, 3);
489630
pub const PROGRESS_OUTLINE: RoundedRectangle = RoundedRectangle::with_equal_corners(
490-
Rectangle::new(
491-
Point::new(Self::MIDDLE_X - (Self::PROGRESS_SIZE.width as i32 / 2), 25),
492-
Self::PROGRESS_SIZE,
493-
),
494-
Size::new(3, 3),
631+
Rectangle::new(Self::PROGRESS_PT, Self::PROGRESS_SIZE),
632+
Self::PROGRESS_CORNER_RADIUS,
495633
);
634+
pub const BAR_ON_STYLE: PrimitiveStyle<BinaryColor> =
635+
PrimitiveStyle::with_fill(BinaryColor::On);
496636

497637
pub fn draw<D: DrawTarget<Color = BinaryColor>>(
498638
&self,
@@ -506,4 +646,25 @@ impl MeetingSignUI {
506646
Self::PROGRESS_OUTLINE.draw_styled(&style, target)?;
507647
Ok(())
508648
}
649+
650+
pub fn draw_progress<D: DrawTarget<Color = BinaryColor>>(
651+
&self,
652+
target: &mut D,
653+
ratio: ProgressRatio,
654+
) -> Result<(), D::Error> {
655+
let width = ratio.apply_to(Self::PROGRESS_SIZE.width as usize) as u32;
656+
657+
Self::PROGRESS_OUTLINE.draw_styled(&PrimitiveStyle::with_fill(BinaryColor::Off), target)?;
658+
659+
RoundedRectangle::with_equal_corners(
660+
Rectangle::new(
661+
Self::PROGRESS_PT,
662+
Size::new(width, Self::PROGRESS_SIZE.height),
663+
),
664+
Self::PROGRESS_CORNER_RADIUS,
665+
)
666+
.draw_styled(&Self::BAR_ON_STYLE, target)?;
667+
668+
Ok(())
669+
}
509670
}

0 commit comments

Comments
 (0)