1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 import os
19 import select
20 import time
21 from threading import Thread, Lock, Event
22
23 PINS = [2, 3, 4, 14, 15, 17, 18, 22, 23, 24, 25, 27]
24
25
26 OUTPUT = 1
27 INPUT = 2
28 DISABLED = 3
29
30
31 NONE = "none"
32 RISING = "rising"
33 FALLING = "falling"
34 BOTH = "both"
35
36 ARG_PULL_DISABLE = 0
37 ARG_PULL_DOWN = 1
38 ARG_PULL_UP = 2
39
40 HIGH = 1
41 LOW = 0
42
43
46 self.gpio_dir = "/sys/class/gpio/gpio%d" % pin
47 self.pin = pin
48 self.direction = DISABLED
49 self.edge = NONE
50 self.last = 1
51 self.mutex = Lock()
52 self.poll_thread = None
53 self.poll_thread_stop = None
54 if pin in PINS:
55 if not os.path.exists(self.gpio_dir):
56 with open("/sys/class/gpio/export", "w") as f:
57 f.write("%d\n" % pin)
58
60 here = os.path.dirname(os.path.realpath(__file__))
61 os.system(here + "/pullup.sbin %d %d" % (pin, enable))
62
65
68
71
74
76 """Run function used in poll_thread"""
77
78 po = select.epoll()
79 po.register(self.fvalue, select.POLLIN | select.EPOLLPRI | select.EPOLLET)
80 last_time = 0
81 first_time = True
82
83
84 while not self.poll_thread_stop.is_set():
85 event = po.poll(1)
86 if len(event) == 0:
87
88 continue
89 self.fvalue.seek(0)
90 if not first_time:
91 timenow = time.time()
92 if (timenow - last_time) > (bouncetime/1000) or last_time == 0:
93 callback(self.pin)
94 last_time = timenow
95 else:
96 first_time = False
97
99 with self.mutex:
100 with open(self.gpio_dir + "/edge", "w") as fedge:
101 self.edge = edge
102 fedge.write(edge)
103
105 if self.poll_thread and self.poll_thread.isAlive():
106
107 self.poll_thread_stop.set()
108
109 while self.poll_thread.isAlive():
110 self.poll_thread.join(1)
111
116
118 """Blocks until the given edge has happened
119 @param edge: Either gpio.FALLING, gpio.RISING, gpio.BOTH
120 @type edge: string
121 @throws: ValueError
122 """
123 if self.direction != INPUT:
124 raise ValueError("GPIO must be configured to be an input first.")
125 if edge not in [RISING, FALLING, BOTH]:
126 raise ValueError("Invalid edge!")
127 self.__set_edge(edge)
128
129
130 po = select.epoll()
131 po.register(self.fvalue, select.POLLIN | select.EPOLLPRI | select.EPOLLET)
132
133 first_time = True
134
135 while True:
136 event = po.poll(60)
137 if len(event) == 0:
138
139 if self.edge == NONE:
140 break
141 else:
142 continue
143 self.fvalue.seek(0)
144 if not first_time:
145 break
146 else:
147 first_time = False
148
149 - def edge_detect(self, edge, callback=None, bouncetime=200):
150 """Sets up edge detection interrupt.
151 @param edge: either gpio.NONE, gpio.RISING, gpio.FALLING, or gpio.BOTH
152 @type edge: int
153 @param callback: Function to call when given edge has been detected.
154 @type callback: function
155 @param bouncetime: Debounce time in milliseconds.
156 @type bouncetime: int
157 @note: First parameter of callback function will be the pin number of gpio that called it.
158 """
159 if self.direction != INPUT:
160 raise ValueError("GPIO must be configured to be an input first.")
161 if callback is None and edge != NONE:
162 raise ValueError("Callback function must be given if edge is not NONE")
163 if edge not in [NONE, RISING, FALLING, BOTH]:
164 raise ValueError("Edge must be NONE, RISING, FALLING, or BOTH")
165
166 self.__set_edge(edge)
167
168 if edge != NONE:
169 self.__end_thread()
170 self.poll_thread_stop = Event()
171 self.poll_thread = Thread(target=Pin.__poll_thread_run, args=(self, callback, bouncetime))
172 self.poll_thread.start()
173
174
197
199
200 """Detects whether the GPIO has been clicked or on since the pin has been initialized or
201 since the last time was_clicked() has been called.
202 @returns: boolean
203 """
204 level = self.get_level()
205 clicked = level == 1 and self.last == 0
206 self.last = level
207 return clicked
208
210 """Returns the current level of the GPIO pin.
211 @returns: int (1 for HIGH, 0 for LOW)
212 @note: The GPIO pins are active low.
213 """
214 if self.direction != INPUT:
215 raise ValueError("GPIO must be configured to be an INPUT!")
216 with self.mutex:
217 self.fvalue.seek(0)
218 return int(self.fvalue.read())
219
221 """Sets the level of the GPIO port.
222 @param level: Level to set. Must be either HIGH or LOW.
223 @param level: int
224 """
225 if self.direction != OUTPUT:
226 raise ValueError("GPIO must be configured to be an OUTPUT!")
227 if level != 0 and level != 1:
228 raise ValueError("Level must be either 1 or 0.")
229 with self.mutex:
230
231 os.system("echo %s > %s/value" % (str(level), self.gpio_dir))
232
233
234
235
238 self.gpios = [Pin(i) for i in range(max(PINS) + 1)]
239
241 class UninitializedError(Exception):
242 pass
243
244 if not pin in PINS:
245 raise ValueError("Invalid GPIO")
246 if self.gpios[pin].direction != direction:
247 raise UninitializedError()
248
253
255 inputs = [g for g in self.gpios if g.direction == INPUT]
256 return [g.pin for g in inputs if g.was_clicked()]
257
261
265
266
267 g = Pins()
268 for name in ['configure', 'get_level', 'set_level', 'was_clicked']:
269 globals()[name] = getattr(g, name)
270