esp-idf-hal/examples/rmt_morse_code.rs
gak 2b6fe48fa0
WIP: RMT (tx) (#44)
* WIP: Initial tx rmt implementation

* Rename Bulder to Config to line up with other mods

* WIP Potential traits for a "pulses" mod in embedded-hal

* WIP: Initial tx rmt implementation

* Rename Bulder to Config to line up with other mods

* WIP Potential traits for a "pulses" mod in embedded-hal

* Copied example code from ledc fork. Semi-working example.

* Rename level to pulse. dev-dep versions.

* WIP RMT, Examples

* WIP RMT Fix neopixel example

* WIP RMT morse code work

* WIP RMT fixed morse example

* WIP RMT more config, docs.

* WIP RMT fix compilation

* WIP RMT Fix doc comment in example.

* WIP RMT: notes on examples

* WIP RMT Group up same level config items.

* WIP RMT Change half_inserted to an Option

* WIP RMT Don't need to return Result in clear()

* WIP RMT Config refactor. Into for enum.

* Result instead of Option in PulseTick

* Don't use floats for ticks calcs

* WIP Duration for ticks

* Cleanups.

* Moved Duration logic into PulseDuration.

* Remove unused tick_hz field.

* Cleanups.

* WIP playing around with generics

* Refactored out Vec storage out of Writer

* WIP Heap and Stack implementations for data

* WIP about to nuke tricky const code

* Cleanups

* Own PinState. start/start_blocking.

* Wrap duty percent with range safety.

* Use units::Hertz

* Use Hertz in ticks_hz

* Stop transmitting implementation

* Fix morse example

* rmt peripheral for channels (4 for now)

* Small tweaks

* Morse example to demo more functionality

* WIP Fixes and testing functionality

* Cleanups and notes

* Assign devices to number of channels. Cleanups.

* Sprinkled TODO's around for docs/fixes

* Longer messages don't repeat so remove it for now

* Rename data to signal because data is a too generic term

* More docs

* Don't mention receiving in description.

* More docs

* Rename `add` to `push` like Vec. More docs.

* Rename `Writer` to `Transmit` to line up with rmt docs.

* Accidentally had idle_level in carrier config.

* More docs and cleanups. std compatibility.

* Remove chip::HwChannel import because of a * import later.

* Separate duration to ticks calc. Music example.

* Added comment on example.

* clippy: ok_or_else, Default.

* fixes: mut self, allows/features, alloc, &Pulse.

* only need feature alloc for the use warning.

* rename Signal implementations

* Alloc, morse example fix.

* Didn't need that map. iter does the ref.

* Clippy repairs. println! instead of info!
2022-03-02 19:11:30 +02:00

162 lines
5.2 KiB
Rust

//! 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.
//! * Stopping/releasing a [`Pin`] and [`Channel`], to be used again.
//!
use embedded_hal::delay::blocking::DelayUs;
use embedded_hal::digital::blocking::InputPin;
use esp_idf_hal::delay::Ets;
use esp_idf_hal::gpio::{Gpio16, Gpio17, Input, Output, Pin};
use esp_idf_hal::peripherals::Peripherals;
use esp_idf_hal::rmt::config::{CarrierConfig, DutyPercent, Loop, TransmitConfig};
use esp_idf_hal::rmt::VariableLengthSignal; // This example requires `alloc` feature.
use esp_idf_hal::rmt::{PinState, Pulse, PulseTicks, Transmit, CHANNEL0};
use esp_idf_hal::units::FromValueType;
fn main() -> anyhow::Result<()> {
esp_idf_sys::link_patches();
let peripherals = Peripherals::take().unwrap();
let led: Gpio17<Output> = peripherals.pins.gpio17.into_output()?;
let stop: Gpio16<Input> = peripherals.pins.gpio16.into_input()?;
let channel = peripherals.rmt.channel0;
let carrier = CarrierConfig::new()
.duty_percent(DutyPercent::new(50)?)
.frequency(611.Hz());
let mut config = TransmitConfig::new()
.carrier(Some(carrier))
.looping(Loop::Count(100))
.clock_divider(255);
let tx = send_morse_code(&config, led, channel, "HELLO ")?;
println!("Keep sending until pin {} is set low.", stop.pin());
while stop.is_high()? {
Ets.delay_ms(100)?;
}
println!("Pin {} is set to low--stopping message.", stop.pin());
// Release pin and channel so we can use them again.
let (led, channel) = tx.release()?;
// 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(&config, led, channel, "GOODBYE")?;
Ok(())
}
fn send_morse_code(
config: &TransmitConfig,
led: Gpio17<Output>,
channel: CHANNEL0,
message: &str,
) -> anyhow::Result<Transmit<Gpio17<Output>, CHANNEL0>> {
println!("Sending morse message '{}' to pin {}.", message, led.pin());
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 = Transmit::new(led, channel, config)?;
tx.start(signal)?;
// Return `tx` so we can release the pin and channel later.
Ok(tx)
}
fn high() -> Pulse {
Pulse::new(PinState::High, PulseTicks::max())
}
fn low() -> Pulse {
Pulse::new(PinState::Low, PulseTicks::max())
}
enum Code {
Dot,
Dash,
WordGap,
}
impl Code {
pub fn push_pulse(&self, pulses: &mut Vec<Pulse>) {
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 find_codes(c: &char) -> &'static [Code] {
for (found, codes) in CODES.iter() {
if found == c {
return codes;
}
}
&[]
}
fn str_pulses(s: &str) -> Vec<Pulse> {
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]),
];