Home > Arduino > Using Rotary Encoders with Arduino

Using Rotary Encoders with Arduino

Rot enc

Introduction

There seems to be a lot of confusion among Arduino beginners about how rotary encoders work and how you best use them with Arduino.

I will try to explain a little bit and show some examples to get you started. The example circuit and the code should be enough to get you started if you don’t want to read the other mumbo jumbo.

What is a rotary encoder?

A rotary encoder is electromechanical component with a shaft. Shaft rotation is recorded and converted to electrical pulses that tell in which direction the shaft is rotating. The shaft has unlimited 360 degree rotation.

A rotary encoder can tell you:

  • That the shaft is rotating
  • How much it is rotating
  • In which direction it is rotating

A rotary encoder can NOT tell you:

  • The position or the orientation of the shaft (knobs with indicators are useless)

Protocol

I’m not going to get into the inner workings of rotary encoders but let’s mention how a simple rotary encoder commuinicate.

There are two output pins (called A and B) that are used to for reading rotation and a third pin that normally connects to GND.
When you turn the encoder, switches inside the encoder opens and closes to turn the outputs HIGH or LOW.

The rotary encoders “speak” gray code which means that the encoder pins are cycling through a predetermined pattern of HIGH and LOW signals.
When you turn the shaft of the encoder clockwise the signal on the encoder pins loops through a pattern like this:

A B
0 0
1 0
1 1
0 1

If we can detect that the pins are changing we know that the shaft is rotating. How often the pulses change tells us how fast it’s turning. How many times it has changed tells us how far it has been turned etc.
When you turn the shaft counter-clockwise it loops through the sequence backwards and that’s how we know in which direction it’s rotating.
Most shaft encoders have fixed stops or “stability positions” so that the shaft stops or clicks at every 0pen-0pen or closed-closed position or both.
Your datasheet will reveal which type you have. There will be a pulse diagram in of the pulses at clockwise operation that looks something like one of the below pictures. The dotted lines indicate shaft stops.

Stops at every closed position

Stops at both closed and open position

Bounce

Cheap encoders are known to cause a lot of contact bounce so you will need to debounce the signal from the encoders. We will use interrupts on the Arduino for detecting encoder rotation so it’s easier to debounce the encoder with hardware and get nice clean pulses than to try and debounce using software delay.

I have tried some variants and settled with 47nF ceramic capacitors and Schmitt Triggers on the outputs. I have also connected the shield on my encoders to ground through another 47nF ceramic capacitor.

Example circuit

This is how to debounce and connect an encoder to the Arduino:

Example circuit with debounced rotary encoder connected to Arduino

I connect encoder output A to D2 on the Arduino because D2 is an interrupt pin (interrupt 0) which we will use to detect rotation.
Output B can go to any digital pin. I use pin 4 in the example.

If you want to connect another encoder to the same Arduino, use pin D3 for output A on that encoder and enable interrupt 1.

I you have an Arduino Mega you can use four additional interrupts on pins D18, D19, D20, D21.

Arduino sketch

This is an example that uses one interrupt to detect rotation on a rotary encoder which only stops when it’s in closed position.
If you have an encoder that stops at both closed and open you will have to turn your encoder two clicks to register a turn. Correct this by changing the interrupt type from RISING to CHANGE sol that an interrupt is triggered on every change.

int pinA = 2; //Encoder pin A connects to interrupt 0 (D2)
int pinB = 4; //Encoder pin B connects to D4
int iValue = 0; //A variable that will be increased or decreased
                //when we turn the encoder

void setup() {
  Serial.begin(9600);
  
  pinMode(pinA, INPUT);  
  pinMode(pinB, INPUT);
  
  // Enable interrupt on encoder pin A
  // Trigger at RISING if your encoder stops (clicks) only at high pulse
  // Trigger at CHANGE if your encoder stops (clicks) at both high and low
  // positions or if it has no stops
  attachInterrupt(0, encoderClick, RISING);
}


void loop() {
  // continuously print the value to see how it changes
  Serial.println(iValue);
  delay(200);
}


void encoderClick(){
  // encoder must have turned one click because interrupt 0 was triggered
  // read value from both encoder pins
  int valA = digitalRead(pinA);
  int valB = digitalRead(pinB);
  
  // compare pins to determine in which direction encoder was turned
  if (valA != valB){
      // pinA just changed but pinB had not yet changed
      // Direction must be clockwise if A changes before B
      iValue++;
  }
  else{
      // pinA just changed and pinB had already done so.
      // Direction must be counter-clockwise if B changes before A
      iValue--;
  }
}

Download the sketch here

About these ads
Categories: Arduino
  1. Luis Cohen
    2013-01-22 at 15:21

    Thanks for the explanation. I want to use a rotary enconder to input data, visible on to a LCD. Pressing the encoder wakes up the controller, turning right incresase the counting from zero to 300, pressing it again for 2 seconds, engages a stepper motor to turn according to a preloaded program. What I’m looking for is a way to control the windage of my scope automatically. The scope’s knob rotates in 5 degrees segments (72 for one rotation). Is it possible to do it using a arduino micro pro, a stepper motor driver, a rotary encoder and a lcd?

  2. Wes
    2013-05-17 at 23:53

    Could a 3d printer be build with a continuous motion servo and a rotary encoder? If you know starting position the position forward could be calculated.

    • 2013-05-18 at 00:00

      In theory, maybe. I have no idea how well it would work in practice. I think it would be easier to achieve decent precision with steppers.

  3. Nic O Lai
    2013-09-26 at 05:29

    Great article, thanks! A few questions; Will this setup allow accurate control of a stepper motor in small increments that change direction frequently? If the Arduino Mega has the ability to manage 4 inputs, does this mean that 4 encoder signals can be fed into the Arduino to control 4 stepper motors at the same time? Is it possible to assign different movement ratios to each of the encoder-motor pairs? (example: 50 pulses on encoder 1=1 step on motor 1, and 10 pulses on encoder 2=1 step on motor 2) In my application it is important that motors move together proportionately. Is this possible? Many thanks, Nic

    • 2013-09-26 at 23:34

      I can’t tell because I don’t understand what you want to accomplish or how you expect to control four encoders at the same time. How many hands do you have?
      When you are using rotary encoders you will also have to accept that you will miss some pulses and get double pulses sometimes. You probably won’t get rid of bounce to 100%
      I don’t know how that would effect your application.

      • Nic O Lai
        2013-09-27 at 03:24

        Hi Perhof, My application resembles a pantograph with a tracing stylus on an xyz system driving 3 rotary encoders to indicate position and direction. This sylus is operated manually (with one hand). The output to 3 stepper motors carves xyz. The ability to control ratios independently on each encoder-motor pair is important. Regarding missing pulses, I suppose the system could be homed frequently if needed. If it all works well, I may attempt to add 2 additional encoders to enable the stylus to rotate from a normally vertical orientation. Can the system you describe in the article work here? Many thanks for any help you can offer, Nic

      • 2013-09-27 at 10:59

        I don’t know how well it would work as I have no experience in stepper motors and their libraries. I guess you’ll have to try it.
        I recommend that you generally keep as little code as possible in the interrupt handler when working with interrupts, just increase or decrease an integer with the required movement and then let the main loop adjust the motors in response to the integer values.

  4. 2014-05-01 at 10:58

    Thank you for posting this work and the code, too!
    I use it with some mods so to have readings only while turning the encoder.
    Now I have a question: could it be possible to ask arduino through a numeric value to turn a shaft in a specific value?? let’s say that encoder reads a full turn of this shaft cw and ccw (+360 deg & -360 deg, encoder values according to the resolution: + 650 cw & -650 ccw)… so with two relays as switches for the motor that drives the shaft to get for example the value +322… or -165… I am radio amature and I build my own rotator for a logarithmic antenna (directional antenna). I would like to drive/turn the mast at a specific possition according to the encoder readings.
    Thanks again for your help,
    Yiannis-SV2GWW.

    PS My attempt with magnetometer/compass HMC5883L for the same work was too stupid. The metall works fuled the compass and the RF from HF destroyed any magnetic field. Compass esqaped and now it is in Mykonos island for summer holidays!!

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: