Skip to content

Commit

Permalink
experimenting with bounds checks
Browse files Browse the repository at this point in the history
  • Loading branch information
davidbeechey committed Dec 16, 2024
1 parent 8289d8a commit b3385e7
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 24 deletions.
1 change: 1 addition & 0 deletions boards/stm32f767zi/Cargo.lock

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

1 change: 1 addition & 0 deletions boards/stm32f767zi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ critical-section = "1.1"
embedded-storage = "0.3.1"
static_cell = "2"

hyped_core = { path = "../../lib/core" }
hyped_sensors = { path = "../../lib/sensors" }

hyped_adc = { path = "../../lib/io/hyped_adc" }
Expand Down
23 changes: 18 additions & 5 deletions boards/stm32f767zi/src/tasks/temperature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use defmt_rtt as _;
use embassy_stm32::i2c::I2c;
use embassy_stm32::time::Hertz;
use embassy_sync::blocking_mutex::Mutex;
use hyped_core::types::SensorValueBounds::{Critical, Safe, Warning};
use hyped_sensors::temperature::{Status, Temperature, TemperatureAddresses};

/// Test task that just reads the temperature from the sensor and prints it to the console
Expand All @@ -19,8 +20,12 @@ pub async fn read_temp() -> ! {
)));
let mut hyped_i2c = Stm32f767ziI2c::new(i2c);

let mut temperature_sensor = Temperature::new(&mut hyped_i2c, TemperatureAddresses::Address3f)
.expect(
let mut temperature_sensor = Temperature::new(
&mut hyped_i2c,
TemperatureAddresses::Address3f,
None,
)
.expect(
"Failed to create temperature sensor. Check the wiring and the I2C address of the sensor.",
);

Expand All @@ -42,9 +47,17 @@ pub async fn read_temp() -> ! {
}

match temperature_sensor.read() {
Some(temperature) => {
defmt::info!("Temperature: {:?}", temperature);
}
Some(temperature) => match temperature {
Safe(temp) => {
defmt::info!("Temperature: {}°C (safe)", temp);
}
Warning(temp) => {
defmt::warn!("Temperature: {}°C (unsafe)", temp);
}
Critical(temp) => {
defmt::error!("Temperature: {}°C (critical)", temp);
}
},
None => {
defmt::info!("Failed to read temperature.");
}
Expand Down
2 changes: 2 additions & 0 deletions boards/stm32l432kc/Cargo.lock

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

1 change: 1 addition & 0 deletions boards/stm32l476rg/Cargo.lock

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

1 change: 1 addition & 0 deletions boards/stm32l476rg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ critical-section = "1.1"
embedded-storage = "0.3.1"
static_cell = "2"

hyped_core ={ path = "../../lib/core" }
hyped_sensors = { path = "../../lib/sensors" }

hyped_adc = { path = "../../lib/io/hyped_adc" }
Expand Down
23 changes: 18 additions & 5 deletions boards/stm32l476rg/src/tasks/temperature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use defmt_rtt as _;
use embassy_stm32::i2c::I2c;
use embassy_stm32::time::Hertz;
use embassy_sync::blocking_mutex::Mutex;
use hyped_core::types::SensorValueBounds::{Critical, Safe, Warning};
use hyped_sensors::temperature::{Status, Temperature, TemperatureAddresses};

/// Test task that just reads the temperature from the sensor and prints it to the console
Expand All @@ -19,8 +20,12 @@ pub async fn read_temp() -> ! {
)));
let mut hyped_i2c = Stm32l476rgI2c::new(i2c);

let mut temperature_sensor = Temperature::new(&mut hyped_i2c, TemperatureAddresses::Address3f)
.expect(
let mut temperature_sensor = Temperature::new(
&mut hyped_i2c,
TemperatureAddresses::Address3f,
None,
)
.expect(
"Failed to create temperature sensor. Check the wiring and the I2C address of the sensor.",
);

Expand All @@ -42,9 +47,17 @@ pub async fn read_temp() -> ! {
}

match temperature_sensor.read() {
Some(temperature) => {
defmt::info!("Temperature: {:?}", temperature);
}
Some(temperature) => match temperature {
Safe(temp) => {
defmt::info!("Temperature: {}°C (safe)", temp);
}
Warning(temp) => {
defmt::warn!("Temperature: {}°C (warning)", temp);
}
Critical(temp) => {
defmt::error!("Temperature: {}°C (critical)", temp);
}
},
None => {
defmt::info!("Failed to read temperature.");
}
Expand Down
13 changes: 13 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash

# Run cargo build in the root directory
echo "Building main workspace"
cargo build

# Find first layer of directories in /boards and run cargo build in each
for dir in boards/*/
do
dir=${dir%*/}
echo "Building $dir"
cd $dir && cargo build && cd ../..
done
10 changes: 10 additions & 0 deletions lib/core/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ impl DigitalSignal {
}
}

#[derive(PartialEq, Debug)]
pub enum SensorValueBounds<T: PartialEq> {
/// This is the normal range of values for the sensor.
Safe(T),
/// Sensor values are outwith the normal range, but not yet critical.
Warning(T),
/// This is the range of values that are considered critical and will trigger an emergency.
Critical(T),
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
65 changes: 51 additions & 14 deletions lib/sensors/src/temperature.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use hyped_core::types::SensorValueBounds;
use hyped_i2c::{HypedI2c, I2cError};

/// Temperature implements the logic to read the temperature from the STTS22H temperature sensor
Expand All @@ -10,27 +11,37 @@ use hyped_i2c::{HypedI2c, I2cError};
pub struct Temperature<'a, T: HypedI2c> {
i2c: &'a mut T,
device_address: u8,
calculate_bounds: fn(f32) -> SensorValueBounds<f32>,
}

impl<'a, T: HypedI2c> Temperature<'a, T> {
/// Create a new instance of the temperature sensor and attempt to configure it
///
/// # Arguments
/// * `i2c` - The I2C peripheral to communicate with the temperature sensor
/// * `device_address` - The I2C address of the temperature sensor
/// * `calculate_bounds` - An optional function to override the default bounds calculation
pub fn new(
i2c: &'a mut T,
device_address: TemperatureAddresses,
calculate_bounds: Option<fn(f32) -> SensorValueBounds<f32>>,
) -> Result<Self, TemperatureError> {
// Set up the temperature sensor by sending the configuration settings to the STTS22H_CTRL register
let device_address = device_address as u8;
match i2c.write_byte_to_register(device_address, STTS22H_CTRL, STTS22H_CONFIG_SETTINGS) {
Ok(_) => Ok(Self {
i2c,
device_address,
calculate_bounds: calculate_bounds.unwrap_or(default_calculate_bounds),
}),
Err(e) => Err(TemperatureError::I2cError(e)),
}
}

/// Read the temperature from the sensor and return it as a floating point value in degrees Celsius
pub fn read(&mut self) -> Option<f32> {
/// Read the temperature from the sensor and return it as a floating point value in degrees Celsius.
/// Returns None if there was an error reading the temperature, otherwise returns the temperature
/// wrapped in a SensorValueBounds enum to indicate if the temperature is safe, warning, or critical.
pub fn read(&mut self) -> Option<SensorValueBounds<f32>> {
// Read the high and low bytes of the temperature and combine them to get the temperature
let temperature_high_byte =
match self.i2c.read_byte(self.device_address, STTS22H_DATA_TEMP_H) {
Expand All @@ -50,12 +61,17 @@ impl<'a, T: HypedI2c> Temperature<'a, T> {
let combined: f32 =
((temperature_high_byte as u16) << 8 | temperature_low_byte as u16) as f32;

// Check if the temperature is negative
if combined >= TWO_POWER_15 {
// Convert the temperature to a negative value
return Some((combined - TWO_POWER_16) * STTS22H_TEMP_SCALING_FACTOR);
return Some((self.calculate_bounds)(
(combined - TWO_POWER_16) * STTS22H_TEMP_SCALING_FACTOR,
));
}

Some(combined * STTS22H_TEMP_SCALING_FACTOR)
Some((self.calculate_bounds)(
combined * STTS22H_TEMP_SCALING_FACTOR,
))
}

/// Check the status of the temperature sensor
Expand Down Expand Up @@ -106,6 +122,20 @@ impl Status {
}
}

/// Default calculation of the bounds for the temperature sensor. The bounds are set to:
/// - Safe: 20.0 to 80.0 degrees Celsius
/// - Warning: 0.0 to 20.0 and 80.0 to 100.0 degrees Celsius
/// - Critical: Below 0.0 and above 100.0 degrees Celsius
pub fn default_calculate_bounds(value: f32) -> SensorValueBounds<f32> {
if value < 0.0 || value > 100.0 {
SensorValueBounds::Critical(value)
} else if value < 20.0 || value > 80.0 {
SensorValueBounds::Warning(value)
} else {
SensorValueBounds::Safe(value)
}
}

// Registers for the STTS22H temperature sensor
const STTS22H_CTRL: u8 = 0x04;
const STTS22H_DATA_TEMP_L: u8 = 0x06;
Expand All @@ -132,13 +162,14 @@ mod tests {
use super::*;
use embassy_sync::blocking_mutex::Mutex;
use heapless::FnvIndexMap;
use hyped_core::types::SensorValueBounds::{Critical, Safe, Warning};
use hyped_i2c::mock_i2c::MockI2c;

#[test]
fn test_write_config() {
let i2c_values = Mutex::new(RefCell::new(FnvIndexMap::new()));
let mut i2c = MockI2c::new(&i2c_values);
let _ = Temperature::new(&mut i2c, TemperatureAddresses::Address3f).unwrap();
let _ = Temperature::new(&mut i2c, TemperatureAddresses::Address3f, None).unwrap();
let i2c_value = i2c
.get_writes()
.get(&(TemperatureAddresses::Address3f as u8, STTS22H_CTRL))
Expand All @@ -159,8 +190,9 @@ mod tests {
);
let i2c_values = Mutex::new(RefCell::new(i2c_values));
let mut i2c = MockI2c::new(&i2c_values);
let mut temperature = Temperature::new(&mut i2c, TemperatureAddresses::Address3f).unwrap();
assert_eq!(temperature.read(), Some(0.0));
let mut temperature =
Temperature::new(&mut i2c, TemperatureAddresses::Address3f, None).unwrap();
assert_eq!(temperature.read(), Some(Warning(0.0)));
}

#[test]
Expand All @@ -176,8 +208,9 @@ mod tests {
);
let i2c_values = Mutex::new(RefCell::new(i2c_values));
let mut i2c = MockI2c::new(&i2c_values);
let mut temperature = Temperature::new(&mut i2c, TemperatureAddresses::Address3f).unwrap();
assert_eq!(temperature.read(), Some(25.0));
let mut temperature =
Temperature::new(&mut i2c, TemperatureAddresses::Address3f, None).unwrap();
assert_eq!(temperature.read(), Some(Safe(25.0)));
}

#[test]
Expand All @@ -193,8 +226,9 @@ mod tests {
);
let i2c_values = Mutex::new(RefCell::new(i2c_values));
let mut i2c = MockI2c::new(&i2c_values);
let mut temperature = Temperature::new(&mut i2c, TemperatureAddresses::Address3f).unwrap();
assert_eq!(temperature.read(), Some(-10.0));
let mut temperature =
Temperature::new(&mut i2c, TemperatureAddresses::Address3f, None).unwrap();
assert_eq!(temperature.read(), Some(Critical(-10.0)));
}

#[test]
Expand All @@ -206,7 +240,8 @@ mod tests {
);
let i2c_values = Mutex::new(RefCell::new(i2c_values));
let mut i2c = MockI2c::new(&i2c_values);
let mut temperature = Temperature::new(&mut i2c, TemperatureAddresses::Address3f).unwrap();
let mut temperature =
Temperature::new(&mut i2c, TemperatureAddresses::Address3f, None).unwrap();
assert_eq!(temperature.check_status(), Status::Busy);
}

Expand All @@ -219,7 +254,8 @@ mod tests {
);
let i2c_values = Mutex::new(RefCell::new(i2c_values));
let mut i2c: MockI2c<'_> = MockI2c::new(&i2c_values);
let mut temperature = Temperature::new(&mut i2c, TemperatureAddresses::Address3f).unwrap();
let mut temperature =
Temperature::new(&mut i2c, TemperatureAddresses::Address3f, None).unwrap();
assert_eq!(temperature.check_status(), Status::TempOverUpperLimit);
}

Expand All @@ -232,7 +268,8 @@ mod tests {
);
let i2c_values = Mutex::new(RefCell::new(i2c_values));
let mut i2c: MockI2c<'_> = MockI2c::new(&i2c_values);
let mut temperature = Temperature::new(&mut i2c, TemperatureAddresses::Address3f).unwrap();
let mut temperature =
Temperature::new(&mut i2c, TemperatureAddresses::Address3f, None).unwrap();
assert_eq!(temperature.check_status(), Status::TempUnderLowerLimit);
}
}

0 comments on commit b3385e7

Please sign in to comment.