From 4ce27fbe4ecdabce7c4c87872b9ae7e32b97c9ec Mon Sep 17 00:00:00 2001 From: blamster19 Date: Sat, 17 Feb 2024 21:52:36 +0100 Subject: [PATCH 1/2] example: add example 7in5 v2 program The program tests rotations and draws analog clock (based on example code for different models from the repository), tests default fonts of embedded-graphics crate and displays an image of Ferris from `exampl es/assets/` directory. --- examples/assets/ferris.raw | Bin 0 -> 17806 bytes examples/epd7in5_v2.rs | 172 +++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 examples/assets/ferris.raw create mode 100644 examples/epd7in5_v2.rs diff --git a/examples/assets/ferris.raw b/examples/assets/ferris.raw new file mode 100644 index 0000000000000000000000000000000000000000..f60803dddec4a98893506d03f2b610389412b144 GIT binary patch literal 17806 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 Result<(), SPIError> { + // Set up the device + let mut spi = SpidevDevice::open("/dev/spidev0.0").expect("spidev directory"); + let options = SpidevOptions::new() + .bits_per_word(8) + .max_speed_hz(10_000_000) + .mode(spidev::SpiModeFlags::SPI_MODE_0) + .build(); + spi.configure(&options).expect("spi configuration"); + + let cs = SysfsPin::new(26); + cs.export().expect("cs export"); + while !cs.is_exported() {} + cs.set_direction(Direction::Out).expect("CS Direction"); + cs.set_value(1).expect("CS Value set to 1"); + + let busy = SysfsPin::new(24); + busy.export().expect("busy export"); + while !busy.is_exported() {} + busy.set_direction(Direction::In).expect("busy Direction"); + + let dc = SysfsPin::new(25); + dc.export().expect("dc export"); + while !dc.is_exported() {} + dc.set_direction(Direction::Out).expect("dc Direction"); + dc.set_value(1).expect("dc Value set to 1"); + + let rst = SysfsPin::new(17); + rst.export().expect("rst export"); + while !rst.is_exported() {} + rst.set_direction(Direction::Out).expect("rst Direction"); + rst.set_value(1).expect("rst Value set to 1"); + + let mut delay = Delay {}; + + let mut epd7in5 = Epd7in5::new(&mut spi, busy, dc, rst, &mut delay, None).expect("epd new"); + let mut display = Display7in5::default(); + println!("Device successfully initialized!"); + + // Test graphics display + + println!("Test all the rotations"); + + display.set_rotation(DisplayRotation::Rotate0); + draw_text(&mut display, "Rotate 0!", 5, 50); + + display.set_rotation(DisplayRotation::Rotate90); + draw_text(&mut display, "Rotate 90!", 5, 50); + + display.set_rotation(DisplayRotation::Rotate180); + draw_text(&mut display, "Rotate 180!", 5, 50); + + display.set_rotation(DisplayRotation::Rotate270); + draw_text(&mut display, "Rotate 270!", 5, 50); + + epd7in5.update_and_display_frame(&mut spi, display.buffer(), &mut delay)?; + delay.delay_ms(5000); + + // Draw an analog clock + println!("Draw a clock"); + display.clear(Color::White).ok(); + let style = PrimitiveStyleBuilder::new() + .stroke_color(Color::Black) + .stroke_width(1) + .build(); + + let _ = Circle::with_center(Point::new(64, 64), 80) + .into_styled(style) + .draw(&mut display); + let _ = Line::new(Point::new(64, 64), Point::new(0, 64)) + .into_styled(style) + .draw(&mut display); + let _ = Line::new(Point::new(64, 64), Point::new(80, 80)) + .into_styled(style) + .draw(&mut display); + epd7in5.update_and_display_frame(&mut spi, display.buffer(), &mut delay)?; + delay.delay_ms(5000); + + // Draw some text + println!("Print text in all sizes"); + // Color is inverted - black means white, white means black; the output will be black text on white background + display.clear(Color::Black).ok(); + let fonts = [ + &FONT_4X6, + &FONT_5X7, + &FONT_5X8, + &FONT_6X9, + &FONT_6X10, + &FONT_6X12, + &FONT_6X13, + &FONT_6X13_BOLD, + &FONT_6X13_ITALIC, + &FONT_7X13, + &FONT_7X13_BOLD, + &FONT_7X13_ITALIC, + &FONT_7X14, + &FONT_7X14_BOLD, + &FONT_8X13, + &FONT_8X13_BOLD, + &FONT_8X13_ITALIC, + &FONT_9X15, + &FONT_9X15_BOLD, + &FONT_9X18, + &FONT_9X18_BOLD, + &FONT_10X20, + ]; + for n in 0..fonts.len() { + let style = MonoTextStyleBuilder::new() + .font(fonts[n]) + .text_color(Color::White) + .background_color(Color::Black) + .build(); + let text_style = TextStyleBuilder::new().baseline(Baseline::Top).build(); + let y = 10 + n * 30; + let _ = Text::with_text_style( + "Rust is awesome!", + Point::new(20, y.try_into().unwrap()), + style, + text_style, + ) + .draw(&mut display); + } + epd7in5.update_and_display_frame(&mut spi, display.buffer(), &mut delay)?; + delay.delay_ms(5000); + + // Draw an image + println!("Draw Ferris"); + display.clear(Color::Black).ok(); + let data = include_bytes!("./assets/ferris.raw"); + let raw_image = ImageRaw::::new(data, 460); + let image = Image::new(&raw_image, Point::zero()); + image.draw(&mut display.color_converted()).unwrap(); + epd7in5.update_and_display_frame(&mut spi, display.buffer(), &mut delay)?; + + // Clear and sleep + println!("Clear the display"); + display.clear(Color::Black).ok(); + epd7in5.update_and_display_frame(&mut spi, display.buffer(), &mut delay)?; + println!("Finished tests - going to sleep"); + epd7in5.sleep(&mut spi, &mut delay) +} + +fn draw_text(display: &mut Display7in5, text: &str, x: i32, y: i32) { + let style = MonoTextStyleBuilder::new() + .font(&embedded_graphics::mono_font::ascii::FONT_6X10) + .text_color(Color::White) + .background_color(Color::Black) + .build(); + + let text_style = TextStyleBuilder::new().baseline(Baseline::Top).build(); + + let _ = Text::with_text_style(text, Point::new(x, y), style, text_style).draw(display); +} From aa8a73eb00103d071fb7f4ee03bded81a3b13b21 Mon Sep 17 00:00:00 2001 From: Christoph Gross <11088935+caemor@users.noreply.github.com> Date: Tue, 29 Oct 2024 16:27:00 +0100 Subject: [PATCH 2/2] Update examples/epd7in5_v2.rs Add documentation to the example --- examples/epd7in5_v2.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/epd7in5_v2.rs b/examples/epd7in5_v2.rs index 4086d1e2..b2ba074f 100644 --- a/examples/epd7in5_v2.rs +++ b/examples/epd7in5_v2.rs @@ -1,3 +1,4 @@ +// This example tests rotations and draws analog clock, tests default fonts of embedded-graphics crate and displays an image of Ferris from examples/assets/ directory. use embedded_graphics::{ image::Image, image::ImageRaw,