Interrupts
This commit is contained in:
16
src/graph.rs
16
src/graph.rs
@ -19,14 +19,14 @@ fn map_float(x: f32, x_min: f32, x_max: f32, y_min: f32, y_max: f32) -> f32 {
|
||||
((x - x_min) / (x_max - x_min)) * (y_max - y_min) + y_min
|
||||
}
|
||||
|
||||
// const DEFAULT_LUT: [Rgb565; 5] = [
|
||||
// Rgb565::new(0, 16, 11),
|
||||
// Rgb565::new(11, 20, 17),
|
||||
// Rgb565::new(23, 20, 18),
|
||||
// Rgb565::new(31, 24, 12),
|
||||
// Rgb565::new(31, 41, 0),
|
||||
// ];
|
||||
const DEFAULT_LUT: [Rgb565; 4] = [Rgb565::GREEN, Rgb565::YELLOW, Rgb565::RED, Rgb565::RED];
|
||||
const DEFAULT_LUT: [Rgb565; 5] = [
|
||||
Rgb565::new(0, 16, 11),
|
||||
Rgb565::new(11, 20, 17),
|
||||
Rgb565::new(23, 20, 18),
|
||||
Rgb565::new(31, 24, 12),
|
||||
Rgb565::new(31, 41, 0),
|
||||
];
|
||||
// const DEFAULT_LUT: [Rgb565; 4] = [Rgb565::GREEN, Rgb565::YELLOW, Rgb565::RED, Rgb565::RED];
|
||||
|
||||
fn rgb565_interpolate(a: Rgb565, b: Rgb565, x: f32) -> Rgb565 {
|
||||
Rgb565::new(
|
||||
|
||||
155
src/main.rs
155
src/main.rs
@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,31 +1,85 @@
|
||||
use buoyant::layout::HorizontalAlignment;
|
||||
use buoyant::layout::VerticalAlignment;
|
||||
use buoyant::primitives::Size;
|
||||
use buoyant::view::AsDrawable;
|
||||
use buoyant::view::HStack;
|
||||
use buoyant::view::Spacer;
|
||||
use buoyant::view::Text;
|
||||
use buoyant::view::VStack;
|
||||
use buoyant::view::View;
|
||||
use buoyant::view::ViewExt;
|
||||
use embedded_graphics::Drawable;
|
||||
use embedded_graphics::framebuffer::Framebuffer;
|
||||
use embedded_graphics::framebuffer::buffer_size;
|
||||
use embedded_graphics::image::GetPixel;
|
||||
use embedded_graphics::image::ImageRaw;
|
||||
use embedded_graphics::pixelcolor::Rgb565;
|
||||
use embedded_graphics::pixelcolor::raw::LittleEndian;
|
||||
use embedded_graphics::prelude::DrawTarget;
|
||||
use embedded_graphics::prelude::RgbColor;
|
||||
use esp_hal::tsens::Temperature;
|
||||
use profont::PROFONT_18_POINT;
|
||||
use profont::PROFONT_24_POINT;
|
||||
|
||||
use crate::AppState;
|
||||
use crate::MenuIndicatorType;
|
||||
use crate::Tendency;
|
||||
use crate::colors::FRAME_STROKE_COLOR;
|
||||
use crate::colors::MAIN_TEXT_COLOR;
|
||||
use crate::colors::SUB_TEXT_COLOR;
|
||||
use crate::graph::graph_data;
|
||||
use crate::graph::max_indicator;
|
||||
use crate::graph::min_indicator;
|
||||
use crate::tendency_indicator;
|
||||
use crate::views::icon::icon_box_view;
|
||||
|
||||
pub fn detailed_view(indicator: MenuIndicatorType, tendency: Tendency) -> impl View<Rgb565> {
|
||||
pub fn detailed<T: DrawTarget<Color = Rgb565> + GetPixel<Color = Rgb565>>(
|
||||
app_state: AppState,
|
||||
indicator: MenuIndicatorType,
|
||||
target: &mut T,
|
||||
) {
|
||||
let _ = detailed_view_top(indicator, app_state)
|
||||
.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| match indicator {
|
||||
MenuIndicatorType::Temperature => x.temperature,
|
||||
MenuIndicatorType::Humidity => x.humidity,
|
||||
MenuIndicatorType::Co2 => x.eco2,
|
||||
MenuIndicatorType::Voc => x.tvoc,
|
||||
});
|
||||
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,
|
||||
embedded_graphics::prelude::Point::new(0, 53),
|
||||
);
|
||||
let _ = image.draw(target);
|
||||
}
|
||||
|
||||
pub fn detailed_view_top(indicator: MenuIndicatorType, app_state: AppState) -> impl View<Rgb565> {
|
||||
VStack::new((
|
||||
// Header
|
||||
HStack::new((
|
||||
icon_box_view(FRAME_STROKE_COLOR, indicator.get_corresponding_icon()),
|
||||
Spacer::default().flex_frame().with_max_width(10),
|
||||
tendency_indicator(tendency),
|
||||
Text::new(indicator.get_value_str(), &PROFONT_24_POINT)
|
||||
tendency_indicator(indicator.get_tendency(&app_state)),
|
||||
Text::new(indicator.get_value_str(&app_state), &PROFONT_24_POINT)
|
||||
.foreground_color(MAIN_TEXT_COLOR),
|
||||
Text::new(indicator.get_corresponding_unit_string(), &PROFONT_18_POINT)
|
||||
.foreground_color(SUB_TEXT_COLOR)
|
||||
@ -34,13 +88,7 @@ pub fn detailed_view(indicator: MenuIndicatorType, tendency: Tendency) -> impl V
|
||||
.with_vertical_alignment(VerticalAlignment::Bottom)
|
||||
.with_max_height(25),
|
||||
Spacer::default(),
|
||||
Text::new("Temperature", &PROFONT_18_POINT)
|
||||
.foreground_color(SUB_TEXT_COLOR)
|
||||
.flex_frame()
|
||||
.with_infinite_max_height()
|
||||
.with_vertical_alignment(VerticalAlignment::Bottom)
|
||||
.with_max_height(25),
|
||||
Spacer::default().flex_frame().with_max_width(10),
|
||||
//Spacer::default().flex_frame().with_max_width(10),
|
||||
)),
|
||||
// Window
|
||||
Spacer::default()
|
||||
|
||||
@ -21,32 +21,23 @@ use crate::views::icon::icon_box_view;
|
||||
pub fn menu_view(app_state: AppState) -> impl View<Rgb565> {
|
||||
VStack::new((
|
||||
HStack::new((
|
||||
main_menu_indicator(
|
||||
MenuIndicatorType::Temperature(app_state.sample.temperature),
|
||||
app_state.tendencies.temperature,
|
||||
),
|
||||
main_menu_indicator(
|
||||
MenuIndicatorType::Humidity(app_state.sample.humidity),
|
||||
app_state.tendencies.humidity,
|
||||
),
|
||||
main_menu_indicator(MenuIndicatorType::Temperature, app_state),
|
||||
main_menu_indicator(MenuIndicatorType::Humidity, app_state),
|
||||
))
|
||||
.with_spacing(2),
|
||||
HStack::new((
|
||||
main_menu_indicator(
|
||||
MenuIndicatorType::Co2(app_state.sample.eco2 as u32),
|
||||
app_state.tendencies.eco2,
|
||||
),
|
||||
main_menu_indicator(
|
||||
MenuIndicatorType::Voc(app_state.sample.tvoc as u32),
|
||||
app_state.tendencies.tvoc,
|
||||
),
|
||||
main_menu_indicator(MenuIndicatorType::Co2, app_state),
|
||||
main_menu_indicator(MenuIndicatorType::Voc, app_state),
|
||||
))
|
||||
.with_spacing(2),
|
||||
))
|
||||
.with_spacing(2)
|
||||
}
|
||||
|
||||
fn main_menu_indicator(indicator_type: MenuIndicatorType, tendency: Tendency) -> impl View<Rgb565> {
|
||||
fn main_menu_indicator(
|
||||
indicator_type: MenuIndicatorType,
|
||||
app_state: AppState,
|
||||
) -> impl View<Rgb565> {
|
||||
Rectangle
|
||||
.corner_radius(5)
|
||||
.stroked(FRAME_STROKE)
|
||||
@ -64,8 +55,8 @@ fn main_menu_indicator(indicator_type: MenuIndicatorType, tendency: Tendency) ->
|
||||
)),
|
||||
HStack::new((
|
||||
Spacer::default(),
|
||||
tendency_indicator(tendency),
|
||||
Text::new(indicator_type.get_value_str(), &PROFONT_24_POINT)
|
||||
tendency_indicator(indicator_type.get_tendency(&app_state)),
|
||||
Text::new(indicator_type.get_value_str(&app_state), &PROFONT_24_POINT)
|
||||
.foreground_color(MAIN_TEXT_COLOR),
|
||||
Text::new(
|
||||
indicator_type.get_corresponding_unit_string(),
|
||||
|
||||
Reference in New Issue
Block a user