Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RMT Onewire Peripheral #454

Merged
merged 26 commits into from
Aug 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
**/*.rs.bk
ulp/ulp_start.o
install-rust-toolchain.sh
/.devcontainer
ivmarkov marked this conversation as resolved.
Show resolved Hide resolved
components_esp32.lock
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ embassy-sync = [] # For now, the dependecy on the `embassy-sync` crate is non-op
# - When not enabled (default) the code for the new ADC oneshot driver will be compiled;
# - Since we don't wrap the legacy _continuous_ ADC driver, the new _continuous_ ADC driver is always compiled.
adc-oneshot-legacy = []
# Similar to adc-oneshot-legacy
# - When enabled (default), the code for the legacy RMT TX/RX driver will be compiled.
# - When disabled the code for the new onewire RMT driver will be compiled.
rmt-legacy = []
# Propagated esp-idf-sys features
native = ["esp-idf-sys/native"]
pio = ["esp-idf-sys/pio"]
Expand Down
272 changes: 148 additions & 124 deletions examples/rmt_morse_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,150 +14,174 @@
//! * Background sending.
//! * Taking a [`Pin`] and [`Channel`] by ref mut, so that they can be used again later.
//!
use esp_idf_hal::delay::Ets;
use esp_idf_hal::gpio::*;
use esp_idf_hal::peripheral::*;
use esp_idf_hal::peripherals::Peripherals;
use esp_idf_hal::rmt::config::{CarrierConfig, DutyPercent, Loop, TransmitConfig};
use esp_idf_hal::rmt::*;
use esp_idf_hal::units::FromValueType;

#[cfg(any(feature = "rmt-legacy", esp_idf_version_major = "4"))]
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(&mut channel, &mut led, &config, "HELLO ")?;

let stop = PinDriver::input(stop)?;
example::main()
}

println!("Keep sending until pin {} is set low.", stop.pin());
#[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");

while stop.is_high() {
Ets::delay_ms(100);
loop {
std::thread::sleep(std::time::Duration::from_millis(1000));
}
}

println!("Pin {} set to low. Stopped.", stop.pin());

// Release pin and channel so we can use them again.
drop(tx);
#[cfg(any(feature = "rmt-legacy", esp_idf_version_major = "4"))]
mod example {
use esp_idf_hal::units::FromValueType;
use esp_idf_hal::{
delay::Ets,
gpio::{OutputPin, PinDriver},
peripheral::Peripheral,
prelude::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(&mut channel, &mut led, &config, "HELLO ")?;

let stop = PinDriver::input(stop)?;

println!("Keep sending until pin {} is set low.", stop.pin());

while stop.is_high() {
Ets::delay_ms(100);
}

// Wait so the messages don't get garbled.
Ets::delay_ms(3000);
println!("Pin {} set to low. Stopped.", stop.pin());

// Now send a single message and stop.
println!("Saying GOODBYE!");
config.looping = Loop::None;
send_morse_code(channel, led, &config, "GOODBYE")?;
// Release pin and channel so we can use them again.
drop(tx);

Ok(())
}
// Wait so the messages don't get garbled.
Ets::delay_ms(3000);

fn send_morse_code<'d>(
channel: impl Peripheral<P = impl RmtChannel> + 'd,
led: impl Peripheral<P = impl OutputPin> + 'd,
config: &TransmitConfig,
message: &str,
) -> anyhow::Result<TxRmtDriver<'d>> {
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)
}
// Now send a single message and stop.
println!("Saying GOODBYE!");
config.looping = Loop::None;
send_morse_code(channel, led, &config, "GOODBYE")?;

fn high() -> Pulse {
Pulse::new(PinState::High, PulseTicks::max())
}
Ok(())
}

fn low() -> Pulse {
Pulse::new(PinState::Low, PulseTicks::max())
}
fn send_morse_code<'d>(
channel: impl Peripheral<P = impl RmtChannel> + 'd,
led: impl Peripheral<P = impl OutputPin> + 'd,
config: &TransmitConfig,
message: &str,
) -> anyhow::Result<TxRmtDriver<'d>> {
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,
}
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()]),
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 high() -> Pulse {
Pulse::new(PinState::High, PulseTicks::max())
}
&[]
}

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);
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<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());
// Create a gap after each symbol.
pulses.push(low());
pulses.push(low());
}
pulses
}
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]),
];
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]),
];
}
Loading
Loading