+# Main classes
+class PinWatcher():
+ def __init__(self, pin, histlen):
+ GPIO.setup(pin, GPIO.IN)
+ assert histlen > 1 # otherwise our logic goes nuts...
+ self._pin = pin
+ self._histlen = histlen
+ # state change detection
+ self._state = None
+ self._newstate = None # != None iff we are currently seeing a state change
+ self._newstatelen = 0 # only valid if newstate != None
+ # start state change handler thread
+ self._q = queue.Queue()
+ self._t = threading.Thread(target=self.queue_consumer)
+ self._t.start()
+
+ def queue_consumer(self):
+ while True:
+ el = self._q.get()
+ if el is None: return # we are supposed to terminate
+ # handle the state change
+ (oldstate, newstate) = el
+ self.callback(oldstate, newstate)
+
+ def read(self):
+ curstate = GPIO.input(self._pin)
+ assert curstate in (0, 1)
+ if curstate != self._state:
+ # the state is about to change
+ if curstate == self._newstate:
+ # we already saw this new state
+ self._newstatelen += 1
+ if self._newstatelen >= self._histlen:
+ self._q.put((self._state, curstate)) # send stuff to the other thread
+ self._state = curstate
+ self._newstate = None
+ else:
+ # now check for how long we see this new state
+ self._newstate = curstate
+ self._newstatelen = 1
+ else:
+ # old state is preserved
+ self._newstate = None
+
+ def quit(self):
+ self._q.put(None)
+ self._t.join()