Faster fill
This commit is contained in:
203
Cargo.lock
generated
203
Cargo.lock
generated
@ -2,6 +2,17 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aht20-async"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c93801d1c9009ecef32e156413ef8f318478d1e7c7e38dc96654f412cf37827"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"crc_all",
|
||||
"embedded-hal-async",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aht20-driver"
|
||||
version = "2.0.0"
|
||||
@ -16,6 +27,12 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c583acf993cf4245c4acb0a2cc2ab1f9cc097de73411bb6d3647ff6af2b1013d"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
@ -107,6 +124,7 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
name = "co2-meter"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aht20-async",
|
||||
"aht20-driver",
|
||||
"buoyant",
|
||||
"critical-section",
|
||||
@ -118,6 +136,7 @@ dependencies = [
|
||||
"embedded-graphics-framebuf",
|
||||
"embedded-hal-bus",
|
||||
"ens160",
|
||||
"ens160-aq",
|
||||
"esp-alloc",
|
||||
"esp-backtrace",
|
||||
"esp-bootloader-esp-idf",
|
||||
@ -128,6 +147,7 @@ dependencies = [
|
||||
"libm",
|
||||
"mipidsi",
|
||||
"profont",
|
||||
"static_cell",
|
||||
"tinybmp",
|
||||
]
|
||||
|
||||
@ -143,6 +163,12 @@ version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a62ec9ff5f7965e4d7280bd5482acd20aadb50d632cf6c1d74493856b011fa73"
|
||||
|
||||
[[package]]
|
||||
name = "crc_all"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c46c1a17ebeef917714db3ae9a17bd2184f7e9977d8e020c6c8bcf59a28a6f1b"
|
||||
|
||||
[[package]]
|
||||
name = "critical-section"
|
||||
version = "1.2.0"
|
||||
@ -266,7 +292,7 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -566,6 +592,20 @@ dependencies = [
|
||||
"embedded-storage",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embuild"
|
||||
version = "0.31.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4caa4f198bb9152a55c0103efb83fa4edfcbb8625f4c9e94ae8ec8e23827c563"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 1.3.2",
|
||||
"filetime",
|
||||
"log",
|
||||
"shlex",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ens160"
|
||||
version = "0.6.1"
|
||||
@ -577,6 +617,22 @@ dependencies = [
|
||||
"maybe-async-cfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ens160-aq"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b00bf02a6f1112f67da6a6638be8497415d7cf1ccba56d332889886108858f93"
|
||||
dependencies = [
|
||||
"bitfield 0.14.0",
|
||||
"byteorder",
|
||||
"embedded-hal 1.0.0",
|
||||
"embuild",
|
||||
"libm",
|
||||
"log",
|
||||
"maybe-async-cfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enumset"
|
||||
version = "1.1.10"
|
||||
@ -893,6 +949,18 @@ dependencies = [
|
||||
"vcell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"libredox",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "float-cmp"
|
||||
version = "0.9.0"
|
||||
@ -1085,6 +1153,17 @@ version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked_list_allocator"
|
||||
version = "0.10.5"
|
||||
@ -1313,6 +1392,15 @@ version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "riscv"
|
||||
version = "0.15.0"
|
||||
@ -1440,6 +1528,12 @@ dependencies = [
|
||||
"unsafe-libyaml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
@ -1467,6 +1561,15 @@ version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "static_cell"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0530892bb4fa575ee0da4b86f86c667132a94b74bb72160f58ee5a4afec74c23"
|
||||
dependencies = [
|
||||
"portable-atomic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
@ -1538,13 +1641,33 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
"thiserror-impl 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1667,7 +1790,7 @@ version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1676,6 +1799,15 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
@ -1685,6 +1817,71 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.14"
|
||||
|
||||
@ -36,6 +36,9 @@ embassy-executor = {version = "0.9.1", features = ["arch-riscv32"]}
|
||||
embassy-time = "0.5.0"
|
||||
embassy-sync = "0.7.2"
|
||||
embedded-graphics-framebuf = "0.5.0"
|
||||
static_cell = "2.1.1"
|
||||
ens160-aq = {version = "0.2.11"}
|
||||
aht20-async = "1.0.0"
|
||||
|
||||
|
||||
|
||||
@ -50,5 +53,5 @@ debug = 2
|
||||
debug-assertions = false
|
||||
incremental = false
|
||||
lto = 'fat'
|
||||
opt-level = 's'
|
||||
opt-level = 3
|
||||
overflow-checks = false
|
||||
|
||||
@ -1,16 +1,28 @@
|
||||
use defmt::info;
|
||||
use embedded_graphics::pixelcolor::Rgb565;
|
||||
use embedded_graphics::prelude::DrawTarget;
|
||||
use embedded_hal_bus::spi::ExclusiveDevice;
|
||||
use embedded_hal_bus::spi::NoDelay;
|
||||
use esp_hal::Blocking;
|
||||
use esp_hal::delay::Delay;
|
||||
use esp_hal::gpio::Level;
|
||||
use esp_hal::gpio::Output;
|
||||
use esp_hal::gpio::OutputConfig;
|
||||
use esp_hal::gpio::OutputPin;
|
||||
use esp_hal::spi::master::Instance;
|
||||
use esp_hal::spi::master::Spi;
|
||||
use esp_hal::time::Rate;
|
||||
use mipidsi::Display;
|
||||
use mipidsi::NoResetPin;
|
||||
use mipidsi::interface::SpiInterface;
|
||||
use mipidsi::models::ST7789;
|
||||
use static_cell::StaticCell;
|
||||
|
||||
pub type MainDisplay<'a> = Display<
|
||||
SpiInterface<'a, ExclusiveDevice<Spi<'a, Blocking>, Output<'a>, NoDelay>, Output<'a>>,
|
||||
ST7789,
|
||||
NoResetPin,
|
||||
>;
|
||||
|
||||
static SPI_BUFFER: StaticCell<[u8; 1024]> = StaticCell::new();
|
||||
|
||||
pub fn setup_display<'a>(
|
||||
spi: impl Instance + 'static,
|
||||
@ -19,8 +31,7 @@ pub fn setup_display<'a>(
|
||||
cs: impl OutputPin + 'static,
|
||||
dc: impl OutputPin + 'static,
|
||||
timer: &mut Delay,
|
||||
buffer: &mut [u8],
|
||||
) -> impl DrawTarget<Color = Rgb565> {
|
||||
) -> MainDisplay<'a> {
|
||||
let spi = esp_hal::spi::master::Spi::new(
|
||||
spi,
|
||||
esp_hal::spi::master::Config::default()
|
||||
@ -36,10 +47,11 @@ pub fn setup_display<'a>(
|
||||
let spi_device = ExclusiveDevice::new_no_delay(spi, cs_output).unwrap();
|
||||
|
||||
// Define the display interface with no chip select
|
||||
let spi_buffer = SPI_BUFFER.init([0u8; 1024]);
|
||||
let di = SpiInterface::new(
|
||||
spi_device,
|
||||
Output::new(dc, Level::Low, OutputConfig::default()),
|
||||
buffer,
|
||||
spi_buffer,
|
||||
);
|
||||
info!("Display creating, initializing ...");
|
||||
|
||||
|
||||
14
src/graph.rs
14
src/graph.rs
@ -11,7 +11,6 @@ use embedded_graphics::prelude::Size;
|
||||
use embedded_graphics::primitives::Line;
|
||||
use embedded_graphics::primitives::PrimitiveStyle;
|
||||
use embedded_graphics::text::Text;
|
||||
use embedded_graphics::text::renderer::TextRenderer;
|
||||
use heapless::format;
|
||||
use profont::PROFONT_10_POINT;
|
||||
|
||||
@ -90,22 +89,27 @@ where
|
||||
for x in 0..size.width {
|
||||
// Start coloring from up to bottom
|
||||
let mut met_curve = false;
|
||||
let mut cross_y = x % 7;
|
||||
|
||||
for y in 0..size.height {
|
||||
let mut y = 0;
|
||||
while y < size.height {
|
||||
let position = Point::new(x as i32, y as i32);
|
||||
let pixel = target.pixel(position).unwrap();
|
||||
let height_factor = map_float(y as f32, 0., size.height as f32, 1., 0.);
|
||||
let height_color = color_lut(height_factor, &DEFAULT_LUT);
|
||||
|
||||
if pixel == Rgb565::WHITE {
|
||||
if target.pixel(position).unwrap() == Rgb565::WHITE {
|
||||
let _ = Pixel(position, height_color).draw(target);
|
||||
met_curve = true;
|
||||
} else if met_curve && (x as i32 - y as i32) % 7 == 0 {
|
||||
y += 1;
|
||||
} else if met_curve {
|
||||
let _ = Pixel(
|
||||
position,
|
||||
rgb565_interpolate(height_color, Rgb565::BLACK, 1. - height_factor),
|
||||
)
|
||||
.draw(target);
|
||||
y += 7 - (y % 7);
|
||||
} else {
|
||||
y += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
293
src/main.rs
293
src/main.rs
@ -6,23 +6,36 @@
|
||||
holding buffers for the duration of a data transfer."
|
||||
)]
|
||||
|
||||
use core::cell::RefCell;
|
||||
use core::hint;
|
||||
|
||||
use buoyant::primitives::ProposedDimension;
|
||||
use buoyant::view::AsDrawable;
|
||||
use critical_section::Mutex;
|
||||
use defmt::info;
|
||||
use defmt::trace;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||
use embassy_sync::channel::Channel;
|
||||
use embassy_time::Duration;
|
||||
use embassy_time::Timer;
|
||||
use embedded_graphics::Drawable;
|
||||
use embedded_graphics::draw_target::DrawTarget;
|
||||
use embedded_graphics::framebuffer::Framebuffer;
|
||||
use embedded_graphics::framebuffer::buffer_size;
|
||||
use embedded_graphics::geometry::Dimensions;
|
||||
use embedded_graphics::image::GetPixel;
|
||||
use embedded_graphics::pixelcolor::Rgb565;
|
||||
use embedded_graphics::pixelcolor::raw::LittleEndian;
|
||||
use embedded_graphics::prelude::PixelColor;
|
||||
use embedded_graphics::prelude::Primitive;
|
||||
use embedded_graphics::prelude::RgbColor;
|
||||
use embedded_graphics::primitives::PrimitiveStyle;
|
||||
use embedded_graphics_framebuf::FrameBuf;
|
||||
use embedded_graphics_framebuf::backends::FrameBufferBackend;
|
||||
use esp_hal::clock::CpuClock;
|
||||
use esp_hal::gpio::Input;
|
||||
use esp_hal::gpio::InputConfig;
|
||||
use esp_hal::gpio::Level;
|
||||
use esp_hal::gpio::Output;
|
||||
use esp_hal::gpio::OutputConfig;
|
||||
use esp_hal::gpio::Pull;
|
||||
use esp_hal::interrupt::software::SoftwareInterruptControl;
|
||||
use esp_hal::timer::timg::TimerGroup;
|
||||
@ -31,18 +44,25 @@ use esp_rtos::main;
|
||||
extern crate alloc;
|
||||
extern crate esp_alloc;
|
||||
|
||||
// mod colors;
|
||||
// mod graph;
|
||||
mod colors;
|
||||
mod display;
|
||||
mod graph;
|
||||
mod images;
|
||||
mod sampler;
|
||||
//mod views;
|
||||
mod views;
|
||||
|
||||
esp_bootloader_esp_idf::esp_app_desc!();
|
||||
use esp_backtrace as _;
|
||||
use esp_println as _;
|
||||
use heapless::HistoryBuf;
|
||||
use heapless::format;
|
||||
use static_cell::StaticCell;
|
||||
|
||||
use crate::display::MainDisplay;
|
||||
use crate::images::StaticImage;
|
||||
use crate::sampler::History;
|
||||
use crate::sampler::Sample;
|
||||
use crate::sampler::Sampler;
|
||||
|
||||
pub enum ApplicationEvent {
|
||||
ButtonPress,
|
||||
@ -51,14 +71,16 @@ pub enum ApplicationEvent {
|
||||
}
|
||||
|
||||
static EVENT_CHANNEL: Channel<CriticalSectionRawMutex, ApplicationEvent, 8> = Channel::new();
|
||||
static MAIN_DISPLAY: Mutex<RefCell<Option<MainDisplay>>> = Mutex::new(RefCell::new(None));
|
||||
static SAMPLER: StaticCell<Sampler> = StaticCell::new();
|
||||
|
||||
#[main]
|
||||
async fn main(spawner: Spawner) -> ! {
|
||||
async fn main(spawner: Spawner) {
|
||||
esp_alloc::heap_allocator!(size: 32 * 1024);
|
||||
|
||||
// generator version: 1.0.1
|
||||
info!("Starting up.");
|
||||
//images::prepare_images();
|
||||
images::prepare_images();
|
||||
info!("Prepared images.");
|
||||
|
||||
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
|
||||
@ -70,7 +92,6 @@ async fn main(spawner: Spawner) -> ! {
|
||||
info!("Init done.");
|
||||
info!("Setting up display");
|
||||
let mut timer = esp_hal::delay::Delay::new();
|
||||
let mut buffer = [0_u8; 512];
|
||||
let mut display = display::setup_display(
|
||||
peripherals.SPI2,
|
||||
peripherals.GPIO4,
|
||||
@ -78,7 +99,6 @@ async fn main(spawner: Spawner) -> ! {
|
||||
peripherals.GPIO0,
|
||||
peripherals.GPIO1,
|
||||
&mut timer,
|
||||
&mut buffer,
|
||||
);
|
||||
|
||||
info!("Clearing screen");
|
||||
@ -86,20 +106,32 @@ async fn main(spawner: Spawner) -> ! {
|
||||
let fbuf_data = [Rgb565::new(0, 0, 0); 240 * 240];
|
||||
let _ = display.fill_contiguous(&display.bounding_box(), fbuf_data);
|
||||
}
|
||||
critical_section::with(|cs| {
|
||||
MAIN_DISPLAY.borrow_ref_mut(cs).replace(display);
|
||||
});
|
||||
|
||||
// Setup button
|
||||
let btn = Input::new(
|
||||
peripherals.GPIO10,
|
||||
InputConfig::default().with_pull(Pull::Down),
|
||||
);
|
||||
|
||||
spawner.spawn(button_listener(btn));
|
||||
spawner.spawn(event_handler());
|
||||
// Setup sampler
|
||||
let sampler = SAMPLER.init(Sampler::new(
|
||||
peripherals.I2C0,
|
||||
peripherals.GPIO8,
|
||||
peripherals.GPIO9,
|
||||
&mut timer,
|
||||
));
|
||||
|
||||
loop {}
|
||||
spawner.spawn(button_listener(btn)).unwrap();
|
||||
spawner.spawn(event_handler()).unwrap();
|
||||
spawner.spawn(sampler_task(sampler)).unwrap();
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn button_listener(mut btn: Input<'static>) {
|
||||
info!("Button listner task launched");
|
||||
let sender = EVENT_CHANNEL.sender();
|
||||
loop {
|
||||
btn.wait_for_rising_edge().await;
|
||||
@ -111,17 +143,242 @@ async fn button_listener(mut btn: Input<'static>) {
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn event_handler() {
|
||||
info!("Event handler task launched");
|
||||
let receiver = EVENT_CHANNEL.receiver();
|
||||
let mut state = false;
|
||||
let mut display = critical_section::with(|cs| MAIN_DISPLAY.borrow_ref_mut(cs).take().unwrap());
|
||||
let mut fbuf =
|
||||
Framebuffer::<Rgb565, _, LittleEndian, 240, 240, { buffer_size::<Rgb565>(240, 240) }>::new(
|
||||
);
|
||||
|
||||
let mut last_5_mins: HistoryBuf<Sample, { 60 * 5 }> = HistoryBuf::new();
|
||||
last_5_mins.write(Sample::zero());
|
||||
let mut redraw = true;
|
||||
let mut current_view = ViewState::Main;
|
||||
|
||||
loop {
|
||||
if redraw {
|
||||
redraw = false;
|
||||
let mut draw_graph = None;
|
||||
let tendencies = Tendencies::from_history(&last_5_mins);
|
||||
let _ = fbuf
|
||||
.bounding_box()
|
||||
.into_styled(PrimitiveStyle::with_fill(Rgb565::BLACK))
|
||||
.draw(&mut fbuf);
|
||||
match current_view {
|
||||
ViewState::Main => {
|
||||
let _ = views::menu::menu_view(*last_5_mins.last().unwrap(), tendencies)
|
||||
.as_drawable(fbuf.bounding_box().size, Rgb565::WHITE)
|
||||
.draw(&mut fbuf);
|
||||
}
|
||||
ViewState::TemperatureGraph => draw_graph = Some(MeasurementType::Temperature),
|
||||
ViewState::HumidityGraph => draw_graph = Some(MeasurementType::Humidity),
|
||||
ViewState::ECo2Graph => draw_graph = Some(MeasurementType::ECo2),
|
||||
ViewState::TVocGraph => draw_graph = Some(MeasurementType::TVoc),
|
||||
}
|
||||
|
||||
if let Some(graph_type) = draw_graph {
|
||||
info!("Drawing graph");
|
||||
views::detail::detailed(&last_5_mins, tendencies, graph_type, &mut fbuf);
|
||||
info!("Drew graph");
|
||||
}
|
||||
|
||||
let _ = display.fill_contiguous(
|
||||
&fbuf.bounding_box(),
|
||||
fbuf.data()
|
||||
.chunks(2)
|
||||
.map(|x| unsafe { core::mem::transmute::<_, Rgb565>([x[0], x[1]]) }),
|
||||
);
|
||||
}
|
||||
let event = receiver.receive().await;
|
||||
match event {
|
||||
ApplicationEvent::ButtonPress => {
|
||||
state = !state;
|
||||
let _ = fbuf.clear(if state { Rgb565::WHITE } else { Rgb565::BLACK });
|
||||
let _ = display.fill_contiguous(&display.bounding_box(), fbuf.data.iter().copied());
|
||||
current_view = current_view.next();
|
||||
redraw = true;
|
||||
}
|
||||
ApplicationEvent::NewSample(x) => {
|
||||
last_5_mins.write(x);
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ViewState {
|
||||
Main,
|
||||
TemperatureGraph,
|
||||
HumidityGraph,
|
||||
ECo2Graph,
|
||||
TVocGraph,
|
||||
}
|
||||
|
||||
impl ViewState {
|
||||
pub fn next(&self) -> Self {
|
||||
match self {
|
||||
ViewState::Main => ViewState::TemperatureGraph,
|
||||
ViewState::TemperatureGraph => ViewState::HumidityGraph,
|
||||
ViewState::HumidityGraph => ViewState::ECo2Graph,
|
||||
ViewState::ECo2Graph => ViewState::TVocGraph,
|
||||
ViewState::TVocGraph => ViewState::Main,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const LOW_PASS_LENGTH: usize = 10;
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn sampler_task(sampler: &'static mut Sampler<'static>) {
|
||||
info!("Sampler task launched");
|
||||
let sender = EVENT_CHANNEL.sender();
|
||||
sender
|
||||
.send(ApplicationEvent::NewSample(Sample::zero()))
|
||||
.await;
|
||||
let mut low_pass: HistoryBuf<Sample, LOW_PASS_LENGTH> = HistoryBuf::new();
|
||||
let mut delay = esp_hal::delay::Delay::new();
|
||||
let mut count = 0;
|
||||
|
||||
loop {
|
||||
Timer::after(Duration::from_millis(500)).await;
|
||||
let sample = sampler.sample(&mut delay);
|
||||
low_pass.write(sample);
|
||||
count += 1;
|
||||
|
||||
if count >= 2 {
|
||||
sender.send(ApplicationEvent::NewSample(sample)).await;
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Tendencies {
|
||||
temperature: Tendency,
|
||||
humidity: Tendency,
|
||||
eco2: Tendency,
|
||||
tvoc: Tendency,
|
||||
}
|
||||
|
||||
impl Tendencies {
|
||||
pub fn from_history<const N: usize>(history: &HistoryBuf<Sample, N>) -> Self {
|
||||
let mut iter = history.oldest_ordered().rev().copied().take(5);
|
||||
let len = history.len().min(5);
|
||||
|
||||
if len <= 1 {
|
||||
return Tendencies {
|
||||
temperature: Tendency::Steady,
|
||||
humidity: Tendency::Steady,
|
||||
eco2: Tendency::Steady,
|
||||
tvoc: Tendency::Steady,
|
||||
};
|
||||
}
|
||||
|
||||
let mut last = iter.next().unwrap();
|
||||
let mut avg_slope = Sample::zero();
|
||||
for x in iter {
|
||||
avg_slope = avg_slope + (last - x);
|
||||
last = x;
|
||||
}
|
||||
|
||||
avg_slope = avg_slope * (1. / len as f32);
|
||||
|
||||
const TEMPERATURE_TENDENCY_TRESHOLD: f32 = 0.3;
|
||||
const HUMIDITY_TENDENCY_TRESHOLD: f32 = 0.3;
|
||||
const ECO2_TENDENCY_TRESHOLD: f32 = 50.;
|
||||
const TVOC_TENDENCY_TRESHOLD: f32 = 50.;
|
||||
Tendencies {
|
||||
temperature: if avg_slope.temperature > TEMPERATURE_TENDENCY_TRESHOLD {
|
||||
Tendency::Rising
|
||||
} else if avg_slope.temperature < -TEMPERATURE_TENDENCY_TRESHOLD {
|
||||
Tendency::Falling
|
||||
} else {
|
||||
Tendency::Steady
|
||||
},
|
||||
humidity: if avg_slope.humidity > HUMIDITY_TENDENCY_TRESHOLD {
|
||||
Tendency::Rising
|
||||
} else if avg_slope.humidity < -HUMIDITY_TENDENCY_TRESHOLD {
|
||||
Tendency::Falling
|
||||
} else {
|
||||
Tendency::Steady
|
||||
},
|
||||
eco2: if avg_slope.eco2 > ECO2_TENDENCY_TRESHOLD {
|
||||
Tendency::Rising
|
||||
} else if avg_slope.eco2 < -ECO2_TENDENCY_TRESHOLD {
|
||||
Tendency::Falling
|
||||
} else {
|
||||
Tendency::Steady
|
||||
},
|
||||
tvoc: if avg_slope.tvoc > TVOC_TENDENCY_TRESHOLD {
|
||||
Tendency::Rising
|
||||
} else if avg_slope.tvoc < -TVOC_TENDENCY_TRESHOLD {
|
||||
Tendency::Falling
|
||||
} else {
|
||||
Tendency::Steady
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Tendency {
|
||||
Rising,
|
||||
Steady,
|
||||
Falling,
|
||||
}
|
||||
|
||||
impl Tendency {
|
||||
pub fn get_corresponding_icon(&self) -> &'static StaticImage {
|
||||
match self {
|
||||
Self::Rising => &images::TENDENCY_RISING,
|
||||
Self::Steady => &images::TENDENCY_STEADY,
|
||||
Self::Falling => &images::TENDENCY_FALLING,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum MeasurementType {
|
||||
Temperature,
|
||||
Humidity,
|
||||
ECo2,
|
||||
TVoc,
|
||||
}
|
||||
|
||||
impl MeasurementType {
|
||||
pub fn get_corresponding_icon(&self) -> &'static StaticImage {
|
||||
match self {
|
||||
MeasurementType::Temperature => &images::TEMPERATURE_ICON,
|
||||
MeasurementType::Humidity => &images::HUMIDITY_ICON,
|
||||
MeasurementType::ECo2 => &images::CO2_ICON,
|
||||
MeasurementType::TVoc => &images::VOC_ICON,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_corresponding_unit_string(&self) -> &'static str {
|
||||
match self {
|
||||
MeasurementType::Temperature => "C",
|
||||
MeasurementType::Humidity => "%",
|
||||
MeasurementType::ECo2 => "ppm",
|
||||
MeasurementType::TVoc => "ppb",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_value_str(&self, sample: Sample) -> heapless::String<16> {
|
||||
match self {
|
||||
MeasurementType::Temperature => format!(16; "{:.1}", sample.temperature).unwrap(),
|
||||
MeasurementType::Humidity => format!(16; "{:.1}", sample.humidity).unwrap(),
|
||||
MeasurementType::ECo2 => format!(16; "{}", sample.eco2 as u32).unwrap(),
|
||||
MeasurementType::TVoc => format!(16; "{}", sample.tvoc as u32).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_tendency(&self, tendencies: Tendencies) -> Tendency {
|
||||
match self {
|
||||
MeasurementType::Temperature => tendencies.temperature,
|
||||
MeasurementType::Humidity => tendencies.humidity,
|
||||
MeasurementType::ECo2 => tendencies.eco2,
|
||||
MeasurementType::TVoc => tendencies.tvoc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,26 +5,19 @@ use core::ops::Sub;
|
||||
|
||||
use aht20_driver::AHT20;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::vec::Vec;
|
||||
use core::default::Default;
|
||||
use embedded_hal_bus::i2c::RcDevice;
|
||||
//use ens160_aq::Ens160;
|
||||
use ens160::Ens160;
|
||||
use esp_hal::Blocking;
|
||||
use esp_hal::DriverMode;
|
||||
use esp_hal::delay::Delay;
|
||||
use esp_hal::gpio::interconnect::PeripheralOutput;
|
||||
use esp_hal::i2c;
|
||||
use esp_hal::i2c::master::I2c;
|
||||
use esp_hal::i2c::master::Instance;
|
||||
use esp_hal::peripherals;
|
||||
use esp_hal::peripherals::Peripherals;
|
||||
use esp_hal::time::Duration;
|
||||
use esp_hal::time::Instant;
|
||||
use heapless::Deque;
|
||||
use heapless::HistoryBuf;
|
||||
|
||||
use crate::sampler;
|
||||
|
||||
pub struct Sampler<'a> {
|
||||
ens160: Ens160<RcDevice<I2c<'a, Blocking>>>,
|
||||
aht20: aht20_driver::AHT20Initialized<RcDevice<I2c<'a, Blocking>>>,
|
||||
@ -133,6 +126,7 @@ impl<'a> Sampler<'a> {
|
||||
|
||||
pub fn sample(&mut self, timer: &mut Delay) -> Sample {
|
||||
let aht20_measurement = self.aht20.measure(timer).unwrap();
|
||||
|
||||
Sample {
|
||||
temperature: aht20_measurement.temperature,
|
||||
humidity: aht20_measurement.humidity,
|
||||
|
||||
@ -17,28 +17,29 @@ 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 heapless::HistoryBuf;
|
||||
use profont::PROFONT_18_POINT;
|
||||
use profont::PROFONT_24_POINT;
|
||||
|
||||
use crate::AppState;
|
||||
use crate::MenuIndicatorType;
|
||||
use crate::Tendency;
|
||||
use crate::MeasurementType;
|
||||
use crate::Tendencies;
|
||||
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::sampler::Sample;
|
||||
use crate::views::icon::icon_box_view;
|
||||
use crate::views::menu::tendency_indicator;
|
||||
|
||||
pub fn detailed<T: DrawTarget<Color = Rgb565> + GetPixel<Color = Rgb565>>(
|
||||
app_state: AppState,
|
||||
indicator: MenuIndicatorType,
|
||||
pub fn detailed<T: DrawTarget<Color = Rgb565> + GetPixel<Color = Rgb565>, const N: usize>(
|
||||
history: &HistoryBuf<Sample, N>,
|
||||
tendencies: Tendencies,
|
||||
indicator: MeasurementType,
|
||||
target: &mut T,
|
||||
) {
|
||||
let _ = detailed_view_top(indicator, app_state)
|
||||
let _ = detailed_view_top(indicator, tendencies, *history.last().unwrap())
|
||||
.as_drawable(Size::new(240, 240), Rgb565::WHITE)
|
||||
.draw(target);
|
||||
|
||||
@ -50,19 +51,15 @@ pub fn detailed<T: DrawTarget<Color = Rgb565> + GetPixel<Color = Rgb565>>(
|
||||
{ 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 iter = history.oldest_ordered().map(|x| match indicator {
|
||||
MeasurementType::Temperature => x.temperature,
|
||||
MeasurementType::Humidity => x.humidity,
|
||||
MeasurementType::ECo2 => x.eco2,
|
||||
MeasurementType::TVoc => x.tvoc,
|
||||
});
|
||||
graph_data(iter.clone(), history.len(), &mut graph_fb);
|
||||
min_indicator(iter.clone(), history.len(), &mut graph_fb);
|
||||
max_indicator(iter.clone(), history.len(), &mut graph_fb);
|
||||
|
||||
let img_raw = ImageRaw::<Rgb565, LittleEndian>::new(graph_fb.data(), 240);
|
||||
let image = embedded_graphics::image::Image::new(
|
||||
@ -72,14 +69,18 @@ pub fn detailed<T: DrawTarget<Color = Rgb565> + GetPixel<Color = Rgb565>>(
|
||||
let _ = image.draw(target);
|
||||
}
|
||||
|
||||
pub fn detailed_view_top(indicator: MenuIndicatorType, app_state: AppState) -> impl View<Rgb565> {
|
||||
pub fn detailed_view_top(
|
||||
indicator: MeasurementType,
|
||||
tendencies: Tendencies,
|
||||
sample: Sample,
|
||||
) -> 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(indicator.get_tendency(&app_state)),
|
||||
Text::new(indicator.get_value_str(&app_state), &PROFONT_24_POINT)
|
||||
tendency_indicator(indicator.get_tendency(tendencies)),
|
||||
Text::new(indicator.get_value_str(sample), &PROFONT_24_POINT)
|
||||
.foreground_color(MAIN_TEXT_COLOR),
|
||||
Text::new(indicator.get_corresponding_unit_string(), &PROFONT_18_POINT)
|
||||
.foreground_color(SUB_TEXT_COLOR)
|
||||
|
||||
@ -7,8 +7,8 @@ use embedded_graphics::prelude::*;
|
||||
use profont::PROFONT_18_POINT;
|
||||
use profont::PROFONT_24_POINT;
|
||||
|
||||
use crate::AppState;
|
||||
use crate::MenuIndicatorType;
|
||||
use crate::MeasurementType;
|
||||
use crate::Tendencies;
|
||||
use crate::Tendency;
|
||||
use crate::colors::FRAME_BACKGROUD_COLOR;
|
||||
use crate::colors::FRAME_STROKE;
|
||||
@ -16,18 +16,19 @@ use crate::colors::FRAME_STROKE_COLOR;
|
||||
use crate::colors::MAIN_TEXT_COLOR;
|
||||
use crate::colors::SUB_TEXT_COLOR;
|
||||
use crate::get_image;
|
||||
use crate::sampler::Sample;
|
||||
use crate::views::icon::icon_box_view;
|
||||
|
||||
pub fn menu_view(app_state: AppState) -> impl View<Rgb565> {
|
||||
pub fn menu_view(sample: Sample, tendencies: Tendencies) -> impl View<Rgb565> {
|
||||
VStack::new((
|
||||
HStack::new((
|
||||
main_menu_indicator(MenuIndicatorType::Temperature, app_state),
|
||||
main_menu_indicator(MenuIndicatorType::Humidity, app_state),
|
||||
main_menu_indicator(MeasurementType::Temperature, tendencies, sample),
|
||||
main_menu_indicator(MeasurementType::Humidity, tendencies, sample),
|
||||
))
|
||||
.with_spacing(2),
|
||||
HStack::new((
|
||||
main_menu_indicator(MenuIndicatorType::Co2, app_state),
|
||||
main_menu_indicator(MenuIndicatorType::Voc, app_state),
|
||||
main_menu_indicator(MeasurementType::ECo2, tendencies, sample),
|
||||
main_menu_indicator(MeasurementType::TVoc, tendencies, sample),
|
||||
))
|
||||
.with_spacing(2),
|
||||
))
|
||||
@ -35,8 +36,9 @@ pub fn menu_view(app_state: AppState) -> impl View<Rgb565> {
|
||||
}
|
||||
|
||||
fn main_menu_indicator(
|
||||
indicator_type: MenuIndicatorType,
|
||||
app_state: AppState,
|
||||
indicator_type: MeasurementType,
|
||||
tendencies: Tendencies,
|
||||
sample: Sample,
|
||||
) -> impl View<Rgb565> {
|
||||
Rectangle
|
||||
.corner_radius(5)
|
||||
@ -55,8 +57,8 @@ fn main_menu_indicator(
|
||||
)),
|
||||
HStack::new((
|
||||
Spacer::default(),
|
||||
tendency_indicator(indicator_type.get_tendency(&app_state)),
|
||||
Text::new(indicator_type.get_value_str(&app_state), &PROFONT_24_POINT)
|
||||
tendency_indicator(indicator_type.get_tendency(tendencies)),
|
||||
Text::new(indicator_type.get_value_str(sample), &PROFONT_24_POINT)
|
||||
.foreground_color(MAIN_TEXT_COLOR),
|
||||
Text::new(
|
||||
indicator_type.get_corresponding_unit_string(),
|
||||
@ -76,7 +78,7 @@ fn main_menu_indicator(
|
||||
})
|
||||
}
|
||||
|
||||
fn tendency_indicator(tendency: Tendency) -> impl View<Rgb565> {
|
||||
pub fn tendency_indicator(tendency: Tendency) -> impl View<Rgb565> {
|
||||
HStack::new((
|
||||
Image::new(get_image!(tendency.get_corresponding_icon()))
|
||||
.flex_frame()
|
||||
|
||||
Reference in New Issue
Block a user