endbasic_rpi/
gpio.rs

1// EndBASIC
2// Copyright 2021 Julio Merino
3//
4// Licensed under the Apache License, Version 2.0 (the "License"); you may not
5// use this file except in compliance with the License.  You may obtain a copy
6// of the License at:
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
13// License for the specific language governing permissions and limitations
14// under the License.
15
16//! GPIO implementation for the Raspberry Pi.
17
18use endbasic_std::gpio::{Pin, PinMode, Pins};
19use rppal::gpio;
20use std::collections::HashMap;
21use std::io;
22
23/// Implementation of the EndBASIC GPIO operations for a Raspberry Pi using the rppal library.
24#[derive(Default)]
25pub struct RppalPins {
26    chip: Option<gpio::Gpio>,
27    inputs: HashMap<Pin, gpio::InputPin>,
28    outputs: HashMap<Pin, gpio::OutputPin>,
29}
30
31/// Converts a `gpio::Error` to an `io::Error`.
32pub(crate) fn gpio_error_to_io_error(e: gpio::Error) -> io::Error {
33    match e {
34        gpio::Error::Io(e) => e,
35        gpio::Error::PermissionDenied(path) => io::Error::new(
36            io::ErrorKind::PermissionDenied,
37            format!("Cannot open {}: permission denied", path),
38        ),
39        gpio::Error::PinNotAvailable(pin) => {
40            io::Error::new(io::ErrorKind::InvalidInput, format!("Unknown pin number {}", pin))
41        }
42        e => io::Error::new(io::ErrorKind::Other, e.to_string()),
43    }
44}
45
46impl RppalPins {
47    /// Gets access to the default GPIO chip and lazily opens it if not yet open.
48    fn get_chip(&mut self) -> io::Result<&mut gpio::Gpio> {
49        if self.chip.is_none() {
50            self.chip = Some(gpio::Gpio::new().map_err(gpio_error_to_io_error)?);
51        }
52        Ok(self.chip.as_mut().unwrap())
53    }
54}
55
56impl Pins for RppalPins {
57    fn setup(&mut self, pin: Pin, mode: PinMode) -> io::Result<()> {
58        self.clear(pin)?;
59        let chip = self.get_chip()?;
60        let gpio_pin = chip.get(pin.0).map_err(gpio_error_to_io_error)?;
61        match mode {
62            PinMode::In => {
63                self.inputs.insert(pin, gpio_pin.into_input());
64            }
65            PinMode::InPullDown => {
66                self.inputs.insert(pin, gpio_pin.into_input_pulldown());
67            }
68            PinMode::InPullUp => {
69                self.inputs.insert(pin, gpio_pin.into_input_pullup());
70            }
71            PinMode::Out => {
72                self.outputs.insert(pin, gpio_pin.into_output());
73            }
74        };
75        Ok(())
76    }
77
78    fn clear(&mut self, pin: Pin) -> io::Result<()> {
79        self.inputs.remove(&pin);
80        self.outputs.remove(&pin);
81        Ok(())
82    }
83
84    fn clear_all(&mut self) -> io::Result<()> {
85        self.inputs.clear();
86        self.outputs.clear();
87        Ok(())
88    }
89
90    fn read(&mut self, pin: Pin) -> io::Result<bool> {
91        if !self.inputs.contains_key(&pin) || self.outputs.contains_key(&pin) {
92            return Err(io::Error::new(
93                io::ErrorKind::AlreadyExists,
94                "Pin not configured for read; use GPIO_SETUP first",
95            ));
96        }
97        let pin = self.inputs.get(&pin).unwrap();
98        match pin.read() {
99            gpio::Level::High => Ok(true),
100            gpio::Level::Low => Ok(false),
101        }
102    }
103
104    fn write(&mut self, pin: Pin, v: bool) -> io::Result<()> {
105        if self.inputs.contains_key(&pin) || !self.outputs.contains_key(&pin) {
106            return Err(io::Error::new(
107                io::ErrorKind::AlreadyExists,
108                "Pin not configured for write; use GPIO_SETUP first",
109            ));
110        }
111        let pin = self.outputs.get_mut(&pin).unwrap();
112        if v {
113            pin.write(gpio::Level::High);
114        } else {
115            pin.write(gpio::Level::Low);
116        }
117        Ok(())
118    }
119}