Skip to content

Commit

Permalink
Add timestamps to sensor readings (oxidecomputer#1023)
Browse files Browse the repository at this point in the history
This PR adds timestamps to readings within the `sensors` task, which must be
supplied when calling `Sensor.post` and `Sensor.nodata`

In addition, sensor data is reorganized into a struct-of-arrays which stores the
most recent reading **and** the most recent error, along with a marker
indicating which (of the two) is most recent.  Using a struct-of-arrays saves a
significant amount of RAM (which would otherwise be lost to padding between
`struct` members).

The existing `Sensor.get` API is unchanged to preserve compatibility with
Humility; the PR adds `Sensor.get_reading` which returns both the value and the
timestamp.
  • Loading branch information
mkeeter authored Dec 15, 2022
1 parent 9bd66ea commit 3540d09
Show file tree
Hide file tree
Showing 12 changed files with 197 additions and 90 deletions.
9 changes: 6 additions & 3 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion app/gimlet/rev-b.toml
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ interrupts = {"flash_controller.irq" = 0b1}
name = "task-sensor"
features = ["itm"]
priority = 4
max-sizes = {flash = 8192, ram = 4096 }
max-sizes = {flash = 8192, ram = 8192 }
stacksize = 1024
start = true

Expand Down
2 changes: 1 addition & 1 deletion app/gimlet/rev-c.toml
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ interrupts = {"flash_controller.irq" = 0b1}
name = "task-sensor"
features = ["itm"]
priority = 4
max-sizes = {flash = 8192, ram = 4096 }
max-sizes = {flash = 8192, ram = 8192 }
stacksize = 1024
start = true

Expand Down
25 changes: 20 additions & 5 deletions idl/sensor.idol
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,35 @@ Interface(
args: {
"id": (
type: "SensorId",
recv: From("usize", None),
recv: From("u32", None),
)
},
reply: Result(
ok: "f32",
err: CLike("SensorError"),
),
),
"get_reading": (
args: {
"id": (
type: "SensorId",
recv: From("u32", None),
)
},
reply: Result(
ok: "Reading",
err: CLike("SensorError"),
),
encoding: Hubpack,
),
"post": (
args: {
"id": (
type: "SensorId",
recv: From("usize", None),
recv: From("u32", None),
),
"value": "f32",
"timestamp": "u64",
},
reply: Result(
ok: "()",
Expand All @@ -32,12 +46,13 @@ Interface(
args: {
"id": (
type: "SensorId",
recv: From("usize", None),
recv: From("u32", None),
),
"nodata": (
type: "NoData",
recv: FromPrimitive("u8"),
)
),
"timestamp": "u64",
},
reply: Result(
ok: "()",
Expand All @@ -48,7 +63,7 @@ Interface(
args: {
"id": (
type: "SensorId",
recv: From("usize", None),
recv: From("u32", None),
)
},
reply: Result(
Expand Down
50 changes: 32 additions & 18 deletions task/power/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,63 +432,77 @@ fn main() -> ! {

for (c, dev) in CONTROLLER_CONFIG.iter().zip(devices.iter_mut()) {
if c.state == PowerState::A0 && state != PowerState::A0 {
sensor.nodata(c.voltage, NoData::DeviceOff).unwrap();
sensor.nodata(c.current, NoData::DeviceOff).unwrap();
let now = sys_get_timer().now;
sensor.nodata(c.voltage, NoData::DeviceOff, now).unwrap();
sensor.nodata(c.current, NoData::DeviceOff, now).unwrap();

if let Some(id) = c.temperature {
sensor.nodata(id, NoData::DeviceOff).unwrap();
sensor.nodata(id, NoData::DeviceOff, now).unwrap();
}

continue;
}

if let Some(id) = c.temperature {
match dev.read_temperature() {
let r = dev.read_temperature();
let now = sys_get_timer().now;
match r {
Ok(reading) => {
sensor.post(id, reading.0).unwrap();
sensor.post(id, reading.0, now).unwrap();
}
Err(_) => {
sensor.nodata(id, NoData::DeviceError).unwrap();
sensor.nodata(id, NoData::DeviceError, now).unwrap();
}
}
}

match dev.read_iout() {
let r = dev.read_iout();
let now = sys_get_timer().now;
match r {
Ok(reading) => {
sensor.post(c.current, reading.0).unwrap();
sensor.post(c.current, reading.0, now).unwrap();
}
Err(_) => {
sensor.nodata(c.current, NoData::DeviceError).unwrap();
sensor.nodata(c.current, NoData::DeviceError, now).unwrap();
}
}

match dev.read_vout() {
let r = dev.read_vout();
let now = sys_get_timer().now;
match r {
Ok(reading) => {
sensor.post(c.voltage, reading.0).unwrap();
let now = sys_get_timer().now;
sensor.post(c.voltage, reading.0, now).unwrap();
}
Err(_) => {
sensor.nodata(c.voltage, NoData::DeviceError).unwrap();
sensor.nodata(c.voltage, NoData::DeviceError, now).unwrap();
}
}

if let Some(id) = c.input_voltage {
match dev.read_vin() {
let r = dev.read_vin();
let now = sys_get_timer().now;
match r {
Ok(reading) => {
sensor.post(id, reading.0).unwrap();
let now = sys_get_timer().now;
sensor.post(id, reading.0, now).unwrap();
}
Err(_) => {
sensor.nodata(id, NoData::DeviceError).unwrap();
sensor.nodata(id, NoData::DeviceError, now).unwrap();
}
}
}

if let Some(id) = c.input_current {
match dev.read_iin() {
let r = dev.read_iin();
let now = sys_get_timer().now;
match r {
Ok(reading) => {
sensor.post(id, reading.0).unwrap();
let now = sys_get_timer().now;
sensor.post(id, reading.0, now).unwrap();
}
Err(_) => {
sensor.nodata(id, NoData::DeviceError).unwrap();
sensor.nodata(id, NoData::DeviceError, now).unwrap();
}
}
}
Expand Down
12 changes: 7 additions & 5 deletions task/sensor-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ version = "0.1.0"
edition = "2021"

[dependencies]
num-traits = { workspace = true }
zerocopy = { workspace = true }
hubpack.workspace = true
num-traits.workspace = true
serde.workspace = true
zerocopy.workspace = true

derive-idol-err = { path = "../../lib/derive-idol-err" }
drv-i2c-api = { path = "../../drv/i2c-api" }
userlib = { path = "../../sys/userlib" }
derive-idol-err.path = "../../lib/derive-idol-err"
drv-i2c-api.path = "../../drv/i2c-api"
userlib.path = "../../sys/userlib"

# This section is here to discourage RLS/rust-analyzer from doing test builds,
# since test builds don't work for cross compilation.
Expand Down
4 changes: 2 additions & 2 deletions task/sensor-api/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ fn main() -> Result<()> {
" #[allow(dead_code)]
pub const {sensor}_SENSOR: SensorId = \
// {}
SensorId(NUM_I2C_SENSORS + {sensor_id});",
SensorId(NUM_I2C_SENSORS as u32 + {sensor_id});",
d.description
)
.unwrap();
Expand All @@ -88,7 +88,7 @@ fn main() -> Result<()> {
for _ in 0..sensor_count {
writeln!(
&mut sensors_text,
" SensorId(NUM_I2C_SENSORS + {sensor_id}),"
" SensorId(NUM_I2C_SENSORS as u32 + {sensor_id}),"
)
.unwrap();
sensor_id += 1;
Expand Down
37 changes: 27 additions & 10 deletions task/sensor-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,46 @@

use derive_idol_err::IdolError;
use drv_i2c_api::ResponseCode;
use hubpack::SerializedSize;
use serde::{Deserialize, Serialize};
use userlib::*;

#[derive(zerocopy::AsBytes, Copy, Clone, Debug, Eq, PartialEq)]
#[derive(
zerocopy::AsBytes,
Copy,
Clone,
Debug,
Eq,
PartialEq,
Serialize,
Deserialize,
SerializedSize,
)]
#[repr(C)]
pub struct SensorId(pub usize);
pub struct SensorId(pub u32);

impl From<usize> for SensorId {
fn from(id: usize) -> Self {
impl From<u32> for SensorId {
fn from(id: u32) -> Self {
SensorId(id)
}
}

impl From<SensorId> for usize {
impl From<SensorId> for u32 {
fn from(id: SensorId) -> Self {
id.0
}
}

#[derive(Copy, Clone, Debug)]
pub enum Reading {
Absent,
Value(f32),
NoData(NoData),
#[derive(Copy, Clone, Debug, SerializedSize, Serialize, Deserialize)]
pub struct Reading {
pub timestamp: u64,
pub value: f32,
}

impl Reading {
pub fn new(value: f32, timestamp: u64) -> Self {
Self { timestamp, value }
}
}

//
Expand Down
16 changes: 10 additions & 6 deletions task/sensor-polling/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,14 @@ impl TemperatureSensor {
Device::Mwocp68 => {
for (i, &s) in self.temperature_sensors.iter().enumerate() {
let m = Mwocp68::new(&dev, i.try_into().unwrap());
let post_result = match m.read_temperature() {
Ok(v) => sensor_api.post(s, v.0),
let r = m.read_temperature();
let now = sys_get_timer().now;
let post_result = match r {
Ok(v) => sensor_api.post(s, v.0, now),
Err(e) => {
let e = Error::Mwocp68Error(e);
ringbuf_entry!(Trace::TemperatureReadFailed(s, e));
sensor_api.nodata(s, e.into())
sensor_api.nodata(s, e.into(), now)
}
};
if let Err(e) = post_result {
Expand All @@ -88,12 +90,14 @@ impl TemperatureSensor {
}
for (i, &s) in self.speed_sensors.iter().enumerate() {
let m = Mwocp68::new(&dev, i.try_into().unwrap());
let post_result = match m.read_speed() {
Ok(v) => sensor_api.post(s, v.0),
let r = m.read_speed();
let now = sys_get_timer().now;
let post_result = match r {
Ok(v) => sensor_api.post(s, v.0, now),
Err(e) => {
let e = Error::Mwocp68Error(e);
ringbuf_entry!(Trace::SpeedReadFailed(s, e));
sensor_api.nodata(s, e.into())
sensor_api.nodata(s, e.into(), now)
}
};
if let Err(e) = post_result {
Expand Down
12 changes: 7 additions & 5 deletions task/sensor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ edition = "2021"
target = "thumbv7em-none-eabihf"

[dependencies]
cfg-if = { workspace = true }
cortex-m = { workspace = true }
idol-runtime = { workspace = true }
num-traits = { workspace = true }
zerocopy = { workspace = true }
cfg-if.workspace = true
cortex-m.workspace = true
hubpack.workspace = true
idol-runtime.workspace = true
num-traits.workspace = true
serde.workspace = true
zerocopy.workspace = true

drv-i2c-api = { path = "../../drv/i2c-api" }
drv-i2c-devices = { path = "../../drv/i2c-devices" }
Expand Down
Loading

0 comments on commit 3540d09

Please sign in to comment.