use esp_hal::{
Async,
gpio::Level,
peripherals::GPIO44,
rmt::{PulseCode, TxChannelConfig, TxChannelCreator},
};
use crate::{
Error,
pulses::{self, MAX_RAW_PULSES, NEC_REPEAT, NEC_STOP},
remote::RmtTx,
};
const IR_DIVIDER: u8 = 10;
const IR_CARRIER_HIGH: u16 = 88;
const IR_CARRIER_LOW: u16 = 175;
const fn burst((mark, space): (u16, u16)) -> PulseCode {
PulseCode::new(Level::High, mark, Level::Low, space)
}
#[derive(Debug)]
pub struct IrTransmitter {
channel: RmtTx,
}
impl IrTransmitter {
pub const MAX_PULSES: usize = MAX_RAW_PULSES;
pub const MAX_DURATION: u16 = pulses::MAX_DURATION;
pub async fn send_nec(&mut self, address: u8, command: u8) -> Result<(), Error> {
self.send_nec_extended(u16::from_le_bytes([address, !address]), command)
.await
}
pub async fn send_nec_extended(&mut self, address: u16, command: u8) -> Result<(), Error> {
let mut codes = [PulseCode::end_marker(); 34];
for (code, pulse) in codes.iter_mut().zip(pulses::nec(address, command)) {
*code = burst(pulse);
}
self.transmit(&codes).await
}
pub async fn send_nec_repeat(&mut self) -> Result<(), Error> {
self.transmit(&[burst(NEC_REPEAT), burst(NEC_STOP)]).await
}
pub async fn send_raw(&mut self, pulses: &[(u16, u16)]) -> Result<(), Error> {
if !pulses::is_sendable(pulses) {
return Err(Error::Ir);
}
let mut codes = [PulseCode::end_marker(); MAX_RAW_PULSES + 1];
for (code, &pulse) in codes.iter_mut().zip(pulses) {
*code = burst(pulse);
}
self.transmit(&codes[..=pulses.len()]).await
}
async fn transmit(&mut self, codes: &[PulseCode]) -> Result<(), Error> {
self.channel.transmit(codes).await.map_err(|_| Error::Ir)
}
}
pub fn ir_transmitter(
channel: impl TxChannelCreator<'static, Async>,
pin: GPIO44<'static>,
) -> Result<IrTransmitter, Error> {
let config = TxChannelConfig::default()
.with_clk_divider(IR_DIVIDER)
.with_idle_output(true)
.with_idle_output_level(Level::Low)
.with_carrier_modulation(true)
.with_carrier_high(IR_CARRIER_HIGH)
.with_carrier_low(IR_CARRIER_LOW)
.with_carrier_level(Level::High);
let channel = channel
.configure_tx(&config)
.map_err(|_| Error::Rmt)?
.with_pin(pin);
Ok(IrTransmitter { channel })
}