Compare commits
6 Commits
76208d1322
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 147f76f381 | |||
| 8b923b8a8b | |||
| d6213d2498 | |||
| 064656f643 | |||
| 31b5d19127 | |||
| 3b4110bd9f |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,5 +1,8 @@
|
|||||||
# Cargo
|
# Cargo
|
||||||
target/
|
target/
|
||||||
|
|
||||||
|
# Firmware blobs
|
||||||
|
cyw43-firmware/
|
||||||
|
|
||||||
# VSCode
|
# VSCode
|
||||||
.vscode
|
.vscode
|
||||||
|
|||||||
1754
Cargo.lock
generated
1754
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
22
Cargo.toml
22
Cargo.toml
@@ -9,7 +9,7 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Embassy
|
# Embassy
|
||||||
embassy-rp = { version = "0.3.1", features = [
|
embassy-rp = { version = "0.4.0", features = [
|
||||||
"rp235xa",
|
"rp235xa",
|
||||||
"binary-info",
|
"binary-info",
|
||||||
"critical-section-impl",
|
"critical-section-impl",
|
||||||
@@ -24,12 +24,19 @@ embassy-executor = { version = "0.7.0", features = [
|
|||||||
embassy-time = "0.4.0"
|
embassy-time = "0.4.0"
|
||||||
embassy-embedded-hal = "0.3.0"
|
embassy-embedded-hal = "0.3.0"
|
||||||
embassy-futures = "0.1.1"
|
embassy-futures = "0.1.1"
|
||||||
embassy-sync = "0.6.2"
|
embassy-sync = "0.6.2" # TODO: Update when embassy-embedded-hal on crates.io is tracking 0.7.0
|
||||||
|
trouble-host = { version = "0.1.0", default-features = false, features = [
|
||||||
|
"peripheral",
|
||||||
|
"gatt",
|
||||||
|
"derive",
|
||||||
|
] }
|
||||||
|
|
||||||
# System
|
# System
|
||||||
cortex-m-rt = "0.7.5"
|
cortex-m-rt = "0.7.5"
|
||||||
cortex-m = "0.7.7"
|
cortex-m = "0.7.7"
|
||||||
critical-section = "1.2.0"
|
critical-section = "1.2.0"
|
||||||
|
cyw43 = { version = "0.3.0", features = ["bluetooth"] }
|
||||||
|
cyw43-pio = "0.4.0"
|
||||||
rtt-target = "0.6.1"
|
rtt-target = "0.6.1"
|
||||||
|
|
||||||
# embedded-graphics
|
# embedded-graphics
|
||||||
@@ -37,16 +44,23 @@ embedded-graphics = "0.8.1"
|
|||||||
embedded-graphics-framebuf = "0.5.0"
|
embedded-graphics-framebuf = "0.5.0"
|
||||||
|
|
||||||
# Peripherals
|
# Peripherals
|
||||||
scd4x = { version = "0.4.0", features = ["scd41"] }
|
scd4x = { version = "0.4.1", features = ["scd41"] }
|
||||||
ssd1351 = "0.5.0"
|
ssd1351 = "0.5.0"
|
||||||
display-interface-spi = "0.5.0"
|
display-interface-spi = "0.5.0"
|
||||||
|
|
||||||
# Extra
|
# Extra
|
||||||
static_cell = "2.1.0"
|
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.1.0", default-features = false }
|
||||||
fixed = "1.29.0"
|
fixed = "1.29.0"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
reqwest = { version = "0.12.20", features = ["blocking"] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
# Skips downloading the Wi-Fi & Bluetooth chipset firmware, assumes that it is in `./cyw43-firmware`
|
||||||
|
skip-cyw43-firmware = []
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = "s"
|
opt-level = "s"
|
||||||
|
|
||||||
|
|||||||
23
README.md
23
README.md
@@ -9,24 +9,33 @@ The effects of increased CO₂ concentration on cognitive function become increa
|
|||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- Rust compiler & cargo package manager: https://www.rust-lang.org/
|
- Rust compiler & cargo package manager: https://www.rust-lang.org/
|
||||||
- RPi Pico SDK: https://github.com/raspberrypi/pico-sdk
|
- [probe-rs](https://probe.rs/) for programming the device, receiving RTT logging output & debugging
|
||||||
- RPi picotool: https://github.com/raspberrypi/picotool
|
|
||||||
- ARM bare metal compiler toolchain
|
- ARM bare metal compiler toolchain
|
||||||
- `arm-none-eabi-gcc` (& `arm-none-eabi-newlib`) on Arch Linux, your system may have different package names
|
- `arm-none-eabi-gcc` (& `arm-none-eabi-newlib`) on Arch Linux, your system may have different package names
|
||||||
- The ARMv8 or RISC-V Rust toolchain:
|
- The ARMv8 or RISC-V Rust toolchain:
|
||||||
- ARMv8: `rustup target add thumbv8m.main-none-eabihf`
|
- ARMv8: `rustup target add thumbv8m.main-none-eabihf`
|
||||||
- RISC-V: `rustup target add riscv32imac-unknown-none-elf`
|
- RISC-V: `rustup target add riscv32imac-unknown-none-elf`
|
||||||
|
|
||||||
### Environment configuration
|
### Notes
|
||||||
|
|
||||||
This build system assumes the environment variables: `PICO_BOARD`, `PICO_PLATFORM` & `PICO_SDK_PATH` are set. `picotool` should also be available in the `PATH`.
|
This project's [`Cargo.toml`](./Cargo.toml) includes a custom build profile ("`dist`") which optimises the executable for maximum performance at the cost of slower build times. This profile is designed for flashing a final build onto an end user's device.
|
||||||
|
|
||||||
The [env-vars.sh](./scripts/env-vars.sh) script sets these values to `pico2_w`, `rp2350-arm-s` and my personal Pico SDK path, respectively. The environment variable definitions in this file can be set with `source ./scripts/env-vars.sh`.
|
By install probe-rs as mentioned in the prerequisities you will have access to `cargo flash` and `cargo embed` for flashing the executable onto the device.
|
||||||
|
|
||||||
### Compiling & running
|
### Compiling & running
|
||||||
|
|
||||||
- `cargo build` (or `cargo build --release`)
|
- `cargo build` (or `cargo build --release` / `cargo build --profile dist`)
|
||||||
|
|
||||||
If you wish to run the binary on your Pico (connected in BOOTSEL mode):
|
If you wish to run the binary on your Pico (connected in BOOTSEL mode):
|
||||||
|
|
||||||
- `cargo run` (or `cargo run --release`)
|
- `cargo run` (or `cargo run --release` / `cargo run --profile dist`)
|
||||||
|
|
||||||
|
## Hacking & debugging
|
||||||
|
|
||||||
|
This project uses probe-rs for flashing & debugging, though one may use `picotool` and `OpenOCD` instead if you so wish. Ensure that you have a debug probe which supports to RP235x series of chips and supports the SWD protocol. I use the Raspberry Pi debug probe (USB VID:PID `2e8a:000c`) for debugging the device using the on-board debugging pins.
|
||||||
|
|
||||||
|
## TODO / Future improvements
|
||||||
|
|
||||||
|
- Companion smartphone app using BLE to receive measurement data from the device for notifications & further analysis/tracking
|
||||||
|
- Low-power state, reducing screen brightness, sensor sampling rate and putting periphals to sleep
|
||||||
|
- 3D printed case housing the MCU, sensor, display and rechargable lithium ion battery
|
||||||
|
|||||||
41
build.rs
41
build.rs
@@ -6,6 +6,9 @@ fn main() {
|
|||||||
let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
|
let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
|
||||||
println!("cargo:rustc-link-search={}", out.display());
|
println!("cargo:rustc-link-search={}", out.display());
|
||||||
|
|
||||||
|
#[cfg(not(feature = "skip-cyw43-firmware"))]
|
||||||
|
download_cyw43_firmware();
|
||||||
|
|
||||||
// ARM build
|
// ARM build
|
||||||
|
|
||||||
let memory_x = include_bytes!("memory.x");
|
let memory_x = include_bytes!("memory.x");
|
||||||
@@ -22,3 +25,41 @@ fn main() {
|
|||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Downloads to firmware for the Wi-Fi / Bluetooth chipset on the RPi Pico 2 W
|
||||||
|
#[cfg(not(feature = "skip-cyw43-firmware"))]
|
||||||
|
fn download_cyw43_firmware() {
|
||||||
|
let download_folder = "cyw43-firmware";
|
||||||
|
let url_base = "https://raw.githubusercontent.com/embassy-rs/embassy/refs/tags/cyw43-v0.3.0/cyw43-firmware";
|
||||||
|
let file_names = [
|
||||||
|
"43439A0.bin",
|
||||||
|
"43439A0_btfw.bin",
|
||||||
|
"43439A0_clm.bin",
|
||||||
|
"LICENSE-permissive-binary-license-1.0.txt",
|
||||||
|
"README.md",
|
||||||
|
];
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
println!("cargo:rerun-if-changed={}", download_folder);
|
||||||
|
std::fs::create_dir_all(download_folder).expect("failed to create firmware directory");
|
||||||
|
|
||||||
|
// download each file into the directory "cyw43-firmware"
|
||||||
|
for file in file_names {
|
||||||
|
let url = format!("{}/{}", url_base, file);
|
||||||
|
// only fetch if it doesn't exist
|
||||||
|
if std::path::Path::new(download_folder).join(file).exists() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match reqwest::blocking::get(&url) {
|
||||||
|
Ok(response) => {
|
||||||
|
let content = response.bytes().expect("failed to read file content");
|
||||||
|
let file_path = PathBuf::from(download_folder).join(file);
|
||||||
|
std::fs::write(file_path, &content).expect("failed to write file");
|
||||||
|
}
|
||||||
|
Err(err) => panic!(
|
||||||
|
"failed to download the cyw43 firmware from {}: {}, required for BLE support",
|
||||||
|
url, err
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,11 +8,15 @@ use scd4x::Scd4x;
|
|||||||
|
|
||||||
use crate::{I2c0BusMutex, SENSOR_DATA_SIGNAL};
|
use crate::{I2c0BusMutex, SENSOR_DATA_SIGNAL};
|
||||||
|
|
||||||
|
const BACKGROUND_CO2_PPM: u16 = 427;
|
||||||
|
|
||||||
/// 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) {
|
||||||
debug_rprintln!("Sensor read task started");
|
debug_rprintln!("Sensor read task started");
|
||||||
|
|
||||||
|
// Initialise SCD41
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@@ -20,6 +24,11 @@ pub async fn sensor_read_task(i2c_bus: &'static I2c0BusMutex) {
|
|||||||
scd41.wake_up();
|
scd41.wake_up();
|
||||||
scd41.reinit().unwrap();
|
scd41.reinit().unwrap();
|
||||||
|
|
||||||
|
// https://climate.nasa.gov/vital-signs/carbon-dioxide/?intent=121
|
||||||
|
scd41
|
||||||
|
.set_automatic_self_calibration_target(BACKGROUND_CO2_PPM)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
match scd41.serial_number() {
|
match scd41.serial_number() {
|
||||||
Ok(serial) => debug_rprintln!("[SCD41] Serial number: {}", serial),
|
Ok(serial) => debug_rprintln!("[SCD41] Serial number: {}", serial),
|
||||||
Err(error) => debug_rprintln!(
|
Err(error) => debug_rprintln!(
|
||||||
@@ -28,6 +37,8 @@ pub async fn sensor_read_task(i2c_bus: &'static I2c0BusMutex) {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Measurement loop
|
||||||
|
|
||||||
scd41.start_periodic_measurement().unwrap();
|
scd41.start_periodic_measurement().unwrap();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@@ -53,4 +64,6 @@ pub async fn sensor_read_task(i2c_bus: &'static I2c0BusMutex) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scd41.stop_periodic_measurement().unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user