use embedded_graphics::Drawable; use embedded_graphics::framebuffer::{Framebuffer, buffer_size}; use embedded_graphics::geometry::Point; use embedded_graphics::image::ImageDrawable; use embedded_graphics::mono_font::MonoTextStyle; use embedded_graphics::mono_font::ascii::FONT_10X20; use embedded_graphics::pixelcolor::Rgb565; use embedded_graphics::pixelcolor::raw::LittleEndian; use embedded_graphics::text::{Alignment, Text}; use embedded_hal_bus::spi::ExclusiveDevice; use embedded_iconoir::prelude::IconoirNewIcon; use embedded_iconoir::size24px::development::ElectronicsChip; use embedded_iconoir::size24px::devices::SaveFloppyDisk; use embedded_iconoir::size24px::other::Clock; use embedded_iconoir::size24px::system::Cpu; use embedded_iconoir::size24px::weather::TemperatureLow; use embedded_iconoir::{Icon, icons}; use esp_hal::mcpwm::operator::PwmPin; use esp_hal::peripherals::MCPWM0; use esp_hal::spi::master::Spi; use mipidsi::interface::SpiInterface; use mipidsi::models::ST7735s; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::prelude::RgbColor; use core::fmt::Debug; use crate::Data; use crate::graph::Graph; use crate::label::Label; pub struct DisplayController { display: Display, backlight: Backlight, cpu_graph: Graph<60, 5, ICON_SIZE>, memory_graph: Graph<60, 5, ICON_SIZE>, cpu_label: Label>, memory_label: Label>, disk_label: Label>, temp_label: Label>, uptime_label: Label>, } type Display = mipidsi::Display< SpiInterface< 'static, ExclusiveDevice< Spi<'static, esp_hal::Blocking>, esp_hal::gpio::Output<'static>, esp_hal::delay::Delay, >, esp_hal::gpio::Output<'static>, >, ST7735s, esp_hal::gpio::Output<'static>, >; type Backlight = PwmPin<'static, MCPWM0<'static>, 0, true>; pub const PADDING: i32 = 5; pub const ICON_SIZE: i32 = 24; impl DisplayController { const CPU_ICON: Point = Point::new(PADDING, PADDING); const MEMORY_ICON: Point = Point::new(PADDING, Self::CPU_ICON.y + ICON_SIZE + PADDING); const TEMP_ICON: Point = Point::new(PADDING * 3 + 80, Self::MEMORY_ICON.y + ICON_SIZE + PADDING); const DISK_ICON: Point = Point::new(PADDING, Self::MEMORY_ICON.y + ICON_SIZE + PADDING); const UPTIME_ICON: Point = Point::new(PADDING, Self::DISK_ICON.y + ICON_SIZE + PADDING); const CPU_GRAPH: Point = Point::new(PADDING * 3 + 80, Self::CPU_ICON.y + ICON_SIZE); const MEMORY_GRAPH: Point = Point::new(PADDING * 3 + 80, Self::MEMORY_ICON.y + ICON_SIZE); pub fn new(display: Display, backlight: Backlight) -> Self { let cpu_icon = icons::size24px::system::Cpu::new(Rgb565::WHITE); let memory_icon = icons::size24px::development::ElectronicsChip::new(Rgb565::WHITE); let disk_icon = icons::size24px::devices::SaveFloppyDisk::new(Rgb565::WHITE); let temp_icon = icons::size24px::weather::TemperatureLow::new(Rgb565::WHITE); let uptime_icon = icons::size24px::other::Clock::new(Rgb565::WHITE); Self { display, backlight, cpu_label: Label::new(cpu_icon, Self::CPU_ICON), memory_label: Label::new(memory_icon, Self::MEMORY_ICON), disk_label: Label::new(disk_icon, Self::DISK_ICON), temp_label: Label::new(temp_icon, Self::TEMP_ICON), uptime_label: Label::new(uptime_icon, Self::UPTIME_ICON), cpu_graph: Graph::new(Self::CPU_GRAPH), memory_graph: Graph::new(Self::MEMORY_GRAPH), } } pub fn set_backlight(&mut self, brightness: u16) { self.backlight.set_timestamp(brightness); } pub fn clear(&mut self) { self.display.clear(Rgb565::BLACK).unwrap(); } pub fn draw_no_connection(&mut self) { self.clear(); static CHARACTER_STYLE: MonoTextStyle<'static, Rgb565> = MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE); let text = Text::with_alignment( "No Data", Point::new(PADDING, PADDING + 11), CHARACTER_STYLE, Alignment::Left, ); text.draw(&mut self.display).unwrap(); } pub fn draw_data(&mut self, data: &Data) { let mut fb = Framebuffer::< Rgb565, _, LittleEndian, 320, 240, { buffer_size::(320, 240) }, >::new(); self.cpu_label .draw(&mut fb, format_args!("{:0.1}%", data.cpu_usage * 100.0)); self.memory_label .draw(&mut fb, format_args!("{:0.1}%", data.memory_usage * 100.0)); self.temp_label .draw(&mut fb, format_args!("{:0.0}C", data.max_temp)); self.disk_label .draw(&mut fb, format_args!("{:0.1}%", data.disk_usage * 100.0)); self.draw_uptime(&mut fb, data); self.cpu_graph.add_value(data.cpu_usage); self.memory_graph.add_value(data.memory_usage); self.cpu_graph.draw(&mut fb); self.memory_graph.draw(&mut fb); fb.as_image().draw(&mut self.display).unwrap(); } fn draw_uptime(&self, draw_target: &mut T, data: &Data) where T: DrawTarget, ::Error: Debug, { let days = data.uptime / 86_400; let hours = (data.uptime % 86_400) / 3_600; let minutes = (data.uptime % 3_600) / 60; let seconds = data.uptime % 60; let args = if days > 0 { format_args!("{}d {}h", days, hours) } else if hours > 0 { format_args!("{}h {}m", hours, minutes) } else if minutes > 0 { format_args!("{}m {}s", minutes, seconds) } else { format_args!("{}s", seconds) }; self.uptime_label.draw(draw_target, args); } }