feat: clean up part 1

This commit is contained in:
Kat Inskip 2025-10-18 04:31:20 -07:00
parent 6841142729
commit 77826ad4cd
Signed by: kat
GPG key ID: 465E64DECEA8CF0F
4 changed files with 132 additions and 98 deletions

View file

@ -27,3 +27,7 @@ esp-radio = { version = "0.16.0", features = ["smoltcp", "wifi", "esp32c3", "uns
esp-rtos = { version = "0.1.1", features = ["esp32c3", "embassy"] } esp-rtos = { version = "0.1.1", features = ["esp32c3", "embassy"] }
ili9341 = "0.6.0" ili9341 = "0.6.0"
static_cell = "2.1.1" static_cell = "2.1.1"
# https://docs.espressif.com/projects/rust/esp-wifi/0.15.0/esp32c3/esp_wifi/index.html#optimization-level
[profile.dev.package.esp-wifi]
opt-level = 3

View file

@ -32,6 +32,7 @@
buildy buildy
pkgs.rustup pkgs.rustup
pkgs.espflash pkgs.espflash
pkgs.clippy
pkgs.pkg-config pkgs.pkg-config
pkgs.stdenv.cc pkgs.stdenv.cc
pkgs.libusb1 pkgs.libusb1
@ -45,6 +46,7 @@
LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath buildInputs}"; LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath buildInputs}";
shellHook = '' shellHook = ''
export PATH="${lib.makeBinPath [pkgs.rust-analyzer]}:$PATH"
export PROJECT_DIR="$(pwd)"; export PROJECT_DIR="$(pwd)";
# custom bashrc stuff # custom bashrc stuff
export PS1_PREFIX="(esp-rs)" export PS1_PREFIX="(esp-rs)"

View file

@ -1,4 +1,4 @@
[toolchain] [toolchain]
channel = "stable" channel = "stable"
components = ["rust-src", "clippy", "rust-analyzer"] components = ["rust-src", "rustc-dev"]
targets = ["riscv32imc-unknown-none-elf"] targets = ["riscv32imc-unknown-none-elf"]

View file

@ -1,5 +1,6 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(trait_alias)]
extern crate alloc; extern crate alloc;
@ -22,7 +23,10 @@ use {
alignment::HorizontalAlignment as TextHorizontalAlignment, alignment::HorizontalAlignment as TextHorizontalAlignment,
}, },
embedded_layout::{ embedded_layout::{
layout::linear::LinearLayout, view_group::ViewGroup,
layout::{
linear::LinearLayout,
},
prelude::*, prelude::*,
}, },
embedded_hal_bus::spi::{ExclusiveDevice, NoDelay}, embedded_hal_bus::spi::{ExclusiveDevice, NoDelay},
@ -53,34 +57,38 @@ const PASSWORD: &str = env!("WIFI_PASSWORD");
esp_bootloader_esp_idf::esp_app_desc!(); esp_bootloader_esp_idf::esp_app_desc!();
/*
* Make it easier to follow the TFT related types. .-.
*/
type TFTSpiDevice<'spi> = ExclusiveDevice<Spi<'spi, Blocking>, Output<'spi>, NoDelay>; type TFTSpiDevice<'spi> = ExclusiveDevice<Spi<'spi, Blocking>, Output<'spi>, NoDelay>;
type TFTSpiInterface<'spi> = type TFTSpiInterface<'spi> =
SPIInterface<ExclusiveDevice<Spi<'spi, Blocking>, Output<'spi>, NoDelay>, Output<'spi>>; SPIInterface<ExclusiveDevice<Spi<'spi, Blocking>, Output<'spi>, NoDelay>, Output<'spi>>;
type Ili<'spi> = Ili9341<TFTSpiInterface<'spi>, Output<'spi>>; type Ili<'spi> = Ili9341<TFTSpiInterface<'spi>, Output<'spi>>;
pub struct TFT<'spi> { /*
display: Ili<'spi>, Provide an type alias DColour (display colour) since Bgr565 is the actual colour ordering for my display.
} */
impl<'spi> TFT<'spi> { type DColor = Bgr565;
fn draw_target(&mut self) -> DrawFlipper<'_, 'spi> {
DrawFlipper {
display: &mut self.display,
}
}
}
fn candyflip(color: Bgr565) -> Rgb565 { /*
unsafe { * Provide an alias for something that can be put into a ViewGroup for the use of embedded-layout.
core::mem::transmute::<Bgr565, Rgb565>(color) */
}
} trait Drawy = Drawable<Color = DColor> + ViewGroup;
fn flipcandy(color: Rgb565) -> Bgr565 {
unsafe { /*
core::mem::transmute::<Rgb565, Bgr565>(color) * So, my specific ILI9341-derived display doesn't JUST have wrong colour channels (Rgb vs Gbr like
} * in the actual driver implementation).
} *
* It also is *mirrored* horizontally. The underlying library and the driver implementation both don't
* expose anything reasonable to handle this.
*
* The driver exposes "Orientation" of Horizontal, Portrait, HorizontalFlipped and PortraitFlipped.
* This is inadequate for the problems in the implementation I have.
*/
struct DrawFlipper<'a, 'spi> { struct DrawFlipper<'a, 'spi> {
display: &'a mut Ili<'spi>, display: &'a mut Ili<'spi>,
@ -88,7 +96,7 @@ struct DrawFlipper<'a, 'spi> {
impl<'a, 'spi> DrawTarget for DrawFlipper<'a, 'spi> { impl<'a, 'spi> DrawTarget for DrawFlipper<'a, 'spi> {
type Error = <Ili<'spi> as DrawTarget>::Error; type Error = <Ili<'spi> as DrawTarget>::Error;
type Color = Bgr565;//<Ili<'spi> as DrawTarget>::Color; type Color = DColor;//<Ili<'spi> as DrawTarget>::Color;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error> fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where where
I: IntoIterator<Item = Pixel<Self::Color>>, I: IntoIterator<Item = Pixel<Self::Color>>,
@ -131,20 +139,40 @@ impl<'a> Dimensions for DrawFlipper<'a, '_> {
} }
} }
#[derive(Clone,Copy)] /*
enum StyleVariant { * Container for implementing the TFT type
Header, */
Regular
pub struct TFT<'spi> {
display: Ili<'spi>,
} }
impl<'spi> TFT<'spi> { impl<'spi> TFT<'spi> {
const H_BG: Bgr565 = Bgr565::CSS_DARK_SLATE_GRAY; fn draw_target(&mut self) -> DrawFlipper<'_, 'spi> {
const H_S: Bgr565 = Bgr565::CYAN; DrawFlipper {
const H_FG: Bgr565 = Bgr565::WHITE; display: &mut self.display,
}
}
}
const R_BG: Bgr565 = Bgr565::CSS_DARK_SLATE_GRAY; /*
const R_S: Bgr565 = Bgr565::BLUE; * Provide a decent-ish way of replacing the colour channel ordering.
const R_FG: Bgr565 = Bgr565::WHITE; *
* Ask not questions about the drug terminology!
*/
fn candyflip(color: DColor) -> Rgb565 {
unsafe {
core::mem::transmute::<DColor, Rgb565>(color)
}
}
/*
* Implement rendering helpers for the weird display
*/
impl<'spi> TFT<'spi> {
const ROOT_BG: DColor = DColor::BLACK;
pub fn new( pub fn new(
spi2: SPI2<'spi>, spi2: SPI2<'spi>,
@ -185,43 +213,61 @@ impl<'spi> TFT<'spi> {
.with_mode(Mode::_0) .with_mode(Mode::_0)
} }
pub fn clear(&mut self, color: Bgr565) { pub fn clear(&mut self, color: DColor) {
self.display.clear(candyflip(color)).unwrap(); let _ = self.display.clear(candyflip(color));
}
pub fn clear_root(&mut self) {
let _ = self.display.clear(candyflip(Self::ROOT_BG));
} }
pub fn part_clear(&mut self, x: i32, y: i32, w: u32, h: u32) { pub fn part_clear(&mut self, x: i32, y: i32, w: u32, h: u32) {
Rectangle::new(Point::new(x, y), Size::new(w, h)) Rectangle::new(Point::new(x, y), Size::new(w, h))
.into_styled(PrimitiveStyle::with_fill(Bgr565::BLACK)) .into_styled(PrimitiveStyle::with_fill(DColor::BLACK))
.draw(&mut self.draw_target()) .draw(&mut self.draw_target())
.unwrap(); .unwrap();
} }
pub fn containing_recty(&mut self, x: i32, y: i32, w: u32, h: u32, style: StyleVariant) -> Styled<Rectangle, PrimitiveStyle<Bgr565>> { pub fn contained_text<'a>(text: &'a str, margin: u32) -> impl Drawy + 'a {
let bg = match style {
StyleVariant::Header => Self::H_BG,
StyleVariant::Regular => Self::R_BG,
};
let s = match style {
StyleVariant::Header => Self::H_S,
StyleVariant::Regular => Self::R_S,
};
let style = PrimitiveStyleBuilder::new() let style = PrimitiveStyleBuilder::new()
.fill_color(bg) .stroke_color(DColor::RED)
.stroke_color(s) .stroke_width(3)
.stroke_width(1) .fill_color(DColor::CSS_DARK_SLATE_GRAY)
.build(); .build();
Rectangle::new(Point::new(x, y), Size::new(w, h)) let text_style = MonoTextStyle::new(&FONT_6X10, DColor::WHITE);
.clone() let text = Text::new(text, Point::new_equal((margin/2) as i32), text_style);
let margin_size = Size::new_equal(margin);
let bound = text.bounding_box();
let size = bound.size + margin_size;
let height_offset = Point::new(0, -((margin/2) as i32));
Chain::new(
text
).append(
Rectangle::new(height_offset, size)
.into_styled(style) .into_styled(style)
)
} }
pub fn container(&mut self, margin: i32, y: i32, h: u32, style: StyleVariant) -> Styled<Rectangle, PrimitiveStyle<Bgr565>> { pub fn fullscreen_alert(&mut self, text: &str, clear: bool) {
let width = self.display.bounding_box().size.width; if clear {
self.containing_recty(margin, y, width - margin as u32, h, style) let _ = self.clear_root();
}
let display_area = self.display.bounding_box();
LinearLayout::vertical(
Chain::new(
LinearLayout::horizontal(
Self::contained_text("Initialized controller!", 16)
)
)
).with_alignment(horizontal::Center)
.arrange()
.align_to(&display_area, horizontal::Center, vertical::Center)
.draw(&mut self.draw_target())
.unwrap();
} }
pub fn println(&mut self, text: &str, x: i32, y: i32) { pub fn println(&mut self, text: &str, x: i32, y: i32) {
let style = MonoTextStyle::new(&FONT_6X10, Bgr565::WHITE); let style = MonoTextStyle::new(&FONT_6X10, DColor::WHITE);
Text::with_alignment(text, Point::new(x, y), style, Alignment::Center) Text::with_alignment(text, Point::new(x, y), style, Alignment::Center)
.draw(&mut self.draw_target()) .draw(&mut self.draw_target())
.unwrap(); .unwrap();
@ -276,49 +322,53 @@ async fn net_task(mut runner: Runner<'static, WifiDevice<'static>>) {
}*/ }*/
fn init_heap() {
const HEAP_SIZE: usize = 64 * 1024;
static mut HEAP: core::mem::MaybeUninit<[u8; HEAP_SIZE]> = core::mem::MaybeUninit::uninit();
unsafe {
esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new(
addr_of_mut!(HEAP) as *mut u8,
HEAP_SIZE,
esp_alloc::MemoryCapability::Internal.into(),
));
}
}
struct Controller<'tft> { struct Controller<'tft> {
pub tft: TFT<'tft>, pub display: TFT<'tft>,
} }
impl Controller<'_> { impl Controller<'_> {
async fn init(peripherals: Peripherals) -> Self { async fn init(peripherals: Peripherals) -> Self {
let tft = Self::init_screen(peripherals).await; let mut display = Self::init_screen(peripherals).await;
let controller = Self { let mut controller = Self {
tft, display,
}; };
controller.display.fullscreen_alert("Controller initialized!", true);
controller controller
} }
async fn init_screen<'tft>(peripherals: Peripherals) -> TFT<'tft> { async fn init_screen<'tft>(peripherals: Peripherals) -> TFT<'tft> {
let dc = peripherals.GPIO9; // Refer to https://www.espboards.dev/esp32/esp32-c3-super-mini/#esp32-c3-super-mini-pinout
let mosi = peripherals.GPIO6; let rst = peripherals.GPIO0;
let sclk = peripherals.GPIO4; let sclk = peripherals.GPIO4;
let miso = peripherals.GPIO5; let miso = peripherals.GPIO5;
let mosi = peripherals.GPIO6;
let cs = peripherals.GPIO7; let cs = peripherals.GPIO7;
let rst = peripherals.GPIO0; let dc = peripherals.GPIO9;
let mut tft = TFT::new(peripherals.SPI2, sclk, miso, mosi, cs, rst, dc); let mut tft = TFT::new(peripherals.SPI2, sclk, miso, mosi, cs, rst, dc);
let _ = tft.draw_target().clear(Bgr565::BLACK);
tft tft
} }
} }
/*struct WifiController {
timer: TimerGroup,
}
impl WifiController {
async fn init_wifi(peripherals: Peripherals) -> Self {
let timer = TimerGroup::new(peripherals.TIMG1);
let rng = Rng::new();
Self {
timer
}
}
}*/
#[esp_rtos::main] #[esp_rtos::main]
async fn main(spawner: Spawner) { async fn main(spawner: Spawner) {
init_heap(); // Note that for alloc to work, `./.cargo/config.toml` should contain alloc under build-std.
esp_alloc::heap_allocator!(size: 64*1024);
#[cfg(feature = "log")] #[cfg(feature = "log")]
{ {
// The default log level can be specified here. // The default log level can be specified here.
@ -347,28 +397,6 @@ async fn main(spawner: Spawner) {
let mut controller = Controller::init(peripherals).await; let mut controller = Controller::init(peripherals).await;
let display_area = controller.tft.display.bounding_box();
let text_style = MonoTextStyle::new(&FONT_6X10, Bgr565::WHITE);
let margin = 2;
let display_width = display_area.size.width;
let display_height = display_area.size.height;
LinearLayout::vertical(
Chain::new(
LinearLayout::horizontal(
Chain::new(
Text::new("Initializing~!", Point::new(margin, margin), text_style)
).append(
controller.tft.container(margin, -10, 20, StyleVariant::Header)
)
)
)
).with_alignment(horizontal::Center)
.arrange()
.align_to(&display_area, horizontal::Center, vertical::Center)
.draw(&mut controller.tft.draw_target())
.unwrap();
loop { loop {
// your business logic // your business logic
} }