diff --git a/Cargo.lock b/Cargo.lock index bdfc9be66..96dc2d12c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4488,6 +4488,7 @@ dependencies = [ "idol-runtime", "mutable-statics", "num-traits", + "paste", "ringbuf", "serde", "task-sensor-api", diff --git a/app/psc/base.toml b/app/psc/base.toml index 530b3126b..ff901ac3c 100644 --- a/app/psc/base.toml +++ b/app/psc/base.toml @@ -215,7 +215,7 @@ notifications = ["timer"] [tasks.sensor] name = "task-sensor" priority = 3 -max-sizes = {flash = 8192, ram = 4096 } +max-sizes = {flash = 8192, ram = 8192 } stacksize = 1024 start = true notifications = ["timer"] diff --git a/app/sidecar/base.toml b/app/sidecar/base.toml index c7d1e256b..e9ce78840 100644 --- a/app/sidecar/base.toml +++ b/app/sidecar/base.toml @@ -180,7 +180,7 @@ task-slots = ["sys", "i2c_driver", "sprot"] name = "task-sensor" features = [] priority = 4 -max-sizes = {flash = 8192, ram = 4096 } +max-sizes = {flash = 8192, ram = 8192 } stacksize = 1024 start = true notifications = ["timer"] diff --git a/drv/i2c-devices/src/max31790.rs b/drv/i2c-devices/src/max31790.rs index 20463ebae..d3966e637 100644 --- a/drv/i2c-devices/src/max31790.rs +++ b/drv/i2c-devices/src/max31790.rs @@ -8,6 +8,7 @@ use crate::Validate; use bitfield::bitfield; use core::convert::TryFrom; use drv_i2c_api::*; +use ringbuf::*; use userlib::units::*; use userlib::*; @@ -250,6 +251,14 @@ fn write_reg16( device.write(&[register as u8, (val >> 8) as u8, (val & 0xff) as u8]) } +#[derive(Copy, Clone, PartialEq)] +enum Trace { + ZeroTach(Fan), + None, +} + +ringbuf!(Trace, 6, Trace::None); + impl Max31790 { pub fn new(device: &I2cDevice) -> Self { Self { device: *device } @@ -311,7 +320,18 @@ impl Max31790 { const NP: u32 = 2; const FREQ: u32 = 8192; - if count == TACH_POR_VALUE { + if count == 0 { + // + // We don't really expect this: generally, if a fan is off (or is + // otherwise emiting non-detectable tach input pulses), the + // controller will report the power-on-reset value for the tach + // count, not 0. So if we see a zero count, we will assume that + // this is an error rather than a 0 RPM reading, and record it to + // a (small) ring buffer and return accordingly. + // + ringbuf_entry!(Trace::ZeroTach(fan)); + Err(ResponseCode::BadDeviceState) + } else if count == TACH_POR_VALUE { Ok(Rpm(0)) } else { let rpm = (60 * FREQ * SR) / (count * NP); diff --git a/idl/sensor.idol b/idl/sensor.idol index c3261c4e6..917c7d647 100644 --- a/idl/sensor.idol +++ b/idl/sensor.idol @@ -71,6 +71,34 @@ Interface( encoding: Hubpack, idempotent: true, ), + "get_min": ( + description: "returns the minimum value recorded and its timestamp", + args: { + "id": ( + type: "SensorId", + ) + }, + reply: Result( + ok: "(f32, u64)", + err: CLike("SensorApiError"), + ), + encoding: Hubpack, + idempotent: true, + ), + "get_max": ( + description: "returns the maximum value recorded and its timestamp", + args: { + "id": ( + type: "SensorId", + ) + }, + reply: Result( + ok: "(f32, u64)", + err: CLike("SensorApiError"), + ), + encoding: Hubpack, + idempotent: true, + ), "post": ( args: { "id": ( diff --git a/task/sensor/Cargo.toml b/task/sensor/Cargo.toml index 1529e162c..9d3a43362 100644 --- a/task/sensor/Cargo.toml +++ b/task/sensor/Cargo.toml @@ -12,6 +12,7 @@ cortex-m.workspace = true hubpack.workspace = true idol-runtime.workspace = true num-traits.workspace = true +paste.workspace = true serde.workspace = true zerocopy.workspace = true diff --git a/task/sensor/src/main.rs b/task/sensor/src/main.rs index 7d076b33c..2e7f7b652 100644 --- a/task/sensor/src/main.rs +++ b/task/sensor/src/main.rs @@ -59,6 +59,12 @@ struct ServerImpl { data_value: SensorArray, data_time: SensorArray, + min_value: SensorArray, + min_time: SensorArray, + + max_value: SensorArray, + max_time: SensorArray, + err_value: SensorArray, err_time: SensorArray, @@ -136,6 +142,40 @@ impl idl::InOrderSensorImpl for ServerImpl { } } + fn get_min( + &mut self, + _: &RecvMessage, + id: SensorId, + ) -> Result<(f32, u64), RequestError> { + Ok(( + self.min_value + .get(id) + .cloned() + .ok_or(SensorApiError::InvalidSensor)?, + self.min_time + .get(id) + .cloned() + .ok_or(SensorApiError::InvalidSensor)?, + )) + } + + fn get_max( + &mut self, + _: &RecvMessage, + id: SensorId, + ) -> Result<(f32, u64), RequestError> { + Ok(( + self.max_value + .get(id) + .cloned() + .ok_or(SensorApiError::InvalidSensor)?, + self.max_time + .get(id) + .cloned() + .ok_or(SensorApiError::InvalidSensor)?, + )) + } + fn post( &mut self, _: &RecvMessage, @@ -153,6 +193,17 @@ impl idl::InOrderSensorImpl for ServerImpl { self.last_reading[id] = Some(r); self.data_value[id] = value; self.data_time[id] = timestamp; + + if value < self.min_value[id] { + self.min_value[id] = value; + self.min_time[id] = timestamp; + } + + if value > self.max_value[id] { + self.max_value[id] = value; + self.max_time[id] = timestamp; + } + Ok(()) } @@ -238,24 +289,35 @@ fn main() -> ! { // sys_set_timer(Some(deadline), notifications::TIMER_MASK); - let (last_reading, data_value, data_time, err_value, err_time, nerrors) = mutable_statics::mutable_statics! { - static mut LAST_READING: [Option; NUM_SENSORS] = [|| None; _]; - static mut DATA_VALUE: [f32; NUM_SENSORS] = [|| f32::NAN; _]; - static mut DATA_TIME: [u64; NUM_SENSORS] = [|| 0u64; _]; - static mut ERR_VALUE: [NoData; NUM_SENSORS] = [|| NoData::DeviceUnavailable; _]; - static mut ERR_TIME: [u64; NUM_SENSORS] = [|| 0; _]; - static mut NERRORS: [u32; NUM_SENSORS] = [|| 0; _]; - }; - - let mut server = ServerImpl { - last_reading: SensorArray(last_reading), - data_value: SensorArray(data_value), - data_time: SensorArray(data_time), - err_value: SensorArray(err_value), - err_time: SensorArray(err_time), - nerrors: SensorArray(nerrors), - deadline, - }; + macro_rules! declare_server { + ($($name:ident: $t:ty = $n:expr;)*) => {{ + paste::paste! { + let ($($name),*) = mutable_statics::mutable_statics! { + $( + static mut [<$name:upper>]: [$t; NUM_SENSORS] = [|| $n; _]; + )* + }; + let ($($name),*) = ($(SensorArray($name)),*); + ServerImpl { + deadline, + $($name),* + } + }} + }; + } + + let mut server = declare_server!( + last_reading: Option = None; + data_value: f32 = f32::NAN; + data_time: u64 = 0u64; + min_value: f32 = f32::MAX; + min_time: u64 = 0u64; + max_value: f32 = f32::MIN; + max_time: u64 = 0u64; + err_value: NoData = NoData::DeviceUnavailable; + err_time: u64 = 0; + nerrors: u32 = 0; + ); let mut buffer = [0; idl::INCOMING_SIZE];