Skip to content

Commit

Permalink
SNS - Create MockI2c (and temperature sensor tests) (#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidbeechey authored Oct 28, 2024
1 parent 0e7a806 commit 12a4da3
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 10 deletions.
4 changes: 2 additions & 2 deletions boards/stm32f767zi/src/tasks/temperature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ use hyped_sensors::temperature::{Status, Temperature, TemperatureAddresses};
pub async fn read_temp() -> ! {
let p = embassy_stm32::init(Default::default());
let i2c = I2c::new_blocking(p.I2C1, p.PB8, p.PB9, Hertz(100_000), Default::default());
let hyped_i2c = Stm32f767ziI2c::new(i2c);
let mut hyped_i2c = Stm32f767ziI2c::new(i2c);

let mut temperature_sensor = Temperature::new(hyped_i2c, TemperatureAddresses::Address3f)
let mut temperature_sensor = Temperature::new(&mut hyped_i2c, TemperatureAddresses::Address3f)
.expect(
"Failed to create temperature sensor. Check the wiring and the I2C address of the sensor.",
);
Expand Down
4 changes: 2 additions & 2 deletions boards/stm32l476rg/src/tasks/temperature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ use hyped_sensors::temperature::{Status, Temperature, TemperatureAddresses};
pub async fn read_temp() -> ! {
let p = embassy_stm32::init(Default::default());
let i2c = I2c::new_blocking(p.I2C1, p.PB8, p.PB9, Hertz(100_000), Default::default());
let hyped_i2c = Stm32l476rgI2c::new(i2c);
let mut hyped_i2c = Stm32l476rgI2c::new(i2c);

let mut temperature_sensor = Temperature::new(hyped_i2c, TemperatureAddresses::Address3f)
let mut temperature_sensor = Temperature::new(&mut hyped_i2c, TemperatureAddresses::Address3f)
.expect(
"Failed to create temperature sensor. Check the wiring and the I2C address of the sensor.",
);
Expand Down
53 changes: 53 additions & 0 deletions lib/io/src/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,56 @@ pub trait HypedI2c {
data: u8,
) -> Result<(), I2cError>;
}

pub mod mock_i2c {
use heapless::FnvIndexMap;

/// A fixed-size map of I2C values, indexed by device address and register address
type I2cValues = FnvIndexMap<(u8, u8), Option<u8>, 16>;

/// A mock I2C instance which can be used for testing
pub struct MockI2c {
values: I2cValues,
writes: I2cValues,
}

impl crate::i2c::HypedI2c for MockI2c {
/// Reads a byte by looking up the device address and register address in the map
fn read_byte(&mut self, device_address: u8, register_address: u8) -> Option<u8> {
self.values
.get(&(device_address, register_address))
.copied()
.unwrap()
}

/// Writes a byte to the map so that it can be read later to check the value
fn write_byte_to_register(
&mut self,
device_address: u8,
register_address: u8,
data: u8,
) -> Result<(), super::I2cError> {
match self
.writes
.insert((device_address, register_address), Some(data))
{
Ok(_) => Ok(()),
Err(_) => Err(super::I2cError::Unknown),
}
}
}

impl MockI2c {
pub fn new(values: I2cValues) -> MockI2c {
MockI2c {
values,
writes: I2cValues::new(),
}
}

/// Returns a reference to the I2C values map
pub fn get_writes(&self) -> &I2cValues {
&self.writes
}
}
}
125 changes: 119 additions & 6 deletions lib/sensors/src/temperature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ use hyped_io::i2c::{HypedI2c, I2cError};
/// The temperature is read from the sensor and converted to a floating point value in degrees Celsius.
///
/// Data sheet: https://www.st.com/resource/en/datasheet/stts22h.pdf
pub struct Temperature<T: HypedI2c> {
i2c: T,
pub struct Temperature<'a, T: HypedI2c + 'a> {
i2c: &'a mut T,
device_address: u8,
}

impl<T: HypedI2c> Temperature<T> {
impl<'a, T: HypedI2c> Temperature<'a, T> {
/// Create a new instance of the temperature sensor and attempt to configure it
pub fn new(mut i2c: T, device_address: TemperatureAddresses) -> Result<Self, TemperatureError> {
pub fn new(
i2c: &'a mut T,
device_address: TemperatureAddresses,
) -> 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) {
Expand Down Expand Up @@ -45,9 +48,13 @@ impl<T: HypedI2c> Temperature<T> {
};
let combined: f32 =
((temperature_high_byte as u16) << 8 | temperature_low_byte as u16) as f32;
let temperature = combined * STTS22H_TEMP_SCALING_FACTOR;

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

Some(combined * STTS22H_TEMP_SCALING_FACTOR)
}

/// Check the status of the temperature sensor
Expand Down Expand Up @@ -77,6 +84,7 @@ pub enum TemperatureError {
}

/// Represents the possible statuses of the temperature sensor
#[derive(Debug, PartialEq)]
pub enum Status {
Busy,
TempOverUpperLimit,
Expand Down Expand Up @@ -113,3 +121,108 @@ const STTS22H_CONFIG_SETTINGS: u8 = 0x3c;

// Scaling factor to convert the temperature from the sensor to degrees Celsius
const STTS22H_TEMP_SCALING_FACTOR: f32 = 0.01;
const TWO_POWER_15: f32 = 32768.0;
const TWO_POWER_16: f32 = 65536.0;

#[cfg(test)]
mod tests {
use super::*;
use heapless::FnvIndexMap;
use hyped_io::i2c::mock_i2c::MockI2c;

#[test]
fn test_write_config() {
let i2c_values = FnvIndexMap::new();
let mut i2c = MockI2c::new(i2c_values);
let _ = Temperature::new(&mut i2c, TemperatureAddresses::Address3f);
assert_eq!(
i2c.get_writes()
.get(&(TemperatureAddresses::Address3f as u8, STTS22H_CTRL)),
Some(&Some(STTS22H_CONFIG_SETTINGS))
);
}

#[test]
fn test_temperature_read_0() {
let mut i2c_values = FnvIndexMap::new();
let _ = i2c_values.insert(
(TemperatureAddresses::Address3f as u8, STTS22H_DATA_TEMP_H),
Some(0x00),
);
let _ = i2c_values.insert(
(TemperatureAddresses::Address3f as u8, STTS22H_DATA_TEMP_L),
Some(0x00),
);
let mut i2c = MockI2c::new(i2c_values);
let mut temperature = Temperature::new(&mut i2c, TemperatureAddresses::Address3f).unwrap();
assert_eq!(temperature.read(), Some(0.0));
}

#[test]
fn test_temperature_read_25() {
let mut i2c_values = FnvIndexMap::new();
let _ = i2c_values.insert(
(TemperatureAddresses::Address3f as u8, STTS22H_DATA_TEMP_H),
Some(0x09),
);
let _ = i2c_values.insert(
(TemperatureAddresses::Address3f as u8, STTS22H_DATA_TEMP_L),
Some(0xc4),
);
let mut i2c = MockI2c::new(i2c_values);
let mut temperature = Temperature::new(&mut i2c, TemperatureAddresses::Address3f).unwrap();
assert_eq!(temperature.read(), Some(25.0));
}

#[test]
fn test_temperature_read_minus_10() {
let mut i2c_values = FnvIndexMap::new();
let _ = i2c_values.insert(
(TemperatureAddresses::Address3f as u8, STTS22H_DATA_TEMP_H),
Some(0xfc),
);
let _ = i2c_values.insert(
(TemperatureAddresses::Address3f as u8, STTS22H_DATA_TEMP_L),
Some(0x18),
);
let mut i2c = MockI2c::new(i2c_values);
let mut temperature = Temperature::new(&mut i2c, TemperatureAddresses::Address3f).unwrap();
assert_eq!(temperature.read(), Some(-10.0));
}

#[test]
fn test_temperature_status_busy() {
let mut i2c_values = FnvIndexMap::new();
let _ = i2c_values.insert(
(TemperatureAddresses::Address3f as u8, STTS22H_STATUS),
Some(STTS22H_STATUS_BUSY),
);
let mut i2c = MockI2c::new(i2c_values);
let mut temperature = Temperature::new(&mut i2c, TemperatureAddresses::Address3f).unwrap();
assert_eq!(temperature.check_status(), Status::Busy);
}

#[test]
fn test_temperature_status_temp_over_upper_limit() {
let mut i2c_values = FnvIndexMap::new();
let _ = i2c_values.insert(
(TemperatureAddresses::Address3f as u8, STTS22H_STATUS),
Some(STTS22H_TEMP_OVER_UPPER_LIMIT),
);
let mut i2c = MockI2c::new(i2c_values);
let mut temperature = Temperature::new(&mut i2c, TemperatureAddresses::Address3f).unwrap();
assert_eq!(temperature.check_status(), Status::TempOverUpperLimit);
}

#[test]
fn test_temperature_status_temp_under_lower_limit() {
let mut i2c_values = FnvIndexMap::new();
let _ = i2c_values.insert(
(TemperatureAddresses::Address3f as u8, STTS22H_STATUS),
Some(STTS22H_TEMP_UNDER_LOWER_LIMIT),
);
let mut i2c = MockI2c::new(i2c_values);
let mut temperature = Temperature::new(&mut i2c, TemperatureAddresses::Address3f).unwrap();
assert_eq!(temperature.check_status(), Status::TempUnderLowerLimit);
}
}

0 comments on commit 12a4da3

Please sign in to comment.