1pub mod config;
5
6use rppal::{gpio::Gpio, gpio::InputPin};
7
8use config::GpioConfig;
9use serde_derive::Serialize;
10use std::collections::HashMap;
11
12#[derive(Debug)]
13pub struct InterruptCtrl {
14 gpio: Gpio,
15 pins: Vec<InputPin>,
16 topic_map: HashMap<u8, String>,
17}
18
19#[derive(Debug)]
20pub enum ICError {
21 GenericError(String),
22}
23
24impl std::error::Error for ICError {}
25
26impl From<rppal::gpio::Error> for ICError {
27 fn from(gpioe: rppal::gpio::Error) -> ICError {
28 ICError::GenericError(format!("{:?}", gpioe))
29 }
30}
31
32impl std::fmt::Display for ICError {
33 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
34 use ICError::*;
35 match self {
36 GenericError(e) => write!(f, "InterruptCtrl Error: {}", e),
37 }
38 }
39}
40
41#[derive(Debug, Serialize)]
42struct Message {
43 pin: u8,
44 message: String,
45}
46
47impl Message {
48 pub fn new(pin: u8, message: String) -> Message {
49 Message { pin, message }
50 }
51}
52
53impl ToString for Message {
54 fn to_string(&self) -> String {
55 serde_json::to_string(self).unwrap_or_else(|_| "Unknown Payload".to_string())
56 }
57}
58
59impl InterruptCtrl {
60 pub fn from_gpio_config(configs: &[GpioConfig]) -> Result<Self, ICError> {
61 let gpio = Gpio::new()?;
62
63 let pins: Result<Vec<InputPin>, _> = configs
64 .iter()
65 .map(|c| {
66 gpio.get(c.pin)
67 .map(|p| match c.with_pullup() {
68 true => p.into_input_pullup(),
69 false => p.into_input(),
70 })
71 .map(|mut p| {
72 p.set_interrupt(c.trigger.into())
73 .unwrap_or_else(|_| panic!());
74 p
75 })
76 })
77 .collect();
78 let topics: HashMap<u8, String> = configs
79 .iter()
80 .map(|c| (c.pin, c.topic.to_string()))
81 .collect();
82
83 let pins = pins?;
84 let retval = InterruptCtrl {
85 gpio,
86 pins,
87 topic_map: topics,
88 };
89
90 Ok(retval)
91 }
92
93 pub fn poll<C>(&self, mut callback: C) -> Result<(), ICError>
94 where
95 C: FnMut(&str, &str),
96 {
97 let pin_refs: Vec<_> = self.pins.iter().collect();
98 let result = self.gpio.poll_interrupts(&pin_refs, false, None)?; if let Some((pin, level)) = result {
100 let topic = self.topic_map.get(&pin.pin());
101 if let Some(topic) = topic {
102 let message = Message::new(pin.pin(), format!("{:?}", level)).to_string();
103 callback(topic, &message);
104 } else {
105 unreachable!();
106 }
107 };
108 Ok(())
109 }
110}
111
112#[cfg(test)]
113mod test {
114 use crate::config::TriggerType;
115
116 use super::*;
117
118 #[test]
119 fn can_create_interrupt_ctrl() {
120 let gpios = vec![GpioConfig::new(1, "foo", TriggerType::Falling)];
121 let _ctrl = InterruptCtrl::from_gpio_config(&gpios).expect("Unable to create Ctrl");
122 }
123
124 #[test]
125 fn cannot_create_same_pin_twice() {
126 let gpios = vec![
127 GpioConfig::new(1, "yes", TriggerType::Falling),
128 GpioConfig::new(1, "no", TriggerType::Rising),
129 ];
130 let ctrl = InterruptCtrl::from_gpio_config(&gpios);
131 assert!(ctrl.is_err());
132 }
133}