Last active
October 16, 2025 14:13
-
-
Save DonizeteVida/3c99657fb74367828e086511a1c44657 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use std::{thread::sleep, time::Duration}; | |
| use anyhow::{Ok, Result}; | |
| use image::{EncodableLayout, ImageReader}; | |
| use serialport::{SerialPort, new}; | |
| enum DisplayCommand { | |
| Clear = 102, | |
| ScreenOff = 108, | |
| ScreenOn = 109, | |
| DisplayBitmap = 197, | |
| } | |
| #[derive(Debug)] | |
| struct Display { | |
| pub width: u16, | |
| pub height: u16, | |
| serial_port: Box<dyn SerialPort>, | |
| } | |
| impl Display { | |
| fn new() -> Result<Self> { | |
| let mut ports = serialport::available_ports()?; | |
| if ports.is_empty() { | |
| panic!("No ports found") | |
| } | |
| let port_info = ports.pop().unwrap(); | |
| let mut serial_port = serialport::new(port_info.port_name, 115_200).open()?; | |
| serial_port.write_request_to_send(true)?; | |
| return Ok(Self { | |
| width: 320, | |
| height: 480, | |
| serial_port, | |
| }); | |
| } | |
| fn send(&mut self, bytes: &[u8]) { | |
| self.serial_port.write_all(bytes).unwrap(); | |
| if cfg!(debug_assertions) && false { | |
| println!("{:?}", bytes.to_ascii_lowercase()); | |
| } | |
| } | |
| fn send_statefull_command( | |
| &mut self, | |
| display_command: DisplayCommand, | |
| x: u16, | |
| y: u16, | |
| _x: u16, | |
| _y: u16, | |
| ) { | |
| let mut buffer: [u8; 6] = [0; 6]; | |
| // X 10 bits, 8 MSB written, 2 remaining | |
| buffer[0] = (x >> 2) as u8; | |
| // X 10 bits, 2 LSB written, 0 remaining | |
| // Y 10 bits, 6 MSB written, 4 remaining | |
| buffer[1] = (x << 6) as u8 + (y >> 4) as u8; | |
| // Y 10 bits, 4 LSB written, 0 remaining | |
| // _X 10 bits, 4 MSB written, 6 remaining | |
| buffer[2] = (y << 4) as u8 + (_x >> 6) as u8; | |
| // _X 10 bits, 6 LSB written, 0 remaining | |
| // _Y 10 bits, 2 MSB written, 8 remaining | |
| buffer[3] = (_x << 2) as u8 + (_y >> 8) as u8; | |
| // _Y 10 bits, 8 LSB written, 0 remaining | |
| buffer[4] = _y as u8; | |
| buffer[5] = display_command as u8; | |
| self.send(&buffer); | |
| } | |
| fn send_stateless_command(&mut self, display_command: DisplayCommand) { | |
| let mut buf = [0u8; 6]; | |
| buf[5] = display_command as u8; | |
| self.send(&buf) | |
| } | |
| fn clear_display(&mut self) { | |
| self.send_stateless_command(DisplayCommand::Clear); | |
| } | |
| fn turn_screen_on(&mut self) { | |
| self.send_stateless_command(DisplayCommand::ScreenOn) | |
| } | |
| fn turn_screen_off(&mut self) { | |
| self.send_stateless_command(DisplayCommand::ScreenOff) | |
| } | |
| fn send_rect_draw(&mut self, start_x: u16, start_y: u16, end_x: u16, end_y: u16) { | |
| self.send_statefull_command( | |
| DisplayCommand::DisplayBitmap, | |
| start_x, | |
| start_y, | |
| end_x, | |
| end_y, | |
| ) | |
| } | |
| } | |
| fn rgb888_to_rgb565(buffer: &[u8]) -> [u8; 2] { | |
| let r = buffer[0] as u16; | |
| let g = buffer[1] as u16; | |
| let b = buffer[2] as u16; | |
| //it will convert | |
| //RGB888 - 24 bits to | |
| //RGB565 - 16 bits | |
| let word = ((r & 0b11111000) << 8) | ((g & 0b11111100) << 3) | (b >> 3); | |
| [word as u8, (word >> 8) as u8] | |
| } | |
| fn display_draw_image(display: &mut Display, path: &str) -> Result<()> { | |
| display.send_rect_draw(0, 0, display.width - 1, display.height - 1); | |
| let image = ImageReader::open(path)?.decode()?.into_rgb8(); | |
| let bytes = image | |
| .as_bytes() | |
| .chunks(3) | |
| .flat_map(rgb888_to_rgb565) | |
| .collect::<Vec<_>>(); | |
| assert!(bytes.len() == (display.width as usize * display.height as usize * 2)); | |
| display.send(bytes.as_ref()); | |
| Ok(()) | |
| } | |
| fn display_draw_gif(display: &mut Display, path: &str) -> Result<()> { | |
| let file = std::fs::File::open(path)?; | |
| let mut options = gif::DecodeOptions::new(); | |
| options.set_color_output(gif::ColorOutput::RGBA); | |
| options.check_frame_consistency(true); | |
| options.check_lzw_end_code(true); | |
| let mut frames = Vec::<Vec<u8>>::new(); | |
| let mut decoder = options.read_info(file)?; | |
| while let Some(frame) = decoder.read_next_frame()? { | |
| let frame = frame | |
| .buffer | |
| .chunks(4) | |
| .flat_map(rgb888_to_rgb565) | |
| .collect::<Vec<_>>(); | |
| frames.push(frame); | |
| } | |
| let mut iter = frames.iter(); | |
| while let Some(frame) = iter.next() { | |
| display.send_rect_draw(0, 0, display.width - 1, display.height - 1); | |
| display.send(frame.as_ref()); | |
| } | |
| Ok(()) | |
| } | |
| fn main() -> Result<()> { | |
| let mut display = Display::new().expect("Display can't be created"); | |
| if cfg!(debug_assertions) && false { | |
| println!("{:#?}", display); | |
| } | |
| display.turn_screen_on(); | |
| sleep(Duration::from_secs(1)); | |
| display.clear_display(); | |
| display_draw_image(&mut display, "sample.jpg")?; | |
| display_draw_gif(&mut display, "nyancat.gif")?; | |
| Ok(()) | |
| } |
Author
DonizeteVida
commented
Oct 16, 2025

Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment