#[macro_use] extern crate chan; extern crate chan_signal; use std::io::prelude::*; use std::fs::File; use std::thread; use std::thread::JoinHandle; use std::path::Path; use std::time::Duration; use chan_signal::Signal; static GPIO_PORT_BTN: &'static str = "17"; static GPIO_PORT_LED: &'static str = "18"; static GPIO_DIRECTION_IN: &'static str = "in"; static GPIO_DIRECTION_OUT: &'static str = "out"; static GPIO_BTN_ON: &'static str = "0"; //static GPIO_BTN_OFF: &'static str = "1"; static GPIO_LED_ON: &'static str = "0"; static GPIO_LED_OFF: &'static str = "1"; fn write(path: &str, value: &str) { let mut file = File::create(Path::new(path)) .expect(format!("Open file '{}' failed", path).as_str()); file.write_all(value.as_bytes()) .expect(format!("Write value '{}' to '{}' file failed", value, path).as_str()); #[cfg(debug_assertions)] println!("Wrote value '{}' to '{}'.", value, path); } fn read(path: &str) -> String { let mut file = File::open(Path::new(path)) .expect(format!("Open file '{}' failed", path).as_str()); let mut contents = String::new(); file.read_to_string(&mut contents) .expect(format!("Read from '{}' file failed", path).as_str()); #[cfg(debug_assertions)] println!("Read value '{}' from '{}'.", contents, path); contents } fn export(port: &str) { write("/sys/class/gpio/export", port) } fn unexport(port: &str) { write("/sys/class/gpio/unexport", port) } fn set_direction(port: &str, direction: &str) { write(format!("/sys/class/gpio/gpio{}/direction", port).as_str(), direction); } fn set_value(port: &str, value: &str) { write(format!("/sys/class/gpio/gpio{}/value", port).as_str(), value); } fn get_value(port: &str) -> String { String::from(read(format!("/sys/class/gpio/gpio{}/value", port).as_str()).trim()) } fn main() { let signal = chan_signal::notify(&[Signal::INT, Signal::TERM]); let (sdone, rdone) = chan::sync(0); let (spanic, rpanic) = chan::sync(0); let worker = thread::Builder::new() .name("worker".to_string()) .spawn(move || run(rdone)) .expect("Create worker thread failed"); let watchdog = thread::Builder::new() .name("watchdog".to_string()) .spawn(move || watchdog(worker, spanic)) .expect("Create watchdog thread failed"); chan_select! { rpanic.recv() => { }, signal.recv() -> signal => { #[cfg(debug_assertions)] println!("received signal: {:?}", signal); sdone.send(()); } } watchdog.join().expect("Watchdog thread paniced"); } fn watchdog(thread: JoinHandle<()>, _spanic: chan::Sender<()>) { thread.join().expect("Thread paniced"); } fn run(rdone: chan::Receiver<()>) { // init export(GPIO_PORT_BTN); export(GPIO_PORT_LED); set_direction(GPIO_PORT_BTN, GPIO_DIRECTION_IN); set_direction(GPIO_PORT_LED, GPIO_DIRECTION_OUT); let tick = chan::tick(Duration::from_millis(1000 / 5)); let mut btn_last = false; let mut led_on = false; let mut enabled = true; loop { chan_select! { tick.recv() => { #[cfg(debug_assertions)] println!("tick"); // switch enabled if button state changed let btn = get_value(GPIO_PORT_BTN) == GPIO_BTN_ON; if btn && btn != btn_last { enabled = !enabled; } // set led state if led_on { set_value(GPIO_PORT_LED, GPIO_LED_ON); } else { set_value(GPIO_PORT_LED, GPIO_LED_OFF); } btn_last = btn; led_on = !led_on; }, rdone.recv() => { // unexport unexport(GPIO_PORT_BTN); unexport(GPIO_PORT_LED); return; }, } } }