use futures::channel::mpsc;
use futures::stream::Stream;
use rppal::gpio::{self, Gpio, Level};
use std::time::Duration;
use std::thread;
/// Is the button pressed (Down) or released (Up).
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum State {
Down,
Up,
}
/// Enum representing the 6 buttons on the back of the device.
///
/// The buttons are numbered 1, 2, 3 on the left (top-to-bottom) and
/// 4, 5, 6 on the right (top-to-bottom) when the screen is facing
/// you.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Button {
Btn1,
Btn2,
Btn3,
Btn4,
Btn5,
Btn6,
}
/// Returns an async Stream of Up/Down events for the buttons wired to
/// the given GPIO pin numbers.
pub fn events(pins: [u8; 6]) -> gpio::Result<impl Stream<Item=(Button, State)>> {
let gpio = Gpio::new()?;
let btns = [
gpio.get(pins[0])?.into_input_pullup(),
gpio.get(pins[1])?.into_input_pullup(),
gpio.get(pins[2])?.into_input_pullup(),
gpio.get(pins[3])?.into_input_pullup(),
gpio.get(pins[4])?.into_input_pullup(),
gpio.get(pins[5])?.into_input_pullup(),
];
let (tx, rx) = mpsc::unbounded();
thread::spawn(move || {
// The last state sent on the stream for each button.
let mut states = [State::Up; 6];
// Previous values for each button, with each bit in the u8
// corresponding to high/low (1/0) GPIO reads for that pin.
let mut history: [u8; 6] = [0; 6];
// Start polling GPIO pins
loop {
for i in 0..6 {
let btn = &btns[i];
let state = states[i];
let level = btn.read();
// We're only interested if the state has changed from
// what we last sent on the channel.
let changed = match level {
Level::High => state == State::Down,
Level::Low => state == State::Up,
};
if changed {
// Did we read 8 stable values in a row prior to
// this (i.e. all bits are 0 or 1)?
let stable = history[i] == 0 || history[i] == 255;
if stable {
let new_state = match level {
Level::High => State::Up,
Level::Low => State::Down,
};
states[i] = new_state;
let msg = (btn_number(i), new_state);
if let Err(_err) = tx.unbounded_send(msg) {
// Receiver has been dropped - stop polling
break;
}
}
}
history[i] = match level {
Level::High => history[i].rotate_left(1) | 0b00000001,
Level::Low => history[i].rotate_left(1) & 0b11111110,
};
}
thread::sleep(Duration::from_millis(3));
}
});
Ok(rx)
}
// converts usize to Button
fn btn_number(i: usize) -> Button {
match i {
0 => Button::Btn1,
1 => Button::Btn2,
2 => Button::Btn3,
3 => Button::Btn4,
4 => Button::Btn5,
5 => Button::Btn6,
// we know i is always in range 0..6
_ => unreachable!(),
}
}