Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement new measurement wire format #82

Merged
merged 6 commits into from
Nov 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ one-wire-bus = "0.1.1"
stm32l0 = "0.10.0"
stm32l0xx-hal = { git = "ssh://git@github.com/stm32-rs/stm32l0xx-hal.git", branch = "master", features = ["rt", "mcu-STM32L071KBTx"] }
shtcx = { git = "https://github.com/dbrgn/shtcx-rs", branch = "master" }
bitfield = "0.13"

[target.'cfg(target_arch = "arm")'.dependencies]
panic-halt = "0.2" # Use ITM or panic-persist in the future
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![cfg_attr(not(test), no_std)]
pub mod delay;
pub mod measurement;
pub mod supply_monitor;
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use stm32l0xx_hal::{self as hal, i2c::I2c, pac, serial, time};
mod delay;
mod ds18b20_utils;
mod leds;
mod measurement;
mod monotonic_stm32l0;
mod supply_monitor;
mod version;
Expand Down
163 changes: 163 additions & 0 deletions src/measurement.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use bitfield::{bitfield, Bit, BitRange};

#[derive(Copy, Clone, Default)]
pub struct U12(u16);
rnestler marked this conversation as resolved.
Show resolved Hide resolved

impl U12 {
pub fn new(value: u16) -> Self {
Self(value.min(0xFFF))
}
}

pub const MAX_MSG_LEN: usize = 8;

#[derive(Copy, Clone, Default)]
pub struct MeasurementMessage {
pub t_water: Option<U12>,
pub t_inside: Option<u16>,
pub rh_inside: Option<u16>,
pub v_supply: Option<U12>,
}

trait MeasurementValue {
const SIZE: usize;
fn encode(&self, output: &mut EncodedMeasurement<[u8; MAX_MSG_LEN]>, bit_index: &mut usize);
}

impl MeasurementValue for U12 {
const SIZE: usize = 12;
fn encode(&self, output: &mut EncodedMeasurement<[u8; MAX_MSG_LEN]>, bit_index: &mut usize) {
output.set_bit_range(*bit_index + Self::SIZE - 1, *bit_index, self.0);
*bit_index += Self::SIZE;
}
}

impl MeasurementValue for u16 {
const SIZE: usize = 16;
fn encode(&self, output: &mut EncodedMeasurement<[u8; MAX_MSG_LEN]>, bit_index: &mut usize) {
output.set_bit_range(*bit_index + Self::SIZE - 1, *bit_index, *self);
*bit_index += Self::SIZE;
}
}

bitfield! {
pub struct EncodedMeasurement(MSB0 [u8]);
dbrgn marked this conversation as resolved.
Show resolved Hide resolved
}

/// The encoder encodes `MeasurementValue`s into an `EncodedMeasurement` output buffer.
///
/// It keeps track of the offset and calculates the number of bytes written when finishing.
struct Encoder<'a> {
bit_index: usize,
data_mask: u8,
output: &'a mut EncodedMeasurement<[u8; MAX_MSG_LEN]>,
}

impl<'a> Encoder<'a> {
fn new(output: &'a mut EncodedMeasurement<[u8; MAX_MSG_LEN]>) -> Self {
Self {
bit_index: 8,
data_mask: 0,
output,
}
}

fn encode(&mut self, mask_bit: usize, value: &impl MeasurementValue) {
value.encode(self.output, &mut self.bit_index);
self.data_mask.set_bit(mask_bit, true);
}

/// Finish encoding, return the number of bytes encoded.
fn finish(self) -> usize {
self.output.0[0] = self.data_mask;
(self.bit_index + 4) / 8
}
}

impl MeasurementMessage {
/// Encode the measurement into the given buffer.
///
/// Returns the number of bytes which should be sent
pub fn encode(&self, output: &mut EncodedMeasurement<[u8; MAX_MSG_LEN]>) -> usize {
let mut encoder = Encoder::new(output);
if let Some(t_water) = self.t_water {
encoder.encode(0, &t_water);
}
if let Some(t_inside) = self.t_inside {
encoder.encode(1, &t_inside);
}
if let Some(rh_inside) = self.rh_inside {
encoder.encode(2, &rh_inside);
}
if let Some(v_supply) = self.v_supply {
encoder.encode(3, &v_supply);
}
encoder.finish()
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_measurement_encode_empty() {
let input = MeasurementMessage::default();
let expeced_result = [0];

let mut output = EncodedMeasurement([0u8; MAX_MSG_LEN]);
let length = input.encode(&mut output) as usize;

assert_eq!(length, 1);
assert_eq!(output.0[0..length], expeced_result);
}

#[test]
fn test_measurement_encode_t_water() {
let input = MeasurementMessage {
t_water: Some(U12(0b0000_0101_1010)),
..MeasurementMessage::default()
};
let expeced_result = [1, 0b0000_0101, 0b1010_0000];
let mut output = EncodedMeasurement([0u8; MAX_MSG_LEN]);

let length = input.encode(&mut output) as usize;
println!("{:012b}", input.t_water.unwrap().0);
for b in &output.0[1..length] {
print!("{:08b} ", b);
}
println!();
assert_eq!(length, 3);
assert_eq!(output.0[0..length], expeced_result);
}

#[test]
fn test_measurement_encode_all() {
let input = MeasurementMessage {
t_water: Some(U12(0b0000_0101_1010)),
t_inside: Some(0b1100_0011_1010_0101),
rh_inside: Some(0b0011_1100_0101_1010),
v_supply: Some(U12(0b1111_1010_0101)),
};
let expeced_result = [
0x0F,
0b0000_0101,
0b1010_1100,
0b0011_1010,
0b0101_0011,
0b1100_0101,
0b1010_1111,
0b1010_0101,
];
let mut output = EncodedMeasurement([0u8; MAX_MSG_LEN]);

let length = input.encode(&mut output) as usize;
println!("{:012b}", input.t_water.unwrap().0);
for b in &output.0[1..length] {
print!("{:08b} ", b);
}
println!();
assert_eq!(length, MAX_MSG_LEN);
assert_eq!(output.0[0..length], expeced_result);
}
}