#[cfg(mock)]
mod mock {
use std::io;
pub struct Spidev;
pub fn read_adc(_spi: &mut Spidev) -> io::Result<u16> {
Ok(0)
}
}
use bincode::{Decode, Encode};
use cu29::prelude::*;
use cu29::reflect::Reflect;
use serde::{Deserialize, Serialize};
#[cfg(hardware)]
use spidev::{SpiModeFlags, Spidev, SpidevOptions, SpidevTransfer};
#[cfg(mock)]
use mock::Spidev;
#[derive(Reflect)]
#[reflect(from_reflect = false)]
pub struct ADS7883 {
#[reflect(ignore)]
spi: Spidev,
integrated_value: u64,
}
const INTEGRATION_FACTOR: u64 = 8;
#[cfg(hardware)]
fn open_spi(dev_device: Option<&str>, max_speed_hz: Option<u32>) -> std::io::Result<Spidev> {
let dev_device = dev_device.unwrap_or("/dev/spidev0.0");
let max_speed_hz = max_speed_hz.unwrap_or(48_000_000);
let mut spi = Spidev::open(dev_device)?;
let options = SpidevOptions::new()
.bits_per_word(8)
.max_speed_hz(max_speed_hz)
.mode(SpiModeFlags::SPI_MODE_1)
.build();
spi.configure(&options)?;
Ok(spi)
}
#[derive(
Debug, Clone, Copy, Default, Encode, Decode, PartialEq, Serialize, Deserialize, Reflect,
)]
pub struct ADCReadingPayload<T>
where
T: Into<u128> + Copy + Reflect, {
pub analog_value: T,
}
pub type ADSReadingPayload = ADCReadingPayload<u16>;
impl From<&ADCReadingPayload<u16>> for f32 {
fn from(payload: &ADCReadingPayload<u16>) -> f32 {
payload.analog_value as f32
}
}
impl Freezable for ADS7883 {
fn freeze<E: bincode::enc::Encoder>(
&self,
encoder: &mut E,
) -> Result<(), bincode::error::EncodeError> {
Encode::encode(&self.integrated_value, encoder)
}
fn thaw<D: bincode::de::Decoder>(
&mut self,
decoder: &mut D,
) -> Result<(), bincode::error::DecodeError> {
self.integrated_value = Decode::decode(decoder)?;
Ok(())
}
}
#[inline]
#[cfg(hardware)]
fn read_adc(spi: &mut Spidev) -> std::io::Result<u16> {
let mut rx_buf = [0u8; 2];
let mut transfer = SpidevTransfer::read(&mut rx_buf);
spi.transfer(&mut transfer)?;
let adc_value = (((rx_buf[0] as u16) << 8) | (rx_buf[1] as u16)) >> 2;
Ok(adc_value)
}
#[cfg(mock)]
use mock::read_adc;
impl CuSrcTask for ADS7883 {
type Resources<'r> = ();
type Output<'m> = output_msg!(ADSReadingPayload);
fn new(config: Option<&ComponentConfig>, _resources: Self::Resources<'_>) -> CuResult<Self>
where
Self: Sized,
{
#[cfg(hardware)]
let spi = {
let (spi_dev, max_speed_hz) = read_spi_config(config)?;
open_spi(spi_dev.as_deref(), max_speed_hz)
.map_err(|e| CuError::new_with_cause("Could not open the ADS7883 SPI device", e))?
};
#[cfg(mock)]
let spi = {
let _ = config;
Spidev {}
};
Ok(ADS7883 {
spi,
integrated_value: 0,
})
}
fn start(&mut self, ctx: &CuContext) -> CuResult<()> {
debug!(ctx, "ADS7883 started at {}", ctx.now());
self.integrated_value = read_adc_value(&mut self.spi)? as u64;
self.integrated_value *= INTEGRATION_FACTOR;
Ok(())
}
fn process(&mut self, ctx: &CuContext, new_msg: &mut Self::Output<'_>) -> CuResult<()> {
let bf = ctx.now();
let analog_value = read_adc_value(&mut self.spi)?;
let af = ctx.now();
let tov = (af + bf) / 2u64;
self.integrated_value = ((self.integrated_value + analog_value as u64)
* INTEGRATION_FACTOR)
/ (INTEGRATION_FACTOR + 1);
let result = (self.integrated_value / INTEGRATION_FACTOR) as u16;
let output = ADSReadingPayload {
analog_value: result,
};
new_msg.set_payload(output);
new_msg.tov = Some(tov).into();
new_msg.metadata.set_status(result);
Ok(())
}
}
pub mod test_support {
use super::*;
#[derive(Reflect)]
pub struct ADS78883TestSink;
impl Freezable for ADS78883TestSink {}
impl CuSinkTask for ADS78883TestSink {
type Resources<'r> = ();
type Input<'m> = input_msg!(ADSReadingPayload);
fn new(
_config: Option<&ComponentConfig>,
_resources: Self::Resources<'_>,
) -> CuResult<Self> {
Ok(Self {})
}
fn process(&mut self, ctx: &CuContext, new_msg: &Self::Input<'_>) -> CuResult<()> {
debug!(ctx, "Received: {}", &new_msg.payload());
Ok(())
}
}
}
#[cfg(hardware)]
fn read_spi_config(config: Option<&ComponentConfig>) -> CuResult<(Option<String>, Option<u32>)> {
config.map_or(Ok((None, None)), |cfg| {
Ok((
cfg.get::<String>("spi_dev")?,
cfg.get::<u32>("max_speed_hz")?,
))
})
}
fn read_adc_value(spi: &mut Spidev) -> CuResult<u16> {
read_adc(spi)
.map_err(|e| CuError::new_with_cause("Could not read the ADC value from the ADS7883", e))
}