use core::fmt::LowerHex;
use embassy_futures::join::join;
use embassy_futures::select::*;
use embassy_time::{Duration, Timer};
use esp_hal::rmt::{Rx, Tx};
use esp_hal::{
Async,
gpio::{
DriveMode, DriveStrength, Flex, InputConfig, Level, OutputConfig, Pin, Pull,
interconnect::*,
},
rmt::{
Channel, ConfigError, PulseCode, RxChannelConfig, RxChannelCreator, TxChannelConfig,
TxChannelCreator,
},
};
use thiserror_no_std::Error as ThisError;
const RMT_CLK_DIVIDER: u8 = 80;
const RX_IDLE_THRESHOLD: u16 = 1000;
const RX_FILTER_THRESHOLD: u8 = 10;
pub struct OneWire<'a> {
rx: Channel<'a, Async, Rx>,
tx: Channel<'a, Async, Tx>,
input: InputSignal<'a>,
}
impl<'a> OneWire<'a> {
pub fn new<Txc: TxChannelCreator<'a, Async>, Rxc: RxChannelCreator<'a, Async>, P: Pin + 'a>(
txcc: Txc,
rxcc: Rxc,
pin: P,
) -> Result<Self, Error> {
let rx_config = RxChannelConfig::default()
.with_clk_divider(RMT_CLK_DIVIDER)
.with_idle_threshold(RX_IDLE_THRESHOLD)
.with_filter_threshold(RX_FILTER_THRESHOLD)
.with_carrier_modulation(false);
let tx_config = TxChannelConfig::default()
.with_clk_divider(RMT_CLK_DIVIDER)
.with_carrier_modulation(false);
let mut pin: Flex = Flex::new(pin);
pin.apply_input_config(&InputConfig::default().with_pull(Pull::Up));
pin.apply_output_config(
&OutputConfig::default()
.with_drive_mode(DriveMode::OpenDrain)
.with_drive_strength(DriveStrength::_40mA),
);
pin.set_input_enable(true);
pin.set_output_enable(true);
let (input, output) = pin.split();
let tx = txcc.configure_tx(&tx_config)?.with_pin(output.with_output_inverter(true));
let rx = rxcc
.configure_rx(&rx_config)?
.with_pin(input.clone().with_input_inverter(true));
Ok(OneWire { rx, tx, input })
}
}
impl<'a> OneWire<'a> {
pub async fn reset(&mut self) -> Result<bool, Error> {
let data = [
PulseCode::new(Level::Low, 60, Level::High, 600),
PulseCode::new(Level::Low, 600, Level::Low, 0),
PulseCode::end_marker(),
];
let mut indata = [PulseCode::end_marker(); 10];
let _res = self.send_and_receive(&mut indata, &data).await?;
Ok(indata[0].length1() > 0
&& indata[0].length2() > 0
&& indata[1].length1() > 100
&& indata[1].length1() < 200)
}
const RX_TIMEOUT: Duration = Duration::from_millis(10);
pub async fn send_and_receive(
&mut self,
indata: &mut [PulseCode],
data: &[PulseCode],
) -> Result<usize, Error> {
if self.input.level() == Level::Low {
Err(Error::InputNotHigh)?;
}
match select(
join(self.rx.receive(indata), self.tx.transmit(data)),
Timer::after(Self::RX_TIMEOUT),
)
.await
{
Either::First((rx_res, tx_res)) => {
tx_res.map_err(Error::SendError)?;
rx_res.map_err(Error::ReceiveError)
}
Either::Second(()) => Err(Error::ReceiveTimedOut),
}
}
const ZERO_BIT_LEN: u16 = 70;
const ONE_BIT_LEN: u16 = 3;
const READ_SAMPLE_THRESHOLD: u16 = 20;
pub fn encode_bit(bit: bool) -> PulseCode {
if bit {
PulseCode::new(
Level::High,
Self::ONE_BIT_LEN,
Level::Low,
Self::ZERO_BIT_LEN,
)
} else {
PulseCode::new(
Level::High,
Self::ZERO_BIT_LEN,
Level::Low,
Self::ONE_BIT_LEN,
)
}
}
pub fn decode_bit(code: PulseCode) -> bool {
code.length1() < Self::READ_SAMPLE_THRESHOLD
}
pub async fn exchange_byte(&mut self, byte: u8) -> Result<u8, Error> {
let mut data = [PulseCode::end_marker(); 10];
let mut indata = [PulseCode::end_marker(); 10];
for (n, slot) in data.iter_mut().take(8).enumerate() {
*slot = Self::encode_bit(byte & (1 << n) != 0);
}
let _res = self.send_and_receive(&mut indata, &data).await?;
let mut res: u8 = 0;
for (n, &code) in indata.iter().take(8).enumerate() {
if Self::decode_bit(code) {
res |= 1 << n;
}
}
Ok(res)
}
pub async fn send_byte(&mut self, byte: u8) -> Result<(), Error> {
let mut data = [PulseCode::end_marker(); 10];
for (n, slot) in data.iter_mut().take(8).enumerate() {
*slot = Self::encode_bit(byte & (1 << n) != 0);
}
self.tx.transmit(&data).await?;
Ok(())
}
const MAX_EXCHANGE_BITS: usize = 8;
pub async fn exchange_bits(&mut self, bits: &[bool], out: &mut [bool]) -> Result<(), Error> {
debug_assert_eq!(bits.len(), out.len());
debug_assert!(bits.len() <= Self::MAX_EXCHANGE_BITS);
let n_bits = bits.len();
let mut data = [PulseCode::end_marker(); Self::MAX_EXCHANGE_BITS + 1];
let mut indata = [PulseCode::end_marker(); Self::MAX_EXCHANGE_BITS + 1];
for n in 0..n_bits {
data[n] = Self::encode_bit(bits[n]);
}
let _res = self
.send_and_receive(&mut indata[..n_bits + 1], &data[..n_bits + 1])
.await?;
for n in 0..n_bits {
out[n] = Self::decode_bit(indata[n]);
}
Ok(())
}
pub async fn send_u64(&mut self, val: u64) -> Result<(), Error> {
for byte in val.to_le_bytes() {
self.send_byte(byte).await?;
}
Ok(())
}
pub async fn send_address(&mut self, val: Address) -> Result<(), Error> {
self.send_u64(val.0).await
}
}
#[derive(Debug, ThisError)]
pub enum Error {
#[error("1-Wire bus was not idle-high before a transaction (missing pull-up?)")]
InputNotHigh,
#[error("no 1-Wire response sampled before the RX timeout")]
ReceiveTimedOut,
#[error("RMT RX channel error: {0:?}")]
ReceiveError(esp_hal::rmt::Error),
#[error("RMT TX channel error: {0:?}")]
SendError(esp_hal::rmt::Error),
#[error("RMT channel configuration error: {0:?}")]
ConfigError(#[from] ConfigError),
}
impl From<esp_hal::rmt::Error> for Error {
fn from(e: esp_hal::rmt::Error) -> Error {
Error::SendError(e)
}
}
pub fn crc8(data: &[u8]) -> u8 {
let mut crc = 0u8;
for &byte in data {
let mut b = byte;
for _ in 0..8 {
let mix = (crc ^ b) & 0x01;
crc >>= 1;
if mix != 0 {
crc ^= 0x8C;
}
b >>= 1;
}
}
crc
}
#[derive(PartialEq, Eq, Clone, Copy, Hash)]
pub struct Address(pub u64);
impl LowerHex for Address {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
for byte in self.0.to_le_bytes() {
core::write!(f, "{:02x}", byte)?;
}
Ok(())
}
}
impl core::fmt::Display for Address {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
for (i, byte) in self.0.to_le_bytes().iter().enumerate() {
if i > 0 {
core::write!(f, ":")?;
}
core::write!(f, "{:02X}", byte)?;
}
Ok(())
}
}
impl core::fmt::Debug for Address {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::write!(f, "Address({})", self)
}
}
pub struct Search {
command: u8,
address: u64,
#[cfg(feature = "search-masks")]
address_mask: u64,
last_discrepancy: Option<usize>,
complete: bool,
}
#[derive(Debug, ThisError)]
pub enum SearchError {
#[error("all devices have been enumerated")]
SearchComplete,
#[error("no device responded to the search")]
NoDevicesPresent,
#[error("enumerated ROM address failed its CRC-8 check")]
CrcMismatch,
#[error("1-Wire bus error during search: {0}")]
BusError(#[from] Error),
}
impl Search {
pub fn new() -> Search {
Search {
command: 0xF0,
address: 0,
#[cfg(feature = "search-masks")]
address_mask: 0,
last_discrepancy: None,
complete: false,
}
}
pub fn new_alarm() -> Search {
Search {
command: 0xEC,
address: 0,
#[cfg(feature = "search-masks")]
address_mask: 0,
last_discrepancy: None,
complete: false,
}
}
#[cfg(feature = "search-masks")]
pub fn new_with_mask(fixed_bits: u64, bit_mask: u64) -> Search {
Search {
command: 0xEC,
address: fixed_bits,
address_mask: bit_mask,
last_discrepancy: None,
complete: false,
}
}
pub async fn next<'d>(&mut self, ow: &mut OneWire<'d>) -> Result<Address, SearchError> {
if self.complete {
return Err(SearchError::SearchComplete);
}
let have_devices = ow.reset().await?;
let mut last_zero = None;
ow.send_byte(self.command).await?;
if have_devices {
for id_bit_number in 0..64 {
let mut id_bits = [false; 2];
ow.exchange_bits(&[true, true], &mut id_bits).await?;
let search_direction = match id_bits {
#[cfg(feature = "search-masks")]
_ if self.address_mask & (1 << id_bit_number) != 0 => {
self.address & (1 << id_bit_number) != 0
}
[false, true] => false,
[true, false] => true,
[true, true] => {
return Err(SearchError::NoDevicesPresent);
}
[false, false] => {
if self.last_discrepancy == Some(id_bit_number) {
true
} else if Some(id_bit_number) > self.last_discrepancy {
last_zero = Some(id_bit_number);
false
} else {
self.address & (1 << id_bit_number) != 0
}
}
};
if search_direction {
self.address |= 1 << id_bit_number;
} else {
self.address &= !(1 << id_bit_number);
}
let mut sent = [false; 1];
ow.exchange_bits(&[search_direction], &mut sent).await?;
}
self.last_discrepancy = last_zero;
self.complete = last_zero.is_none();
let rom = self.address.to_le_bytes();
if crc8(&rom[..7]) != rom[7] {
return Err(SearchError::CrcMismatch);
}
Ok(Address(self.address))
} else {
Err(SearchError::NoDevicesPresent)
}
}
}
impl Default for Search {
fn default() -> Self {
Self::new()
}
}