Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: introduce a sensor abstraction #474

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c58be44
feat: introduce a sensor abstraction
ROMemories Oct 17, 2024
28cf4c8
feat(sensors): add support for defmt
ROMemories Oct 18, 2024
2e8f68d
fixup! feat: introduce a sensor abstraction
ROMemories Oct 18, 2024
2f9c923
fixup! feat: introduce a sensor abstraction
ROMemories Oct 18, 2024
8a60047
fixup! feat: introduce a sensor abstraction
ROMemories Oct 18, 2024
1070cea
fixup! feat: introduce a sensor abstraction
ROMemories Oct 18, 2024
e39d300
fixup! feat: introduce a sensor abstraction
ROMemories Oct 18, 2024
a41e0f0
fixup! feat: introduce a sensor abstraction
ROMemories Oct 18, 2024
0b9ff53
fixup! feat: introduce a sensor abstraction
ROMemories Oct 18, 2024
d400d4d
fixup! feat: introduce a sensor abstraction
ROMemories Oct 18, 2024
848c07b
fixup! feat: introduce a sensor abstraction
ROMemories Oct 18, 2024
a23c4dc
fixup! feat: introduce a sensor abstraction
ROMemories Oct 18, 2024
50d0272
fixup! feat: introduce a sensor abstraction
ROMemories Oct 18, 2024
40e4f49
fixup! feat: introduce a sensor abstraction
ROMemories Oct 22, 2024
74455ec
fixup! feat: introduce a sensor abstraction
ROMemories Oct 22, 2024
1fe05fa
fixup! feat: introduce a sensor abstraction
ROMemories Oct 22, 2024
4792e00
fixup! feat: introduce a sensor abstraction
ROMemories Oct 22, 2024
df634e4
fixup! feat: introduce a sensor abstraction
ROMemories Oct 22, 2024
da6aea5
fixup! feat: introduce a sensor abstraction
ROMemories Oct 22, 2024
6f2eff7
fixup! feat: introduce a sensor abstraction
ROMemories Oct 23, 2024
c471c39
fixup! feat: introduce a sensor abstraction
ROMemories Oct 23, 2024
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: 1 addition & 1 deletion .github/workflows/build-deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:

- name: Build rustdoc docs
run: |
cargo doc -p riot-rs --features bench,csprng,executor-thread,external-interrupts,hwrng,i2c,no-boards,random,threading,usb
cargo doc -p riot-rs --features bench,csprng,executor-thread,external-interrupts,hwrng,i2c,no-boards,random,sensors,threading,usb
echo "<meta http-equiv=\"refresh\" content=\"0; url=riot_rs\">" > target/doc/index.html
mkdir -p ./_site/dev/docs/api && mv target/doc/* ./_site/dev/docs/api

Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ jobs:
# TODO: we'll eventually want to enable relevant features
- name: Run crate tests
run: |
cargo test --no-default-features --features i2c,no-boards -p riot-rs -p riot-rs-embassy -p riot-rs-embassy-common -p riot-rs-runqueue -p riot-rs-threads -p riot-rs-macros
cargo test --no-default-features --features i2c,no-boards -p riot-rs -p riot-rs-embassy -p riot-rs-embassy-common -p riot-rs-runqueue -p riot-rs-sensors -p riot-rs-threads -p riot-rs-macros
cargo test -p rbi -p ringbuffer -p coapcore

# We need to set `RUSTDOCFLAGS` as well in the following jobs, because it
Expand Down Expand Up @@ -176,7 +176,7 @@ jobs:
- name: clippy
uses: clechasseur/rs-clippy-check@v3
with:
args: --verbose --locked --features no-boards,external-interrupts -p riot-rs -p riot-rs-boards -p riot-rs-chips -p riot-rs-debug -p riot-rs-embassy -p riot-rs-embassy-common -p riot-rs-macros -p riot-rs-random -p riot-rs-rt -p riot-rs-threads -p riot-rs-utils -- --deny warnings
args: --verbose --locked --features no-boards,external-interrupts,sensors -p riot-rs -p riot-rs-boards -p riot-rs-chips -p riot-rs-debug -p riot-rs-embassy -p riot-rs-embassy-common -p riot-rs-macros -p riot-rs-random -p riot-rs-rt -p riot-rs-sensors -p riot-rs-threads -p riot-rs-utils -- --deny warnings

- run: echo 'RUSTFLAGS=--cfg context="esp32c6"' >> $GITHUB_ENV
- name: clippy for ESP32
Expand Down Expand Up @@ -206,7 +206,7 @@ jobs:
- run: echo 'RUSTFLAGS=' >> $GITHUB_ENV

- name: rustdoc
run: RUSTDOCFLAGS='-D warnings' cargo doc -p riot-rs --features bench,csprng,executor-thread,external-interrupts,hwrng,i2c,no-boards,random,threading,usb
run: RUSTDOCFLAGS='-D warnings' cargo doc -p riot-rs --features bench,csprng,executor-thread,external-interrupts,hwrng,i2c,no-boards,random,sensors,threading,usb

- name: rustdoc for ESP32
run: RUSTDOCFLAGS='-D warnings --cfg context="esp32c6"' cargo doc --target=riscv32imac-unknown-none-elf --features external-interrupts,i2c,esp-hal/esp32c6,esp-hal-embassy/esp32c6 -p riot-rs-esp
Expand Down
32 changes: 32 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ members = [
"src/riot-rs-nrf",
"src/riot-rs-random",
"src/riot-rs-rp",
"src/riot-rs-sensors",
"src/riot-rs-stm32",
"tests/benchmarks/bench_sched_flags",
"tests/benchmarks/bench_sched_yield",
Expand Down Expand Up @@ -85,9 +86,11 @@ riot-rs-boards = { path = "src/riot-rs-boards", default-features = false }
riot-rs-debug = { path = "src/riot-rs-debug", default-features = false }
riot-rs-embassy = { path = "src/riot-rs-embassy", default-features = false }
riot-rs-embassy-common = { path = "src/riot-rs-embassy-common" }
riot-rs-macros = { path = "src/riot-rs-macros" }
riot-rs-random = { path = "src/riot-rs-random" }
riot-rs-rt = { path = "src/riot-rs-rt" }
riot-rs-runqueue = { path = "src/riot-rs-runqueue" }
riot-rs-sensors = { path = "src/riot-rs-sensors" }
riot-rs-stm32 = { path = "src/riot-rs-stm32" }
riot-rs-utils = { path = "src/riot-rs-utils", default-features = false }

Expand All @@ -102,6 +105,7 @@ once_cell = { version = "=1.19.0", default-features = false, features = [
"critical-section",
] }
paste = { version = "1.0" }
pin-project = "1.1.6"
static_cell = { version = "2.0.0", features = ["nightly"] }

[profile.dev]
Expand Down
10 changes: 10 additions & 0 deletions src/riot-rs-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,13 @@ trybuild = "1.0.89"

[lib]
proc-macro = true

[features]
# These features could be codegened
max-reading-value-min-count-2 = []
max-reading-value-min-count-3 = []
max-reading-value-min-count-4 = []
max-reading-value-min-count-6 = []
max-reading-value-min-count-7 = []
max-reading-value-min-count-9 = []
max-reading-value-min-count-12 = []
134 changes: 134 additions & 0 deletions src/riot-rs-macros/src/define_count_adjusted_sensor_enums.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/// Generates sensor-related enums whose number of variants needs to be adjusted based on Cargo
/// features, to accommodate the sensor driver returning the largest number of values.
///
/// One single type must be defined so that it can be used in the Future returned by sensor
/// drivers, which must be the same for every sensor driver so it can be part of the `Sensor`
/// trait.
#[proc_macro]
pub fn define_count_adjusted_sensor_enums(_item: TokenStream) -> TokenStream {
use quote::quote;

#[allow(clippy::wildcard_imports)]
use define_count_adjusted_enum::*;

// The order of these feature-gated statements is important as these features are not meant to
// be mutually exclusive.
#[allow(unused_variables, reason = "overridden by feature selection")]
let count = 1;
#[cfg(feature = "max-reading-value-min-count-2")]
let count = 2;
#[cfg(feature = "max-reading-value-min-count-3")]
let count = 3;
#[cfg(feature = "max-reading-value-min-count-4")]
let count = 4;
#[cfg(feature = "max-reading-value-min-count-6")]
let count = 6;
#[cfg(feature = "max-reading-value-min-count-7")]
let count = 7;
#[cfg(feature = "max-reading-value-min-count-9")]
let count = 9;
#[cfg(feature = "max-reading-value-min-count-12")]
let count = 12;

let physical_values_variants = (1..=count).map(|i| {
let variant = variant_name(i);
quote! { #variant([Value; #i]) }
});
let physical_values_first_value = (1..=count).map(|i| {
let variant = variant_name(i);
quote! {
Self::#variant(values) => {
if let Some(value) = values.first() {
*value
} else {
// NOTE(no-panic): there is always at least one value
unreachable!();
}
}
}
});

let reading_axes_variants = (1..=count).map(|i| {
let variant = variant_name(i);
quote! { #variant([ReadingAxis; #i]) }
});

let values_iter = (1..=count)
.map(|i| {
let variant = variant_name(i);
quote! { Self::#variant(values) => values.iter().copied() }
})
.collect::<Vec<_>>();

let expanded = quote! {
/// Values returned by a sensor driver.
///
/// This type implements [`Reading`] to iterate over the values.
///
/// # Note
///
/// This type is automatically generated, the number of variants is automatically adjusted.
#[derive(Debug, Copy, Clone)]
pub enum Values {
#[doc(hidden)]
#(#physical_values_variants),*
}

impl Reading for Values {
fn value(&self) -> Value {
match self {
#(#physical_values_first_value),*
}
}

fn values(&self) -> impl ExactSizeIterator<Item = Value> {
match self {
#(#values_iter),*
}
}
}

/// Metadata required to interpret values returned by [`Sensor::wait_for_reading()`].
///
/// # Note
///
/// This type is automatically generated, the number of variants is automatically adjusted.
#[derive(Debug, Copy, Clone)]
pub enum ReadingAxes {
#[doc(hidden)]
#(#reading_axes_variants),*,
}

impl ReadingAxes {
/// Returns an iterator over the underlying [`ReadingAxis`] items.
///
/// For a given sensor driver, the number and order of items match the one of
/// [`Values`].
/// [`Iterator::zip()`] can be useful to zip the returned iterator with the one
/// obtained with [`Reading::values()`].
pub fn iter(&self) -> impl Iterator<Item = ReadingAxis> + '_ {
match self {
#(#values_iter),*,
}
}

/// Returns the first [`ReadingAxis`].
pub fn first(&self) -> ReadingAxis {
if let Some(value) = self.iter().next() {
value
} else {
// NOTE(no-panic): there is always at least one value.
unreachable!();
}
}
}
};

TokenStream::from(expanded)
}

mod define_count_adjusted_enum {
pub fn variant_name(index: usize) -> syn::Ident {
quote::format_ident!("V{index}")
}
}
1 change: 1 addition & 0 deletions src/riot-rs-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod utils;
use proc_macro::TokenStream;

include!("config.rs");
include!("define_count_adjusted_sensor_enums.rs");
include!("spawner.rs");
include!("task.rs");
include!("thread.rs");
31 changes: 31 additions & 0 deletions src/riot-rs-sensors/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[package]
name = "riot-rs-sensors"
version.workspace = true
authors.workspace = true
license.workspace = true
edition.workspace = true
repository.workspace = true

[lints]
workspace = true

[dependencies]
defmt = { workspace = true, optional = true }
embassy-sync = { workspace = true }
linkme = { workspace = true }
pin-project = { workspace = true }
riot-rs-macros = { workspace = true }

[features]
defmt = ["dep:defmt"]

# These features could be codegened
max-reading-value-min-count-2 = ["riot-rs-macros/max-reading-value-min-count-2"]
max-reading-value-min-count-3 = ["riot-rs-macros/max-reading-value-min-count-3"]
max-reading-value-min-count-4 = ["riot-rs-macros/max-reading-value-min-count-4"]
max-reading-value-min-count-6 = ["riot-rs-macros/max-reading-value-min-count-6"]
max-reading-value-min-count-7 = ["riot-rs-macros/max-reading-value-min-count-7"]
max-reading-value-min-count-9 = ["riot-rs-macros/max-reading-value-min-count-9"]
max-reading-value-min-count-12 = [
"riot-rs-macros/max-reading-value-min-count-12",
]
24 changes: 24 additions & 0 deletions src/riot-rs-sensors/src/category.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/// Categories a sensor driver can be part of.
///
/// A sensor driver can be part of multiple categories.
///
/// # For sensor driver implementors
///
/// Missing variants can be added when required.
/// Please open an issue to discuss it.
// Built upon https://doc.riot-os.org/group__drivers__saul.html#ga8f2dfec7e99562dbe5d785467bb71bbb
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Category {
/// Accelerometer.
Accelerometer,
/// Humidity sensor.
Humidity,
/// Humidity and temperature sensor.
HumidityTemperature,
/// Push button.
PushButton,
/// Temperature sensor.
Temperature,
}
44 changes: 44 additions & 0 deletions src/riot-rs-sensors/src/label.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/// Label of a [`Value`](crate::sensor::Value) part of a
/// [`Values`](crate::sensor::Values) tuple.
///
/// # For sensor driver implementors
///
/// Missing variants can be added when required.
/// Please open an issue to discuss it.
///
/// [`Label::Main`] must be used for sensor drivers returning a single
/// [`Value`](crate::sensor::Value), even if a more specific label exists for the
/// physical quantity.
/// This allows consumers displaying the label to ignore it for sensor drivers returning a single
/// [`Value`](crate::sensor::Value).
/// Other labels are reserved for sensor drivers returning multiple physical quantities.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Label {
/// Used for sensor drivers returning a single [`Value`](crate::sensor::Value).
Main,
/// Humidity.
Humidity,
/// Temperature.
Temperature,
/// X axis.
X,
/// Y axis.
Y,
/// Z axis.
Z,
}

impl core::fmt::Display for Label {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Main => write!(f, ""),
Self::Humidity => write!(f, "Humidity"),
Self::Temperature => write!(f, "Temperature"),
Self::X => write!(f, "X"),
Self::Y => write!(f, "Y"),
Self::Z => write!(f, "Z"),
}
}
}
Loading
Loading