//! RMT Transmit Example -- Morse Code //! //! To try this out, connect a piezo buzzer via a resistor into pin 17. It should repeat "HELLO" //! in morse code. //! //! Set pin 16 to low to stop the signal and make the buzzer sound "GOODBYE" in morse code. //! //! Example loosely based off which has a circuit you can follow. //! https://github.com/espressif/esp-idf/tree/master/examples/peripherals/rmt/morse_code //! //! This example demonstrates: //! * A carrier signal. //! * Looping. //! * Background sending. //! * Taking a [`Pin`] and [`Channel`] by ref mut, so that they can be used again later. //! #![allow(unknown_lints)] #![allow(unexpected_cfgs)] #[cfg(any(feature = "rmt-legacy", esp_idf_version_major = "4"))] fn main() -> anyhow::Result<()> { example::main() } #[cfg(not(any(feature = "rmt-legacy", esp_idf_version_major = "4")))] fn main() -> anyhow::Result<()> { println!("This example requires feature `rmt-legacy` enabled or using ESP-IDF v4.4.X"); loop { std::thread::sleep(std::time::Duration::from_millis(1000)); } } #[cfg(any(feature = "rmt-legacy", esp_idf_version_major = "4"))] mod example { use esp_idf_hal::gpio::Pull; use esp_idf_hal::units::FromValueType; use esp_idf_hal::{ delay::Ets, gpio::{OutputPin, PinDriver}, peripherals::Peripherals, rmt::{ config::{CarrierConfig, DutyPercent, Loop, TransmitConfig}, PinState, Pulse, PulseTicks, RmtChannel, TxRmtDriver, VariableLengthSignal, }, }; pub fn main() -> anyhow::Result<()> { esp_idf_hal::sys::link_patches(); let peripherals = Peripherals::take()?; let mut channel = peripherals.rmt.channel0; let mut led = peripherals.pins.gpio17; let stop = peripherals.pins.gpio16; let carrier = CarrierConfig::new() .duty_percent(DutyPercent::new(50)?) .frequency(611.Hz()); let mut config = TransmitConfig::new() .carrier(Some(carrier)) .looping(Loop::Endless) .clock_divider(255); let tx = send_morse_code( unsafe { channel.reborrow() }, unsafe { led.reborrow() }, &config, "HELLO ", )?; let stop = PinDriver::input(stop, Pull::Down)?; println!("Keep sending until pin {} is set low.", stop.pin()); while stop.is_high() { Ets::delay_ms(100); } println!("Pin {} set to low. Stopped.", stop.pin()); // Release pin and channel so we can use them again. drop(tx); // Wait so the messages don't get garbled. Ets::delay_ms(3000); // Now send a single message and stop. println!("Saying GOODBYE!"); config.looping = Loop::None; send_morse_code(channel, led, &config, "GOODBYE")?; Ok(()) } fn send_morse_code<'d>( channel: impl RmtChannel + 'd, led: impl OutputPin + 'd, config: &TransmitConfig, message: &str, ) -> anyhow::Result> { println!("Sending morse message '{message}'."); let mut signal = VariableLengthSignal::new(); let pulses = str_pulses(message); // We've been collecting `Pulse` but `VariableLengthSignal` needs `&Pulse`: let pulses: Vec<&Pulse> = pulses.iter().collect(); signal.push(pulses)?; let mut tx = TxRmtDriver::new(channel, led, config)?; tx.start(signal)?; // Return `tx` so we can release the pin and channel later. Ok(tx) } enum Code { Dot, Dash, WordGap, } impl Code { pub fn push_pulse(&self, pulses: &mut Vec) { match &self { Code::Dot => pulses.extend_from_slice(&[high(), low()]), Code::Dash => pulses.extend_from_slice(&[high(), high(), high(), low()]), Code::WordGap => { pulses.extend_from_slice(&[low(), low(), low(), low(), low(), low()]) } } } } fn high() -> Pulse { Pulse::new(PinState::High, PulseTicks::max()) } fn low() -> Pulse { Pulse::new(PinState::Low, PulseTicks::max()) } fn find_codes(c: &char) -> &'static [Code] { for (found, codes) in CODES.iter() { if found == c { return codes; } } &[] } fn str_pulses(s: &str) -> Vec { let mut pulses = vec![]; for c in s.chars() { for code in find_codes(&c) { code.push_pulse(&mut pulses); } // Create a gap after each symbol. pulses.push(low()); pulses.push(low()); } pulses } const CODES: &[(char, &[Code])] = &[ (' ', &[Code::WordGap]), ('A', &[Code::Dot, Code::Dash]), ('B', &[Code::Dash, Code::Dot, Code::Dot, Code::Dot]), ('C', &[Code::Dash, Code::Dot, Code::Dash, Code::Dot]), ('D', &[Code::Dash, Code::Dot, Code::Dot]), ('E', &[Code::Dot]), ('F', &[Code::Dot, Code::Dot, Code::Dash, Code::Dot]), ('G', &[Code::Dash, Code::Dash, Code::Dot]), ('H', &[Code::Dot, Code::Dot, Code::Dot, Code::Dot]), ('I', &[Code::Dot, Code::Dot]), ('J', &[Code::Dot, Code::Dash, Code::Dash, Code::Dash]), ('K', &[Code::Dash, Code::Dot, Code::Dash]), ('L', &[Code::Dot, Code::Dash, Code::Dot, Code::Dot]), ('M', &[Code::Dash, Code::Dash]), ('N', &[Code::Dash, Code::Dot]), ('O', &[Code::Dash, Code::Dash, Code::Dash]), ('P', &[Code::Dot, Code::Dash, Code::Dash, Code::Dot]), ('Q', &[Code::Dash, Code::Dash, Code::Dot, Code::Dash]), ('R', &[Code::Dot, Code::Dash, Code::Dot]), ('S', &[Code::Dot, Code::Dot, Code::Dot]), ('T', &[Code::Dash]), ('U', &[Code::Dot, Code::Dot, Code::Dash]), ('V', &[Code::Dot, Code::Dot, Code::Dot, Code::Dash]), ('W', &[Code::Dot, Code::Dash, Code::Dash]), ('X', &[Code::Dash, Code::Dot, Code::Dot, Code::Dash]), ('Y', &[Code::Dash, Code::Dot, Code::Dash, Code::Dash]), ('Z', &[Code::Dash, Code::Dash, Code::Dot, Code::Dot]), ]; }