1#![no_std]
24
25use embedded_hal::delay::DelayNs;
26use embedded_hal::i2c::I2c;
27
28pub const LCD_ADDRESS: u8 = 0x3E;
30
31pub const RGB_ADDRESS: u8 = 0x62;
33
34pub const RGB_ADDRESS_V5: u8 = 0x30;
36
37const LCD_CLEARDISPLAY: u8 = 0x01;
39const LCD_RETURNHOME: u8 = 0x02;
40const LCD_ENTRYMODESET: u8 = 0x04;
41const LCD_DISPLAYCONTROL: u8 = 0x08;
42const LCD_CURSORSHIFT: u8 = 0x10;
43const LCD_FUNCTIONSET: u8 = 0x20;
44const LCD_SETCGRAMADDR: u8 = 0x40;
45#[allow(dead_code)]
46const LCD_SETDDRAMADDR: u8 = 0x80;
47
48#[allow(dead_code)]
50const LCD_ENTRYRIGHT: u8 = 0x00;
51const LCD_ENTRYLEFT: u8 = 0x02;
52const LCD_ENTRYSHIFTINCREMENT: u8 = 0x01;
53const LCD_ENTRYSHIFTDECREMENT: u8 = 0x00;
54
55const LCD_DISPLAYON: u8 = 0x04;
57#[allow(dead_code)]
58const LCD_DISPLAYOFF: u8 = 0x00;
59const LCD_CURSORON: u8 = 0x02;
60const LCD_CURSOROFF: u8 = 0x00;
61const LCD_BLINKON: u8 = 0x01;
62const LCD_BLINKOFF: u8 = 0x00;
63
64const LCD_DISPLAYMOVE: u8 = 0x08;
66#[allow(dead_code)]
67const LCD_CURSORMOVE: u8 = 0x00;
68const LCD_MOVERIGHT: u8 = 0x04;
69const LCD_MOVELEFT: u8 = 0x00;
70
71#[allow(dead_code)]
73const LCD_8BITMODE: u8 = 0x10;
74#[allow(dead_code)]
75const LCD_4BITMODE: u8 = 0x00;
76const LCD_2LINE: u8 = 0x08;
77#[allow(dead_code)]
78const LCD_1LINE: u8 = 0x00;
79const LCD_5X10_DOTS: u8 = 0x04;
80#[allow(dead_code)]
81const LCD_5X8_DOTS: u8 = 0x00;
82
83const REG_MODE1: u8 = 0x00;
85const REG_MODE2: u8 = 0x01;
86const REG_OUTPUT: u8 = 0x08;
87const REG_RED: u8 = 0x04;
88const REG_GREEN: u8 = 0x03;
89const REG_BLUE: u8 = 0x02;
90
91#[derive(Debug, Clone, Copy)]
93pub enum DotSize {
94 Dots5x8,
96 Dots5x10,
98}
99
100#[derive(Debug, Clone, Copy)]
102pub enum LcdError<E> {
103 I2c(E),
105 InvalidParameter,
107}
108
109impl<E: core::fmt::Debug> core::fmt::Display for LcdError<E> {
110 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
111 match self {
112 LcdError::I2c(e) => write!(f, "I2C Error: {:?}", e),
113 LcdError::InvalidParameter => write!(f, "Invalid parameter"),
114 }
115 }
116}
117
118impl<E> From<E> for LcdError<E> {
119 fn from(error: E) -> Self {
120 LcdError::I2c(error)
121 }
122}
123
124pub struct GroveLcd<I2C, D> {
126 i2c: I2C,
127 delay: D,
128 display_function: u8,
129 display_control: u8,
130 display_mode: u8,
131 num_lines: u8,
132 curr_line: u8,
133 rgb_addr: u8,
134}
135
136impl<I2C, D, E> GroveLcd<I2C, D>
137where
138 I2C: I2c<Error = E>,
139 D: DelayNs,
140{
141 pub fn new(i2c: I2C, delay: D) -> Self {
143 Self {
144 i2c,
145 delay,
146 display_function: 0,
147 display_control: 0,
148 display_mode: 0,
149 num_lines: 0,
150 curr_line: 0,
151 rgb_addr: RGB_ADDRESS,
152 }
153 }
154
155 pub fn begin(&mut self, cols: u8, rows: u8) -> Result<(), LcdError<E>> {
161 self.begin_with_dotsize(cols, rows, DotSize::Dots5x8)
162 }
163
164 pub fn begin_with_dotsize(
166 &mut self,
167 _cols: u8,
168 lines: u8,
169 dotsize: DotSize,
170 ) -> Result<(), LcdError<E>> {
171 if lines > 1 {
172 self.display_function |= LCD_2LINE;
173 }
174 self.num_lines = lines;
175 self.curr_line = 0;
176
177 if matches!(dotsize, DotSize::Dots5x10) && lines == 1 {
179 self.display_function |= LCD_5X10_DOTS;
180 }
181
182 self.delay.delay_ms(100);
184
185 let mut last_err = None;
188 for _attempt in 0..3 {
189 match self.command(LCD_FUNCTIONSET | self.display_function) {
190 Ok(_) => {
191 last_err = None;
192 break;
193 }
194 Err(e) => {
195 last_err = Some(e);
196 self.delay.delay_ms(10);
197 }
198 }
199 }
200 if let Some(e) = last_err {
201 return Err(e);
202 }
203
204 self.delay.delay_us(4500);
205
206 self.command(LCD_FUNCTIONSET | self.display_function)?;
207 self.delay.delay_us(150);
208
209 self.command(LCD_FUNCTIONSET | self.display_function)?;
210 self.command(LCD_FUNCTIONSET | self.display_function)?;
211
212 self.display_control = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
214 self.display()?;
215
216 self.clear()?;
218
219 self.display_mode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
221 self.command(LCD_ENTRYMODESET | self.display_mode)?;
222
223 let mut rgb_v5_detected = false;
225 for _attempt in 0..3 {
226 self.delay.delay_ms(10);
227 if self.i2c.write(RGB_ADDRESS_V5, &[]).is_ok() {
229 rgb_v5_detected = true;
230 break;
231 }
232 }
233
234 if rgb_v5_detected {
235 self.rgb_addr = RGB_ADDRESS_V5;
236 self.set_reg(0x00, 0x07).ok(); self.delay.delay_ms(200);
238 self.set_reg(0x04, 0x15).ok(); } else {
240 let mut rgb_v4_detected = false;
242 for _attempt in 0..3 {
243 self.delay.delay_ms(10);
244 if self.i2c.write(RGB_ADDRESS, &[]).is_ok() {
245 rgb_v4_detected = true;
246 break;
247 }
248 }
249
250 if rgb_v4_detected {
251 self.rgb_addr = RGB_ADDRESS;
252 self.set_reg(REG_MODE1, 0).ok();
253 self.set_reg(REG_OUTPUT, 0xFF).ok();
254 self.set_reg(REG_MODE2, 0x20).ok();
255 }
256 }
257
258 let _ = self.set_rgb(255, 255, 255);
260
261 Ok(())
262 }
263
264 pub fn clear(&mut self) -> Result<(), LcdError<E>> {
266 self.command(LCD_CLEARDISPLAY)?;
267 self.delay.delay_ms(2);
268 Ok(())
269 }
270
271 pub fn home(&mut self) -> Result<(), LcdError<E>> {
273 self.command(LCD_RETURNHOME)?;
274 self.delay.delay_ms(2);
275 Ok(())
276 }
277
278 pub fn set_cursor(&mut self, col: u8, row: u8) -> Result<(), LcdError<E>> {
284 let row = row.min(self.num_lines.saturating_sub(1));
285 let val = if row == 0 {
286 col | 0x80
287 } else {
288 col | 0xc0
289 };
290
291 let data = [0x80, val];
292 self.i2c.write(LCD_ADDRESS, &data)?;
293 Ok(())
294 }
295
296 pub fn no_display(&mut self) -> Result<(), LcdError<E>> {
298 self.display_control &= !LCD_DISPLAYON;
299 self.command(LCD_DISPLAYCONTROL | self.display_control)
300 }
301
302 pub fn display(&mut self) -> Result<(), LcdError<E>> {
304 self.display_control |= LCD_DISPLAYON;
305 self.command(LCD_DISPLAYCONTROL | self.display_control)
306 }
307
308 pub fn no_cursor(&mut self) -> Result<(), LcdError<E>> {
310 self.display_control &= !LCD_CURSORON;
311 self.command(LCD_DISPLAYCONTROL | self.display_control)
312 }
313
314 pub fn cursor(&mut self) -> Result<(), LcdError<E>> {
316 self.display_control |= LCD_CURSORON;
317 self.command(LCD_DISPLAYCONTROL | self.display_control)
318 }
319
320 pub fn no_blink(&mut self) -> Result<(), LcdError<E>> {
322 self.display_control &= !LCD_BLINKON;
323 self.command(LCD_DISPLAYCONTROL | self.display_control)
324 }
325
326 pub fn blink(&mut self) -> Result<(), LcdError<E>> {
328 self.display_control |= LCD_BLINKON;
329 self.command(LCD_DISPLAYCONTROL | self.display_control)
330 }
331
332 pub fn scroll_display_left(&mut self) -> Result<(), LcdError<E>> {
334 self.command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT)
335 }
336
337 pub fn scroll_display_right(&mut self) -> Result<(), LcdError<E>> {
339 self.command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT)
340 }
341
342 pub fn left_to_right(&mut self) -> Result<(), LcdError<E>> {
344 self.display_mode |= LCD_ENTRYLEFT;
345 self.command(LCD_ENTRYMODESET | self.display_mode)
346 }
347
348 pub fn right_to_left(&mut self) -> Result<(), LcdError<E>> {
350 self.display_mode &= !LCD_ENTRYLEFT;
351 self.command(LCD_ENTRYMODESET | self.display_mode)
352 }
353
354 pub fn autoscroll(&mut self) -> Result<(), LcdError<E>> {
356 self.display_mode |= LCD_ENTRYSHIFTINCREMENT;
357 self.command(LCD_ENTRYMODESET | self.display_mode)
358 }
359
360 pub fn no_autoscroll(&mut self) -> Result<(), LcdError<E>> {
362 self.display_mode &= !LCD_ENTRYSHIFTINCREMENT;
363 self.command(LCD_ENTRYMODESET | self.display_mode)
364 }
365
366 pub fn create_char(&mut self, location: u8, charmap: &[u8; 8]) -> Result<(), LcdError<E>> {
372 let location = location & 0x7;
373 self.command(LCD_SETCGRAMADDR | (location << 3))?;
374
375 for &byte in charmap {
376 self.write_data(byte)?;
377 }
378
379 Ok(())
380 }
381
382 pub fn set_rgb(&mut self, r: u8, g: u8, b: u8) -> Result<(), LcdError<E>> {
389 if self.rgb_addr == RGB_ADDRESS_V5 {
391 self.set_reg(0x06, r)?; self.set_reg(0x07, g)?; self.set_reg(0x08, b)?; } else {
395 self.set_reg(REG_RED, r)?; self.set_reg(REG_GREEN, g)?; self.set_reg(REG_BLUE, b)?; }
399 Ok(())
400 }
401
402 pub fn backlight_off(&mut self) -> Result<(), LcdError<E>> {
404 self.set_rgb(0, 0, 0)
405 }
406
407 pub fn backlight_white(&mut self) -> Result<(), LcdError<E>> {
409 self.set_rgb(255, 255, 255)
410 }
411
412 pub fn print(&mut self, s: &str) -> Result<(), LcdError<E>> {
414 for c in s.chars() {
415 self.write_data(c as u8)?;
416 }
417 Ok(())
418 }
419
420 pub fn write(&mut self, value: u8) -> Result<(), LcdError<E>> {
422 self.write_data(value)
423 }
424
425 fn command(&mut self, value: u8) -> Result<(), LcdError<E>> {
428 let data = [0x80, value];
429 self.i2c.write(LCD_ADDRESS, &data)?;
430 Ok(())
431 }
432
433 fn write_data(&mut self, value: u8) -> Result<(), LcdError<E>> {
434 let data = [0x40, value];
435 self.i2c.write(LCD_ADDRESS, &data)?;
436 Ok(())
437 }
438
439 fn set_reg(&mut self, reg: u8, value: u8) -> Result<(), LcdError<E>> {
440 let data = [reg, value];
441 self.i2c.write(self.rgb_addr, &data)?;
442 Ok(())
443 }
444
445 pub fn release(self) -> (I2C, D) {
447 (self.i2c, self.delay)
448 }
449}