From a33cb94a35a21e1213dfaaf4445a740640750038 Mon Sep 17 00:00:00 2001 From: Yandrik Date: Mon, 18 Nov 2024 16:34:52 +0100 Subject: [PATCH] feat: add lvgl based impl --- lvgl-based/.cargo/config.toml | 5 +- lvgl-based/Cargo.toml | 12 + lvgl-based/sdkconfig.defaults | 2 +- lvgl-based/src/bin/starter.rs | 266 +++++++++++++++++++++ lvgl-based/src/bin/timer.rs | 432 ++++++++++++++++++++++++++++++++++ lvgl-based/src/main.rs | 3 +- lvgl-based/tree.text | 322 +++++++++++++++++++++++++ 7 files changed, 1038 insertions(+), 4 deletions(-) create mode 100644 lvgl-based/src/bin/starter.rs create mode 100644 lvgl-based/src/bin/timer.rs create mode 100644 lvgl-based/tree.text diff --git a/lvgl-based/.cargo/config.toml b/lvgl-based/.cargo/config.toml index 404d868..6e3b72b 100644 --- a/lvgl-based/.cargo/config.toml +++ b/lvgl-based/.cargo/config.toml @@ -4,7 +4,7 @@ target = "xtensa-esp32-espidf" [target.xtensa-esp32-espidf] linker = "ldproxy" # runner = "espflash --monitor" # Select this runner for espflash v1.x.x -runner = "espflash flash --monitor" # Select this runner for espflash v2.x.x +runner = "espflash flash --monitor --baud 921600" # Select this runner for espflash v2.x.x rustflags = [ # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110 "--cfg", @@ -31,3 +31,6 @@ CROSS_COMPILE = "xtensa-esp32-elf" # Directory for custom fonts (written in C) that Lvgl can use LVGL_FONTS_DIR = {relative = true, value = "custom-fonts"} + +PATH="/usr/lib64/ccache:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/var/lib/snapd/snap/bin:/home/yannik/.rustup/toolchains/esp/xtensa-esp32s2-elf/esp-2021r2-patch5-8_4_0/xtensa-esp32s2-elf/bin:/home/yannik/.rustup/toolchains/esp/xtensa-esp32s3-elf/esp-2021r2-patch5-8_4_0/xtensa-esp32s3-elf/bin:/home/yannik/.rustup/toolchains/esp/xtensa-esp32-elf/esp-2021r2-patch5-8_4_0/xtensa-esp32-elf/bin:/home/yannik/.rustup/toolchains/esp/riscv32-esp-elf/esp-2021r2-patch5-8_4_0/riscv32-esp-elf/bin:" +LIBCLANG_PATH="/home/yannik/.rustup/toolchains/esp/xtensa-esp32-elf-clang/esp-15.0.0-20221201/esp-clang/lib" diff --git a/lvgl-based/Cargo.toml b/lvgl-based/Cargo.toml index 5bf3a8d..9c38d0e 100644 --- a/lvgl-based/Cargo.toml +++ b/lvgl-based/Cargo.toml @@ -15,6 +15,12 @@ opt-level = "s" debug = true # Symbols are nice and they don't increase the size on Flash opt-level = "z" +[[bin]] +name = "starter" + +[[bin]] +name = "timer" + [features] default = ["embassy", "esp-idf-svc/native", "std"] @@ -50,6 +56,12 @@ display-interface-spi = "0.5.0" mipidsi = "0.8.0" static_cell = "2.1.0" +xpt2046 = { git = "https://github.com/Yandrik/xpt2046.git", version = "0.3.1" } + +embedded-graphics-profiler-display = { version = "0.1.0", path = "../embedded-graphics-profiler-display", features = ["std"] } + + + [build-dependencies] embuild = "0.32.0" diff --git a/lvgl-based/sdkconfig.defaults b/lvgl-based/sdkconfig.defaults index 94aa88e..f5791c8 100644 --- a/lvgl-based/sdkconfig.defaults +++ b/lvgl-based/sdkconfig.defaults @@ -1,6 +1,6 @@ # Rust often needs a bit of an extra main task stack size compared to C (the default is 3K) CONFIG_ESP_MAIN_TASK_STACK_SIZE=8000 -# CONFIG_ESP_MAIN_TASK_STACK_SIZE=16000 +CONFIG_ESP_MAIN_TASK_STACK_SIZE=48000 # Use this to set FreeRTOS kernel tick frequency to 1000 Hz (100 Hz by default). # This allows to use 1 ms granuality for thread sleeps (10 ms by default). diff --git a/lvgl-based/src/bin/starter.rs b/lvgl-based/src/bin/starter.rs new file mode 100644 index 0000000..80776d8 --- /dev/null +++ b/lvgl-based/src/bin/starter.rs @@ -0,0 +1,266 @@ +use std::{ + cell::RefCell, + sync::mpsc::channel, + thread, + time::{Duration, Instant}, +}; +use std::cmp::min; +use cstr_core::CString; +use display_interface_spi::SPIInterface; +use embedded_graphics_core::{draw_target::DrawTarget, prelude::Point}; +use esp_idf_hal::spi::SpiSingleDeviceDriver; +use esp_idf_hal::{ + delay::{self, Delay}, + gpio::*, + peripherals::Peripherals, + spi::{config::DriverConfig, Dma, SpiConfig, SpiDeviceDriver}, + units::FromValueType, // for converting 26MHz to value +}; +use lvgl::{ + font::Font, + input_device::{ + pointer::{Pointer, PointerInputData}, + InputDriver, + }, + style::Style, + widgets::{Btn, Label}, + Align, + Color, + Display, + DrawBuffer, + LvError, + Part, + TextAlign, + Widget, +}; +use mipidsi::{ + models::ILI9486Rgb565, + options::{ColorInversion, ColorOrder, Orientation, Rotation}, + Builder, +}; +use xpt2046::Xpt2046; +use embedded_graphics_profiler_display::ProfilerDisplay; + +fn main() -> Result<(), LvError> { + const HOR_RES: u32 = 320; + const VER_RES: u32 = 240; + const LINES: u32 = 20; + + // It is necessary to call this function once. Otherwise some patches to the + // runtime implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 + esp_idf_svc::sys::link_patches(); + + // Initialize lvgl + lvgl::init(); + + // Bind the log crate to the ESP Logging facilities + esp_idf_svc::log::EspLogger::initialize_default(); + + log::info!("================= Starting App ======================="); + + let peripherals = Peripherals::take().unwrap(); + + // #[allow(unused)] + let pins = peripherals.pins; + + let sclk = pins.gpio14; + let miso = pins.gpio12; + let mosi = pins.gpio13; + let cs = pins.gpio15; + let dc = pins.gpio2; + + let spi = SpiDeviceDriver::new_single( + peripherals.spi2, + sclk, // sclk + mosi, // sdo + Some(miso), // sdi + Some(cs), // cs + &DriverConfig::new().dma(Dma::Channel1(4096)), + &SpiConfig::new().write_only(true).baudrate(10.MHz().into()), + ) + .unwrap(); + + // let rst = PinDriver::output(pins.gpio33).unwrap(); + let dc = PinDriver::output(dc).unwrap(); + let di = SPIInterface::new(spi, dc); + + // Turn backlight on + let mut bklt = PinDriver::output(pins.gpio21).unwrap(); + bklt.set_high().unwrap(); + + // Configuration for M5Stack Core Development Kit V1.0 + // Puts display in landscape mode with the three buttons at the bottom of screen + // let mut m5stack_display = Builder::ili9342c_rgb565(di) + // .with_display_size(320, 240) + // .with_color_order(ColorOrder::Bgr) + // .with_orientation(Orientation::Portrait(false)) + // .with_invert_colors(mipidsi::ColorInversion::Inverted) + // .init(&mut delay::Ets, Some(rst)) + // .unwrap(); + + // 0.6.0 + // let mut raw_display = Builder::ili9342c_rgb565(di) + // .with_orientation(Orientation::Portrait(false)) + // .with_color_order(ColorOrder::Bgr) + // .with_invert_colors(true) + // .init(&mut Delay::new_default(), None::>) + // .unwrap(); + + let raw_display = Builder::new(ILI9486Rgb565, di) + .orientation(Orientation { + rotation: Rotation::Deg90, + mirrored: true, + }) + .color_order(ColorOrder::Bgr) + .invert_colors(ColorInversion::Inverted) + .init(&mut Delay::new_default()) + .unwrap(); + + let mut raw_display = ProfilerDisplay::new(raw_display); + + // Stack size value - 20,000 for 10 lines, 40,000 for 20 lines + // let (touch_send, touch_recv) = channel(); + let touch_irq = pins.gpio36; + let touch_mosi = pins.gpio32; + let touch_miso = pins.gpio39; + let touch_clk = pins.gpio25; + let touch_cs = pins.gpio33; + + let mut touch_driver = Xpt2046::new( + SpiDeviceDriver::new_single( + peripherals.spi3, + touch_clk, + touch_mosi, + Some(touch_miso), + Some(touch_cs), + &DriverConfig::new(), + &SpiConfig::new().write_only(true).baudrate(2.MHz().into()), + ) + .unwrap(), + PinDriver::input(touch_irq).unwrap(), + xpt2046::Orientation::LandscapeFlipped, + ); + touch_driver.set_num_samples(1); + touch_driver.init(&mut Delay::new_default()).unwrap(); + + let touch_driver = RefCell::new(touch_driver); + + let _lvgl_thread = thread::Builder::new() + .stack_size(40_000) + .spawn(move || { + println!("thread started"); + + let buffer = DrawBuffer::<{ (HOR_RES * LINES) as usize }>::default(); + let display = Display::register(buffer, HOR_RES, VER_RES, |refresh| { + raw_display.draw_iter(refresh.as_pixels()).unwrap(); + }) + .unwrap(); + + // Register a new input device that's capable of reading the current state of + // the input + // let mut last_touch = RefCell::new(None); + let _touch_screen = Pointer::register( + || { + let mut td_ref = touch_driver.borrow_mut(); + td_ref.run().expect("Running Touch driver failed"); + if td_ref.is_touched() { + let point = td_ref.get_touch_point(); + PointerInputData::Touch(point).pressed().once() + } else { + PointerInputData::Touch(Point::new(0, 0)).released().once() + } + }, + &display, + ); + + // Create screen and widgets + let mut screen = display.get_scr_act().unwrap(); + let mut screen_style = Style::default(); + screen_style.set_bg_color(Color::from_rgb((0, 0, 0))); + screen_style.set_radius(0); + screen.add_style(Part::Main, &mut screen_style); + + let mut time = Label::new().unwrap(); + let mut style_time = Style::default(); + style_time.set_text_color(Color::from_rgb((255, 255, 255))); // white + style_time.set_text_align(TextAlign::Center); + + // Custom font requires lvgl-sys in Cargo.toml and 'use lvgl_sys' in this file + style_time.set_text_font(unsafe { Font::new_raw(lvgl_sys::gotham_bold_80) }); + + time.add_style(Part::Main, &mut style_time); + + // Time text will be centered in screen + time.set_align(Align::Center, 0, 0); + + let mut button = Btn::create(&mut screen).unwrap(); + button.set_align(Align::LeftMid, 30, 0); + button.set_size(180, 80); + let mut btn_lbl = Label::create(&mut button).unwrap(); + btn_lbl.set_text(CString::new("Click me!").unwrap().as_c_str()); + + let mut btn_state = false; + button.on_event(|_btn, event| { + // println!("Button received event: {:?}", event); + if let lvgl::Event::Clicked = event { + if btn_state { + let nt = CString::new("Click me!").unwrap(); + btn_lbl.set_text(nt.as_c_str()).unwrap(); + } else { + let nt = CString::new("Clicked!").unwrap(); + btn_lbl.set_text(nt.as_c_str()).unwrap(); + } + btn_state = !btn_state; + } + }); + + let mut i = 0; + + loop { + let start_time = Instant::now(); + if i > 59 { + i = 0; + } + + let val = CString::new(format!("21:{:02}", i)).unwrap(); + time.set_text(&val).unwrap(); + i += 1; + + let start_draw_time = Instant::now(); + lvgl::task_handler(); + + // Simulate clock - so sleep for one second so time text is incremented in + // seconds + delay::FreeRtos::delay_ms(10); + + lvgl::tick_inc(Instant::now().duration_since(start_time)); + + + let end_time = Instant::now(); + let draw_time = raw_display.get_time(); + let prep_time = start_draw_time - start_time; + let proc_time = end_time - start_draw_time; + let proc_time = proc_time - min(draw_time, proc_time); + + if draw_time.as_micros() > 0 { + println!( + "draw time: {}.{:03}ms | prep time: {}.{:03}ms | proc time: {}.{:03}ms | total time: {}.{:03}ms", + draw_time.as_millis(), + draw_time.as_micros() % 100, + prep_time.as_millis(), + prep_time.as_micros() % 100, + proc_time.as_millis(), + proc_time.as_micros() % 100, + (draw_time + prep_time + proc_time).as_millis(), + (draw_time + prep_time + proc_time).as_micros() % 100, ); + } + raw_display.reset_time(); + } + }) + .unwrap(); + + loop { + // Don't exit application + delay::FreeRtos::delay_ms(1_000_000); + } +} diff --git a/lvgl-based/src/bin/timer.rs b/lvgl-based/src/bin/timer.rs new file mode 100644 index 0000000..845fa33 --- /dev/null +++ b/lvgl-based/src/bin/timer.rs @@ -0,0 +1,432 @@ +use std::{ + cell::RefCell, + cmp::min, + sync::mpsc::channel, + thread, + time::{Duration, Instant}, +}; + +use cstr_core::CString; +use display_interface_spi::SPIInterface; +use embedded_graphics_core::{draw_target::DrawTarget, prelude::Point}; +use embedded_graphics_profiler_display::ProfilerDisplay; +use esp_idf_hal::spi::SpiSingleDeviceDriver; +use esp_idf_hal::{ + delay::{self, Delay}, + gpio::*, + peripherals::Peripherals, + spi::{config::DriverConfig, Dma, SpiConfig, SpiDeviceDriver}, + units::FromValueType, // for converting 26MHz to value +}; +use lvgl::{ + font::Font, + input_device::{ + pointer::{Pointer, PointerInputData}, + InputDriver, + }, + style::Style, + widgets::{Btn, Label}, + Align, + Color, + Display, + DrawBuffer, + Event, + LvError, + Part, + TextAlign, + Widget, +}; +use mipidsi::{ + models::ILI9486Rgb565, + options::{ColorInversion, ColorOrder, Orientation, Rotation}, + Builder, +}; +use xpt2046::Xpt2046; + +struct AppData { + timer_start: Instant, + timer_set_duration: Duration, + timer_remaining_duration: Duration, + timer_running: bool, + timer_paused: bool, +} + +impl AppData { + fn new() -> Self { + Self { + timer_start: Instant::now(), + timer_set_duration: Duration::from_secs(10), + timer_remaining_duration: Duration::from_secs(10), + timer_running: false, + timer_paused: false, + } + } + + fn set_timer_duration(&mut self, duration: Duration) { + self.timer_set_duration = duration; + } + + fn add_secs(&mut self, secs: u64) { + self.set_timer_duration( + self.timer_set_duration + .checked_add(Duration::from_secs(secs)) + .unwrap_or(Duration::from_secs(5999)) + .min(Duration::from_secs(5999)), + ); + } + + fn sub_secs(&mut self, secs: u64) { + self.set_timer_duration( + self.timer_set_duration + .checked_sub(Duration::from_secs(secs)) + .unwrap_or(Duration::from_secs(10)) + .max(Duration::from_secs(10)), + ); + } + + fn start_timer(&mut self) { + if !self.timer_paused { + self.timer_remaining_duration = self.timer_set_duration; + } + + self.timer_start = Instant::now(); + self.timer_running = true; + self.timer_paused = false; + } + + fn pause_timer(&mut self) { + self.timer_paused = true; + self.timer_remaining_duration = self + .timer_remaining_duration + .checked_sub(self.timer_start.elapsed()) + .unwrap_or(Duration::from_secs(0)); + } + + fn reset_timer(&mut self) { + self.timer_start = Instant::now(); + self.timer_paused = false; + self.timer_running = false; + self.timer_remaining_duration = self.timer_set_duration; + } + + fn remaining(&self) -> Duration { + if self.timer_stopped() { + self.timer_set_duration + } else if self.timer_paused() { + self.timer_remaining_duration + } else { + self.timer_remaining_duration + .checked_sub(self.timer_start.elapsed()) + .unwrap_or(Duration::from_secs(0)) + } + } + + fn timer_stopped(&self) -> bool { + !self.timer_running + } + + fn timer_paused(&self) -> bool { + self.timer_running && self.timer_paused + } + + fn timer_running(&self) -> bool { + self.timer_running && !self.timer_paused + } + + fn timer_finished(&self) -> bool { + self.timer_running && self.remaining() == Duration::from_secs(0) + } +} + +fn main() -> Result<(), LvError> { + const HOR_RES: u32 = 320; + const VER_RES: u32 = 240; + const LINES: u32 = 20; + + // It is necessary to call this function once. Otherwise some patches to the + // runtime implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 + esp_idf_svc::sys::link_patches(); + + // Initialize lvgl + lvgl::init(); + + // Bind the log crate to the ESP Logging facilities + esp_idf_svc::log::EspLogger::initialize_default(); + + log::info!("================= Starting App ======================="); + + let peripherals = Peripherals::take().unwrap(); + + // #[allow(unused)] + let pins = peripherals.pins; + + let sclk = pins.gpio14; + let miso = pins.gpio12; + let mosi = pins.gpio13; + let cs = pins.gpio15; + let dc = pins.gpio2; + + let spi = SpiDeviceDriver::new_single( + peripherals.spi2, + sclk, // sclk + mosi, // sdo + Some(miso), // sdi + Some(cs), // cs + &DriverConfig::new().dma(Dma::Channel1(4096)), + &SpiConfig::new().write_only(true).baudrate(10.MHz().into()), + ) + .unwrap(); + + // let rst = PinDriver::output(pins.gpio33).unwrap(); + let dc = PinDriver::output(dc).unwrap(); + let di = SPIInterface::new(spi, dc); + + // Turn backlight on + let mut bklt = PinDriver::output(pins.gpio21).unwrap(); + bklt.set_high().unwrap(); + + // Configuration for M5Stack Core Development Kit V1.0 + // Puts display in landscape mode with the three buttons at the bottom of screen + // let mut m5stack_display = Builder::ili9342c_rgb565(di) + // .with_display_size(320, 240) + // .with_color_order(ColorOrder::Bgr) + // .with_orientation(Orientation::Portrait(false)) + // .with_invert_colors(mipidsi::ColorInversion::Inverted) + // .init(&mut delay::Ets, Some(rst)) + // .unwrap(); + + // 0.6.0 + // let mut raw_display = Builder::ili9342c_rgb565(di) + // .with_orientation(Orientation::Portrait(false)) + // .with_color_order(ColorOrder::Bgr) + // .with_invert_colors(true) + // .init(&mut Delay::new_default(), None::>) + // .unwrap(); + + let raw_display = Builder::new(ILI9486Rgb565, di) + .orientation(Orientation { + rotation: Rotation::Deg90, + mirrored: true, + }) + .color_order(ColorOrder::Bgr) + .invert_colors(ColorInversion::Inverted) + .init(&mut Delay::new_default()) + .unwrap(); + + let mut raw_display = ProfilerDisplay::new(raw_display); + + // Stack size value - 20,000 for 10 lines, 40,000 for 20 lines + // let (touch_send, touch_recv) = channel(); + let touch_irq = pins.gpio36; + let touch_mosi = pins.gpio32; + let touch_miso = pins.gpio39; + let touch_clk = pins.gpio25; + let touch_cs = pins.gpio33; + + let mut touch_driver = Xpt2046::new( + SpiDeviceDriver::new_single( + peripherals.spi3, + touch_clk, + touch_mosi, + Some(touch_miso), + Some(touch_cs), + &DriverConfig::new(), + &SpiConfig::new().write_only(true).baudrate(2.MHz().into()), + ) + .unwrap(), + PinDriver::input(touch_irq).unwrap(), + xpt2046::Orientation::LandscapeFlipped, + ); + touch_driver.set_num_samples(1); + touch_driver.init(&mut Delay::new_default()).unwrap(); + + let touch_driver = RefCell::new(touch_driver); + + let _lvgl_thread = thread::Builder::new() + .stack_size(40_000) + .spawn(move || { + println!("thread started"); + + let mut appdata = AppData::new(); + + let buffer = DrawBuffer::<{ (HOR_RES * LINES) as usize }>::default(); + let display = Display::register(buffer, HOR_RES, VER_RES, |refresh| { + raw_display.draw_iter(refresh.as_pixels()).unwrap(); + }) + .unwrap(); + + // Register a new input device that's capable of reading the current state of + // the input + // let mut last_touch = RefCell::new(None); + let _touch_screen = Pointer::register( + || { + let mut td_ref = touch_driver.borrow_mut(); + td_ref.run().expect("Running Touch driver failed"); + if td_ref.is_touched() { + let point = td_ref.get_touch_point(); + // println!("touched {:?}", point); + PointerInputData::Touch(Point::new(point.x + 20, 240 - point.y)).pressed().once() + } else { + // println!("untouched"); + PointerInputData::Touch(Point::new(0, 0)).released().once() + } + }, + &display, + ); + + // Create screen and widgets + let mut screen = display.get_scr_act().unwrap(); + let mut screen_style = Style::default(); + screen_style.set_bg_color(Color::from_rgb((0, 0, 0))); + screen_style.set_radius(0); + screen.add_style(Part::Main, &mut screen_style); + + let mut time = Label::new().unwrap(); + let mut style_time = Style::default(); + style_time.set_text_color(Color::from_rgb((255, 255, 255))); // white + style_time.set_text_align(TextAlign::Center); + + // Custom font requires lvgl-sys in Cargo.toml and 'use lvgl_sys' in this file + + time.add_style(Part::Main, &mut style_time); + + // Time text will be centered in screen + time.set_align(Align::Center, 0, 0); + + let mut button_add = Btn::create(&mut screen).unwrap(); + // button_add.set_align(Align::Center, -50, -100); + button_add.set_pos(130, 150); + button_add.set_size(30, 30); + let mut btn_lbl1 = Label::create(&mut button_add).unwrap(); + btn_lbl1.set_text(CString::new(b"+").unwrap().as_c_str()); + + button_add.on_event(|_btn, event| { + if let Event::Pressed = event { + println!("pressed"); + if appdata.timer_stopped() { + appdata.add_secs(10); + } + } + // println!("Button received event: {:?}", event); + }); + + let mut button_sub = Btn::create(&mut screen).unwrap(); + button_sub.set_pos(170, 150); + button_sub.set_size(30, 30); + let mut btn_lbl2 = Label::create(&mut button_sub).unwrap(); + btn_lbl2.set_text(CString::new(b"-").unwrap().as_c_str()); + + button_sub.on_event(|_btn, event| { + if let Event::Pressed = event { + if appdata.timer_stopped() { + appdata.sub_secs(10); + } + } + }); + + let mut button_reset = Btn::create(&mut screen).unwrap(); + button_reset.set_pos(123, 190); + button_reset.set_size(35, 35); + let mut btn_lbl3 = Label::create(&mut button_reset).unwrap(); + btn_lbl3.set_text(CString::new(b"\xEF\x80\xA1").unwrap().as_c_str()); + + + const PLAY: &'static [u8; 3] = b"\xEF\x81\x8B"; + const PAUSE: &'static [u8; 3] = b"\xEF\x81\x8C"; + const STOP: &'static [u8; 3] = b"\xEF\x81\x8D"; + + let mut button_start_stop = Btn::create(&mut screen).unwrap(); + button_start_stop.set_pos(173, 190); + button_start_stop.set_size(35, 35); + let mut btn_lbl4 = Label::create(&mut button_start_stop).unwrap(); + btn_lbl4.set_text(CString::new(b"\xEF\x81\x8B").unwrap().as_c_str()); + + button_reset.on_event(|_btn, event| { + if let Event::Pressed = event { + appdata.reset_timer(); + btn_lbl4.set_text(CString::new(PLAY).unwrap().as_c_str()); + } + }); + + button_start_stop.on_event(|_btn, event| { + if let Event::Pressed = event { + if appdata.timer_finished() { + // println!("Resetting finished timer"); + appdata.reset_timer(); + btn_lbl4.set_text(CString::new(PLAY).unwrap().as_c_str()); + } else if appdata.timer_running() { + // println!("Pausing timer"); + appdata.pause_timer(); + btn_lbl4.set_text(CString::new(PLAY).unwrap().as_c_str()); + } else { + // println!("Starting timer"); + appdata.start_timer(); + if appdata.timer_finished() { + // println!("Timer already finished"); + btn_lbl4.set_text(CString::new(STOP).unwrap().as_c_str()); + } else { + // println!("Timer running"); + btn_lbl4.set_text(CString::new(PAUSE).unwrap().as_c_str()); + } + } + } + }); + + let mut was_finished = false; + + loop { + let start_time = Instant::now(); + let rem_time = appdata.remaining(); + let val = CString::new(format!("{:02}:{:02}:{:03}", + rem_time.as_secs() / 60, + rem_time.as_secs() % 60, + rem_time.as_millis() % 1000)).unwrap(); + time.set_text(&val).unwrap(); + + if !was_finished && appdata.timer_finished() { + was_finished = true; + btn_lbl4.set_text(CString::new(STOP).unwrap().as_c_str()); + } + was_finished = appdata.timer_finished(); + + let start_draw_time = Instant::now(); + lvgl::task_handler(); + + // Simulate clock - so sleep for one second so time text is incremented in + // seconds + // delay::FreeRtos::delay_ms(1); + + lvgl::tick_inc(Instant::now().duration_since(start_time)); + + + let end_time = Instant::now(); + let draw_time = raw_display.get_time(); + let prep_time = start_draw_time - start_time; + let proc_time = end_time - start_draw_time; + let proc_time = proc_time - min(draw_time, proc_time); + + if draw_time.as_micros() > 0 { + println!( + "draw time: {}.{:03}ms | prep time: {}.{:03}ms | proc time: {}.{:03}ms | total time: {}.{:03}ms", + draw_time.as_millis(), + draw_time.as_micros() % 100, + prep_time.as_millis(), + prep_time.as_micros() % 100, + proc_time.as_millis(), + proc_time.as_micros() % 100, + (draw_time + prep_time + proc_time).as_millis(), + (draw_time + prep_time + proc_time).as_micros() % 100, ); + } + raw_display.reset_time(); + + delay::FreeRtos::delay_ms(1); + } + + }) + .unwrap(); + + loop { + // Don't exit application + delay::FreeRtos::delay_ms(1_000_000); + } +} diff --git a/lvgl-based/src/main.rs b/lvgl-based/src/main.rs index d15e701..45a8ac1 100644 --- a/lvgl-based/src/main.rs +++ b/lvgl-based/src/main.rs @@ -125,7 +125,6 @@ fn main() -> Result<(), LvError> { style_time.set_text_align(TextAlign::Center); // Custom font requires lvgl-sys in Cargo.toml and 'use lvgl_sys' in this file - style_time.set_text_font(unsafe { Font::new_raw(lvgl_sys::gotham_bold_80) }); time.add_style(Part::Main, &mut style_time); @@ -148,7 +147,7 @@ fn main() -> Result<(), LvError> { lvgl::task_handler(); // Simulate clock - so sleep for one second so time text is incremented in seconds - delay::FreeRtos::delay_ms(1000); + delay::FreeRtos::delay_ms(1); lvgl::tick_inc(Instant::now().duration_since(start)); } diff --git a/lvgl-based/tree.text b/lvgl-based/tree.text new file mode 100644 index 0000000..a542737 --- /dev/null +++ b/lvgl-based/tree.text @@ -0,0 +1,322 @@ +rust-m5stack-lvgl-demo v0.1.0 (/home/yannik/repos/kolibri-cyd-tester-app-embassy/lvgl-based) +├── cstr_core v0.2.6 +│ ├── cty v0.2.2 +│ └── memchr v2.7.4 +├── display-interface-spi v0.5.0 +│ ├── byte-slice-cast v1.2.2 +│ ├── display-interface v0.5.0 +│ ├── embedded-hal v1.0.0 +│ └── embedded-hal-async v1.0.0 +│ └── embedded-hal v1.0.0 +├── embedded-graphics-core v0.4.0 +│ ├── az v1.2.1 +│ └── byteorder v1.5.0 +├── esp-idf-hal v0.44.1 +│ ├── atomic-waker v1.1.2 +│ ├── critical-section v1.2.0 +│ ├── embassy-sync v0.6.0 +│ │ ├── cfg-if v1.0.0 +│ │ ├── critical-section v1.2.0 +│ │ ├── embedded-io-async v0.6.1 +│ │ │ └── embedded-io v0.6.1 +│ │ ├── futures-util v0.3.31 +│ │ │ ├── futures-core v0.3.31 +│ │ │ ├── futures-task v0.3.31 +│ │ │ ├── pin-project-lite v0.2.15 +│ │ │ └── pin-utils v0.1.0 +│ │ └── heapless v0.8.0 +│ │ ├── hash32 v0.3.1 +│ │ │ └── byteorder v1.5.0 +│ │ └── stable_deref_trait v1.2.0 +│ ├── embedded-can v0.4.1 +│ │ └── nb v1.1.0 +│ ├── embedded-hal v0.2.7 +│ │ ├── nb v0.1.3 +│ │ │ └── nb v1.1.0 +│ │ └── void v1.0.2 +│ ├── embedded-hal v1.0.0 +│ ├── embedded-hal-async v1.0.0 (*) +│ ├── embedded-hal-nb v1.0.0 +│ │ ├── embedded-hal v1.0.0 +│ │ └── nb v1.1.0 +│ ├── embedded-io v0.6.1 +│ ├── embedded-io-async v0.6.1 (*) +│ ├── enumset v1.1.5 +│ │ └── enumset_derive v0.10.0 (proc-macro) +│ │ ├── darling v0.20.10 +│ │ │ ├── darling_core v0.20.10 +│ │ │ │ ├── fnv v1.0.7 +│ │ │ │ ├── ident_case v1.0.1 +│ │ │ │ ├── proc-macro2 v1.0.89 +│ │ │ │ │ └── unicode-ident v1.0.13 +│ │ │ │ ├── quote v1.0.37 +│ │ │ │ │ └── proc-macro2 v1.0.89 (*) +│ │ │ │ └── syn v2.0.87 +│ │ │ │ ├── proc-macro2 v1.0.89 (*) +│ │ │ │ ├── quote v1.0.37 (*) +│ │ │ │ └── unicode-ident v1.0.13 +│ │ │ └── darling_macro v0.20.10 (proc-macro) +│ │ │ ├── darling_core v0.20.10 (*) +│ │ │ ├── quote v1.0.37 (*) +│ │ │ └── syn v2.0.87 (*) +│ │ ├── proc-macro2 v1.0.89 (*) +│ │ ├── quote v1.0.37 (*) +│ │ └── syn v2.0.87 (*) +│ ├── esp-idf-sys v0.35.0 +│ │ ├── build-time v0.1.3 (proc-macro) +│ │ │ ├── chrono v0.4.38 +│ │ │ │ ├── iana-time-zone v0.1.61 +│ │ │ │ └── num-traits v0.2.19 +│ │ │ │ [build-dependencies] +│ │ │ │ └── autocfg v1.4.0 +│ │ │ ├── once_cell v1.20.2 +│ │ │ ├── proc-macro2 v1.0.89 (*) +│ │ │ ├── quote v1.0.37 (*) +│ │ │ └── syn v2.0.87 (*) +│ │ ├── const_format v0.2.33 +│ │ │ └── const_format_proc_macros v0.2.33 (proc-macro) +│ │ │ ├── proc-macro2 v1.0.89 (*) +│ │ │ ├── quote v1.0.37 (*) +│ │ │ └── unicode-xid v0.2.6 +│ │ └── libc v0.2.162 +│ │ [build-dependencies] +│ │ ├── anyhow v1.0.93 +│ │ ├── bindgen v0.69.5 +│ │ │ ├── bitflags v2.6.0 +│ │ │ ├── cexpr v0.6.0 +│ │ │ │ └── nom v7.1.3 +│ │ │ │ ├── memchr v2.7.4 +│ │ │ │ └── minimal-lexical v0.2.1 +│ │ │ ├── clang-sys v1.8.1 +│ │ │ │ ├── glob v0.3.1 +│ │ │ │ ├── libc v0.2.162 +│ │ │ │ └── libloading v0.8.5 +│ │ │ │ └── cfg-if v1.0.0 +│ │ │ │ [build-dependencies] +│ │ │ │ └── glob v0.3.1 +│ │ │ ├── itertools v0.12.1 +│ │ │ │ └── either v1.13.0 +│ │ │ ├── lazy_static v1.5.0 +│ │ │ ├── lazycell v1.3.0 +│ │ │ ├── log v0.4.22 +│ │ │ ├── prettyplease v0.2.25 +│ │ │ │ ├── proc-macro2 v1.0.89 (*) +│ │ │ │ └── syn v2.0.87 (*) +│ │ │ ├── proc-macro2 v1.0.89 (*) +│ │ │ ├── quote v1.0.37 (*) +│ │ │ ├── regex v1.11.1 +│ │ │ │ ├── aho-corasick v1.1.3 +│ │ │ │ │ └── memchr v2.7.4 +│ │ │ │ ├── memchr v2.7.4 +│ │ │ │ ├── regex-automata v0.4.9 +│ │ │ │ │ ├── aho-corasick v1.1.3 (*) +│ │ │ │ │ ├── memchr v2.7.4 +│ │ │ │ │ └── regex-syntax v0.8.5 +│ │ │ │ └── regex-syntax v0.8.5 +│ │ │ ├── rustc-hash v1.1.0 +│ │ │ ├── shlex v1.3.0 +│ │ │ ├── syn v2.0.87 (*) +│ │ │ └── which v4.4.2 +│ │ │ ├── either v1.13.0 +│ │ │ ├── home v0.5.9 +│ │ │ └── rustix v0.38.40 +│ │ │ ├── bitflags v2.6.0 +│ │ │ └── linux-raw-sys v0.4.14 +│ │ ├── cargo_metadata v0.18.1 +│ │ │ ├── camino v1.1.9 +│ │ │ │ └── serde v1.0.215 +│ │ │ │ └── serde_derive v1.0.215 (proc-macro) +│ │ │ │ ├── proc-macro2 v1.0.89 (*) +│ │ │ │ ├── quote v1.0.37 (*) +│ │ │ │ └── syn v2.0.87 (*) +│ │ │ ├── cargo-platform v0.1.8 +│ │ │ │ └── serde v1.0.215 (*) +│ │ │ ├── semver v1.0.23 +│ │ │ │ └── serde v1.0.215 (*) +│ │ │ ├── serde v1.0.215 (*) +│ │ │ ├── serde_json v1.0.132 +│ │ │ │ ├── itoa v1.0.11 +│ │ │ │ ├── memchr v2.7.4 +│ │ │ │ ├── ryu v1.0.18 +│ │ │ │ └── serde v1.0.215 (*) +│ │ │ └── thiserror v1.0.69 +│ │ │ └── thiserror-impl v1.0.69 (proc-macro) +│ │ │ ├── proc-macro2 v1.0.89 (*) +│ │ │ ├── quote v1.0.37 (*) +│ │ │ └── syn v2.0.87 (*) +│ │ ├── embuild v0.32.0 +│ │ │ ├── anyhow v1.0.93 +│ │ │ ├── bindgen v0.69.5 (*) +│ │ │ ├── bitflags v1.3.2 +│ │ │ ├── cmake v0.1.51 +│ │ │ │ └── cc v1.2.1 +│ │ │ │ └── shlex v1.3.0 +│ │ │ ├── filetime v0.2.25 +│ │ │ │ ├── cfg-if v1.0.0 +│ │ │ │ └── libc v0.2.162 +│ │ │ ├── globwalk v0.8.1 +│ │ │ │ ├── bitflags v1.3.2 +│ │ │ │ ├── ignore v0.4.23 +│ │ │ │ │ ├── crossbeam-deque v0.8.5 +│ │ │ │ │ │ ├── crossbeam-epoch v0.9.18 +│ │ │ │ │ │ │ └── crossbeam-utils v0.8.20 +│ │ │ │ │ │ └── crossbeam-utils v0.8.20 +│ │ │ │ │ ├── globset v0.4.15 +│ │ │ │ │ │ ├── aho-corasick v1.1.3 (*) +│ │ │ │ │ │ ├── bstr v1.11.0 +│ │ │ │ │ │ │ └── memchr v2.7.4 +│ │ │ │ │ │ ├── log v0.4.22 +│ │ │ │ │ │ ├── regex-automata v0.4.9 (*) +│ │ │ │ │ │ └── regex-syntax v0.8.5 +│ │ │ │ │ ├── log v0.4.22 +│ │ │ │ │ ├── memchr v2.7.4 +│ │ │ │ │ ├── regex-automata v0.4.9 (*) +│ │ │ │ │ ├── same-file v1.0.6 +│ │ │ │ │ └── walkdir v2.5.0 +│ │ │ │ │ └── same-file v1.0.6 +│ │ │ │ └── walkdir v2.5.0 (*) +│ │ │ ├── home v0.5.9 +│ │ │ ├── log v0.4.22 +│ │ │ ├── regex v1.11.1 (*) +│ │ │ ├── remove_dir_all v0.8.4 +│ │ │ │ ├── cfg-if v1.0.0 +│ │ │ │ ├── cvt v0.1.2 +│ │ │ │ │ └── cfg-if v1.0.0 +│ │ │ │ ├── fs_at v0.2.1 +│ │ │ │ │ ├── cfg-if v1.0.0 +│ │ │ │ │ ├── cvt v0.1.2 (*) +│ │ │ │ │ ├── libc v0.2.162 +│ │ │ │ │ └── nix v0.29.0 +│ │ │ │ │ ├── bitflags v2.6.0 +│ │ │ │ │ ├── cfg-if v1.0.0 +│ │ │ │ │ └── libc v0.2.162 +│ │ │ │ │ [build-dependencies] +│ │ │ │ │ └── cfg_aliases v0.2.1 +│ │ │ │ ├── libc v0.2.162 +│ │ │ │ └── normpath v1.3.0 +│ │ │ ├── serde v1.0.215 (*) +│ │ │ ├── serde_json v1.0.132 (*) +│ │ │ ├── shlex v1.3.0 +│ │ │ ├── strum v0.24.1 +│ │ │ │ └── strum_macros v0.24.3 (proc-macro) +│ │ │ │ ├── heck v0.4.1 +│ │ │ │ ├── proc-macro2 v1.0.89 (*) +│ │ │ │ ├── quote v1.0.37 (*) +│ │ │ │ ├── rustversion v1.0.18 (proc-macro) +│ │ │ │ └── syn v1.0.109 +│ │ │ │ ├── proc-macro2 v1.0.89 (*) +│ │ │ │ ├── quote v1.0.37 (*) +│ │ │ │ └── unicode-ident v1.0.13 +│ │ │ ├── tempfile v3.14.0 +│ │ │ │ ├── cfg-if v1.0.0 +│ │ │ │ ├── fastrand v2.2.0 +│ │ │ │ ├── once_cell v1.20.2 +│ │ │ │ └── rustix v0.38.40 (*) +│ │ │ ├── thiserror v1.0.69 (*) +│ │ │ └── which v4.4.2 (*) +│ │ ├── envy v0.4.2 +│ │ │ └── serde v1.0.215 (*) +│ │ ├── regex v1.11.1 (*) +│ │ ├── serde v1.0.215 (*) +│ │ ├── strum v0.24.1 (*) +│ │ └── which v4.4.2 (*) +│ ├── heapless v0.8.0 (*) +│ ├── log v0.4.22 +│ ├── nb v1.1.0 +│ └── num_enum v0.7.3 +│ └── num_enum_derive v0.7.3 (proc-macro) +│ ├── proc-macro2 v1.0.89 (*) +│ ├── quote v1.0.37 (*) +│ └── syn v2.0.87 (*) +│ [build-dependencies] +│ └── embuild v0.32.0 (*) +├── esp-idf-svc v0.49.1 +│ ├── embassy-futures v0.1.1 +│ ├── embassy-time-driver v0.1.0 +│ │ └── document-features v0.2.10 (proc-macro) +│ │ └── litrs v0.4.1 +│ ├── embedded-hal-async v1.0.0 (*) +│ ├── embedded-svc v0.28.0 +│ │ ├── embedded-io v0.6.1 +│ │ ├── embedded-io-async v0.6.1 (*) +│ │ ├── enumset v1.1.5 (*) +│ │ ├── heapless v0.8.0 (*) +│ │ └── serde v1.0.215 +│ │ └── serde_derive v1.0.215 (proc-macro) (*) +│ ├── enumset v1.1.5 (*) +│ ├── esp-idf-hal v0.44.1 (*) +│ ├── heapless v0.8.0 (*) +│ ├── log v0.4.22 +│ ├── num_enum v0.7.3 (*) +│ └── uncased v0.9.10 +│ [build-dependencies] +│ └── version_check v0.9.5 +│ [build-dependencies] +│ └── embuild v0.32.0 (*) +├── esp-idf-sys v0.35.0 (*) +├── log v0.4.22 +├── lvgl v0.6.2 (https://github.com/enelson1001/lv_binding_rust#723ad383) +│ ├── bitflags v2.6.0 +│ ├── cstr_core v0.2.6 (*) +│ ├── ctor v0.2.8 (proc-macro) +│ │ ├── quote v1.0.37 (*) +│ │ └── syn v2.0.87 (*) +│ ├── cty v0.2.2 +│ ├── embedded-graphics v0.8.1 +│ │ ├── az v1.2.1 +│ │ ├── byteorder v1.5.0 +│ │ ├── embedded-graphics-core v0.4.0 (*) +│ │ ├── float-cmp v0.9.0 +│ │ │ └── num-traits v0.2.19 +│ │ │ [build-dependencies] +│ │ │ └── autocfg v1.4.0 +│ │ └── micromath v2.1.0 +│ ├── lvgl-sys v0.6.2 (https://github.com/enelson1001/lv_binding_rust#723ad383) +│ │ └── cty v0.2.2 +│ │ [build-dependencies] +│ │ ├── bindgen v0.65.1 +│ │ │ ├── bitflags v1.3.2 +│ │ │ ├── cexpr v0.6.0 (*) +│ │ │ ├── clang-sys v1.8.1 (*) +│ │ │ ├── lazy_static v1.5.0 +│ │ │ ├── lazycell v1.3.0 +│ │ │ ├── log v0.4.22 +│ │ │ ├── peeking_take_while v0.1.2 +│ │ │ ├── prettyplease v0.2.25 (*) +│ │ │ ├── proc-macro2 v1.0.89 (*) +│ │ │ ├── quote v1.0.37 (*) +│ │ │ ├── regex v1.11.1 (*) +│ │ │ ├── rustc-hash v1.1.0 +│ │ │ ├── shlex v1.3.0 +│ │ │ ├── syn v2.0.87 (*) +│ │ │ └── which v4.4.2 (*) +│ │ └── cc v1.2.1 (*) +│ └── paste v1.0.15 (proc-macro) +│ [build-dependencies] +│ ├── lvgl-codegen v0.6.2 (https://github.com/enelson1001/lv_binding_rust#723ad383) +│ │ ├── Inflector v0.11.4 +│ │ │ ├── lazy_static v1.5.0 +│ │ │ └── regex v1.11.1 (*) +│ │ ├── lazy_static v1.5.0 +│ │ ├── proc-macro2 v1.0.89 (*) +│ │ ├── quote v1.0.37 (*) +│ │ ├── regex v1.11.1 (*) +│ │ └── syn v2.0.87 (*) +│ ├── lvgl-sys v0.6.2 (https://github.com/enelson1001/lv_binding_rust#723ad383) +│ │ └── cty v0.2.2 +│ │ [build-dependencies] +│ │ ├── bindgen v0.65.1 (*) +│ │ └── cc v1.2.1 (*) +│ ├── proc-macro2 v1.0.89 (*) +│ └── quote v1.0.37 (*) +├── lvgl-sys v0.6.2 (https://github.com/enelson1001/lv_binding_rust#723ad383) (*) +├── mipidsi v0.8.0 +│ ├── display-interface v0.5.0 +│ ├── embedded-graphics-core v0.4.0 (*) +│ ├── embedded-hal v1.0.0 +│ ├── heapless v0.8.0 (*) +│ └── nb v1.1.0 +└── static_cell v2.1.0 + └── portable-atomic v1.9.0 +[build-dependencies] +└── embuild v0.32.0 (*)