use std::fmt;
use std::thread;
use std::time::Duration;
use core::fmt::Debug;
use rppal::gpio::{Gpio, OutputPin};
pub const GPIO_BUZZER: u8 = 13;
#[derive(Debug)]
pub struct Buzzer {
pin: Option<Box<OutputPin>>,
simulation: bool,
is_setup: bool,
}
impl Buzzer {
pub fn new() -> Result<Buzzer, Error> {
Ok(Self {
pin: None,
simulation: false,
is_setup: false,
})
}
pub fn setup(&mut self) -> Result <(), Error> {
if !self.is_setup {
if !self.simulation {
let gpio = Gpio::new()?;
let output = gpio.get(GPIO_BUZZER)?.into_output();
self.pin = Some(Box::new(output));
}
self.is_setup = true;
}
Ok(())
}
pub fn note(&mut self, frequency : f64, duration: f64) -> Result<(), Error>{
assert!(frequency > 0.0);
if !self.is_setup {
let _result = self.setup();
}
if !self.simulation {
let pin = self.pin.as_deref_mut().unwrap();
pin.set_pwm_frequency(frequency, 0.90)?;
thread::sleep(Duration::from_millis((duration * 1000.0) as u64));
pin.clear_pwm()?;
}
Ok(())
}
pub fn midi_note(&mut self, note_number : u32, duration: f64) -> Result <(), Error>{
assert!(note_number > 0);
let freq = Buzzer::midi_note_to_frequency(note_number);
self.note(freq, duration)?;
Ok(())
}
fn midi_note_to_frequency(note_number : u32) -> f64 {
assert!(note_number > 0);
let base: f64 = 2.0;
base.powf((note_number as f64 - 69.0) / 12.0) * 440.0
}
pub fn stop(&mut self) -> Result <(), Error>{
if !self.is_setup {
let _result = self.setup();
}
if !self.simulation {
let pin = self.pin.as_deref_mut().unwrap();
pin.clear_pwm()?;
}
Ok(())
}
}
#[derive(Debug)]
pub enum Error {
Gpio(rppal::gpio::Error),
}
impl std::error::Error for Error {}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &*self {
Error::Gpio(err) => write!(f, "Gpio error: {}", &err),
}
}
}
impl From<rppal::gpio::Error> for Error {
fn from(err: rppal::gpio::Error) -> Error {
Error::Gpio(err)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_buzzer_setup() -> Result<(), Error> {
let mut buzzer = Buzzer::new()?;
buzzer.simulation = true;
assert!(buzzer.is_setup == false);
let _result = buzzer.setup();
assert!(buzzer.is_setup == true);
Ok(())
}
#[test]
fn test_buzzer_midi_note_to_frequency() -> Result<(), Error> {
let tests: [(u32, f64); 5] = [(11, 15.434),(21, 27.5),(40, 82.407),(57, 220.0),(112, 5274.0)];
for i in 0..tests.len() {
let (note, expected_freq) = tests[i];
let computed_frq = Buzzer::midi_note_to_frequency(note);
assert!((expected_freq >= computed_frq - 0.1) && (expected_freq <= computed_frq + 0.1));
}
Ok(())
}
#[test]
fn test_buzzer_note() -> Result<(), Error> {
let mut buzzer = Buzzer::new()?;
buzzer.simulation = true;
buzzer.note(493.0, 0.5)?;
assert!(buzzer.is_setup == true);
Ok(())
}
#[test]
#[should_panic]
fn test_buzzer_note_invalid() {
let mut buzzer = Buzzer::new().unwrap();
buzzer.simulation = true;
let _result = buzzer.note(-1.0, 0.5);
}
#[test]
fn test_buzzer_midi_note() -> Result<(), Error> {
let mut buzzer = Buzzer::new()?;
buzzer.simulation = true;
buzzer.midi_note(71, 0.5)?;
assert!(buzzer.is_setup == true);
Ok(())
}
#[test]
#[should_panic]
fn test_buzzer_midi_note_invalid() {
let mut buzzer = Buzzer::new().unwrap();
buzzer.simulation = true;
let _result = buzzer.midi_note(0, 0.5);
}
}