esp_hal_buzzer/
lib.rs

1//! # Buzzer
2//!
3//! ## Overview
4//! This driver provides an abstraction over LEDC to drive a piezo-electric
5//! buzzer through a user-friendly API.
6//!
7//! The [songs] module contains pre-programmed songs to play through the buzzer.
8//! ## Example
9//!
10//! ```rust,ignore
11//! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
12//!
13//! let mut ledc = Ledc::new(peripherals.LEDC, &clocks);
14//! ledc.set_global_slow_clock(LSGlobalClkSource::APBClk);
15//!
16//! let mut buzzer = Buzzer::new(
17//!     &ledc,
18//!     timer::Number::Timer0,
19//!     channel::Number::Channel1,
20//!     io.pins.gpio6,
21//!     &clocks,
22//! );
23//!
24//! // Play a 1000Hz frequency
25//! buzzer.play(1000).unwrap()
26//! ```
27//!
28//! ## Feature Flags
29#![doc = document_features::document_features!()]
30#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
31#![deny(missing_docs)]
32#![no_std]
33
34use core::fmt::Debug;
35
36use esp_hal::{
37    clock::Clocks,
38    delay::Delay,
39    gpio::{AnyPin, Level, Output, OutputConfig, OutputPin},
40    ledc::{
41        channel::{self, Channel, ChannelIFace},
42        timer::{self, Timer, TimerIFace},
43        Ledc, LowSpeed,
44    },
45    time::Rate,
46};
47
48pub mod notes;
49
50/// Errors from Buzzer
51#[derive(Debug)]
52#[cfg_attr(feature = "defmt", derive(defmt::Format))]
53pub enum Error {
54    /// Errors from [channel::Error]
55    Channel(channel::Error),
56
57    /// Errors from [timer::Error]
58    Timer(timer::Error),
59
60    /// Error when the volume pin isn't set and we try to use it
61    VolumeNotSet,
62
63    /// When the volume level is out of range. Either too low or too high.
64    VolumeOutOfRange,
65}
66
67/// Converts [channel::Error] into [self::Error]
68impl From<channel::Error> for Error {
69    fn from(error: channel::Error) -> Self {
70        Error::Channel(error)
71    }
72}
73
74/// Converts [timer::Error] into [self::Error]
75impl From<timer::Error> for Error {
76    fn from(error: timer::Error) -> Self {
77        Error::Timer(error)
78    }
79}
80
81/// Represents a tone value to play through the buzzer
82pub struct ToneValue {
83    /// Frequency of the tone in Hz
84    /// *Use 0 for a silent tone*
85    pub frequency: u32,
86
87    /// Duration for the frequency in ms
88    pub duration: u32,
89}
90
91/// Represents different volume strategies for the buzzer.
92///
93/// - [VolumeType::OnOff] is a simple on or off volume. It's similar as using
94///   `.mute()` except that the volume control is on a second pin independent of
95///   the buzzer.
96///
97/// - [VolumeType::Duty] uses the duty as the volume control. It acts like a PWM
98///   by switching the power on and off. This may require extra logic gates in
99///   the circuit.
100#[derive(Debug)]
101#[cfg_attr(feature = "defmt", derive(defmt::Format))]
102pub enum VolumeType {
103    /// An On / Off based volume
104    OnOff,
105
106    /// A duty based volume where 0% is the lowest and 100% the highest.
107    Duty,
108}
109
110/// Volume configuration for the buzzer
111struct Volume<'d> {
112    /// Output pin for the volume
113    volume_pin: AnyPin<'d>,
114
115    /// Type of the volume
116    volume_type: VolumeType,
117
118    /// Volume level
119    ///
120    /// For [VolumeType::OnOff], should be 0 for Off, or 1 or more for On
121    /// For [VolumeType::Duty], should be between 0 and 100.
122    level: u8,
123}
124
125/// A buzzer instance driven by Ledc
126pub struct Buzzer<'a> {
127    timer: Timer<'a, LowSpeed>,
128    channel_number: channel::Number,
129    output_pin: AnyPin<'a>,
130    delay: Delay,
131    volume: Option<Volume<'a>>,
132}
133
134impl<'a> Buzzer<'a> {
135    /// Create a new buzzer for the given pin
136    pub fn new(
137        ledc: &'a Ledc,
138        timer_number: timer::Number,
139        channel_number: channel::Number,
140        output_pin: impl OutputPin + 'a,
141    ) -> Self {
142        let timer = ledc.timer(timer_number);
143        Self {
144            timer,
145            channel_number,
146            output_pin: output_pin.degrade(),
147            delay: Delay::new(),
148            volume: None::<Volume>,
149        }
150    }
151
152    /// Add a volume control for the buzzer.
153    pub fn with_volume(mut self, volume_pin: impl OutputPin + 'a, volume_type: VolumeType) -> Self {
154        self.volume = Some(Volume {
155            volume_pin: volume_pin.degrade(),
156            volume_type,
157            level: 50,
158        });
159
160        self
161    }
162
163    /// Set the volume of the buzzer
164    ///
165    /// For [VolumeType::Duty], the level should be between 0 and 100.
166    /// For [VolumeType::OnOff], it will only be mute on 0 and playing on 1 or
167    /// more
168    pub fn set_volume(&mut self, level: u8) -> Result<(), Error> {
169        if let Some(ref mut volume) = self.volume {
170            match volume.volume_type {
171                VolumeType::OnOff => {
172                    // Only turn off when level is set to 0, else set to high
173                    Output::new(
174                        unsafe { volume.volume_pin.clone_unchecked() },
175                        if level != 0 { Level::High } else { Level::Low },
176                        OutputConfig::default(),
177                    );
178                    Ok(())
179                }
180                VolumeType::Duty => {
181                    match level {
182                        0..=99 => {
183                            volume.level = level;
184
185                            // Put a dummy config in the timer if it's not already configured
186                            if !self.timer.is_configured() {
187                                self.timer.configure(timer::config::Config {
188                                    duty: timer::config::Duty::Duty11Bit,
189                                    clock_source: timer::LSClockSource::APBClk,
190                                    frequency: Rate::from_hz(20_000),
191                                })?;
192                            }
193
194                            let mut channel = Channel::new(self.channel_number, unsafe {
195                                volume.volume_pin.clone_unchecked()
196                            });
197                            channel
198                                .configure(channel::config::Config {
199                                    timer: &self.timer,
200                                    duty_pct: level,
201                                    pin_config: channel::config::PinConfig::PushPull,
202                                })
203                                .map_err(|e| e.into())
204                        }
205                        100 => {
206                            // If level is 100, we just keep the pin high
207                            Output::new(
208                                unsafe { volume.volume_pin.clone_unchecked() },
209                                Level::High,
210                                OutputConfig::default(),
211                            );
212                            Ok(())
213                        }
214                        _ => Err(Error::VolumeOutOfRange),
215                    }
216                }
217            }
218        } else {
219            Err(Error::VolumeNotSet)
220        }
221    }
222
223    /// Mute the buzzer
224    ///
225    /// The muting is done by simply setting the duty to 0
226    pub fn mute(&mut self) -> Result<(), Error> {
227        let mut channel = Channel::new(self.channel_number, unsafe {
228            self.output_pin.clone_unchecked()
229        });
230        channel
231            .configure(channel::config::Config {
232                timer: &self.timer,
233                duty_pct: 0,
234                pin_config: channel::config::PinConfig::PushPull,
235            })
236            .map_err(|e| e.into())
237    }
238
239    /// Play a frequency through the buzzer
240    pub fn play(&mut self, frequency: u32) -> Result<(), Error> {
241        // Mute if frequency is 0Hz
242        if frequency == 0 {
243            return self.mute();
244        }
245
246        // Max duty resolution for a frequency:
247        // Integer(log2(LEDC_APB_CKL / frequency))
248        let mut result = 0;
249        let mut value = Clocks::get().apb_clock / Rate::from_hz(frequency);
250
251        // Limit duty resolution to 14 bits
252        while value > 1 && result < 14 {
253            value >>= 1;
254            result += 1;
255        }
256
257        self.timer.configure(timer::config::Config {
258            // Safety: This should never fail because resolution is limited to 14 bits
259            duty: timer::config::Duty::try_from(result).unwrap(),
260            clock_source: timer::LSClockSource::APBClk,
261            frequency: Rate::from_hz(frequency),
262        })?;
263
264        let mut channel = Channel::new(self.channel_number, unsafe {
265            self.output_pin.clone_unchecked()
266        });
267        channel.configure(channel::config::Config {
268            timer: &self.timer,
269            // Use volume as duty if set since we use the same channel.
270            duty_pct: self.volume.as_ref().map_or(50, |v| v.level),
271            pin_config: channel::config::PinConfig::PushPull,
272        })?;
273
274        Ok(())
275    }
276
277    /// Play a sound sequence through the buzzer
278    ///
279    /// Uses a pair of frequencies and timings to play a sound sequence.
280    ///
281    /// # Arguments
282    /// * `sequence` - A list of frequencies to play through the buzzer
283    /// * `timings` - A list of timings in ms for each frequencies
284    ///
285    /// # Examples
286    /// Play a single beep at 300Hz for 1 second
287    /// ```
288    /// buzzer.play_tones([300], [1000]);
289    /// ```
290    ///
291    /// Play a sequence of 3 beeps with a break inbetween
292    /// ```
293    /// buzzer.play_tones([200, 0, 200, 0, 200], [200, 50, 200, 50, 200]);
294    /// ```
295    ///
296    /// Play a sequence of 3 beeps with the same duration
297    /// ```
298    /// buzzer.play_tones([100, 200, 300], [100; 3]);
299    /// ```
300    ///
301    /// # Errors
302    /// This function returns an [Error] in case of an error.
303    /// An error can occur when an invalid value is used as a tone
304    pub fn play_tones<const T: usize>(
305        &mut self,
306        sequence: [u32; T],
307        timings: [u32; T],
308    ) -> Result<(), Error> {
309        // Iterate for each frequency / timing pair
310        for (frequency, timing) in sequence.iter().zip(timings.iter()) {
311            self.play(*frequency)?;
312            self.delay.delay_millis(*timing);
313            self.mute()?;
314        }
315        // Mute at the end of the sequence
316        self.mute()
317    }
318
319    /// Play a tone sequence through the buzzer
320    ///
321    /// Uses a pair of frequencies and timings to play a sound sequence.
322    ///
323    /// # Arguments
324    /// * `tones` - A list of type [ToneValue] to play through the buzzer
325    ///
326    /// # Examples
327    /// Play a tone sequence
328    /// ```
329    /// let song = [
330    ///     ToneValue {
331    ///         frequency: 100,
332    ///         duration: 100,
333    ///     },
334    ///     ToneValue {
335    ///         frequency: 200,
336    ///         duration: 100,
337    ///     },
338    ///     ToneValue {
339    ///         frequency: 300,
340    ///         duration: 100,
341    ///     },
342    /// ];
343    /// buzzer.play_song(song);
344    /// ```
345    ///
346    /// # Errors
347    /// This function returns an [Error] in case of an error.
348    /// An error can occur when an invalid value is used as a tone
349    pub fn play_song<const T: usize>(&mut self, tones: [ToneValue; T]) -> Result<(), Error> {
350        let mut sequence: [u32; T] = [0; T];
351        let mut timings: [u32; T] = [0; T];
352        for (index, tone) in tones.iter().enumerate() {
353            sequence[index] = tone.frequency;
354            timings[index] = tone.duration;
355        }
356        self.play_tones(sequence, timings)
357    }
358}