From ee4726d5a2bb2faf4a471c02e38e8bdd7bc5064b Mon Sep 17 00:00:00 2001 From: PC_WSL Date: Thu, 8 May 2025 15:58:47 +0200 Subject: [PATCH] rewrited buzzer for 2,5kHz Buzzer and use PWM icreased SPI frequency to 3.8MHz --- Cargo.lock | 1 + src/buzzer.rs | 87 +++++++++++++++++++++++++++------------------------ src/color.rs | 6 ++-- src/led.rs | 21 ++++++++----- src/main.rs | 37 +++++++++++++++++----- 5 files changed, 94 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ceedde9..62ab535 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -423,6 +423,7 @@ dependencies = [ "gpio", "log", "regex", + "rgb", "rocket", "rppal", "rust-embed", diff --git a/src/buzzer.rs b/src/buzzer.rs index 5e8e81e..2cdd6ab 100644 --- a/src/buzzer.rs +++ b/src/buzzer.rs @@ -1,56 +1,61 @@ -use rppal::gpio::{Gpio, OutputPin}; -use std::{error::Error, time}; +use rppal::pwm::{Pwm, Channel, Polarity}; use tokio::time::sleep; +use std::{error::Error, time::Duration}; pub struct GPIOBuzzer { - pin: OutputPin, + pwm: Pwm, } impl GPIOBuzzer { - pub fn new(pin_num: u8) -> Result> { - let gpio = Gpio::new()?; - let pin = gpio.get(pin_num)?.into_output(); + /// Create a new GPIOBuzzer instance. + /// 0.5 duty cyle + /// # Arguments + /// * "channel" - PWM channel for buzzer PWM0 = GPIO 12 / PWM1 = GPIO 13 + pub fn new(channel: Channel) -> Result> { + // Enable with dummy values; we'll set frequency/duty in the tone method + let duty_cycle:f64 = 0.5; + let pwm = Pwm::with_frequency(channel, 1000.0, duty_cycle, Polarity::Normal, true)?; + pwm.disable()?; // Start disabled - Ok(GPIOBuzzer { pin }) + Ok(GPIOBuzzer { pwm }) } - /// Emits a sound on a passive buzzer. - async fn modulated_tone(&mut self, carrier_hz: u32, sound_hz: u32, duration_ms: u64) { - let carrier_period = - time::Duration::from_micros((1_000_000.0 / carrier_hz as f64 / 2.0) as u64); - let mod_period = 1_000.0 / sound_hz as f64; // in ms - let total_cycles = duration_ms as f64 / mod_period; - - for _ in 0..total_cycles as u64 { - // Modulation on: Carrier on for mod_period / 2 - let cycles_on = (carrier_hz as f64 * (mod_period / 2.0) / 1000.0) as u64; - for _ in 0..cycles_on { - self.pin.set_high(); - sleep(carrier_period).await; - self.pin.set_low(); - sleep(carrier_period).await; - } - - // Modulation off: Carrier on for mod_period / 2 - let pause = time::Duration::from_millis((mod_period / 2.0) as u64); - sleep(pause).await; - } - } - pub async fn beep_ack(&mut self) { - // carrier = 2300 Hz, sound = 440 Hz, duration = 1 sec - self.modulated_tone(2300, 659, 400).await; - self.modulated_tone(2300, 784,100).await; + /// Play a tone using hardware PWM on supported GPIO pins. + /// + /// # Arguments + /// * `frequency` - Frequency in Hz. + /// * `duration_ms` - Duration in milliseconds. + async fn modulated_tone(&mut self, frequency: f64, duration_ms: u64) -> Result<(), Box> { + self.pwm.set_frequency(frequency, 0.5)?; // 50% duty cycle (square wave) + self.pwm.enable()?; + sleep(Duration::from_millis(duration_ms)).await; + self.pwm.disable()?; + Ok(()) } - pub async fn beep_nak(&mut self) { - // carrier = 2300 Hz, sound = 440 Hz, duration = 1 sec - self.modulated_tone(2300, 659, 400).await; - self.modulated_tone(2300, 523, 100).await; + pub async fn beep_ack(&mut self) -> Result<(), Box>{ + let sleep_ms: u64 = 100; + self.modulated_tone(750.0, 100).await?; + sleep(Duration::from_millis(sleep_ms)).await; + self.modulated_tone(1200.0,100).await?; + sleep(Duration::from_millis(sleep_ms)).await; + self.modulated_tone(2300.0,100).await?; + Ok(()) } - pub async fn beep_unnkown(&mut self) { - self.modulated_tone(2300, 784, 150).await; - self.modulated_tone(2300, 659, 150).await; - self.modulated_tone(2300, 500, 150).await; + pub async fn beep_nak(&mut self) -> Result<(), Box>{ + self.modulated_tone(2300.0,100).await?; + self.modulated_tone(2300.0,100).await?; + Ok(()) + } + + pub async fn beep_unnkown(&mut self) -> Result<(), Box>{ + let sleep_ms: u64 = 100; + self.modulated_tone(2300.0,100).await?; + sleep(Duration::from_millis(sleep_ms)).await; + self.modulated_tone(2300.0,100).await?; + sleep(Duration::from_millis(sleep_ms)).await; + self.modulated_tone(2300.0,100).await?; + Ok(()) } } diff --git a/src/color.rs b/src/color.rs index 4a8fd67..66970d1 100644 --- a/src/color.rs +++ b/src/color.rs @@ -15,9 +15,9 @@ pub enum NamedColor { impl Into> for NamedColor { fn into(self) -> Rgb { match self { - NamedColor::Red => Rgb { r: 255, g: 0, b: 0 }, - NamedColor::Green => Rgb { r: 0, g: 255, b: 0 }, - NamedColor::Blue => Rgb { r: 0, g: 0, b: 255 }, + NamedColor::Red => Rgb { r: 150, g: 0, b: 0 }, + NamedColor::Green => Rgb { r: 0, g: 150, b: 0 }, + NamedColor::Blue => Rgb { r: 0, g: 0, b: 150 }, NamedColor::White => Rgb { r: 255, g: 255, diff --git a/src/led.rs b/src/led.rs index 0ae6def..ace6b34 100644 --- a/src/led.rs +++ b/src/led.rs @@ -1,27 +1,34 @@ +use std::time::Duration; + use rppal::spi::{Bus, Mode, SlaveSelect, Spi}; -use smart_leds::{RGB8, SmartLedsWrite}; +use smart_leds::SmartLedsWrite; +use tokio::time::sleep; use ws2812_spi::Ws2812; +use crate::color::NamedColor; + pub struct Led { controller: Ws2812, } +const STATUS_DURATION: Duration = Duration::from_secs(2); // 2s sleep for all status led signals + impl Led { pub fn new() -> Result> { - let spi = Spi::new(Bus::Spi0, SlaveSelect::Ss0, 3_000_000, Mode::Mode0)?; + let spi = Spi::new(Bus::Spi0, SlaveSelect::Ss0, 3_800_000, Mode::Mode0)?; let controller = Ws2812::new(spi); Ok(Led { controller }) } - pub fn turn_green_on(&mut self) -> Result<(), Box> { - let green = [RGB8 { r: 0, g: 255, b: 0 }]; - self.controller.write(green.iter().cloned())?; + pub async fn turn_green_on_1s(&mut self) -> Result<(), Box> { + self.controller.write(NamedColor::Green.into_iter())?; + sleep(STATUS_DURATION).await; + self.controller.write(NamedColor::Off.into_iter())?; Ok(()) } pub fn turn_off(&mut self) -> Result<(), Box> { - let off = [RGB8 { r: 0, g: 0, b: 0 }]; - self.controller.write(off.iter().cloned())?; + self.controller.write(NamedColor::Off.into_iter())?; Ok(()) } } diff --git a/src/main.rs b/src/main.rs index c1d5f84..68e6fa6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,9 @@ use buzzer::GPIOBuzzer; -use color::NamedColor; use id_store::IDStore; -use log::{LevelFilter, error, info, warn}; +use led::Led; +use log::{LevelFilter, debug, error, info, warn}; use pm3::run_pm3; +use rppal::pwm::Channel; use simplelog::{ConfigBuilder, SimpleLogger}; use std::{env, error::Error, sync::Arc}; use tokio::{ @@ -20,7 +21,7 @@ mod pm3; mod webserver; const STORE_PATH: &str = "./data.json"; -const BUZZER_PIN: u8 = 26; +const PWM_CHANNEL_BUZZER: Channel = Channel::Pwm0; //PWM0 = GPIO18/Physical pin 12 fn setup_logger() { let log_level = env::var("LOG_LEVEL") @@ -70,8 +71,12 @@ async fn main() -> Result<(), Box> { IDStore::new() }; + debug!("created store sucessfully"); + let store: Arc> = Arc::new(Mutex::new(raw_store)); - let gpio_buzzer: Arc> = Arc::new(Mutex::new(buzzer::GPIOBuzzer::new(BUZZER_PIN)?)); + let gpio_buzzer: Arc> = + Arc::new(Mutex::new(GPIOBuzzer::new(PWM_CHANNEL_BUZZER)?)); + let status_led: Arc> = Arc::new(Mutex::new(Led::new()?)); let channel_store = store.clone(); tokio::spawn(async move { @@ -82,13 +87,31 @@ async fn main() -> Result<(), Box> { .add_id(id_store::TallyID(tally_id_string)) { info!("Added new id to current day"); - // led.set_named_color_time(NamedColor::Green, 1); //led is green for 1 sec - gpio_buzzer.lock().await.beep_ack().await; + status_led + .lock() + .await + .turn_green_on_1s() + .await + .unwrap_or_else(|e| { + error!("Failed to blink LED {}", e); + }); + + gpio_buzzer + .lock() + .await + .beep_ack() + .await + .unwrap_or_else(|e| error!("Failed to beep Ack {}", e)); if let Err(e) = channel_store.lock().await.export_json(STORE_PATH).await { error!("Failed to save id store to file: {}", e); // TODO: How to handle a failure to save ? - gpio_buzzer.lock().await.beep_nak().await; + gpio_buzzer + .lock() + .await + .beep_nak() + .await + .unwrap_or_else(|e| error!("Failed to beep Nack {}", e)); } } }