diff --git a/lifeline/.gitignore b/lifeline/.gitignore new file mode 100644 index 000000000..53eaa2196 --- /dev/null +++ b/lifeline/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk diff --git a/lifeline/Cargo.lock b/lifeline/Cargo.lock new file mode 100644 index 000000000..7c458968c --- /dev/null +++ b/lifeline/Cargo.lock @@ -0,0 +1,6 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "lifeline" +version = "0.1.0" + diff --git a/lifeline/Cargo.toml b/lifeline/Cargo.toml new file mode 100644 index 000000000..8bfaed320 --- /dev/null +++ b/lifeline/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "lifeline" +version = "0.1.0" +authors = ["Keagan McClelland "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] \ No newline at end of file diff --git a/lifeline/lifeline.service b/lifeline/lifeline.service new file mode 100644 index 000000000..2d7f28fdb --- /dev/null +++ b/lifeline/lifeline.service @@ -0,0 +1,10 @@ +[Unit] +Description=Boot process for system reset. + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/lifeline +RemainAfterExit=true + +[Install] +WantedBy=multi-user.target diff --git a/lifeline/rustfmt.toml b/lifeline/rustfmt.toml new file mode 100644 index 000000000..866c75610 --- /dev/null +++ b/lifeline/rustfmt.toml @@ -0,0 +1 @@ +max_width = 120 \ No newline at end of file diff --git a/lifeline/src/main.rs b/lifeline/src/main.rs new file mode 100644 index 000000000..c13605f08 --- /dev/null +++ b/lifeline/src/main.rs @@ -0,0 +1,14 @@ +use std::time::Duration; + +mod sound; + +use sound::{notes::*, Song}; + +const SUCCESS_SONG: Song = Song(&[(Some(A_4), Duration::from_millis(100))]); + +fn main() { + std::fs::write("/sys/class/pwm/pwmchip0/export", "0").unwrap(); + let res = SUCCESS_SONG.play(); + std::fs::write("/sys/class/pwm/pwmchip0/unexport", "0").unwrap(); + res.unwrap(); +} diff --git a/lifeline/src/sound.rs b/lifeline/src/sound.rs new file mode 100644 index 000000000..3e106b7d1 --- /dev/null +++ b/lifeline/src/sound.rs @@ -0,0 +1,93 @@ +#![allow(dead_code)] + +use std::io::Error; +use std::time::Duration; + +pub mod notes { + pub const A_4: f64 = 440.0; + pub const B_4: f64 = 493.88; + pub const C_5: f64 = 523.25; + pub const D_5: f64 = 587.33; + pub const E_5: f64 = 659.25; + pub const F_5: f64 = 698.46; + pub const G_5: f64 = 783.99; + pub const A_5: f64 = 880.00; + pub const B_5: f64 = 987.77; + pub const E_6: f64 = 1318.51; +} + +pub fn freq_to_period(freq: f64) -> Duration { + Duration::from_secs(1).div_f64(freq) +} + +pub fn play(freq: f64) -> Result<(), Error> { + // set freq + let period = freq_to_period(freq); + let period_bytes = std::fs::read("/sys/class/pwm/pwmchip0/pwm0/period")?; + if period_bytes == b"0\n" { + std::fs::write("/sys/class/pwm/pwmchip0/pwm0/period", format!("{}", 1000))?; + } + std::fs::write("/sys/class/pwm/pwmchip0/pwm0/duty_cycle", "0")?; + std::fs::write("/sys/class/pwm/pwmchip0/pwm0/period", format!("{}", period.as_nanos()))?; + std::fs::write( + "/sys/class/pwm/pwmchip0/pwm0/duty_cycle", + format!("{}", (period / 2).as_nanos()), + )?; + // enable the thing + std::fs::write("/sys/class/pwm/pwmchip0/pwm0/enable", "1")?; + Ok(()) +} + +pub fn stop() -> Result<(), Error> { + // disable the thing + std::fs::write("/sys/class/pwm/pwmchip0/pwm0/enable", "0")?; + // sleep small amount + std::thread::sleep(Duration::from_micros(30)); + Ok(()) +} + +pub fn play_for_duration(freq: f64, duration: Duration) -> Result<(), Error> { + play(freq)?; + std::thread::sleep(duration); + stop() +} + +#[derive(Clone, Debug)] +pub struct Song<'a>(pub &'a [(Option, Duration)]); +impl<'a> Song<'a> { + pub fn play(&self) -> Result<(), Error> { + for (note, duration) in self.0 { + if let Some(note) = note { + play_for_duration(*note, *duration)?; + } else { + std::thread::sleep(*duration); + } + } + Ok(()) + } +} + +impl Song<'static> { + pub fn play_while T>(&'static self, f: F) -> T { + let run = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true)); + let t_run = run.clone(); + let handle = std::thread::spawn(move || -> Result<(), Error> { + while t_run.load(std::sync::atomic::Ordering::SeqCst) { + self.play()?; + } + Ok(()) + }); + let res = f(); + run.store(false, std::sync::atomic::Ordering::SeqCst); + let e = handle.join().unwrap().err(); + if let Some(e) = e { + eprintln!("ERROR PLAYING SOUND: {}\n{:?}", e, e); + } + res + } +} +impl<'a> From<&'a [(Option, Duration)]> for Song<'a> { + fn from(t: &'a [(Option, Duration)]) -> Self { + Song(t) + } +}