Demo source code
```rust
use embedded_graphics::{
mono_font::MonoTextStyleBuilder,
prelude::*,
primitives::{Circle, Line, PrimitiveStyle},
text::{Baseline, Text, TextStyleBuilder},
};
use embedded_hal::delay::DelayNs;
use epd_waveshare::{
color::*,
epd2in13b_v4::{Display2in13bV4, Epd2in13bV4},
graphics::DisplayRotation,
prelude::*,
};
use linux_embedded_hal::Delay;
use rppal::gpio::Gpio;
use rppal::spi::{Bus, Mode, SimpleHalSpiDevice, SlaveSelect, Spi};
// activate spi, gpio in raspi-config
// needs to be run with sudo because of some sysfs_gpio permission problems and follow-up timing problems
// see https://github.com/rust-embedded/rust-sysfs-gpio/issues/5 and follow-up issues
//
// This example first setups SPI communication using the pin layout found
// at https://www.waveshare.com/wiki/2.13inch_e-Paper_HAT_(B). This example uses the layout for the
// Raspberry Pi Zero (RPI Zero). The Chip Select (CS) was taken from the ep2in9 example since CE0 (GPIO8) did
// not seem to work on RPI Zero with 2.13" HAT
//
// The first frame is filled with four texts at different rotations (black on white)
// The second frame uses a buffer for black/white and a seperate buffer for chromatic/white (i.e. red or yellow)
// This example draws a sample clock in black on white and two texts using white on red.
//
// after finishing, put the display to sleep
fn main() {
let mut pwr = Gpio::new().unwrap().get(18).unwrap().into_output();
pwr.set_high();
// let busy = SysfsPin::new(24); // GPIO 24, board J-18
// busy.export().expect("busy export");
// while !busy.is_exported() {}
// busy.set_direction(Direction::In).expect("busy Direction");
let busy = Gpio::new().unwrap().get(24).unwrap().into_input();
// let dc = SysfsPin::new(25); // GPIO 25, board J-22
// 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 dc = Gpio::new().unwrap().get(25).unwrap().into_output();
// let rst = SysfsPin::new(17); // GPIO 17, board J-11
// 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 rst = Gpio::new().unwrap().get(17).unwrap().into_output();
rst.set_low();
// Configure Digital I/O Pin to be used as Chip Select for SPI
// let cs = SysfsPin::new(26); // CE0, board J-24, GPIO 8 -> doesn work. use this from 2in19 example which works
// 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 mut cs = Gpio::new().unwrap().get(8).unwrap().into_output();
cs.set_low();
// let pwr = Gpio::new().unwrap().get(18).unwrap().into_output();
// cs.set_high();
// Configure SPI
// Settings are taken from
let bus = Spi::new(Bus::Spi0, SlaveSelect::Ss0, 10_000_000, Mode::Mode0).unwrap();
bus.set_bits_per_word(8).unwrap();
bus.set_ss_polarity(rppal::spi::Polarity::ActiveLow)
.unwrap();
let mut spi = SimpleHalSpiDevice::new(bus);
// 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 mut delay = Delay {};
let mut epd2in13 =
Epd2in13bV4::new(&mut spi, busy, dc, rst, &mut delay, None).expect("eink initalize error");
println!("Test all the rotations");
let mut display = Display2in13bV4::default();
display.clear(TriColor::White).ok();
display.set_rotation(DisplayRotation::Rotate0);
draw_text(&mut display, "Rotation 0!", 0, 0);
display.set_rotation(DisplayRotation::Rotate90);
draw_text(&mut display, "Rotation 90!", 0, 0);
display.set_rotation(DisplayRotation::Rotate180);
draw_text(&mut display, "Rotation 180!", 0, 0);
display.set_rotation(DisplayRotation::Rotate270);
draw_text(&mut display, "Rotation 270!", 0, 0);
epd2in13
.update_and_display_frame(&mut spi, display.bw_buffer(), &mut delay)
.expect("display frame new graphics");
println!("Waiting 5s");
delay.delay_ms(5000);
println!("Drawing an analog clock and some text");
display.set_rotation(DisplayRotation::Rotate0);
display.clear(TriColor::White).ok();
// draw a analog clock
let _ = Circle::with_center(Point::new(60, 60), 120)
.into_styled(PrimitiveStyle::with_stroke(TriColor::Black, 2))
.draw(&mut display);
let _ = Line::new(Point::new(60, 60), Point::new(76, 28))
.into_styled(PrimitiveStyle::with_stroke(TriColor::Black, 4))
.draw(&mut display);
let _ = Line::new(Point::new(60, 60), Point::new(31, 19))
.into_styled(PrimitiveStyle::with_stroke(TriColor::Chromatic, 2))
.draw(&mut display);
epd2in13
.update_color_frame(
&mut spi,
&mut delay,
display.bw_buffer(),
display.chromatic_buffer(),
)
.unwrap();
epd2in13
.display_frame(&mut spi, &mut delay)
.expect("display frame new graphics");
println!("Waiting 5s");
delay.delay_ms(5000);
println!("Testing diferent fonts and colors");
display.clear(TriColor::White).ok();
// draw text white on Red background by using the chromatic buffer
let style = MonoTextStyleBuilder::new()
.font(&embedded_graphics::mono_font::ascii::FONT_6X10)
.text_color(TriColor::White)
.background_color(TriColor::Chromatic)
.build();
let text_style = TextStyleBuilder::new().baseline(Baseline::Top).build();
let _ = Text::with_text_style("It's working", Point::new(15, 10), style, text_style)
.draw(&mut display);
// use bigger/different font
let style = MonoTextStyleBuilder::new()
.font(&embedded_graphics::mono_font::ascii::FONT_10X20)
.text_color(TriColor::Chromatic)
.background_color(TriColor::Black)
.build();
let _ = Text::with_text_style("It's working", Point::new(0, 40), style, text_style)
.draw(&mut display);
// we used three colors, so we need to update both bw-buffer and chromatic-buffer
epd2in13
.update_color_frame(
&mut spi,
&mut delay,
display.bw_buffer(),
display.chromatic_buffer(),
)
.unwrap();
epd2in13
.display_frame(&mut spi, &mut delay)
.expect("display frame new graphics");
println!("Waiting 5s");
delay.delay_ms(5000);
// clear both bw buffer and chromatic buffer
println!("Clearing screen");
display.clear(TriColor::White).ok();
epd2in13
.update_color_frame(
&mut spi,
&mut delay,
display.bw_buffer(),
display.chromatic_buffer(),
)
.unwrap();
epd2in13.display_frame(&mut spi, &mut delay).unwrap();
println!("Finished tests - going to sleep");
epd2in13.sleep(&mut spi, &mut delay).unwrap();
}
fn draw_text(display: &mut Display2in13bV4, text: &str, x: i32, y: i32) {
let style = MonoTextStyleBuilder::new()
.font(&embedded_graphics::mono_font::ascii::FONT_6X10)
.text_color(TriColor::White)
.background_color(TriColor::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);
}
```
I didn't look into how other screens are implemented and based my code mostly off the bc version, so there might be superfluous code here. I'd appreciate some pointers on what can be removed or improved.
This screen renders color differently from the bc version. Instead of red pixels being represented with a 1, they are represented with a zero. I couldn't find a way to define this on the display level, so I ended up just inverting the bits before I send them off to the screen.
This PR adds support for 2.13" (b) V4 e-ink displays.
DEMO:
https://github.com/user-attachments/assets/84bb8b7b-d999-4b4d-b3e2-1623fc8fc593
Demo source code
```rust use embedded_graphics::{ mono_font::MonoTextStyleBuilder, prelude::*, primitives::{Circle, Line, PrimitiveStyle}, text::{Baseline, Text, TextStyleBuilder}, }; use embedded_hal::delay::DelayNs; use epd_waveshare::{ color::*, epd2in13b_v4::{Display2in13bV4, Epd2in13bV4}, graphics::DisplayRotation, prelude::*, }; use linux_embedded_hal::Delay; use rppal::gpio::Gpio; use rppal::spi::{Bus, Mode, SimpleHalSpiDevice, SlaveSelect, Spi}; // activate spi, gpio in raspi-config // needs to be run with sudo because of some sysfs_gpio permission problems and follow-up timing problems // see https://github.com/rust-embedded/rust-sysfs-gpio/issues/5 and follow-up issues // // This example first setups SPI communication using the pin layout found // at https://www.waveshare.com/wiki/2.13inch_e-Paper_HAT_(B). This example uses the layout for the // Raspberry Pi Zero (RPI Zero). The Chip Select (CS) was taken from the ep2in9 example since CE0 (GPIO8) did // not seem to work on RPI Zero with 2.13" HAT // // The first frame is filled with four texts at different rotations (black on white) // The second frame uses a buffer for black/white and a seperate buffer for chromatic/white (i.e. red or yellow) // This example draws a sample clock in black on white and two texts using white on red. // // after finishing, put the display to sleep fn main() { let mut pwr = Gpio::new().unwrap().get(18).unwrap().into_output(); pwr.set_high(); // let busy = SysfsPin::new(24); // GPIO 24, board J-18 // busy.export().expect("busy export"); // while !busy.is_exported() {} // busy.set_direction(Direction::In).expect("busy Direction"); let busy = Gpio::new().unwrap().get(24).unwrap().into_input(); // let dc = SysfsPin::new(25); // GPIO 25, board J-22 // 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 dc = Gpio::new().unwrap().get(25).unwrap().into_output(); // let rst = SysfsPin::new(17); // GPIO 17, board J-11 // 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 rst = Gpio::new().unwrap().get(17).unwrap().into_output(); rst.set_low(); // Configure Digital I/O Pin to be used as Chip Select for SPI // let cs = SysfsPin::new(26); // CE0, board J-24, GPIO 8 -> doesn work. use this from 2in19 example which works // 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 mut cs = Gpio::new().unwrap().get(8).unwrap().into_output(); cs.set_low(); // let pwr = Gpio::new().unwrap().get(18).unwrap().into_output(); // cs.set_high(); // Configure SPI // Settings are taken from let bus = Spi::new(Bus::Spi0, SlaveSelect::Ss0, 10_000_000, Mode::Mode0).unwrap(); bus.set_bits_per_word(8).unwrap(); bus.set_ss_polarity(rppal::spi::Polarity::ActiveLow) .unwrap(); let mut spi = SimpleHalSpiDevice::new(bus); // 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 mut delay = Delay {}; let mut epd2in13 = Epd2in13bV4::new(&mut spi, busy, dc, rst, &mut delay, None).expect("eink initalize error"); println!("Test all the rotations"); let mut display = Display2in13bV4::default(); display.clear(TriColor::White).ok(); display.set_rotation(DisplayRotation::Rotate0); draw_text(&mut display, "Rotation 0!", 0, 0); display.set_rotation(DisplayRotation::Rotate90); draw_text(&mut display, "Rotation 90!", 0, 0); display.set_rotation(DisplayRotation::Rotate180); draw_text(&mut display, "Rotation 180!", 0, 0); display.set_rotation(DisplayRotation::Rotate270); draw_text(&mut display, "Rotation 270!", 0, 0); epd2in13 .update_and_display_frame(&mut spi, display.bw_buffer(), &mut delay) .expect("display frame new graphics"); println!("Waiting 5s"); delay.delay_ms(5000); println!("Drawing an analog clock and some text"); display.set_rotation(DisplayRotation::Rotate0); display.clear(TriColor::White).ok(); // draw a analog clock let _ = Circle::with_center(Point::new(60, 60), 120) .into_styled(PrimitiveStyle::with_stroke(TriColor::Black, 2)) .draw(&mut display); let _ = Line::new(Point::new(60, 60), Point::new(76, 28)) .into_styled(PrimitiveStyle::with_stroke(TriColor::Black, 4)) .draw(&mut display); let _ = Line::new(Point::new(60, 60), Point::new(31, 19)) .into_styled(PrimitiveStyle::with_stroke(TriColor::Chromatic, 2)) .draw(&mut display); epd2in13 .update_color_frame( &mut spi, &mut delay, display.bw_buffer(), display.chromatic_buffer(), ) .unwrap(); epd2in13 .display_frame(&mut spi, &mut delay) .expect("display frame new graphics"); println!("Waiting 5s"); delay.delay_ms(5000); println!("Testing diferent fonts and colors"); display.clear(TriColor::White).ok(); // draw text white on Red background by using the chromatic buffer let style = MonoTextStyleBuilder::new() .font(&embedded_graphics::mono_font::ascii::FONT_6X10) .text_color(TriColor::White) .background_color(TriColor::Chromatic) .build(); let text_style = TextStyleBuilder::new().baseline(Baseline::Top).build(); let _ = Text::with_text_style("It's working", Point::new(15, 10), style, text_style) .draw(&mut display); // use bigger/different font let style = MonoTextStyleBuilder::new() .font(&embedded_graphics::mono_font::ascii::FONT_10X20) .text_color(TriColor::Chromatic) .background_color(TriColor::Black) .build(); let _ = Text::with_text_style("It's working", Point::new(0, 40), style, text_style) .draw(&mut display); // we used three colors, so we need to update both bw-buffer and chromatic-buffer epd2in13 .update_color_frame( &mut spi, &mut delay, display.bw_buffer(), display.chromatic_buffer(), ) .unwrap(); epd2in13 .display_frame(&mut spi, &mut delay) .expect("display frame new graphics"); println!("Waiting 5s"); delay.delay_ms(5000); // clear both bw buffer and chromatic buffer println!("Clearing screen"); display.clear(TriColor::White).ok(); epd2in13 .update_color_frame( &mut spi, &mut delay, display.bw_buffer(), display.chromatic_buffer(), ) .unwrap(); epd2in13.display_frame(&mut spi, &mut delay).unwrap(); println!("Finished tests - going to sleep"); epd2in13.sleep(&mut spi, &mut delay).unwrap(); } fn draw_text(display: &mut Display2in13bV4, text: &str, x: i32, y: i32) { let style = MonoTextStyleBuilder::new() .font(&embedded_graphics::mono_font::ascii::FONT_6X10) .text_color(TriColor::White) .background_color(TriColor::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); } ```I didn't look into how other screens are implemented and based my code mostly off the bc version, so there might be superfluous code here. I'd appreciate some pointers on what can be removed or improved.
This screen renders color differently from the bc version. Instead of red pixels being represented with a 1, they are represented with a zero. I couldn't find a way to define this on the display level, so I ended up just inverting the bits before I send them off to the screen.