Go Down

Topic: Rotary Encoder Tutorial port to Arduino Uno (Read 4141 times) previous topic - next topic


I'm using this tutorial to try to program my Arduino Uno to read a rotary encoder: http://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino

The difference is he is on a Atmega168 platform where the Uno is Atmega328. I have the A side of my encoder in A5 and the B side in A4, but have tried them reversed too. I've adjusted the code thusly:

#define ENC_A A5
#define ENC_B A4

I haven't made any changes in the read_encoder function though. I suspect the hex values should be different?

At this poing I get the following output:

Counter value: 0
Counter value: 1

Then nothing else, no matter how I turn the knob. Any suggestions? Thanks,



Code: [Select]

old_AB |= ( ENC_PORT & 0x03 );  //add current state

One thing they forgot to mention is that the two pins MUST be the bottom two bits of the port.

Your choices are:
PIND 0,1 (Serial port, probably not a good choice)
PINB 8,9  (Probably a good choice)
PINC A0,A1  (The pins used in the example)

Alternatively you can select other pairs of adjacent pins with a shift before the mask:

For 2,3 change the code to:
Code: [Select]

old_AB |= ( (PIND >> 2) & 0x03 );  //add current state

For 3,4 change to
Code: [Select]

old_AB |= ( (PIND >> 3) & 0x03 );  //add current state

For A4,A5 change to:
Code: [Select]

old_AB |= ( (PINC >> 4) & 0x03 );  //add current state

Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp


Thanks. I've still got dramas though. I've decided to go with A0 and A1. At the moment though I can only ever get the encoder to change values if I'm touching the metal housing with my hand. I'm tried it with 5V common and swapping A and B to see if I've got them right.

Should there be pullup resistors in the circuit(I saw them mentioned in the tutorial, but no details). Also should the common be 5V or GND? Thanks,



For an optical encoder you would have four pins:  +5, Ground, A and B.

Is this a mechanical encoder with only three terminals?  In that case you could connect the common to Ground and use pull-up resistors (either the built-in ones or external fro the input pin to +5).  Alternatively you can connect the common to +5 and use pull-dowm resistors from the input pins to Ground.  Without pull-up or pull-down resistors the switches are switching between +5 and floating or Ground and floating.  When left floating the input could read HIGH or LOW randomly.
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp


Yeah it's a mechanical encoder, specifically this one: http://www.sparkfun.com/products/10596

I guess I'm a bit unclear on the idea of pullup and pulldown resistors and the need for them. I don't quite get why I/O lines float, or why it's suggested to use 220k resistors for such purpose in some of the tutorials. Some tutorials don't even seem to use them. Can you point me to a good reference or what I should be looking up? Thanks,



Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp


As per your suggestion, I was trying to use pins 10 &11 with the following change: "old_AB |= ( (PIND >> 2) & 0x03 );"
but had no luck. I am still only able to read pin 1 and 2 on all 3 ports, so I was wondering what other change might need to be made?
THanks in advance, this was really helpful thread

Code: [Select]

#define ENC_A 10
#define ENC_B 11

//see the website for changing this

void setup()
  /* Setup encoder pins as inputs */
  pinMode(ENC_A, INPUT);
  digitalWrite(ENC_A, HIGH);
  pinMode(ENC_B, INPUT);
  digitalWrite(ENC_B, HIGH);
  Serial.begin (115200);

void loop()
static uint8_t counter = 0;      //this variable will be changed by encoder input
int8_t tmpdata;
  tmpdata = read_encoder();
  if( tmpdata ) {
    Serial.print("Counter value: ");
    Serial.println(counter, DEC);
    counter += tmpdata;

/* returns change in encoder state (-1,0,1) */
int8_t read_encoder()
  static int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
  static uint8_t old_AB = 0;
  old_AB <<= 2;                   //remember previous state
old_AB |= ( (PIND >> 2) & 0x03 );  //add current state
uint8_t new_AB=old_AB >> 4;
  return ( enc_states[( new_AB & 0x0f )]);


Here is a excerpt of a program/project that used the sparkfun encoder. Should be easy to convert to your needs.
Code: [Select]
const int encButton = 5;                         //Rotary Encoder button pin (11)
const int encoder0PinA = 6;                      //Rotary Encoder A pin (12)
const int encoder0PinB = 7;                      //Rotary Encoder B pin (13)
const long debounceDelay = 30;                   //Button debounce time

// Button = pin number to read
int checkButton(int Button) {
  int buttonState = digitalRead(Button);          // Read button
  if (buttonState == HIGH) {                      // If button pressed then wait a bit to allow for debounce
    long Time = millis();
    while ((millis() - Time) < debounceDelay) {   
    buttonState = digitalRead(Button);            // Read button again
    return buttonState;                           // Return button state
  else {                                          //Button not pressed so no need to wait for bebounce
    return LOW;

int readEncoder() {
  static int encoder0PinALast = LOW;
  int eDir = 0;
  int n = digitalRead(encoder0PinA);
  if ((encoder0PinALast == LOW) && (n == HIGH)) {
    if (digitalRead(encoder0PinB) == LOW) {
      eDir = -1;
    else {
      eDir = 1;
  encoder0PinALast = n;
  return eDir;
Don't PM me for help as I will ignore it.



There are more portable ways to do it, particularly in arduino:

Code: [Select]

#define PIN_A  5  //encoder pin a
#define PIN_B 6 //encoder pin b

old_AB |= ((digitalRead(PIN_B))?(1<<1):0) | ((digitalRead(PIN_A)?(1<<0):0);  //( ENC_PORT & 0x03 );  //add current state

When you move pins, you just need to redefine them and compile.


As per your suggestion, I was trying to use pins 10 &11 with the following change: "old_AB |= ( (PIND >> 2) & 0x03 );" but had no luck.
Code: [Select]


old_AB |= ( (PIND >> 2) & 0x03 );  //add current state

You have to use the PINB register for pins 10 and 11.  Try it this way:
Code: [Select]


old_AB |= ( (ENC_PORT >> 2) & 0x03 );  //add current state
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

Go Up

Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

via Egeo 16
Torino, 10131