Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ libtock_low_level_debug = { path = "apis/low_level_debug" }
libtock_platform = { path = "platform" }
libtock_runtime = { path = "runtime" }
libtock_temperature = { path = "apis/temperature" }
libtock_humidity = { path = "apis/humidity" }

[profile.dev]
panic = "abort"
Expand All @@ -38,6 +39,7 @@ members = [
"apis/gpio",
"apis/buttons",
"apis/console",
"apis/humidity",
"apis/leds",
"apis/low_level_debug",
"apis/temperature",
Expand Down
12 changes: 12 additions & 0 deletions apis/humidity/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "libtock_humidity"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
libtock_platform = { path = "../../platform" }

[dev-dependencies]
libtock_unittest = { path = "../../unittest" }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add newline

81 changes: 81 additions & 0 deletions apis/humidity/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#![no_std]

use core::cell::Cell;
use libtock_platform::{
share, subscribe::OneId, DefaultConfig, ErrorCode, Subscribe, Syscalls, Upcall,
};

pub struct Humidity<S: Syscalls>(S);

impl<S: Syscalls> Humidity<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()
}

/// Initiate a humidity measurement.
///
/// This function is used both for synchronous and asynchronous readings
pub fn read_humidity() -> Result<(), ErrorCode> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub fn read_humidity() -> Result<(), ErrorCode> {
pub fn read() -> Result<(), ErrorCode> {

S::command(DRIVER_NUM, READ_HUM, 0, 0).to_result()
}

/// Register an events listener
pub fn register_listener<'share, F: Fn(i32)>(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be Fn(u32).

listener: &'share HumidityListener<F>,
subscribe: share::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 synchronous humidity measurement.
/// Returns Ok(humidity_value) if the operation was successful
/// humidity_value is returned in hundreds of centigrades
pub fn read_humidity_sync() -> Result<i32, ErrorCode> {
let humidity_cell: Cell<Option<i32>> = Cell::new(None);
let listener = HumidityListener(|hum_val| {
humidity_cell.set(Some(hum_val));
});
share::scope(|subscribe| {
if let Ok(()) = Self::register_listener(&listener, subscribe) {
if let Ok(()) = Self::read_humidity() {
while humidity_cell.get() == None {
S::yield_wait();
}
}
}
});

match humidity_cell.get() {
None => Err(ErrorCode::Busy),
Some(hum_val) => Ok(hum_val),
}
}
}

pub struct HumidityListener<F: Fn(i32)>(pub F);
impl<F: Fn(i32)> Upcall<OneId<DRIVER_NUM, 0>> for HumidityListener<F> {
fn upcall(&self, hum_val: u32, _arg1: u32, _arg2: u32) {
self.0(hum_val as i32)
}
}

#[cfg(test)]
mod tests;

// -----------------------------------------------------------------------------
// Driver number and command IDs
// -----------------------------------------------------------------------------

const DRIVER_NUM: u32 = 0x60001;

// Command IDs

const EXISTS: u32 = 0;
const READ_HUM: u32 = 1;
81 changes: 81 additions & 0 deletions apis/humidity/src/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
use core::cell::Cell;
use libtock_platform::{share, ErrorCode, Syscalls, YieldNoWaitReturn};
use libtock_unittest::fake;

type Humidity = super::Humidity<fake::Syscalls>;

#[test]
fn no_driver() {
let _kernel = fake::Kernel::new();
assert_eq!(Humidity::exists(), Err(ErrorCode::NoDevice));
}

#[test]
fn driver_check() {
let kernel = fake::Kernel::new();
let driver = fake::Humidity::new();
kernel.add_driver(&driver);

assert_eq!(Humidity::exists(), Ok(()));
}

#[test]
fn read_humidity() {
let kernel = fake::Kernel::new();
let driver = fake::Humidity::new();
kernel.add_driver(&driver);

assert_eq!(Humidity::read_humidity(), Ok(()));
assert!(driver.is_busy());

assert_eq!(Humidity::read_humidity(), Err(ErrorCode::Busy));
assert_eq!(Humidity::read_humidity_sync(), Err(ErrorCode::Busy));
}

#[test]
fn register_unregister_listener() {
let kernel = fake::Kernel::new();
let driver = fake::Humidity::new();
kernel.add_driver(&driver);

let humidity_cell: Cell<Option<i32>> = Cell::new(None);
let listener = crate::HumidityListener(|hum_val| {
humidity_cell.set(Some(hum_val));
});
share::scope(|subscribe| {
assert_eq!(Humidity::read_humidity(), Ok(()));
driver.set_value(100);
assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall);

assert_eq!(Humidity::register_listener(&listener, subscribe), Ok(()));
assert_eq!(Humidity::read_humidity(), Ok(()));
driver.set_value(100);
assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::Upcall);
assert_eq!(humidity_cell.get(), Some(100));

Humidity::unregister_listener();
assert_eq!(Humidity::read_humidity(), Ok(()));
driver.set_value(100);
assert_eq!(fake::Syscalls::yield_no_wait(), YieldNoWaitReturn::NoUpcall);
});
}

#[test]
fn read_humidity_sync() {
let kernel = fake::Kernel::new();
let driver = fake::Humidity::new();
kernel.add_driver(&driver);

driver.set_value_sync(1000);
assert_eq!(Humidity::read_humidity_sync(), Ok(1000));
}

#[test]
fn negative_value() {
let kernel = fake::Kernel::new();
let driver = fake::Humidity::new();
kernel.add_driver(&driver);

driver.set_value_sync(-1000);
assert_eq!(Humidity::read_humidity_sync(), Ok(-1000));
}
41 changes: 41 additions & 0 deletions examples/humidity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//! A simple libtock-rs example. Checks for humidity driver
//! and samples the sensor every 2 seconds.

#![no_main]
#![no_std]

use core::fmt::Write;
use libtock::console::Console;

use libtock::alarm::{Alarm, Milliseconds};
use libtock::humidity::Humidity;
use libtock::runtime::{set_main, stack_size};

set_main! {main}
stack_size! {0x200}

fn main() {
match Humidity::exists() {
Ok(()) => writeln!(Console::writer(), "humidity driver available").unwrap(),
Err(_) => {
writeln!(Console::writer(), "humidity driver unavailable").unwrap();
return;
}
}
Comment on lines +18 to +24

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
match Humidity::exists() {
Ok(()) => writeln!(Console::writer(), "humidity driver available").unwrap(),
Err(_) => {
writeln!(Console::writer(), "humidity driver unavailable").unwrap();
return;
}
}
if Humidity::exists().is_err() {
writeln!(Console::writer(), "humidity driver unavailable").unwrap();
return;
}


loop {
match Humidity::read_humidity_sync() {
Ok(hum_val) => writeln!(
Console::writer(),
"Humidity: {}{}.{}*C\n",
if hum_val > 0 { "" } else { "-" },
i32::abs(hum_val) / 100,
i32::abs(hum_val) % 100
)
.unwrap(),
Err(_) => writeln!(Console::writer(), "error while reading humidity",).unwrap(),
}

Alarm::sleep_for(Milliseconds(2000)).unwrap();
}
}
Loading