diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..1d36631
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "aht20-driver"]
+ path = aht20-driver
+ url = git@github.com:chalbin73/aht20-driver.git
diff --git a/Cargo.lock b/Cargo.lock
index 336a041..1c798c5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5,8 +5,6 @@ version = 4
[[package]]
name = "aht20-driver"
version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c920c092aef3156141a32df93663d683f06c733187f924b09d917efa53f08fb"
dependencies = [
"crc-any",
"embedded-hal 1.0.0",
@@ -74,6 +72,19 @@ version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
+[[package]]
+name = "buoyant"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbc36827a22794f33600a539fc2f16e6c535f9de5cf26509f8ce7ddb666cda1b"
+dependencies = [
+ "embedded-graphics",
+ "embedded-graphics-core",
+ "heapless 0.8.0",
+ "paste",
+ "u8g2-fonts",
+]
+
[[package]]
name = "bytemuck"
version = "1.24.0"
@@ -97,6 +108,7 @@ name = "co2-meter"
version = "0.1.0"
dependencies = [
"aht20-driver",
+ "buoyant",
"critical-section",
"defmt 1.0.1",
"embedded-graphics",
@@ -107,9 +119,11 @@ dependencies = [
"esp-bootloader-esp-idf",
"esp-hal",
"esp-println",
- "lcd-async",
+ "heapless 0.9.2",
+ "libm",
"mipidsi",
"profont",
+ "tinybmp",
]
[[package]]
@@ -950,24 +964,18 @@ dependencies = [
"syn 2.0.111",
]
-[[package]]
-name = "lcd-async"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "362f088c5c176ceedd4d0e817d17ae3b79480495deac605158a7cd86450450c5"
-dependencies = [
- "embedded-graphics",
- "embedded-graphics-core",
- "embedded-hal 1.0.0",
- "embedded-hal-async",
-]
-
[[package]]
name = "libc"
version = "0.2.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
+[[package]]
+name = "libm"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
+
[[package]]
name = "linked_list_allocator"
version = "0.10.5"
@@ -1441,6 +1449,15 @@ dependencies = [
"syn 2.0.111",
]
+[[package]]
+name = "tinybmp"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df43af2cb7b369009aa14144959bb4f2720ab62034c9073242f2d3a186c2edb6"
+dependencies = [
+ "embedded-graphics",
+]
+
[[package]]
name = "toml_datetime"
version = "0.7.3"
@@ -1477,6 +1494,16 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
+[[package]]
+name = "u8g2-fonts"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "825f57be1429fd60f335a4aade11e128a7ae4f89d75ca3a003cb8410a91093f7"
+dependencies = [
+ "embedded-graphics",
+ "embedded-graphics-core",
+]
+
[[package]]
name = "ufmt-write"
version = "0.1.0"
diff --git a/Cargo.toml b/Cargo.toml
index 49bac7d..54b5827 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -23,14 +23,19 @@ esp-backtrace = { version = "0.18.1", features = [
"panic-handler",
] }
esp-println = { version = "0.16.1", features = ["defmt-espflash", "esp32c3"] }
-aht20-driver = { version = "2.0", default-features = false }
+aht20-driver = { path = "aht20-driver" , default-features = false }
ens160 = { version = "0.6", default-features = false}
-embedded-hal-bus = "0.3.0"
+embedded-hal-bus = {version = "0.3.0", features = ["alloc"]}
mipidsi = "0.9.0"
embedded-graphics = "0.8.1"
-lcd-async = "0.1.1"
+
profont = "0.7.0"
+buoyant = "0.5.3"
+heapless = "0.9.2"
+tinybmp = "0.6.0"
+libm = "0.2.15"
+
[profile.dev]
diff --git a/aht20-driver b/aht20-driver
new file mode 160000
index 0000000..1bb4b02
--- /dev/null
+++ b/aht20-driver
@@ -0,0 +1 @@
+Subproject commit 1bb4b02145d266c0cdef91d3e2064dee4203d8fa
diff --git a/assets/Makefile b/assets/Makefile
new file mode 100644
index 0000000..c4b9027
--- /dev/null
+++ b/assets/Makefile
@@ -0,0 +1,18 @@
+SVGS = $(wildcard ./*.svg)
+TARGET_BMPS = $(SVGS:.svg=.bmp)
+BACKCOLOR = 080808
+
+.PHONY: all
+
+all: $(TARGET_BMPS)
+ @echo $(SVGS)
+
+clean:
+ rm $(TARGET_BMPS)
+
+%.bmp: %.png
+ @convert $< -background "#$(BACKCOLOR)" -alpha remove -define bmp:subtype=RGB565 $@
+
+%.png: %.svg
+ inkscape $< -o $@
+
diff --git a/assets/a.bmp b/assets/a.bmp
new file mode 100644
index 0000000..3141fb5
Binary files /dev/null and b/assets/a.bmp differ
diff --git a/assets/a.svg b/assets/a.svg
new file mode 100644
index 0000000..7d84e84
--- /dev/null
+++ b/assets/a.svg
@@ -0,0 +1,52 @@
+
+
diff --git a/assets/co2-icon.bmp b/assets/co2-icon.bmp
new file mode 100644
index 0000000..a74e213
Binary files /dev/null and b/assets/co2-icon.bmp differ
diff --git a/assets/co2-icon.svg b/assets/co2-icon.svg
new file mode 100644
index 0000000..391b8ec
--- /dev/null
+++ b/assets/co2-icon.svg
@@ -0,0 +1,89 @@
+
+
+
+
diff --git a/assets/humidity-icon.bmp b/assets/humidity-icon.bmp
new file mode 100644
index 0000000..1b72f13
Binary files /dev/null and b/assets/humidity-icon.bmp differ
diff --git a/assets/humidity-icon.svg b/assets/humidity-icon.svg
new file mode 100644
index 0000000..6ac5f37
--- /dev/null
+++ b/assets/humidity-icon.svg
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
diff --git a/assets/indic-falling.bmp b/assets/indic-falling.bmp
new file mode 100644
index 0000000..859d6ad
Binary files /dev/null and b/assets/indic-falling.bmp differ
diff --git a/assets/indic-falling.svg b/assets/indic-falling.svg
new file mode 100644
index 0000000..8d10fc3
--- /dev/null
+++ b/assets/indic-falling.svg
@@ -0,0 +1,94 @@
+
+
+
+
diff --git a/assets/indic-rising.bmp b/assets/indic-rising.bmp
new file mode 100644
index 0000000..bc67411
Binary files /dev/null and b/assets/indic-rising.bmp differ
diff --git a/assets/indic-rising.svg b/assets/indic-rising.svg
new file mode 100644
index 0000000..6a6818a
--- /dev/null
+++ b/assets/indic-rising.svg
@@ -0,0 +1,94 @@
+
+
+
+
diff --git a/assets/indic-steady.bmp b/assets/indic-steady.bmp
new file mode 100644
index 0000000..9f35aee
Binary files /dev/null and b/assets/indic-steady.bmp differ
diff --git a/assets/indic-steady.svg b/assets/indic-steady.svg
new file mode 100644
index 0000000..4ac30f3
--- /dev/null
+++ b/assets/indic-steady.svg
@@ -0,0 +1,94 @@
+
+
+
+
diff --git a/assets/temperature-icon.bmp b/assets/temperature-icon.bmp
new file mode 100644
index 0000000..67e6285
Binary files /dev/null and b/assets/temperature-icon.bmp differ
diff --git a/assets/temperature-icon.svg b/assets/temperature-icon.svg
new file mode 100644
index 0000000..2ada07b
--- /dev/null
+++ b/assets/temperature-icon.svg
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/assets/voc-icon.bmp b/assets/voc-icon.bmp
new file mode 100644
index 0000000..7ffb243
Binary files /dev/null and b/assets/voc-icon.bmp differ
diff --git a/assets/voc-icon.svg b/assets/voc-icon.svg
new file mode 100644
index 0000000..8aec3ee
--- /dev/null
+++ b/assets/voc-icon.svg
@@ -0,0 +1,46 @@
+
+
diff --git a/src/bin/colors.rs b/src/bin/colors.rs
new file mode 100644
index 0000000..f37e25d
--- /dev/null
+++ b/src/bin/colors.rs
@@ -0,0 +1,13 @@
+use embedded_graphics::pixelcolor::Rgb565;
+use embedded_graphics::prelude::RgbColor;
+use embedded_graphics::prelude::WebColors;
+
+pub const BACKGROUND_COLOR: Rgb565 = Rgb565::BLACK;
+//pub const FRAME_BACKGROUD_COLOR: Rgb565 = Rgb565::new(1, 2, 1);
+pub const FRAME_BACKGROUD_COLOR: Rgb565 = Rgb565::new(0, 0, 0);
+pub const FRAME_STROKE_COLOR: Rgb565 = Rgb565::new(4, 9, 4);
+
+pub const MAIN_TEXT_COLOR: Rgb565 = Rgb565::WHITE;
+pub const SUB_TEXT_COLOR: Rgb565 = Rgb565::CSS_DARK_GRAY;
+
+pub const FRAME_STROKE: u32 = 3;
diff --git a/src/bin/graph.rs b/src/bin/graph.rs
new file mode 100644
index 0000000..4af5b3a
--- /dev/null
+++ b/src/bin/graph.rs
@@ -0,0 +1,135 @@
+use core::iter::Iterator;
+use embedded_graphics::Pixel;
+use embedded_graphics::pixelcolor::Rgb565;
+use embedded_graphics::prelude::DrawTarget;
+use embedded_graphics::prelude::Drawable;
+use embedded_graphics::prelude::Point;
+use embedded_graphics::prelude::Primitive;
+use embedded_graphics::prelude::RgbColor;
+use embedded_graphics::prelude::Size;
+use embedded_graphics::primitives::Line;
+use embedded_graphics::primitives::PrimitiveStyle;
+
+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),
+];
+
+fn rgb565_interpolate(a: Rgb565, b: Rgb565, x: f32) -> Rgb565 {
+ Rgb565::new(
+ (a.r() as f32 * (1. - x)) as u8 + (b.r() as f32 * x) as u8,
+ (a.g() as f32 * (1. - x)) as u8 + (b.g() as f32 * x) as u8,
+ (a.b() as f32 * (1. - x)) as u8 + (b.b() as f32 * x) as u8,
+ )
+}
+
+fn color_lut(mut x: f32, colors: &[Rgb565]) -> Rgb565 {
+ if x == 1. {
+ return colors[colors.len() - 1];
+ }
+
+ if x == 0. {
+ return colors[0];
+ }
+
+ x *= (colors.len() - 1) as f32;
+
+ let index = libm::floorf(x);
+ let interp = x - index;
+
+ rgb565_interpolate(colors[index as usize], colors[index as usize + 1], interp)
+}
+
+pub fn graph_data>(data: &[f32], target: &mut T) {
+ let min = data.iter().copied().reduce(f32::min).unwrap_or(0.);
+ let max = data.iter().copied().reduce(f32::max).unwrap_or(0.);
+ data.iter().map(|x| x);
+ let size = Size::new(
+ target.bounding_box().size.width,
+ target.bounding_box().size.height,
+ );
+
+ let mut start = Point::new(
+ 0,
+ map_float(data[0], min, max, size.height as f32, 0.) as i32,
+ );
+ let point_count = size.width / 2;
+
+ for y in 0..size.height {
+ let value = map_float(y as f32, size.height as f32, 0., min, max);
+ let lut_color = color_lut(
+ map_float(y as f32, size.height as f32, 0., 0., 1.),
+ &DEFAULT_LUT,
+ );
+
+ let color = rgb565_interpolate(Rgb565::BLACK, lut_color, 0.3);
+ for x in 0..size.width {
+ let pos = map_float(
+ x as f32,
+ 0.,
+ size.width as f32 - 1.,
+ 0.,
+ (data.len() - 1) as f32,
+ );
+
+ // Sample
+ let index = libm::floorf(pos).min((data.len() - 2) as f32);
+ let interpolation = pos - index;
+ let curve_value = data[index as usize] * (1. - interpolation)
+ + data[index as usize + 1] * interpolation;
+ // if value <= curve_value && (x + y) % 2 == 0 {
+ // let _ = Pixel(Point::new(x as i32, y as i32), color).draw(target);
+ // }
+ if (x as i32 - y as i32) % 6 == 0 {
+ if value <= curve_value {
+ let mut pixel_color = Rgb565::new(1, 2, 1);
+ pixel_color = color;
+ let _ = Pixel(Point::new(x as i32 - 2, y as i32), pixel_color).draw(target);
+ }
+ }
+ }
+ }
+
+ for (i, x) in data.iter().skip(1).enumerate() {
+ let point = Point::new(
+ map_float(i as f32, 0., data.len() as f32 - 1., 0., size.width as f32) as i32,
+ map_float(*x, min, max, size.height as f32, 0.) as i32,
+ );
+ let factor = map_float(*x, min, max, 0., 1.);
+ let _ = Line::new(start, point)
+ .into_styled(PrimitiveStyle::with_stroke(
+ color_lut(factor, &DEFAULT_LUT),
+ 2,
+ ))
+ .draw(target);
+ start = point;
+ }
+
+ // for i in 0..point_count {
+ // // Sample data
+ // let index = map_float(
+ // i as f32,
+ // 0.,
+ // point_count as f32,
+ // 0 as f32,
+ // data.len() as f32,
+ // ) as usize;
+ // let value = data[index];
+ // let point = Point::new(
+ // map_float(i as f32, 0., point_count as f32, 0., size.width as f32) as i32,
+ // map_float(value, min, max, 0., size.height as f32) as i32,
+ // );
+ //
+ // let _ = Line::new(start, point)
+ // .into_styled(PrimitiveStyle::with_stroke(Rgb565::WHITE, 2))
+ // .draw(target);
+ // start = point;
+ // }
+}
diff --git a/src/bin/images.rs b/src/bin/images.rs
new file mode 100644
index 0000000..4e0785e
--- /dev/null
+++ b/src/bin/images.rs
@@ -0,0 +1,55 @@
+use core::cell::UnsafeCell;
+use core::include_bytes;
+use core::mem::MaybeUninit;
+
+use critical_section::Mutex;
+use embedded_graphics::pixelcolor::Rgb565;
+use tinybmp::Bmp;
+
+pub type StaticImage = Mutex>>>;
+
+#[rustfmt::skip]
+pub static HUMIDITY_ICON: StaticImage = Mutex::new(UnsafeCell::new(MaybeUninit::zeroed()));
+pub static TEMPERATURE_ICON: StaticImage = Mutex::new(UnsafeCell::new(MaybeUninit::zeroed()));
+pub static VOC_ICON: StaticImage = Mutex::new(UnsafeCell::new(MaybeUninit::zeroed()));
+pub static CO2_ICON: StaticImage = Mutex::new(UnsafeCell::new(MaybeUninit::zeroed()));
+
+// Tendency indicators
+pub static TENDENCY_RISING: StaticImage = Mutex::new(UnsafeCell::new(MaybeUninit::zeroed()));
+pub static TENDENCY_STEADY: StaticImage = Mutex::new(UnsafeCell::new(MaybeUninit::zeroed()));
+pub static TENDENCY_FALLING: StaticImage = Mutex::new(UnsafeCell::new(MaybeUninit::zeroed()));
+
+macro_rules! load_image {
+ ($stat:expr, $path:expr) => {
+ critical_section::with(|cs| {
+ *$stat.borrow(cs).get() =
+ MaybeUninit::new(Bmp::from_slice(include_bytes!($path)).unwrap());
+ })
+ };
+}
+
+#[macro_export]
+macro_rules! get_image {
+ ($image:expr) => {
+ unsafe {
+ use core::mem::MaybeUninit;
+ use tinybmp::Bmp;
+ &*core::mem::transmute::<*mut MaybeUninit>, *mut Bmp>(
+ critical_section::with(|cs| $image.borrow(cs).get()),
+ )
+ }
+ };
+}
+
+pub fn prepare_images() {
+ unsafe {
+ load_image!(HUMIDITY_ICON, "../../assets/humidity-icon.bmp");
+ load_image!(TEMPERATURE_ICON, "../../assets/temperature-icon.bmp");
+ load_image!(VOC_ICON, "../../assets/voc-icon.bmp");
+ load_image!(CO2_ICON, "../../assets/co2-icon.bmp");
+
+ load_image!(TENDENCY_RISING, "../../assets/indic-rising.bmp");
+ load_image!(TENDENCY_STEADY, "../../assets/indic-steady.bmp");
+ load_image!(TENDENCY_FALLING, "../../assets/indic-falling.bmp");
+ }
+}
diff --git a/src/bin/main.rs b/src/bin/main.rs
index 6695ffe..d756fc2 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -7,30 +7,51 @@
)]
#![allow(unreachable_code)]
+mod colors;
+mod graph;
+mod images;
+mod sampler;
+mod views;
+
+use buoyant::primitives::Size;
+use buoyant::view::AsDrawable;
+use buoyant::view::Image;
+use buoyant::view::ViewExt;
+use core::default::Default;
+use core::iter::Iterator;
use defmt::info;
use embedded_graphics::Drawable;
-use embedded_graphics::framebuffer::{Framebuffer, buffer_size};
-use embedded_graphics::image::{Image, ImageRaw};
-use embedded_graphics::pixelcolor::Rgb565;
-use embedded_graphics::pixelcolor::raw::{BigEndian, LittleEndian};
-use embedded_graphics::prelude::{Angle, DrawTarget, Point, Primitive, RgbColor, WebColors};
-use embedded_graphics::primitives::{
- Circle, PrimitiveStyle, PrimitiveStyleBuilder, Sector, StyledDrawable, Triangle,
-};
-use embedded_graphics::text::renderer::TextRenderer;
+use embedded_graphics::framebuffer::Framebuffer;
+use embedded_graphics::framebuffer::buffer_size;
+use embedded_graphics::image::ImageRaw;
+use embedded_graphics::pixelcolor::raw::LittleEndian;
+use embedded_graphics::prelude::Point;
+use embedded_graphics::prelude::RgbColor;
use embedded_hal_bus::spi::ExclusiveDevice;
use esp_hal::clock::CpuClock;
use esp_hal::delay::Delay;
-use esp_hal::gpio::{Level, Output, OutputConfig};
-use esp_hal::main;
-use esp_hal::riscv::asm::delay;
-use esp_hal::rtc_cntl::Rtc;
-use esp_hal::time::{Duration, Instant, Rate};
-use esp_hal::timer::Timer;
-use esp_hal::timer::timg::TimerGroup;
-use mipidsi::interface::{Interface, SpiInterface};
+use esp_hal::gpio::Level;
+use esp_hal::gpio::Output;
+use esp_hal::gpio::OutputConfig;
+use esp_hal::time::Rate;
+use heapless::format;
+use mipidsi::interface::SpiInterface;
use mipidsi::models::ST7789;
-use {esp_backtrace as _, esp_println as _};
+use mipidsi::options::Orientation;
+use mipidsi::options::Rotation;
+
+use buoyant::view::HStack;
+use buoyant::view::Spacer;
+use buoyant::view::View;
+use core::env;
+use embedded_graphics::pixelcolor::Rgb565;
+use esp_backtrace as _;
+use esp_hal::main;
+use esp_println as _;
+use tinybmp::Bmp;
+
+use crate::graph::graph_data;
+use crate::images::StaticImage;
extern crate alloc;
@@ -41,6 +62,7 @@ esp_bootloader_esp_idf::esp_app_desc!();
#[main]
fn main() -> ! {
// generator version: 1.0.1
+ images::prepare_images();
esp_alloc::heap_allocator!(size: 32 * 1024);
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
@@ -81,125 +103,97 @@ fn main() -> ! {
.init(&mut timer)
.unwrap();
- display
- .set_tearing_effect(mipidsi::options::TearingEffect::Vertical)
- .unwrap();
+ let mut fb =
+ Framebuffer::(240, 240) }>::new(
+ );
+ //fb.clear(Rgb565::BLACK).unwrap();
- // Create styles used by the drawing operations.
- let sector_style = PrimitiveStyleBuilder::new()
- .stroke_color(Rgb565::BLACK)
- .stroke_width(2)
- .fill_color(Rgb565::YELLOW)
- .build();
- let eye_style = PrimitiveStyleBuilder::new()
- .stroke_color(Rgb565::BLACK)
- .stroke_width(1)
- .fill_color(Rgb565::BLACK)
- .build();
-
- //let output_settings = ::new().scale(4).build();
- //let mut window = Window::new("Pacman", &output_settings);
-
- // The current progress of the animation
- const STEPS: i32 = 10;
- let mut progress: i32 = 0;
- let delay = Delay::new();
-
- loop {
- let mut fb = Framebuffer::<
- Rgb565,
- _,
- LittleEndian,
- 320,
- 240,
- { buffer_size::(320, 240) },
- >::new();
- fb.clear(Rgb565::WHITE).unwrap();
-
- let p = (progress - STEPS).abs();
-
- // Draw a Sector as the main Pacman feature.
- let _ = Sector::new(
- Point::new(2, 2),
- 61,
- Angle::from_degrees((p * 30 / STEPS) as f32),
- Angle::from_degrees((360 - 2 * p * 30 / STEPS) as f32),
- )
- .draw_styled(§or_style, &mut fb);
-
- // Draw a Circle as the eye.
- let _ = Circle::new(Point::new(36, 16), 5).draw_styled(&eye_style, &mut fb);
-
- delay.delay_millis(50);
-
- progress = (progress + 1) % (2 * STEPS + 1);
-
- let img_raw = ImageRaw::::new(fb.data(), 320);
- let image = Image::new(&img_raw, Point::zero());
- image.draw(&mut display).unwrap();
- }
-
- // display
- // .set_pixels(0, 0, 50, 50, core::iter::repeat(Rgb565::BLUE))
+ // views::menu::menu_view()
+ // .as_drawable(Size::new(240, 240), Rgb565::WHITE)
+ // .draw(&mut fb)
// .unwrap();
- // //display.set_pixel(10, 10, Rgb565::WHITE).unwrap();
- // let i2c = I2c::new(peripherals.I2C0, Config::default())
- // .unwrap()
- // .with_sda(peripherals.GPIO8)
- // .with_scl(peripherals.GPIO9);
- //
- // esp_alloc::heap_allocator!(#[esp_hal::ram(reclaimed)] size: 66320);
- //
- // let rc = RefCell::new(i2c);
- //
- // let mut device = Ens160::new(embedded_hal_bus::i2c::RefCellDevice::new(&rc), 0x53);
- // timer.delay_millis(500);
- // device.reset().unwrap();
- // info!("Reset device");
- // timer.delay_millis(500);
- // device.operational().unwrap();
- // info!("Device operational");
- //
- // // Configure the AHT20 temperature and humidity sensor.
- // let mut aht20_uninit = aht20_driver::AHT20::new(
- // embedded_hal_bus::i2c::RefCellDevice::new(&rc),
- // aht20_driver::SENSOR_ADDRESS,
- // );
- // let mut aht20 = aht20_uninit.init(&mut timer).unwrap();
- //
- // // Take the temperature and humidity measurement.
- //
- // loop {
- // //timer.delay_millis(5000);
- // if let Ok(status) = device.status() {
- // if status.data_is_ready() {
- // let aht20_measurement = aht20.measure(&mut timer).unwrap();
- // //println!("temperature (aht20): {}C", aht20_measurement.temperature);
- // //println!("humidity (aht20): {}%", aht20_measurement.humidity);
- //
- // let tvoc = device.tvoc().unwrap();
- // let eco2 = device.eco2().unwrap();
- // //info!("eco2: {}, tvoc: {}", *eco2, tvoc);
- // print!(
- // "{}, {}, {}, {}\n",
- // *eco2, tvoc, aht20_measurement.temperature, aht20_measurement.humidity
- // );
- // // from eCO2
- // // directly
- // //let air_quality_index = device.air_quality_index().unwrap();
- // }
- // }
- // }
- //
- //
+ let mut x = [0.; 100];
+ for (i, x) in x.iter_mut().enumerate() {
+ *x = libm::sinf(i as f32 / 10.);
+ }
+ graph_data(&x, &mut fb);
+
+ let img_raw = ImageRaw::::new(fb.data(), 240);
+ let image = embedded_graphics::image::Image::new(&img_raw, Point::zero());
+ image.draw(&mut display).unwrap();
+ // embedded_graphics::image::Image::new(get_image!(images::HUMIDITY_ICON), Point::zero())
+ // .draw(&mut display)
+ // .unwrap();
+
+ info!("Finished !");
loop {
- info!("Hello world!");
- let delay_start = Instant::now();
- while delay_start.elapsed() < Duration::from_millis(500) {}
+ core::hint::spin_loop();
}
// 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
}
-fn draw_interface(target: &mut T) {}
+pub enum MenuIndicatorType {
+ Temperature(f32),
+ Humidity(f32),
+ Co2(u32),
+ Voc(u32),
+}
+
+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,
+ }
+ }
+
+ pub fn get_corresponding_unit_string(&self) -> &'static str {
+ match self {
+ MenuIndicatorType::Temperature(_) => "C",
+ MenuIndicatorType::Humidity(_) => "%",
+ MenuIndicatorType::Co2(_) => "ppm",
+ MenuIndicatorType::Voc(_) => "ppb",
+ }
+ }
+
+ pub fn get_value_str(&self) -> 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(),
+ }
+ }
+}
+
+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,
+ }
+ }
+}
+
+fn tendency_indicator(tendency: Tendency) -> impl View {
+ HStack::new((
+ Image::new(get_image!(tendency.get_corresponding_icon()))
+ .flex_frame()
+ .with_min_size(10, 20)
+ .with_max_size(10, 20),
+ Spacer::default(),
+ ))
+ .flex_frame()
+ .with_max_width(15)
+}
diff --git a/src/bin/sampler.rs b/src/bin/sampler.rs
new file mode 100644
index 0000000..f4426bb
--- /dev/null
+++ b/src/bin/sampler.rs
@@ -0,0 +1,53 @@
+use core::cell::RefCell;
+
+use aht20_driver::AHT20;
+use alloc::rc::Rc;
+use alloc::vec::Vec;
+use core::default::Default;
+use embedded_hal_bus::i2c::RcDevice;
+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;
+
+pub struct Sampler<'a> {
+ ens160: Ens160>>,
+ aht20: aht20_driver::AHT20Initialized>>,
+}
+
+impl<'a> Sampler<'a> {
+ pub fn new(
+ i2c: impl Instance + 'a,
+ sda: impl PeripheralOutput<'a>,
+ scl: impl PeripheralOutput<'a>,
+ mut timer: Delay,
+ ) -> Self {
+ let i2c = I2c::new(i2c, Default::default())
+ .unwrap()
+ .with_sda(sda)
+ .with_scl(scl);
+
+ let i2c = Rc::new(RefCell::new(i2c));
+
+ let mut ens160 = Ens160::new(embedded_hal_bus::i2c::RcDevice::new(i2c.clone()), 0x53);
+ timer.delay_millis(500);
+ ens160.reset().unwrap();
+ timer.delay_millis(500);
+ ens160.operational().unwrap();
+
+ let aht20_uninit = AHT20::new(
+ embedded_hal_bus::i2c::RcDevice::new(i2c.clone()),
+ aht20_driver::SENSOR_ADDRESS,
+ );
+
+ let aht20 = aht20_uninit.init(&mut timer).unwrap();
+
+ Sampler { ens160, aht20 }
+ }
+}
diff --git a/src/bin/views.rs b/src/bin/views.rs
new file mode 100644
index 0000000..38dde75
--- /dev/null
+++ b/src/bin/views.rs
@@ -0,0 +1,3 @@
+pub mod detail;
+pub mod icon;
+pub mod menu;
diff --git a/src/bin/views/detail.rs b/src/bin/views/detail.rs
new file mode 100644
index 0000000..0bcf0ff
--- /dev/null
+++ b/src/bin/views/detail.rs
@@ -0,0 +1,52 @@
+use buoyant::layout::HorizontalAlignment;
+use buoyant::layout::VerticalAlignment;
+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::pixelcolor::Rgb565;
+use profont::PROFONT_18_POINT;
+use profont::PROFONT_24_POINT;
+
+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::tendency_indicator;
+use crate::views::icon::icon_box_view;
+
+pub fn detailed_view(indicator: MenuIndicatorType, tendency: Tendency) -> impl View {
+ 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)
+ .foreground_color(MAIN_TEXT_COLOR),
+ Text::new(indicator.get_corresponding_unit_string(), &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(),
+ 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),
+ )),
+ // Window
+ Spacer::default()
+ .flex_frame()
+ .with_infinite_max_width()
+ .with_infinite_max_height(),
+ ))
+ .with_alignment(HorizontalAlignment::Leading)
+}
diff --git a/src/bin/views/icon.rs b/src/bin/views/icon.rs
new file mode 100644
index 0000000..8d0f5c1
--- /dev/null
+++ b/src/bin/views/icon.rs
@@ -0,0 +1,18 @@
+use buoyant::view::View;
+use buoyant::view::ViewExt;
+use buoyant::view::ZStack;
+use buoyant::view::shape::Rectangle;
+use embedded_graphics::pixelcolor::Rgb565;
+
+use crate::get_image;
+use crate::images::StaticImage;
+
+pub fn icon_box_view(box_color: Rgb565, icon: &'static StaticImage) -> impl View {
+ ZStack::new((
+ Rectangle.corner_radius(10).foreground_color(box_color),
+ buoyant::view::Image::new(get_image!(icon)),
+ ))
+ .flex_frame()
+ .with_max_size(53, 53)
+ .with_min_size(53, 53)
+}
diff --git a/src/bin/views/menu.rs b/src/bin/views/menu.rs
new file mode 100644
index 0000000..23d7a7a
--- /dev/null
+++ b/src/bin/views/menu.rs
@@ -0,0 +1,85 @@
+use buoyant::view::VStack;
+use buoyant::view::View;
+
+use buoyant::view::prelude::*;
+use embedded_graphics::pixelcolor::Rgb565;
+use embedded_graphics::prelude::*;
+use profont::PROFONT_18_POINT;
+use profont::PROFONT_24_POINT;
+
+use crate::MenuIndicatorType;
+use crate::Tendency;
+use crate::colors::FRAME_BACKGROUD_COLOR;
+use crate::colors::FRAME_STROKE;
+use crate::colors::FRAME_STROKE_COLOR;
+use crate::colors::MAIN_TEXT_COLOR;
+use crate::colors::SUB_TEXT_COLOR;
+use crate::get_image;
+use crate::views::icon::icon_box_view;
+
+pub fn menu_view() -> impl View {
+ VStack::new((
+ HStack::new((
+ main_menu_indicator(MenuIndicatorType::Temperature(31.5), Tendency::Falling),
+ main_menu_indicator(MenuIndicatorType::Humidity(36.2), Tendency::Steady),
+ ))
+ .with_spacing(5),
+ HStack::new((
+ main_menu_indicator(MenuIndicatorType::Co2(1329), Tendency::Rising),
+ main_menu_indicator(MenuIndicatorType::Voc(29), Tendency::Falling),
+ ))
+ .with_spacing(5),
+ ))
+ .with_spacing(5)
+}
+
+fn main_menu_indicator(indicator_type: MenuIndicatorType, tendency: Tendency) -> impl View {
+ Rectangle
+ .corner_radius(10)
+ .stroked(FRAME_STROKE)
+ .foreground_color(FRAME_STROKE_COLOR)
+ .background(Alignment::Center, || {
+ ZStack::new((
+ Rectangle
+ .corner_radius(15)
+ .foreground_color(FRAME_BACKGROUD_COLOR),
+ VStack::new((
+ HStack::new((
+ Spacer::default(),
+ icon_box_view(FRAME_STROKE_COLOR, indicator_type.get_corresponding_icon()),
+ Spacer::default(),
+ )),
+ HStack::new((
+ Spacer::default(),
+ tendency_indicator(tendency),
+ Text::new(indicator_type.get_value_str(), &PROFONT_24_POINT)
+ .foreground_color(MAIN_TEXT_COLOR),
+ Text::new(
+ indicator_type.get_corresponding_unit_string(),
+ &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(),
+ )),
+ ))
+ .with_alignment(HorizontalAlignment::Center)
+ .flex_frame(),
+ ))
+ })
+}
+
+fn tendency_indicator(tendency: Tendency) -> impl View {
+ HStack::new((
+ Image::new(get_image!(tendency.get_corresponding_icon()))
+ .flex_frame()
+ .with_min_size(10, 20)
+ .with_max_size(10, 20),
+ Spacer::default(),
+ ))
+ .flex_frame()
+ .with_max_width(15)
+}