turing_smart_screen/
screen.rs

1use image::ImageBuffer;
2use serialport::SerialPort;
3
4pub const WIDTH: u16 = 320;
5pub const HEIGHT: u16 = 480;
6const SCREEN_SERIAL: &str = "USB35INCHIPSV2";
7
8#[allow(dead_code)]
9pub enum Orientation {
10	Portrait = 0,
11	ReversePortrait = 1,
12	Landscape = 2,
13	ReverseLandscape = 3,
14}
15
16#[allow(dead_code)]
17pub enum ScreenCommand {
18	Reset = 101,
19	Clear = 102,
20	ToBlack = 103,
21	ScreenOff = 108,
22	ScreenOn = 109,
23	SetBrigthness = 110,
24	SetOrientation = 121,
25	DisplayBitmap = 197,
26}
27
28pub struct Screen {
29	port: Box<dyn SerialPort>
30}
31
32impl Screen {
33	/// Automagically finds the port the screen is connected to.
34	/// If the screen is not found, an empty string is returned.
35	/// If the screen is found, the port name is returned.
36	pub fn find_port() -> Result<String, serialport::Error> {
37		let ports = serialport::available_ports()?;
38		let mut port = "".to_string();
39		for p in ports {
40			match p.port_type {
41				serialport::SerialPortType::UsbPort(info) => {
42					let sn = info.serial_number.unwrap_or("--".to_string());
43					if sn == SCREEN_SERIAL {
44						port = p.port_name.clone();
45					}
46				}
47				_ => { /* ignore */ }
48			}
49		}
50
51		Ok(port)
52	}
53
54	/// Creates a new Screen instance.
55	/// Requires the port name as a parameter. It can be obtained by calling the `find_port()` function.
56	pub fn new(port_name: String) -> Result<Screen, serialport::Error> {
57		let port = serialport::new(&port_name, 115_200)
58			.timeout(std::time::Duration::from_secs(1))
59			.open()?;
60		Ok(Screen { port })
61	}
62}
63
64impl Screen {
65	fn send_command(&mut self, x: u16, y: u16, ex: u16, ey: u16, cmd: ScreenCommand) -> Result<(), crate::errors::ScreenError> {
66		let mut byte_buffer = [0u8; 6];
67		byte_buffer[0] = (x >> 2) as u8;
68		byte_buffer[1] = (((x & 3) << 6) + (y >> 4)) as u8;
69		byte_buffer[2] = (((y & 15) << 4) + (ex >> 6)) as u8;
70		byte_buffer[3] = (((ex & 63) << 2) + (ey >> 8)) as u8;
71		byte_buffer[4] = (ey & 255) as u8;
72		byte_buffer[5] = cmd as u8;
73
74		self.port.write(&byte_buffer).map_err(|_| crate::errors::ScreenError::WriteError)?;
75
76		Ok(())
77	}
78
79	/// Sets the screens orientation
80	#[allow(unused)]
81	pub fn orientation(&mut self, orientation: Orientation) -> Result<(), crate::errors::ScreenError> {
82		let bytes = vec![0, 0, 0, 0, 0, ScreenCommand::SetOrientation as u8, (orientation as u8) + 100, 3, 200, 4, 0];
83
84		self.port.write(&bytes).map_err(|_| crate::errors::ScreenError::WriteError)?;
85
86		Ok(())
87	}
88
89	#[allow(unused)]
90	/// Clears the screen to white.
91	/// Does not work correctly in landscape mode, switch to Portrait mode before using this function.
92	pub fn clear(&mut self) -> Result<(), crate::errors::ScreenError> {
93		self.send_command(0, 0, 0, 0, ScreenCommand::Clear)
94	}
95
96	#[allow(unused)]
97	/// Clears the screen to black.
98	/// Does not work correctly in landscape mode, switch to Portrait mode before using this function.
99	pub fn to_black(&mut self) -> Result<(), crate::errors::ScreenError> {
100		self.send_command(0, 0, 0, 0, ScreenCommand::ToBlack)
101	}
102
103	#[allow(unused)]
104	/// Sets the brightness of the screen.
105	/// Level must be between 0 and 255. 0 is the brightest, 255 is the darkest.
106	pub fn brightness(&mut self, level: u8) -> Result<(), crate::errors::ScreenError> {
107		let level = 255 - level; // Invert level
108		self.send_command(level as u16, 0, 0, 0, ScreenCommand::SetBrigthness)
109	}
110
111	#[allow(unused)]
112	/// Turns the screen off. It will still be powered on, but the screen will be black.
113	/// To turn the screen back on, use the screen_on() function. Retains the current image.
114	pub fn screen_off(&mut self) -> Result<(), crate::errors::ScreenError> {
115		self.send_command(0, 0, 0, 0, ScreenCommand::ScreenOff)
116	}
117
118	#[allow(unused)]
119	/// Turns the screen on. The screen will display the last image that was drawn.
120	pub fn screen_on(&mut self) -> Result<(), crate::errors::ScreenError> {
121		self.send_command(0, 0, 0, 0, ScreenCommand::ScreenOn)
122	}
123
124	// // Not working
125	// #[allow(unused)]
126	// pub fn reset(&mut self) {
127	// 	self.send_command(0, 0, 0, 0, ScreenCommand::RESET);
128
129	// 	std::thread::sleep(std::time::Duration::from_secs(3));
130
131	// 	// Reconnect to the screen
132	// 	// self.port = serialport::new(&self.port_name, 115_200)
133	// 	// 	.timeout(std::time::Duration::from_secs(1))
134	// 	// 	.open()
135	// 	// 	.expect("Failed to open port");
136	// }
137
138	#[allow(unused)]
139	/// Draws an `ImageBuffer` to the screen.
140	/// The image must be 320x480 or 480x320. Although not checked, the orientation of the image should match the orientation of the screen.
141	/// Otherwise the screen will still interpret the image as if it were in the wrong orientation, part of the image may be cut off and the screen will wrap around to the start in rendering.
142	pub fn draw(&mut self, img: ImageBuffer<image::Rgb<u8>, Vec<u8>>) -> Result<(), crate::errors::ScreenError> {
143		if !((img.width() == WIDTH.into() || img.height() == HEIGHT.into()) || (img.width() == HEIGHT.into() || img.height() == WIDTH.into())) {
144			// panic!("Canvas size must be 320x480 or 480x320");
145			return Err(crate::errors::ScreenError::WrongImageSize);
146		}
147
148		let width = img.width();
149		let height = img.height();
150
151		// Set the display region
152		self.send_command(0, 0, (width - 1) as u16, (height - 1) as u16, ScreenCommand::DisplayBitmap)?;
153
154		let pixels: Vec<_> = img.pixels().collect();
155		let width = width as usize;
156
157		for (i, chunk) in pixels.chunks_exact(width * 8).enumerate() {
158			let mut bytes: Vec<u8> = Vec::with_capacity(chunk.len() * 2);
159			for pixel in chunk {
160				let r = (pixel[0] >> 3) as u16;
161				let g = (pixel[1] >> 2) as u16;
162				let b = (pixel[2] >> 3) as u16;
163				let rgb565 = (r << 11) | (g << 5) | b;
164				bytes.push((rgb565 & 0xFF) as u8); // LSB
165				bytes.push((rgb565 >> 8) as u8); // MSB
166			}
167			self.port.write(&bytes).map_err(|_| crate::errors::ScreenError::WriteError)?;
168		}
169
170		// Write the remaining pixels if any
171		let remainder = pixels.chunks_exact(width * 8).remainder();
172		if !remainder.is_empty() {
173			let mut bytes: Vec<u8> = Vec::with_capacity(remainder.len() * 2);
174			for pixel in remainder {
175				let r = (pixel[0] >> 3) as u16;
176				let g = (pixel[1] >> 2) as u16;
177				let b = (pixel[2] >> 3) as u16;
178				let rgb565 = (r << 11) | (g << 5) | b;
179				bytes.push((rgb565 & 0xFF) as u8); // LSB
180				bytes.push((rgb565 >> 8) as u8); // MSB
181			}
182			self.port.write(&bytes).map_err(|_| crate::errors::ScreenError::WriteError)?;
183		}
184
185		Ok(())
186	}
187}