gpiocdev_embedded_hal/async/
async_io.rs

1// SPDX-FileCopyrightText: 2024 Kent Gibson <warthog618@gmail.com>
2//
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5use std::path::Path;
6
7use crate::{state_to_value, Error};
8use embedded_hal::digital::PinState;
9use gpiocdev::async_io::AsyncRequest;
10use gpiocdev::line::{Config, EdgeDetection, EdgeKind, Offset, Value};
11use gpiocdev::Request;
12
13/// Provides the [`embedded_hal::digital`] and [`embedded_hal_async::digital::Wait`]
14/// traits for a [`gpiocdev::Request`] containing a single input pin using the
15/// async-io reactor.
16///
17/// Holding the [`InputPin`] grants exclusive access to the pin.
18///
19/// Do NOT drop the [`InputPin`] until you are completely done with it.
20/// Dropping and re-requesting the line is far more expensive than getting the value.
21pub struct InputPin {
22    req: AsyncRequest,
23    offset: Offset,
24    config: Config,
25}
26
27impl InputPin {
28    /// Creates a new input pin for the given `offset` on the given `chip`.
29    ///
30    /// ```no_run
31    /// use embedded_hal::digital::InputPin;
32    /// use embedded_hal_async::digital::Wait;
33    /// # use gpiocdev_embedded_hal::Error;
34    ///
35    /// # async fn example() -> Result<(), Error> {
36    /// let mut pin = gpiocdev_embedded_hal::async_io::InputPin::new("/dev/gpiochip0", 4)?;
37    /// if pin.is_high()? {
38    ///     println!("Input is high.");
39    /// }
40    /// pin.wait_for_falling_edge().await?;
41    /// # Ok(())
42    /// # }
43    /// ```
44    pub fn new<P>(chip: P, offset: u32) -> Result<Self, Error>
45    where
46        P: AsRef<Path>,
47    {
48        Ok(crate::InputPin::new(chip, offset)?.into())
49    }
50
51    #[inline]
52    fn is_high(&mut self) -> Result<bool, Error> {
53        Ok(self.req.as_ref().value(self.offset)?
54            == state_to_value(PinState::High, self.config.active_low))
55    }
56
57    #[inline]
58    fn is_low(&mut self) -> Result<bool, Error> {
59        Ok(!self.is_high()?)
60    }
61
62    /// Set this pin to output mode.
63    pub fn into_output_pin(self, state: PinState) -> Result<crate::OutputPin, Error> {
64        let pin: crate::InputPin = self.into();
65        pin.into_output_pin(state)
66    }
67
68    /// The value of the line following the most recent edge.
69    fn last_value(&mut self) -> Result<Value, Error> {
70        if self.config.value.is_none() {
71            self.config.value = Some(self.req.as_ref().value(self.offset)?);
72        }
73        Ok(self.config.value.unwrap())
74    }
75
76    async fn wait_for_edge(&mut self, edge: EdgeDetection) -> Result<(), Error> {
77        self.enable_edge_detection(edge)?;
78        loop {
79            let event = self.req.read_edge_event().await?;
80
81            if match edge {
82                EdgeDetection::BothEdges => true,
83                EdgeDetection::RisingEdge => event.kind == EdgeKind::Rising,
84                EdgeDetection::FallingEdge => event.kind == EdgeKind::Falling,
85            } {
86                let value = match event.kind {
87                    EdgeKind::Rising => Value::Active,
88                    EdgeKind::Falling => Value::Inactive,
89                };
90                self.config.value = Some(value);
91                return Ok(());
92            }
93        }
94    }
95
96    async fn wait_for_level(&mut self, value: Value) -> Result<(), Error> {
97        let edge = match value {
98            Value::Active => EdgeDetection::RisingEdge,
99            Value::Inactive => EdgeDetection::FallingEdge,
100        };
101        self.enable_edge_detection(edge)?;
102        if self.last_value()? == value {
103            return Ok(());
104        }
105        loop {
106            let event = self.req.read_edge_event().await?;
107
108            let nv = match event.kind {
109                EdgeKind::Rising => Value::Active,
110                EdgeKind::Falling => Value::Inactive,
111            };
112            self.config.value = Some(nv);
113            if value == nv {
114                return Ok(());
115            }
116        }
117    }
118    /// Ensure that the request configuration has edge detection enabled for the requested edge.
119    fn enable_edge_detection(&mut self, edge: EdgeDetection) -> Result<(), Error> {
120        let new_detection = match self.config.edge_detection {
121            Some(EdgeDetection::BothEdges) => return Ok(()),
122            Some(x) => {
123                if x == edge {
124                    return Ok(());
125                };
126                EdgeDetection::BothEdges
127            }
128            None => edge,
129        };
130        let req = self.req.as_ref();
131        req.reconfigure(req.config().with_edge_detection(new_detection))?;
132        self.config.edge_detection = Some(new_detection);
133        // force reading of current value
134        self.config.value = None;
135        Ok(())
136    }
137}
138
139impl From<InputPin> for Request {
140    /// Convert the [`InputPin`] into the contained [`Request`].
141    fn from(pin: InputPin) -> Self {
142        pin.req.into()
143    }
144}
145
146impl From<crate::InputPin> for InputPin {
147    /// Convert a synchronous [`InputPin`](crate::InputPin) into an async_io [`InputPin`].
148    fn from(pin: crate::InputPin) -> Self {
149        InputPin {
150            req: pin.0.req.into(),
151            offset: pin.0.offset,
152            config: pin.0.config,
153        }
154    }
155}
156
157impl From<InputPin> for crate::InputPin {
158    /// Convert an async_io [`InputPin`] into a synchronous [`InputPin`](crate::InputPin).
159    fn from(pin: InputPin) -> Self {
160        crate::InputPin(crate::Pin {
161            req: pin.req.into(),
162            offset: pin.offset,
163            config: pin.config,
164        })
165    }
166}
167
168impl embedded_hal::digital::ErrorType for InputPin {
169    /// Errors returned by the [`InputPin`].
170    type Error = Error;
171}
172
173impl embedded_hal::digital::InputPin for InputPin {
174    #[inline]
175    fn is_high(&mut self) -> Result<bool, Self::Error> {
176        self.is_high()
177    }
178
179    #[inline]
180    fn is_low(&mut self) -> Result<bool, Self::Error> {
181        self.is_low()
182    }
183}
184
185impl embedded_hal_async::digital::Wait for InputPin {
186    /// Wait for the pin to go high.
187    ///
188    /// # Note
189    /// The implementation is event driven, and only looks for events when waiting
190    /// for the level to change.
191    /// If the returned future resolves then subsequent calls are effectively a no-op.
192    /// Wait on an edge or the pin to go low instead.
193    #[inline]
194    async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
195        self.wait_for_level(Value::Active).await
196    }
197
198    /// Wait for the pin to go low.
199    ///
200    /// # Note
201    /// The implementation is event driven, and only looks for events when waiting
202    /// for the level to change.
203    /// If the returned future resolves then subsequent calls are effectively a no-op.
204    /// Wait on an edge or the pin to go high instead.
205    #[inline]
206    async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
207        self.wait_for_level(Value::Inactive).await
208    }
209
210    #[inline]
211    async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
212        self.wait_for_edge(EdgeDetection::RisingEdge).await
213    }
214
215    #[inline]
216    async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
217        self.wait_for_edge(EdgeDetection::FallingEdge).await
218    }
219
220    #[inline]
221    async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
222        self.wait_for_edge(EdgeDetection::BothEdges).await
223    }
224}