forked from tock/libtock-rs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
464: Air Quality API r=jrvanwhy a=RaresCon ### Pull Request Overview This PR adds an Air Quality API, which includes: - function for checking the existence of the driver - functions for initating readings for CO2 and TVOC levels - a private function and enum for synchronous reading of values for CO2 and TVOC levels and 3 public functions for easier use of the API - unit tests Alongside the API, this PR adds a fake driver (and unit tests for it) for testing the API. ### Testing Strategy This pull request was tested using unit tests made specifically for this API and fake driver. ### TODO or Help Wanted This pull request still needs feedback / code review. I will add documentation in the files and an example application using this API. ### Documentation Updated - [x] No updates required. Co-authored-by: RaresCon <rares.constantin2002@gmail.com> Co-authored-by: Rareș Constantin <95525840+RaresCon@users.noreply.github.com>
- Loading branch information
Showing
8 changed files
with
508 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[package] | ||
name = "libtock_air_quality" | ||
version = "0.1.0" | ||
authors = ["Tock Project Developers <tock-dev@googlegroups.com>"] | ||
license = "MIT/Apache-2.0" | ||
edition = "2021" | ||
repository = "https://www.github.com/tock/libtock-rs" | ||
description = "libtock air quality driver" | ||
|
||
[dependencies] | ||
libtock_platform = { path = "../../platform" } | ||
|
||
[dev-dependencies] | ||
libtock_unittest = { path = "../../unittest" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
#![no_std] | ||
|
||
use core::cell::Cell; | ||
use libtock_platform::subscribe::OneId; | ||
use libtock_platform::{ | ||
share::scope, share::Handle, DefaultConfig, ErrorCode, Subscribe, Syscalls, Upcall, | ||
}; | ||
use Value::{Tvoc, CO2}; | ||
|
||
enum Value { | ||
CO2 = READ_CO2 as isize, | ||
Tvoc = READ_TVOC as isize, | ||
} | ||
|
||
pub struct AirQuality<S: Syscalls>(S); | ||
|
||
impl<S: Syscalls> AirQuality<S> { | ||
/// Returns Ok() if the driver was present.This does not necessarily mean | ||
/// that the driver is working. | ||
pub fn exists() -> Result<(), ErrorCode> { | ||
S::command(DRIVER_NUM, EXISTS, 0, 0).to_result() | ||
} | ||
|
||
/// Register an events listener | ||
pub fn register_listener<'share, F: Fn(u32)>( | ||
listener: &'share AirQualityListener<F>, | ||
subscribe: Handle<Subscribe<'share, S, DRIVER_NUM, 0>>, | ||
) -> Result<(), ErrorCode> { | ||
S::subscribe::<_, _, DefaultConfig, DRIVER_NUM, 0>(subscribe, listener) | ||
} | ||
|
||
/// Unregister the events listener | ||
pub fn unregister_listener() { | ||
S::unsubscribe(DRIVER_NUM, 0) | ||
} | ||
|
||
/// Initiate a CO2 measurement. | ||
/// | ||
/// This function is used both for synchronous and asynchronous readings | ||
pub fn read_co2() -> Result<(), ErrorCode> { | ||
S::command(DRIVER_NUM, READ_CO2, 0, 0).to_result() | ||
} | ||
|
||
/// Initiate a TVOC measurement. | ||
/// | ||
/// This function is used both for synchronous and asynchronous readings | ||
pub fn read_tvoc() -> Result<(), ErrorCode> { | ||
S::command(DRIVER_NUM, READ_TVOC, 0, 0).to_result() | ||
} | ||
|
||
/// Public wrapper for `read_data_sync` for CO2 synchronous measurement | ||
pub fn read_co2_sync() -> Result<u32, ErrorCode> { | ||
Self::read_data_sync(CO2) | ||
} | ||
|
||
/// Public wrapper for `read_data_sync` for TVOC synchronous measurement | ||
pub fn read_tvoc_sync() -> Result<u32, ErrorCode> { | ||
Self::read_data_sync(Tvoc) | ||
} | ||
|
||
/// Read both CO2 and TVOC values synchronously | ||
pub fn read_sync() -> Result<(u32, u32), ErrorCode> { | ||
match (Self::read_data_sync(CO2), Self::read_data_sync(Tvoc)) { | ||
(Ok(co2_value), Ok(tvoc_value)) => Ok((co2_value, tvoc_value)), | ||
(Err(co2_error), _) => Err(co2_error), | ||
(_, Err(tvoc_error)) => Err(tvoc_error), | ||
} | ||
} | ||
|
||
/// Initiate a synchronous CO2 or TVOC measurement, based on the `read_type`. | ||
/// Returns Ok(value) if the operation was successful | ||
fn read_data_sync(read_type: Value) -> Result<u32, ErrorCode> { | ||
let data_cell: Cell<Option<u32>> = Cell::new(None); | ||
let listener = AirQualityListener(|data_val| { | ||
data_cell.set(Some(data_val)); | ||
}); | ||
|
||
scope(|subscribe| { | ||
Self::register_listener(&listener, subscribe)?; | ||
match read_type { | ||
CO2 => { | ||
Self::read_co2()?; | ||
while data_cell.get() == None { | ||
S::yield_wait(); | ||
} | ||
|
||
match data_cell.get() { | ||
None => Err(ErrorCode::Fail), | ||
Some(co2_value) => Ok(co2_value), | ||
} | ||
} | ||
Tvoc => { | ||
Self::read_tvoc()?; | ||
while data_cell.get() == None { | ||
S::yield_wait(); | ||
} | ||
|
||
match data_cell.get() { | ||
None => Err(ErrorCode::Fail), | ||
Some(tvoc_value) => Ok(tvoc_value), | ||
} | ||
} | ||
} | ||
}) | ||
} | ||
} | ||
|
||
pub struct AirQualityListener<F: Fn(u32)>(pub F); | ||
impl<F: Fn(u32)> Upcall<OneId<DRIVER_NUM, 0>> for AirQualityListener<F> { | ||
fn upcall(&self, data_val: u32, _arg1: u32, _arg2: u32) { | ||
self.0(data_val) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests; | ||
|
||
// ----------------------------------------------------------------------------- | ||
// Driver number and command IDs | ||
// ----------------------------------------------------------------------------- | ||
|
||
const DRIVER_NUM: u32 = 0x60007; | ||
|
||
// Command IDs | ||
|
||
const EXISTS: u32 = 0; | ||
const READ_CO2: u32 = 2; | ||
const READ_TVOC: u32 = 3; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
use crate::AirQualityListener; | ||
use core::cell::Cell; | ||
use libtock_platform::{share::scope, ErrorCode, Syscalls, YieldNoWaitReturn}; | ||
use libtock_unittest::fake; | ||
|
||
type AirQuality = super::AirQuality<fake::Syscalls>; | ||
|
||
#[test] | ||
fn no_driver() { | ||
let _kernel = fake::Kernel::new(); | ||
assert_eq!(AirQuality::exists(), Err(ErrorCode::NoDevice)); | ||
} | ||
|
||
#[test] | ||
fn driver_check() { | ||
let kernel = fake::Kernel::new(); | ||
let driver = fake::AirQuality::new(); | ||
kernel.add_driver(&driver); | ||
|
||
assert_eq!(AirQuality::exists(), Ok(())); | ||
} | ||
|
||
#[test] | ||
fn read_co2() { | ||
let kernel = fake::Kernel::new(); | ||
let driver = fake::AirQuality::new(); | ||
kernel.add_driver(&driver); | ||
|
||
assert_eq!(AirQuality::read_co2(), Ok(())); | ||
assert!(driver.is_busy()); | ||
|
||
assert_eq!(AirQuality::read_co2(), Err(ErrorCode::Busy)); | ||
assert_eq!(AirQuality::read_co2_sync(), Err(ErrorCode::Busy)); | ||
} | ||
|
||
#[test] | ||
fn read_tvoc() { | ||
let kernel = fake::Kernel::new(); | ||
let driver = fake::AirQuality::new(); | ||
kernel.add_driver(&driver); | ||
|
||
assert_eq!(AirQuality::read_tvoc(), Ok(())); | ||
assert!(driver.is_busy()); | ||
|
||
assert_eq!(AirQuality::read_tvoc(), Err(ErrorCode::Busy)); | ||
assert_eq!(AirQuality::read_tvoc_sync(), Err(ErrorCode::Busy)); | ||
} | ||
|
||
#[test] | ||
fn register_unregister_listener() { | ||
let kernel = fake::Kernel::new(); | ||
let driver = fake::AirQuality::new(); | ||
kernel.add_driver(&driver); | ||
|
||
let data_cell: Cell<Option<u32>> = Cell::new(None); | ||
let listener = AirQualityListener(|data_val| { | ||
data_cell.set(Some(data_val)); | ||
}); | ||
|
||
scope(|subscribe| { | ||
assert_eq!(AirQuality::read_co2(), Ok(())); | ||
driver.set_value(100); | ||
assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); | ||
|
||
assert_eq!(AirQuality::read_tvoc(), Ok(())); | ||
driver.set_value(100); | ||
assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); | ||
|
||
assert_eq!(AirQuality::register_listener(&listener, subscribe), Ok(())); | ||
|
||
assert_eq!(AirQuality::read_co2(), Ok(())); | ||
driver.set_value(100); | ||
assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); | ||
assert_eq!(data_cell.get(), Some(100)); | ||
|
||
assert_eq!(AirQuality::read_tvoc(), Ok(())); | ||
driver.set_value(100); | ||
assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall); | ||
assert_eq!(data_cell.get(), Some(100)); | ||
|
||
AirQuality::unregister_listener(); | ||
assert_eq!(AirQuality::read_co2(), Ok(())); | ||
driver.set_value(100); | ||
assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); | ||
|
||
assert_eq!(AirQuality::read_tvoc(), Ok(())); | ||
driver.set_value(100); | ||
assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall); | ||
}); | ||
} | ||
|
||
#[test] | ||
fn read_co2_sync() { | ||
let kernel = fake::Kernel::new(); | ||
let driver = fake::AirQuality::new(); | ||
kernel.add_driver(&driver); | ||
|
||
driver.set_value_sync(100); | ||
assert_eq!(AirQuality::read_co2_sync(), Ok(100)); | ||
} | ||
|
||
#[test] | ||
fn read_tvoc_sync() { | ||
let kernel = fake::Kernel::new(); | ||
let driver = fake::AirQuality::new(); | ||
kernel.add_driver(&driver); | ||
|
||
driver.set_value_sync(100); | ||
assert_eq!(AirQuality::read_tvoc_sync(), Ok(100)); | ||
} | ||
|
||
#[test] | ||
fn read_sync() { | ||
let kernel = fake::Kernel::new(); | ||
let driver = fake::AirQuality::new(); | ||
kernel.add_driver(&driver); | ||
|
||
driver.set_values_sync(100, 200); | ||
assert_eq!(AirQuality::read_sync(), Ok((100, 200))) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.