mirror of
https://github.com/esp-rs/esp-idf-hal.git
synced 2025-09-27 12:21:02 +00:00
234 lines
6.5 KiB
Rust
234 lines
6.5 KiB
Rust
//! Play a song using a piezo buzzer.
|
|
//!
|
|
//! Should play "Ode to Joy" on pin 17.
|
|
//!
|
|
//! Based off the ESP-IDF rmt musical buzzer example:
|
|
//! https://github.com/espressif/esp-idf/blob/b092fa073047c957545a0ae9504f04972a8c6d74/examples/peripherals/rmt/musical_buzzer/main/musical_buzzer_example_main.c
|
|
|
|
#![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 std::time::Duration;
|
|
|
|
use esp_idf_hal::delay::Ets;
|
|
use esp_idf_hal::{
|
|
prelude::Peripherals,
|
|
rmt::{self, config::TransmitConfig, TxRmtDriver},
|
|
units::Hertz,
|
|
};
|
|
use notes::*;
|
|
|
|
pub fn main() -> anyhow::Result<()> {
|
|
esp_idf_hal::sys::link_patches();
|
|
|
|
let peripherals = Peripherals::take()?;
|
|
let led = peripherals.pins.gpio17;
|
|
let channel = peripherals.rmt.channel0;
|
|
let config = TransmitConfig::new();
|
|
let mut tx: TxRmtDriver<'static> = TxRmtDriver::new(channel, led, &config)?;
|
|
|
|
loop {
|
|
play_song(&mut tx, ODE_TO_JOY)?;
|
|
Ets::delay_ms(3000);
|
|
}
|
|
}
|
|
|
|
pub fn play_song(tx: &mut TxRmtDriver<'static>, song: &[NoteValue]) -> anyhow::Result<()> {
|
|
for note_value in song {
|
|
note_value.play(tx)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub struct NoteValueIter {
|
|
ticks: rmt::PulseTicks,
|
|
tone_cycles: u32,
|
|
pause_cycles: u32,
|
|
}
|
|
|
|
impl NoteValueIter {
|
|
fn new(ticks_per_sec: Hertz, note: &NoteValue) -> Self {
|
|
// Calculate the frequency for a piezo buzzer.
|
|
let dur_ms = note.duration.as_millis();
|
|
let cycles_per_sec = note.note.0; // pitch
|
|
let ticks_per_cycle = ticks_per_sec.0 as u128 / cycles_per_sec as u128;
|
|
let ticks_per_half = (ticks_per_cycle / 2_u128) as u16;
|
|
let ticks = rmt::PulseTicks::new(ticks_per_half).unwrap();
|
|
|
|
let total_cycles = (cycles_per_sec as u128 * dur_ms / 1000_u128) as u32;
|
|
// Pause for the last 40ms of every note
|
|
let pause_cycles = (cycles_per_sec as u128 * 40_u128 / 1000_u128) as u32;
|
|
let tone_cycles = total_cycles - pause_cycles;
|
|
|
|
Self {
|
|
ticks,
|
|
tone_cycles,
|
|
pause_cycles,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::iter::Iterator for NoteValueIter {
|
|
type Item = rmt::Symbol;
|
|
|
|
// runs in ISR
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if self.tone_cycles + self.pause_cycles > 0 {
|
|
let high_state = if self.tone_cycles > 0 {
|
|
self.tone_cycles -= 1;
|
|
rmt::PinState::High
|
|
} else {
|
|
self.pause_cycles -= 1;
|
|
rmt::PinState::Low
|
|
};
|
|
let level0 = rmt::Pulse::new(high_state, self.ticks);
|
|
let level1 = rmt::Pulse::new(rmt::PinState::Low, self.ticks);
|
|
Some(rmt::Symbol::new(level0, level1))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Note(u16);
|
|
|
|
#[allow(dead_code)]
|
|
pub mod notes {
|
|
use super::Note;
|
|
|
|
pub const A4: Note = Note(440);
|
|
pub const AS4: Note = Note(466);
|
|
pub const B4: Note = Note(494);
|
|
pub const C5: Note = Note(523);
|
|
pub const CS5: Note = Note(554);
|
|
pub const D5: Note = Note(587);
|
|
pub const DS5: Note = Note(622);
|
|
pub const E5: Note = Note(659);
|
|
pub const F5: Note = Note(698);
|
|
pub const FS5: Note = Note(740);
|
|
pub const G5: Note = Note(784);
|
|
pub const GS5: Note = Note(831);
|
|
pub const A5: Note = Note(880);
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct NoteValue {
|
|
note: Note,
|
|
duration: Duration,
|
|
}
|
|
|
|
impl NoteValue {
|
|
pub fn play(&self, tx: &mut TxRmtDriver<'static>) -> anyhow::Result<()> {
|
|
let ticks_hz = tx.counter_clock()?;
|
|
tx.start_iter_blocking(self.iter(ticks_hz))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn iter(&self, ticks_hz: Hertz) -> NoteValueIter {
|
|
NoteValueIter::new(ticks_hz, self)
|
|
}
|
|
}
|
|
|
|
macro_rules! n {
|
|
($n: expr, $duration: expr) => {
|
|
NoteValue {
|
|
note: $n,
|
|
duration: Duration::from_millis($duration),
|
|
}
|
|
};
|
|
}
|
|
|
|
const ODE_TO_JOY: &[NoteValue] = &[
|
|
n!(FS5, 400),
|
|
n!(FS5, 600),
|
|
n!(G5, 400),
|
|
n!(A5, 400),
|
|
n!(A5, 400),
|
|
n!(G5, 400),
|
|
n!(FS5, 400),
|
|
n!(E5, 400),
|
|
n!(D5, 400),
|
|
n!(D5, 400),
|
|
n!(E5, 400),
|
|
n!(FS5, 400),
|
|
n!(FS5, 400),
|
|
n!(FS5, 200),
|
|
n!(E5, 200),
|
|
n!(E5, 800),
|
|
n!(FS5, 400),
|
|
n!(FS5, 600),
|
|
n!(G5, 400),
|
|
n!(A5, 400),
|
|
n!(A5, 400),
|
|
n!(G5, 400),
|
|
n!(FS5, 400),
|
|
n!(E5, 400),
|
|
n!(D5, 400),
|
|
n!(D5, 400),
|
|
n!(E5, 400),
|
|
n!(FS5, 400),
|
|
n!(E5, 400),
|
|
n!(E5, 200),
|
|
n!(D5, 200),
|
|
n!(D5, 800),
|
|
n!(E5, 400),
|
|
n!(E5, 400),
|
|
n!(FS5, 400),
|
|
n!(D5, 400),
|
|
n!(E5, 400),
|
|
n!(FS5, 200),
|
|
n!(G5, 200),
|
|
n!(FS5, 400),
|
|
n!(D5, 400),
|
|
n!(E5, 400),
|
|
n!(FS5, 200),
|
|
n!(G5, 200),
|
|
n!(FS5, 400),
|
|
n!(E5, 400),
|
|
n!(D5, 400),
|
|
n!(E5, 400),
|
|
n!(A4, 400),
|
|
n!(A4, 400),
|
|
n!(FS5, 400),
|
|
n!(FS5, 600),
|
|
n!(G5, 400),
|
|
n!(A5, 400),
|
|
n!(A5, 400),
|
|
n!(G5, 400),
|
|
n!(FS5, 400),
|
|
n!(E5, 400),
|
|
n!(D5, 400),
|
|
n!(D5, 400),
|
|
n!(E5, 400),
|
|
n!(FS5, 400),
|
|
n!(E5, 400),
|
|
n!(E5, 200),
|
|
n!(D5, 200),
|
|
n!(D5, 800),
|
|
];
|
|
}
|
|
|
|
// #[cfg(any(feature = "rmt-legacy", esp_idf_version_major = "4"))]
|
|
// #[cfg(any(feature = "rmt-legacy", esp_idf_version_major = "4"))]
|
|
// #[cfg(any(feature = "rmt-legacy", esp_idf_version_major = "4"))]
|
|
// #[cfg(any(feature = "rmt-legacy", esp_idf_version_major = "4"))]
|
|
// #[allow(dead_code)]
|
|
// #[cfg(any(feature = "rmt-legacy", esp_idf_version_major = "4"))]
|