Package rstem :: Package gpio
[hide private]
[frames] | no frames]

Source Code for Package rstem.gpio

  1  #!/usr/bin/env python3 
  2  # 
  3  # Copyright (c) 2014, Scott Silver Labs, LLC. 
  4  # 
  5  # Licensed under the Apache License, Version 2.0 (the "License"); 
  6  # you may not use this file except in compliance with the License. 
  7  # You may obtain a copy of the License at 
  8  # 
  9  #       http://www.apache.org/licenses/LICENSE-2.0 
 10  # 
 11  # Unless required by applicable law or agreed to in writing, software 
 12  # distributed under the License is distributed on an "AS IS" BASIS, 
 13  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 14  # See the License for the specific language governing permissions and 
 15  # limitations under the License. 
 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  # Directions 
 26  OUTPUT = 1 
 27  INPUT = 2 
 28  DISABLED = 3 
 29   
 30  # Edge Detection 
 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   
44 -class Pin:
45 - def __init__(self, pin):
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
59 - def __pullup(self, pin, enable):
60 here = os.path.dirname(os.path.realpath(__file__)) 61 os.system(here + "/pullup.sbin %d %d" % (pin, enable))
62
63 - def __enable_pullup(self, pin):
64 self.__pullup(pin, ARG_PULL_UP)
65
66 - def __disable_pullup(self, pin):
67 self.__pullup(pin, ARG_PULL_DISABLE)
68
69 - def __enable_pulldown(self, pin):
70 self.__pullup(pin, ARG_PULL_DOWN)
71
72 - def __disable_pulldown(self, pin):
73 self.__pullup(pin, ARG_PULL_DISABLE)
74
75 - def __poll_thread_run(self, callback, bouncetime):
76 """Run function used in poll_thread""" 77 # NOTE: self will not change once this is called 78 po = select.epoll() 79 po.register(self.fvalue, select.POLLIN | select.EPOLLPRI | select.EPOLLET) 80 last_time = 0 81 first_time = True # used to ignore first trigger 82 # TODO: ignore second trigger too if edge = rising/falling? 83 84 while not self.poll_thread_stop.is_set(): 85 event = po.poll(1) 86 if len(event) == 0: 87 # timeout 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
98 - def __set_edge(self, edge):
99 with self.mutex: 100 with open(self.gpio_dir + "/edge", "w") as fedge: 101 self.edge = edge 102 fedge.write(edge)
103
104 - def __end_thread(self):
105 if self.poll_thread and self.poll_thread.isAlive(): 106 # self.poll_thread_running = False 107 self.poll_thread_stop.set() 108 109 while self.poll_thread.isAlive(): 110 self.poll_thread.join(1)
111
112 - def remove_edge_detect(self):
113 """Removes edge detect interrupt""" 114 self.__set_edge(NONE) 115 self.__end_thread()
116
117 - def wait_for_edge(self, edge):
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 # wait for edge 130 po = select.epoll() 131 po.register(self.fvalue, select.POLLIN | select.EPOLLPRI | select.EPOLLET) 132 # last_time = 0 133 first_time = True # used to ignore first trigger 134 135 while True: 136 event = po.poll(60) 137 if len(event) == 0: 138 # timeout to see if edge has changed 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() # end any previous callback functions 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
175 - def configure(self, direction):
176 """Configure the GPIO pin to either be an input, output or disabled. 177 @param direction: Either gpio.INPUT, gpio.OUTPUT, or gpio.DISABLED 178 @type direction: int 179 """ 180 if direction not in [INPUT, OUTPUT, DISABLED]: 181 raise ValueError("Direction must be INPUT, OUTPUT or DISABLED") 182 183 with self.mutex: 184 with open(self.gpio_dir + "/direction", "w") as fdirection: 185 self.direction = direction 186 if direction == OUTPUT: 187 # For future use 188 fdirection.write("out") 189 self.fvalue = open(self.gpio_dir + "/value", "w") 190 elif direction == INPUT: 191 self.__enable_pullup(self.pin) 192 fdirection.write("in") 193 self.fvalue = open(self.gpio_dir + "/value", "r") 194 elif direction == DISABLED: 195 fdirection.write("in") 196 self.fvalue.close()
197
198 - def was_clicked(self):
199 # TODO: make work for any type of edge change and rename function 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
209 - def get_level(self):
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
220 - def set_level(self, level):
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 # write value wasn't working for some reason... 231 os.system("echo %s > %s/value" % (str(level), self.gpio_dir))
232 # self.fvalue.seek(0) 233 # self.fvalue.write(str(level)) 234 235
236 -class Pins:
237 - def __init__(self):
238 self.gpios = [Pin(i) for i in range(max(PINS) + 1)]
239
240 - def __validate_gpio(self, pin, direction):
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
249 - def configure(self, pin, direction):
250 if not pin in PINS: 251 raise ValueError("Invalid GPIO") 252 self.gpios[pin].configure(direction)
253
254 - def was_clicked(self):
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
258 - def get_level(self, pin):
259 self.__validate_gpio(pin, INPUT) 260 return self.gpios[pin].get_level()
261
262 - def set_level(self, pin, level):
263 self.__validate_gpio(pin, OUTPUT) 264 self.gpios[pin].set_level(level)
265 266 # Export functions in this module 267 g = Pins() 268 for name in ['configure', 'get_level', 'set_level', 'was_clicked']: 269 globals()[name] = getattr(g, name) 270