Improve line graph drawing function & begin input handling
This commit is contained in:
parent
219e43fee8
commit
3255976961
@ -1,11 +1,3 @@
|
|||||||
[alias]
|
|
||||||
|
|
||||||
build-arm = "build --target=thumbv8m.main-none-eabihf"
|
|
||||||
build-riscv = "build --target=riscv32imac-unknown-none-elf"
|
|
||||||
|
|
||||||
run-arm = "run --target=thumbv8m.main-none-eabihf"
|
|
||||||
run-riscv = "run --target=riscv32imac-unknown-none-elf"
|
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv8m.main-none-eabihf"
|
target = "thumbv8m.main-none-eabihf"
|
||||||
|
|
||||||
@ -18,10 +10,8 @@ rustflags = [
|
|||||||
"-C",
|
"-C",
|
||||||
"target-cpu=cortex-m33",
|
"target-cpu=cortex-m33",
|
||||||
]
|
]
|
||||||
|
runner = "probe-rs run --chip RP235x"
|
||||||
runner = "picotool load -u -v -x -t elf"
|
|
||||||
|
|
||||||
[target.riscv32imac-unknown-none-elf]
|
[target.riscv32imac-unknown-none-elf]
|
||||||
rustflags = ["-C", "link-arg=--nmagic", "-C", "link-arg=-Trp235x_riscv.x"]
|
rustflags = ["-C", "link-arg=--nmagic", "-C", "link-arg=-Trp235x_riscv.x"]
|
||||||
|
runner = "probe-rs run --chip RP235x"
|
||||||
runner = "picotool load -u -v -x -t elf"
|
|
||||||
|
20
Cargo.lock
generated
20
Cargo.lock
generated
@ -703,15 +703,6 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itertools"
|
|
||||||
version = "0.14.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lalrpop"
|
name = "lalrpop"
|
||||||
version = "0.19.12"
|
version = "0.19.12"
|
||||||
@ -723,7 +714,7 @@ dependencies = [
|
|||||||
"diff",
|
"diff",
|
||||||
"ena",
|
"ena",
|
||||||
"is-terminal",
|
"is-terminal",
|
||||||
"itertools 0.10.5",
|
"itertools",
|
||||||
"lalrpop-util",
|
"lalrpop-util",
|
||||||
"petgraph",
|
"petgraph",
|
||||||
"regex",
|
"regex",
|
||||||
@ -896,18 +887,20 @@ name = "pico-enviro-sensor"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"circular-buffer",
|
"circular-buffer",
|
||||||
|
"cortex-m",
|
||||||
"cortex-m-rt",
|
"cortex-m-rt",
|
||||||
|
"critical-section",
|
||||||
"display-interface-spi",
|
"display-interface-spi",
|
||||||
"embassy-embedded-hal",
|
"embassy-embedded-hal",
|
||||||
"embassy-executor",
|
"embassy-executor",
|
||||||
|
"embassy-futures",
|
||||||
"embassy-rp",
|
"embassy-rp",
|
||||||
"embassy-sync",
|
"embassy-sync",
|
||||||
"embassy-time",
|
"embassy-time",
|
||||||
"embedded-graphics",
|
"embedded-graphics",
|
||||||
"embedded-graphics-framebuf",
|
"embedded-graphics-framebuf",
|
||||||
|
"fixed",
|
||||||
"heapless",
|
"heapless",
|
||||||
"itertools 0.14.0",
|
|
||||||
"num-traits",
|
|
||||||
"rtt-target",
|
"rtt-target",
|
||||||
"scd4x",
|
"scd4x",
|
||||||
"ssd1351",
|
"ssd1351",
|
||||||
@ -1144,7 +1137,8 @@ checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "scd4x"
|
name = "scd4x"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "git+https://github.com/twokilohertz/scd4x-rs.git?branch=conversion-fixes#07730523c51d6909530c000de271832125760385"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "550fedc97e7880654aa7dc840f5eb3b2201a01aae566c4584a3601bb0086cd4c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"embedded-hal 1.0.0",
|
"embedded-hal 1.0.0",
|
||||||
"log",
|
"log",
|
||||||
|
10
Cargo.toml
10
Cargo.toml
@ -22,12 +22,14 @@ embassy-executor = { version = "0.7.0", features = [
|
|||||||
"task-arena-size-65536",
|
"task-arena-size-65536",
|
||||||
] }
|
] }
|
||||||
embassy-time = "0.4.0"
|
embassy-time = "0.4.0"
|
||||||
embassy-sync = "0.6.2"
|
|
||||||
embassy-embedded-hal = "0.3.0"
|
embassy-embedded-hal = "0.3.0"
|
||||||
static_cell = "2.1.0"
|
embassy-futures = "0.1.1"
|
||||||
|
embassy-sync = "0.6.2"
|
||||||
|
|
||||||
# System
|
# System
|
||||||
cortex-m-rt = "0.7.5"
|
cortex-m-rt = "0.7.5"
|
||||||
|
cortex-m = "0.7.7"
|
||||||
|
critical-section = "1.2.0"
|
||||||
rtt-target = "0.6.1"
|
rtt-target = "0.6.1"
|
||||||
|
|
||||||
# embedded-graphics
|
# embedded-graphics
|
||||||
@ -40,10 +42,10 @@ ssd1351 = "0.5.0"
|
|||||||
display-interface-spi = "0.5.0"
|
display-interface-spi = "0.5.0"
|
||||||
|
|
||||||
# Extra
|
# Extra
|
||||||
|
static_cell = "2.1.0"
|
||||||
heapless = "0.8.0"
|
heapless = "0.8.0"
|
||||||
circular-buffer = { version = "1.0.0", default-features = false }
|
circular-buffer = { version = "1.0.0", default-features = false }
|
||||||
itertools = { version = "0.14.0", default-features = false }
|
fixed = "1.29.0"
|
||||||
num-traits = { version = "0.2.19", default-features = false }
|
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = "s"
|
opt-level = "s"
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
export PICO_BOARD=pico2_w
|
export PICO_BOARD=pico2_w
|
||||||
export PICO_PLATFORM=rp2350-arm-s
|
export PICO_PLATFORM=rp2350-arm-s
|
||||||
export PICO_SDK_PATH=~/devel/clones/pico-sdk/
|
export PICO_SDK_PATH=~/devel/clones/pico-sdk
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
// System
|
// System
|
||||||
use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig;
|
use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig;
|
||||||
use embassy_rp::{gpio::Output, spi::Config};
|
use embassy_rp::{gpio::Output, spi::Config};
|
||||||
use embedded_graphics_framebuf::FrameBuf;
|
use embedded_graphics_framebuf::FrameBuf;
|
||||||
use rtt_target::rprintln;
|
use fixed::types::U16F16;
|
||||||
|
use rtt_target::debug_rprintln;
|
||||||
|
|
||||||
// Display
|
// Display
|
||||||
use display_interface_spi::SPIInterface;
|
use display_interface_spi::SPIInterface;
|
||||||
@ -16,9 +19,9 @@ use ssd1351::{
|
|||||||
use embedded_graphics::{
|
use embedded_graphics::{
|
||||||
draw_target::DrawTarget,
|
draw_target::DrawTarget,
|
||||||
mono_font::{ascii::FONT_6X10, MonoTextStyle},
|
mono_font::{ascii::FONT_6X10, MonoTextStyle},
|
||||||
pixelcolor::Rgb565,
|
pixelcolor::{Rgb565, Rgb888},
|
||||||
prelude::{Point, Primitive, RgbColor, Size, WebColors},
|
prelude::{Point, Primitive, RgbColor, Size, WebColors},
|
||||||
primitives::{Line, PrimitiveStyle, PrimitiveStyleBuilder, Rectangle},
|
primitives::{Line, PrimitiveStyle, Rectangle, StyledDrawable},
|
||||||
text::{Alignment, Text, TextStyleBuilder},
|
text::{Alignment, Text, TextStyleBuilder},
|
||||||
Drawable,
|
Drawable,
|
||||||
};
|
};
|
||||||
@ -26,15 +29,13 @@ use embedded_graphics::{
|
|||||||
// Containers
|
// Containers
|
||||||
use circular_buffer::CircularBuffer;
|
use circular_buffer::CircularBuffer;
|
||||||
use heapless::String;
|
use heapless::String;
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use core::fmt::Write;
|
|
||||||
use num_traits::{Num, NumCast};
|
|
||||||
|
|
||||||
use crate::{Spi0BusMutex, SENSOR_DATA_SIGNAL};
|
use crate::{Spi0BusMutex, SENSOR_DATA_SIGNAL};
|
||||||
|
|
||||||
const DISPLAY_WIDTH: usize = 128;
|
const DISPLAY_WIDTH: usize = 128;
|
||||||
const DISPLAY_HEIGHT: usize = 128;
|
const DISPLAY_HEIGHT: usize = 128;
|
||||||
|
const DISPLAY_PADDING: usize = 5;
|
||||||
|
type SensorDataBuffer = CircularBuffer<60, U16F16>;
|
||||||
|
|
||||||
/// Output to the SSD1351 display
|
/// Output to the SSD1351 display
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
@ -45,7 +46,7 @@ pub async fn display_output_task(
|
|||||||
rst: &'static mut Output<'static>,
|
rst: &'static mut Output<'static>,
|
||||||
spi_config: Config,
|
spi_config: Config,
|
||||||
) {
|
) {
|
||||||
rprintln!("Display output task started");
|
debug_rprintln!("Display output task started");
|
||||||
|
|
||||||
let spi_dev = SpiDeviceWithConfig::new(spi_bus, cs, spi_config);
|
let spi_dev = SpiDeviceWithConfig::new(spi_bus, cs, spi_config);
|
||||||
let interface = SPIInterface::new(spi_dev, dc);
|
let interface = SPIInterface::new(spi_dev, dc);
|
||||||
@ -73,14 +74,14 @@ pub async fn display_output_task(
|
|||||||
let humidity_text_style = MonoTextStyle::new(&FONT_6X10, Rgb565::CSS_AQUAMARINE);
|
let humidity_text_style = MonoTextStyle::new(&FONT_6X10, Rgb565::CSS_AQUAMARINE);
|
||||||
|
|
||||||
// Format string buffers
|
// Format string buffers
|
||||||
let mut co2_text_buf = String::<16>::new();
|
let mut co2_text_buf = String::<20>::new();
|
||||||
let mut temp_text_buf = String::<16>::new();
|
let mut temp_text_buf = String::<20>::new();
|
||||||
let mut humidity_text_buf = String::<16>::new();
|
let mut humidity_text_buf = String::<20>::new();
|
||||||
|
|
||||||
// Ring buffer for storing past measurement data
|
// Ring buffer for storing past measurement data
|
||||||
let mut co2_samples = CircularBuffer::<60, u16>::new();
|
let mut co2_samples = SensorDataBuffer::new();
|
||||||
let mut temp_samples = CircularBuffer::<60, f32>::new();
|
let mut temp_samples = SensorDataBuffer::new();
|
||||||
let mut humidity_samples = CircularBuffer::<60, f32>::new();
|
let mut humidity_samples = SensorDataBuffer::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Clear the framebuffer
|
// Clear the framebuffer
|
||||||
@ -98,28 +99,19 @@ pub async fn display_output_task(
|
|||||||
write!(&mut humidity_text_buf, "RH: {:.1} %", sensor_data.humidity).unwrap();
|
write!(&mut humidity_text_buf, "RH: {:.1} %", sensor_data.humidity).unwrap();
|
||||||
|
|
||||||
// Record samples
|
// Record samples
|
||||||
co2_samples.push_back(sensor_data.co2);
|
co2_samples.push_back(U16F16::from_num(sensor_data.co2));
|
||||||
temp_samples.push_back(sensor_data.temperature);
|
temp_samples.push_back(U16F16::from_num(sensor_data.temperature));
|
||||||
humidity_samples.push_back(sensor_data.humidity);
|
humidity_samples.push_back(U16F16::from_num(sensor_data.humidity));
|
||||||
|
co2_samples.make_contiguous();
|
||||||
|
temp_samples.make_contiguous();
|
||||||
|
humidity_samples.make_contiguous();
|
||||||
|
|
||||||
let co2_min = *co2_samples.iter().min().unwrap();
|
let co2_min = *co2_samples.iter().min().unwrap();
|
||||||
let co2_max = *co2_samples.iter().max().unwrap();
|
let co2_max = *co2_samples.iter().max().unwrap();
|
||||||
let temp_min = *temp_samples
|
let temp_min = *temp_samples.iter().min().unwrap();
|
||||||
.iter()
|
let temp_max = *temp_samples.iter().max().unwrap();
|
||||||
.reduce(|a: &f32, b: &f32| if a.le(b) { a } else { b })
|
let humid_min = *humidity_samples.iter().min().unwrap();
|
||||||
.unwrap();
|
let humid_max = *humidity_samples.iter().max().unwrap();
|
||||||
let temp_max = *temp_samples
|
|
||||||
.iter()
|
|
||||||
.reduce(|a: &f32, b: &f32| if a.ge(b) { a } else { b })
|
|
||||||
.unwrap();
|
|
||||||
let humid_min = *humidity_samples
|
|
||||||
.iter()
|
|
||||||
.reduce(|a: &f32, b: &f32| if a.le(b) { a } else { b })
|
|
||||||
.unwrap();
|
|
||||||
let humid_max = *humidity_samples
|
|
||||||
.iter()
|
|
||||||
.reduce(|a: &f32, b: &f32| if a.ge(b) { a } else { b })
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Note about drawing positions:
|
Note about drawing positions:
|
||||||
@ -130,44 +122,53 @@ pub async fn display_output_task(
|
|||||||
|
|
||||||
// Draw line graphs
|
// Draw line graphs
|
||||||
|
|
||||||
if co2_samples.len() >= 2 {
|
const LINE_GRAPH_WIDTH: u32 = (DISPLAY_WIDTH - (DISPLAY_PADDING * 2)) as u32;
|
||||||
draw_line_graph(
|
const LINE_GRAPH_HEIGHT: u32 = 36;
|
||||||
&co2_samples,
|
|
||||||
co2_max.into(),
|
|
||||||
co2_min.into(),
|
|
||||||
4,
|
|
||||||
Rgb565::CSS_DARK_GREEN,
|
|
||||||
Some(Rgb565::new(3, 5, 3)),
|
|
||||||
&mut framebuf,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if temp_samples.len() >= 2 {
|
draw_line_graph(
|
||||||
draw_line_graph(
|
Rectangle::new(
|
||||||
&temp_samples,
|
Point::new(DISPLAY_PADDING as i32, DISPLAY_PADDING as i32),
|
||||||
temp_max as i32,
|
Size::new(LINE_GRAPH_WIDTH, LINE_GRAPH_HEIGHT),
|
||||||
temp_min as i32,
|
),
|
||||||
38,
|
co2_min,
|
||||||
Rgb565::CSS_ORANGE,
|
co2_max,
|
||||||
Some(Rgb565::new(3, 5, 3)),
|
co2_samples.as_slices().0,
|
||||||
&mut framebuf,
|
Rgb565::CSS_DARK_GREEN,
|
||||||
)
|
Some(Rgb888::new(24, 24, 24).into()),
|
||||||
.unwrap();
|
&mut framebuf,
|
||||||
}
|
);
|
||||||
|
|
||||||
if humidity_samples.len() >= 2 {
|
draw_line_graph(
|
||||||
draw_line_graph(
|
Rectangle::new(
|
||||||
&humidity_samples,
|
Point::new(
|
||||||
humid_max as i32,
|
DISPLAY_PADDING as i32,
|
||||||
humid_min as i32,
|
(LINE_GRAPH_HEIGHT + (DISPLAY_PADDING as u32 * 2)) as i32,
|
||||||
72,
|
),
|
||||||
Rgb565::CSS_AQUA,
|
Size::new(LINE_GRAPH_WIDTH, LINE_GRAPH_HEIGHT),
|
||||||
Some(Rgb565::new(3, 5, 3)),
|
),
|
||||||
&mut framebuf,
|
temp_min,
|
||||||
)
|
temp_max,
|
||||||
.unwrap();
|
temp_samples.as_slices().0,
|
||||||
}
|
Rgb565::CSS_ORANGE,
|
||||||
|
Some(Rgb888::new(24, 24, 24).into()),
|
||||||
|
&mut framebuf,
|
||||||
|
);
|
||||||
|
|
||||||
|
draw_line_graph(
|
||||||
|
Rectangle::new(
|
||||||
|
Point::new(
|
||||||
|
DISPLAY_PADDING as i32,
|
||||||
|
((LINE_GRAPH_HEIGHT * 2) + (DISPLAY_PADDING as u32 * 3)) as i32,
|
||||||
|
),
|
||||||
|
Size::new(LINE_GRAPH_WIDTH, LINE_GRAPH_HEIGHT),
|
||||||
|
),
|
||||||
|
humid_min,
|
||||||
|
humid_max,
|
||||||
|
humidity_samples.as_slices().0,
|
||||||
|
Rgb565::CSS_AQUA,
|
||||||
|
Some(Rgb888::new(24, 24, 24).into()),
|
||||||
|
&mut framebuf,
|
||||||
|
);
|
||||||
|
|
||||||
// Draw the text to the screen
|
// Draw the text to the screen
|
||||||
|
|
||||||
@ -206,62 +207,61 @@ pub async fn display_output_task(
|
|||||||
);
|
);
|
||||||
display.fill_contiguous(&area, framebuf.data).unwrap();
|
display.fill_contiguous(&area, framebuf.data).unwrap();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn draw_line_graph<'a, I, V, D, C>(
|
|
||||||
collection: I,
|
fn draw_line_graph<C, D>(
|
||||||
graph_max: i32,
|
bounds: Rectangle,
|
||||||
graph_min: i32,
|
y_min: U16F16,
|
||||||
y_start: i32,
|
y_max: U16F16,
|
||||||
line_colour: C,
|
samples: &[U16F16],
|
||||||
back_colour: Option<C>,
|
fg_colour: C,
|
||||||
target: &mut D,
|
bg_colour: Option<C>,
|
||||||
) -> Result<(), D::Error>
|
target: &mut D,
|
||||||
where
|
) where
|
||||||
I: IntoIterator<Item = &'a V>,
|
C: RgbColor,
|
||||||
I::IntoIter: DoubleEndedIterator,
|
D: DrawTarget<Color = C>,
|
||||||
V: ?Sized + 'a + Num + NumCast + Clone,
|
{
|
||||||
C: RgbColor,
|
// Draw background colour first, if supplied
|
||||||
D: DrawTarget<Color = C>,
|
|
||||||
{
|
if let Some(bg_col) = bg_colour {
|
||||||
let mut x_pos: i32 = (DISPLAY_WIDTH - 5) as i32;
|
let _ = bounds.draw_styled(&PrimitiveStyle::with_fill(bg_col), target);
|
||||||
|
}
|
||||||
match back_colour {
|
|
||||||
Some(c) => {
|
// Draw the data points
|
||||||
let style = PrimitiveStyleBuilder::new().fill_color(c).build();
|
|
||||||
|
if samples.len() < 2 {
|
||||||
Rectangle::new(Point::new(4, y_start), Size::new(120, 30))
|
// Drawing a line requires a minimum of two points
|
||||||
.into_styled(style)
|
return;
|
||||||
.draw(target)?;
|
}
|
||||||
}
|
|
||||||
None => {}
|
let graph_width = U16F16::from_num(bounds.size.width);
|
||||||
}
|
let graph_height = U16F16::from_num(bounds.size.height);
|
||||||
|
let y_range = y_max - y_min;
|
||||||
for (a, b) in collection.into_iter().rev().tuple_windows::<(_, _)>() {
|
let mut x_offset: i32 = bounds.top_left.x;
|
||||||
let range: i32 = if (graph_max - graph_min) == 0 {
|
let n_samples: U16F16 = U16F16::from_num(samples.len());
|
||||||
1_i32
|
let mut n_samples_seen: U16F16 = U16F16::from_num(1);
|
||||||
} else {
|
|
||||||
graph_max - graph_min
|
for sample in samples.windows(2) {
|
||||||
};
|
let x_pos_start: i32 = x_offset;
|
||||||
|
let y_pos_start: i32 = bounds.top_left.y
|
||||||
let a_i32: i32 = match NumCast::from(a.clone()) {
|
+ (graph_height - (((sample[0] - y_min) / y_range) * graph_height)).to_num::<i32>();
|
||||||
Some(v) => v,
|
let x_pos_end: i32 = bounds.top_left.x
|
||||||
None => 0,
|
+ (((n_samples_seen + U16F16::from_num(1)) / n_samples)
|
||||||
};
|
* (graph_width - U16F16::from_num(1)))
|
||||||
let b_i32: i32 = match NumCast::from(b.clone()) {
|
.to_num::<i32>();
|
||||||
Some(v) => v,
|
let y_pos_end: i32 = bounds.top_left.y
|
||||||
None => 0,
|
+ (graph_height - (((sample[1] - y_min) / y_range) * graph_height)).to_num::<i32>();
|
||||||
};
|
|
||||||
|
let _ = Line::new(
|
||||||
let a_y_pos: i32 = y_start + (((graph_max - a_i32) * 30) / range);
|
Point::new(x_pos_start, y_pos_start),
|
||||||
let b_y_pos: i32 = y_start + (((graph_max - b_i32) * 30) / range);
|
Point::new(x_pos_end, y_pos_end),
|
||||||
|
)
|
||||||
Line::new(Point::new(x_pos, a_y_pos), Point::new(x_pos - 2, b_y_pos))
|
.into_styled(PrimitiveStyle::with_stroke(fg_colour, 1))
|
||||||
.into_styled(PrimitiveStyle::with_stroke(line_colour, 1))
|
.draw(target);
|
||||||
.draw(target)?;
|
|
||||||
|
x_offset = x_pos_end;
|
||||||
x_pos -= 2;
|
n_samples_seen += U16F16::from_num(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
60
src/input_task.rs
Normal file
60
src/input_task.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
use embassy_futures::select::{select3, Either3};
|
||||||
|
use embassy_rp::gpio::Input;
|
||||||
|
use embassy_time::{with_timeout, Duration, Instant};
|
||||||
|
use rtt_target::debug_rprintln;
|
||||||
|
|
||||||
|
const DEBOUNCE_TIME_MILLIS: u64 = 20;
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
pub async fn input_handling_task(
|
||||||
|
mut button_enter: Input<'static>,
|
||||||
|
mut button_left: Input<'static>,
|
||||||
|
mut button_right: Input<'static>,
|
||||||
|
) {
|
||||||
|
debug_rprintln!("Input handling task started");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let enter_pressed = button_enter.wait_for_falling_edge();
|
||||||
|
let left_pressed = button_left.wait_for_falling_edge();
|
||||||
|
let right_pressed = button_right.wait_for_falling_edge();
|
||||||
|
|
||||||
|
match select3(enter_pressed, left_pressed, right_pressed).await {
|
||||||
|
Either3::First(_) => {
|
||||||
|
let pressed_at = Instant::now();
|
||||||
|
if with_timeout(Duration::from_secs(2), button_enter.wait_for_rising_edge())
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
if pressed_at.elapsed().as_millis() < DEBOUNCE_TIME_MILLIS {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
debug_rprintln!("Enter button pressed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Either3::Second(_) => {
|
||||||
|
let pressed_at = Instant::now();
|
||||||
|
if with_timeout(Duration::from_secs(2), button_left.wait_for_rising_edge())
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
if pressed_at.elapsed().as_millis() < DEBOUNCE_TIME_MILLIS {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
debug_rprintln!("Left button pressed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Either3::Third(_) => {
|
||||||
|
let pressed_at = Instant::now();
|
||||||
|
if with_timeout(Duration::from_secs(2), button_right.wait_for_rising_edge())
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
if pressed_at.elapsed().as_millis() < DEBOUNCE_TIME_MILLIS {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
debug_rprintln!("Right button pressed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
21
src/main.rs
21
src/main.rs
@ -2,16 +2,17 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
mod display_task;
|
mod display_task;
|
||||||
|
mod input_task;
|
||||||
mod sensor_task;
|
mod sensor_task;
|
||||||
|
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
|
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{debug_rprintln, debug_rtt_init_print};
|
||||||
|
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_rp::{
|
use embassy_rp::{
|
||||||
block::ImageDef,
|
block::ImageDef,
|
||||||
gpio::{Level, Output},
|
gpio::{Input, Level, Output, Pull},
|
||||||
i2c,
|
i2c,
|
||||||
peripherals::{I2C0, SPI0},
|
peripherals::{I2C0, SPI0},
|
||||||
spi,
|
spi,
|
||||||
@ -27,6 +28,7 @@ use embassy_time::Timer;
|
|||||||
use static_cell::StaticCell;
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
use display_task::display_output_task;
|
use display_task::display_output_task;
|
||||||
|
use input_task::input_handling_task;
|
||||||
use sensor_task::sensor_read_task;
|
use sensor_task::sensor_read_task;
|
||||||
|
|
||||||
type I2c0BusMutex = Mutex<NoopRawMutex, RefCell<i2c::I2c<'static, I2C0, i2c::Blocking>>>;
|
type I2c0BusMutex = Mutex<NoopRawMutex, RefCell<i2c::I2c<'static, I2C0, i2c::Blocking>>>;
|
||||||
@ -41,11 +43,11 @@ static SENSOR_DATA_SIGNAL: Signal<CriticalSectionRawMutex, scd4x::types::SensorD
|
|||||||
|
|
||||||
/// Entrypoint
|
/// Entrypoint
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(spawner: Spawner) {
|
async fn main(spawner: Spawner) -> ! {
|
||||||
// Initialise RTT logging
|
// Initialise RTT logging
|
||||||
|
|
||||||
rtt_init_print!();
|
debug_rtt_init_print!();
|
||||||
rprintln!("RTT logging initialised");
|
debug_rprintln!("RTT logging initialised");
|
||||||
|
|
||||||
let peripherals = embassy_rp::init(Default::default());
|
let peripherals = embassy_rp::init(Default::default());
|
||||||
|
|
||||||
@ -70,7 +72,7 @@ async fn main(spawner: Spawner) {
|
|||||||
let rst = SPI0_RST_PIN.init(Output::new(peripherals.PIN_15, Level::Low));
|
let rst = SPI0_RST_PIN.init(Output::new(peripherals.PIN_15, Level::Low));
|
||||||
|
|
||||||
let mut spi_config = spi::Config::default();
|
let mut spi_config = spi::Config::default();
|
||||||
spi_config.frequency = 4_000_000u32; // 4 MHz
|
spi_config.frequency = 20_000_000u32; // 20 MHz
|
||||||
let spi_bus = spi::Spi::new_blocking_txonly(peripherals.SPI0, sclk, mosi, spi_config.clone());
|
let spi_bus = spi::Spi::new_blocking_txonly(peripherals.SPI0, sclk, mosi, spi_config.clone());
|
||||||
static SPI0_BUS: StaticCell<Spi0BusMutex> = StaticCell::new();
|
static SPI0_BUS: StaticCell<Spi0BusMutex> = StaticCell::new();
|
||||||
let shared_spi0_bus = SPI0_BUS.init(Mutex::new(RefCell::new(spi_bus)));
|
let shared_spi0_bus = SPI0_BUS.init(Mutex::new(RefCell::new(spi_bus)));
|
||||||
@ -84,6 +86,11 @@ async fn main(spawner: Spawner) {
|
|||||||
spi_config.clone(),
|
spi_config.clone(),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let enter_button = Input::new(peripherals.PIN_8, Pull::Up);
|
||||||
|
let left_button = Input::new(peripherals.PIN_6, Pull::Up);
|
||||||
|
let right_button = Input::new(peripherals.PIN_7, Pull::Up);
|
||||||
|
spawner.must_spawn(input_handling_task(enter_button, left_button, right_button));
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
Timer::after_secs(1).await;
|
Timer::after_secs(1).await;
|
||||||
}
|
}
|
||||||
@ -92,7 +99,7 @@ async fn main(spawner: Spawner) {
|
|||||||
/// Panic handler
|
/// Panic handler
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(info: &core::panic::PanicInfo) -> ! {
|
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||||
rprintln!("Panicked! {}", info);
|
debug_rprintln!("Panicked! {}", info);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// System
|
// System
|
||||||
use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice;
|
use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice;
|
||||||
use embassy_time::Timer;
|
use embassy_time::Timer;
|
||||||
use rtt_target::rprintln;
|
use rtt_target::debug_rprintln;
|
||||||
|
|
||||||
// Sensor
|
// Sensor
|
||||||
use scd4x::Scd4x;
|
use scd4x::Scd4x;
|
||||||
@ -11,7 +11,7 @@ use crate::{I2c0BusMutex, SENSOR_DATA_SIGNAL};
|
|||||||
/// Read CO2/temp./humidity data from the sensor
|
/// Read CO2/temp./humidity data from the sensor
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
pub async fn sensor_read_task(i2c_bus: &'static I2c0BusMutex) {
|
pub async fn sensor_read_task(i2c_bus: &'static I2c0BusMutex) {
|
||||||
rprintln!("Sensor read task started");
|
debug_rprintln!("Sensor read task started");
|
||||||
|
|
||||||
Timer::after_millis(30).await; // SCD41 power-up delay
|
Timer::after_millis(30).await; // SCD41 power-up delay
|
||||||
let i2c_dev = I2cDevice::new(i2c_bus);
|
let i2c_dev = I2cDevice::new(i2c_bus);
|
||||||
@ -21,8 +21,8 @@ pub async fn sensor_read_task(i2c_bus: &'static I2c0BusMutex) {
|
|||||||
scd41.reinit().unwrap();
|
scd41.reinit().unwrap();
|
||||||
|
|
||||||
match scd41.serial_number() {
|
match scd41.serial_number() {
|
||||||
Ok(serial) => rprintln!("[SCD41] Serial number: {}", serial),
|
Ok(serial) => debug_rprintln!("[SCD41] Serial number: {}", serial),
|
||||||
Err(error) => rprintln!(
|
Err(error) => debug_rprintln!(
|
||||||
"[SCD41] Error: did not respond to get_serial_number: {:?}",
|
"[SCD41] Error: did not respond to get_serial_number: {:?}",
|
||||||
error
|
error
|
||||||
),
|
),
|
||||||
@ -35,8 +35,8 @@ pub async fn sensor_read_task(i2c_bus: &'static I2c0BusMutex) {
|
|||||||
|
|
||||||
match scd41.measurement() {
|
match scd41.measurement() {
|
||||||
Ok(data) => {
|
Ok(data) => {
|
||||||
rprintln!(
|
debug_rprintln!(
|
||||||
"[SCD41] CO2: {} ppm, temperature: {} C, humidity: {} RH",
|
"[SCD41] CO2: {} ppm, temperature: {} C, humidity: {} % RH",
|
||||||
data.co2,
|
data.co2,
|
||||||
data.temperature,
|
data.temperature,
|
||||||
data.humidity
|
data.humidity
|
||||||
@ -45,7 +45,7 @@ pub async fn sensor_read_task(i2c_bus: &'static I2c0BusMutex) {
|
|||||||
SENSOR_DATA_SIGNAL.signal(data);
|
SENSOR_DATA_SIGNAL.signal(data);
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
rprintln!(
|
debug_rprintln!(
|
||||||
"[SCD41] Error: failed to retrieve measurement data: {:?}",
|
"[SCD41] Error: failed to retrieve measurement data: {:?}",
|
||||||
error
|
error
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user