From 3546cf62c67b5a58771d15058f2037025c49a52f Mon Sep 17 00:00:00 2001 From: Sofia Date: Sat, 16 May 2026 22:03:34 +0300 Subject: [PATCH] Add State-system --- src/display.rs | 10 ++-- src/font.rs | 2 + src/main.rs | 34 ++++++------- src/state.rs | 126 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 20 deletions(-) create mode 100644 src/state.rs diff --git a/src/display.rs b/src/display.rs index 4eb5d76..9e4d53c 100644 --- a/src/display.rs +++ b/src/display.rs @@ -3,7 +3,7 @@ use core::ops::{Add, Mul}; use embedded_hal::{delay::DelayNs, spi::SpiBus}; use esp_hal::{DriverMode, gpio::Output, spi::master::Spi}; -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Rgb565(pub u8, pub u8, pub u8); impl Rgb565 { @@ -72,7 +72,7 @@ impl Add for Rgb565 { } } -#[derive(Default, Clone, Copy, Debug)] +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Position { pub x: i16, pub y: i16, @@ -84,7 +84,7 @@ impl Position { } } -#[derive(Default, Clone, Copy)] +#[derive(Default, Clone, Copy, PartialEq, Eq)] pub struct Color { pub bytes: [u8; 2], } @@ -371,4 +371,8 @@ impl<'d, DM: DriverMode, T: DelayNs> Display<'d, DM, T> { self.write(Writeable::Data(&color.bytes)); } } + + pub fn clear(&mut self, color: Color) { + self.draw_rect(Position::new(0, 0), Position::new(240, 240), color); + } } diff --git a/src/font.rs b/src/font.rs index c04c7b3..6ab0f48 100644 --- a/src/font.rs +++ b/src/font.rs @@ -10,12 +10,14 @@ use crate::display::{self, Color, Display, Position, Rgb565}; static OPEN_SANS: &'static [u8] = include_bytes!("./OpenSans_Condensed-Regular.ttf"); +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] pub enum HorizontalAlignment { RightToLeft, Center, LeftToRight, } +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] pub enum VerticalAlignment { TopToBottom, Center, diff --git a/src/main.rs b/src/main.rs index 94fca94..6d76c98 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,7 @@ use core::fmt::Write; -use alloc::{borrow::ToOwned, str}; +use alloc::{borrow::ToOwned, boxed::Box, str}; use embedded_hal::delay::DelayNs; use esp_alloc::export::enumset::EnumSet; use esp_hal::{ @@ -28,6 +28,7 @@ use crate::{ at_commands::ATCommands, display::{Display, Position, Rgb565, SetAddressMode}, font::{FontRenderer, HorizontalAlignment, VerticalAlignment}, + state::{HelloState, StateManager}, }; extern crate alloc; @@ -35,6 +36,7 @@ extern crate alloc; mod at_commands; mod display; mod font; +mod state; // This creates a default app-descriptor required by the esp-idf bootloader. // For more information see: @@ -156,13 +158,18 @@ fn main() -> ! { ); }; - at_commands.init(); - render_response(at_commands.raw_command("ATI".to_owned())); - render_response(at_commands.raw_command("AT+CMGF?".to_owned())); - render_response(at_commands.raw_command("AT+CPIN=1234".to_owned())); - render_response(at_commands.raw_command("AT+CPIN?".to_owned())); - render_response(at_commands.raw_command("AT+CMGF=1".to_owned())); - render_response(at_commands.raw_command("AT+CSCS=?".to_owned())); + // at_commands.init(); + // render_response(at_commands.raw_command("ATI".to_owned())); + // render_response(at_commands.raw_command("AT+CMGF?".to_owned())); + // render_response(at_commands.raw_command("AT+CPIN=1234".to_owned())); + // render_response(at_commands.raw_command("AT+CPIN?".to_owned())); + // render_response(at_commands.raw_command("AT+CMGF=1".to_owned())); + // render_response(at_commands.raw_command("AT+CSCS=?".to_owned())); + + let mut state_mgr = StateManager { + data: state::StateData::from(display, at_commands), + curr_state: Box::new(HelloState), + }; // render_response( // at_commands.raw_two_part_command("AT+CMGS=\"number\"".to_owned(), "hello".to_owned()), // ); @@ -187,15 +194,8 @@ fn main() -> ! { let mut test_delay = Delay::new(); loop { - // log::info!("Input 1: {}", input_1.is_high()); - // log::info!("Input 2: {}", input_2.is_high()); - // log::info!("Input 3: {}", input_3.is_high()); - // log::info!("Input 4: {}", input_4.is_high()); - // log::info!("Input 5: {}", input_5.is_high()); - // log::info!("Input 6: {}", input_6.is_high()); - // log::info!("Input 7: {}", input_7.is_high()); - // input_7.toggle(); - test_delay.delay_millis(100); + state_mgr.update(); + state_mgr.draw(); } // for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v1.1.0/examples diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000..6cb5d30 --- /dev/null +++ b/src/state.rs @@ -0,0 +1,126 @@ +use alloc::{boxed::Box, string::String, vec::Vec}; +use esp_hal::{Blocking, delay::Delay}; + +use crate::{ + at_commands::ATCommands, + display::{Color, Display, Position, Rgb565}, + font::{FontRenderer, HorizontalAlignment, VerticalAlignment}, +}; + +#[derive(PartialEq, Eq, Clone)] +pub struct TextSettings { + h_align: HorizontalAlignment, + v_align: VerticalAlignment, + fg: Rgb565, + bg: Rgb565, +} + +impl Default for TextSettings { + fn default() -> Self { + Self { + h_align: HorizontalAlignment::LeftToRight, + v_align: VerticalAlignment::TopToBottom, + fg: Rgb565::white(), + bg: Rgb565::black(), + } + } +} + +#[derive(PartialEq, Eq, Clone)] +pub enum DrawCommands { + Clear(Color), + DrawText(String, Position, TextSettings), +} + +pub struct StateData<'a> { + display: Display<'a, Blocking, Delay>, + at_commands: ATCommands<'a, 'a>, + font_renderer: FontRenderer<'a>, + pub delay: Delay, + + prev_commands: Vec, + commands: Vec, +} + +impl<'a> StateData<'a> { + pub fn from( + display: Display<'a, Blocking, Delay>, + at_commands: ATCommands<'a, 'a>, + ) -> StateData<'a> { + StateData { + display, + at_commands, + font_renderer: FontRenderer::create(30), + delay: Delay::new(), + + prev_commands: Vec::new(), + commands: Vec::new(), + } + } + + pub fn clear_screen(&mut self, color: Color) { + self.commands.push(DrawCommands::Clear(color)); + } + + pub fn draw_text>(&mut self, text: T, pos: Position, settings: TextSettings) { + self.commands + .push(DrawCommands::DrawText(text.into(), pos, settings)); + } +} + +pub trait State { + fn update(&self, data: &mut StateData) -> Option>; + fn draw(&self, data: &mut StateData); +} + +pub struct StateManager<'a> { + pub data: StateData<'a>, + pub curr_state: Box, +} + +impl<'a> StateManager<'a> { + pub fn update(&mut self) { + match self.curr_state.update(&mut self.data) { + Some(next_state) => self.curr_state = next_state, + None => {} + } + } + + pub fn draw(&mut self) { + self.data.commands.clear(); + self.curr_state.draw(&mut self.data); + if self.data.commands != self.data.prev_commands { + self.data.prev_commands = self.data.commands.clone(); + for command in &self.data.commands { + match command { + DrawCommands::Clear(color) => self.data.display.clear(*color), + DrawCommands::DrawText(text, position, text_settings) => { + self.data.font_renderer.render( + &mut self.data.display, + text, + *position, + text_settings.h_align, + text_settings.v_align, + text_settings.bg, + text_settings.fg, + ) + } + } + } + } + } +} + +pub struct HelloState; + +impl State for HelloState { + fn update(&self, data: &mut StateData) -> Option> { + data.delay.delay_millis(100); + None + } + + fn draw(&self, data: &mut StateData) { + data.clear_screen(Rgb565::black().as_color()); + data.draw_text("Hello World", Position::new(0, 0), TextSettings::default()); + } +}