1use embassy_time::{block_for, Duration, Instant, Timer};
6use embedded_hal::digital::OutputPin;
7
8pub mod fonts;
9
10mod types;
11pub use types::*;
12
13const REFRESH_INTERVAL: Duration = Duration::from_micros(500);
14
15pub struct LedMatrix<P, const ROWS: usize, const COLS: usize>
19where
20 P: OutputPin + 'static,
21{
22 pin_rows: [P; ROWS],
23 pin_cols: [P; COLS],
24 frame_buffer: Frame<COLS, ROWS>,
25 row_p: usize,
26 brightness: Brightness,
27}
28
29impl<P, const ROWS: usize, const COLS: usize> LedMatrix<P, ROWS, COLS>
30where
31 P: OutputPin,
32{
33 pub fn new(pin_rows: [P; ROWS], pin_cols: [P; COLS]) -> Self {
35 LedMatrix {
36 pin_rows,
37 pin_cols,
38 frame_buffer: Frame::empty(),
39 row_p: 0,
40 brightness: Default::default(),
41 }
42 }
43
44 pub fn clear(&mut self) {
46 self.frame_buffer.clear();
47 for row in self.pin_rows.iter_mut() {
48 row.set_high().ok();
49 }
50
51 for col in self.pin_cols.iter_mut() {
52 col.set_high().ok();
53 }
54 }
55
56 pub fn on(&mut self, x: usize, y: usize) {
58 self.frame_buffer.set(x, y);
59 }
60
61 pub fn off(&mut self, x: usize, y: usize) {
63 self.frame_buffer.unset(x, y);
64 }
65
66 pub fn apply(&mut self, frame: Frame<COLS, ROWS>) {
68 self.frame_buffer = frame;
69 }
70
71 pub fn set_brightness(&mut self, brightness: Brightness) {
73 self.brightness = brightness;
74 }
75
76 pub fn increase_brightness(&mut self) {
78 self.brightness += 1;
79 }
80
81 pub fn decrease_brightness(&mut self) {
83 self.brightness -= 1;
84 }
85
86 pub fn render(&mut self) {
88 for row in self.pin_rows.iter_mut() {
89 row.set_low().ok();
90 }
91
92 for (cid, col) in self.pin_cols.iter_mut().enumerate() {
93 if self.frame_buffer.is_set(cid, self.row_p) {
94 col.set_low().ok();
95 } else {
96 col.set_high().ok();
97 }
98 }
99
100 block_for(Duration::from_micros(
102 ((Brightness::MAX.level() - self.brightness.level()) as u64) * 6000 / Brightness::MAX.level() as u64,
103 ));
104
105 self.pin_rows[self.row_p].set_high().ok();
106
107 self.row_p = (self.row_p + 1) % self.pin_rows.len();
108 }
109
110 pub async fn display(&mut self, frame: Frame<COLS, ROWS>, length: Duration) {
113 self.apply(frame);
114 let end = Instant::now() + length;
115 while Instant::now() < end {
116 self.render();
117 Timer::after(REFRESH_INTERVAL).await;
118 }
119 self.clear();
120 }
121
122 pub async fn scroll(&mut self, text: &str) {
124 self.scroll_with_speed(text, Duration::from_secs((text.len() / 2) as u64))
125 .await;
126 }
127
128 pub async fn scroll_with_speed(&mut self, text: &str, speed: Duration) {
130 self.animate(text.as_bytes(), AnimationEffect::Slide, speed).await;
131 }
132
133 pub async fn animate(&mut self, data: &[u8], effect: AnimationEffect, duration: Duration) {
135 let mut animation: Animation<'_, COLS, ROWS> =
136 Animation::new(AnimationData::Bytes(data), effect, duration).unwrap();
137 loop {
138 match animation.next(Instant::now()) {
139 AnimationState::Apply(f) => {
140 self.apply(f);
141 }
142 AnimationState::Wait => {}
143 AnimationState::Done => {
144 break;
145 }
146 }
147 self.render();
148 Timer::after(REFRESH_INTERVAL).await;
149 }
150 self.clear();
151 }
152
153 pub async fn animate_frames(&mut self, data: &[Frame<COLS, ROWS>], effect: AnimationEffect, duration: Duration) {
155 let mut animation: Animation<'_, COLS, ROWS> =
156 Animation::new(AnimationData::Frames(data), effect, duration).unwrap();
157 loop {
158 match animation.next(Instant::now()) {
159 AnimationState::Apply(f) => {
160 self.apply(f);
161 }
162 AnimationState::Wait => {}
163 AnimationState::Done => {
164 break;
165 }
166 }
167 self.render();
168 Timer::after(REFRESH_INTERVAL).await;
169 }
170 self.clear();
171 }
172
173 pub fn into_inner(self) -> ([P; ROWS], [P;COLS]) {
176 (self.pin_rows, self.pin_cols)
177 }
178}
179
180#[derive(Clone, Copy)]
182pub enum AnimationEffect {
183 None,
185 Slide,
187}
188
189enum AnimationData<'a, const XSIZE: usize, const YSIZE: usize> {
190 Frames(&'a [Frame<XSIZE, YSIZE>]),
191 Bytes(&'a [u8]),
192}
193
194impl<'a, const XSIZE: usize, const YSIZE: usize> AnimationData<'a, XSIZE, YSIZE> {
195 fn len(&self) -> usize {
196 match self {
197 AnimationData::Frames(f) => f.len(),
198 AnimationData::Bytes(f) => f.len(),
199 }
200 }
201
202 fn frame(&self, idx: usize) -> Frame<XSIZE, YSIZE> {
203 match self {
204 AnimationData::Frames(f) => f[idx],
205 AnimationData::Bytes(f) => f[idx].into(),
206 }
207 }
208}
209
210struct Animation<'a, const XSIZE: usize, const YSIZE: usize> {
211 frames: AnimationData<'a, XSIZE, YSIZE>,
212 sequence: usize,
213 frame_index: usize,
214 index: usize,
215 length: usize,
216 effect: AnimationEffect,
217 wait: Duration,
218 next: Instant,
219}
220
221#[derive(PartialEq, Debug)]
222enum AnimationState<const XSIZE: usize, const YSIZE: usize> {
223 Wait,
224 Apply(Frame<XSIZE, YSIZE>),
225 Done,
226}
227
228impl<'a, const XSIZE: usize, const YSIZE: usize> Animation<'a, XSIZE, YSIZE> {
229 pub fn new(
230 frames: AnimationData<'a, XSIZE, YSIZE>,
231 effect: AnimationEffect,
232 duration: Duration,
233 ) -> Result<Self, AnimationError> {
234 assert!(frames.len() > 0);
235 let length = match effect {
236 AnimationEffect::Slide => frames.len() * XSIZE,
237 AnimationEffect::None => frames.len(),
238 };
239
240 if let Some(wait) = duration.checked_div(length as u32) {
241 Ok(Self {
242 frames,
243 frame_index: 0,
244 sequence: 0,
245 index: 0,
246 length,
247 effect,
248 wait,
249 next: Instant::now(),
250 })
251 } else {
252 Err(AnimationError::TooFast)
253 }
254 }
255 fn current(&self) -> Frame<XSIZE, YSIZE> {
256 let mut current = self.frames.frame(self.frame_index);
257
258 let mut next = if self.frame_index < self.frames.len() - 1 {
259 self.frames.frame(self.frame_index + 1)
260 } else {
261 Frame::empty()
262 };
263
264 current.shift_left(self.sequence);
265 next.shift_right(XSIZE - self.sequence);
266
267 current.or(&next);
268 current
269 }
270
271 fn next(&mut self, now: Instant) -> AnimationState<XSIZE, YSIZE> {
272 if self.next <= now {
273 if self.index < self.length {
274 let current = self.current();
275 if self.sequence >= XSIZE - 1 {
276 self.sequence = match self.effect {
277 AnimationEffect::None => XSIZE,
278 AnimationEffect::Slide => 0,
279 };
280 self.frame_index += 1;
281 } else {
282 self.sequence += 1;
283 }
284
285 self.index += 1;
286 self.next += self.wait;
287 AnimationState::Apply(current)
288 } else {
289 AnimationState::Done
290 }
291 } else {
292 AnimationState::Wait
293 }
294 }
295}
296
297#[derive(Debug, Clone, Copy)]
298#[cfg_attr(feature = "defmt", derive(defmt::Format))]
299pub enum AnimationError {
301 TooFast,
303}
304
305