diff --git a/Cargo.lock b/Cargo.lock index bd21a4d..78aaf65 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,6 +83,12 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + [[package]] name = "bytemuck" version = "1.21.0" @@ -232,6 +238,24 @@ dependencies = [ "winapi", ] +[[package]] +name = "display-interface" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba2aab1ef3793e6f7804162debb5ac5edb93b3d650fbcc5aeb72fcd0e6c03a0" + +[[package]] +name = "display-interface-spi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9ec30048b1955da2038fcc3c017f419ab21bb0001879d16c0a3749dc6b7a" +dependencies = [ + "byte-slice-cast", + "display-interface", + "embedded-hal 1.0.0", + "embedded-hal-async", +] + [[package]] name = "document-features" version = "0.2.11" @@ -400,6 +424,29 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fc247028eae04174b6635104a35b1ed336aabef4654f5e87a8f32327d231970" +[[package]] +name = "embedded-graphics" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0649998afacf6d575d126d83e68b78c0ab0e00ca2ac7e9b3db11b4cbe8274ef0" +dependencies = [ + "az", + "byteorder", + "embedded-graphics-core", + "float-cmp", + "micromath", +] + +[[package]] +name = "embedded-graphics-core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba9ecd261f991856250d2207f6d8376946cd9f412a2165d3b75bc87a0bc7a044" +dependencies = [ + "az", + "byteorder", +] + [[package]] name = "embedded-hal" version = "0.2.7" @@ -498,6 +545,15 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" @@ -697,6 +753,12 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "micromath" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c8dda44ff03a2f238717214da50f65d5a53b45cd213a7370424ffdb6fae815" + [[package]] name = "nb" version = "0.1.3" @@ -800,13 +862,16 @@ name = "pico-enviro-sensor" version = "0.1.0" dependencies = [ "cortex-m-rt", + "display-interface-spi", "embassy-embedded-hal", "embassy-executor", "embassy-rp", "embassy-sync", "embassy-time", + "embedded-graphics", "rtt-target", "scd4x", + "ssd1351", "static_cell", ] @@ -1113,6 +1178,18 @@ dependencies = [ "rgb", ] +[[package]] +name = "ssd1351" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21c426aced0940ed7c25b8134d6a0bf9745ade9f788ca54bec14c99e67120d6a" +dependencies = [ + "display-interface", + "display-interface-spi", + "embedded-graphics-core", + "embedded-hal 1.0.0", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" diff --git a/Cargo.toml b/Cargo.toml index a8a7ca8..3872c9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ embassy-time = "0.4.0" embassy-sync = "0.6.2" embassy-embedded-hal = "0.3.0" static_cell = "2.1.0" +embedded-graphics = "0.8.1" # System cortex-m-rt = "0.7.5" @@ -34,6 +35,8 @@ rtt-target = "0.6.1" scd4x = { git = "https://github.com/twokilohertz/scd4x-rs.git", branch = "conversion-fixes", features = [ "scd41", ] } +ssd1351 = "0.5.0" +display-interface-spi = "0.5.0" [profile.dev] opt-level = "s" diff --git a/src/display_task.rs b/src/display_task.rs new file mode 100644 index 0000000..d1601e6 --- /dev/null +++ b/src/display_task.rs @@ -0,0 +1,51 @@ +// System +use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; +use embassy_rp::{gpio::Output, spi::Config}; +use rtt_target::rprintln; + +// Display +use display_interface_spi::SPIInterface; +use ssd1351::{ + builder::Builder, + mode::GraphicsMode, + properties::{DisplayRotation, DisplaySize}, +}; + +// Graphics +use embedded_graphics::{ + pixelcolor::Rgb565, + prelude::{Point, Primitive, WebColors}, + primitives::{Circle, PrimitiveStyle}, + Drawable, +}; + +use crate::Spi0BusMutex; + +/// Output to the SSD1351 display +#[embassy_executor::task] +pub async fn display_output_task( + spi_bus: &'static Spi0BusMutex, + cs: Output<'static>, + dc: Output<'static>, + rst: &'static mut Output<'static>, + spi_config: Config, +) { + rprintln!("Display output task started"); + + let spi_dev = SpiDeviceWithConfig::new(spi_bus, cs, spi_config); + let interface = SPIInterface::new(spi_dev, dc); + + let mut display: GraphicsMode<_> = Builder::new() + .with_size(DisplaySize::Display128x128) + .with_rotation(DisplayRotation::Rotate0) + .connect_interface(interface) + .into(); + + display.reset(rst, &mut embassy_time::Delay).unwrap(); + display.init().unwrap(); + + Circle::new(Point::new(0, 0), 128) + .into_styled(PrimitiveStyle::with_fill(Rgb565::CSS_CHARTREUSE)) + .draw(&mut display) + .unwrap(); +} diff --git a/src/main.rs b/src/main.rs index 8f2639c..4a8508b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] +mod display_task; mod sensor_task; use core::cell::RefCell; @@ -8,18 +9,26 @@ use core::cell::RefCell; use rtt_target::{rprintln, rtt_init_print}; use embassy_executor::Spawner; -use embassy_rp::{block::ImageDef, i2c::Blocking, i2c::I2c, peripherals::I2C0}; +use embassy_rp::{ + block::ImageDef, + gpio::{Level, Output}, + i2c, + peripherals::{I2C0, SPI0}, + spi, +}; use embassy_sync::blocking_mutex::{raw::NoopRawMutex, Mutex}; use embassy_time::Timer; use static_cell::StaticCell; +use display_task::display_output_task; use sensor_task::sensor_read_task; embassy_rp::bind_interrupts!(struct Irqs { I2C0_IRQ => embassy_rp::i2c::InterruptHandler; }); -type I2c0BusType = Mutex>>; +type I2c0BusMutex = Mutex>>; +type Spi0BusMutex = Mutex>>; /// Entrypoint #[embassy_executor::main] @@ -31,17 +40,41 @@ async fn main(spawner: Spawner) { let peripherals = embassy_rp::init(Default::default()); - // Initialise I2C0 on pins 4 & 5 for the SCD41 sensor + // Initialise I2C0 (SDA: pin 4, SCL: pin 5) let sda = peripherals.PIN_4; let scl = peripherals.PIN_5; let mut i2c_config = embassy_rp::i2c::Config::default(); i2c_config.frequency = 400_000u32; // 400 kHz let i2c_bus = embassy_rp::i2c::I2c::new_blocking(peripherals.I2C0, scl, sda, i2c_config); - static I2C0_BUS: StaticCell = StaticCell::new(); + static I2C0_BUS: StaticCell = StaticCell::new(); let shared_i2c0_bus = I2C0_BUS.init(Mutex::new(RefCell::new(i2c_bus))); + // Start new task for reading data from the sensor spawner.must_spawn(sensor_read_task(shared_i2c0_bus)); + // Initialise SPI0 (MOSI: pin 19, SCLK: pin 18, CS: pin 17, DC: pin 14, RST: pin 15) + let mosi = peripherals.PIN_19; + let sclk = peripherals.PIN_18; + let cs = Output::new(peripherals.PIN_17, Level::Low); + let dc = Output::new(peripherals.PIN_14, Level::Low); + static SPI0_RST_PIN: StaticCell> = StaticCell::new(); // Initialised before launching task + let rst = SPI0_RST_PIN.init(Output::new(peripherals.PIN_15, Level::Low)); + + let mut spi_config = spi::Config::default(); + spi_config.frequency = 4_000_000u32; // 4 MHz + let spi_bus = spi::Spi::new_blocking_txonly(peripherals.SPI0, sclk, mosi, spi_config.clone()); + static SPI0_BUS: StaticCell = StaticCell::new(); + let shared_spi0_bus = SPI0_BUS.init(Mutex::new(RefCell::new(spi_bus))); + + // Start new task for outputting to the display + spawner.must_spawn(display_output_task( + shared_spi0_bus, + cs, + dc, + rst, + spi_config.clone(), + )); + loop { Timer::after_secs(1).await; } diff --git a/src/sensor_task.rs b/src/sensor_task.rs index 52b3cdd..c20ab59 100644 --- a/src/sensor_task.rs +++ b/src/sensor_task.rs @@ -6,10 +6,11 @@ use rtt_target::rprintln; // Sensor use scd4x::Scd4x; -use crate::I2c0BusType; +use crate::I2c0BusMutex; +/// Read CO2/temp./humidity data from the sensor #[embassy_executor::task] -pub async fn sensor_read_task(i2c_bus: &'static I2c0BusType) { +pub async fn sensor_read_task(i2c_bus: &'static I2c0BusMutex) { rprintln!("Sensor read task started"); Timer::after_millis(30).await; // SCD41 power-up delay @@ -30,7 +31,7 @@ pub async fn sensor_read_task(i2c_bus: &'static I2c0BusType) { scd41.start_periodic_measurement().unwrap(); loop { - Timer::after_secs(5).await; // start_periodic_measurement() returns new sensor data every 5 seconds + Timer::after_secs(5).await; // start_periodic_measurement returns new sensor data every 5 seconds match scd41.measurement() { Ok(data) => {