From 876679c5d439b70cead399b959b14c9b823c776f Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Sun, 9 Jun 2024 14:42:49 -0700 Subject: [PATCH 1/9] Add `embedded-hal-async` support This commit adds support for the `embedded-hal-async` crate in addition to `embedded-hal`. I've done this by adding a separate `AsyncSht4x` type, based on the assumption that most projects won't need to use both the blocking `embedded-hal` traits and the `embedded-hal-async` traits at the same time, and providing `async fn` methods on a separate type with the same names as the blocking ones seemed a bit nicer than having one type that has both `fn measure` and `async fn measure_async` and so on. Support for `embedded-hal-async` is gated behind the `embedded-hal-async` feature flag, so the dependency is not enabled by default. Note that this branch depends on my PR #6, which updates this crate to use `embedded-hal` v1.0, and currently contains the commit from that change as well. Once #6 has merged, this branch will need to be rebased onto the main branch. It also depends on my upstream PR adding `embedded-hal-async` support to `sensirion-i2c-rs`, sensirion/sensirion-i2c-rs#30, which has been [merged], but hasn't been published to crates.io yet. Currently, this branch adds a Cargo `[patch]` to use a Git dep on `sensirion-i2c-rs`. So, this change cannot be released to crates.io until upstream publishes a new release of `sensirion-i2c-rs`. Hopefully they do that soon! :) [merged]: https://github.com/Sensirion/sensirion-i2c-rs/commit/f7b9f3a81b777bc6e6b2f0acb4c1ef9c57dfa06d --- Cargo.toml | 12 +++- README.md | 24 +++++++ src/async_sht4x.rs | 165 +++++++++++++++++++++++++++++++++++++++++++++ src/error.rs | 3 +- src/lib.rs | 6 ++ src/sht4x.rs | 20 +++--- 6 files changed, 217 insertions(+), 13 deletions(-) create mode 100644 src/async_sht4x.rs diff --git a/Cargo.toml b/Cargo.toml index f78bb64..cc98589 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,12 +20,22 @@ exclude = [ ".gitignore", ] - [dependencies] defmt = { version = "0.3.2", optional = true } embedded-hal = "1.0.0" +embedded-hal-async = { version = "1.0.0", optional = true } fixed = "1.20.0" sensirion-i2c = "0.3.0" [features] defmt = ["dep:defmt"] +embedded-hal-async = ["dep:embedded-hal-async", "sensirion-i2c/embedded-hal-async"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +# Necessary for `embedded-hal-async` support until a new release of +# `sensirion-i2c-rs` is published. +[patch.crates-io] +sensirion-i2c = { git = "https://github.com/sensirion/sensirion-i2c-rs", rev = "f7b9f3a81b777bc6e6b2f0acb4c1ef9c57dfa06d" } diff --git a/README.md b/README.md index d17f43d..de1193c 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,30 @@ if let Ok(measurement) = measurement { } ``` +## `embedded-hal-async` support + +This crate has optional support for the [`embedded-hal-async`] crate, which +provides `async` versions of the `I2c` and `DelayNs` traits. Async support +is an off-by-default optional feature, so that projects which aren't using +[`embedded-hal-async`] can avoid the additional dependency. + +To use this crate with `embedded-hal-async`, enable the `embedded-hal-async` +feature flag in your `Cargo.toml`: + +```toml +sht4x = { version = "0.2", features = ["embedded-hal-async"] } +``` + +Once the `embedded-hal-async` feature is enabled, construct an instance of +the `AsyncSht4x` struct, providing types implementing the +[`embedded_hal_async::i2c::I2c`] and [`embedded_hal_async::delay::DelayNs`] +traits. The `AsyncSht4x` struct is identical to the `Sht4x` struct, +except that its methods are `async fn`s. + +[`embedded-hal-async`]: https://crates.io/crates/embedded-hal-async +[`embedded_hal_async::i2c::I2c`]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/i2c/trait.I2c.html +[`embedded_hal_async::delay::DelayNs`]: https://docs.rs/embedded-hal-async/latest/embedded_hal_async/delay/trait.DelayNs.html + ## Related Work diff --git a/src/async_sht4x.rs b/src/async_sht4x.rs new file mode 100644 index 0000000..c7a48b6 --- /dev/null +++ b/src/async_sht4x.rs @@ -0,0 +1,165 @@ +use crate::{ + commands::Command, + error::Error, + sht4x::RESPONSE_LEN, + types::{Address, HeatingDuration, HeatingPower, Measurement, Precision, SensorData}, +}; +use core::marker::PhantomData; +use embedded_hal_async::{delay::DelayNs, i2c::I2c}; +use sensirion_i2c::i2c_async; + +/// Async driver for STH4x sensors. +/// +/// This type behaves identically to the [`Sht4x`](crate::Sht4x) type, except +/// that it uses the `embedded-hal-async` [`I2c`] and [`DelayNs`] traits instead +/// of the `embedded-hal` traits, and all of its methods are `async fn`s. +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Debug)] +pub struct AsyncSht4x { + i2c: I, + address: Address, + // If we want to globally define the delay type for this struct, we have to consume the type + // parameter. + _delay: PhantomData, +} + +impl AsyncSht4x +where + I: I2c, + D: DelayNs, +{ + /// Creates a new driver instance using the given I2C bus. It configures the default I2C + /// address 0x44 used by most family members. + /// + /// For operating multiple devices on the same bus, + /// [`shared-bus`](https://github.com/Rahix/shared-bus) might come in handy. + pub fn new(i2c: I) -> Self { + Self::new_with_address(i2c, Address::Address0x44) + } + + /// Crates a new driver instance using the given I2C bus and address. This constructor allows + /// to instantiate the driver for the SHT40-BD1B which uses the non-default I2C address 0x45. + /// + /// For operating multiple devices on the same bus, + /// [`shared-bus`](https://github.com/Rahix/shared-bus) might come in handy. + pub fn new_with_address(i2c: I, address: Address) -> Self { + Self { + i2c, + address, + _delay: PhantomData, + } + } + + /// Destroys the driver and returns the used I2C bus. + pub fn destroy(self) -> I { + self.i2c + } + + /// Activates the heater and performs a measurement returning measurands in SI units. + /// + /// **Note:** The heater is designed to be used up to 10 % of the sensor's lifetime. Please + /// check the + /// [datasheet](https://sensirion.com/media/documents/33FD6951/624C4357/Datasheet_SHT4x.pdf), + /// section 4.9 _Heater Operation_ for details. + pub async fn heat_and_measure( + &mut self, + power: HeatingPower, + duration: HeatingDuration, + delay: &mut D, + ) -> Result> { + let raw = self.heat_and_measure_raw(power, duration, delay).await?; + + Ok(Measurement::from(raw)) + } + + /// Activates the heater and performs a measurement returning raw sensor data. + /// + /// **Note:** The heater is designed to be used up to 10 % of the sensor's lifetime. Please + /// check the + /// [datasheet](https://sensirion.com/media/documents/33FD6951/624C4357/Datasheet_SHT4x.pdf), + /// section 4.9 _Heater Operation_ for details. + pub async fn heat_and_measure_raw( + &mut self, + power: HeatingPower, + duration: HeatingDuration, + delay: &mut D, + ) -> Result> { + let command = Command::from((power, duration)); + + self.write_command_and_delay_for_execution(command, delay) + .await?; + let response = self.read_response().await?; + let raw = crate::sht4x::sensor_data_from_response(&response); + + Ok(raw) + } + + /// Performs a measurement returning measurands in SI units. + pub async fn measure( + &mut self, + precision: Precision, + delay: &mut D, + ) -> Result> { + let raw = self.measure_raw(precision, delay).await?; + Ok(Measurement::from(raw)) + } + + /// Performs a measurement returning raw sensor data. + pub async fn measure_raw( + &mut self, + precision: Precision, + delay: &mut D, + ) -> Result> { + let command = Command::from(precision); + + self.write_command_and_delay_for_execution(command, delay) + .await?; + let response = self.read_response().await?; + let raw = crate::sht4x::sensor_data_from_response(&response); + + Ok(raw) + } + + /// Reads the sensor's serial number. + pub async fn serial_number(&mut self, delay: &mut D) -> Result> { + self.write_command_and_delay_for_execution(Command::SerialNumber, delay) + .await?; + let response = self.read_response().await?; + + Ok(u32::from_be_bytes([ + response[0], + response[1], + response[3], + response[4], + ])) + } + + /// Performs a soft reset of the sensor. + pub async fn soft_reset(&mut self, delay: &mut D) -> Result<(), Error> { + self.write_command_and_delay_for_execution(Command::SoftReset, delay) + .await + } + + async fn read_response(&mut self) -> Result<[u8; RESPONSE_LEN], Error> { + let mut response = [0; RESPONSE_LEN]; + + i2c_async::read_words_with_crc(&mut self.i2c, self.address.into(), &mut response).await?; + + Ok(response) + } + + async fn write_command_and_delay_for_execution( + &mut self, + command: Command, + delay: &mut D, + ) -> Result<(), Error> { + let code = command.code(); + + i2c_async::write_command_u8(&mut self.i2c, self.address.into(), code) + .await + .map_err(Error::I2c)?; + delay.delay_ms(command.duration_ms()).await; + + Ok(()) + } +} diff --git a/src/error.rs b/src/error.rs index 5c68796..49d92b3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,3 @@ -use embedded_hal::i2c::I2c; use sensirion_i2c::i2c; /// Error conditions from accessing SHT4x sensors. @@ -14,7 +13,7 @@ pub enum Error { impl From> for Error where - I: I2c, + I: embedded_hal::i2c::ErrorType, { fn from(err: i2c::Error) -> Self { match err { diff --git a/src/lib.rs b/src/lib.rs index 51c3c40..bd46055 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,18 @@ #![deny(unsafe_code)] #![no_std] #![doc = include_str!("../README.md")] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] mod commands; mod error; mod sht4x; mod types; +#[cfg(feature = "embedded-hal-async")] +mod async_sht4x; +#[cfg(feature = "embedded-hal-async")] +pub use self::async_sht4x::AsyncSht4x; + pub use crate::error::*; pub use crate::sht4x::*; pub use crate::types::*; diff --git a/src/sht4x.rs b/src/sht4x.rs index a59a5cf..bf969b7 100644 --- a/src/sht4x.rs +++ b/src/sht4x.rs @@ -7,7 +7,7 @@ use core::marker::PhantomData; use embedded_hal::{delay::DelayNs, i2c::I2c}; use sensirion_i2c::i2c; -const RESPONSE_LEN: usize = 6; +pub(crate) const RESPONSE_LEN: usize = 6; /// Driver for STH4x sensors. #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -108,7 +108,7 @@ where self.write_command_and_delay_for_execution(command, delay)?; let response = self.read_response()?; - let raw = self.sensor_data_from_response(&response); + let raw = sensor_data_from_response(&response); Ok(raw) } @@ -133,7 +133,7 @@ where self.write_command_and_delay_for_execution(command, delay)?; let response = self.read_response()?; - let raw = self.sensor_data_from_response(&response); + let raw = sensor_data_from_response(&response); Ok(raw) } @@ -164,13 +164,6 @@ where Ok(response) } - fn sensor_data_from_response(&self, response: &[u8; RESPONSE_LEN]) -> SensorData { - SensorData { - temperature: u16::from_be_bytes([response[0], response[1]]), - humidity: u16::from_be_bytes([response[3], response[4]]), - } - } - fn write_command_and_delay_for_execution( &mut self, command: Command, @@ -184,3 +177,10 @@ where Ok(()) } } + +pub(crate) fn sensor_data_from_response(response: &[u8; RESPONSE_LEN]) -> SensorData { + SensorData { + temperature: u16::from_be_bytes([response[0], response[1]]), + humidity: u16::from_be_bytes([response[3], response[4]]), + } +} From 50da8ad1a509e66c376bd6898f7b661b8cd89694 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Sat, 20 Jul 2024 09:57:30 -0700 Subject: [PATCH 2/9] Update to `sensirion-i2c` v0.4 Now that my changes have been published upstream, we can remove the Git patch. This PR should now be okay to merge! --- Cargo.toml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cc98589..3ee17bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ defmt = { version = "0.3.2", optional = true } embedded-hal = "1.0.0" embedded-hal-async = { version = "1.0.0", optional = true } fixed = "1.20.0" -sensirion-i2c = "0.3.0" +sensirion-i2c = "0.4" [features] defmt = ["dep:defmt"] @@ -34,8 +34,3 @@ embedded-hal-async = ["dep:embedded-hal-async", "sensirion-i2c/embedded-hal-asyn [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] - -# Necessary for `embedded-hal-async` support until a new release of -# `sensirion-i2c-rs` is published. -[patch.crates-io] -sensirion-i2c = { git = "https://github.com/sensirion/sensirion-i2c-rs", rev = "f7b9f3a81b777bc6e6b2f0acb4c1ef9c57dfa06d" } From 53dce0137ce7b12081331a4773f63d7d52eeada1 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Sat, 20 Jul 2024 09:58:44 -0700 Subject: [PATCH 3/9] Rename async driver to `Sht4xAsync` This way, the naming scheme is more consistent with the async driver I added to the `sgp30` crate. See: dbrgn/sgp30-rs@cb769b04bb07b8daa5ab0a807ad01849804534bf --- README.md | 4 ++-- src/async_sht4x.rs | 4 ++-- src/lib.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index de1193c..0d52fe1 100644 --- a/README.md +++ b/README.md @@ -73,9 +73,9 @@ sht4x = { version = "0.2", features = ["embedded-hal-async"] } ``` Once the `embedded-hal-async` feature is enabled, construct an instance of -the `AsyncSht4x` struct, providing types implementing the +the `Sht4xAsync` struct, providing types implementing the [`embedded_hal_async::i2c::I2c`] and [`embedded_hal_async::delay::DelayNs`] -traits. The `AsyncSht4x` struct is identical to the `Sht4x` struct, +traits. The `Sht4xAsync` struct is identical to the `Sht4x` struct, except that its methods are `async fn`s. [`embedded-hal-async`]: https://crates.io/crates/embedded-hal-async diff --git a/src/async_sht4x.rs b/src/async_sht4x.rs index c7a48b6..cfd5e36 100644 --- a/src/async_sht4x.rs +++ b/src/async_sht4x.rs @@ -15,7 +15,7 @@ use sensirion_i2c::i2c_async; /// of the `embedded-hal` traits, and all of its methods are `async fn`s. #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Debug)] -pub struct AsyncSht4x { +pub struct Sht4xAsync { i2c: I, address: Address, // If we want to globally define the delay type for this struct, we have to consume the type @@ -23,7 +23,7 @@ pub struct AsyncSht4x { _delay: PhantomData, } -impl AsyncSht4x +impl Sht4xAsync where I: I2c, D: DelayNs, diff --git a/src/lib.rs b/src/lib.rs index bd46055..ad9ee8f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ mod types; #[cfg(feature = "embedded-hal-async")] mod async_sht4x; #[cfg(feature = "embedded-hal-async")] -pub use self::async_sht4x::AsyncSht4x; +pub use self::async_sht4x::Sht4xAsync; pub use crate::error::*; pub use crate::sht4x::*; From 72f8f3fbf0098061c209b2a744e0e88ca8535626 Mon Sep 17 00:00:00 2001 From: Christian Meusel Date: Wed, 20 Nov 2024 12:34:29 +0100 Subject: [PATCH 4/9] Streamline error type parameter name It's about the error and not the I2C implementation type. --- src/error.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/error.rs b/src/error.rs index 49d92b3..ffb5e36 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,11 +11,11 @@ pub enum Error { Crc, } -impl From> for Error +impl From> for Error where - I: embedded_hal::i2c::ErrorType, + E: embedded_hal::i2c::ErrorType, { - fn from(err: i2c::Error) -> Self { + fn from(err: i2c::Error) -> Self { match err { i2c::Error::Crc => Error::Crc, i2c::Error::I2cRead(e) => Error::I2c(e), From 764b6c9912f2f2e9c3d344a57c7fd8e39c1e4fe5 Mon Sep 17 00:00:00 2001 From: Christian Meusel Date: Wed, 20 Nov 2024 12:40:48 +0100 Subject: [PATCH 5/9] Bump depenencies to latest release --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3ee17bf..91d3e2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,10 +21,10 @@ exclude = [ ] [dependencies] -defmt = { version = "0.3.2", optional = true } +defmt = { version = "0.3.8", optional = true } embedded-hal = "1.0.0" embedded-hal-async = { version = "1.0.0", optional = true } -fixed = "1.20.0" +fixed = "1.28.0" sensirion-i2c = "0.4" [features] From d188cfbce3179be88e64825b92df2be32b0f652f Mon Sep 17 00:00:00 2001 From: Christian Meusel Date: Sat, 1 Mar 2025 20:45:46 +0100 Subject: [PATCH 6/9] Streamline async driver module name too With the driver struct already named Sht4xAsync, its source file should follow the same order. --- src/lib.rs | 4 ++-- src/{async_sht4x.rs => sht4x_async.rs} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename src/{async_sht4x.rs => sht4x_async.rs} (100%) diff --git a/src/lib.rs b/src/lib.rs index ad9ee8f..c4f178d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,9 +9,9 @@ mod sht4x; mod types; #[cfg(feature = "embedded-hal-async")] -mod async_sht4x; +mod sht4x_async; #[cfg(feature = "embedded-hal-async")] -pub use self::async_sht4x::Sht4xAsync; +pub use self::sht4x_async::Sht4xAsync; pub use crate::error::*; pub use crate::sht4x::*; diff --git a/src/async_sht4x.rs b/src/sht4x_async.rs similarity index 100% rename from src/async_sht4x.rs rename to src/sht4x_async.rs From 6f8c2596f7cb3af998403d786606bcc660127056 Mon Sep 17 00:00:00 2001 From: Christian Meusel Date: Sat, 1 Mar 2025 21:18:34 +0100 Subject: [PATCH 7/9] Share extracting serial number from response Sensor data extraction is already shared and the serial number extraction code should be shared as well. --- src/sht4x.rs | 12 +++++------- src/sht4x_async.rs | 8 +------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/sht4x.rs b/src/sht4x.rs index bf969b7..2615f4e 100644 --- a/src/sht4x.rs +++ b/src/sht4x.rs @@ -142,13 +142,7 @@ where pub fn serial_number(&mut self, delay: &mut D) -> Result> { self.write_command_and_delay_for_execution(Command::SerialNumber, delay)?; let response = self.read_response()?; - - Ok(u32::from_be_bytes([ - response[0], - response[1], - response[3], - response[4], - ])) + Ok(serial_number_from_response(&response)) } /// Performs a soft reset of the sensor. @@ -184,3 +178,7 @@ pub(crate) fn sensor_data_from_response(response: &[u8; RESPONSE_LEN]) -> Sensor humidity: u16::from_be_bytes([response[3], response[4]]), } } + +pub(crate) fn serial_number_from_response(response: &[u8; RESPONSE_LEN]) -> u32 { + u32::from_be_bytes([response[0], response[1], response[3], response[4]]) +} diff --git a/src/sht4x_async.rs b/src/sht4x_async.rs index cfd5e36..f5e04b6 100644 --- a/src/sht4x_async.rs +++ b/src/sht4x_async.rs @@ -125,13 +125,7 @@ where self.write_command_and_delay_for_execution(Command::SerialNumber, delay) .await?; let response = self.read_response().await?; - - Ok(u32::from_be_bytes([ - response[0], - response[1], - response[3], - response[4], - ])) + Ok(crate::sht4x::serial_number_from_response(&response)) } /// Performs a soft reset of the sensor. From a1d34270d483c2de860c3c0e2ecc74a81f35891b Mon Sep 17 00:00:00 2001 From: Christian Meusel Date: Sat, 1 Mar 2025 21:42:39 +0100 Subject: [PATCH 8/9] Make extracting payload from response more explicit It's written in the sensors data sheet but I did not remember this after looking into the code after quite some time. --- src/sht4x.rs | 25 +++++++++++++++++-------- src/sht4x_async.rs | 6 +++--- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/sht4x.rs b/src/sht4x.rs index 2615f4e..6e82d12 100644 --- a/src/sht4x.rs +++ b/src/sht4x.rs @@ -7,6 +7,7 @@ use core::marker::PhantomData; use embedded_hal::{delay::DelayNs, i2c::I2c}; use sensirion_i2c::i2c; +const PAYLOAD_LEN: usize = 4; pub(crate) const RESPONSE_LEN: usize = 6; /// Driver for STH4x sensors. @@ -108,7 +109,7 @@ where self.write_command_and_delay_for_execution(command, delay)?; let response = self.read_response()?; - let raw = sensor_data_from_response(&response); + let raw = sensor_data_from_response(response); Ok(raw) } @@ -133,7 +134,7 @@ where self.write_command_and_delay_for_execution(command, delay)?; let response = self.read_response()?; - let raw = sensor_data_from_response(&response); + let raw = sensor_data_from_response(response); Ok(raw) } @@ -142,7 +143,7 @@ where pub fn serial_number(&mut self, delay: &mut D) -> Result> { self.write_command_and_delay_for_execution(Command::SerialNumber, delay)?; let response = self.read_response()?; - Ok(serial_number_from_response(&response)) + Ok(serial_number_from_response(response)) } /// Performs a soft reset of the sensor. @@ -172,13 +173,21 @@ where } } -pub(crate) fn sensor_data_from_response(response: &[u8; RESPONSE_LEN]) -> SensorData { +fn response_payload(response: [u8; RESPONSE_LEN]) -> [u8; PAYLOAD_LEN] { + // Response data comes in chunks of three bytes: [MSB, LSB, CRC]. The CRCs got already checked + // by sensirion_i2c::read_words_with_crc. So we just have to extract the payload data here. + [response[0], response[1], response[3], response[4]] +} + +pub(crate) fn sensor_data_from_response(response: [u8; RESPONSE_LEN]) -> SensorData { + let payload = response_payload(response); SensorData { - temperature: u16::from_be_bytes([response[0], response[1]]), - humidity: u16::from_be_bytes([response[3], response[4]]), + temperature: u16::from_be_bytes([payload[0], payload[1]]), + humidity: u16::from_be_bytes([payload[2], payload[3]]), } } -pub(crate) fn serial_number_from_response(response: &[u8; RESPONSE_LEN]) -> u32 { - u32::from_be_bytes([response[0], response[1], response[3], response[4]]) +pub(crate) fn serial_number_from_response(response: [u8; RESPONSE_LEN]) -> u32 { + let payload = response_payload(response); + u32::from_be_bytes(payload) } diff --git a/src/sht4x_async.rs b/src/sht4x_async.rs index f5e04b6..d3b85a7 100644 --- a/src/sht4x_async.rs +++ b/src/sht4x_async.rs @@ -89,7 +89,7 @@ where self.write_command_and_delay_for_execution(command, delay) .await?; let response = self.read_response().await?; - let raw = crate::sht4x::sensor_data_from_response(&response); + let raw = crate::sht4x::sensor_data_from_response(response); Ok(raw) } @@ -115,7 +115,7 @@ where self.write_command_and_delay_for_execution(command, delay) .await?; let response = self.read_response().await?; - let raw = crate::sht4x::sensor_data_from_response(&response); + let raw = crate::sht4x::sensor_data_from_response(response); Ok(raw) } @@ -125,7 +125,7 @@ where self.write_command_and_delay_for_execution(Command::SerialNumber, delay) .await?; let response = self.read_response().await?; - Ok(crate::sht4x::serial_number_from_response(&response)) + Ok(crate::sht4x::serial_number_from_response(response)) } /// Performs a soft reset of the sensor. From 40cef0dabc5c5fed0a2ada2e8f5caa75d4e51075 Mon Sep 17 00:00:00 2001 From: Christian Meusel Date: Sat, 1 Mar 2025 21:55:54 +0100 Subject: [PATCH 9/9] Factor out response data processing to responses.rs --- src/lib.rs | 1 + src/responses.rs | 23 +++++++++++++++++++++++ src/sht4x.rs | 23 +---------------------- src/sht4x_async.rs | 8 ++++---- 4 files changed, 29 insertions(+), 26 deletions(-) create mode 100644 src/responses.rs diff --git a/src/lib.rs b/src/lib.rs index c4f178d..9d27622 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ mod commands; mod error; +mod responses; mod sht4x; mod types; diff --git a/src/responses.rs b/src/responses.rs new file mode 100644 index 0000000..40a9f0f --- /dev/null +++ b/src/responses.rs @@ -0,0 +1,23 @@ +use crate::types::SensorData; + +const PAYLOAD_LEN: usize = 4; +pub(crate) const RESPONSE_LEN: usize = 6; + +fn response_payload(response: [u8; RESPONSE_LEN]) -> [u8; PAYLOAD_LEN] { + // Response data comes in chunks of three bytes: [MSB, LSB, CRC]. The CRCs got already checked + // by sensirion_i2c::read_words_with_crc. So we just have to extract the payload data here. + [response[0], response[1], response[3], response[4]] +} + +pub(crate) fn sensor_data_from_response(response: [u8; RESPONSE_LEN]) -> SensorData { + let payload = response_payload(response); + SensorData { + temperature: u16::from_be_bytes([payload[0], payload[1]]), + humidity: u16::from_be_bytes([payload[2], payload[3]]), + } +} + +pub(crate) fn serial_number_from_response(response: [u8; RESPONSE_LEN]) -> u32 { + let payload = response_payload(response); + u32::from_be_bytes(payload) +} diff --git a/src/sht4x.rs b/src/sht4x.rs index 6e82d12..279f9e6 100644 --- a/src/sht4x.rs +++ b/src/sht4x.rs @@ -1,15 +1,13 @@ use crate::{ commands::Command, error::Error, + responses::{sensor_data_from_response, serial_number_from_response, RESPONSE_LEN}, types::{Address, HeatingDuration, HeatingPower, Measurement, Precision, SensorData}, }; use core::marker::PhantomData; use embedded_hal::{delay::DelayNs, i2c::I2c}; use sensirion_i2c::i2c; -const PAYLOAD_LEN: usize = 4; -pub(crate) const RESPONSE_LEN: usize = 6; - /// Driver for STH4x sensors. #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Debug, Eq, Hash, PartialEq)] @@ -172,22 +170,3 @@ where Ok(()) } } - -fn response_payload(response: [u8; RESPONSE_LEN]) -> [u8; PAYLOAD_LEN] { - // Response data comes in chunks of three bytes: [MSB, LSB, CRC]. The CRCs got already checked - // by sensirion_i2c::read_words_with_crc. So we just have to extract the payload data here. - [response[0], response[1], response[3], response[4]] -} - -pub(crate) fn sensor_data_from_response(response: [u8; RESPONSE_LEN]) -> SensorData { - let payload = response_payload(response); - SensorData { - temperature: u16::from_be_bytes([payload[0], payload[1]]), - humidity: u16::from_be_bytes([payload[2], payload[3]]), - } -} - -pub(crate) fn serial_number_from_response(response: [u8; RESPONSE_LEN]) -> u32 { - let payload = response_payload(response); - u32::from_be_bytes(payload) -} diff --git a/src/sht4x_async.rs b/src/sht4x_async.rs index d3b85a7..f82824a 100644 --- a/src/sht4x_async.rs +++ b/src/sht4x_async.rs @@ -1,7 +1,7 @@ use crate::{ commands::Command, error::Error, - sht4x::RESPONSE_LEN, + responses::{sensor_data_from_response, serial_number_from_response, RESPONSE_LEN}, types::{Address, HeatingDuration, HeatingPower, Measurement, Precision, SensorData}, }; use core::marker::PhantomData; @@ -89,7 +89,7 @@ where self.write_command_and_delay_for_execution(command, delay) .await?; let response = self.read_response().await?; - let raw = crate::sht4x::sensor_data_from_response(response); + let raw = sensor_data_from_response(response); Ok(raw) } @@ -115,7 +115,7 @@ where self.write_command_and_delay_for_execution(command, delay) .await?; let response = self.read_response().await?; - let raw = crate::sht4x::sensor_data_from_response(response); + let raw = sensor_data_from_response(response); Ok(raw) } @@ -125,7 +125,7 @@ where self.write_command_and_delay_for_execution(Command::SerialNumber, delay) .await?; let response = self.read_response().await?; - Ok(crate::sht4x::serial_number_from_response(response)) + Ok(serial_number_from_response(response)) } /// Performs a soft reset of the sensor.