Skip to main content

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::any::Any;
21use std::collections::HashMap;
22use std::io;
23
24/// Implementation of the EndBASIC GPIO operations for a Raspberry Pi using the rppal library.
25#[derive(Default)]
26pub struct RppalPins {
27    chip: Option<gpio::Gpio>,
28    inputs: HashMap<Pin, gpio::InputPin>,
29    outputs: HashMap<Pin, gpio::OutputPin>,
30}
31
32/// Converts a `gpio::Error` to an `io::Error`.
33pub(crate) fn gpio_error_to_io_error(e: gpio::Error) -> io::Error {
34    match e {
35        gpio::Error::Io(e) => e,
36        gpio::Error::PermissionDenied(path) => io::Error::new(
37            io::ErrorKind::PermissionDenied,
38            format!("Cannot open {}: permission denied", path),
39        ),
40        gpio::Error::PinNotAvailable(pin) => {
41            io::Error::new(io::ErrorKind::InvalidInput, format!("Unknown pin number {}", pin))
42        }
43        e => io::Error::other(e.to_string()),
44    }
45}
46
47impl RppalPins {
48    /// Gets access to the default GPIO chip and lazily opens it if not yet open.
49    fn get_chip(&mut self) -> io::Result<&mut gpio::Gpio> {
50        if self.chip.is_none() {
51            self.chip = Some(gpio::Gpio::new().map_err(gpio_error_to_io_error)?);
52        }
53        Ok(self.chip.as_mut().unwrap())
54    }
55}
56
57impl Pins for RppalPins {
58    fn as_any(&self) -> &dyn Any {
59        self
60    }
61
62    fn as_any_mut(&mut self) -> &mut dyn Any {
63        self
64    }
65
66    fn setup(&mut self, pin: Pin, mode: PinMode) -> io::Result<()> {
67        self.clear(pin)?;
68        let chip = self.get_chip()?;
69        let gpio_pin = chip.get(pin.0).map_err(gpio_error_to_io_error)?;
70        match mode {
71            PinMode::In => {
72                self.inputs.insert(pin, gpio_pin.into_input());
73            }
74            PinMode::InPullDown => {
75                self.inputs.insert(pin, gpio_pin.into_input_pulldown());
76            }
77            PinMode::InPullUp => {
78                self.inputs.insert(pin, gpio_pin.into_input_pullup());
79            }
80            PinMode::Out => {
81                self.outputs.insert(pin, gpio_pin.into_output());
82            }
83        };
84        Ok(())
85    }
86
87    fn clear(&mut self, pin: Pin) -> io::Result<()> {
88        self.inputs.remove(&pin);
89        self.outputs.remove(&pin);
90        Ok(())
91    }
92
93    fn clear_all(&mut self) -> io::Result<()> {
94        self.inputs.clear();
95        self.outputs.clear();
96        Ok(())
97    }
98
99    fn read(&mut self, pin: Pin) -> io::Result<bool> {
100        if !self.inputs.contains_key(&pin) || self.outputs.contains_key(&pin) {
101            return Err(io::Error::new(
102                io::ErrorKind::AlreadyExists,
103                "Pin not configured for read; use GPIO_SETUP first",
104            ));
105        }
106        let pin = self.inputs.get(&pin).unwrap();
107        match pin.read() {
108            gpio::Level::High => Ok(true),
109            gpio::Level::Low => Ok(false),
110        }
111    }
112
113    fn write(&mut self, pin: Pin, v: bool) -> io::Result<()> {
114        if self.inputs.contains_key(&pin) || !self.outputs.contains_key(&pin) {
115            return Err(io::Error::new(
116                io::ErrorKind::AlreadyExists,
117                "Pin not configured for write; use GPIO_SETUP first",
118            ));
119        }
120        let pin = self.outputs.get_mut(&pin).unwrap();
121        if v {
122            pin.write(gpio::Level::High);
123        } else {
124            pin.write(gpio::Level::Low);
125        }
126        Ok(())
127    }
128}