use crate::driver::onewire::{Address, OneWire, Search, SearchError, crc8};
use embassy_time::{Duration, Instant, Timer};
use esp_hal::{gpio::AnyPin, peripherals::RMT, rmt::Rmt, time::Rate};
use heapless::Vec;
use thiserror_no_std::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error("failed to configure the RMT peripheral: {0:?}")]
RmtConfigError(#[from] esp_hal::rmt::ConfigError),
#[error("temperature conversion did not complete within 750 ms")]
ConversionTimeout,
#[error("DS18B20 scratchpad failed its CRC-8 check")]
ScratchpadCrcMismatch,
#[error("1-Wire bus error: {0}")]
OneWireError(#[from] crate::driver::onewire::Error),
#[error("more sensors connected than the driver supports")]
TooManySensorsConnected,
}
pub struct Ds18b20Driver {
ow: OneWire<'static>,
addresses: Vec<Address, { Self::MAX_SENSORS }>,
}
impl Ds18b20Driver {
const MAX_SENSORS: usize = 16;
pub fn new(rmt: RMT<'static>, pin: AnyPin<'static>) -> Result<Self, Error> {
let rmt = Rmt::new(rmt, Rate::from_mhz(80_u32))?.into_async();
#[cfg(feature = "fire27")]
let ow = OneWire::new(rmt.channel0, rmt.channel2, pin)?;
#[cfg(feature = "cores3")]
let ow = OneWire::new(rmt.channel0, rmt.channel4, pin)?;
Ok(Ds18b20Driver {
ow,
addresses: Vec::new(),
})
}
pub async fn rescan(&mut self) -> Result<(), Error> {
self.addresses.clear();
let mut search = Search::new();
loop {
match search.next(&mut self.ow).await {
Ok(address) => {
debug!("found device {}", address);
if self.addresses.push(address).is_err() {
return Err(Error::TooManySensorsConnected);
}
}
Err(SearchError::CrcMismatch) => warn!("ROM CRC mismatch, skipping device"),
Err(SearchError::SearchComplete) | Err(SearchError::NoDevicesPresent) => break,
Err(SearchError::BusError(e)) => return Err(e.into()),
}
}
Ok(())
}
pub async fn read_all_temperatures(
&mut self,
) -> Result<impl Iterator<Item = (Address, f32)>, Error> {
if self.addresses.is_empty() {
self.rescan().await?;
}
trace!("Broadcasting a measure temperature command to all attached sensors");
self.ow.reset().await?;
for cmd in [0xCC, 0x44] {
self.ow.send_byte(cmd).await?;
}
self.wait_conversion_complete().await?;
let mut temperatures = Vec::<(Address, f32), { Self::MAX_SENSORS }>::new();
for i in 0..self.addresses.len() {
let address = self.addresses[i];
match self.read_scratchpad(address).await {
Ok(temperature_celsius) => {
let _ = temperatures.push((address, temperature_celsius));
debug!("sensor {}: {}°C", address, temperature_celsius);
}
Err(e) => warn!("sensor {} lost: {:?}", address, e),
}
}
Ok(temperatures.into_iter())
}
async fn wait_conversion_complete(&mut self) -> Result<(), Error> {
let start = Instant::now();
loop {
let mut bit = [false; 1];
self.ow.exchange_bits(&[true], &mut bit).await?;
if bit[0] {
return Ok(());
}
if start.elapsed() > Duration::from_millis(800) {
return Err(Error::ConversionTimeout);
}
Timer::after(Duration::from_millis(10)).await;
}
}
async fn read_scratchpad(&mut self, address: Address) -> Result<f32, Error> {
self.ow.reset().await?;
self.ow.send_byte(0x55).await?; self.ow.send_address(address).await?;
self.ow.send_byte(0xBE).await?; let mut scratch = [0u8; 9];
for byte in scratch.iter_mut() {
*byte = self.ow.exchange_byte(0xFF).await?;
}
if crc8(&scratch[..8]) != scratch[8] {
return Err(Error::ScratchpadCrcMismatch);
}
Ok(fixed::types::I12F4::from_le_bytes([scratch[0], scratch[1]]).into())
}
}