-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathRotaryEncoder.py
119 lines (99 loc) · 3.76 KB
/
RotaryEncoder.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
import RPi.GPIO as GPIO
import math
import threading
from Queue import Queue
EventLeft = 'L'
EventRight = 'R'
EventUp = 'U'
EventDown = 'D'
class RotaryEncoderWorker(object):
def __init__(self, a_pin, b_pin, s_pin, Q):
self.gpio = GPIO
self.gpio.setwarnings(False)
self.queue = Q
self.a_pin = a_pin
self.b_pin = b_pin
self.s_pin = s_pin
self.gpio.setmode(GPIO.BCM)
#Rotary
self.gpio.setup(self.a_pin, self.gpio.IN, pull_up_down=self.gpio.PUD_UP)
self.gpio.setup(self.b_pin, self.gpio.IN, pull_up_down=self.gpio.PUD_UP)
self.gpio.add_event_detect(self.a_pin, GPIO.BOTH, callback=self.RotaryCall)
self.gpio.add_event_detect(self.b_pin, GPIO.BOTH, callback=self.RotaryCall)
#Switch
self.gpio.setup(self.s_pin, self.gpio.IN, pull_up_down=self.gpio.PUD_UP)
self.gpio.add_event_detect(self.s_pin, GPIO.BOTH, callback=self.SwitchCall, bouncetime=50) # depending on hardware the debouncing can be done by software or hardware here we used software version
self.SWPrev = 1
self.last_delta = 0
self.r_seq = 0
# steps_per_cycle and remainder are only used in get_cycles which
# returns a coarse-granularity step count. By default
# steps_per_cycle is 4 as there are 4 steps per
# detent on my encoder, and get_cycles() will return -1 or 1
# for each full detent step.
self.steps_per_cycle = 4
self.remainder = 0
self.cycles = 0
#self.lock = threading.Lock()
self.delta = 0
def SwitchCall(self, channel):
state = self.gpio.input(self.s_pin)
if state == 0:
if self.SWPrev == 1:
#print ("Switch Down")
self.SWPrev = 0
self.queue.put(EventDown)
else: #still pressed
pass
else: # state = 1
if self.SWPrev == 0:
#print ("Switch Up")
self.SWPrev = 1
self.queue.put(EventUp)
else: #still pressed
pass
# Inspired by http://guy.carpenter.id.au/gaugette/blog/2013/01/14/rotary-encoder-library-for-the-raspberry-pi/
# 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 RotaryCall(self, channel):
delta = 0
a_state = self.gpio.input(self.a_pin)
b_state = self.gpio.input(self.b_pin)
r_seq = (a_state ^ b_state) | b_state << 1
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
self.delta += delta
self.remainder += delta #self.get_delta()
self.cycles = self.remainder // self.steps_per_cycle
self.remainder %= self.steps_per_cycle # remainder always remains positive
# Check rotary status
if self.cycles == 1:
self.queue.put(EventLeft)
self.delta = 0
elif self.cycles == -1:
self.queue.put(EventRight)
self.delta = 0
else:
pass
# it does not work as expected fix me
def __del__(self):
self.gpio.cleanup()
#workaround to close gpio in a proper way, function shall be called at exit
def Exit(self):
self.gpio.cleanup()