use embedded_svc::timer::{PeriodicTimer, TimerService}; use esp_idf_hal::gpio::{Gpio4, Output}; use esp_idf_hal::ledc::{config::TimerConfig, Channel, Timer}; use esp_idf_hal::ledc::{CHANNEL0, TIMER0}; use esp_idf_hal::prelude::*; use esp_idf_svc::timer::{EspTimer, EspTimerService}; use esp_idf_sys::{ledc_mode_t_LEDC_LOW_SPEED_MODE, ledc_set_freq, EspError}; use log::{info, warn}; use std::sync::{Arc, Mutex, MutexGuard}; use std::time::Duration; type LedcChannel = Channel, P>; #[derive(Clone, Copy, Debug)] struct BeepState { beat: u8, ringtone: ringtone::Type, } impl BeepState { pub fn new() -> Self { Self { beat: 0, ringtone: ringtone::SILENCE, } } pub fn from_ringtone(ringtone: ringtone::Type) -> Self { Self { beat: 0, ringtone } } } type BeepChannel = Channel, Gpio4>; pub struct Beep { watch_timer: Option, state: BeepState, channel: Arc>, } impl Beep { pub fn new() -> anyhow::Result { let pin = unsafe { Gpio4::::new() } .into_output() .map_err(|err| anyhow::anyhow!("Failed to set GPIO 4 as ledc output. {}", err))?; let hw_timer = unsafe { TIMER0::new() }; let hw_channel = unsafe { CHANNEL0::new() }; let channel = Self::init_channel(pin, hw_timer, hw_channel, 1000.Hz().into()) .map_err(|err| anyhow::anyhow!("Failed to initialize channel. {}", err))?; return anyhow::Ok(Beep { watch_timer: None, state: BeepState::new(), channel: Arc::new(Mutex::new(channel)), }); } fn init_channel( pin: Gpio4, timer: TIMER0, channel: CHANNEL0, frequency: Hertz, ) -> Result, TIMER0, CHANNEL0>, EspError> { let config = TimerConfig::default().frequency(frequency); let timer = Timer::new(timer, &config)?; let channel = Channel::new(channel, timer, pin)?; return Ok(channel); } pub fn play(&mut self, ringtone: ringtone::Type) -> anyhow::Result<()> { if self.state.ringtone != ringtone { self.state = BeepState::from_ringtone(ringtone); info!("change: {:?}", ringtone); } else { return Ok(()); } let state = Arc::new(Mutex::new(self.state)); let channel = self.channel.to_owned(); let mut timer = EspTimerService::new()? .timer(move || match state.lock().as_mut() { Ok(state) => { if let Err(err) = Self::play_once(state, channel.lock().unwrap()) { warn!("{}", err); } } Err(err) => { warn!("Failed to lock state. {}", err); } }) .map_err(|err| anyhow::anyhow!("Init Timer Failed. {}", err))?; timer.every(Duration::from_millis(250))?; self.watch_timer = Some(timer); return anyhow::Ok(()); } fn play_once( state: &mut BeepState, mut channel: MutexGuard, ) -> anyhow::Result<()> { if state.ringtone.len() <= state.beat as usize { state.beat = 0; } let curr = state.ringtone[state.beat as usize]; if curr == 0 { channel.set_duty(0).expect("Failed to set duty"); } else { unsafe { ledc_set_freq(ledc_mode_t_LEDC_LOW_SPEED_MODE, 0, curr); } channel.set_duty(60).expect("Failed to set duty"); } state.beat += 1; if state.beat == 16 { state.beat = 0; } return anyhow::Ok(()); } } pub mod ringtone { pub type Type = [u32; 16]; pub const ADAPTER_DOWN: Type = [2300, 2000, 2250, 1950, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; pub const BATTERY_LOW: Type = [ 2000, 1950, 0, 0, 1980, 1900, 0, 0, 2000, 1900, 0, 0, 1980, 1900, 0, 0, ]; pub const SILENCE: Type = [0; 16]; pub const SHUTDOWN: Type = [ 3450, 3500, 0, 3500, 3050, 3000, 0, 3000, 3050, 1000, 1000, 1000, 0, 0, 0, 0, ]; }