diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c53a03f..f187833 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -17,20 +17,6 @@ jobs:
with:
command: check
- test:
- name: Run Test Suite
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: stable
- override: true
- - uses: actions-rs/cargo@v1
- with:
- command: test
-
fmt:
name: Check Formatting
runs-on: ubuntu-latest
@@ -62,16 +48,3 @@ jobs:
with:
command: clippy
args: -- -D warnings
-
- license-check:
- name: Check Licenses and Security Advisories
- runs-on: ubuntu-latest
- strategy:
- matrix:
- checks:
- - advisories bans licenses sources
- steps:
- - uses: actions/checkout@v2
- - uses: EmbarkStudios/cargo-deny-action@v1
- with:
- command: check ${{ matrix.checks }}
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/bme680-rust.iml b/.idea/bme680-rust.iml
new file mode 100644
index 0000000..29b11b5
--- /dev/null
+++ b/.idea/bme680-rust.iml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..fbf8647
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
index b29e573..dba02d2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,30 +1,25 @@
[package]
-authors = ["marcelbuesing "]
+authors = ["marcelbuesing ", "denisnutiu "]
description = "A pure Rust implementation for the BME680 environmental sensor."
-documentation = "https://docs.rs/bme680"
+documentation = "https://github.com/dnutiu/bme680-rust"
license = "MIT"
name = "bme680"
-repository = "https://github.com/marcelbuesing/bme680-hal"
-version = "0.6.0"
-edition = "2018"
+repository = "https://github.com/dnutiu/bme680-rust"
+version = "0.9.0"
+edition = "2021"
[badges]
-travis-ci = { repository = "https://github.com/marcelbuesing/bme680-hal", branch = "master" }
maintenance = { status = "passively-maintained" }
[dependencies]
-bitflags = "1.2"
-embedded-hal = "=1.0.0-alpha.5"
+bitflags = "2.6"
+embedded-hal = "=1.0.0"
log = "0.4"
-serde = { version = "1.0", optional = true, default-features = false }
+serde = { version = "1.0", optional = true, default-features = false, features = ["derive"] }
+anyhow = { version = "1.0" , default-features = false}
[dev-dependencies]
-env_logger = "0.9"
-futures = { version = "0.3" }
-i2cdev = "0.5"
-influx_db_client = { version = "0.5", default-features= false, features = ["rustls-tls"] }
-tokio = {version = "1.5", features = ["full"] }
-url = "2.1"
+env_logger = "0.11.5"
-[target.'cfg(target_os = "linux")'.dev-dependencies]
-linux-embedded-hal = "0.4.0-alpha.1"
+[target.'cfg(target_os = "linux")'.dependencies]
+linux-embedded-hal = "0.4.0"
\ No newline at end of file
diff --git a/README.md b/README.md
index adb5c64..1fab7fc 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,27 @@
-BME680 + Rust [](https://github.com/marcelbuesing/bme680/actions?query=workflow%3A"Continuous+integration")
-[](https://github.com/marcelbuesing/bme680/actions?query=workflow%3A"Continuous+integration")
+BME680 Rust Library
=============
-This repository contains a pure Rust implementation for the [BME680](https://www.bosch-sensortec.com/bst/products/all_products/bme680) environmental sensor. The library can be used to read the gas, pressure, humidity and temperature sensors via I²C.
+This repository contains a pure Rust implementation for the [BME680](https://www.bosch-sensortec.com/bst/products/all_products/bme680) environmental sensor.
+The library can be used to read the gas, pressure, humidity and temperature sensors via I²C.
-The library uses the [embedded-hal](https://github.com/japaric/embedded-hal) library to abstract reading and writing via I²C. In the examples you can find a demo how to use the library in Linux using the [linux-embedded-hal](https://github.com/japaric/linux-embedded-hal) implementation.
+It is a fork of the library written by Marcel Buessing: https://github.com/marcelbuesing/bme680.
-# Alternative
-[drogue-bme680](https://github.com/drogue-iot/drogue-bme680)
+To use this library, create a new project and add it as a dependency:
-# Example getting started Linux
+```toml
+[dependencies]
+bme680 = {git = "https://github.com/dnutiu/bme680-rust.git", version = "0.9.0"}
+```
+
+# Getting started on Raspberry Pi
+
+Assuming that you have connected the sensor to the Raspberry PI's GPIO ports.
+
+Install required libraries for developing I2C.
+
+```bash
+sudo apt-get install build-essential libi2c-dev i2c-tools python-dev libffi-dev
+```
Determine the I2C device path
@@ -34,11 +46,16 @@ pi@raspberrypi:~ $ i2cdetect -y 1
70: -- -- -- -- -- -- 76
```
-# Example Influx Client
-The examples folder contains an example for a simple influx database client inserting collected values.
-Below you may find examples of Chronograf graphs of an indoor measurement over a period of 30 days.
+The read-sensor-data from the examples folder is configured to use the I2C device `/dev/i2c-1` and the `0x76` address.
+
+To run the example, clone the repository and go to the examples' folder:
-
-
-
-
+```bash
+git clone https://github.com/dnutiu/bme680-rust.git && cd bme680-rust/examples/read-sensor-data
+```
+Then run the example:
+
+```bash
+export RUST_LOG=info
+cargo run
+```
diff --git a/ci/no_std_test/Cargo.toml b/ci/no_std_test/Cargo.toml
deleted file mode 100644
index 4fe4c66..0000000
--- a/ci/no_std_test/Cargo.toml
+++ /dev/null
@@ -1,9 +0,0 @@
-[package]
-name = "no_std_test"
-version = "0.1.0"
-authors = ["marcelbuesing "]
-edition = "2018"
-
-[dependencies]
-bme680 = { path = "../../"}
-log = "0.4"
diff --git a/ci/no_std_test/src/lib.rs b/ci/no_std_test/src/lib.rs
deleted file mode 100644
index ebb5111..0000000
--- a/ci/no_std_test/src/lib.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-#![no_std]
-
-use bme680::*;
-use log::info;
-
-pub fn test_no_std() {
- info!("chipid {:?}", BME680_CHIP_ID);
-}
\ No newline at end of file
diff --git a/examples/influx_client.rs b/examples/influx_client.rs
deleted file mode 100644
index b41e8cd..0000000
--- a/examples/influx_client.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-///
-/// This example demonstrates how to read values from the sensor and
-/// continously send them to an influx database.
-/// Make sure you adapt the influx constants and likely also the i2c device id and I2CAddress.
-///
-use bme680::{
- Bme680, FieldDataCondition, I2CAddress, IIRFilterSize, OversamplingSetting, PowerMode,
- SettingsBuilder,
-};
-use influx_db_client::{points, Client, Point, Points, Precision, Value};
-use linux_embedded_hal::*;
-use std::time::Duration;
-use tokio::time::sleep;
-use url::Url;
-
-const INFLUX_ADDRESS: &str = "http://127.0.0.1:8086";
-const INFLUX_USER: &str = "user";
-const INFLUX_PASSWORD: &str = "pass";
-const INFLUX_DATABASE: &str = "influxdb";
-
-#[tokio::main]
-async fn main() -> Result<(), ()> {
- // Init device
- let i2c = I2cdev::new("/dev/i2c-1").unwrap();
- let mut delayer = Delay {};
- let mut dev = Bme680::init(i2c, &mut delayer, I2CAddress::Primary)
- .map_err(|e| eprintln!("Init failed: {:?}", e))?;
-
- let settings = SettingsBuilder::new()
- .with_humidity_oversampling(OversamplingSetting::OS2x)
- .with_pressure_oversampling(OversamplingSetting::OS4x)
- .with_temperature_oversampling(OversamplingSetting::OS8x)
- .with_temperature_filter(IIRFilterSize::Size3)
- .with_gas_measurement(Duration::from_millis(1500), 320, 25)
- .with_run_gas(true)
- .build();
- dev.set_sensor_settings(&mut delayer, settings)
- .map_err(|e| eprintln!("Setting sensor settings failed: {:?}", e))?;
-
- let client = Client::new(Url::parse(INFLUX_ADDRESS).unwrap(), INFLUX_DATABASE)
- .set_authentication(INFLUX_USER, INFLUX_PASSWORD);
-
- loop {
- dev.set_sensor_mode(&mut delayer, PowerMode::ForcedMode)
- .map_err(|e| eprintln!("Setting sensor mode failed: {:?}", e))?;
- let (data, state) = dev
- .get_sensor_data(&mut delayer)
- .map_err(|e| eprintln!("Retrieving sensor data failed: {:?}", e))?;
-
- println!("State {:?}", state);
- println!("Temperature {}°C", data.temperature_celsius());
- println!("Pressure {}hPa", data.pressure_hpa());
- println!("Humidity {}%", data.humidity_percent());
- println!("Gas Resistence {}Ω", data.gas_resistance_ohm());
-
- if state == FieldDataCondition::NewData {
- let temperature_f = ipoint(
- "temperature",
- Value::Float(data.temperature_celsius() as f64),
- );
- let pressure_f = ipoint("pressure", Value::Float(data.pressure_hpa() as f64));
- let humidity_f = ipoint("humidity", Value::Float(data.humidity_percent() as f64));
- let gas_f = ipoint(
- "gasresistence",
- Value::Float(data.gas_resistance_ohm() as f64),
- );
-
- let points = points!(temperature_f, pressure_f, humidity_f, gas_f);
-
- if let Err(e) = client
- .write_points(points, Some(Precision::Seconds), None)
- .await
- {
- eprintln!("Error: {:?}", e);
- }
- }
- sleep(Duration::from_secs(10)).await;
- }
-}
-
-/// Sends a measured value to the influx database
-fn ipoint(type_name: &str, value: Value) -> Point {
- let point = Point::new("sensor")
- .add_field("value", value)
- .add_tag("id", Value::String("VMAC".to_string()))
- .add_tag("name", Value::String("bme680".to_string()))
- .add_tag("type", Value::String(type_name.to_string()));
-
- point
-}
diff --git a/examples/read-sensor-data/Cargo.toml b/examples/read-sensor-data/Cargo.toml
new file mode 100644
index 0000000..2fbb188
--- /dev/null
+++ b/examples/read-sensor-data/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "read-sensor-data"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+bme680 = {path = "../../"}
+env_logger = "0.11.5"
+linux-embedded-hal = "0.4.0"
+anyhow = { version = "1.0.80" }
+log = "0.4"
+embedded-hal = "=1.0.0"
\ No newline at end of file
diff --git a/examples/reading_temperature.rs b/examples/read-sensor-data/src/main.rs
similarity index 72%
rename from examples/reading_temperature.rs
rename to examples/read-sensor-data/src/main.rs
index 83b7aa1..e79e9f6 100644
--- a/examples/reading_temperature.rs
+++ b/examples/read-sensor-data/src/main.rs
@@ -1,23 +1,19 @@
-#![no_std]
-
-use bme680::*;
-use core::result;
+use bme680::i2c::Address;
+use bme680::{Bme680, IIRFilterSize, OversamplingSetting, PowerMode, SettingsBuilder};
use core::time::Duration;
-use embedded_hal::blocking::delay::DelayMs;
-use embedded_hal::blocking::i2c;
+use embedded_hal::delay::DelayNs;
use linux_embedded_hal as hal;
use linux_embedded_hal::Delay;
use log::info;
-fn main(
-) -> result::Result<(), Error<::Error, ::Error>>
-{
+// Please export RUST_LOG=info in order to see logs in the console.
+fn main() -> Result<(), anyhow::Error> {
env_logger::init();
let i2c = hal::I2cdev::new("/dev/i2c-1").unwrap();
let mut delayer = Delay {};
- let mut dev = Bme680::init(i2c, &mut delayer, I2CAddress::Primary)?;
+ let mut dev = Bme680::init(i2c, &mut delayer, Address::Primary)?;
let mut delay = Delay {};
let settings = SettingsBuilder::new()
@@ -25,15 +21,14 @@ fn main(
.with_pressure_oversampling(OversamplingSetting::OS4x)
.with_temperature_oversampling(OversamplingSetting::OS8x)
.with_temperature_filter(IIRFilterSize::Size3)
- .with_gas_measurement(Duration::from_millis(1500), 320, 25)
- .with_temperature_offset(-2.2)
+ .with_gas_measurement(Duration::from_millis(1500), 320, 23)
.with_run_gas(true)
.build();
- let profile_dur = dev.get_profile_dur(&settings.0)?;
+ let profile_dur = dev.get_profile_duration(&settings.0)?;
info!("Profile duration {:?}", profile_dur);
info!("Setting sensor settings");
- dev.set_sensor_settings(&mut delayer, settings)?;
+ dev.set_sensor_settings(&mut delayer, &settings)?;
info!("Setting forced power modes");
dev.set_sensor_mode(&mut delayer, PowerMode::ForcedMode)?;
@@ -41,17 +36,17 @@ fn main(
info!("Sensor settings: {:?}", sensor_settings);
loop {
- delay.delay_ms(5000u32);
+ let _ = delay.delay_ms(5000u32);
let power_mode = dev.get_sensor_mode();
info!("Sensor power mode: {:?}", power_mode);
info!("Setting forced power modes");
dev.set_sensor_mode(&mut delayer, PowerMode::ForcedMode)?;
info!("Retrieving sensor data");
- let (data, _state) = dev.get_sensor_data(&mut delayer)?;
+ let (data, _state) = dev.get_measurement(&mut delayer)?;
info!("Sensor Data {:?}", data);
info!("Temperature {}°C", data.temperature_celsius());
info!("Pressure {}hPa", data.pressure_hpa());
info!("Humidity {}%", data.humidity_percent());
info!("Gas Resistence {}Ω", data.gas_resistance_ohm());
}
-}
+}
\ No newline at end of file
diff --git a/examples/res/influx_gas_resistance.png b/examples/res/influx_gas_resistance.png
deleted file mode 100644
index ccb0f6f..0000000
Binary files a/examples/res/influx_gas_resistance.png and /dev/null differ
diff --git a/examples/res/influx_humidity.png b/examples/res/influx_humidity.png
deleted file mode 100644
index 973e4ac..0000000
Binary files a/examples/res/influx_humidity.png and /dev/null differ
diff --git a/examples/res/influx_pressure.png b/examples/res/influx_pressure.png
deleted file mode 100644
index 0e873c3..0000000
Binary files a/examples/res/influx_pressure.png and /dev/null differ
diff --git a/examples/res/influx_temperature.png b/examples/res/influx_temperature.png
deleted file mode 100644
index 09def9e..0000000
Binary files a/examples/res/influx_temperature.png and /dev/null differ
diff --git a/src/calc.rs b/src/calc.rs
deleted file mode 100644
index 96546f2..0000000
--- a/src/calc.rs
+++ /dev/null
@@ -1,171 +0,0 @@
-use crate::CalibData;
-use core::time::Duration;
-
-pub struct Calc {}
-
-impl Calc {
- pub fn calc_heater_res(calib: &CalibData, amb_temp: i8, temp: u16) -> u8 {
- // cap temperature
- let temp = if temp <= 400 { temp } else { 400 };
-
- let var1 = amb_temp as i32 * calib.par_gh3 as i32 / 1000i32 * 256i32;
- let var2 = (calib.par_gh1 as i32 + 784i32)
- * (((calib.par_gh2 as i32 + 154009i32) * temp as i32 * 5i32 / 100i32 + 3276800i32)
- / 10i32);
- let var3 = var1 + var2 / 2i32;
- let var4 = var3 / (calib.res_heat_range as i32 + 4i32);
- let var5 = 131i32 * calib.res_heat_val as i32 + 65536i32;
- let heatr_res_x100 = (var4 / var5 - 250i32) * 34i32;
- ((heatr_res_x100 + 50i32) / 100i32) as u8
- }
-
- pub fn calc_heater_dur(duration: Duration) -> u8 {
- let mut factor: u8 = 0u8;
- // TODO replace once https://github.com/rust-lang/rust/pull/50167 has been merged
- const MILLIS_PER_SEC: u64 = 1_000;
- const NANOS_PER_MILLI: u64 = 1_000_000;
- let mut dur = (duration.as_secs() as u64 * MILLIS_PER_SEC)
- + (duration.subsec_nanos() as u64 / NANOS_PER_MILLI);
- if dur as i32 >= 0xfc0i32 {
- 0xffu8 // Max duration
- } else {
- loop {
- if dur as i32 <= 0x3fi32 {
- break;
- }
- dur = (dur as i32 / 4i32) as u64;
- factor = (factor as i32 + 1i32) as u8;
- }
- (dur as i32 + factor as i32 * 64i32) as u8
- }
- }
-
- ///
- /// * `calib` - Calibration data used during initalization
- /// * `temp_adc`
- /// * `temp_offset` - If set, the temperature t_fine will be increased by given
- /// value in celsius. Temperature offset in Celsius, e.g. 4, -8, 1.25
- pub fn calc_temperature(
- calib: &CalibData,
- temp_adc: u32,
- temp_offset: Option,
- ) -> (i16, i32) {
- let var1: i64 = (temp_adc as i64 >> 3) - ((calib.par_t1 as i64) << 1);
- let var2: i64 = (var1 * (calib.par_t2 as i64)) >> 11;
- let var3: i64 = ((var1 >> 1) * (var1 >> 1)) >> 12;
- let var3: i64 = (var3 * ((calib.par_t3 as i64) << 4)) >> 14;
-
- let temp_offset = match temp_offset {
- None => 0i32,
- Some(offset) if offset == 0.0 => 0i32,
- Some(offset) => {
- let signum: i32 = if offset.gt(&0.0) { 1 } else { -1 };
- signum * (((((offset * 100.0) as i32).abs() << 8) - 128) / 5)
- }
- };
-
- let t_fine: i32 = (var2 + var3) as i32 + temp_offset;
- let calc_temp: i16 = (((t_fine * 5) + 128) >> 8) as i16;
- (calc_temp, t_fine)
- }
-
- pub fn calc_pressure(calib: &CalibData, t_fine: i32, pres_adc: u32) -> u32 {
- let mut var1: i32 = (t_fine >> 1) - 64000;
- let mut var2: i32 = ((((var1 >> 2) * (var1 >> 2)) >> 11) * calib.par_p6 as i32) >> 2;
- var2 += (var1 * (calib.par_p5 as i32)) << 1;
- var2 = (var2 >> 2i32) + ((calib.par_p4 as i32) << 16i32);
- var1 = (((((var1 >> 2i32) * (var1 >> 2i32)) >> 13i32) * ((calib.par_p3 as i32) << 5i32))
- >> 3i32)
- + ((calib.par_p2 as i32 * var1) >> 1i32);
- var1 >>= 18i32;
- var1 = ((32768i32 + var1) * calib.par_p1 as i32) >> 15i32;
- let mut pressure_comp: i32 = 1048576u32.wrapping_sub(pres_adc) as i32;
- pressure_comp = ((pressure_comp - (var2 >> 12i32)) as u32).wrapping_mul(3125u32) as i32;
- if pressure_comp >= 0x40000000i32 {
- pressure_comp = ((pressure_comp as u32).wrapping_div(var1 as u32) << 1i32) as i32;
- } else {
- pressure_comp = ((pressure_comp << 1i32) as u32).wrapping_div(var1 as u32) as i32;
- }
- var1 = (calib.par_p9 as i32
- * (((pressure_comp >> 3i32) * (pressure_comp >> 3i32)) >> 13i32))
- >> 12i32;
- var2 = ((pressure_comp >> 2i32) * calib.par_p8 as i32) >> 13i32;
- let var3: i32 = ((pressure_comp >> 8i32)
- * (pressure_comp >> 8i32)
- * (pressure_comp >> 8i32)
- * calib.par_p10 as i32)
- >> 17i32;
- pressure_comp += (var1 + var2 + var3 + ((calib.par_p7 as i32) << 7i32)) >> 4i32;
- pressure_comp as u32
- }
-
- pub fn calc_humidity(calib: &CalibData, t_fine: i32, hum_adc: u16) -> u32 {
- let temp_scaled: i32 = (t_fine * 5i32 + 128i32) >> 8i32;
- let var1: i32 = hum_adc as i32
- - calib.par_h1 as i32 * 16i32
- - ((temp_scaled * calib.par_h3 as i32 / 100i32) >> 1i32);
- let var2: i32 = (calib.par_h2 as i32
- * (temp_scaled * calib.par_h4 as i32 / 100i32
- + ((temp_scaled * (temp_scaled * calib.par_h5 as i32 / 100i32)) >> 6i32) / 100i32
- + (1i32 << 14i32)))
- >> 10i32;
- let var3: i32 = var1 * var2;
- let var4: i32 = (calib.par_h6 as i32) << 7i32;
- let var4: i32 = (var4 + temp_scaled * calib.par_h7 as i32 / 100i32) >> 4i32;
- let var5: i32 = ((var3 >> 14i32) * (var3 >> 14i32)) >> 10i32;
- let var6: i32 = (var4 * var5) >> 1i32;
- let mut calc_hum: i32 = (((var3 + var6) >> 10i32) * 1000i32) >> 12i32;
- if calc_hum > 100000i32 {
- calc_hum = 100000i32;
- } else if calc_hum < 0i32 {
- calc_hum = 0i32;
- }
- calc_hum as u32
- }
-
- pub fn calc_gas_resistance(calib: &CalibData, gas_res_adc: u16, gas_range: u8) -> u32 {
- let lookup_table1: [u32; 16] = [
- 2147483647u32,
- 2147483647u32,
- 2147483647u32,
- 2147483647u32,
- 2147483647u32,
- 2126008810u32,
- 2147483647u32,
- 2130303777u32,
- 2147483647u32,
- 2147483647u32,
- 2143188679u32,
- 2136746228u32,
- 2147483647u32,
- 2126008810u32,
- 2147483647u32,
- 2147483647u32,
- ];
- let lookup_table2: [u32; 16] = [
- 4096000000u32,
- 2048000000u32,
- 1024000000u32,
- 512000000u32,
- 255744255u32,
- 127110228u32,
- 64000000u32,
- 32258064u32,
- 16016016u32,
- 8000000u32,
- 4000000u32,
- 2000000u32,
- 1,
- 500000u32,
- 250000u32,
- 125000u32,
- ];
- let var1: i64 = ((1340 + 5 * calib.range_sw_err as i64)
- * lookup_table1[gas_range as usize] as i64)
- >> 16;
- let var2: u64 = (((gas_res_adc as i64) << 15) - 16777216 + var1) as u64;
- let var3: i64 = (lookup_table2[gas_range as usize] as i64 * var1) >> 9;
- let calc_gas_res: u32 = ((var3 + ((var2 as i64) >> 1i64)) / var2 as i64) as u32;
- calc_gas_res
- }
-}
diff --git a/src/calculation.rs b/src/calculation.rs
new file mode 100644
index 0000000..45bcce5
--- /dev/null
+++ b/src/calculation.rs
@@ -0,0 +1,203 @@
+use crate::CalibrationData;
+use core::time::Duration;
+
+/// Calculates values needed or returned by the BME80 Sensor.
+pub struct Calculation {}
+
+static GAS_LOOKUP_TABLE_1: [u32; 16] = [
+ 2147483647u32,
+ 2147483647u32,
+ 2147483647u32,
+ 2147483647u32,
+ 2147483647u32,
+ 2126008810u32,
+ 2147483647u32,
+ 2130303777u32,
+ 2147483647u32,
+ 2147483647u32,
+ 2143188679u32,
+ 2136746228u32,
+ 2147483647u32,
+ 2126008810u32,
+ 2147483647u32,
+ 2147483647u32,
+];
+
+static GAS_LOOKUP_TABLE_2: [u32; 16] = [
+ 4096000000u32,
+ 2048000000u32,
+ 1024000000u32,
+ 512000000u32,
+ 255744255u32,
+ 127110228u32,
+ 64000000u32,
+ 32258064u32,
+ 16016016u32,
+ 8000000u32,
+ 4000000u32,
+ 2000000u32,
+ 1,
+ 500000u32,
+ 250000u32,
+ 125000u32,
+];
+
+impl Calculation {
+ /// Calculates and returns the sensor's heater resistance.
+ /// * `calibration_data` - The calibration data of the sensor.
+ /// * `ambient_temperature` - The ambient temperature.
+ /// * `heater_temperature` - The heater temperature.
+ pub fn heater_resistance(
+ calibration_data: &CalibrationData,
+ ambient_temperature: i8,
+ heater_temperature: u16,
+ ) -> u8 {
+ // cap temperature
+ let temp = if heater_temperature <= 400 {
+ heater_temperature
+ } else {
+ 400
+ };
+
+ let var1 = ambient_temperature as i32 * calibration_data.par_gh3 as i32 / 1000i32 * 256i32;
+ let var2 = (calibration_data.par_gh1 as i32 + 784i32)
+ * (((calibration_data.par_gh2 as i32 + 154009i32) * temp as i32 * 5i32 / 100i32
+ + 3276800i32)
+ / 10i32);
+ let var3 = var1 + var2 / 2i32;
+ let var4 = var3 / (calibration_data.res_heat_range as i32 + 4i32);
+ let var5 = 131i32 * calibration_data.res_heat_val as i32 + 65536i32;
+ let heatr_res_x100 = (var4 / var5 - 250i32) * 34i32;
+ ((heatr_res_x100 + 50i32) / 100i32) as u8
+ }
+
+ /// Calculates and returns the heater duration.
+ /// * `duration` The duration time
+ pub fn heater_duration(duration: Duration) -> u8 {
+ let mut factor: u8 = 0u8;
+ const MILLIS_PER_SEC: u64 = 1_000;
+ const NANOS_PER_MILLI: u64 = 1_000_000;
+ let mut dur = (duration.as_secs() * MILLIS_PER_SEC)
+ + (duration.subsec_nanos() as u64 / NANOS_PER_MILLI);
+ if dur as i32 >= 0xfc0i32 {
+ 0xffu8 // Max duration
+ } else {
+ loop {
+ if dur as i32 <= 0x3fi32 {
+ break;
+ }
+ dur = (dur as i32 / 4i32) as u64;
+ factor = (factor as i32 + 1i32) as u8;
+ }
+ (dur as i32 + factor as i32 * 64i32) as u8
+ }
+ }
+
+ /// Calculates and returns the sensor temperature.
+ /// * `calibration_data` - Calibration data used during initialization
+ /// * `temp_adc` - The temperature reading of the analog to digital converter.
+ /// * `temp_offset` - If set, the temperature t_fine will be increased by given
+ /// value in celsius. Temperature offset in Celsius, e.g. 4, -8, 1.25
+ pub fn temperature(
+ calibration_data: &CalibrationData,
+ temp_adc: u32,
+ temp_offset: Option,
+ ) -> (i16, i32) {
+ let var1: i64 = (temp_adc as i64 >> 3) - ((calibration_data.par_t1 as i64) << 1);
+ let var2: i64 = (var1 * (calibration_data.par_t2 as i64)) >> 11;
+ let var3: i64 = ((var1 >> 1) * (var1 >> 1)) >> 12;
+ let var3: i64 = (var3 * ((calibration_data.par_t3 as i64) << 4)) >> 14;
+
+ let temp_offset = match temp_offset {
+ None => 0i32,
+ Some(offset) if offset == 0.0 => 0i32,
+ Some(offset) => {
+ let signum: i32 = if offset.gt(&0.0) { 1 } else { -1 };
+ signum * (((((offset * 100.0) as i32).abs() << 8) - 128) / 5)
+ }
+ };
+
+ let t_fine: i32 = (var2 + var3) as i32 + temp_offset;
+ let calc_temp: i16 = (((t_fine * 5) + 128) >> 8) as i16;
+ (calc_temp, t_fine)
+ }
+
+ /// Calculates and returns the pressure of the sensor.
+ ///
+ /// * `t_fine` - The resolution temperature obtained after applying calibration data.
+ /// * `pressure_adc` - The pressure value as returned by the analog to digital converter.
+ pub fn pressure(calibration_data: &CalibrationData, t_fine: i32, pressure_adc: u32) -> u32 {
+ let mut var1: i32 = (t_fine >> 1) - 64000;
+ let mut var2: i32 =
+ ((((var1 >> 2) * (var1 >> 2)) >> 11) * calibration_data.par_p6 as i32) >> 2;
+ var2 += (var1 * (calibration_data.par_p5 as i32)) << 1;
+ var2 = (var2 >> 2i32) + ((calibration_data.par_p4 as i32) << 16i32);
+ var1 = (((((var1 >> 2i32) * (var1 >> 2i32)) >> 13i32)
+ * ((calibration_data.par_p3 as i32) << 5i32))
+ >> 3i32)
+ + ((calibration_data.par_p2 as i32 * var1) >> 1i32);
+ var1 >>= 18i32;
+ var1 = ((32768i32 + var1) * calibration_data.par_p1 as i32) >> 15i32;
+ let mut pressure_comp: i32 = 1048576u32.wrapping_sub(pressure_adc) as i32;
+ pressure_comp = ((pressure_comp - (var2 >> 12i32)) as u32).wrapping_mul(3125u32) as i32;
+ if pressure_comp >= 0x40000000i32 {
+ pressure_comp = ((pressure_comp as u32).wrapping_div(var1 as u32) << 1i32) as i32;
+ } else {
+ pressure_comp = ((pressure_comp << 1i32) as u32).wrapping_div(var1 as u32) as i32;
+ }
+ var1 = (calibration_data.par_p9 as i32
+ * (((pressure_comp >> 3i32) * (pressure_comp >> 3i32)) >> 13i32))
+ >> 12i32;
+ var2 = ((pressure_comp >> 2i32) * calibration_data.par_p8 as i32) >> 13i32;
+ let var3: i32 = ((pressure_comp >> 8i32)
+ * (pressure_comp >> 8i32)
+ * (pressure_comp >> 8i32)
+ * calibration_data.par_p10 as i32)
+ >> 17i32;
+ pressure_comp += (var1 + var2 + var3 + ((calibration_data.par_p7 as i32) << 7i32)) >> 4i32;
+ pressure_comp as u32
+ }
+
+ /// Calculates and returns the humidity of the sensor.
+ ///
+ /// * `t_fine` - The resolution temperature obtained after applying calibration data.
+ /// * `humidity_adc` - The humidity value as returned by the analog to digital converter.
+ pub fn humidity(calibration_data: &CalibrationData, t_fine: i32, humidity_adc: u16) -> u32 {
+ let temp_scaled: i32 = (t_fine * 5i32 + 128i32) >> 8i32;
+ let var1: i32 = humidity_adc as i32
+ - calibration_data.par_h1 as i32 * 16i32
+ - ((temp_scaled * calibration_data.par_h3 as i32 / 100i32) >> 1i32);
+ let var2: i32 = (calibration_data.par_h2 as i32
+ * (temp_scaled * calibration_data.par_h4 as i32 / 100i32
+ + ((temp_scaled * (temp_scaled * calibration_data.par_h5 as i32 / 100i32))
+ >> 6i32)
+ / 100i32
+ + (1i32 << 14i32)))
+ >> 10i32;
+ let var3: i32 = var1 * var2;
+ let var4: i32 = (calibration_data.par_h6 as i32) << 7i32;
+ let var4: i32 = (var4 + temp_scaled * calibration_data.par_h7 as i32 / 100i32) >> 4i32;
+ let var5: i32 = ((var3 >> 14i32) * (var3 >> 14i32)) >> 10i32;
+ let var6: i32 = (var4 * var5) >> 1i32;
+ let calc_hum: i32 = (((var3 + var6) >> 10i32) * 1000i32) >> 12i32;
+ calc_hum.clamp(0, 100000) as u32
+ }
+
+ /// Calculates and returns the gas resistance.
+ ///
+ /// * `gas_resistance_adc` - The gas resistance reading from the analog to digital converter.
+ /// * `gas_range` - The lookup table gas range.
+ pub fn gas_resistance(
+ calibration_data: &CalibrationData,
+ gas_resistance_adc: u16,
+ gas_range: u8,
+ ) -> u32 {
+ let var1: i64 = ((1340 + 5 * calibration_data.range_sw_err as i64)
+ * GAS_LOOKUP_TABLE_1[gas_range as usize] as i64)
+ >> 16;
+ let var2: u64 = (((gas_resistance_adc as i64) << 15) - 16777216 + var1) as u64;
+ let var3: i64 = (GAS_LOOKUP_TABLE_2[gas_range as usize] as i64 * var1) >> 9;
+ let calc_gas_res: u32 = ((var3 + ((var2 as i64) >> 1i64)) / var2 as i64) as u32;
+ calc_gas_res
+ }
+}
diff --git a/src/i2c.rs b/src/i2c.rs
new file mode 100644
index 0000000..a610b0a
--- /dev/null
+++ b/src/i2c.rs
@@ -0,0 +1,105 @@
+use anyhow::anyhow;
+use core::fmt::{Display, Formatter};
+use embedded_hal::i2c::I2c;
+
+///
+/// Represents the I2C address of the BME680 Sensor.
+///
+#[derive(Debug, Clone, Copy, Default)]
+pub enum Address {
+ /// Primary Address 0x76
+ #[default]
+ Primary,
+ /// Secondary Address 0x77
+ Secondary,
+ /// Alternative address
+ Other(u8),
+}
+
+impl Address {
+ pub fn addr(&self) -> u8 {
+ match &self {
+ Address::Primary => 0x76u8,
+ Address::Secondary => 0x77u8,
+ Address::Other(addr) => *addr,
+ }
+ }
+}
+
+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.
+pub(crate) struct I2CUtility {}
+
+impl I2CUtility {
+ /// Reads a byte from the I2C bus.
+ pub fn read_byte(
+ i2c_handle: &mut I2C,
+ device_address: u8,
+ register_address: u8,
+ ) -> Result {
+ let mut buf = [0; 1];
+
+ i2c_handle
+ .write(device_address, &[register_address])
+ .map_err(|e| {
+ anyhow!(
+ "Failed to write a byte {} to device {}: {:?}",
+ register_address,
+ device_address,
+ e
+ )
+ })?;
+
+ match i2c_handle.read(device_address, &mut buf) {
+ Ok(()) => Ok(buf[0]),
+ Err(_e) => Err(anyhow!(
+ "Failed to read byte {} from device {}",
+ register_address,
+ device_address
+ )),
+ }
+ }
+
+ /// Reads bytes from the I2C bus.
+ pub fn read_bytes(
+ i2c_handle: &mut I2C,
+ device_address: u8,
+ register_address: u8,
+ buffer: &mut [u8],
+ ) -> Result<(), anyhow::Error> {
+ i2c_handle
+ .write(device_address, &[register_address])
+ .map_err(|_e| {
+ anyhow!(
+ "Failed to write a byte {} from device {}",
+ register_address,
+ device_address
+ )
+ })?;
+
+ match i2c_handle.read(device_address, buffer) {
+ Ok(()) => Ok(()),
+ Err(_e) => Err(anyhow!(
+ "Failed to read bytes from register {} and device {}",
+ register_address,
+ device_address
+ )),
+ }
+ }
+
+ /// Writes bytes to the I2C bus.
+ pub fn write_bytes(
+ i2c_handle: &mut I2C,
+ device_address: u8,
+ buffer: &[u8],
+ ) -> Result<(), anyhow::Error> {
+ i2c_handle
+ .write(device_address, buffer)
+ .map_err(|_e| anyhow!("Failed to write bytes to address {}", device_address))
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 61e007a..ff0a5a4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,104 +3,32 @@
//!
//! The library uses the embedded-hal crate to abstract reading and writing via I²C.
//! 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
-
-//! extern crate bme680;
-//! extern crate embedded_hal;
-//! // Note that you'll have to import your board crates types corresponding to
-//! // Delay and I2cdev.
-//!
-//! use bme680::*;
-//! use embedded_hal::blocking::i2c;
-//! use hal::*;
-//! use std::result;
-//! use std::time::Duration;
-//! use std::thread::sleep;
-//!
-//! # mod hal {
-//! # use super::*;
-//! # use embedded_hal::blocking::delay;
-//! #
-//! # #[derive(Debug)]
-//! # pub struct Delay {}
-//! #
-//! # impl delay::DelayMs for Delay {
-//! # fn delay_ms(&mut self, _ms: u8) {}
-//! # }
-//! #
-//! # #[derive(Debug)]
-//! # pub enum I2CError {}
-//! #
-//! # pub struct I2cdev {}
-//! #
-//! # impl i2c::Write for I2cdev {
-//! # type Error = I2CError;
-//! #
-//! # fn write<'w>(&mut self, addr: u8, bytes: &'w [u8]) -> result::Result<(), Self::Error> {
-//! # Ok(())
-//! # }
-//! # }
-//! #
-//! # impl i2c::Read for I2cdev {
-//! # type Error = I2CError;
-//! #
-//! # fn read<'w>(&mut self, addr: u8, bytes: &'w mut [u8]) -> result::Result<(), Self::Error> {
-//! # Ok(())
-//! # }
-//! # }
-//! # }
-//!
-//! fn main() -> result::Result<(), Error<::Error, ::Error>>
-//! {
-//! // Initialize device
-//! let i2c = I2cdev {}; // Your I2C device construction will look different, perhaps using I2cdev::new(..)
-//! let mut delayer = Delay {}; // Your Delay construction will look different, perhaps using Delay::new(..)
-//! let mut dev = Bme680::init(i2c, &mut delayer, I2CAddress::Primary)?;
-//! let settings = SettingsBuilder::new()
-//! .with_humidity_oversampling(OversamplingSetting::OS2x)
-//! .with_pressure_oversampling(OversamplingSetting::OS4x)
-//! .with_temperature_oversampling(OversamplingSetting::OS8x)
-//! .with_temperature_filter(IIRFilterSize::Size3)
-//! .with_gas_measurement(Duration::from_millis(1500), 320, 25)
-//! .with_run_gas(true)
-//! .build();
-//! dev.set_sensor_settings(&mut delayer, settings)?;
-//! let profile_duration = dev.get_profile_dur(&settings.0)?;
-//!
-//! // Read sensor data
-//! dev.set_sensor_mode(&mut delayer, PowerMode::ForcedMode)?;
-//! sleep(profile_duration);
-//! let (data, _state) = dev.get_sensor_data(&mut delayer)?;
-//!
-//! println!("Temperature {}°C", data.temperature_celsius());
-//! println!("Pressure {}hPa", data.pressure_hpa());
-//! println!("Humidity {}%", data.humidity_percent());
-//! println!("Gas Resistence {}Ω", data.gas_resistance_ohm());
-//!
-//! Ok(())
-//! }
-//! ```
#![no_std]
#![forbid(unsafe_code)]
pub use self::settings::{
- DesiredSensorSettings, GasSett, IIRFilterSize, OversamplingSetting, SensorSettings, Settings,
- SettingsBuilder, TphSett,
+ DesiredSensorSettings, GasSettings, IIRFilterSize, OversamplingSetting, SensorSettings,
+ Settings, SettingsBuilder, TemperatureSettings,
};
+use core::cell::RefCell;
-mod calc;
+mod calculation;
+pub mod i2c;
mod settings;
-use crate::calc::Calc;
-use crate::hal::delay::blocking::DelayMs;
-use crate::hal::i2c::blocking::{Read, Write};
+use crate::calculation::Calculation;
+use crate::hal::delay::DelayNs;
+use crate::hal::i2c::I2c;
+use anyhow::anyhow;
+use core::marker::PhantomData;
+use core::ops::DerefMut;
use core::time::Duration;
-use core::{marker::PhantomData, result};
use embedded_hal as hal;
use log::{debug, error, info};
+use i2c::{Address, I2CUtility};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
@@ -167,42 +95,8 @@ const BME680_HEAT_STAB_MSK: u8 = 0x10;
const BME680_TMP_BUFFER_LENGTH: usize = 40;
const BME680_REG_BUFFER_LENGTH: usize = 6;
-/// All possible errors in this crate
-#[derive(Debug)]
-pub enum Error {
- ///
- /// aka BME680_E_COM_FAIL
- ///
- I2CWrite(W),
- I2CRead(R),
- 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),
-}
-
-/// Abbreviates `std::result::Result` type
-pub type Result = result::Result>;
-
///
-/// Power mode settings
+/// Power mode settings of the sensor.
///
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum PowerMode {
@@ -211,7 +105,6 @@ pub enum PowerMode {
}
impl PowerMode {
- // TODO replace with TryFrom once stabilized
fn from(power_mode: u8) -> Self {
match power_mode {
BME680_SLEEP_MODE => PowerMode::SleepMode,
@@ -220,6 +113,7 @@ impl PowerMode {
}
}
+ /// Retrieves the power mode value.
fn value(&self) -> u8 {
match self {
PowerMode::SleepMode => BME680_SLEEP_MODE,
@@ -228,44 +122,10 @@ impl PowerMode {
}
}
-///
-/// I2C Slave Address
-/// To determine the slave address of your device you can use `i2cdetect -y 1` on linux.
-/// The 7-bit device address is 111011x. The 6 MSB bits are fixed.
-/// The last bit is changeable by SDO value and can be changed during operation.
-/// Connecting SDO to GND results in slave address 1110110 (0x76); connecting it to V DDIO results in slave
-/// address 1110111 (0x77), which is the same as BMP280’s I2C address.
-///
-#[derive(Debug, Clone, Copy)]
-pub enum I2CAddress {
- /// Primary Slave Address 0x76
- Primary,
- /// Secondary Slave Address 0x77
- Secondary,
- /// Alternative address
- Other(u8),
-}
-
-impl I2CAddress {
- pub fn addr(&self) -> u8 {
- match &self {
- I2CAddress::Primary => 0x76u8,
- I2CAddress::Secondary => 0x77u8,
- I2CAddress::Other(addr) => *addr,
- }
- }
-}
-
-impl Default for I2CAddress {
- fn default() -> I2CAddress {
- I2CAddress::Primary
- }
-}
-
-/// Calibration data used during initalization
+/// Calibration data used during initialization
#[derive(Debug, Default, Copy)]
#[repr(C)]
-pub struct CalibData {
+pub struct CalibrationData {
pub par_h1: u16,
pub par_h2: u16,
pub par_h3: i8,
@@ -294,7 +154,7 @@ pub struct CalibData {
pub range_sw_err: u8,
}
-impl Clone for CalibData {
+impl Clone for CalibrationData {
fn clone(&self) -> Self {
*self
}
@@ -310,7 +170,7 @@ pub struct FieldData {
/// Index of heater profile used
gas_index: u8,
/// Measurement index
- meas_index: u8,
+ measurement_index: u8,
temperature: i16,
pressure: u32,
humidity: u32,
@@ -370,156 +230,127 @@ pub enum FieldDataCondition {
Unchanged,
}
-struct I2CUtil {}
-
-impl I2CUtil {
- pub fn read_byte(
- i2c: &mut I2C,
- dev_id: u8,
- reg_addr: u8,
- ) -> Result::Error, ::Error>
- where
- I2C: Read + Write,
- {
- let mut buf = [0; 1];
-
- i2c.write(dev_id, &[reg_addr]).map_err(Error::I2CWrite)?;
-
- match i2c.read(dev_id, &mut buf) {
- Ok(()) => Ok(buf[0]),
- Err(e) => Err(Error::I2CRead(e)),
- }
- }
-
- pub fn read_bytes(
- i2c: &mut I2C,
- dev_id: u8,
- reg_addr: u8,
- buf: &mut [u8],
- ) -> Result<(), ::Error, ::Error>
- where
- I2C: Read + Write,
- {
- i2c.write(dev_id, &[reg_addr]).map_err(Error::I2CWrite)?;
-
- match i2c.read(dev_id, buf) {
- Ok(()) => Ok(()),
- Err(e) => Err(Error::I2CRead(e)),
- }
- }
-}
-
/// Driver for the BME680 environmental sensor
#[repr(C)]
pub struct Bme680 {
- i2c: I2C,
+ i2c_bus_handle: RefCell,
delay: PhantomData,
- dev_id: I2CAddress,
- calib: CalibData,
- // TODO remove ? as it may not reflect the state of the device
- tph_sett: TphSett,
- // TODO remove ? as it may not reflect the state of the device
- gas_sett: GasSett,
- // TODO remove ? as it may not reflect the state of the device
+ device_address: Address,
+ calibration_data: CalibrationData,
+ temperature_offset: f32,
power_mode: PowerMode,
}
-fn boundary_check(
+/// Checks if an u8 value is within the boundary and returns a Result containing an error
+/// if the value is not within the boundary.
+///
+/// * `value` - The value to check for
+/// * `value_name` - The name of the value
+/// * `min` - The minimum boundary.
+/// * `max` - The maximum boundary.
+fn boundary_check_u8(
value: Option,
value_name: &'static str,
min: u8,
max: u8,
-) -> Result::Error, ::Error>
-where
- I2C: Read + Write,
-{
- let value = value.ok_or(Error::BoundaryCheckFailure(value_name))?;
+) -> Result {
+ let value = value.ok_or(anyhow!("Boundary check failed for {}", value_name))?;
if value < min {
const MIN: &str = "Boundary check failure, value exceeds maximum";
error!("{}, value name: {}", MIN, value_name);
- return Err(Error::BoundaryCheckFailure(MIN));
+ return Err(anyhow!(
+ "Failed MIN={} boundary check for {}",
+ MIN,
+ value_name
+ ));
}
if value > max {
const MAX: &str = "Boundary check, value exceeds minimum";
error!("{}, value name: {}", MAX, value_name);
- return Err(Error::BoundaryCheckFailure(MAX));
+ return Err(anyhow!(
+ "Failed MAX={} boundary check for {}",
+ MAX,
+ value_name
+ ));
}
Ok(value)
}
-impl Bme680
+impl Bme680
where
- D: DelayMs,
- I2C: Read + Write,
+ D: DelayNs,
+ I2C: I2c,
{
+ /// Sends the soft reset command to the chip.
pub fn soft_reset(
- i2c: &mut I2C,
+ i2c_handle: &mut I2C,
delay: &mut D,
- dev_id: I2CAddress,
- ) -> Result<(), ::Error, ::Error> {
+ device_address: Address,
+ ) -> Result<(), anyhow::Error> {
let tmp_buff: [u8; 2] = [BME680_SOFT_RESET_ADDR, BME680_SOFT_RESET_CMD];
-
- i2c.write(dev_id.addr(), &tmp_buff)
- .map_err(Error::I2CWrite)?;
-
- delay
- .delay_ms(BME680_RESET_PERIOD)
- .map_err(|_| Error::Delay)?;
+ I2CUtility::write_bytes(i2c_handle, device_address.addr(), &tmp_buff)?;
+ delay.delay_ms(BME680_RESET_PERIOD as u32);
Ok(())
}
+ /// Initializes the BME680 sensor with an I2C handle.
pub fn init(
- mut i2c: I2C,
+ mut i2c_handle: I2C,
delay: &mut D,
- dev_id: I2CAddress,
- ) -> Result, ::Error, ::Error> {
- Bme680::soft_reset(&mut i2c, delay, dev_id)?;
+ device_address: Address,
+ ) -> Result, anyhow::Error> {
+ Bme680::soft_reset(&mut i2c_handle, delay, device_address)?;
debug!("Reading chip id");
/* Soft reset to restore it to default values*/
- let chip_id = I2CUtil::read_byte::(&mut i2c, dev_id.addr(), BME680_CHIP_ID_ADDR)?;
+ let chip_id = I2CUtility::read_byte::(
+ &mut i2c_handle,
+ device_address.addr(),
+ BME680_CHIP_ID_ADDR,
+ )?;
debug!("Chip id: {}", chip_id);
if chip_id == BME680_CHIP_ID {
- debug!("Reading calib data");
- let calib = Bme680::::get_calib_data::(&mut i2c, dev_id)?;
- debug!("Calib data {:?}", calib);
- let dev = Bme680 {
- i2c,
+ debug!("Reading calibration data");
+ let calibration_data =
+ Bme680::::get_calibration_data::(&mut i2c_handle, device_address)?;
+ debug!("Calibration data {:?}", calibration_data);
+ let device = Bme680 {
+ i2c_bus_handle: RefCell::new(i2c_handle),
delay: PhantomData,
- dev_id,
- calib,
+ device_address,
+ calibration_data,
+ temperature_offset: 0.0,
power_mode: PowerMode::ForcedMode,
- tph_sett: Default::default(),
- gas_sett: Default::default(),
};
info!("Finished device init");
- Ok(dev)
+ Ok(device)
} else {
error!("Device does not match chip id {}", BME680_CHIP_ID);
- Err(Error::DeviceNotFound)
+ Err(anyhow!("Device address not found"))
}
}
- fn bme680_set_regs(
- &mut self,
- reg: &[(u8, u8)],
- ) -> Result<(), ::Error, ::Error> {
- if reg.is_empty() || reg.len() > (BME680_TMP_BUFFER_LENGTH / 2) as usize {
- return Err(Error::InvalidLength);
+ /// Sets the sensor registers.
+ fn bme680_set_registers(&mut self, registers: &[(u8, u8)]) -> Result<(), anyhow::Error> {
+ if registers.is_empty() || registers.len() > (BME680_TMP_BUFFER_LENGTH / 2) {
+ return Err(anyhow!("Invalid register length!"));
}
- for (reg_addr, reg_data) in reg {
- let tmp_buff: [u8; 2] = [*reg_addr, *reg_data];
+ for (register_address, register_data) in registers {
+ let buffer: [u8; 2] = [*register_address, *register_data];
debug!(
- "Setting register reg: {:?} tmp_buf: {:?}",
- reg_addr, tmp_buff
+ "Setting register reg: {:?} buffer: {:?}",
+ register_address, buffer
);
- self.i2c
- .write(self.dev_id.addr(), &tmp_buff)
- .map_err(Error::I2CWrite)?;
+
+ I2CUtility::write_bytes(
+ self.i2c_bus_handle.borrow_mut().deref_mut(),
+ self.device_address.addr(),
+ &buffer,
+ )?;
}
Ok(())
@@ -529,18 +360,23 @@ where
pub fn set_sensor_settings(
&mut self,
delay: &mut D,
- settings: Settings,
- ) -> Result<(), ::Error, ::Error> {
+ settings: &Settings,
+ ) -> Result<(), anyhow::Error> {
let (sensor_settings, desired_settings) = settings;
- let tph_sett = sensor_settings.tph_sett;
- let gas_sett = sensor_settings.gas_sett;
+ let tph_sett = sensor_settings.temperature_settings;
+ let gas_sett = sensor_settings.gas_settings;
+
+ self.temperature_offset = sensor_settings
+ .temperature_settings
+ .temperature_offset
+ .unwrap_or(0f32);
let mut reg: [(u8, u8); BME680_REG_BUFFER_LENGTH] = [(0, 0); BME680_REG_BUFFER_LENGTH];
let intended_power_mode = self.power_mode;
if desired_settings.contains(DesiredSensorSettings::GAS_MEAS_SEL) {
debug!("GAS_MEAS_SEL: true");
- self.set_gas_config(gas_sett)?;
+ self.set_gas_settings(gas_sett)?;
}
let power_mode = self.power_mode;
@@ -548,25 +384,28 @@ where
let mut element_index = 0;
// Selecting the filter
- if desired_settings.contains(DesiredSensorSettings::FILTER_SEL) {
- let mut data =
- I2CUtil::read_byte(&mut self.i2c, self.dev_id.addr(), BME680_CONF_ODR_FILT_ADDR)?;
+ if desired_settings.contains(DesiredSensorSettings::FILTER_SIZE_SEL) {
+ let mut data = I2CUtility::read_byte(
+ self.i2c_bus_handle.borrow_mut().deref_mut(),
+ self.device_address.addr(),
+ BME680_CONF_ODR_FILT_ADDR,
+ )?;
debug!("FILTER_SEL: true");
data = (data as i32 & !0x1ci32
- | (tph_sett.filter.unwrap_or(IIRFilterSize::Size0) as i32) << 2i32 & 0x1ci32)
+ | (tph_sett.filter_size.unwrap_or(IIRFilterSize::Size0) as i32) << 2i32 & 0x1ci32)
as u8;
reg[element_index] = (BME680_CONF_ODR_FILT_ADDR, data);
element_index += 1;
}
- if desired_settings.contains(DesiredSensorSettings::HCNTRL_SEL) {
+ if desired_settings.contains(DesiredSensorSettings::HUMIDITY_CONTROL_SEL) {
debug!("HCNTRL_SEL: true");
let gas_sett_heatr_ctrl =
- boundary_check::(gas_sett.heatr_ctrl, "GasSett.heatr_ctrl", 0x0u8, 0x8u8)?;
- let mut data = I2CUtil::read_byte(
- &mut self.i2c,
- self.dev_id.addr(),
+ boundary_check_u8(gas_sett.heater_control, "GasSett.heatr_ctrl", 0x0u8, 0x8u8)?;
+ let mut data = I2CUtility::read_byte(
+ self.i2c_bus_handle.borrow_mut().deref_mut(),
+ self.device_address.addr(),
BME680_CONF_HEAT_CTRL_ADDR,
)?;
data = (data as i32 & !0x8i32 | gas_sett_heatr_ctrl as i32 & 0x8) as u8;
@@ -578,13 +417,16 @@ where
if desired_settings
.contains(DesiredSensorSettings::OST_SEL | DesiredSensorSettings::OSP_SEL)
{
- let mut data =
- I2CUtil::read_byte(&mut self.i2c, self.dev_id.addr(), BME680_CONF_T_P_MODE_ADDR)?;
+ let mut data = I2CUtility::read_byte(
+ self.i2c_bus_handle.borrow_mut().deref_mut(),
+ self.device_address.addr(),
+ BME680_CONF_T_P_MODE_ADDR,
+ )?;
if desired_settings.contains(DesiredSensorSettings::OST_SEL) {
debug!("OST_SEL: true");
- let tph_sett_os_temp = boundary_check::(
- tph_sett.os_temp.map(|x| x as u8),
+ let tph_sett_os_temp = boundary_check_u8(
+ tph_sett.temperature_oversampling.map(|x| x as u8),
"TphSett.os_temp",
0,
5,
@@ -594,7 +436,9 @@ where
if desired_settings.contains(DesiredSensorSettings::OSP_SEL) {
debug!("OSP_SEL: true");
- let tph_sett_os_pres = tph_sett.os_temp.expect("OS TEMP");
+ let tph_sett_os_pres = tph_sett
+ .temperature_oversampling
+ .unwrap_or(OversamplingSetting::OS1x);
data = (data as i32 & !0x1ci32 | (tph_sett_os_pres as i32) << 2i32 & 0x1ci32) as u8;
}
reg[element_index] = (BME680_CONF_T_P_MODE_ADDR, data);
@@ -604,10 +448,17 @@ where
// Selecting humidity oversampling for the sensor
if desired_settings.contains(DesiredSensorSettings::OSH_SEL) {
debug!("OSH_SEL: true");
- let tph_sett_os_hum =
- boundary_check::(tph_sett.os_hum.map(|x| x as u8), "TphSett.os_hum", 0, 5)?;
- let mut data =
- I2CUtil::read_byte(&mut self.i2c, self.dev_id.addr(), BME680_CONF_OS_H_ADDR)?;
+ let tph_sett_os_hum = boundary_check_u8(
+ tph_sett.humidity_oversampling.map(|x| x as u8),
+ "TphSett.os_hum",
+ 0,
+ 5,
+ )?;
+ let mut data = I2CUtility::read_byte(
+ self.i2c_bus_handle.borrow_mut().deref_mut(),
+ self.device_address.addr(),
+ BME680_CONF_OS_H_ADDR,
+ )?;
data = (data as i32 & !0x7i32 | tph_sett_os_hum as i32 & 0x7i32) as u8;
reg[element_index] = (BME680_CONF_OS_H_ADDR, data);
element_index += 1;
@@ -617,23 +468,23 @@ where
if desired_settings
.contains(DesiredSensorSettings::RUN_GAS_SEL | DesiredSensorSettings::NBCONV_SEL)
{
- let mut data = I2CUtil::read_byte(
- &mut self.i2c,
- self.dev_id.addr(),
+ let mut data = I2CUtility::read_byte(
+ self.i2c_bus_handle.borrow_mut().deref_mut(),
+ self.device_address.addr(),
BME680_CONF_ODR_RUN_GAS_NBC_ADDR,
)?;
if desired_settings.contains(DesiredSensorSettings::RUN_GAS_SEL) {
debug!("RUN_GAS_SEL: true");
data = (data as i32 & !0x10i32
- | (gas_sett.run_gas_measurement as i32) << 4i32 & 0x10i32)
+ | (gas_sett.enable_gas_measurement as i32) << 4i32 & 0x10i32)
as u8;
}
if desired_settings.contains(DesiredSensorSettings::NBCONV_SEL) {
debug!("NBCONV_SEL: true");
let gas_sett_nb_conv =
- boundary_check::(Some(gas_sett.nb_conv), "GasSett.nb_conv", 0, 10)?;
+ boundary_check_u8(Some(gas_sett.nb_conv), "GasSett.nb_conv", 0, 10)?;
data = (data as i32 & !0xfi32 | gas_sett_nb_conv as i32 & 0xfi32) as u8;
}
@@ -641,11 +492,10 @@ where
element_index += 1;
}
- self.bme680_set_regs(®[0..element_index])?;
+ self.bme680_set_registers(®[0..element_index])?;
// Restore previous intended power mode
self.power_mode = intended_power_mode;
- self.tph_sett = tph_sett;
Ok(())
}
@@ -657,20 +507,24 @@ where
pub fn get_sensor_settings(
&mut self,
desired_settings: DesiredSensorSettings,
- ) -> Result::Error, ::Error> {
+ ) -> Result {
let reg_addr: u8 = 0x70u8;
let mut data_array: [u8; BME680_REG_BUFFER_LENGTH] = [0; BME680_REG_BUFFER_LENGTH];
let mut sensor_settings: SensorSettings = Default::default();
- sensor_settings.tph_sett.temperature_offset = self.tph_sett.temperature_offset;
- I2CUtil::read_bytes(&mut self.i2c, self.dev_id.addr(), reg_addr, &mut data_array)?;
+ I2CUtility::read_bytes(
+ self.i2c_bus_handle.borrow_mut().deref_mut(),
+ self.device_address.addr(),
+ reg_addr,
+ &mut data_array,
+ )?;
if desired_settings.contains(DesiredSensorSettings::GAS_MEAS_SEL) {
- sensor_settings.gas_sett = self.get_gas_config()?;
+ sensor_settings.gas_settings = self.get_gas_settings()?;
}
- if desired_settings.contains(DesiredSensorSettings::FILTER_SEL) {
- sensor_settings.tph_sett.filter = Some(IIRFilterSize::from_u8(
+ if desired_settings.contains(DesiredSensorSettings::FILTER_SIZE_SEL) {
+ sensor_settings.temperature_settings.filter_size = Some(IIRFilterSize::from_u8(
((data_array[5usize] as i32 & 0x1ci32) >> 2i32) as u8,
));
}
@@ -680,24 +534,29 @@ where
{
let os_temp: u8 = ((data_array[4usize] as i32 & 0xe0i32) >> 5i32) as u8;
let os_pres: u8 = ((data_array[4usize] as i32 & 0x1ci32) >> 2i32) as u8;
- sensor_settings.tph_sett.os_temp = Some(OversamplingSetting::from_u8(os_temp));
- sensor_settings.tph_sett.os_pres = Some(OversamplingSetting::from_u8(os_pres));
+ sensor_settings
+ .temperature_settings
+ .temperature_oversampling = Some(OversamplingSetting::from_u8(os_temp));
+ sensor_settings.temperature_settings.pressure_oversampling =
+ Some(OversamplingSetting::from_u8(os_pres));
}
if desired_settings.contains(DesiredSensorSettings::OSH_SEL) {
let os_hum: u8 = (data_array[2usize] as i32 & 0x7i32) as u8;
- sensor_settings.tph_sett.os_hum = Some(OversamplingSetting::from_u8(os_hum));
+ sensor_settings.temperature_settings.humidity_oversampling =
+ Some(OversamplingSetting::from_u8(os_hum));
}
- if desired_settings.contains(DesiredSensorSettings::HCNTRL_SEL) {
- sensor_settings.gas_sett.heatr_ctrl = Some((data_array[0usize] as i32 & 0x8i32) as u8);
+ if desired_settings.contains(DesiredSensorSettings::HUMIDITY_CONTROL_SEL) {
+ sensor_settings.gas_settings.heater_control =
+ Some((data_array[0usize] as i32 & 0x8i32) as u8);
}
if desired_settings
.contains(DesiredSensorSettings::RUN_GAS_SEL | DesiredSensorSettings::NBCONV_SEL)
{
- sensor_settings.gas_sett.nb_conv = (data_array[1usize] as i32 & 0xfi32) as u8;
- sensor_settings.gas_sett.run_gas_measurement =
+ sensor_settings.gas_settings.nb_conv = (data_array[1usize] as i32 & 0xfi32) as u8;
+ sensor_settings.gas_settings.enable_gas_measurement =
((data_array[1usize] as i32 & 0x10i32) >> 4i32) == 0;
}
@@ -713,295 +572,305 @@ where
&mut self,
delay: &mut D,
target_power_mode: PowerMode,
- ) -> Result<(), ::Error, ::Error> {
- let mut tmp_pow_mode: u8;
- let mut current_power_mode: PowerMode;
+ ) -> Result<(), anyhow::Error> {
+ let mut power_mode_byte: u8;
+ let mut power_mode: PowerMode;
// Call repeatedly until in sleep
loop {
- tmp_pow_mode =
- I2CUtil::read_byte(&mut self.i2c, self.dev_id.addr(), BME680_CONF_T_P_MODE_ADDR)?;
+ power_mode_byte = I2CUtility::read_byte(
+ self.i2c_bus_handle.borrow_mut().deref_mut(),
+ self.device_address.addr(),
+ BME680_CONF_T_P_MODE_ADDR,
+ )?;
// Put to sleep before changing mode
- current_power_mode = PowerMode::from(tmp_pow_mode & BME680_MODE_MSK);
+ power_mode = PowerMode::from(power_mode_byte & BME680_MODE_MSK);
- debug!("Current power mode: {:?}", current_power_mode);
+ debug!("Current power mode: {:?}", power_mode);
- if current_power_mode != PowerMode::SleepMode {
+ if power_mode != PowerMode::SleepMode {
// Set to sleep
- tmp_pow_mode &= !BME680_MODE_MSK;
- debug!("Setting to sleep tmp_pow_mode: {}", tmp_pow_mode);
- self.bme680_set_regs(&[(BME680_CONF_T_P_MODE_ADDR, tmp_pow_mode)])?;
- delay
- .delay_ms(BME680_POLL_PERIOD_MS)
- .map_err(|_| Error::Delay)?;
+ power_mode_byte &= !BME680_MODE_MSK;
+ debug!("Setting to sleep tmp_pow_mode: {}", power_mode_byte);
+ self.bme680_set_registers(&[(BME680_CONF_T_P_MODE_ADDR, power_mode_byte)])?;
+ delay.delay_ms(BME680_POLL_PERIOD_MS as u32);
} else {
- // TODO do while in Rust?
break;
}
}
// Already in sleep
if target_power_mode != PowerMode::SleepMode {
- tmp_pow_mode = tmp_pow_mode & !BME680_MODE_MSK | target_power_mode.value();
- debug!("Already in sleep Target power mode: {}", tmp_pow_mode);
- self.bme680_set_regs(&[(BME680_CONF_T_P_MODE_ADDR, tmp_pow_mode)])?;
+ power_mode_byte = power_mode_byte & !BME680_MODE_MSK | target_power_mode.value();
+ debug!("Already in sleep Target power mode: {}", power_mode_byte);
+ self.bme680_set_registers(&[(BME680_CONF_T_P_MODE_ADDR, power_mode_byte)])?;
}
Ok(())
}
/// Retrieve current sensor power mode via registers
- pub fn get_sensor_mode(
- &mut self,
- ) -> Result::Error, ::Error> {
- let regs =
- I2CUtil::read_byte(&mut self.i2c, self.dev_id.addr(), BME680_CONF_T_P_MODE_ADDR)?;
- let mode = regs & BME680_MODE_MSK;
+ pub fn get_sensor_mode(&mut self) -> Result {
+ let registers = I2CUtility::read_byte(
+ self.i2c_bus_handle.borrow_mut().deref_mut(),
+ self.device_address.addr(),
+ BME680_CONF_T_P_MODE_ADDR,
+ )?;
+ let mode = registers & BME680_MODE_MSK;
Ok(PowerMode::from(mode))
}
- pub fn bme680_set_profile_dur(&mut self, tph_sett: TphSett, duration: Duration) {
- let os_to_meas_cycles: [u8; 6] = [0u8, 1u8, 2u8, 4u8, 8u8, 16u8];
- // TODO check if the following unwrap_ors do not change behaviour
- // TODO replace once https://github.com/rust-lang/rust/pull/50167 has been merged
- const MILLIS_PER_SEC: u64 = 1_000;
- const NANOS_PER_MILLI: u64 = 1_000_000;
- let millis = (duration.as_secs() as u64 * MILLIS_PER_SEC)
- + (duration.subsec_nanos() as u64 / NANOS_PER_MILLI);
-
- let mut meas_cycles = os_to_meas_cycles
- [tph_sett.os_temp.unwrap_or(OversamplingSetting::OSNone) as usize]
- as u64;
- meas_cycles = meas_cycles.wrapping_add(
- os_to_meas_cycles[tph_sett.os_pres.unwrap_or(OversamplingSetting::OSNone) as usize]
- as u64,
- );
- meas_cycles = meas_cycles.wrapping_add(
- os_to_meas_cycles[tph_sett.os_hum.unwrap_or(OversamplingSetting::OSNone) as usize]
- as u64,
- );
- let mut tph_dur = meas_cycles.wrapping_mul(1963u64);
- tph_dur = tph_dur.wrapping_add(477u64.wrapping_mul(4u64));
- tph_dur = tph_dur.wrapping_add(477u64.wrapping_mul(5u64));
- tph_dur = tph_dur.wrapping_add(500u64);
- tph_dur = tph_dur.wrapping_div(1000u64);
- tph_dur = tph_dur.wrapping_add(1u64);
- self.gas_sett.heatr_dur = Some(Duration::from_millis(millis - tph_dur));
- }
-
- pub fn get_profile_dur(
+ pub fn get_profile_duration(
&self,
sensor_settings: &SensorSettings,
- ) -> Result::Error, ::Error> {
+ ) -> Result {
let os_to_meas_cycles: [u8; 6] = [0u8, 1u8, 2u8, 4u8, 8u8, 16u8];
- // TODO check if the following unwrap_ors do not change behaviour
- let mut meas_cycles = os_to_meas_cycles[sensor_settings
- .tph_sett
- .os_temp
+ let mut measurement_cycles = os_to_meas_cycles[sensor_settings
+ .temperature_settings
+ .temperature_oversampling
.unwrap_or(OversamplingSetting::OSNone)
as usize] as u32;
- meas_cycles = meas_cycles.wrapping_add(
+ measurement_cycles = measurement_cycles.wrapping_add(
os_to_meas_cycles[sensor_settings
- .tph_sett
- .os_pres
+ .temperature_settings
+ .pressure_oversampling
.unwrap_or(OversamplingSetting::OSNone) as usize] as u32,
);
- meas_cycles = meas_cycles.wrapping_add(
+ measurement_cycles = measurement_cycles.wrapping_add(
os_to_meas_cycles[sensor_settings
- .tph_sett
- .os_hum
+ .temperature_settings
+ .humidity_oversampling
.unwrap_or(OversamplingSetting::OSNone) as usize] as u32,
);
- let mut tph_dur = meas_cycles.wrapping_mul(1963u32);
- tph_dur = tph_dur.wrapping_add(477u32.wrapping_mul(4u32));
- tph_dur = tph_dur.wrapping_add(477u32.wrapping_mul(5u32));
- tph_dur = tph_dur.wrapping_add(500u32);
- tph_dur = tph_dur.wrapping_div(1000u32);
- tph_dur = tph_dur.wrapping_add(1u32);
- let mut duration = Duration::from_millis(tph_dur as u64);
- if sensor_settings.gas_sett.run_gas_measurement {
- duration += sensor_settings.gas_sett.heatr_dur.expect("Heatrdur");
+ let mut temperature_duration = measurement_cycles.wrapping_mul(1963u32);
+ temperature_duration = temperature_duration.wrapping_add(477u32.wrapping_mul(4u32));
+ temperature_duration = temperature_duration.wrapping_add(477u32.wrapping_mul(5u32));
+ temperature_duration = temperature_duration.wrapping_add(500u32);
+ temperature_duration = temperature_duration.wrapping_div(1000u32);
+ temperature_duration = temperature_duration.wrapping_add(1u32);
+ let mut duration = Duration::from_millis(temperature_duration as u64);
+ if sensor_settings.gas_settings.enable_gas_measurement {
+ duration += sensor_settings
+ .gas_settings
+ .heater_duration
+ .unwrap_or_default();
}
Ok(duration)
}
- fn get_calib_data(
+ fn get_calibration_data(
i2c: &mut I2CX,
- dev_id: I2CAddress,
- ) -> Result::Error, ::Error>
+ device_address: Address,
+ ) -> Result
where
- I2CX: Read + Write,
+ I2CX: I2c,
{
- let mut calib: CalibData = Default::default();
+ let mut calibration_data: CalibrationData = Default::default();
- let mut coeff_array: [u8; BME680_COEFF_ADDR1_LEN + BME680_COEFF_ADDR2_LEN] =
+ let mut coefficients_array: [u8; BME680_COEFF_ADDR1_LEN + BME680_COEFF_ADDR2_LEN] =
[0; BME680_COEFF_ADDR1_LEN + BME680_COEFF_ADDR2_LEN];
- I2CUtil::read_bytes::(
+ I2CUtility::read_bytes::(
i2c,
- dev_id.addr(),
+ device_address.addr(),
BME680_COEFF_ADDR1,
- &mut coeff_array[0..(BME680_COEFF_ADDR1_LEN - 1)],
- )?;
-
- I2CUtil::read_bytes::(
+ &mut coefficients_array[0..(BME680_COEFF_ADDR1_LEN - 1)],
+ )
+ .map_err(|_e| {
+ anyhow!(
+ "Failed to get calibration data from device: {}",
+ device_address
+ )
+ })?;
+
+ I2CUtility::read_bytes::(
i2c,
- dev_id.addr(),
+ device_address.addr(),
BME680_COEFF_ADDR2,
- &mut coeff_array
+ &mut coefficients_array
[BME680_COEFF_ADDR1_LEN..(BME680_COEFF_ADDR1_LEN + BME680_COEFF_ADDR2_LEN - 1)],
- )?;
+ )
+ .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_t2 = ((coefficients_array[2usize] as i32) << 8i32
+ | coefficients_array[1usize] as i32) as i16;
+ calibration_data.par_t3 = coefficients_array[3usize] as i8;
+ calibration_data.par_p1 = ((coefficients_array[6usize] as i32) << 8i32
+ | coefficients_array[5usize] as i32) as u16;
+ calibration_data.par_p2 = ((coefficients_array[8usize] as i32) << 8i32
+ | coefficients_array[7usize] as i32) as i16;
+ calibration_data.par_p3 = coefficients_array[9usize] as i8;
+ calibration_data.par_p4 = ((coefficients_array[12usize] as i32) << 8i32
+ | coefficients_array[11usize] as i32) as i16;
+ calibration_data.par_p5 = ((coefficients_array[14usize] as i32) << 8i32
+ | coefficients_array[13usize] as i32) as i16;
+ calibration_data.par_p6 = coefficients_array[16usize] as i8;
+ calibration_data.par_p7 = coefficients_array[15usize] as i8;
+ calibration_data.par_p8 = ((coefficients_array[20usize] as i32) << 8i32
+ | coefficients_array[19usize] as i32) as i16;
+ calibration_data.par_p9 = ((coefficients_array[22usize] as i32) << 8i32
+ | coefficients_array[21usize] as i32) as i16;
+ calibration_data.par_p10 = coefficients_array[23usize];
+ calibration_data.par_h1 = ((coefficients_array[27usize] as i32) << 4i32
+ | coefficients_array[26usize] as i32 & 0xfi32) as u16;
+ calibration_data.par_h2 = ((coefficients_array[25usize] as i32) << 4i32
+ | coefficients_array[26usize] as i32 >> 4i32) as u16;
+ calibration_data.par_h3 = coefficients_array[28usize] as i8;
+ calibration_data.par_h4 = coefficients_array[29usize] as i8;
+ calibration_data.par_h5 = coefficients_array[30usize] as i8;
+ calibration_data.par_h6 = coefficients_array[31usize];
+ calibration_data.par_h7 = coefficients_array[32usize] as i8;
+ calibration_data.par_gh1 = coefficients_array[37usize] as i8;
+ calibration_data.par_gh2 = ((coefficients_array[36usize] as i32) << 8i32
+ | coefficients_array[35usize] as i32) as i16;
+ calibration_data.par_gh3 = coefficients_array[38usize] as i8;
+
+ calibration_data.res_heat_range = (I2CUtility::read_byte::(
+ i2c,
+ device_address.addr(),
+ BME680_ADDR_RES_HEAT_RANGE_ADDR,
+ )
+ .map_err(|_e| anyhow!("Failed to read from register BME680_ADDR_RES_HEAT_RANGE_ADDR"))?
+ & 0x30)
+ / 16;
+
+ calibration_data.res_heat_val = I2CUtility::read_byte::(
+ i2c,
+ device_address.addr(),
+ BME680_ADDR_RES_HEAT_VAL_ADDR,
+ )
+ .map_err(|_e| anyhow!("Failed to read from register BME680_ADDR_RES_HEAT_VAL_ADDR"))?
+ as i8;
- calib.par_t1 = ((coeff_array[34usize] as i32) << 8i32 | coeff_array[33usize] as i32) as u16;
- calib.par_t2 = ((coeff_array[2usize] as i32) << 8i32 | coeff_array[1usize] as i32) as i16;
- calib.par_t3 = coeff_array[3usize] as i8;
- calib.par_p1 = ((coeff_array[6usize] as i32) << 8i32 | coeff_array[5usize] as i32) as u16;
- calib.par_p2 = ((coeff_array[8usize] as i32) << 8i32 | coeff_array[7usize] as i32) as i16;
- calib.par_p3 = coeff_array[9usize] as i8;
- calib.par_p4 = ((coeff_array[12usize] as i32) << 8i32 | coeff_array[11usize] as i32) as i16;
- calib.par_p5 = ((coeff_array[14usize] as i32) << 8i32 | coeff_array[13usize] as i32) as i16;
- calib.par_p6 = coeff_array[16usize] as i8;
- calib.par_p7 = coeff_array[15usize] as i8;
- calib.par_p8 = ((coeff_array[20usize] as i32) << 8i32 | coeff_array[19usize] as i32) as i16;
- calib.par_p9 = ((coeff_array[22usize] as i32) << 8i32 | coeff_array[21usize] as i32) as i16;
- calib.par_p10 = coeff_array[23usize];
- calib.par_h1 =
- ((coeff_array[27usize] as i32) << 4i32 | coeff_array[26usize] as i32 & 0xfi32) as u16;
- calib.par_h2 =
- ((coeff_array[25usize] as i32) << 4i32 | coeff_array[26usize] as i32 >> 4i32) as u16;
- calib.par_h3 = coeff_array[28usize] as i8;
- calib.par_h4 = coeff_array[29usize] as i8;
- calib.par_h5 = coeff_array[30usize] as i8;
- calib.par_h6 = coeff_array[31usize];
- calib.par_h7 = coeff_array[32usize] as i8;
- calib.par_gh1 = coeff_array[37usize] as i8;
- calib.par_gh2 =
- ((coeff_array[36usize] as i32) << 8i32 | coeff_array[35usize] as i32) as i16;
- calib.par_gh3 = coeff_array[38usize] as i8;
-
- calib.res_heat_range =
- (I2CUtil::read_byte::(i2c, dev_id.addr(), BME680_ADDR_RES_HEAT_RANGE_ADDR)?
- & 0x30)
- / 16;
-
- calib.res_heat_val =
- I2CUtil::read_byte::(i2c, dev_id.addr(), BME680_ADDR_RES_HEAT_VAL_ADDR)? as i8;
-
- calib.range_sw_err =
- (I2CUtil::read_byte::(i2c, dev_id.addr(), BME680_ADDR_RANGE_SW_ERR_ADDR)?
- & BME680_RSERROR_MSK)
- / 16;
-
- Ok(calib)
+ calibration_data.range_sw_err = (I2CUtility::read_byte::(
+ i2c,
+ device_address.addr(),
+ BME680_ADDR_RANGE_SW_ERR_ADDR,
+ )
+ .map_err(|_e| anyhow!("Failed to read from register BME680_ADDR_RANGE_SW_ERR_ADDR"))?
+ & BME680_RSERROR_MSK)
+ / 16;
+
+ Ok(calibration_data)
}
- fn set_gas_config(
- &mut self,
- gas_sett: GasSett,
- ) -> Result<(), ::Error, ::Error> {
+ fn set_gas_settings(&mut self, gas_settings: GasSettings) -> Result<(), anyhow::Error> {
if self.power_mode != PowerMode::ForcedMode {
- return Err(Error::DefinePwrMode);
+ return Err(anyhow!("Current power mode is not forced"));
}
- // TODO check whether unwrap_or changes behaviour
let reg: [(u8, u8); 2] = [
(
BME680_RES_HEAT0_ADDR,
- Calc::calc_heater_res(
- &self.calib,
- gas_sett.ambient_temperature,
- gas_sett.heatr_temp.unwrap_or(0),
+ Calculation::heater_resistance(
+ &self.calibration_data,
+ gas_settings.ambient_temperature,
+ gas_settings.heater_temperature.unwrap_or(0),
),
),
(
BME680_GAS_WAIT0_ADDR,
- Calc::calc_heater_dur(gas_sett.heatr_dur.unwrap_or_else(|| Duration::from_secs(0))),
+ Calculation::heater_duration(
+ gas_settings
+ .heater_duration
+ .unwrap_or_else(|| Duration::from_secs(0)),
+ ),
),
];
- self.gas_sett.nb_conv = 0;
- self.bme680_set_regs(®)
+ self.bme680_set_registers(®)
}
- fn get_gas_config(&mut self) -> Result::Error, ::Error> {
- let heatr_temp = Some(I2CUtil::read_byte(
- &mut self.i2c,
- self.dev_id.addr(),
+ fn get_gas_settings(&mut self) -> Result {
+ let heater_temperature = Some(I2CUtility::read_byte(
+ self.i2c_bus_handle.borrow_mut().deref_mut(),
+ self.device_address.addr(),
BME680_ADDR_SENS_CONF_START,
)? as u16);
- let heatr_dur_ms = I2CUtil::read_byte(
- &mut self.i2c,
- self.dev_id.addr(),
+ let heater_duration_ms = I2CUtility::read_byte(
+ self.i2c_bus_handle.borrow_mut().deref_mut(),
+ self.device_address.addr(),
BME680_ADDR_GAS_CONF_START,
)? as u64;
- let gas_sett = GasSett {
- heatr_temp,
- heatr_dur: Some(Duration::from_millis(heatr_dur_ms)),
+ let gas_sett = GasSettings {
+ heater_temperature,
+ heater_duration: Some(Duration::from_millis(heater_duration_ms)),
..Default::default()
};
Ok(gas_sett)
}
- /// Retrieve the current sensor informations
- pub fn get_sensor_data(
+ /// Retrieve the current sensor measurement.
+ pub fn get_measurement(
&mut self,
delay: &mut D,
- ) -> Result<(FieldData, FieldDataCondition), ::Error, ::Error> {
- let mut buff: [u8; BME680_FIELD_LENGTH] = [0; BME680_FIELD_LENGTH];
+ ) -> Result<(FieldData, FieldDataCondition), anyhow::Error> {
+ let mut buffer: [u8; BME680_FIELD_LENGTH] = [0; BME680_FIELD_LENGTH];
- debug!("Buf {:?}, len: {}", buff, buff.len());
+ debug!("Buf {:?}, len: {}", buffer, buffer.len());
let mut data: FieldData = Default::default();
const TRIES: u8 = 10;
for _ in 0..TRIES {
- I2CUtil::read_bytes(
- &mut self.i2c,
- self.dev_id.addr(),
+ I2CUtility::read_bytes(
+ self.i2c_bus_handle.borrow_mut().deref_mut(),
+ self.device_address.addr(),
BME680_FIELD0_ADDR,
- &mut buff,
+ &mut buffer,
)?;
- debug!("Field data read {:?}, len: {}", buff, buff.len());
+ debug!("Field data read {:?}, len: {}", buffer, buffer.len());
- data.status = buff[0] & BME680_NEW_DATA_MSK;
- data.gas_index = buff[0] & BME680_GAS_INDEX_MSK;
- data.meas_index = buff[1];
+ data.status = buffer[0] & BME680_NEW_DATA_MSK;
+ data.gas_index = buffer[0] & BME680_GAS_INDEX_MSK;
+ data.measurement_index = buffer[1];
- let adc_pres = (buff[2] as u32).wrapping_mul(4096)
- | (buff[3] as u32).wrapping_mul(16)
- | (buff[4] as u32).wrapping_div(16);
- let adc_temp = (buff[5] as u32).wrapping_mul(4096)
- | (buff[6] as u32).wrapping_mul(16)
- | (buff[7] as u32).wrapping_div(16);
- let adc_hum = ((buff[8] as u32).wrapping_mul(256) | buff[9] as u32) as u16;
- let adc_gas_res =
- ((buff[13] as u32).wrapping_mul(4) | (buff[14] as u32).wrapping_div(64)) as u16;
- let gas_range = buff[14] & BME680_GAS_RANGE_MSK;
+ let adc_pressure = (buffer[2] as u32).wrapping_mul(4096)
+ | (buffer[3] as u32).wrapping_mul(16)
+ | (buffer[4] as u32).wrapping_div(16);
+ let adc_temperature = (buffer[5] as u32).wrapping_mul(4096)
+ | (buffer[6] as u32).wrapping_mul(16)
+ | (buffer[7] as u32).wrapping_div(16);
+ let adc_humidity = ((buffer[8] as u32).wrapping_mul(256) | buffer[9] as u32) as u16;
+ let adc_gas_resistance =
+ ((buffer[13] as u32).wrapping_mul(4) | (buffer[14] as u32).wrapping_div(64)) as u16;
+ let gas_range = buffer[14] & BME680_GAS_RANGE_MSK;
- data.status |= buff[14] & BME680_GASM_VALID_MSK;
- data.status |= buff[14] & BME680_HEAT_STAB_MSK;
+ data.status |= buffer[14] & BME680_GASM_VALID_MSK;
+ data.status |= buffer[14] & BME680_HEAT_STAB_MSK;
if data.status & BME680_NEW_DATA_MSK != 0 {
- let (temp, t_fine) =
- Calc::calc_temperature(&self.calib, adc_temp, self.tph_sett.temperature_offset);
+ let (temp, t_fine) = Calculation::temperature(
+ &self.calibration_data,
+ adc_temperature,
+ Some(self.temperature_offset),
+ );
debug!(
"adc_temp: {} adc_pres: {} adc_hum: {} adc_gas_res: {}, t_fine: {}",
- adc_temp, adc_pres, adc_hum, adc_gas_res, t_fine
+ adc_temperature, adc_pressure, adc_humidity, adc_gas_resistance, t_fine
);
data.temperature = temp;
- data.pressure = Calc::calc_pressure(&self.calib, t_fine, adc_pres);
- data.humidity = Calc::calc_humidity(&self.calib, t_fine, adc_hum);
- data.gas_resistance =
- Calc::calc_gas_resistance(&self.calib, adc_gas_res, gas_range);
+ data.pressure = Calculation::pressure(&self.calibration_data, t_fine, adc_pressure);
+ data.humidity = Calculation::humidity(&self.calibration_data, t_fine, adc_humidity);
+ data.gas_resistance = Calculation::gas_resistance(
+ &self.calibration_data,
+ adc_gas_resistance,
+ gas_range,
+ );
return Ok((data, FieldDataCondition::NewData));
}
- delay
- .delay_ms(BME680_POLL_PERIOD_MS)
- .map_err(|_| Error::Delay)?;
+ delay.delay_ms(BME680_POLL_PERIOD_MS as u32);
}
Ok((data, FieldDataCondition::Unchanged))
}
diff --git a/src/settings.rs b/src/settings.rs
index f921c6a..0bfa31f 100644
--- a/src/settings.rs
+++ b/src/settings.rs
@@ -14,16 +14,15 @@ pub enum OversamplingSetting {
}
impl OversamplingSetting {
- // TODO replace with TryFrom once stabilized
- pub fn from_u8(os: u8) -> OversamplingSetting {
- match os {
+ pub fn from_u8(value: u8) -> OversamplingSetting {
+ match value {
0 => OversamplingSetting::OSNone,
1 => OversamplingSetting::OS1x,
2 => OversamplingSetting::OS2x,
3 => OversamplingSetting::OS4x,
4 => OversamplingSetting::OS8x,
5 => OversamplingSetting::OS16x,
- _ => panic!("Unknown oversampling setting: {}", os),
+ _ => panic!("Unknown oversampling setting: {}", value),
}
}
}
@@ -43,9 +42,8 @@ pub enum IIRFilterSize {
}
impl IIRFilterSize {
- // TODO replace with TryFrom once stabilized
- pub fn from_u8(os: u8) -> IIRFilterSize {
- match os {
+ pub fn from_u8(value: u8) -> IIRFilterSize {
+ match value {
0 => IIRFilterSize::Size0,
1 => IIRFilterSize::Size1,
2 => IIRFilterSize::Size3,
@@ -54,7 +52,7 @@ impl IIRFilterSize {
5 => IIRFilterSize::Size31,
6 => IIRFilterSize::Size63,
7 => IIRFilterSize::Size127,
- _ => panic!("Unknown IIRFilterSize: {}", os),
+ _ => panic!("Unknown IIRFilterSize: {}", value),
}
}
}
@@ -62,20 +60,20 @@ impl IIRFilterSize {
/// Temperature settings
#[derive(Debug, Default, Copy)]
#[repr(C)]
-pub struct TphSett {
+pub struct TemperatureSettings {
/// Humidity oversampling
- pub os_hum: Option,
+ pub humidity_oversampling: Option,
/// Temperature oversampling
- pub os_temp: Option,
+ pub temperature_oversampling: Option,
/// Pressure oversampling
- pub os_pres: Option,
+ pub pressure_oversampling: Option,
/// Filter coefficient
- pub filter: Option,
+ pub filter_size: Option,
/// If set, the temperature t_fine will be increased by the given value in celsius.
pub temperature_offset: Option,
}
-impl Clone for TphSett {
+impl Clone for TemperatureSettings {
fn clone(&self) -> Self {
*self
}
@@ -84,20 +82,22 @@ impl Clone for TphSett {
/// Gas measurement settings
#[derive(Debug, Default, Copy)]
#[repr(C)]
-pub struct GasSett {
+pub struct GasSettings {
+ /// nb_conv is used to select heater set-points of the sensor.
pub nb_conv: u8,
/// Heater control
- pub heatr_ctrl: Option,
+ pub heater_control: Option,
/// Enable measurement of gas, disabled by default
- pub run_gas_measurement: bool,
- /// Heater temperature
- pub heatr_temp: Option,
- /// Profile duration
- pub heatr_dur: Option,
+ pub enable_gas_measurement: bool,
+ /// The heater temperature
+ pub heater_temperature: Option,
+ /// The Heating duration
+ pub heater_duration: Option,
+ /// The ambient temperature.
pub ambient_temperature: i8,
}
-impl Clone for GasSett {
+impl Clone for GasSettings {
fn clone(&self) -> Self {
*self
}
@@ -107,9 +107,9 @@ impl Clone for GasSett {
#[derive(Debug, Default, Copy)]
pub struct SensorSettings {
/// Gas settings
- pub gas_sett: GasSett,
+ pub gas_settings: GasSettings,
/// Temperature settings
- pub tph_sett: TphSett,
+ pub temperature_settings: TemperatureSettings,
}
impl Clone for SensorSettings {
@@ -132,15 +132,15 @@ bitflags! {
/// To set gas measurement setting.
const GAS_MEAS_SEL = 8;
/// To set filter setting.
- const FILTER_SEL = 16;
+ const FILTER_SIZE_SEL = 16;
/// To set humidity control setting.
- const HCNTRL_SEL = 32;
+ const HUMIDITY_CONTROL_SEL = 32;
/// To set run gas setting.
const RUN_GAS_SEL = 64;
/// To set NB conversion setting.
const NBCONV_SEL = 128;
/// To set all gas sensor related settings
- const GAS_SENSOR_SEL = Self::GAS_MEAS_SEL.bits | Self::RUN_GAS_SEL.bits | Self::NBCONV_SEL.bits;
+ const GAS_SENSOR_SEL = Self::GAS_MEAS_SEL.bits() | Self::RUN_GAS_SEL.bits() | Self::NBCONV_SEL.bits();
}
}
@@ -171,74 +171,96 @@ pub struct SettingsBuilder {
pub type Settings = (SensorSettings, DesiredSensorSettings);
impl SettingsBuilder {
+ /// Constructs a new instance of the SettingsBuilder.
pub fn new() -> SettingsBuilder {
SettingsBuilder::default()
}
- pub fn with_temperature_filter(mut self, filter: IIRFilterSize) -> SettingsBuilder {
- self.sensor_settings.tph_sett.filter = Some(filter);
- self.desired_settings |= DesiredSensorSettings::FILTER_SEL;
+ /// With temperature filter.
+ pub fn with_temperature_filter(mut self, filter_size: IIRFilterSize) -> SettingsBuilder {
+ self.sensor_settings.temperature_settings.filter_size = Some(filter_size);
+ self.desired_settings |= DesiredSensorSettings::FILTER_SIZE_SEL;
self
}
- pub fn with_humidity_control(mut self, heatr_control: u8) -> SettingsBuilder {
- self.sensor_settings.gas_sett.heatr_ctrl = Some(heatr_control);
- self.desired_settings |= DesiredSensorSettings::HCNTRL_SEL;
+ /// With gas heater control.
+ pub fn with_gas_heater_control(mut self, heater_control: u8) -> SettingsBuilder {
+ self.sensor_settings.gas_settings.heater_control = Some(heater_control);
+ self.desired_settings |= DesiredSensorSettings::HUMIDITY_CONTROL_SEL;
self
}
+ /// With temperature oversampling
pub fn with_temperature_oversampling(
mut self,
- os_temp: OversamplingSetting,
+ temperature_oversampling: OversamplingSetting,
) -> SettingsBuilder {
- self.sensor_settings.tph_sett.os_temp = Some(os_temp);
+ self.sensor_settings
+ .temperature_settings
+ .temperature_oversampling = Some(temperature_oversampling);
self.desired_settings |= DesiredSensorSettings::OST_SEL;
self
}
- pub fn with_pressure_oversampling(mut self, os_pres: OversamplingSetting) -> SettingsBuilder {
- self.sensor_settings.tph_sett.os_pres = Some(os_pres);
+ /// With pressure oversampling.
+ pub fn with_pressure_oversampling(
+ mut self,
+ pressure_oversampling: OversamplingSetting,
+ ) -> SettingsBuilder {
+ self.sensor_settings
+ .temperature_settings
+ .pressure_oversampling = Some(pressure_oversampling);
self.desired_settings |= DesiredSensorSettings::OSP_SEL;
self
}
- pub fn with_humidity_oversampling(mut self, os_hum: OversamplingSetting) -> SettingsBuilder {
- self.sensor_settings.tph_sett.os_hum = Some(os_hum);
+ /// With humidity oversampling.
+ pub fn with_humidity_oversampling(
+ mut self,
+ humidity_oversampling: OversamplingSetting,
+ ) -> SettingsBuilder {
+ self.sensor_settings
+ .temperature_settings
+ .humidity_oversampling = Some(humidity_oversampling);
self.desired_settings |= DesiredSensorSettings::OSH_SEL;
self
}
+ /// With gas measurement.
pub fn with_gas_measurement(
mut self,
- heatr_dur: Duration,
- heatr_temp: u16,
+ heater_duration: Duration,
+ heater_temperature: u16,
ambient_temperature: i8,
) -> SettingsBuilder {
- self.sensor_settings.gas_sett.heatr_dur = Some(heatr_dur);
- self.sensor_settings.gas_sett.heatr_temp = Some(heatr_temp);
- self.sensor_settings.gas_sett.ambient_temperature = ambient_temperature;
+ self.sensor_settings.gas_settings.heater_duration = Some(heater_duration);
+ self.sensor_settings.gas_settings.heater_temperature = Some(heater_temperature);
+ self.sensor_settings.gas_settings.ambient_temperature = ambient_temperature;
self.desired_settings |= DesiredSensorSettings::GAS_SENSOR_SEL;
self
}
+ /// With nb_conv.
pub fn with_nb_conv(mut self, nb_conv: u8) -> SettingsBuilder {
- self.sensor_settings.gas_sett.nb_conv = nb_conv;
+ self.sensor_settings.gas_settings.nb_conv = nb_conv;
self.desired_settings |= DesiredSensorSettings::GAS_SENSOR_SEL;
self
}
+ /// With run gas.
pub fn with_run_gas(mut self, run_gas: bool) -> SettingsBuilder {
- self.sensor_settings.gas_sett.run_gas_measurement = run_gas;
+ self.sensor_settings.gas_settings.enable_gas_measurement = run_gas;
self.desired_settings |= DesiredSensorSettings::GAS_SENSOR_SEL;
self
}
- /// Temperature offset in Celsius, e.g. 4, -8, 1.25
+ /// With temperature offset in Celsius, e.g. 4, -8, 1.25
pub fn with_temperature_offset(mut self, offset: f32) -> SettingsBuilder {
- self.sensor_settings.tph_sett.temperature_offset = Some(offset);
+ self.sensor_settings.temperature_settings.temperature_offset = Some(offset);
self
}
+ /// Builds the settings object
pub fn build(self) -> Settings {
(self.sensor_settings, self.desired_settings)
}