From cb1528311c760027311ad01cdbcbcb89d052ded3 Mon Sep 17 00:00:00 2001 From: Andreas Goetz Date: Fri, 1 Mar 2013 16:46:28 +0100 Subject: [PATCH] Added enhanced RotaryEncoder class Added Button class --- gaugette/button.py | 38 +++++++++++ gaugette/rotary_encoder.py | 133 +++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 gaugette/button.py create mode 100644 gaugette/rotary_encoder.py diff --git a/gaugette/button.py b/gaugette/button.py new file mode 100644 index 0000000..0983c7a --- /dev/null +++ b/gaugette/button.py @@ -0,0 +1,38 @@ +import wiringpi +import time + +class Button: + + # button states + OFF = 0 # button inactive (not pressed) + PRESS = 1 # button pressed and quickly released + LONG = 2 # button pressed and still held + ACTIVE = 3 # button pressed but not yet released (internal state) + + def __init__(self, pin, longpress=0.3): + self.pin = pin + self.longpress = longpress + self.state = Button.OFF + self.io = wiringpi.GPIO(wiringpi.GPIO.WPI_MODE_PINS) + self.io.pinMode(self.pin, self.io.INPUT) + self.io.pullUpDnControl(self.pin, self.io.PUD_UP) + + def get_state(self): + state = self.io.digitalRead(self.pin) ^ 1 # low-active + + # button state model + if self.state == Button.PRESS: self.state = Button.OFF + + if self.state == Button.OFF and state: + self.state = Button.ACTIVE + self.pressed = time.time() + elif self.state == Button.ACTIVE: + if state: + if time.time()-self.pressed >= self.longpress: self.state = Button.LONG + else: + self.state = Button.PRESS + elif self.state == Button.LONG and not state: + self.state = Button.OFF + + #if self.state not in [Button.OFF, Button.ACTIVE]: print("Button: %s" % ["OFF","PRESS","LONG","ACTIVE"][self.state]) + return self.state diff --git a/gaugette/rotary_encoder.py b/gaugette/rotary_encoder.py new file mode 100644 index 0000000..1e4d6a5 --- /dev/null +++ b/gaugette/rotary_encoder.py @@ -0,0 +1,133 @@ +#---------------------------------------------------------------------- +# rotary_encoder.py from https://github.com/guyc/py-gaugette +# Guy Carpenter, Clearwater Software +# +# This is a class for reading quadrature rotary encoders +# like the PEC11 Series available from Adafruit: +# http://www.adafruit.com/products/377 +# The datasheet for this encoder is here: +# http://www.adafruit.com/datasheets/pec11.pdf +# +# This library expects the common pin C to be connected +# to ground. Pins A and B will have their pull-up resistor +# pulled high. +# +# Usage: +# +# import gaugette.rotary_encoder +# A_PIN = 7 # use wiring pin numbers here +# B_PIN = 9 +# encoder = gaugette.rotary_encoder.RotaryEncoder(A_PIN, B_PIN) +# while 1: +# delta = encoder.delta() # returns 0,1,or -1 +# if delta!=0: +# print delta +import wiringpi +import math +import threading +import time +class RotaryEncoder(object): + #---------------------------------------------------------------------- + # Pass the wiring pin numbers here. See: + # https://projects.drogon.net/raspberry-pi/wiringpi/pins/ + #---------------------------------------------------------------------- + def __init__(self, a_pin, b_pin): + self.a_pin = a_pin + self.b_pin = b_pin + self.gpio = wiringpi.GPIO(wiringpi.GPIO.WPI_MODE_PINS) + self.gpio.pinMode(self.a_pin, self.gpio.INPUT) + self.gpio.pullUpDnControl(self.a_pin, self.gpio.PUD_UP) + self.gpio.pinMode(self.b_pin, self.gpio.INPUT) + self.gpio.pullUpDnControl(self.b_pin, self.gpio.PUD_UP) + self.last_delta = 0 + self.r_seq = self.rotation_sequence() + # Gets the 2-bit rotation state of the current position + # This is deprecated - we now use rotation_sequence instead. + def rotation_state(self): + a_state = self.gpio.digitalRead(self.a_pin) + b_state = self.gpio.digitalRead(self.b_pin) + r_state = a_state | b_state << 1 + return r_state + # Returns the quadrature encoder state converted into + # a numerical sequence 0,1,2,3,0,1,2,3... + # + # Turning the encoder clockwise generates these + # values for switches B and A: + # B A + # 0 0 + # 0 1 + # 1 1 + # 1 0 + # We convert these to an ordinal sequence number by returning + # seq = (A ^ B) | B << 2 + # + def rotation_sequence(self): + a_state = self.gpio.digitalRead(self.a_pin) + b_state = self.gpio.digitalRead(self.b_pin) + r_seq = (a_state ^ b_state) | b_state << 1 + return r_seq + # Returns offset values of -2,-1,0,1,2 + def get_delta(self): + delta = 0 + r_seq = self.rotation_sequence() + if r_seq != self.r_seq: + delta = (r_seq - self.r_seq) % 4 + if delta==3: + delta = -1 + elif delta==2: + delta = int(math.copysign(delta, self.last_delta)) # same direction as previous, 2 steps + self.last_delta = delta + self.r_seq = r_seq + return delta + + class Worker(threading.Thread): + def __init__(self, a_pin, b_pin): + threading.Thread.__init__(self) + self.lock = threading.Lock() + self.encoder = RotaryEncoder(a_pin, b_pin) + self.daemon = True + self.delta = 0 + def run(self): + while True: + delta = self.encoder.get_delta() + with self.lock: + self.delta += delta + time.sleep(0.001) + def get_delta(self): + # revisit - should use locking + with self.lock: + delta = self.delta + self.delta = 0 + return delta + + +# extension by cpuidle@gmx.de + +class RotaryEncoder2(rotary_encoder.RotaryEncoder): + #---------------------------------------------------------------------- + # Pass the wiring pin numbers here. See: + # https://projects.drogon.net/raspberry-pi/wiringpi/pins/ + #---------------------------------------------------------------------- + def __init__(self, a_pin, b_pin): + self.ao = self.bo = 1 + super(RotaryEncoder2, self).__init__(a_pin, b_pin) + + def rotation_sequence(self): + a_state = self.gpio.digitalRead(self.a_pin) + b_state = self.gpio.digitalRead(self.b_pin) + r_seq = (a_state << 1) | b_state + return r_seq + + # Returns offset values of -1,0,1 + def get_delta(self): + delta = 0 + r_seq = self.rotation_sequence() + if r_seq != self.r_seq: + if self.r_seq == 3: + if r_seq == 1: + delta = 1 + else: + delta = -1 + self.r_seq = r_seq + return delta + \ No newline at end of file