rustpi_io/
gpio.rs

1// This file is part of RustpiIO.
2//
3// Copyright 2018
4//
5// Contributors: Tom Meyer
6//
7// RustpiIO is free software: you can redistribute it and/or modify
8// it under the terms of the GNU General Public License as published by
9// the Free Software Foundation, either version 3 of the License, or
10// (at your option) any later version.
11//
12// RustpiIO is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with RustpiIO.  If not, see <http://www.gnu.org/licenses/>
19
20use std::fs::OpenOptions;
21use std::io::prelude::*;
22use std::io::Result;
23use std::io::ErrorKind;
24use std::io::Error;
25use std::path::Path;
26use std::fmt;
27
28use globals::GPIO_PATH;
29
30#[derive(Debug, Clone, Copy, PartialEq)]
31pub enum GPIOMode {
32    Read,
33    Write,
34}
35
36#[derive(Debug, Clone, Copy)]
37pub enum GPIOData {
38    Low = 0,
39    High = 1,
40}
41
42/// A software representation for a GPIO pin of the raspberry pi.
43pub struct GPIO {
44    pin: u8,
45    mode: GPIOMode,
46}
47
48impl GPIO {
49    /// Returns the configured pin of the GPIO struct.
50    pub fn gpio_number(&self) -> u8 {
51        self.pin
52    }
53
54    /// Returns the configured mode of the GPIO struct.
55    pub fn current_mode(&self) -> GPIOMode {
56        self.mode
57    }
58
59    /// Changes the mode of the pin and writes the corresponding value to the fitting direction file
60    pub fn set_mode(&mut self, mode: GPIOMode) -> Result<&mut Self> {
61        let mut direction = OpenOptions::new()
62            .write(true)
63            .open(format!("{}gpio{}/direction", GPIO_PATH, self.pin))?;
64        match mode {
65            GPIOMode::Read => try!(direction.write_all("in".as_bytes())),
66            GPIOMode::Write => try!(direction.write_all("out".as_bytes())),
67        };
68        self.mode = mode;
69        Ok(self)
70    }
71
72    /// Initializes the gpio. Exports the pin with the /sys/class/gpio/export file
73    /// and calls the set_mode() function with the given mode.
74    /// Returns an Error if the gpio was already exported earlier (inside or outside of the application)
75    pub fn new(gpio: u8, mode: GPIOMode) -> Result<Self> {
76        if Path::new(&format!("{}gpio{}/", GPIO_PATH, gpio)).exists() {
77            return Err(Error::new(
78                ErrorKind::AddrInUse,
79                "Error: gpio was already initialized",
80            ));
81        }
82        {
83            let mut export = OpenOptions::new()
84                .write(true)
85                .open(format!("{}export", GPIO_PATH))?;
86            export.write_all(format!("{}", gpio).as_bytes())?;
87        }
88        let mut result = GPIO {
89            pin: gpio,
90            mode: mode,
91        };
92        result.set_mode(mode)?;
93        Ok(result)
94    }
95
96    /// Reads the current value of the pin in both Read and Write mode.
97    /// Returns an Error if a value other than "1" or "0" is read
98    pub fn value(&self) -> Result<GPIOData> {
99        let mut value = try!(
100            OpenOptions::new()
101                .read(true)
102                .open(format!("{}gpio{}/value", GPIO_PATH, self.pin))
103        );
104        let mut buffer = vec![];
105        try!(value.read_to_end(&mut buffer));
106        match buffer[0] as char {
107            '0' => Ok(GPIOData::Low),
108            '1' => Ok(GPIOData::High),
109            _ => Err(Error::new(
110                ErrorKind::InvalidData,
111                "read value other than 1 or 0",
112            )),
113        }
114    }
115
116    /// Sets the value of the gpio to HIGH or LOW
117    /// Returns an Error if the GPIO::Mode is not Write
118    pub fn set(&self, data: GPIOData) -> Result<()> {
119        if self.mode != GPIOMode::Write {
120            return Err(Error::new(
121                ErrorKind::PermissionDenied,
122                "Error: gpio is not in write mode",
123            ));
124        }
125        let buffer = match data {
126            GPIOData::Low => "0",
127            GPIOData::High => "1",
128        };
129        let mut direction = OpenOptions::new()
130            .write(true)
131            .open(format!("{}gpio{}/value", GPIO_PATH, self.pin))?;
132        try!(direction.write_all(buffer.as_bytes()));
133        Ok(())
134    }
135}
136
137/// Closes the gpio and write its pin number into /sys/class/gpio/unexport
138impl Drop for GPIO {
139    fn drop(&mut self) {
140        if let Ok(mut unexport) = OpenOptions::new()
141            .write(true)
142            .open(format!("{}unexport", GPIO_PATH))
143        {
144            match unexport.write_all(format!("{}", self.pin).as_bytes()) {
145                Err(why) => panic!("couldn't close gpio {}: {}", self.pin, why),
146                Ok(_) => {}
147            }
148        } else {
149            panic!("file error: {}")
150        }
151    }
152}
153
154/// Writes "LOW" or "HIGH"
155impl fmt::Display for GPIOData {
156    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157        match *self {
158            GPIOData::Low => write!(f, "LOW"),
159            GPIOData::High => write!(f, "HIGH"),
160        }
161    }
162}
163
164///Writes "Read" or "Write"
165impl fmt::Display for GPIOMode {
166    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
167        match *self {
168            GPIOMode::Read => write!(f, "Read"),
169            GPIOMode::Write => write!(f, "Write"),
170        }
171    }
172}