forked from jdupl/onion-gpio-sysfs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
onionGpio.py
170 lines (130 loc) · 5.69 KB
/
onionGpio.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#!/usr/bin/python3
"Module for interfacing with gpio pins using the linux sysfs interface"
__version__ = "0.3"
__author__ = 'Lazar, Justin Duplessis and Eric Wolf'
__maintainer__ = "Eric Wolf" # for this fork
__email__ = "[email protected]"
__contact__ = "https://github.com/Deric-W/onion-gpio-sysfs"
from os.path import isfile
from errno import EBUSY
from enum import Enum
from typing import Optional
from select import select
# file paths
GPIO_BASE_PATH = "/sys/class/gpio"
GPIO_EXPORT_PATH = GPIO_BASE_PATH + "/export"
GPIO_UNEXPORT_PATH = GPIO_BASE_PATH + "/unexport"
GPIO_PATH = GPIO_BASE_PATH + "/gpio%d"
# file names
GPIO_VALUE_FILE = "value"
GPIO_DIRECTION_FILE = "direction"
GPIO_ACTIVE_LOW_FILE = "active_low"
GPIO_EDGE_FILE = "edge"
class Value(Enum):
"""Enum representing the logical state"""
LOW = "0"
HIGH = "1"
class Direction(Enum):
"""Enum representing the direction"""
INPUT = "in"
OUTPUT = "out"
OUTPUT_LOW = "low"
OUTPUT_HIGH = "high"
class ActiveLow(Enum):
"""Enum representing the active_low status"""
HIGH = "0"
LOW = "1"
class Edge(Enum):
"""Enum representing the edge status"""
NONE = "none"
RISING = "rising"
FALLING = "falling"
BOTH = "both"
class OnionGpio: # sysfs documentation: https://www.kernel.org/doc/Documentation/gpio/sysfs.txt
"""Base class for sysfs GPIO access"""
def __init__(self, gpio: int, ignore_busy: bool = False) -> None:
"""init with the gpio pin number and if the interface being used should be ignored"""
self.gpio = gpio # gpio number
path = GPIO_PATH % self.gpio # directory containing the gpio files
self.gpioValueFile = path + '/' + GPIO_VALUE_FILE # file to set/get value
self.gpioDirectionFile = path + '/' + GPIO_DIRECTION_FILE # file to set/get direction
self.gpioActiveLowFile = path + '/' + GPIO_ACTIVE_LOW_FILE # file to set/get active_low
self.gpioEdgeFile = path + '/' + GPIO_EDGE_FILE # file to set/get edge
try:
initGpio(gpio) # init gpio sysfs interface
except OSError as err:
if err.errno == EBUSY and ignore_busy: # interface already in use but we should ignore
pass
else: # something else happend or we should not ignore the busy interface
raise
def release(self) -> None:
"""release gpio sysfs interface""" # call once per object
freeGpio(self.gpio)
# context manager methods
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
self.release() # dont call inside context manager
return False # we dont want to hide exceptions
# value functions
def getValue(self) -> Value:
"""Read current GPIO value"""
with open(self.gpioValueFile, 'r') as fd:
return Value(fd.readline().rstrip("\n")) # catch ValueError if file content is not 0 or 1
def setValue(self, value: Value) -> None:
"""Set the desired GPIO value"""
with open(self.gpioValueFile, 'w') as fd:
fd.write(value.value)
# direction functions
def supportsDirection(self) -> bool:
"""check if the gpio supports setting the direction"""
return isfile(self.gpioDirectionFile)
def getDirection(self) -> Direction:
"""Read current GPIO direction"""
# read from the direction file
with open(self.gpioDirectionFile, 'r') as fd:
return Direction(fd.readline().rstrip("\n")) # catch ValueError if file content not valid
def setDirection(self, direction: Direction) -> None:
"""Set the desired GPIO direction"""
# write to the direction file
with open(self.gpioDirectionFile, 'w') as fd:
fd.write(direction.value)
# active-low functions
def getActiveLow(self) -> ActiveLow:
"""Read if current GPIO is active-low"""
with open(self.gpioActiveLowFile, 'r') as fd:
return ActiveLow(fd.readline().rstrip("\n")) # catch ValueError if file content not valid
def setActiveLow(self, active_low: ActiveLow) -> None:
"""Set the desired GPIO direction"""
with open(self.gpioActiveLowFile, 'w') as fd:
fd.write(active_low.value)
# note: active_low setting is reset when the gpio sysfs interface
# is released!
# edge methods
def supportsEdge(self) -> bool:
"""check if the gpio supports edges"""
return isfile(self.gpioEdgeFile)
def getEdge(self) -> Edge:
"""get edge setting of gpio"""
with open(self.gpioEdgeFile, "r") as fd:
return Edge(fd.readline().rstrip("\n")) # catch ValueError if file content not valid
def setEdge(self, edge: Edge) -> None:
"""set edge setting of gpio"""
with open(self.gpioEdgeFile, "w") as fd:
fd.write(edge.value)
# note: edge setting is reset when the gpio sysfs interface
# is released!
def waitForEdge(self, timeout: Optional[float] = None) -> None:
"""wait for edge on gpio"""
with open(self.gpioValueFile, "r") as fd:
fd.read() # somehow needs to be read before using select to work
if fd not in select([], [], [fd], timeout)[2]: # wait for value file exceptional condition
raise TimeoutError("received no edge on gpio {0}".format(self.gpio))
def initGpio(gpio: int) -> None:
"""Write to the gpio export to make the gpio available in sysfs"""
with open(GPIO_EXPORT_PATH, 'w') as fd:
fd.write(str(gpio))
def freeGpio(gpio: int) -> None:
"""Write to the gpio unexport to release the gpio sysfs interface"""
with open(GPIO_UNEXPORT_PATH, 'w') as fd:
fd.write(str(gpio))