Interrupts

This commit is contained in:
2025-12-02 10:09:43 +01:00
parent ea9852d5bc
commit 71c1dc4fd9
4 changed files with 170 additions and 98 deletions

View File

@ -19,9 +19,11 @@ use buoyant::primitives::geometry::Rectangle;
use buoyant::view::AsDrawable;
use buoyant::view::Image;
use buoyant::view::ViewExt;
use core::cell::RefCell;
use core::default::Default;
use core::iter::Iterator;
use core::ops::Sub;
use critical_section::Mutex;
use defmt::info;
use embedded_graphics::Drawable;
use embedded_graphics::framebuffer::Framebuffer;
@ -40,16 +42,15 @@ use esp_hal::clock::CpuClock;
use esp_hal::delay::Delay;
use esp_hal::gpio::Input;
use esp_hal::gpio::InputConfig;
use esp_hal::gpio::Io;
use esp_hal::gpio::Level;
use esp_hal::gpio::Output;
use esp_hal::gpio::OutputConfig;
use esp_hal::gpio::Pull;
use esp_hal::interrupt;
use esp_hal::riscv::asm::delay;
use esp_hal::handler;
use esp_hal::peripherals;
use esp_hal::time::Rate;
use heapless::deque;
use heapless::format;
use heapless::history_buf;
use mipidsi::interface::SpiInterface;
use mipidsi::models::ST7789;
@ -70,6 +71,7 @@ use crate::images::StaticImage;
use crate::sampler::History;
use crate::sampler::Sample;
use crate::sampler::Sampler;
use crate::views::detail;
extern crate alloc;
@ -77,15 +79,22 @@ extern crate alloc;
// For more information see: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description>
esp_bootloader_esp_idf::esp_app_desc!();
static INPUT_BUTTON: Mutex<RefCell<Option<Input>>> = Mutex::new(RefCell::new(None));
static BUTTON_PRESSED: Mutex<RefCell<bool>> = Mutex::new(RefCell::new(false));
#[main]
fn main() -> ! {
// generator version: 1.0.1
info!("Starting up.");
images::prepare_images();
info!("Prepared images.");
esp_alloc::heap_allocator!(size: 32 * 1024);
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
let peripherals = esp_hal::init(config);
let mut timer = Delay::new();
let mut io = Io::new(peripherals.IO_MUX);
info!("Init done.");
let spi = esp_hal::spi::master::Spi::new(
peripherals.SPI2,
@ -109,11 +118,13 @@ fn main() -> ! {
Output::new(peripherals.GPIO1, Level::Low, OutputConfig::default()),
&mut buffer,
);
info!("Display creating, initializing ...");
let mut display = mipidsi::Builder::new(ST7789, di)
.invert_colors(mipidsi::options::ColorInversion::Inverted)
.init(&mut timer)
.unwrap();
info!("Initialized");
let mut fb =
Framebuffer::<Rgb565, _, LittleEndian, 240, 240, { buffer_size::<Rgb565>(240, 240) }>::new(
@ -128,37 +139,53 @@ fn main() -> ! {
// .draw(&mut display)
// .unwrap();
info!("Creating sampler");
let mut sampler = Sampler::new(
peripherals.I2C0,
peripherals.GPIO8,
peripherals.GPIO9,
&mut timer,
);
timer.delay_millis(2000);
let _ = sampler.sample(&mut timer);
info!("Sensor initialized");
let input_button = Input::new(
peripherals.GPIO10,
InputConfig::default().with_pull(Pull::Down),
);
let mut last_button_state = Level::Low;
// Input button interrupt
{
esp_hal::interrupt::enable(
peripherals::Interrupt::GPIO,
esp_hal::interrupt::Priority::Priority1,
)
.unwrap();
let mut input_button = Input::new(
peripherals.GPIO10,
InputConfig::default().with_pull(Pull::Up),
);
io.set_interrupt_handler(interrupt_handler);
critical_section::with(|cs| {
input_button.listen(esp_hal::gpio::Event::FallingEdge);
INPUT_BUTTON.borrow_ref_mut(cs).replace(input_button);
});
}
info!("Setup interrupts");
let mut history = History::new(&mut sampler, &mut timer);
let mut view_state = ViewState::Main;
let mut x = 0;
loop {
// input state
let mut button_pressed = false;
if last_button_state == Level::Low && input_button.is_high() {
button_pressed = true;
}
last_button_state = input_button.level();
let button_pressed = critical_section::with(|cs| {
let val = *BUTTON_PRESSED.borrow(cs).borrow();
*BUTTON_PRESSED.borrow_ref_mut(cs) = false;
val
});
if button_pressed {
view_state = view_state.circulate();
}
if history.update(&mut sampler, &mut timer) || button_pressed {
if false && (history.update(&mut sampler, &mut timer) || button_pressed) {
// let iter = history.min5.oldest_ordered().map(|x| x.eco2);
// embedded_graphics::primitives::Rectangle::new(Point::zero(), fb.bounding_box().size)
// .into_styled(PrimitiveStyle::with_fill(Rgb565::BLACK))
@ -192,6 +219,7 @@ fn main() -> ! {
// for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v1.0.0/examples/src/bin
}
#[derive(Clone, Copy)]
pub struct Tendencies {
temperature: Tendency,
humidity: Tendency,
@ -259,6 +287,7 @@ impl Tendencies {
}
}
#[derive(Clone, Copy)]
pub struct AppState<'a> {
sample: Sample,
tendencies: Tendencies,
@ -297,75 +326,66 @@ impl ViewState {
.draw(target);
}
ViewState::GraphTemperature => {
let _ = views::detail::detailed_view(
MenuIndicatorType::Temperature(10.),
Tendency::Steady,
)
.as_drawable(Size::new(240, 240), Rgb565::WHITE)
.draw(target);
let mut graph_fb = Framebuffer::<
Rgb565,
_,
LittleEndian,
240,
{ 240 - 53 },
{ buffer_size::<Rgb565>(240, 240) },
>::new();
let iter = app_state.history.min5.oldest_ordered().map(|x| x.eco2);
graph_data(iter.clone(), app_state.history.min5.len(), &mut graph_fb);
min_indicator(iter.clone(), app_state.history.min5.len(), &mut graph_fb);
max_indicator(iter.clone(), app_state.history.min5.len(), &mut graph_fb);
let img_raw = ImageRaw::<Rgb565, LittleEndian>::new(graph_fb.data(), 240);
let image = embedded_graphics::image::Image::new(&img_raw, Point::new(0, 53));
let _ = image.draw(target);
detail::detailed(app_state, MenuIndicatorType::Temperature, target)
}
_ => {
let _ = views::menu::menu_view(app_state)
.as_drawable(Size::new(240, 240), Rgb565::WHITE)
.draw(target);
ViewState::GraphHumidity => {
detail::detailed(app_state, MenuIndicatorType::Humidity, target)
}
ViewState::GraphECo2 => detail::detailed(app_state, MenuIndicatorType::Co2, target),
ViewState::GraphTvoc => detail::detailed(app_state, MenuIndicatorType::Voc, target),
}
}
}
#[derive(Clone, Copy)]
pub enum MenuIndicatorType {
Temperature(f32),
Humidity(f32),
Co2(u32),
Voc(u32),
Temperature,
Humidity,
Co2,
Voc,
}
impl MenuIndicatorType {
pub fn get_corresponding_icon(&self) -> &'static StaticImage {
match self {
MenuIndicatorType::Temperature(_) => &images::TEMPERATURE_ICON,
MenuIndicatorType::Humidity(_) => &images::HUMIDITY_ICON,
MenuIndicatorType::Co2(_) => &images::CO2_ICON,
MenuIndicatorType::Voc(_) => &images::VOC_ICON,
MenuIndicatorType::Temperature => &images::TEMPERATURE_ICON,
MenuIndicatorType::Humidity => &images::HUMIDITY_ICON,
MenuIndicatorType::Co2 => &images::CO2_ICON,
MenuIndicatorType::Voc => &images::VOC_ICON,
}
}
pub fn get_corresponding_unit_string(&self) -> &'static str {
match self {
MenuIndicatorType::Temperature(_) => "C",
MenuIndicatorType::Humidity(_) => "%",
MenuIndicatorType::Co2(_) => "ppm",
MenuIndicatorType::Voc(_) => "ppb",
MenuIndicatorType::Temperature => "C",
MenuIndicatorType::Humidity => "%",
MenuIndicatorType::Co2 => "ppm",
MenuIndicatorType::Voc => "ppb",
}
}
pub fn get_value_str(&self) -> heapless::String<16> {
pub fn get_value_str(&self, app_state: &AppState) -> heapless::String<16> {
match self {
MenuIndicatorType::Temperature(temp) => format!(16; "{:.1}", temp).unwrap(),
MenuIndicatorType::Humidity(hum) => format!(16; "{:.1}", hum).unwrap(),
MenuIndicatorType::Co2(co2) => format!(16; "{}", co2).unwrap(),
MenuIndicatorType::Voc(voc) => format!(16; "{}", voc).unwrap(),
MenuIndicatorType::Temperature => {
format!(16; "{:.1}", app_state.sample.temperature).unwrap()
}
MenuIndicatorType::Humidity => format!(16; "{:.1}", app_state.sample.humidity).unwrap(),
MenuIndicatorType::Co2 => format!(16; "{}", app_state.sample.eco2 as u32).unwrap(),
MenuIndicatorType::Voc => format!(16; "{}", app_state.sample.tvoc as u32).unwrap(),
}
}
pub fn get_tendency(&self, app_state: &AppState) -> Tendency {
match self {
MenuIndicatorType::Temperature => app_state.tendencies.temperature,
MenuIndicatorType::Humidity => app_state.tendencies.humidity,
MenuIndicatorType::Co2 => app_state.tendencies.eco2,
MenuIndicatorType::Voc => app_state.tendencies.tvoc,
}
}
}
#[derive(Clone, Copy)]
pub enum Tendency {
Rising,
Steady,
@ -393,3 +413,16 @@ fn tendency_indicator(tendency: Tendency) -> impl View<Rgb565> {
.flex_frame()
.with_max_width(15)
}
#[handler]
fn interrupt_handler() {
critical_section::with(|cs| {
let mut button = INPUT_BUTTON.borrow_ref_mut(cs);
let Some(button) = button.as_mut() else {
return;
};
*BUTTON_PRESSED.borrow_ref_mut(cs) = true;
button.clear_interrupt();
});
}