use embedded_devices_derive::forward_register_fns;
use embedded_devices_derive::sensor;
use embedded_interfaces::TransportError;
use registers::{
Configuration, ConversionMode, FaultDetectionCycle, FaultThresholdHigh, FaultThresholdLow, FilterMode, WiringMode,
};
use uom::si::f64::ThermodynamicTemperature;
use uom::si::thermodynamic_temperature::degree_celsius;
use crate::utils::callendar_van_dusen;
pub mod registers;
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, thiserror::Error)]
pub enum FaultDetectionError<BusError> {
#[error("transport error")]
Transport(#[from] TransportError<(), BusError>),
#[error("fault detection timeout")]
Timeout,
#[error("fault detected")]
FaultDetected,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, thiserror::Error)]
pub enum MeasurementError<BusError> {
#[error("transport error")]
Transport(#[from] TransportError<(), BusError>),
#[error("fault detected")]
FaultDetected,
}
#[derive(Debug, embedded_devices_derive::Measurement)]
pub struct Measurement {
#[measurement(Temperature)]
pub temperature: ThermodynamicTemperature,
}
#[maybe_async_cfg::maybe(
idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), RegisterInterface),
sync(feature = "sync"),
async(feature = "async")
)]
pub struct MAX31865<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> {
delay: D,
interface: I,
reference_resistor_ratio: f64,
}
pub trait MAX31865Register {}
#[maybe_async_cfg::maybe(
idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), SpiDevice),
sync(feature = "sync"),
async(feature = "async")
)]
impl<D, I> MAX31865<D, embedded_interfaces::spi::SpiDevice<I>>
where
I: hal::spi::r#SpiDevice,
D: hal::delay::DelayNs,
{
#[inline]
pub fn new_spi(delay: D, interface: I, reference_resistor_ratio: f64) -> Self {
Self {
delay,
interface: embedded_interfaces::spi::SpiDevice::new(interface),
reference_resistor_ratio,
}
}
}
#[forward_register_fns]
#[sensor(Temperature)]
#[maybe_async_cfg::maybe(
idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), RegisterInterface),
sync(feature = "sync"),
async(feature = "async")
)]
impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> MAX31865<D, I> {
pub async fn init(
&mut self,
wiring_mode: WiringMode,
filter_mode: FilterMode,
) -> Result<(), FaultDetectionError<I::BusError>> {
self.write_register(
Configuration::default()
.with_wiring_mode(wiring_mode)
.with_filter_mode(filter_mode),
)
.await?;
self.write_register(FaultThresholdLow::default().with_resistance_ratio(0))
.await?;
self.write_register(FaultThresholdHigh::default().with_resistance_ratio(0x7fff))
.await?;
self.detect_faults().await
}
pub async fn detect_faults(&mut self) -> Result<(), FaultDetectionError<I::BusError>> {
let reg_conf = self.read_register::<Configuration>().await?;
self.write_register(
reg_conf
.with_enable_bias_voltage(true)
.with_conversion_mode(ConversionMode::NormallyOff)
.with_clear_fault_status(false)
.with_fault_detection_cycle(FaultDetectionCycle::Automatic),
)
.await?;
self.delay.delay_us(550).await;
const TRIES: u8 = 5;
for _ in 0..TRIES {
let cycle = self
.read_register::<Configuration>()
.await?
.read_fault_detection_cycle();
if cycle == FaultDetectionCycle::Finished {
self.write_register(reg_conf.with_enable_bias_voltage(false)).await?;
let has_fault = self.read_register::<registers::Resistance>().await?.read_fault();
if has_fault {
return Err(FaultDetectionError::FaultDetected);
} else {
return Ok(());
}
}
self.delay.delay_us(100).await;
}
self.write_register(reg_conf.with_enable_bias_voltage(false)).await?;
Err(FaultDetectionError::Timeout)
}
pub fn temperature_to_raw_resistance_ratio(&mut self, temperature: ThermodynamicTemperature) -> u16 {
let temperature = temperature.get::<degree_celsius>() as f32;
let resistance = callendar_van_dusen::temperature_to_resistance_r100(temperature);
let ratio = resistance / (100.0 * self.reference_resistor_ratio) as f32;
if ratio >= 1.0 {
(1 << 15) - 1
} else {
(ratio * (1 << 15) as f32) as u16
}
}
pub fn raw_resistance_ratio_to_temperature(&mut self, raw_resistance: u16) -> ThermodynamicTemperature {
let resistance = (100.0 * raw_resistance as f32 * self.reference_resistor_ratio as f32) / ((1 << 15) as f32);
let temperature = callendar_van_dusen::resistance_to_temperature_r100(resistance);
ThermodynamicTemperature::new::<degree_celsius>(temperature as f64)
}
pub async fn read_temperature(&mut self) -> Result<ThermodynamicTemperature, MeasurementError<I::BusError>> {
let resistance = self.read_register::<registers::Resistance>().await?;
if resistance.read_fault() {
return Err(MeasurementError::FaultDetected);
}
Ok(self.raw_resistance_ratio_to_temperature(resistance.read_resistance_ratio()))
}
}
#[maybe_async_cfg::maybe(
idents(
hal(sync = "embedded_hal", async = "embedded_hal_async"),
RegisterInterface,
OneshotSensor
),
sync(feature = "sync"),
async(feature = "async")
)]
impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::sensor::OneshotSensor
for MAX31865<D, I>
{
type Error = MeasurementError<I::BusError>;
type Measurement = Measurement;
async fn measure(&mut self) -> Result<Self::Measurement, Self::Error> {
let reg_conf = self
.read_register::<Configuration>()
.await?
.with_enable_bias_voltage(false)
.with_conversion_mode(ConversionMode::NormallyOff)
.with_oneshot(false);
self.write_register(reg_conf.with_enable_bias_voltage(true)).await?;
self.delay.delay_us(2000).await;
self.write_register(reg_conf.with_enable_bias_voltage(true).with_oneshot(true))
.await?;
let measurement_time_us = match reg_conf.read_filter_mode() {
FilterMode::F_60Hz => 52000,
FilterMode::F_50Hz => 62500,
};
self.delay.delay_us(2000 + measurement_time_us).await;
self.write_register(reg_conf).await?;
Ok(Measurement {
temperature: self.read_temperature().await?,
})
}
}
#[maybe_async_cfg::maybe(
idents(
hal(sync = "embedded_hal", async = "embedded_hal_async"),
RegisterInterface,
ContinuousSensor
),
sync(feature = "sync"),
async(feature = "async")
)]
impl<D: hal::delay::DelayNs, I: embedded_interfaces::registers::RegisterInterface> crate::sensor::ContinuousSensor
for MAX31865<D, I>
{
type Error = MeasurementError<I::BusError>;
type Measurement = Measurement;
async fn start_measuring(&mut self) -> Result<(), Self::Error> {
let reg_conf = self.read_register::<Configuration>().await?;
self.write_register(
reg_conf
.with_enable_bias_voltage(true)
.with_conversion_mode(ConversionMode::Automatic)
.with_oneshot(false),
)
.await?;
Ok(())
}
async fn stop_measuring(&mut self) -> Result<(), Self::Error> {
let reg_conf = self.read_register::<Configuration>().await?;
self.write_register(
reg_conf
.with_enable_bias_voltage(false)
.with_conversion_mode(ConversionMode::NormallyOff)
.with_oneshot(false),
)
.await?;
Ok(())
}
async fn measurement_interval_us(&mut self) -> Result<u32, Self::Error> {
let reg_conf = self.read_register::<Configuration>().await?;
let measurement_time_us = match reg_conf.read_filter_mode() {
FilterMode::F_60Hz => 1_000_000 / 60,
FilterMode::F_50Hz => 1_000_000 / 50,
};
Ok(measurement_time_us)
}
async fn current_measurement(&mut self) -> Result<Option<Self::Measurement>, Self::Error> {
Ok(Some(Measurement {
temperature: self.read_temperature().await?,
}))
}
async fn is_measurement_ready(&mut self) -> Result<bool, Self::Error> {
Ok(true)
}
async fn next_measurement(&mut self) -> Result<Self::Measurement, Self::Error> {
let interval = self.measurement_interval_us().await?;
self.delay.delay_us(interval).await;
self.current_measurement().await?.ok_or_else(|| {
MeasurementError::Transport(TransportError::Unexpected(
"measurement was not ready even though we expected it to be",
))
})
}
}