I settled for a clumsy attempt to decode direction alone, using this circuit...
Of course, you need some code too - I wrote the following simple demonstration in Python. It detects positive-going transitions on "PinA" and looks at the state of PinB during these transition events to figure the direction in which the encoder has been moved.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/local/bin/python | |
# m0xpd | |
# shack.nasties 'at Gee Male dot com' | |
import RPi.GPIO as GPIO | |
# setup GPIO options... | |
GPIO.setmode(GPIO.BOARD) | |
GPIO.setwarnings(False) | |
# setup IO bits... | |
GPIO.setup(12, GPIO.IN) # PinA is on GPIO 12 | |
GPIO.setup(16, GPIO.IN) # PinB is on GPIO 16 | |
# check initial state... | |
oldPinA = GPIO.input(12) | |
Position = 0 | |
inc = 5 # increment | |
REmax = 127 # Max value | |
REmin = 0 # Min value | |
# Main Operating Loop... | |
while True: | |
encoderPinA=GPIO.input(12) | |
encoderPinB=GPIO.input(16) | |
if (encoderPinA == True) and (oldPinA == False): # Detect "upward" transition on PinA | |
if (encoderPinB == False): # if PinB is low... | |
Position=Position+inc # we're going clokwise | |
if Position > REmax: # limit max value | |
Position = REmax | |
else: # otherwise... | |
Position=Position-inc # we're going anti-clockwise | |
if Position < REmin: # limit min value | |
Position = REmin | |
print Position | |
oldPinA=encoderPinA # save current value for next time | |
After the fun-and-games with the MIDI controller, I'm really excited about object-oriented software for the RPi, so here's an elaboration which includes a class for Rotary Encoders, connected to any two GPIO pins on the RPi. There's also facility to read a push-button (which some encoders - including mine - feature).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/local/bin/python | |
# m0xpd | |
# shack.nasties 'at Gee Male dot com' | |
import RPi.GPIO as GPIO | |
# setup GPIO options... | |
GPIO.setmode(GPIO.BOARD) | |
GPIO.setwarnings(False) | |
class RotaryEnc: | |
'Base Class for Rotary Encoder on the RPi GPIO Pins' | |
def __init__(self,PinA,PinB,button,Position,REmin,REmax,inc): | |
self.PinA = PinA # GPIO Pin for encoder PinA | |
self.PinB = PinB # GPIO Pin for encoder PinB | |
self.button = button # GPIO Pin for encoder push button | |
self.Position = Position # encoder 'position' | |
self.min = REmin # Max value | |
self.max = REmax # Min value | |
self.inc = inc # increment | |
self.old_button = 1 # start value for previous button state | |
self.oldPinA = 1 # start value for previous PinA state | |
self.button_release = 0 # initialise outputs | |
self.button_down = 0 # initialise outputs | |
GPIO.setup(self.PinA, GPIO.IN) # setup IO bits... | |
GPIO.setup(self.PinB, GPIO.IN) # | |
GPIO.setup(self.button, GPIO.IN) # | |
# | |
def read(self): # Function to Read encoder... | |
encoderPinA=GPIO.input(self.PinA) # get inputs... | |
encoderPinB=GPIO.input(self.PinB) # | |
if encoderPinA and not self.oldPinA: # Transition on PinA? | |
if not encoderPinB: # Yes: is PinB High? | |
self.Position=self.Position+self.inc # No - so we're going clockwise | |
if self.Position > self.max: # limit maximum value | |
self.Position = self.max # | |
# | |
else: # | |
# | |
self.Position=self.Position-self.inc # Yes - so we're going anti-clockwise | |
if self.Position < self.min: # limit minimum value | |
self.Position = self.min # | |
self.oldPinA=encoderPinA # No: just save current PinA state for next time | |
# | |
def read_button(self): # Function to Read encoder button... | |
button=GPIO.input(self.button) # get input... | |
if button and not self.old_button: # 'Upward' transition on button? | |
self.button_release=1 # Yes - so set release output | |
else: # | |
self.button_release=0 # No - so clear it | |
if not button and self.old_button: # 'Downward' transition on button? | |
self.button_down = 1 # Yes - so set 'down' button | |
else: # | |
self.button_down = 0 # No - so clear it | |
self.old_button=button # Save current button state for next time | |
# ------------------------------------------------------------------------------------------------------------------- | |
# Here's a simple example of how to use the RotaryEnc Class ... | |
# | |
# Set up Rotary Encoder... | |
# format is : RotaryEnc(PinA,PinB,button,Position,REmin,REmax,inc): | |
Position = 1 # Position is also used as a variable | |
RE1 = RotaryEnc(12,16,18,Position,0,10,1) # Instatiate the encoder | |
# Main Operating Loop... | |
while True: | |
RE1.read() # Read the encoder | |
if Position <> RE1.Position: # has the position changed? | |
print RE1.Position # Yes: so print new value | |
Position = RE1.Position # and update it | |
RE1.read_button() # Read the button | |
if RE1.button_down: # Has it been pressed? | |
print 'Button Pressed' # Yes: so print a message | |
if RE1.button_release: # Has it been released | |
print 'Button Released' # Yes: so print a message |
The code works - but only just...
It is important to sample a rotary encoder at a sufficiently high sample rate to avoid aliasing which - in this case - results in missed "moves" or even moves of incorrect direction.
I cannot get the RPi to sample fast enough to guarantee that every movement is perfectly observed, whether in the "while" loop seen in the programs above, or in a tkinter ".after()" command with a delay argument of 1 millisecond. What's worse, the little graph of processor utilisation at the bottom right of the RPi desktop (like the PC's "Task Manager" or the Mac's "Activity Monitor") reveals that the processor is working hard to sample the encoder at this rate (as you can just see in the screenshot above). I don't know enough about RPi to know if an interrupt-driven approach, such as would be easy and obvious on a PIC, is possible or desirable.
Having said all that, the code above works well enough to let me add a "knob" to my simple RPi MIDI controller, making it much more user-friendly than the original mouse drag-able sliders.
Next, I hope to use the rotary encoder to control the frequency of the DDS generator module from eBay that's just arrived on my doormat - watch this space!
...-.- de m0xpd
I'm looking for some help as a raw beginner. I'm not sure if I'm in the right place.
ReplyDeleteI have a DC motor running with a small piece of Python code on the Pi. I'm using GPio pins to control the motor. The code is taken from one of the many sites that I have visited so I acknowledge the rights of the originator. The code is added below. I did make some modifications and it works.
On the shaft of the motor is as wheel with 8 slots. These interrupt a Photo diode and a photo transistor. I need some help with the code in getting this input into my Pi. I would like to count the slots after a period of time running the motor. Most of the code that I have seen is for quadrature encoders.These are a bit more complex than what I have here.
I appologise if I'm in the wrong area. Please point me in the right direction if I am.
Many thanks
George
#!/usr/bin/python
#http://www.raspberrypi.org/phpBB3/viewtopic.php?f=37&t=55288
import RPi.GPIO as gpio
import time
loop_counter = 0
try:
gpio.setmode(gpio.BOARD)
gpio.setup(7, gpio.OUT)
gpio.setup(11, gpio.OUT)
gpio.setup(13, gpio.OUT)
gpio.setup(15, gpio.OUT)
gpio.output(7, True)
gpio.output(11, True)
while loop_counter < 10:
print "Start of motor loop"
print "Motor forward for .25 second"
gpio.output(13, True)
gpio.output(15, False)
time.sleep(.5)
print "Motor reverse for 0.5 Second"
gpio.output(13, False)
gpio.output(15, True)
time.sleep(.5)
print "Motor forward for 0.15 Second"
gpio.output(13, True)
gpio.output(15, False)
time.sleep(.25)
print "Motor reverse for 0.5 Second"
gpio.output(13, False)
gpio.output(15, True)
time.sleep (.5)
#print "Motor Forward for 0.1 Second"
loop_counter = loop_counter + 1;
print "\n", loop_counter
except KeyboardInterrupt:
print "loop ending"
finally:
gpio.cleanup() # this ensures a clean exit
print "Now the Ports are all sqeeky clean"
George. I really don't know enough to help you directly on this one in the context of the R Pi - I would do this on Arduino or a PIC and have no experience of this sort on the Pi.
ReplyDeleteYou'll want to count pulses from the optical sensor. This is (as you say) a rather different task to responding to a rotary encoder (not least because you're only talking about one input, whereas the rot. enc. uses two). HOWEVER, the "bones" of a rotary encoder interface routine (specifically an interrupt-driven, pulse-counting routine rather than the naive way in which I observed single steps) would teach you what you need to know. That's where I'd look if I were you.
Good luck with your project.
Hello Sorry, you know the same application programming C?
ReplyDeleteFernando
DeleteNot sure if I understand your question / comment...
If you are asking about implementation of a simple rotary encoder read in "C", then the Arduino code for the "Occam's Microcontroller" rig includes essentially the same procedure as was expressed in this post in Python. You'll find the code on this page:
https://sites.google.com/site/occamsmicrocontroller/home/software
The Arduino code isn't pure C - but it is close enough to see what is happening.
Hi there,
ReplyDeleteThanks for your post, I was missing something quite important : POWER ! (Yeah I forgot to connect the 3V3 and I was asking myself : "But why does is this s**t not working?").
You just saved my night and my hair, congratulations !
Best