Skip to content

Commit

Permalink
RMT Onewire Peripheral (#454)
Browse files Browse the repository at this point in the history
* commit test 1

test commit 2

next device

asd

* try example, haven't built yet

* Add feature flags for legacy RMT examples

Remove references to the ds18b20 specific device.

Add a feature flag `rmt-legacy` that, when enabled, will build the
original rmt modules from v4 of the esp-idf. When disabled, the v5
rmt interface can be used for one wire applications.

Implement the Iterator trait for a device search and use the Iterator
in the example.

* Add more feature guards to examples compile

* Add alternative main functions for examples

* Use std threading

* Add std to thread sleep

* use std duration

* correct use for legacy rmt component

* Remove local bindings and use the esp-idf-sys master

* fix a typo

* Remove esp-idf component inclusion

* Update gitignore

* Fix examples

* mut-ex out the example compilation

* add implementation for temperature readings

* Conditional compile

* esp4.4 compile

* Changes to cargo and config

* Remove OWDevice and simplify API

* Remove phantomdata that is not required

* adjust lifetimes for single search at a time

* Keep RMT peripheral and add channel to onewire

Moves the RMT driver into a private `driver` module, which will only be compiled in rmt-legacy mode.

Also reduce the number of cfg feature flags in examples by wrapping the implementation in a module.

* modularise examples

* Fix for CI

* update example name to make it pop

---------

Co-authored-by: Dane Slattery <[email protected]>
Co-authored-by: DaneSlattery <[email protected]>
  • Loading branch information
3 people authored Aug 10, 2024
1 parent 098e2df commit aa0e257
Show file tree
Hide file tree
Showing 10 changed files with 1,340 additions and 882 deletions.
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
components_esp32.lock
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,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

0 comments on commit aa0e257

Please sign in to comment.