~caolan/bramley

ref: 4732f7af2fcec99d25797f78c089c547259238f8 bramley/buttons/src/lib.rs -rw-r--r-- 3.3 KiB
4732f7afCaolan McMahon extract text handling into drawing module and add separate shutdown messages 1 year, 8 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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!(),
    }
}