![tutorial for interfacing rotary encoder with arduino](https://lastminuteengineers.com/wp-content/uploads/2019/11/tutorial-for-interfacing-rotary-encoder-with-arduino.jpg)
A rotary encoder is a type of position sensor that converts the angular position (rotation) of a knob into an output signal that is used to determine what direction the knob is being rotated.
Due to their robustness and fine digital control; they are used in many applications including robotics, CNC machines and printers.
There are two types of rotary encoder – absolute and incremental. The absolute encoder gives us the exact position of the knob in degrees while the incremental encoder reports how many increments the shaft has moved.
The rotary encoder used in this tutorial is of an incremental type.
Rotary Encoders Vs Potentiometers
Rotary Encoders are the modern digital equivalent of the potentiometer and are more versatile than a potentiometer.
They can fully rotate without end stops while a potentiometer can rotate only about 3/4 of the circle.
Potentiometers are best in situations where you need to know the exact position of the knob. However, rotary encoders are best in situations where you need to know the change in position instead of the exact position.
How Rotary Encoders Work
Inside the encoder is a slotted disk connected to the common ground pin C, and two contact pins A and B, as illustrated below.
![rotary encoder internal structure](https://lastminuteengineers.com/wp-content/uploads/arduino/rotary-encoder-internal-structure.png)
When you turn the knob, A and B come in contact with the common ground pin C, in a particular order according to the direction in which you are turning the knob.
When they come in contact with common ground they produce signals. These signals are shifted 90° out of phase with each other as one pin comes in contact before the other pin. This is called quadrature encoding.
![rotary encoder working animation.gif](https://lastminuteengineers.com/wp-content/uploads/arduino/rotary-encoder-working-animation.gif)
When you turn the knob clockwise, the A pin connects first, followed by the B pin. When you turn the knob counterclockwise, the B pin connects first, followed by the A pin.
By tracking when each pin connects to and disconnects from the ground, we can use these signal changes to determine in which direction the knob is being rotated. You can do this by simply observing the state of B when A changes state.
When the A changes state:
- if B != A, then the knob was turned clockwise.
- if B = A, then the knob was turned counterclockwise.
Rotary Encoder Pinout
The pinouts of the rotary encoder are as follows:
![rotary encoder module pinout](https://lastminuteengineers.com/wp-content/uploads/arduino/rotary-encoder-module-pinout.png)
GND is the Ground connection.
VCC is the positive supply voltage, usually 3.3 or 5 Volts.
SW is the active low push button switch output. When the knob is pushed, the voltage goes LOW.
DT (Output B) is the same as the CLK output, but it lags the CLK by a 90° phase shift. This output can be used to determine the direction of rotation.
CLK (Output A) is the primary output pulse for determining the amount of rotation. Each time the knob is rotated by one detent (click) in either direction, the ‘CLK’ output goes through one cycle of going HIGH and then LOW.
Wiring – Connecting Rotary Encoder to Arduino
Now that we know everything about the rotary encoder it is time to put it to use!
Let’s connect Rotary Encoder to Arduino. Connections are fairly simple. Start by connecting +V pin on the module to 5V on the Arduino and GND pin to ground.
Now connect the CLK and DT pins to digital pin#2 and #3 respectively. Finally, connect the SW pin to a digital pin #4.
The following illustration shows the wiring.
![wiring rotary encoder with arduino uno](https://lastminuteengineers.com/wp-content/uploads/arduino/wiring-rotary-encoder-with-arduino-uno.png)
Arduino Code – Reading Rotary Encoders
Now that you have your encoder hooked up you’ll need a sketch to make it all work.
The following sketch detects when the encoder is being rotated, determines which direction it is being rotated and whether or not the button is being pushed.
Try the sketch out; and then we will dissect it in some detail.
// Rotary Encoder Inputs
#define CLK 2
#define DT 3
#define SW 4
int counter = 0;
int currentStateCLK;
int lastStateCLK;
String currentDir ="";
unsigned long lastButtonPress = 0;
void setup() {
// Set encoder pins as inputs
pinMode(CLK,INPUT);
pinMode(DT,INPUT);
pinMode(SW, INPUT_PULLUP);
// Setup Serial Monitor
Serial.begin(9600);
// Read the initial state of CLK
lastStateCLK = digitalRead(CLK);
}
void loop() {
// Read the current state of CLK
currentStateCLK = digitalRead(CLK);
// If last and current state of CLK are different, then pulse occurred
// React to only 1 state change to avoid double count
if (currentStateCLK != lastStateCLK && currentStateCLK == 1){
// If the DT state is different than the CLK state then
// the encoder is rotating CCW so decrement
if (digitalRead(DT) != currentStateCLK) {
counter --;
currentDir ="CCW";
} else {
// Encoder is rotating CW so increment
counter ++;
currentDir ="CW";
}
Serial.print("Direction: ");
Serial.print(currentDir);
Serial.print(" | Counter: ");
Serial.println(counter);
}
// Remember last CLK state
lastStateCLK = currentStateCLK;
// Read the button state
int btnState = digitalRead(SW);
//If we detect LOW signal, button is pressed
if (btnState == LOW) {
//if 50ms have passed since last LOW pulse, it means that the
//button has been pressed, released and pressed again
if (millis() - lastButtonPress > 50) {
Serial.println("Button pressed!");
}
// Remember last button press event
lastButtonPress = millis();
}
// Put in a slight delay to help debounce the reading
delay(1);
}
If everything is fine, you should see below output on serial monitor.
![rotary encoder output on serial monitor](https://lastminuteengineers.com/wp-content/uploads/arduino/rotary-encoder-output-on-serial-monitor.png)
If the rotation being reported is the opposite of what you expect, try swapping the CLK and DT lines.
Code Explanation:
The sketch begins with the declaration of the Arduino pins to which the encoder’s CLK, DT and SW pins are connected.
#define CLK 2
#define DT 3
#define SW 4
Next, a few integers are defined. The counter
variable represents the count that will be modified each time that the knob is rotated one detent (click).
The currentStateCLK
and lastStateCLK
variables hold the state of the CLK output and are used for determining the amount of rotation.
A string called currentDir
will be used when printing the current direction of rotation on the serial monitor.
The lastButtonPress
variable is used to debounce a switch.
int counter = 0;
int currentStateCLK;
int lastStateCLK;
String currentDir ="";
unsigned long lastButtonPress = 0;
Now in the Setup section, we first define the connections to the encoder as inputs, then we enable the input pullup resistor on SW pin. We also setup the serial monitor.
At the end we read the current value of the CLK pin and store it in the lastStateCLK
variable.
pinMode(CLK,INPUT);
pinMode(DT,INPUT);
pinMode(SW, INPUT_PULLUP);
Serial.begin(9600);
lastStateCLK = digitalRead(CLK);
In the loop section, we check the CLK state again and compare it to the lastStateCLK
value. If they are different then it means that the knob has turned and a pulse has occurred. We also check if the value of currentStateCLK
is 1 in order to react to only one state change to avoid double count.
currentStateCLK = digitalRead(CLK);
if (currentStateCLK != lastStateCLK && currentStateCLK == 1){
Inside the if statement we determine the direction of rotation. To do this we simply read the DT pin on the encoder module and compare it to the current state of the CLK pin.
If they are different, it means that the knob is rotated counterclockwise. We then decrement the counter and set currentDir
to “CCW”.
If the two values are the same, it means that the knob is rotated clockwise. We then increment the counter and set currentDir
to “CW”.
if (digitalRead(DT) != currentStateCLK) {
counter --;
currentDir ="CCW";
} else {
counter ++;
currentDir ="CW";
}
We then print our results on the serial monitor.
Serial.print("Direction: ");
Serial.print(currentDir);
Serial.print(" | Counter: ");
Serial.println(counter);
Outside the if statement we update lastStateCLK
with the current state of CLK.
lastStateCLK = currentStateCLK;
Next comes the logic to read and debounce the push button switch. We first read the current button state, if it’s LOW, we wait for 50ms to debounce the push button.
If the button stays LOW for more than 50ms, we print “Button pressed!” message on the serial monitor.
int btnState = digitalRead(SW);
if (btnState == LOW) {
if (millis() - lastButtonPress > 50) {
Serial.println("Button pressed!");
}
lastButtonPress = millis();
}
Then we do it all over again.
Arduino Code – Using Interrupts
In order for rotary encoder to work, we need to continuously monitor changes in DT and CLK signals.
To determine when such changes occur, we can continuously poll them (like we did in our previous sketch). However, this is not the best solution for below reasons.
- We have to busily perform checking to see whether a value has changed. There will be a waste of cycles if signal level does not change.
- There will be latency from the time the event happens to the time when we check. If we need to react immediately, we will be delayed by this latency.
- It is possible to completely miss a signal change if the duration of the change is short.
A solution widely adopted is the use of an interrupt.
Wiring
As most Arduinos (including Arduino UNO) have only two external interrupts, we can only monitor changes in the DT and CLK signals. That’s why we removed the connection of the SW pin from the previous wiring diagram.
So now the wiring looks like this:
![control rotary encoder using interrupts with arduino uno](https://lastminuteengineers.com/wp-content/uploads/arduino/control-rotary-encoder-using-interrupts-with-arduino-uno.png)
Some boards (like the Arduino Mega 2560) have more external interrupts. If you have one of them, you can keep the connection for SW pin and extend below sketch to include code for the button.
Arduino Code
Here’s the sketch that demonstrates the use of the interrupts while reading a rotary encoder.
// Rotary Encoder Inputs
#define CLK 2
#define DT 3
int counter = 0;
int currentStateCLK;
int lastStateCLK;
String currentDir ="";
void setup() {
// Set encoder pins as inputs
pinMode(CLK,INPUT);
pinMode(DT,INPUT);
// Setup Serial Monitor
Serial.begin(9600);
// Read the initial state of CLK
lastStateCLK = digitalRead(CLK);
// Call updateEncoder() when any high/low changed seen
// on interrupt 0 (pin 2), or interrupt 1 (pin 3)
attachInterrupt(0, updateEncoder, CHANGE);
attachInterrupt(1, updateEncoder, CHANGE);
}
void loop() {
//Do some useful stuff here
}
void updateEncoder(){
// Read the current state of CLK
currentStateCLK = digitalRead(CLK);
// If last and current state of CLK are different, then pulse occurred
// React to only 1 state change to avoid double count
if (currentStateCLK != lastStateCLK && currentStateCLK == 1){
// If the DT state is different than the CLK state then
// the encoder is rotating CCW so decrement
if (digitalRead(DT) != currentStateCLK) {
counter --;
currentDir ="CCW";
} else {
// Encoder is rotating CW so increment
counter ++;
currentDir ="CW";
}
Serial.print("Direction: ");
Serial.print(currentDir);
Serial.print(" | Counter: ");
Serial.println(counter);
}
// Remember last CLK state
lastStateCLK = currentStateCLK;
}
Notice that the main loop of this program is kept empty so Arduino will be busy doing nothing.
Meanwhile, this program watches digital pin 2 (corresponds to interrupt 0) and digital pin 3 (corresponds to interrupt 1) for a change in value. In other words, it looks for a voltage change going from HIGH to LOW or LOW to HIGH, which happens when you turn the knob.
When this happens the function updateEncoder
(often called the interrupt service routine or just ISR) is called. The code within this function is executed and then the program returns back to whatever it was doing before.
Below two lines are responsible for all this. The function attachInterrupt()
tells the Arduino which pin to monitor, which ISR to execute if the interrupt is triggered and what type of trigger to look for.
attachInterrupt(0, updateEncoder, CHANGE);
attachInterrupt(1, updateEncoder, CHANGE);
Control Servo Motor with Rotary Encoder
For our next project we will use a rotary encoder to control the position of a servo motor.
This project can be very useful in many situations, for example, when you want to operate a robot arm, as it would let you precisely position the arm and its grip.
In case you are not familiar with servo motor, consider reading (at least skimming) below tutorial.
SUGGESTED READING
![Tutorial for Interfacing Servo Motor with Arduino](https://lastminuteengineers.com/wp-content/uploads/2019/10/Tutorial-for-Interfacing-Servo-Motor-with-Arduino-150x150.jpg)
Wiring
As the wiring diagram shows you’ll need a servo motor. Connect the Red wire of the servo motor to the external 5V supply, the Black/Brown wire to ground and the Orange/Yellow wire to the PWM enabled pin 9.
Of course you can use the Arduino 5V output but keep in mind that the servo can induce electrical noise onto the 5V line that the Arduino uses, which may not what you want.
Therefore it is recommended that you use an external power supply.
![wiring for controlling servo motor with rotary encoder](https://lastminuteengineers.com/wp-content/uploads/arduino/wiring-for-controlling-servo-motor-with-rotary-encoder.png)
Arduino Code
Here’s the sketch to precisely control the servo motor with the rotary encoder. Each time the knob is rotated one detent (click), the position of the servo arm will be changed by one degree.
// Include the Servo Library
#include <Servo.h>
// Rotary Encoder Inputs
#define CLK 2
#define DT 3
Servo servo;
int counter = 0;
int currentStateCLK;
int lastStateCLK;
void setup() {
// Set encoder pins as inputs
pinMode(CLK,INPUT);
pinMode(DT,INPUT);
// Setup Serial Monitor
Serial.begin(9600);
// Attach servo on pin 9 to the servo object
servo.attach(9);
servo.write(counter);
// Read the initial state of CLK
lastStateCLK = digitalRead(CLK);
}
void loop() {
// Read the current state of CLK
currentStateCLK = digitalRead(CLK);
// If last and current state of CLK are different, then pulse occurred
// React to only 1 state change to avoid double count
if (currentStateCLK != lastStateCLK && currentStateCLK == 1){
// If the DT state is different than the CLK state then
// the encoder is rotating CCW so decrement
if (digitalRead(DT) != currentStateCLK) {
counter --;
if (counter<0)
counter=0;
} else {
// Encoder is rotating CW so increment
counter ++;
if (counter>179)
counter=179;
}
// Move the servo
servo.write(counter);
Serial.print("Position: ");
Serial.println(counter);
}
// Remember last CLK state
lastStateCLK = currentStateCLK;
}
If you compare this sketch with our first one you’ll notice many similarities, except few things.
At the start we include the built-in Arduino Servo library and create a servo object to represent our servo motor.
#include <Servo.h>
Servo servo;
In the Setup, we attach the servo object to pin 9 (to which the control pin of the servo motor is connected).
servo.attach(9);
In the loop, we limit the counter to have a range 0 to 179, because a servo motor only accepts a value between this range.
if (digitalRead(DT) != currentStateCLK) {
counter --;
if (counter<0)
counter=0;
} else {
counter ++;
if (counter>179)
counter=179;
}
Finally the counter value is used to position the servo motor.
servo.write(counter);