ups-esp32c3-rust/src/beep.rs

125 lines
4.1 KiB
Rust

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<P, T, C> = Channel<C, T, Timer<T>, 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<CHANNEL0, TIMER0, Timer<TIMER0>, Gpio4<Output>>;
pub struct Beep {
watch_timer: Option<EspTimer>,
state: BeepState,
channel: Arc<Mutex<BeepChannel>>,
}
impl Beep {
pub fn new() -> anyhow::Result<Self> {
let pin = unsafe { Gpio4::<Output>::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<Output>,
timer: TIMER0,
channel: CHANNEL0,
frequency: Hertz,
) -> Result<LedcChannel<Gpio4<Output>, 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<BeepChannel>,
) -> 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,
];
}