Arduino Nano - Rotary Encoder

This tutorial instructs you how to use the rotary encoder with Arduino Nano. Here's what we'll cover:

Hardware Preparation

1×Arduino Nano
1×USB A to Mini-B USB cable
1×Rotary Encoder
1×Jumper Wires
1×Breadboard
1×(Recommended) Screw Terminal Expansion Board for Arduino Nano
1×(Recommended) Breakout Expansion Board for Arduino Nano
1×(Recommended) Power Splitter For Arduino Nano

Or you can buy the following sensor kits:

1×DIYables Sensor Kit (30 sensors/displays)
1×DIYables Sensor Kit (18 sensors/displays)
Disclosure: Some of the links provided in this section are Amazon affiliate links. We may receive a commission for any purchases made through these links at no additional cost to you.
Additionally, some of these links are for products from our own brand, DIYables.

Overview of Rotary Encoder

A spinning knob called a rotary encoder can change twists into a signal. It shows how much something turned and where it is. There are two main kinds:

  • Incremental encoder: It uses fast signals to see how much something moved.
  • Absolute encoder: This type gives a unique code for each spot, which helps figure out exactly where something is, even if the power goes out.

This guide is all about the incremental encoder.

Rotary Encoder Module Pinout

rotary encoder pinout

A rotary encoder module has 4 pins:

  • CLK pin (Output A): is the main pulse that tells us how much rotation has occurred. Whenever you turn the knob by one detent (click) in either direction, the CLK pin outputs a signal that is a completes a full cycle (LOW HIGH LOW).
  • DT pin (Output B): acts like the CLK pin but outputs a signal lags behind CLK signal by 90 degrees. It helps us figure out the direction of rotation (clockwise or anticlockwise).
  • SW pin: is the output from the pushbutton inside the encoder. It’s normally open. If we use a pull-up resistor in this pin, the SW pin will be HIGH when the knob is not pressed, and LOW when it is pressed.
  • VCC pin (+): needs to be connected to VCC (between 3.3 and 5 volts)
  • GND pin: needs to be connected to GND (0V)

Rotary Encoder vs Potentiometer

You might confuse the rotary encoder with the potentiometer, but these are distinct parts. Here's a comparison between them:

  • The rotary encoder can be thought of as the modern version of a potentiometer, but it offers more capabilities.
  • The rotary encoder can spin all the way around without stopping, while the potentiometer can only rotate about three-quarters of the way.
  • The rotary encoder gives out pulses, whereas the potentiometer produces analog voltage.
  • When you only need to know how much the knob has moved without pinpointing its exact position, the rotary encoder is convenient. On the other hand, the potentiometer is valuable when you require precise knowledge of a knob's position.

How Rotary Encoder Works

rotary encoder output

Inside the encoder, there's a disc with slots connected to a pin called C, which is like a shared ground. There are two more pins, A and B.

  • When you twist the knob, pins A and B touch the shared ground pin C, but in a certain order depending on which way you turn the knob (clockwise or anticlockwise).
  • These touches create two signals. They're a bit different in timing because one pin touches the ground before the other. Two signals are 90 degrees out of sync with each other. This is called quadrature encoding.
  • When you turn the knob in clockwise direction, pin A touches the ground before pin B. When you turn the knob to the counterclockwise direction, pin B touches the ground before pin A.
  • By monitoring when each pin touches or leaves the ground, we can figure out which way the knob is turning. We do this by checking what happens to pin B when pin A changes.
How rotary encoder works

When A changes states from LOW to HIGH:

  • If B is LOW, the knob is turned clockwise.
  • If B is HIGH, the knob is turned anticlockwise.

※ NOTE THAT:

Pin A and B are connected to CLK and DT pins. However, depending on the manufacturers, the order may be different. The codes provided below are tested with the rotary encoder from DIYables

How To Program For Rotary Encoder

  • Check the signal from CLK pin
  • If the state changes from LOW to HIGH, check the state of the DT pin.
    • If the state of the DT pin is HIGH, the knob is turned in the anticlockwise direction, increase the counter by 1
    • If the state of the DT pin is LOW, the knob is turned in the clockwise direction, decrease the counter by 1

Wiring Diagram

The wiring diagram between Arduino Nano and rotary encoder

This image is created using Fritzing. Click to enlarge image

Arduino Nano Code – Rotary Encoder

The below Arduino Nano code does:

  • Detects the direction and amount of rotation of the encoder.
    • If detecting the knob turned by one detent (click) in clockwise direction, increase the counter by one.
    • If detecting the knob turned by one detent (click) in anticlockwise direction, decrease the counter by one.
  • Detects if the button is pressed.
/* * This Arduino Nano code was developed by newbiely.com * * This Arduino Nano code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-nano/arduino-nano-rotary-encoder */ #include <ezButton.h> // The library to use for SW pin #define CLK_PIN 2 #define DT_PIN 3 #define SW_PIN 4 #define DIRECTION_CW 0 // clockwise direction #define DIRECTION_CCW 1 // anticlockwise direction int counter = 0; int direction = DIRECTION_CW; int CLK_state; int prev_CLK_state; ezButton button(SW_PIN); void setup() { Serial.begin(9600); // Configure encoder pins as inputs pinMode(CLK_PIN, INPUT); pinMode(DT_PIN, INPUT); button.setDebounceTime(50); // set debounce time to 50 milliseconds // read the initial state of the rotary encoder's CLK pin prev_CLK_state = digitalRead(CLK_PIN); } void loop() { button.loop(); // MUST call the loop() function first // read the current state of the rotary encoder's CLK pin CLK_state = digitalRead(CLK_PIN); // If the state of CLK is changed, then pulse occurred // React to only the rising edge (from LOW to HIGH) to avoid double count if (CLK_state != prev_CLK_state && CLK_state == HIGH) { // if the DT state is HIGH // The encoder is rotating in anticlockwise direction => decrease the counter if (digitalRead(DT_PIN) == HIGH) { counter--; direction = DIRECTION_CCW; } else { // The encoder is rotating in clockwise direction => increase the counter counter++; direction = DIRECTION_CW; } Serial.print("Twisted direction: "); if (direction == DIRECTION_CW) Serial.print("CLOCKWISE"); else Serial.print("ANTICLOCKWISE"); Serial.print(" - counter: "); Serial.println(counter); } // save last CLK state prev_CLK_state = CLK_state; if (button.isPressed()) { Serial.println("The button is pressed"); } }

To simplify the code for button debouncing, the ezButton library is used.

Detailed Instructions

  • Install ezButton library on Arduino IDE. See How To
  • Copy the above code and open with Arduino IDE
  • Click Upload button on Arduino IDE to upload code to Arduino Nano
  • Turn the knob in clockwise, then anticlockwise
  • Press the knob
  • Check out the result on the Serial Monitor.
COM6
Send
Twisted direction: CLOCKWISE - counter: 1 Twisted direction: CLOCKWISE - counter: 2 Twisted direction: CLOCKWISE - counter: 3 Twisted direction: CLOCKWISE - counter: 4 Twisted direction: CLOCKWISE - counter: 5 Twisted direction: ANTICLOCKWISE - counter: 4 Twisted direction: ANTICLOCKWISE - counter: 3 Twisted direction: ANTICLOCKWISE - counter: 2 Twisted direction: ANTICLOCKWISE - counter: 1 Twisted direction: ANTICLOCKWISE - counter: 0 The button is pressed
Autoscroll Show timestamp
Clear output
9600 baud  
Newline  

Code Explanation

Check out the line-by-line comments in the code

Arduino Nano Code – Rotary Encoder with Interrupt

In the previous example code, we use the polling method, which continuously check the pin's state. This has two disadvantages:

  • Waste Arduino Nano resource
  • Some counter may be missed if another code takes long time to excecute.

One approach to handle this is by using interrupts. Interrupts eliminate the need for constant checking of a particular event. This allows the Arduino Nano to carry out other tasks without overlooking an event.

Here’s an example of how to read a rotary encoder with interrupts.

/* * This Arduino Nano code was developed by newbiely.com * * This Arduino Nano code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-nano/arduino-nano-rotary-encoder */ #include <ezButton.h> // The library to use for SW pin #define CLK_PIN 2 // The Arduino Nano pin D2 connect to the pin CLK of the rotary encoder #define DT_PIN 3 // The Arduino Nano pin D3 connect to the pin DT of the rotary encoder #define SW_PIN 4 // The Arduino Nano pin D4 connect to the pin SW of the rotary encoder #define DIRECTION_CW 0 // clockwise direction #define DIRECTION_CCW 1 // anticlockwise direction volatile int counter = 0; volatile int direction = DIRECTION_CW; volatile unsigned long last_time; // for debouncing int prev_counter; ezButton button(SW_PIN); void INTERRUPT_handler() { if ((millis() - last_time) < 50) // debounce time is 10ms return; if (digitalRead(DT_PIN) == HIGH) { // The encoder is rotating in anticlockwise direction => decrease the counter counter--; direction = DIRECTION_CCW; } else { // The encoder is rotating in clockwise direction => increase the counter counter++; direction = DIRECTION_CW; } last_time = millis(); } void setup() { Serial.begin(9600); // Configure encoder pins as inputs pinMode(CLK_PIN, INPUT); pinMode(DT_PIN, INPUT); button.setDebounceTime(50); // set debounce time to 50 milliseconds // use interrupt for CLK pin is enough // call INTERRUPT_handler() when CLK pin changes from LOW to HIGH attachInterrupt(digitalPinToInterrupt(CLK_PIN), INTERRUPT_handler, RISING); } void loop() { button.loop(); // MUST call the loop() function first if (prev_counter != counter) { Serial.print("Twisted direction: "); if (direction == DIRECTION_CW) Serial.print("CLOCKWISE"); else Serial.print("ANTICLOCKWISE"); Serial.print(" - counter: "); Serial.println(counter); prev_counter = counter; } if (button.isPressed()) { Serial.println("The button is pressed"); } // TO DO: your other work here }

Now, As you twist the knob, you'll notice information appearing on the Serial Monitor, much like what you saw in the earlier code.

※ NOTE THAT:

  • If you use the interrupt, you need to connect the encoder's CLK pin to an Arduino Nano pin that can handle interrupts. But remember, not all Arduino Nano pins can do this. For example, on the Arduino Nano, only pins 2 and 3 can work with interrupts.
  • You might come across tutorials on other websites that use two interrupts for a single encoder, but this is unnecessary and wasteful. Just one interrupt is sufficient.
  • It's important to use the volatile keyword for global variables used in the interrupt. Neglecting this could lead to unexpected issues.
  • Keep the code within the interrupt as straightforward as you can. Avoid using Serial.print() or Serial.println() inside the interrupt.

Arduino Nano Rotary Encoder Application

With Rotary Encoder, we can do the following applications but not limit:

  • Arduino Nano - Rotary Encoder controls Position of Sevo Motor
  • Arduino Nano - Rotary Encoder controls Brightness of LED
  • Arduino Nano - Rotary Encoder controls Speed of Stepper Motor

Video Tutorial

Function References

Learn More

※ OUR MESSAGES

  • As freelancers, We are AVAILABLE for HIRE. See how to outsource your project to us
  • Please feel free to share the link of this tutorial. However, Please do not use our content on any other websites. We invested a lot of effort and time to create the content, please respect our work!