#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(missing_docs)]
pub mod commands;
mod crc;
mod error;
mod types;
pub use error::Error;
pub use types::{HeaterDuration, HeaterPower, Measurement, Precision};
pub const DEFAULT_ADDRESS: u8 = 0x44;
const MEASUREMENT_LEN: usize = 6;
const SERIAL_LEN: usize = 6;
pub struct Sht4x<I2C, D> {
i2c: I2C,
delay: D,
address: u8,
}
impl<I2C, D> Sht4x<I2C, D> {
pub fn new(i2c: I2C, delay: D, address: u8) -> Self {
Self {
i2c,
delay,
address,
}
}
pub fn release(self) -> (I2C, D) {
(self.i2c, self.delay)
}
}
fn decode_measurement<E>(buf: &[u8; MEASUREMENT_LEN]) -> Result<Measurement, Error<E>> {
let t = [buf[0], buf[1], buf[2]];
let h = [buf[3], buf[4], buf[5]];
if !crc::verify_word(&t) || !crc::verify_word(&h) {
return Err(Error::Crc);
}
let raw_t = u16::from_be_bytes([t[0], t[1]]);
let raw_h = u16::from_be_bytes([h[0], h[1]]);
Ok(Measurement::from_raw(raw_t, raw_h))
}
fn decode_serial<E>(buf: &[u8; SERIAL_LEN]) -> Result<u32, Error<E>> {
let w0 = [buf[0], buf[1], buf[2]];
let w1 = [buf[3], buf[4], buf[5]];
if !crc::verify_word(&w0) || !crc::verify_word(&w1) {
return Err(Error::Crc);
}
let hi = u16::from_be_bytes([w0[0], w0[1]]);
let lo = u16::from_be_bytes([w1[0], w1[1]]);
Ok(((hi as u32) << 16) | lo as u32)
}
#[cfg(feature = "blocking")]
mod blocking_impl {
use super::*;
use embedded_hal::delay::DelayNs;
use embedded_hal::i2c::I2c;
impl<I2C, D> Sht4x<I2C, D>
where
I2C: I2c,
D: DelayNs,
{
pub fn measure(&mut self, precision: Precision) -> Result<Measurement, Error<I2C::Error>> {
self.i2c
.write(self.address, &[precision.command()])
.map_err(Error::I2c)?;
self.delay.delay_us(precision.duration_us());
let mut buf = [0u8; MEASUREMENT_LEN];
self.i2c
.read(self.address, &mut buf)
.map_err(Error::I2c)?;
decode_measurement(&buf)
}
pub fn measure_with_heater(
&mut self,
power: HeaterPower,
duration: HeaterDuration,
) -> Result<Measurement, Error<I2C::Error>> {
let cmd = types::heater_command(power, duration);
self.i2c.write(self.address, &[cmd]).map_err(Error::I2c)?;
self.delay.delay_us(duration.duration_us());
let mut buf = [0u8; MEASUREMENT_LEN];
self.i2c
.read(self.address, &mut buf)
.map_err(Error::I2c)?;
decode_measurement(&buf)
}
pub fn read_serial_number(&mut self) -> Result<u32, Error<I2C::Error>> {
self.i2c
.write(self.address, &[commands::READ_SERIAL])
.map_err(Error::I2c)?;
self.delay.delay_us(commands::duration_us::SERIAL);
let mut buf = [0u8; SERIAL_LEN];
self.i2c
.read(self.address, &mut buf)
.map_err(Error::I2c)?;
decode_serial(&buf)
}
pub fn soft_reset(&mut self) -> Result<(), Error<I2C::Error>> {
self.i2c
.write(self.address, &[commands::SOFT_RESET])
.map_err(Error::I2c)?;
self.delay.delay_us(commands::duration_us::SOFT_RESET);
Ok(())
}
}
}
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
mod async_impl {
use super::*;
use embedded_hal_async::delay::DelayNs;
use embedded_hal_async::i2c::I2c;
impl<I2C, D> Sht4x<I2C, D>
where
I2C: I2c,
D: DelayNs,
{
pub async fn measure_async(
&mut self,
precision: Precision,
) -> Result<Measurement, Error<I2C::Error>> {
self.i2c
.write(self.address, &[precision.command()])
.await
.map_err(Error::I2c)?;
self.delay.delay_us(precision.duration_us()).await;
let mut buf = [0u8; MEASUREMENT_LEN];
self.i2c
.read(self.address, &mut buf)
.await
.map_err(Error::I2c)?;
decode_measurement(&buf)
}
pub async fn measure_with_heater_async(
&mut self,
power: HeaterPower,
duration: HeaterDuration,
) -> Result<Measurement, Error<I2C::Error>> {
let cmd = types::heater_command(power, duration);
self.i2c
.write(self.address, &[cmd])
.await
.map_err(Error::I2c)?;
self.delay.delay_us(duration.duration_us()).await;
let mut buf = [0u8; MEASUREMENT_LEN];
self.i2c
.read(self.address, &mut buf)
.await
.map_err(Error::I2c)?;
decode_measurement(&buf)
}
pub async fn read_serial_number_async(&mut self) -> Result<u32, Error<I2C::Error>> {
self.i2c
.write(self.address, &[commands::READ_SERIAL])
.await
.map_err(Error::I2c)?;
self.delay.delay_us(commands::duration_us::SERIAL).await;
let mut buf = [0u8; SERIAL_LEN];
self.i2c
.read(self.address, &mut buf)
.await
.map_err(Error::I2c)?;
decode_serial(&buf)
}
pub async fn soft_reset_async(&mut self) -> Result<(), Error<I2C::Error>> {
self.i2c
.write(self.address, &[commands::SOFT_RESET])
.await
.map_err(Error::I2c)?;
self.delay.delay_us(commands::duration_us::SOFT_RESET).await;
Ok(())
}
}
}
#[doc(hidden)]
pub mod __private {
pub use crate::crc::crc8;
}