Arduino Nano ESP32 - Rotary Encoder

In this guide, you'll learn how to use the rotary encoder with an Arduino Nano ESP32. Here's what we'll learn:

Hardware Preparation

1×Arduino Nano ESP32
1×USB Cable Type-C
1×Rotary Encoder
1×Breadboard
1×Jumper Wires
1×(Recommended) Screw Terminal Adapter for Arduino Nano

Or you can buy the following sensor kit:

1×DIYables Sensor Kit 30 types, 69 units
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. We appreciate your support.

Overview of Rotary Encoder

A spinning knob, like on a radio, can send signals that turn into electricity. It helps us know how much it turned and where it's put. There are two main types:

  • Incremental encoder: This one uses quick signals to measure how much something changed its position.
  • Absolute encoder: This type gives a secret code for each spot, which helps us find where something is, even if the power goes away.

This lesson is mainly about the first type, 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: comes from the encoder's button. It’s normally open. When we add a pull-up resistor to this pin, the SW pin will be HIGH when the knob isn't pushed and LOW when it's pushed.
  • 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 may confuse the rotary encoder with the potentiometer. but they are distinct components. Here's a comparison between them:

  • Rotary encoder is like the modern version of potentiometer, but they can do more things.
  • Rotary encoder can spin around in a full circle without stopping, while potentiometer can only turn about three-quarters of the circle.
  • Rotary encoder outputs pulses, while potentiometer outputs the analog voltage.
  • Rotary encoder is handy when you just need to figure out how much the knob has moved, not exactly where it is. Potentiometer is useful when you really need to know exactly where a knob is.

How Rotary Encoder Works

rotary encoder output

Inside the encoder, there's a little disc with slots that's connected to a pin called C, which acts like a shared ground. You've got two more pins, A and B.

  • When you turn the knob, pins A and B touch that shared ground pin C, but the order in which they touch it depends on which way you're turning the knob (either clockwise or counterclockwise).
  • These touches create two signals. They're a bit different in timing because one pin touches the ground before the other. Those signals are out of sync by 90 degrees, which is called quadrature encoding.
  • If you turn the knob clockwise, pin A touches the ground before pin B does. But if you go counterclockwise, pin B hits the ground first before pin A.
  • By checking 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 HIGH, the knob is turned counter-clockwise.
  • If B is LOW, the knob is turned clockwise.

※ 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

  • Arduino Nano ESP32 reads the signal from CLK pin
  • If the state changes from LOW to HIGH, then Arduino Nano ESP32 reads the state of the DT pin.
    • If the DT pin is HIGH, the knob is turned in the counter-clockwise direction, Arduino Nano ESP32 increases the counter by 1
    • If the DT pin is LOW, the knob is turned in the clockwise direction, Arduino Nano ESP32 decreases the counter by 1

Wiring Diagram

The wiring diagram between Arduino Nano ESP32 and rotary encoder

This image is created using Fritzing. Click to enlarge image

Arduino Nano ESP32 Code – Rotary Encoder

The below Arduino Nano ESP32 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 ESP32 code was developed by newbiely.com * * This Arduino Nano ESP32 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-nano-esp32/arduino-nano-esp32-rotary-encoder */ #include <ezButton.h> // The library to use for SW pin #define CLK_PIN D2 // The Arduino Nano ESP32 pin D2 connected to the rotary encoder's CLK pin #define DT_PIN D3 // The Arduino Nano ESP32 pin D3 connected to the rotary encoder's DT pin #define SW_PIN D4 // The Arduino Nano ESP32 pin D4 connected to the rotary encoder's SW pin #define DIRECTION_CW 0 // clockwise direction #define DIRECTION_CCW 1 // counter-clockwise direction int counter = 0; int direction = DIRECTION_CW; int CLK_state; int prev_CLK_state; ezButton button(SW_PIN); // create ezButton object for pin 7; 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 counter-clockwise 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("Rotary Encoder:: direction: "); if (direction == DIRECTION_CW) Serial.print("Clockwise"); else Serial.print("Counter-clockwise"); Serial.print(" - count: "); 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

If this is the first time you use ESP32, see how to setup environment for Arduino Nano ESP32 on Arduino IDE.

  • Install ezButton library on Arduino IDE.
  • Copy the above code and open with Arduino IDE
  • Click Upload button on Arduino IDE to upload code to Arduino Nano ESP32
  • Turn the knob in clockwise, then anticlockwise
  • Press the knob
  • Check out the result on the Serial Monitor.
COM6
Send
Rotary Encoder:: direction: CLOCKWISE - count: 1 Rotary Encoder:: direction: CLOCKWISE - count: 2 Rotary Encoder:: direction: CLOCKWISE - count: 3 Rotary Encoder:: direction: CLOCKWISE - count: 4 Rotary Encoder:: direction: CLOCKWISE - count: 5 Rotary Encoder:: direction: ANTICLOCKWISE - count: 4 Rotary Encoder:: direction: ANTICLOCKWISE - count: 3 Rotary Encoder:: direction: ANTICLOCKWISE - count: 2 Rotary Encoder:: direction: ANTICLOCKWISE - count: 1 Rotary Encoder:: direction: ANTICLOCKWISE - count: 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 ESP32 Code – Rotary Encoder with Interrupt

In the previous code, using polling to continuously check the pin's state can waste Arduino Nano ESP32 resources and lead to missed counts if other code execution is slow.

An effective solution is to utilize the interrupt, which remove the necessity for polling. This enables the Arduino Nano ESP32 to perform other tasks without missing the counts. The below is the Arduino Nano ESP32 code that uses the interrupt to read the direction and position from the rotary encoder.

/* * This Arduino Nano ESP32 code was developed by newbiely.com * * This Arduino Nano ESP32 code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/arduino-nano-esp32/arduino-nano-esp32-rotary-encoder */ #include <ezButton.h> // The library to use for SW pin #define CLK_PIN D2 // The Arduino Nano ESP32 pin D2 connected to the rotary encoder's CLK pin #define DT_PIN D3 // The Arduino Nano ESP32 pin D3 connected to the rotary encoder's DT pin #define SW_PIN D4 // The Arduino Nano ESP32 pin D4 connected to the rotary encoder's SW pin #define DIRECTION_CW 0 // clockwise direction #define DIRECTION_CCW 1 // counter-clockwise direction volatile int counter = 0; volatile int direction = DIRECTION_CW; volatile unsigned long last_time; // for debouncing int prev_counter; ezButton button(SW_PIN); // create ezButton object for pin 7; void IRAM_ATTR ISR_encoder() { if ((millis() - last_time) < 50) // debounce time is 50ms return; if (digitalRead(DT_PIN) == HIGH) { // The encoder is rotating in counter-clockwise 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 ISR_encoder() when CLK pin changes from LOW to HIGH attachInterrupt(digitalPinToInterrupt(CLK_PIN), ISR_encoder, RISING); } void loop() { button.loop(); // MUST call the loop() function first if (prev_counter != counter) { Serial.print("Rotary Encoder:: direction: "); if (direction == DIRECTION_CW) Serial.print("CLOCKWISE"); else Serial.print("ANTICLOCKWISE"); Serial.print(" - count: "); Serial.println(counter); prev_counter = counter; } if (button.isPressed()) { Serial.println("The button is pressed"); } // TO DO: your other work here }

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

※ NOTE THAT:

  • 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 ESP32 Rotary Encoder Application

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

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

Video Tutorial

Function References

Learn More