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