Multi Custom character
In this exercise, we'll display the Ferris image generated with the help of the multi generator. I will not be repeating the project creation steps here. You can either follow the previous chapter steps or just clone the previous chapter project and work on top of that.
I have created the Ferris image using 6 adjacent grids with the generator from the previous page. Here’s the Rust code to utilize those byte arrays.
Generated Byte array for the characters
#![allow(unused)] fn main() { const SYMBOL1: [u8; 8] = [ 0b00110, 0b01000, 0b01110, 0b01000, 0b00100, 0b00011, 0b00100, 0b01000, ]; const SYMBOL2: [u8; 8] = [ 0b00000, 0b00000, 0b00000, 0b10001, 0b10001, 0b11111, 0b00000, 0b00000, ]; const SYMBOL3: [u8; 8] = [ 0b01100, 0b00010, 0b01110, 0b00010, 0b00100, 0b11000, 0b00100, 0b00010, ]; const SYMBOL4: [u8; 8] = [ 0b01000, 0b01000, 0b00100, 0b00011, 0b00001, 0b00010, 0b00101, 0b01000, ]; const SYMBOL5: [u8; 8] = [ 0b00000, 0b00000, 0b00000, 0b11111, 0b01010, 0b10001, 0b00000, 0b00000, ]; const SYMBOL6: [u8; 8] = [ 0b00010, 0b00010, 0b00100, 0b11000, 0b10000, 0b01000, 0b10100, 0b00010, ]; }
Declare them as character
#![allow(unused)] fn main() { lcd.custom_char(&mut Delay, &SYMBOL1, 0); lcd.custom_char(&mut Delay, &SYMBOL2, 1); lcd.custom_char(&mut Delay, &SYMBOL3, 2); lcd.custom_char(&mut Delay, &SYMBOL4, 3); lcd.custom_char(&mut Delay, &SYMBOL5, 4); lcd.custom_char(&mut Delay, &SYMBOL6, 5); }
Display
Let’s write the first 3 grids into the first row, then the second half into the second row of the LCD display.
#![allow(unused)] fn main() { lcd.set_cursor(&mut Delay, 0, 4) .write(&mut Delay, CustomChar(0)) .write(&mut Delay, CustomChar(1)) .write(&mut Delay, CustomChar(2)); lcd.set_cursor(&mut Delay, 1, 4) .write(&mut Delay, CustomChar(3)) .write(&mut Delay, CustomChar(4)) .write(&mut Delay, CustomChar(5)); }
Clone the existing project
You can clone (or refer) project I created and navigate to the lcd-custom-multi folder.
git clone https://github.com/ImplFerris/esp32-projects
cd esp32-projects/lcd-custom-multi/
The Full code
#![no_std] #![no_main] #![deny( clippy::mem_forget, reason = "mem::forget is generally not safe to do with esp_hal types, especially those \ holding buffers for the duration of a data transfer." )] use defmt::info; use embassy_executor::Spawner; use embassy_time::Delay; use embassy_time::{Duration, Timer}; use esp_hal::clock::CpuClock; use esp_hal::timer::timg::TimerGroup; use esp_println as _; // I2C use esp_hal::i2c::master::Config as I2cConfig; // for convenience, importing as alias use esp_hal::i2c::master::I2c; use esp_hal::time::Rate; // HD44780 Driver use liquid_crystal::I2C; use liquid_crystal::LiquidCrystal; use liquid_crystal::prelude::*; #[panic_handler] fn panic(_: &core::panic::PanicInfo) -> ! { loop {} } // This creates a default app-descriptor required by the esp-idf bootloader. // For more information see: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description> esp_bootloader_esp_idf::esp_app_desc!(); const SYMBOL1: [u8; 8] = [ 0b00110, 0b01000, 0b01110, 0b01000, 0b00100, 0b00011, 0b00100, 0b01000, ]; const SYMBOL2: [u8; 8] = [ 0b00000, 0b00000, 0b00000, 0b10001, 0b10001, 0b11111, 0b00000, 0b00000, ]; const SYMBOL3: [u8; 8] = [ 0b01100, 0b00010, 0b01110, 0b00010, 0b00100, 0b11000, 0b00100, 0b00010, ]; const SYMBOL4: [u8; 8] = [ 0b01000, 0b01000, 0b00100, 0b00011, 0b00001, 0b00010, 0b00101, 0b01000, ]; const SYMBOL5: [u8; 8] = [ 0b00000, 0b00000, 0b00000, 0b11111, 0b01010, 0b10001, 0b00000, 0b00000, ]; const SYMBOL6: [u8; 8] = [ 0b00010, 0b00010, 0b00100, 0b11000, 0b10000, 0b01000, 0b10100, 0b00010, ]; #[esp_rtos::main] async fn main(spawner: Spawner) -> ! { // generator version: 1.0.0 let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); let peripherals = esp_hal::init(config); let timg0 = TimerGroup::new(peripherals.TIMG0); esp_rtos::start(timg0.timer0); info!("Embassy initialized!"); let _ = spawner; let i2c_bus = I2c::new( peripherals.I2C0, // I2cConfig is alias of esp_hal::i2c::master::I2c::Config I2cConfig::default().with_frequency(Rate::from_khz(400)), ) .unwrap() .with_scl(peripherals.GPIO18) .with_sda(peripherals.GPIO23) .into_async(); let mut i2c_interface = I2C::new(i2c_bus, 0x27); let mut lcd = LiquidCrystal::new(&mut i2c_interface, Bus4Bits, LCD16X2); lcd.begin(&mut Delay); // Define the characters lcd.custom_char(&mut Delay, &SYMBOL1, 0); lcd.custom_char(&mut Delay, &SYMBOL2, 1); lcd.custom_char(&mut Delay, &SYMBOL3, 2); lcd.custom_char(&mut Delay, &SYMBOL4, 3); lcd.custom_char(&mut Delay, &SYMBOL5, 4); lcd.custom_char(&mut Delay, &SYMBOL6, 5); lcd.set_cursor(&mut Delay, 0, 4) .write(&mut Delay, CustomChar(0)) .write(&mut Delay, CustomChar(1)) .write(&mut Delay, CustomChar(2)); lcd.set_cursor(&mut Delay, 1, 4) .write(&mut Delay, CustomChar(3)) .write(&mut Delay, CustomChar(4)) .write(&mut Delay, CustomChar(5)); loop { Timer::after(Duration::from_secs(1)).await; } }