1#[cfg(feature = "async")]
2use crate::mode::DisplayConfigAsync;
3use crate::{command::AddrMode, mode::DisplayConfig, rotation::DisplayRotation, size::*, Ssd1306};
4#[cfg(feature = "async")]
5use crate::{size::DisplaySizeAsync, Ssd1306Async};
6use core::{cmp::min, fmt};
7#[cfg(feature = "async")]
8use display_interface::AsyncWriteOnlyDataCommand;
9use display_interface::{DisplayError, WriteOnlyDataCommand};
10
11#[maybe_async_cfg::maybe(
12 sync(keep_self),
13 async(
14 feature = "async",
15 idents(
16 TerminalDisplaySize(async = "TerminalDisplaySizeAsync"),
17 DisplaySize(async = "DisplaySizeAsync"),
18 )
19 )
20)]
21pub trait TerminalDisplaySize: DisplaySize {
24 const CHAR_NUM: u8;
26}
27
28#[maybe_async_cfg::maybe(
29 sync(keep_self),
30 async(
31 feature = "async",
32 keep_self,
33 idents(TerminalDisplaySize(async = "TerminalDisplaySizeAsync"),)
34 )
35)]
36impl TerminalDisplaySize for DisplaySize128x64 {
37 const CHAR_NUM: u8 = 128;
38}
39
40#[maybe_async_cfg::maybe(
41 sync(keep_self),
42 async(
43 feature = "async",
44 keep_self,
45 idents(TerminalDisplaySize(async = "TerminalDisplaySizeAsync"),)
46 )
47)]
48impl TerminalDisplaySize for DisplaySize128x32 {
49 const CHAR_NUM: u8 = 64;
50}
51
52#[maybe_async_cfg::maybe(
53 sync(keep_self),
54 async(
55 feature = "async",
56 keep_self,
57 idents(TerminalDisplaySize(async = "TerminalDisplaySizeAsync"),)
58 )
59)]
60impl TerminalDisplaySize for DisplaySize96x16 {
61 const CHAR_NUM: u8 = 24;
62}
63
64#[maybe_async_cfg::maybe(
65 sync(keep_self),
66 async(
67 keep_self,
68 feature = "async",
69 idents(TerminalDisplaySize(async = "TerminalDisplaySizeAsync"),)
70 )
71)]
72impl TerminalDisplaySize for DisplaySize72x40 {
73 const CHAR_NUM: u8 = 45;
74}
75
76#[maybe_async_cfg::maybe(
77 sync(keep_self),
78 async(
79 feature = "async",
80 keep_self,
81 idents(TerminalDisplaySize(async = "TerminalDisplaySizeAsync"),)
82 )
83)]
84impl TerminalDisplaySize for DisplaySize64x48 {
85 const CHAR_NUM: u8 = 48;
86}
87
88struct CursorWrapEvent(u8);
90
91#[derive(Copy, Clone, Debug)]
92struct Cursor {
93 col: u8,
94 row: u8,
95 width: u8,
96 height: u8,
97}
98
99impl Cursor {
100 pub fn new(width_pixels: u8, height_pixels: u8) -> Self {
101 let width = width_pixels / 8;
102 let height = height_pixels / 8;
103 Cursor {
104 col: 0,
105 row: 0,
106 width,
107 height,
108 }
109 }
110
111 pub fn advance(&mut self) -> Option<CursorWrapEvent> {
115 self.col = (self.col + 1) % self.width;
116 if self.col == 0 {
117 self.row = (self.row + 1) % self.height;
118 Some(CursorWrapEvent(self.row))
119 } else {
120 None
121 }
122 }
123
124 pub fn advance_line(&mut self) -> CursorWrapEvent {
127 self.row = (self.row + 1) % self.height;
128 self.col = 0;
129 CursorWrapEvent(self.row)
130 }
131
132 pub fn set_position(&mut self, col: u8, row: u8) {
135 self.col = min(col, self.width - 1);
136 self.row = min(row, self.height - 1);
137 }
138
139 pub fn get_position(&self) -> (u8, u8) {
141 (self.col, self.row)
142 }
143
144 pub fn get_dimensions(&self) -> (u8, u8) {
146 (self.width, self.height)
147 }
148}
149
150#[derive(Clone)]
152pub enum TerminalModeError {
153 InterfaceError(DisplayError),
155 Uninitialized,
157 OutOfBounds,
159}
160
161impl fmt::Debug for TerminalModeError {
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
163 match self {
164 Self::InterfaceError(_) => "InterfaceError".fmt(f),
165 Self::Uninitialized => "Uninitialized".fmt(f),
166 Self::OutOfBounds => "OutOfBound".fmt(f),
167 }
168 }
169}
170
171impl From<DisplayError> for TerminalModeError {
172 fn from(value: DisplayError) -> Self {
173 TerminalModeError::InterfaceError(value)
174 }
175}
176
177#[maybe_async_cfg::maybe(
179 sync(keep_self),
180 async(feature = "async", idents(TerminalMode(async = "TerminalModeAsync")))
181)]
182#[derive(Debug, Copy, Clone, Default)]
183pub struct TerminalMode {
184 cursor: Option<Cursor>,
185}
186
187#[maybe_async_cfg::maybe(
188 sync(keep_self),
189 async(feature = "async", idents(TerminalMode(async = "TerminalModeAsync")))
190)]
191impl TerminalMode {
192 pub fn new() -> Self {
194 Self::default()
195 }
196}
197
198#[maybe_async_cfg::maybe(
199 sync(keep_self),
200 async(
201 feature = "async",
202 idents(
203 DisplaySize(async = "DisplaySizeAsync"),
204 DisplayConfig(async = "DisplayConfigAsync"),
205 WriteOnlyDataCommand(async = "AsyncWriteOnlyDataCommand"),
206 TerminalMode(async = "TerminalModeAsync"),
207 TerminalDisplaySize(async = "TerminalDisplaySizeAsync"),
208 )
209 )
210)]
211impl<DI, SIZE> DisplayConfig for Ssd1306<DI, SIZE, TerminalMode>
212where
213 DI: WriteOnlyDataCommand,
214 SIZE: TerminalDisplaySize,
215{
216 type Error = TerminalModeError;
217
218 async fn set_rotation(&mut self, rot: DisplayRotation) -> Result<(), TerminalModeError> {
222 self.set_rotation(rot).await?;
223 self.reset_pos().await
225 }
226
227 async fn init(&mut self) -> Result<(), TerminalModeError> {
231 self.init_with_addr_mode(AddrMode::Page).await?;
232 self.reset_pos().await
233 }
234}
235
236#[maybe_async_cfg::maybe(
237 sync(keep_self),
238 async(
239 feature = "async",
240 idents(
241 DisplaySize(async = "DisplaySizeAsync"),
242 DisplayConfig(async = "DisplayConfigAsync"),
243 WriteOnlyDataCommand(async = "AsyncWriteOnlyDataCommand"),
244 TerminalMode(async = "TerminalModeAsync"),
245 TerminalDisplaySize(async = "TerminalDisplaySizeAsync"),
246 )
247 )
248)]
249impl<DI, SIZE> Ssd1306<DI, SIZE, TerminalMode>
250where
251 DI: WriteOnlyDataCommand,
252 SIZE: TerminalDisplaySize,
253{
254 pub async fn clear(&mut self) -> Result<(), TerminalModeError> {
256 self.set_addr_mode(AddrMode::Horizontal).await?;
258
259 let offset_x = match self.rotation() {
260 DisplayRotation::Rotate0 | DisplayRotation::Rotate270 => SIZE::OFFSETX,
261 DisplayRotation::Rotate180 | DisplayRotation::Rotate90 => {
262 SIZE::DRIVER_COLS - SIZE::WIDTH - SIZE::OFFSETX
265 }
266 };
267 self.set_draw_area(
268 (offset_x, SIZE::OFFSETY),
269 (SIZE::WIDTH + offset_x, SIZE::HEIGHT + SIZE::OFFSETY),
270 )
271 .await?;
272
273 for _ in 0..SIZE::CHAR_NUM {
275 self.draw(&[0; 8]).await?;
276 }
277
278 self.set_addr_mode(AddrMode::Page).await?;
280 self.reset_pos().await
281 }
282
283 pub async fn print_char(&mut self, c: char) -> Result<(), TerminalModeError> {
285 match c {
286 '\n' => {
287 let CursorWrapEvent(new_line) = self.ensure_cursor()?.advance_line();
288 self.set_position(0, new_line).await?;
289 }
290 '\r' => {
291 self.set_column(0).await?;
292 let (_, cur_line) = self.ensure_cursor()?.get_position();
293 self.ensure_cursor()?.set_position(0, cur_line);
294 }
295 _ => {
296 let bitmap = match self.rotation {
297 DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
298 Self::char_to_bitmap(c)
299 }
300 DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
301 let bitmap = Self::char_to_bitmap(c);
302 Self::rotate_bitmap(bitmap)
303 }
304 };
305
306 self.draw(&bitmap).await?;
307
308 self.advance_cursor().await?;
310 }
311 }
312
313 Ok(())
314 }
315
316 pub fn position(&self) -> Result<(u8, u8), TerminalModeError> {
319 self.mode
320 .cursor
321 .as_ref()
322 .map(Cursor::get_position)
323 .ok_or(TerminalModeError::Uninitialized)
324 }
325
326 pub async fn set_position(&mut self, column: u8, row: u8) -> Result<(), TerminalModeError> {
330 let (width, height) = self.ensure_cursor()?.get_dimensions();
331 if column >= width || row >= height {
332 Err(TerminalModeError::OutOfBounds)
333 } else {
334 let offset_x = match self.rotation() {
335 DisplayRotation::Rotate0 | DisplayRotation::Rotate270 => SIZE::OFFSETX,
336 DisplayRotation::Rotate180 | DisplayRotation::Rotate90 => {
337 SIZE::DRIVER_COLS - SIZE::WIDTH - SIZE::OFFSETX
340 }
341 };
342 match self.rotation() {
343 DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
344 self.set_column(offset_x + column * 8).await?;
345 self.set_row(SIZE::OFFSETY + row * 8).await?;
346 }
347 DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
348 self.set_column(offset_x + row * 8).await?;
349 self.set_row(SIZE::OFFSETY + column * 8).await?;
350 }
351 }
352 self.ensure_cursor()?.set_position(column, row);
353 Ok(())
354 }
355 }
356
357 async fn reset_pos(&mut self) -> Result<(), TerminalModeError> {
359 let (w, h) = match self.rotation() {
361 DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => (SIZE::WIDTH, SIZE::HEIGHT),
362 DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => (SIZE::HEIGHT, SIZE::WIDTH),
363 };
364 self.mode.cursor = Some(Cursor::new(w, h));
365
366 self.set_position(0, 0).await
368 }
369
370 async fn advance_cursor(&mut self) -> Result<(), TerminalModeError> {
373 let cursor = self.ensure_cursor()?;
374
375 cursor.advance();
376 let (c, r) = cursor.get_position();
377 self.set_position(c, r).await
378 }
379
380 fn ensure_cursor(&mut self) -> Result<&mut Cursor, TerminalModeError> {
381 self.mode
382 .cursor
383 .as_mut()
384 .ok_or(TerminalModeError::Uninitialized)
385 }
386
387 fn char_to_bitmap(input: char) -> [u8; 8] {
388 const CHARS: [[u8; 6]; 95] = [
389 [0x00, 0x2f, 0x00, 0x00, 0x00, 0x00],
391 [0x03, 0x00, 0x03, 0x00, 0x00, 0x00],
393 [0x12, 0x3f, 0x12, 0x12, 0x3f, 0x12],
395 [0x2e, 0x2a, 0x7f, 0x2a, 0x3a, 0x00],
397 [0x23, 0x13, 0x08, 0x04, 0x32, 0x31],
399 [0x10, 0x2a, 0x25, 0x2a, 0x10, 0x20],
401 [0x02, 0x01, 0x00, 0x00, 0x00, 0x00],
403 [0x1e, 0x21, 0x00, 0x00, 0x00, 0x00],
405 [0x21, 0x1e, 0x00, 0x00, 0x00, 0x00],
407 [0x08, 0x2a, 0x1c, 0x2a, 0x08, 0x00],
409 [0x08, 0x08, 0x3e, 0x08, 0x08, 0x00],
411 [0x80, 0x60, 0x00, 0x00, 0x00, 0x00],
413 [0x08, 0x08, 0x08, 0x08, 0x08, 0x00],
415 [0x30, 0x30, 0x00, 0x00, 0x00, 0x00],
417 [0x20, 0x10, 0x08, 0x04, 0x02, 0x00],
419 [0x1e, 0x31, 0x29, 0x25, 0x23, 0x1e],
421 [0x22, 0x21, 0x3f, 0x20, 0x20, 0x20],
423 [0x32, 0x29, 0x29, 0x29, 0x29, 0x26],
425 [0x12, 0x21, 0x21, 0x25, 0x25, 0x1a],
427 [0x18, 0x14, 0x12, 0x3f, 0x10, 0x00],
429 [0x17, 0x25, 0x25, 0x25, 0x25, 0x19],
431 [0x1e, 0x25, 0x25, 0x25, 0x25, 0x18],
433 [0x01, 0x01, 0x31, 0x09, 0x05, 0x03],
435 [0x1a, 0x25, 0x25, 0x25, 0x25, 0x1a],
437 [0x06, 0x29, 0x29, 0x29, 0x29, 0x1e],
439 [0x24, 0x00, 0x00, 0x00, 0x00, 0x00],
441 [0x80, 0x64, 0x00, 0x00, 0x00, 0x00],
443 [0x08, 0x14, 0x22, 0x00, 0x00, 0x00],
445 [0x14, 0x14, 0x14, 0x14, 0x14, 0x00],
447 [0x22, 0x14, 0x08, 0x00, 0x00, 0x00],
449 [0x02, 0x01, 0x01, 0x29, 0x05, 0x02],
451 [0x1e, 0x21, 0x2d, 0x2b, 0x2d, 0x0e],
453 [0x3e, 0x09, 0x09, 0x09, 0x09, 0x3e],
455 [0x3f, 0x25, 0x25, 0x25, 0x25, 0x1a],
457 [0x1e, 0x21, 0x21, 0x21, 0x21, 0x12],
459 [0x3f, 0x21, 0x21, 0x21, 0x12, 0x0c],
461 [0x3f, 0x25, 0x25, 0x25, 0x25, 0x21],
463 [0x3f, 0x05, 0x05, 0x05, 0x05, 0x01],
465 [0x1e, 0x21, 0x21, 0x21, 0x29, 0x1a],
467 [0x3f, 0x04, 0x04, 0x04, 0x04, 0x3f],
469 [0x21, 0x21, 0x3f, 0x21, 0x21, 0x00],
471 [0x10, 0x20, 0x20, 0x20, 0x20, 0x1f],
473 [0x3f, 0x04, 0x0c, 0x0a, 0x11, 0x20],
475 [0x3f, 0x20, 0x20, 0x20, 0x20, 0x20],
477 [0x3f, 0x02, 0x04, 0x04, 0x02, 0x3f],
479 [0x3f, 0x02, 0x04, 0x08, 0x10, 0x3f],
481 [0x1e, 0x21, 0x21, 0x21, 0x21, 0x1e],
483 [0x3f, 0x09, 0x09, 0x09, 0x09, 0x06],
485 [0x1e, 0x21, 0x29, 0x31, 0x21, 0x5e],
487 [0x3f, 0x09, 0x09, 0x09, 0x19, 0x26],
489 [0x12, 0x25, 0x25, 0x25, 0x25, 0x18],
491 [0x01, 0x01, 0x01, 0x3f, 0x01, 0x01],
493 [0x1f, 0x20, 0x20, 0x20, 0x20, 0x1f],
495 [0x0f, 0x10, 0x20, 0x20, 0x10, 0x0f],
497 [0x1f, 0x20, 0x10, 0x10, 0x20, 0x1f],
499 [0x21, 0x12, 0x0c, 0x0c, 0x12, 0x21],
501 [0x01, 0x02, 0x3c, 0x02, 0x01, 0x00],
503 [0x21, 0x31, 0x29, 0x25, 0x23, 0x21],
505 [0x3f, 0x21, 0x00, 0x00, 0x00, 0x00],
507 [0x02, 0x04, 0x08, 0x10, 0x20, 0x00],
509 [0x21, 0x3f, 0x00, 0x00, 0x00, 0x00],
511 [0x04, 0x02, 0x3f, 0x02, 0x04, 0x00],
513 [0x40, 0x40, 0x40, 0x40, 0x40, 0x40],
515 [0x01, 0x02, 0x00, 0x00, 0x00, 0x00],
517 [0x10, 0x2a, 0x2a, 0x2a, 0x3c, 0x00],
519 [0x3f, 0x24, 0x24, 0x24, 0x18, 0x00],
521 [0x1c, 0x22, 0x22, 0x22, 0x00, 0x00],
523 [0x18, 0x24, 0x24, 0x24, 0x3f, 0x00],
525 [0x1c, 0x2a, 0x2a, 0x2a, 0x24, 0x00],
527 [0x00, 0x3e, 0x05, 0x01, 0x00, 0x00],
529 [0x18, 0xa4, 0xa4, 0xa4, 0x7c, 0x00],
531 [0x3f, 0x04, 0x04, 0x04, 0x38, 0x00],
533 [0x00, 0x24, 0x3d, 0x20, 0x00, 0x00],
535 [0x20, 0x40, 0x40, 0x3d, 0x00, 0x00],
537 [0x3f, 0x0c, 0x12, 0x20, 0x00, 0x00],
539 [0x1f, 0x20, 0x20, 0x00, 0x00, 0x00],
541 [0x3e, 0x02, 0x3c, 0x02, 0x3c, 0x00],
543 [0x3e, 0x02, 0x02, 0x02, 0x3c, 0x00],
545 [0x1c, 0x22, 0x22, 0x22, 0x1c, 0x00],
547 [0xfc, 0x24, 0x24, 0x24, 0x18, 0x00],
549 [0x18, 0x24, 0x24, 0x24, 0xfc, 0x00],
551 [0x3e, 0x04, 0x02, 0x02, 0x00, 0x00],
553 [0x24, 0x2a, 0x2a, 0x2a, 0x10, 0x00],
555 [0x02, 0x1f, 0x22, 0x20, 0x00, 0x00],
557 [0x1e, 0x20, 0x20, 0x20, 0x1e, 0x00],
559 [0x06, 0x18, 0x20, 0x18, 0x06, 0x00],
561 [0x1e, 0x30, 0x1c, 0x30, 0x1e, 0x00],
563 [0x22, 0x14, 0x08, 0x14, 0x22, 0x00],
565 [0x1c, 0xa0, 0xa0, 0xa0, 0x7c, 0x00],
567 [0x22, 0x32, 0x2a, 0x26, 0x22, 0x00],
569 [0x0c, 0x3f, 0x21, 0x00, 0x00, 0x00],
571 [0x3f, 0x00, 0x00, 0x00, 0x00, 0x00],
573 [0x21, 0x3f, 0x0c, 0x00, 0x00, 0x00],
575 [0x02, 0x01, 0x02, 0x01, 0x00, 0x00],
577 [0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
579 ];
580
581 let g = (input as usize)
582 .checked_sub(b'!'.into())
583 .and_then(|idx| CHARS.get(idx))
584 .unwrap_or(&CHARS[CHARS.len() - 1]);
585
586 [0, g[0], g[1], g[2], g[3], g[4], g[5], 0]
587 }
588
589 fn rotate_bitmap(bitmap: [u8; 8]) -> [u8; 8] {
590 let mut rotated: [u8; 8] = [0; 8];
591
592 for (col, source) in bitmap.iter().enumerate() {
593 for (row, item) in rotated.iter_mut().enumerate() {
595 let bit = source & (1 << row) != 0;
596 if bit {
597 *item |= 1 << col;
598 }
599 }
600 }
601
602 rotated
603 }
604}
605
606#[cfg(feature = "async")]
607impl<DI, SIZE> Ssd1306Async<DI, SIZE, TerminalModeAsync>
608where
609 DI: AsyncWriteOnlyDataCommand,
610 SIZE: TerminalDisplaySizeAsync,
611{
612 pub async fn write_str(&mut self, s: &str) -> Result<(), TerminalModeError> {
614 for c in s.chars() {
615 self.print_char(c).await?;
616 }
617 Ok(())
618 }
619}
620
621impl<DI, SIZE> fmt::Write for Ssd1306<DI, SIZE, TerminalMode>
622where
623 DI: WriteOnlyDataCommand,
624 SIZE: TerminalDisplaySize,
625{
626 fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
627 s.chars().map(move |c| self.print_char(c)).next_back();
628 Ok(())
629 }
630}