use anyhow crate for error handling

This commit is contained in:
Denis-Cosmin Nutiu 2024-03-03 19:02:33 +02:00
parent dfadb52009
commit fe38f02a9c
4 changed files with 53 additions and 80 deletions

View file

@ -17,6 +17,7 @@ embedded-hal = "=1.0.0"
log = "0.4" log = "0.4"
serde = { version = "1.0", optional = true, default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true, default-features = false, features = ["derive"] }
linux-embedded-hal = "0.4.0" linux-embedded-hal = "0.4.0"
anyhow = "1.0.80"
[dev-dependencies] [dev-dependencies]
env_logger = "0.9" env_logger = "0.9"

View file

@ -1,7 +1,7 @@
#![no_std] #![no_std]
use bme680::i2c::Address; use bme680::i2c::Address;
use bme680::{Bme680, Bme680Error, IIRFilterSize, OversamplingSetting, PowerMode, SettingsBuilder}; use bme680::{Bme680, IIRFilterSize, OversamplingSetting, PowerMode, SettingsBuilder};
use core::time::Duration; use core::time::Duration;
use embedded_hal::delay::DelayNs; use embedded_hal::delay::DelayNs;
use linux_embedded_hal as hal; use linux_embedded_hal as hal;
@ -9,7 +9,7 @@ use linux_embedded_hal::Delay;
use log::info; use log::info;
// Please export RUST_LOG=info in order to see logs in the console. // Please export RUST_LOG=info in order to see logs in the console.
fn main() -> Result<(), Bme680Error> { fn main() -> Result<(), anyhow::Error> {
env_logger::init(); env_logger::init();
let i2c = hal::I2cdev::new("/dev/i2c-1").unwrap(); let i2c = hal::I2cdev::new("/dev/i2c-1").unwrap();

View file

@ -1,5 +1,5 @@
use crate::Bme680Error; use core::fmt::{Display, Formatter};
use crate::Bme680Error::{I2CRead, I2CWrite}; use anyhow::anyhow;
use embedded_hal::i2c::I2c; use embedded_hal::i2c::I2c;
/// ///
@ -26,6 +26,12 @@ impl Address {
} }
} }
impl Display for Address {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "Address={:#01x}", self.addr())
}
}
/// I2CUtility is a simple wrapper over the I2c trait to make reading and writing data easier. /// I2CUtility is a simple wrapper over the I2c trait to make reading and writing data easier.
pub(crate) struct I2CUtility {} pub(crate) struct I2CUtility {}
@ -35,16 +41,16 @@ impl I2CUtility {
i2c_handle: &mut I2C, i2c_handle: &mut I2C,
device_address: u8, device_address: u8,
register_address: u8, register_address: u8,
) -> Result<u8, Bme680Error> { ) -> Result<u8, anyhow::Error> {
let mut buf = [0; 1]; let mut buf = [0; 1];
i2c_handle i2c_handle
.write(device_address, &[register_address]) .write(device_address, &[register_address])
.map_err(|_e| I2CWrite)?; .map_err(|e| anyhow!("Failed to write a byte {} to device {}: {:?}", register_address, device_address, e))?;
match i2c_handle.read(device_address, &mut buf) { match i2c_handle.read(device_address, &mut buf) {
Ok(()) => Ok(buf[0]), Ok(()) => Ok(buf[0]),
Err(_e) => Err(I2CRead), Err(_e) => Err(anyhow!("Failed to read byte {} from device {}", register_address, device_address)),
} }
} }
@ -54,14 +60,14 @@ impl I2CUtility {
device_address: u8, device_address: u8,
register_address: u8, register_address: u8,
buffer: &mut [u8], buffer: &mut [u8],
) -> Result<(), Bme680Error> { ) -> Result<(), anyhow::Error> {
i2c_handle i2c_handle
.write(device_address, &[register_address]) .write(device_address, &[register_address])
.map_err(|_e| I2CWrite)?; .map_err(|_e| anyhow!("Failed to write a byte {} from device {}", register_address, device_address))?;
match i2c_handle.read(device_address, buffer) { match i2c_handle.read(device_address, buffer) {
Ok(()) => Ok(()), Ok(()) => Ok(()),
Err(_e) => Err(I2CRead), Err(_e) => Err(anyhow!("Failed to read bytes from register {} and device {}", register_address, device_address)),
} }
} }
@ -70,9 +76,9 @@ impl I2CUtility {
i2c_handle: &mut I2C, i2c_handle: &mut I2C,
device_address: u8, device_address: u8,
buffer: &[u8], buffer: &[u8],
) -> Result<(), Bme680Error> { ) -> Result<(), anyhow::Error> {
i2c_handle i2c_handle
.write(device_address, buffer) .write(device_address, buffer)
.map_err(|_e| I2CWrite) .map_err(|_e| anyhow!("Failed to write bytes to address {}", device_address))
} }
} }

View file

@ -5,7 +5,7 @@
//! In the examples you can find a demo how to use the library in Linux using the linux-embedded-hal crate (e.g. on a RPI). //! In the examples you can find a demo how to use the library in Linux using the linux-embedded-hal crate (e.g. on a RPI).
//! ```no_run //! ```no_run
//! use bme680::{Bme680, Bme680Error, IIRFilterSize, OversamplingSetting, PowerMode, SettingsBuilder}; //! use bme680::{Bme680, IIRFilterSize, OversamplingSetting, PowerMode, SettingsBuilder};
//! use core::time::Duration; //! use core::time::Duration;
//! use embedded_hal::delay::DelayNs; //! use embedded_hal::delay::DelayNs;
//! use linux_embedded_hal as hal; //! use linux_embedded_hal as hal;
@ -14,7 +14,7 @@
//! use bme680::i2c::Address; //! use bme680::i2c::Address;
//! //!
//! // Please export RUST_LOG=info in order to see logs in the console. //! // Please export RUST_LOG=info in order to see logs in the console.
//! fn main() -> Result<(), Bme680Error> //! fn main() -> Result<(), anyhow::Error>
//! { //! {
//! env_logger::init(); //! env_logger::init();
//! //!
@ -80,10 +80,10 @@ use crate::hal::i2c::I2c;
use core::marker::PhantomData; use core::marker::PhantomData;
use core::ops::DerefMut; use core::ops::DerefMut;
use core::time::Duration; use core::time::Duration;
use anyhow::anyhow;
use embedded_hal as hal; use embedded_hal as hal;
use log::{debug, error, info}; use log::{debug, error, info};
use crate::Bme680Error::I2CRead;
use i2c::{Address, I2CUtility}; use i2c::{Address, I2CUtility};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -151,40 +151,6 @@ const BME680_HEAT_STAB_MSK: u8 = 0x10;
const BME680_TMP_BUFFER_LENGTH: usize = 40; const BME680_TMP_BUFFER_LENGTH: usize = 40;
const BME680_REG_BUFFER_LENGTH: usize = 6; const BME680_REG_BUFFER_LENGTH: usize = 6;
/// All possible errors in this crate
/// TODO: use anyhow:
/// - https://antoinerr.github.io/blog-website/2023/01/28/rust-anyhow.html
/// - https://docs.rs/anyhow/latest/anyhow/
#[derive(Debug)]
pub enum Bme680Error {
///
/// aka BME680_E_COM_FAIL
///
I2CWrite,
I2CRead,
Delay,
///
/// aka BME680_E_DEV_NOT_FOUND
///
DeviceNotFound,
///
/// aka BME680_E_INVALID_LENGTH
///
InvalidLength,
///
/// Warning aka BME680_W_DEFINE_PWR_MODE
///
DefinePwrMode,
///
/// Warning aka BME680_W_DEFINE_PWR_MODE
///
NoNewData,
///
/// Warning Boundary Check
///
BoundaryCheckFailure(&'static str),
}
/// ///
/// Power mode settings of the sensor. /// Power mode settings of the sensor.
/// ///
@ -343,19 +309,19 @@ fn boundary_check_u8(
value_name: &'static str, value_name: &'static str,
min: u8, min: u8,
max: u8, max: u8,
) -> Result<u8, Bme680Error> { ) -> Result<u8, anyhow::Error> {
let value = value.ok_or(Bme680Error::BoundaryCheckFailure(value_name))?; let value = value.ok_or(anyhow!("Boundary check failed for {}", value_name))?;
if value < min { if value < min {
const MIN: &str = "Boundary check failure, value exceeds maximum"; const MIN: &str = "Boundary check failure, value exceeds maximum";
error!("{}, value name: {}", MIN, value_name); error!("{}, value name: {}", MIN, value_name);
return Err(Bme680Error::BoundaryCheckFailure(MIN)); return Err(anyhow!("Failed MIN={} boundary check for {}", MIN, value_name));
} }
if value > max { if value > max {
const MAX: &str = "Boundary check, value exceeds minimum"; const MAX: &str = "Boundary check, value exceeds minimum";
error!("{}, value name: {}", MAX, value_name); error!("{}, value name: {}", MAX, value_name);
return Err(Bme680Error::BoundaryCheckFailure(MAX)); return Err(anyhow!("Failed MAX={} boundary check for {}", MAX, value_name));
} }
Ok(value) Ok(value)
} }
@ -370,7 +336,7 @@ where
i2c_handle: &mut I2C, i2c_handle: &mut I2C,
delay: &mut D, delay: &mut D,
device_address: Address, device_address: Address,
) -> Result<(), Bme680Error> { ) -> Result<(), anyhow::Error> {
let tmp_buff: [u8; 2] = [BME680_SOFT_RESET_ADDR, BME680_SOFT_RESET_CMD]; let tmp_buff: [u8; 2] = [BME680_SOFT_RESET_ADDR, BME680_SOFT_RESET_CMD];
I2CUtility::write_bytes(i2c_handle, device_address.addr(), &tmp_buff)?; I2CUtility::write_bytes(i2c_handle, device_address.addr(), &tmp_buff)?;
delay.delay_ms(BME680_RESET_PERIOD as u32); delay.delay_ms(BME680_RESET_PERIOD as u32);
@ -382,7 +348,7 @@ where
mut i2c_handle: I2C, mut i2c_handle: I2C,
delay: &mut D, delay: &mut D,
device_address: Address, device_address: Address,
) -> Result<Bme680<I2C, D>, Bme680Error> { ) -> Result<Bme680<I2C, D>, anyhow::Error> {
Bme680::soft_reset(&mut i2c_handle, delay, device_address)?; Bme680::soft_reset(&mut i2c_handle, delay, device_address)?;
debug!("Reading chip id"); debug!("Reading chip id");
@ -411,14 +377,14 @@ where
Ok(device) Ok(device)
} else { } else {
error!("Device does not match chip id {}", BME680_CHIP_ID); error!("Device does not match chip id {}", BME680_CHIP_ID);
Err(Bme680Error::DeviceNotFound) Err(anyhow!("Device address not found"))
} }
} }
/// Sets the sensor registers. /// Sets the sensor registers.
fn bme680_set_registers(&mut self, registers: &[(u8, u8)]) -> Result<(), Bme680Error> { fn bme680_set_registers(&mut self, registers: &[(u8, u8)]) -> Result<(), anyhow::Error> {
if registers.is_empty() || registers.len() > (BME680_TMP_BUFFER_LENGTH / 2) { if registers.is_empty() || registers.len() > (BME680_TMP_BUFFER_LENGTH / 2) {
return Err(Bme680Error::InvalidLength); return Err(anyhow!("Invalid register length!"));
} }
for (register_address, register_data) in registers { for (register_address, register_data) in registers {
@ -443,7 +409,7 @@ where
&mut self, &mut self,
delay: &mut D, delay: &mut D,
settings: Settings, settings: Settings,
) -> Result<(), Bme680Error> { ) -> Result<(), anyhow::Error> {
let (sensor_settings, desired_settings) = settings; let (sensor_settings, desired_settings) = settings;
let tph_sett = sensor_settings.temperature_settings; let tph_sett = sensor_settings.temperature_settings;
let gas_sett = sensor_settings.gas_settings; let gas_sett = sensor_settings.gas_settings;
@ -589,7 +555,7 @@ where
pub fn get_sensor_settings( pub fn get_sensor_settings(
&mut self, &mut self,
desired_settings: DesiredSensorSettings, desired_settings: DesiredSensorSettings,
) -> Result<SensorSettings, Bme680Error> { ) -> Result<SensorSettings, anyhow::Error> {
let reg_addr: u8 = 0x70u8; let reg_addr: u8 = 0x70u8;
let mut data_array: [u8; BME680_REG_BUFFER_LENGTH] = [0; BME680_REG_BUFFER_LENGTH]; let mut data_array: [u8; BME680_REG_BUFFER_LENGTH] = [0; BME680_REG_BUFFER_LENGTH];
let mut sensor_settings: SensorSettings = Default::default(); let mut sensor_settings: SensorSettings = Default::default();
@ -654,7 +620,7 @@ where
&mut self, &mut self,
delay: &mut D, delay: &mut D,
target_power_mode: PowerMode, target_power_mode: PowerMode,
) -> Result<(), Bme680Error> { ) -> Result<(), anyhow::Error> {
let mut power_mode_byte: u8; let mut power_mode_byte: u8;
let mut power_mode: PowerMode; let mut power_mode: PowerMode;
@ -692,7 +658,7 @@ where
} }
/// Retrieve current sensor power mode via registers /// Retrieve current sensor power mode via registers
pub fn get_sensor_mode(&mut self) -> Result<PowerMode, Bme680Error> { pub fn get_sensor_mode(&mut self) -> Result<PowerMode, anyhow::Error> {
let registers = I2CUtility::read_byte( let registers = I2CUtility::read_byte(
self.i2c_bus_handle.borrow_mut().deref_mut(), self.i2c_bus_handle.borrow_mut().deref_mut(),
self.device_address.addr(), self.device_address.addr(),
@ -705,7 +671,7 @@ where
pub fn get_profile_duration( pub fn get_profile_duration(
&self, &self,
sensor_settings: &SensorSettings, sensor_settings: &SensorSettings,
) -> Result<Duration, Bme680Error> { ) -> Result<Duration, anyhow::Error> {
let os_to_meas_cycles: [u8; 6] = [0u8, 1u8, 2u8, 4u8, 8u8, 16u8]; let os_to_meas_cycles: [u8; 6] = [0u8, 1u8, 2u8, 4u8, 8u8, 16u8];
let mut measurement_cycles = os_to_meas_cycles[sensor_settings let mut measurement_cycles = os_to_meas_cycles[sensor_settings
.temperature_settings .temperature_settings
@ -740,7 +706,7 @@ where
Ok(duration) Ok(duration)
} }
fn get_calibration_data<I2CX>(i2c: &mut I2CX, device_address: Address) -> Result<CalibrationData, Bme680Error> fn get_calibration_data<I2CX>(i2c: &mut I2CX, device_address: Address) -> Result<CalibrationData, anyhow::Error>
where where
I2CX: I2c, I2CX: I2c,
{ {
@ -755,7 +721,7 @@ where
BME680_COEFF_ADDR1, BME680_COEFF_ADDR1,
&mut coefficients_array[0..(BME680_COEFF_ADDR1_LEN - 1)], &mut coefficients_array[0..(BME680_COEFF_ADDR1_LEN - 1)],
) )
.map_err(|_e| I2CRead)?; .map_err(|_e| anyhow!("Failed to get calibration data from device: {}", device_address))?;
I2CUtility::read_bytes::<I2CX>( I2CUtility::read_bytes::<I2CX>(
i2c, i2c,
@ -764,7 +730,7 @@ where
&mut coefficients_array &mut coefficients_array
[BME680_COEFF_ADDR1_LEN..(BME680_COEFF_ADDR1_LEN + BME680_COEFF_ADDR2_LEN - 1)], [BME680_COEFF_ADDR1_LEN..(BME680_COEFF_ADDR1_LEN + BME680_COEFF_ADDR2_LEN - 1)],
) )
.map_err(|_e| I2CRead)?; .map_err(|_e| anyhow!("Failed to get calibration data from device: {}", device_address))?;
calibration_data.par_t1 = ((coefficients_array[34usize] as i32) << 8i32 | coefficients_array[33usize] as i32) as u16; calibration_data.par_t1 = ((coefficients_array[34usize] as i32) << 8i32 | coefficients_array[33usize] as i32) as u16;
calibration_data.par_t2 = ((coefficients_array[2usize] as i32) << 8i32 | coefficients_array[1usize] as i32) as i16; calibration_data.par_t2 = ((coefficients_array[2usize] as i32) << 8i32 | coefficients_array[1usize] as i32) as i16;
@ -795,26 +761,26 @@ where
calibration_data.res_heat_range = calibration_data.res_heat_range =
(I2CUtility::read_byte::<I2CX>(i2c, device_address.addr(), BME680_ADDR_RES_HEAT_RANGE_ADDR) (I2CUtility::read_byte::<I2CX>(i2c, device_address.addr(), BME680_ADDR_RES_HEAT_RANGE_ADDR)
.map_err(|_e| I2CRead)? .map_err(|_e| anyhow!("Failed to read from register BME680_ADDR_RES_HEAT_RANGE_ADDR"))?
& 0x30) & 0x30)
/ 16; / 16;
calibration_data.res_heat_val = calibration_data.res_heat_val =
I2CUtility::read_byte::<I2CX>(i2c, device_address.addr(), BME680_ADDR_RES_HEAT_VAL_ADDR) I2CUtility::read_byte::<I2CX>(i2c, device_address.addr(), BME680_ADDR_RES_HEAT_VAL_ADDR)
.map_err(|_e| I2CRead)? as i8; .map_err(|_e| anyhow!("Failed to read from register BME680_ADDR_RES_HEAT_VAL_ADDR"))? as i8;
calibration_data.range_sw_err = calibration_data.range_sw_err =
(I2CUtility::read_byte::<I2CX>(i2c, device_address.addr(), BME680_ADDR_RANGE_SW_ERR_ADDR) (I2CUtility::read_byte::<I2CX>(i2c, device_address.addr(), BME680_ADDR_RANGE_SW_ERR_ADDR)
.map_err(|_e| I2CRead)? .map_err(|_e| anyhow!("Failed to read from register BME680_ADDR_RANGE_SW_ERR_ADDR"))?
& BME680_RSERROR_MSK) & BME680_RSERROR_MSK)
/ 16; / 16;
Ok(calibration_data) Ok(calibration_data)
} }
fn set_gas_settings(&mut self, gas_settings: GasSettings) -> Result<(), Bme680Error> { fn set_gas_settings(&mut self, gas_settings: GasSettings) -> Result<(), anyhow::Error> {
if self.power_mode != PowerMode::ForcedMode { if self.power_mode != PowerMode::ForcedMode {
return Err(Bme680Error::DefinePwrMode); return Err(anyhow!("Current power mode is not forced"));
} }
let reg: [(u8, u8); 2] = [ let reg: [(u8, u8); 2] = [
@ -839,14 +805,14 @@ where
self.bme680_set_registers(&reg) self.bme680_set_registers(&reg)
} }
fn get_gas_settings(&mut self) -> Result<GasSettings, Bme680Error> { fn get_gas_settings(&mut self) -> Result<GasSettings, anyhow::Error> {
let heater_temperature = Some(I2CUtility::read_byte( let heater_temperature = Some(I2CUtility::read_byte(
self.i2c_bus_handle.borrow_mut().deref_mut(), self.i2c_bus_handle.borrow_mut().deref_mut(),
self.device_address.addr(), self.device_address.addr(),
BME680_ADDR_SENS_CONF_START, BME680_ADDR_SENS_CONF_START,
)? as u16); )? as u16);
let heatr_dur_ms = I2CUtility::read_byte( let heater_duration_ms = I2CUtility::read_byte(
self.i2c_bus_handle.borrow_mut().deref_mut(), self.i2c_bus_handle.borrow_mut().deref_mut(),
self.device_address.addr(), self.device_address.addr(),
BME680_ADDR_GAS_CONF_START, BME680_ADDR_GAS_CONF_START,
@ -854,7 +820,7 @@ where
let gas_sett = GasSettings { let gas_sett = GasSettings {
heater_temperature, heater_temperature,
heater_duration: Some(Duration::from_millis(heatr_dur_ms)), heater_duration: Some(Duration::from_millis(heater_duration_ms)),
..Default::default() ..Default::default()
}; };
@ -865,7 +831,7 @@ where
pub fn get_measurement( pub fn get_measurement(
&mut self, &mut self,
delay: &mut D, delay: &mut D,
) -> Result<(FieldData, FieldDataCondition), Bme680Error> { ) -> Result<(FieldData, FieldDataCondition), anyhow::Error> {
let mut buffer: [u8; BME680_FIELD_LENGTH] = [0; BME680_FIELD_LENGTH]; let mut buffer: [u8; BME680_FIELD_LENGTH] = [0; BME680_FIELD_LENGTH];
debug!("Buf {:?}, len: {}", buffer, buffer.len()); debug!("Buf {:?}, len: {}", buffer, buffer.len());