Initial Commit

This commit is contained in:
CanadianBaconBoi 2025-05-09 16:58:53 +02:00
commit 66fdd992d5
24 changed files with 625 additions and 0 deletions

16
.cargo/config.toml Normal file
View File

@ -0,0 +1,16 @@
[build]
target = "xtensa-esp32-espidf"
[target.xtensa-esp32-espidf]
linker = "ldproxy"
runner = "espflash flash --monitor --baud=921600"
rustflags = [ "--cfg", "espidf_time64"]
[unstable]
build-std = ["std", "panic_abort"]
[env]
MCU="esp32"
# Note: this variable is not used by the pio builder (`cargo build --features pio`)
ESP_IDF_VERSION = "v5.3.2"

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/.vscode
/.embuild
/target
/Cargo.lock

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/docker-watcher-esp.iml" filepath="$PROJECT_DIR$/.idea/docker-watcher-esp.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

35
Cargo.toml Normal file
View File

@ -0,0 +1,35 @@
[package]
name = "docker-watcher-esp"
version = "0.1.0"
authors = ["CanadianBaconBoi <bc.bacon.bits@gmail.com>"]
edition = "2021"
resolver = "2"
rust-version = "1.77"
[[bin]]
name = "docker-watcher-esp"
harness = false # do not use the built in cargo test harness -> resolve rust-analyzer errors
[profile.release]
opt-level = "s"
[profile.dev]
debug = true # Symbols are nice and they don't increase the size on Flash
opt-level = "z"
[features]
default = []
experimental = ["esp-idf-svc/experimental"]
[dependencies]
log = "0.4"
esp-idf-svc = { version = "0.51", features = ["critical-section", "embassy-time-driver", "embassy-sync"] }
mipidsi = "0.9.0"
anyhow = "1.0.98"
embedded-graphics = "0.8.1"
embedded-hal = "1.0.0"
[build-dependencies]
embuild = "0.33"

3
build.rs Normal file
View File

@ -0,0 +1,3 @@
fn main() {
embuild::espidf::sysenv::output();
}

34
diagram.json Normal file
View File

@ -0,0 +1,34 @@
{
"version": 1,
"editor": "wokwi",
"author": "CanadianBaconBoi <bc.bacon.bits@gmail.com>",
"parts": [
{
"type": "board-esp32-devkit-c-v4",
"id": "esp",
"top": 0.59,
"left": 0.67,
"attrs": {
"flashSize": "16"
}
}
],
"connections": [
[
"esp:TX",
"$serialMonitor:RX",
"",
[]
],
[
"esp:RX",
"$serialMonitor:TX",
"",
[]
]
],
"serialMonitor": {
"display": "terminal",
"convertEol": true
}
}

2
rust-toolchain.toml Normal file
View File

@ -0,0 +1,2 @@
[toolchain]
channel = "esp"

10
sdkconfig.defaults Normal file
View File

@ -0,0 +1,10 @@
# 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
# Use this to set FreeRTOS kernel tick frequency to 1000 Hz (100 Hz by default).
# This allows to use 1 ms granularity for thread sleeps (10 ms by default).
#CONFIG_FREERTOS_HZ=1000
# Workaround for https://github.com/espressif/esp-idf/issues/7631
#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n
#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n

3
src/display.rs Normal file
View File

@ -0,0 +1,3 @@
pub mod theme;
pub mod screens;
pub mod tools;

6
src/display/screens.rs Normal file
View File

@ -0,0 +1,6 @@
pub mod base;
pub mod home;
pub mod success;
pub mod error;
pub mod macros;
mod prelude;

View File

@ -0,0 +1,77 @@
use embedded_graphics::geometry::{Point, Size};
use embedded_graphics::pixelcolor::Rgb565;
use embedded_graphics::primitives::CornerRadii;
use embedded_hal::digital::OutputPin;
use mipidsi::interface::InterfacePixelFormat;
use crate::display::tools::DrawTools;
use crate::{W, H};
use crate::display::theme::MACCHIATO;
pub trait DisplayScreen<DI, M, RST>
where
DI: mipidsi::interface::Interface,
M: mipidsi::models::Model<ColorFormat = Rgb565>,
M::ColorFormat: InterfacePixelFormat<DI::Word>,
RST: OutputPin,
{
fn show(index: &mut usize, frame_counter: &mut usize, dirty: &mut bool, display: &mut mipidsi::Display<DI, M, RST>) -> anyhow::Result<(), DI::Error>;
fn input(button: ButtonPosition) -> anyhow::Result<(), DI::Error> {
Ok(())
}
}
pub struct StandardBase;
impl<DI, M, RST> DisplayScreen<DI, M, RST> for StandardBase
where
DI: mipidsi::interface::Interface,
M: mipidsi::models::Model<ColorFormat = Rgb565>,
M::ColorFormat: InterfacePixelFormat<DI::Word>,
RST: OutputPin,
{
fn show(_index: &mut usize, _frame_counter: &mut usize, dirty: &mut bool, display: &mut mipidsi::Display<DI, M, RST>) -> anyhow::Result<(), DI::Error> {
if *dirty {
display.draw_rectangle_bounds(Point::new(0, 0), Point::new(W, H), MACCHIATO.crust)?;
display.draw_rounded_rectangle_bounds(Point::new(10, 10), Point::new(W -10, H -10), MACCHIATO.mantle, CornerRadii::new(Size::new(10, 10)))?;
}
Ok(())
}
}
pub struct ErrorBase;
impl<DI, M, RST> DisplayScreen<DI, M, RST> for ErrorBase
where
DI: mipidsi::interface::Interface,
M: mipidsi::models::Model<ColorFormat = Rgb565>,
M::ColorFormat: InterfacePixelFormat<DI::Word>,
RST: OutputPin,
{
fn show(_index: &mut usize, _frame_counter: &mut usize, dirty: &mut bool, display: &mut mipidsi::Display<DI, M, RST>) -> anyhow::Result<(), DI::Error> {
if *dirty {
display.draw_rectangle_bounds(Point::new(0, 0), Point::new(W, H), MACCHIATO.maroon)?;
display.draw_rounded_rectangle_bounds(Point::new(10, 10), Point::new(W -10, H -10), MACCHIATO.mantle, CornerRadii::new(Size::new(10, 10)))?;
}
Ok(())
}
}
pub struct SuccessBase;
impl<DI, M, RST> DisplayScreen<DI, M, RST> for SuccessBase
where
DI: mipidsi::interface::Interface,
M: mipidsi::models::Model<ColorFormat = Rgb565>,
M::ColorFormat: InterfacePixelFormat<DI::Word>,
RST: OutputPin,
{
fn show(_index: &mut usize, _frame_counter: &mut usize, dirty: &mut bool, display: &mut mipidsi::Display<DI, M, RST>) -> anyhow::Result<(), DI::Error> {
if *dirty {
display.draw_rectangle_bounds(Point::new(0, 0), Point::new(W, H), MACCHIATO.green)?;
display.draw_rounded_rectangle_bounds(Point::new(10, 10), Point::new(W -10, H -10), MACCHIATO.mantle, CornerRadii::new(Size::new(10, 10)))?;
}
Ok(())
}
}
pub enum ButtonPosition {
Up,
Down
}

View File

@ -0,0 +1,27 @@
use crate::{error_screen, FRAMERATE, H, W};
use crate::display::screens::base::{ButtonPosition, ErrorBase};
use crate::DisplayScreen;
use mipidsi::models::Model;
use embedded_graphics::pixelcolor::Rgb565;
use embedded_graphics::prelude::Point;
use mipidsi::interface::InterfacePixelFormat;
use embedded_hal::digital::OutputPin;
use crate::display::tools::DrawTools;
error_screen!(ErrorScreen, |index: &mut usize, frame_counter: &mut usize, dirty: &mut bool, display: &mut mipidsi::Display<_, _, _>|->anyhow::Result<(), _> {
if *dirty {
display.draw_text_centered(Point::new(W/2, H/2), "Error!")?;
*dirty = false;
} else if *frame_counter % FRAMERATE*4 == 0 {
#[cfg(debug_assertions)]
println!("Switching to screen {}", *index + 1);
*index += 1;
*frame_counter = 0;
*dirty = true;
}
return Ok(())
}, |button: ButtonPosition|->anyhow::Result<(), _> {
Ok(())
}
);

View File

@ -0,0 +1,26 @@
use embedded_graphics::geometry::Point;
use crate::{standard_screen, FRAMERATE, H, W};
use crate::display::screens::base::{ButtonPosition, StandardBase};
use crate::DisplayScreen;
use mipidsi::models::Model;
use embedded_graphics::pixelcolor::Rgb565;
use mipidsi::interface::InterfacePixelFormat;
use embedded_hal::digital::OutputPin;
use crate::display::tools::DrawTools;
standard_screen!(StandardScreen, |index: &mut usize, frame_counter: &mut usize, dirty: &mut bool, display: &mut mipidsi::Display<_, _, _>|->anyhow::Result<(), _> {
if *dirty {
display.draw_text_centered(Point::new(W/2, H/2), "Standard!")?;
*dirty = false;
} else if *frame_counter % FRAMERATE*4 == 0 {
#[cfg(debug_assertions)]
println!("Switching to screen {}", *index + 1);
*index += 1;
*frame_counter = 0;
*dirty = true;
}
return Ok(())
}, |button: ButtonPosition|->anyhow::Result<(), _> {
Ok(())
});

View File

@ -0,0 +1,71 @@
#[macro_export]
macro_rules! standard_screen {
( $name: ident, $show: expr, $input: expr ) => {
pub struct $name;
impl<DI, M, RST> DisplayScreen<DI, M, RST> for $name
where
DI: mipidsi::interface::Interface,
M: Model<ColorFormat = Rgb565>,
M::ColorFormat: InterfacePixelFormat<DI::Word>,
RST: OutputPin {
#[inline]
fn show(idex: &mut usize, frame_counter: &mut usize, dirty: &mut bool, display: &mut mipidsi::Display<DI, M, RST>) -> anyhow::Result<(), DI::Error> {
StandardBase::show(idex, frame_counter, dirty, display)?;
$show(idex, frame_counter, dirty, display)
}
#[inline]
fn input(button: ButtonPosition) {
$input(button);
}
}
};
}
#[macro_export]
macro_rules! error_screen {
( $name: ident, $show: expr, $input: expr ) => {
pub struct $name;
impl<DI, M, RST> DisplayScreen<DI, M, RST> for $name
where
DI: mipidsi::interface::Interface,
M: Model<ColorFormat = Rgb565>,
M::ColorFormat: InterfacePixelFormat<DI::Word>,
RST: OutputPin {
#[inline]
fn show(idex: &mut usize, frame_counter: &mut usize, dirty: &mut bool, display: &mut mipidsi::Display<DI, M, RST>) -> anyhow::Result<(), DI::Error> {
ErrorBase::show(idex, frame_counter, dirty, display)?;
$show(idex, frame_counter, dirty, display)
}
#[inline]
fn input(button: ButtonPosition) {
$input(button);
}
}
};
}
#[macro_export]
macro_rules! success_screen {
( $name: ident, $show: expr, $input: expr ) => {
pub struct $name;
impl<DI, M, RST> DisplayScreen<DI, M, RST> for $name
where
DI: mipidsi::interface::Interface,
M: Model<ColorFormat = Rgb565>,
M::ColorFormat: InterfacePixelFormat<DI::Word>,
RST: OutputPin {
#[inline]
fn show(idex: &mut usize, frame_counter: &mut usize, dirty: &mut bool, display: &mut mipidsi::Display<DI, M, RST>) -> anyhow::Result<(), DI::Error> {
SuccessBase::show(idex, frame_counter, dirty, display)?;
$show(idex, frame_counter, dirty, display)
}
#[inline]
fn input(button: ButtonPosition) {
$input(button);
}
}
};
}

View File

@ -0,0 +1,9 @@
use embedded_graphics::geometry::Point;
use crate::{success_screen, FRAMERATE, H, W};
use crate::display::screens::base::{ButtonPosition, SuccessBase};
use crate::DisplayScreen;
use mipidsi::models::Model;
use embedded_graphics::pixelcolor::Rgb565;
use mipidsi::interface::InterfacePixelFormat;
use embedded_hal::digital::OutputPin;
use crate::display::tools::DrawTools;

View File

@ -0,0 +1,18 @@
use crate::display::screens::prelude;
success_screen!(SuccessScreen, |index: &mut usize, frame_counter: &mut usize, dirty: &mut bool, display: &mut mipidsi::Display<_, _, _>|->anyhow::Result<(), _> {
if *dirty {
display.draw_text_centered(Point::new(W/2, H/2), "Success!")?;
*dirty = false;
} else if *frame_counter % FRAMERATE*4 == 0 {
#[cfg(debug_assertions)]
println!("Switching to screen {}", *index + 1);
*index += 1;
*frame_counter = 0;
*dirty = true;
}
return Ok(())
}, |button: ButtonPosition|->anyhow::Result<(), _> {
Ok(())
});

63
src/display/theme.rs Normal file
View File

@ -0,0 +1,63 @@
use embedded_graphics::pixelcolor::{Rgb565};
pub struct Theme {
pub rosewater: Rgb565,
pub flamingo: Rgb565,
pub pink: Rgb565,
pub mauve: Rgb565,
pub red: Rgb565,
pub maroon: Rgb565,
pub peach: Rgb565,
pub yellow: Rgb565,
pub green: Rgb565,
pub teal: Rgb565,
pub sky: Rgb565,
pub sapphire: Rgb565,
pub blue: Rgb565,
pub lavender: Rgb565,
pub text: Rgb565,
pub subtext_1: Rgb565,
pub subtext_0: Rgb565,
pub overlay_2: Rgb565,
pub overlay_1: Rgb565,
pub overlay_0: Rgb565,
pub surface_2: Rgb565,
pub surface_1: Rgb565,
pub surface_0: Rgb565,
pub base: Rgb565,
pub mantle: Rgb565,
pub crust: Rgb565,
}
pub const MACCHIATO: Theme = Theme {
rosewater: to_rgb565([244, 219, 214]),
flamingo: to_rgb565([240, 198, 198]),
pink: to_rgb565([245, 189, 230]),
mauve: to_rgb565([198, 160, 246]),
red: to_rgb565([237, 135, 150]),
maroon: to_rgb565([238, 153, 160]),
peach: to_rgb565([245, 169, 127]),
yellow: to_rgb565([238, 212, 159]),
green: to_rgb565([166, 218, 149]),
teal: to_rgb565([139, 213, 202]),
sky: to_rgb565([145, 215, 227]),
sapphire: to_rgb565([125, 196, 228]),
blue: to_rgb565([138, 173, 244]),
lavender: to_rgb565([183, 189, 248]),
text: to_rgb565([202, 211, 245]),
subtext_1: to_rgb565([184, 192, 224]),
subtext_0: to_rgb565([165, 173, 203]),
overlay_2: to_rgb565([147, 154, 183]),
overlay_1: to_rgb565([128, 135, 162]),
overlay_0: to_rgb565([110, 115, 141]),
surface_2: to_rgb565([91, 96, 120]),
surface_1: to_rgb565([73, 77, 100]),
surface_0: to_rgb565([54, 58, 79]),
base: to_rgb565([36, 39, 58]),
mantle: to_rgb565([30, 32, 48]),
crust: to_rgb565([24, 25, 38]),
};
const fn to_rgb565(rgb: [u8; 3]) -> Rgb565 {
Rgb565::new(rgb[0] >> 3, rgb[1] >> 2, rgb[2] >> 3)
}

72
src/display/tools.rs Normal file
View File

@ -0,0 +1,72 @@
use embedded_graphics::draw_target::DrawTarget;
use embedded_graphics::Drawable;
use embedded_graphics::geometry::{Point, Size};
use embedded_graphics::mono_font::MonoTextStyle;
use embedded_graphics::pixelcolor::Rgb565;
use embedded_graphics::primitives::{CornerRadii, PrimitiveStyle, Rectangle, RoundedRectangle, StyledDrawable};
use embedded_graphics::text::Text;
use embedded_hal::digital::OutputPin;
use mipidsi::Display;
use mipidsi::interface::InterfacePixelFormat;
use crate::display::theme::MACCHIATO;
pub trait DrawTools<DI, M, RST>
where
DI: mipidsi::interface::Interface,
M: mipidsi::models::Model<ColorFormat = Rgb565>,
M::ColorFormat: InterfacePixelFormat<DI::Word>,
RST: OutputPin,
{
fn draw_rectangle_bounds(&mut self, top_left: Point, bottom_right: Point, color: M::ColorFormat) -> anyhow::Result<(), DI::Error>;
fn draw_rectangle_top_left(&mut self, top_left: Point, size: Size, color: M::ColorFormat) -> anyhow::Result<(), DI::Error>;
fn draw_rectangle_center(&mut self, center: Point, size: Size, color: M::ColorFormat) -> anyhow::Result<(), DI::Error>;
fn draw_rounded_rectangle_bounds(&mut self, top_left: Point, bottom_right: Point, color: M::ColorFormat, corner_radii: CornerRadii) -> anyhow::Result<(), DI::Error>;
fn draw_rounded_rectangle_top_left(&mut self, top_left: Point, size: Size, color: M::ColorFormat, corner_radii: CornerRadii) -> anyhow::Result<(), DI::Error>;
fn draw_rounded_rectangle_center(&mut self, center: Point, size: Size, color: M::ColorFormat, corner_radii: CornerRadii) -> anyhow::Result<(), DI::Error>;
fn draw_text_centered(&mut self, center: Point, text: &str) -> anyhow::Result<(), DI::Error>;
}
impl<DI, M, RST> DrawTools<DI, M, RST> for Display<DI, M, RST>
where
DI: mipidsi::interface::Interface,
M: mipidsi::models::Model<ColorFormat = Rgb565>,
M::ColorFormat: InterfacePixelFormat<DI::Word>,
RST: OutputPin,
{
fn draw_rectangle_bounds(&mut self, top_left: Point, bottom_right: Point, color: M::ColorFormat) -> anyhow::Result<(), DI::Error> {
self.draw_rectangle_top_left(top_left, Size::new((bottom_right.x - top_left.x) as u32, (bottom_right.y - top_left.y) as u32), color)
}
fn draw_rectangle_top_left(&mut self, top_left: Point, size: Size, color: M::ColorFormat) -> anyhow::Result<(), DI::Error> {
self.fill_solid(&Rectangle::new(top_left, size), color)
}
fn draw_rectangle_center(&mut self, center: Point, size: Size, color: M::ColorFormat) -> anyhow::Result<(), DI::Error> {
self.draw_rectangle_top_left(center - Point::new((size.width / 2) as i32, (size.height / 2) as i32), size, color)
}
fn draw_rounded_rectangle_bounds(&mut self, top_left: Point, bottom_right: Point, color: M::ColorFormat, corner_radii: CornerRadii) -> anyhow::Result<(), DI::Error> {
self.draw_rounded_rectangle_top_left(top_left, Size::new((bottom_right.x - top_left.x) as u32, (bottom_right.y - top_left.y) as u32), color, corner_radii)
}
fn draw_rounded_rectangle_top_left(&mut self, top_left: Point, size: Size, color: M::ColorFormat, corner_radii: CornerRadii) -> anyhow::Result<(), DI::Error> {
let rectangle = Rectangle::new(top_left, size);
let rounded_rectangle = RoundedRectangle::new(rectangle, corner_radii);
rounded_rectangle.draw_styled(&PrimitiveStyle::with_fill(color), self)
}
fn draw_rounded_rectangle_center(&mut self, center: Point, size: Size, color: M::ColorFormat, corner_radii: CornerRadii) -> anyhow::Result<(), DI::Error> {
self.draw_rounded_rectangle_top_left(center - Point::new((size.width / 2) as i32, (size.height / 2) as i32), size, color, corner_radii)
}
fn draw_text_centered(&mut self, center: Point, text: &str) -> anyhow::Result<(), DI::Error> {
Text::new(text, center - Point::new((text.len()*5) as i32, -10), TEXT_STYLE).draw(self)?;
Ok(())
}
}
static TEXT_STYLE: MonoTextStyle<Rgb565> = MonoTextStyle::new(&embedded_graphics::mono_font::ascii::FONT_10X20, MACCHIATO.text);

BIN
src/ferris.raw Normal file

Binary file not shown.

111
src/main.rs Normal file
View File

@ -0,0 +1,111 @@
mod display;
use std::{
thread,
time::Duration
};
use esp_idf_svc::hal::{
delay::Ets,
gpio::{Gpio16, Gpio18, Gpio19, Gpio20, Gpio23, Gpio4, Gpio5, PinDriver},
peripherals::Peripherals,
spi::{config, SpiDeviceDriver, SpiDriverConfig},
spi::config::MODE_3,
units::*
};
use mipidsi::{Builder, interface::SpiInterface, models::{ST7789}, options::{ColorInversion, Orientation}};
use mipidsi::options::{ColorOrder, Rotation};
use crate::display::screens::base::DisplayScreen;
use crate::display::screens::error::ErrorScreen;
use crate::display::screens::home::StandardScreen;
use crate::display::screens::success::SuccessScreen;
// Display
const H: i32 = 135;
const X_OFF: i32 = 52;
const W: i32 = 240;
const Y_OFF: i32 = 40;
const FRAMERATE: usize = 30;
static mut BUFFER: [u8; (H * W) as usize] = [0u8; (H * W) as usize];
fn main() -> anyhow::Result<()> {
let peripherals: Peripherals = Peripherals::take()?;
let pin_rst: Gpio23 = peripherals.pins.gpio23;
let pin_dc: Gpio16 = peripherals.pins.gpio16;
let pin_bl: Gpio4 = peripherals.pins.gpio4;
let pin_sclk: Gpio18 = peripherals.pins.gpio18;
let pin_mosi: Gpio19 = peripherals.pins.gpio19;
let pin_miso: Gpio20 = peripherals.pins.gpio20;
let pin_cs: Gpio5 = peripherals.pins.gpio5;
let screens: Vec<fn(&mut usize, &mut usize, &mut bool, &mut mipidsi::Display<_, _, _>) -> anyhow::Result<(), _>> = vec!(
StandardScreen::show,
ErrorScreen::show,
SuccessScreen::show,
);
unsafe {
esp_idf_svc::sys::link_patches();
esp_idf_svc::log::EspLogger::initialize_default();
log::info!("Hello, world!");
let spi = peripherals.spi2;
let mut backlight = PinDriver::output(pin_bl)?;
let mut delay = Ets;
// configuring the spi interface, note that in order for the ST7789 to work, the data_mode needs to be set to MODE_3
let config = config::Config::new()
.baudrate(MegaHertz(26).into())
.data_mode(MODE_3);
let device = SpiDeviceDriver::new_single(
spi,
pin_sclk,
pin_mosi,
Some(pin_miso),
Some(pin_cs),
&SpiDriverConfig::new(),
&config,
)?;
// display interface abstraction from SPI and DC
let di = SpiInterface::new(device, PinDriver::output(pin_dc)?, &mut *&raw mut BUFFER);
// create driver
let mut display = Builder::new(ST7789, di)
.display_size(H as u16, W as u16)
.orientation(Orientation::new().rotate(Rotation::Deg270))
.reset_pin(PinDriver::output(pin_rst)?)
.invert_colors(ColorInversion::Inverted)
.color_order(ColorOrder::Rgb)
.display_offset(X_OFF as u16, Y_OFF as u16)
.init(&mut delay)
.unwrap();
// turn on the backlight
backlight.set_high()?;
let mut screen_index = 0;
let mut frame_counter = 0;
let mut dirty = true;
loop {
screens[screen_index](&mut screen_index, &mut frame_counter, &mut dirty, &mut display).unwrap();
if screen_index == screens.len() {
screen_index = 0;
} else if screen_index >= screens.len() + 1 {
screen_index = screens.len() - 1;
}
#[cfg(debug_assertions)]
println!("Sleeping for {}", 1000/FRAMERATE);
thread::sleep(Duration::from_millis((1000 / FRAMERATE) as u64));
frame_counter += 1;
}
}
}

5
wokwi.toml Normal file
View File

@ -0,0 +1,5 @@
[wokwi]
version = 1
gdbServerPort = 3333
elf = "target/xtensa-esp32-espidf/debug/docker-watcher-esp"
firmware = "target/xtensa-esp32-espidf/debug/docker-watcher-esp"