Move sensor code to own module
This commit is contained in:
parent
56ac102a9b
commit
02c8102e35
71
src/co2_sensor.rs
Normal file
71
src/co2_sensor.rs
Normal file
|
@ -0,0 +1,71 @@
|
|||
use embassy_rp::{
|
||||
i2c::{Async, I2c},
|
||||
peripherals::I2C1,
|
||||
};
|
||||
|
||||
/// I2C Address of the co2 sensor
|
||||
const CCS811_I2C_ADDRESS: u16 = 0x5A;
|
||||
/// Application I2C Register addresses of the co2 sensor
|
||||
const CCS811_REGISTER_STATUS: u8 = 0x00;
|
||||
const CCS811_REGISTER_MEAS_MODE: u8 = 0x01;
|
||||
const CCS811_REGISTER_ALG_RESULT_DATA: u8 = 0x02;
|
||||
|
||||
/// Bootloader I2C Register addresses of the co2 sensor
|
||||
const CCS811_REGISTER_BOOTLOADER_APP_START: u8 = 0xF4;
|
||||
|
||||
/// Read `buf.len()` bytes from register `register_address` over I2C
|
||||
pub async fn read_register(
|
||||
i2c: &mut I2c<'_, I2C1, Async>,
|
||||
register_address: u8,
|
||||
buf: &mut [u8],
|
||||
) -> Result<(), embassy_rp::i2c::Error> {
|
||||
i2c.write_async(CCS811_I2C_ADDRESS, [register_address])
|
||||
.await?;
|
||||
i2c.read_async(CCS811_I2C_ADDRESS, buf).await
|
||||
}
|
||||
|
||||
/// Write all `bytes` in to register `register_address` over I2C
|
||||
pub async fn write_register(
|
||||
i2c: &mut I2c<'_, I2C1, Async>,
|
||||
register_address: u8,
|
||||
bytes: impl IntoIterator<Item = u8>,
|
||||
) -> Result<(), embassy_rp::i2c::Error> {
|
||||
let write_buffer = core::iter::once(register_address).chain(bytes);
|
||||
i2c.write_async(CCS811_I2C_ADDRESS, write_buffer).await
|
||||
}
|
||||
|
||||
/// Start the application on the sensor. The device can run custom applications.
|
||||
/// However, there is no documentation about how these applications can be put together.
|
||||
/// Therefore, we're just using whatever is already programmed on the chip.
|
||||
pub async fn start_app(i2c: &mut I2c<'_, I2C1, Async>) -> Result<(), embassy_rp::i2c::Error> {
|
||||
i2c.write_async(CCS811_I2C_ADDRESS, [CCS811_REGISTER_BOOTLOADER_APP_START])
|
||||
.await
|
||||
}
|
||||
|
||||
/// Read the status byte from the device
|
||||
pub async fn get_status(i2c: &mut I2c<'_, I2C1, Async>) -> Result<u8, embassy_rp::i2c::Error> {
|
||||
let mut res = [0];
|
||||
read_register(i2c, CCS811_REGISTER_STATUS, &mut res).await?;
|
||||
Ok(res[0])
|
||||
}
|
||||
|
||||
/// Read the eCO2 value from the `ALG_RESULT_DATA` register on the sensor
|
||||
pub async fn get_measurement(
|
||||
i2c: &mut I2c<'_, I2C1, Async>,
|
||||
) -> Result<u16, embassy_rp::i2c::Error> {
|
||||
let mut res = [0; 2];
|
||||
read_register(i2c, CCS811_REGISTER_ALG_RESULT_DATA, &mut res).await?;
|
||||
Ok(u16::from_be_bytes(res))
|
||||
}
|
||||
|
||||
/// Set the measurement mode register.
|
||||
/// The measurement mode register is a bit field that also controls the
|
||||
/// interrupt behavior.
|
||||
/// By setting this register to `16`, you can select 1Hz constant power mode
|
||||
/// without interrupts.
|
||||
pub async fn set_measurement_mode(
|
||||
i2c: &mut I2c<'_, I2C1, Async>,
|
||||
mode: u8,
|
||||
) -> Result<(), embassy_rp::i2c::Error> {
|
||||
write_register(i2c, CCS811_REGISTER_MEAS_MODE, [mode]).await
|
||||
}
|
100
src/main.rs
100
src/main.rs
|
@ -6,44 +6,33 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::future::ready;
|
||||
mod co2_sensor;
|
||||
|
||||
use byteorder::ByteOrder;
|
||||
use defmt::*;
|
||||
use embassy_embedded_hal::SetConfig;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::bind_interrupts;
|
||||
use embassy_rp::peripherals::{PIN_10, PIN_11, PIN_12, PIN_16, SPI1};
|
||||
use embassy_rp::spi::Spi;
|
||||
use embassy_rp::{bind_interrupts, interrupt};
|
||||
use embassy_rp::{gpio, spi};
|
||||
use embedded_hal_bus::spi::ExclusiveDevice;
|
||||
use embedded_sdmmc::sdcard::{DummyCsPin, SdCard};
|
||||
use embedded_sdmmc::{Block, BlockDevice, BlockIdx, VolumeIdx};
|
||||
use futures::{FutureExt, TryFutureExt};
|
||||
use gpio::{Level, Output};
|
||||
use usbd_hid::descriptor::MouseReport;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
use embassy_rp::i2c::{self, Config};
|
||||
use embassy_time::{Duration, Timer};
|
||||
|
||||
use co2_sensor::{get_measurement, get_status, set_measurement_mode, start_app};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C1_IRQ => i2c::InterruptHandler<embassy_rp::peripherals::I2C1>;
|
||||
});
|
||||
|
||||
struct DummyTimesource();
|
||||
|
||||
/// I2C Address of the co2 sensor
|
||||
const CCS811_I2C_ADDRESS: u16 = 0x5A;
|
||||
/// Application I2C Register addresses of the co2 sensor
|
||||
const CCS811_REGISTER_STATUS: u8 = 0x00;
|
||||
const CCS811_REGISTER_MEAS_MODE: u8 = 0x01;
|
||||
const CCS811_REGISTER_ALG_RESULT_DATA: u8 = 0x02;
|
||||
|
||||
/// Bootloader I2C Register addresses of the co2 sensor
|
||||
const CCS811_REGISTER_BOOTLOADER_APP_START: u8 = 0xF4;
|
||||
const CCS811_REGISTER_BOOTLOADER_STATUS: u8 = CCS811_REGISTER_STATUS;
|
||||
|
||||
impl embedded_sdmmc::TimeSource for DummyTimesource {
|
||||
fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
|
||||
embedded_sdmmc::Timestamp {
|
||||
|
@ -78,73 +67,32 @@ async fn main(spawner: Spawner) {
|
|||
// Wait for sensor to boot
|
||||
Timer::after(Duration::from_secs(1)).await;
|
||||
|
||||
debug!("Writing to I2C");
|
||||
let mut status = [42u8];
|
||||
i2c.write_async(CCS811_I2C_ADDRESS, [CCS811_REGISTER_STATUS])
|
||||
.await
|
||||
.unwrap();
|
||||
debug!("Reading from I2C");
|
||||
i2c.read_async(CCS811_I2C_ADDRESS, &mut status)
|
||||
.await
|
||||
.unwrap();
|
||||
let status = status[0];
|
||||
let status = get_status(&mut i2c).await.unwrap();
|
||||
|
||||
info!("Reported status: {}", status);
|
||||
match status {
|
||||
16u8 => {}
|
||||
144u8 => {
|
||||
warn!("Sensor already in APP mode! Configuration may come from previous boot.");
|
||||
}
|
||||
unexpected => {
|
||||
warn!(
|
||||
"Sensor reported unexpected state after boot: {}",
|
||||
unexpected
|
||||
);
|
||||
info!("CO2 sesor reported status on boot: {}", status);
|
||||
|
||||
// Byte 7 is FW_MODE which indicates if the app is already running.
|
||||
if status & 128u8 == 0 {
|
||||
info!("App is not running yet. Booting sensor...");
|
||||
// App is not running
|
||||
start_app(&mut i2c).await.unwrap();
|
||||
// After APP_START, we have to wait at least 1 ms (according to datasheet)
|
||||
Timer::after(Duration::from_millis(2)).await;
|
||||
|
||||
if get_status(&mut i2c).await.unwrap() & 128u8 == 0 {
|
||||
error!("App still not running after boot! Terminating...");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
info!("App is already running. Skipping boot...");
|
||||
}
|
||||
|
||||
// APP_START does not require to write data
|
||||
i2c.write_async(CCS811_I2C_ADDRESS, [CCS811_REGISTER_BOOTLOADER_APP_START])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// After APP_START, we have to wait 1 ms (according to datasheet)
|
||||
Timer::after(Duration::from_millis(1000)).await;
|
||||
|
||||
// Mode 1 is 1 measurement per second
|
||||
i2c.write_async(CCS811_I2C_ADDRESS, [CCS811_REGISTER_MEAS_MODE, 16])
|
||||
.await
|
||||
.unwrap();
|
||||
// 16 is measurement mode 1 => 1 measurement per second
|
||||
set_measurement_mode(&mut i2c, 16).await.unwrap();
|
||||
|
||||
loop {
|
||||
let mut status = [42u8];
|
||||
i2c.write_async(CCS811_I2C_ADDRESS, [CCS811_REGISTER_STATUS])
|
||||
.await;
|
||||
// .unwrap();
|
||||
i2c.read_async(CCS811_I2C_ADDRESS, &mut status).await;
|
||||
// .unwrap();
|
||||
let status = status[0];
|
||||
|
||||
let mut measured_value_buffer = [42u8; 2];
|
||||
let address_written = i2c
|
||||
.write_async(CCS811_I2C_ADDRESS, [CCS811_REGISTER_ALG_RESULT_DATA])
|
||||
.await
|
||||
.ok();
|
||||
|
||||
let data_written = if let Some(()) = address_written {
|
||||
i2c.read_async(CCS811_I2C_ADDRESS, &mut measured_value_buffer)
|
||||
.await
|
||||
.ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let measured_value = if let Some(()) = data_written {
|
||||
Some(u16::from_be_bytes(measured_value_buffer))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let status = get_status(&mut i2c).await.unwrap();
|
||||
let measured_value = get_measurement(&mut i2c).await.unwrap();
|
||||
info!(
|
||||
"Reported status: {}\tMeasured value: {}",
|
||||
status, measured_value
|
||||
|
|
Loading…
Reference in a new issue