How Rotary Encoder Works With Arduino!

42K19213

Intro: How Rotary Encoder Works With Arduino!

A rotary encoder is a great input device for any project such as a 3D printer's menu. So in this tutorial, we will learn how a rotary encoder works and how to use it with Arduino.

So let's get Started!



STEP 1: Watch the Video !

If you don't want to read all the stuff you can watch my video it will be much easier to understand.

STEP 2: Overview

A rotary encoder, also called a shaft encoder, is an electro-mechanical device that converts the angular position or motion of a shaft or axle to an analog or digital output signals.

There are two main types of rotary encoder

1) Absolute - The output of an absolute encoder indicates the current shaft position, making it an angle transducer.

2) Incremental - The output of an incremental encoder provides information about the motion of the shaft, which typically is processed elsewhere into information such as position, speed, and distance.

Note: One we use in our project is Incremental type so we will focus on that.

STEP 3: What Is Incremental Rotary Encoder ?

An incremental encoder will immediately report changes in position(Clockwise & Counterclockwise), which is an essential capability in some applications. However, it does not report or keeps track of absolute position. As a result, the mechanical system monitored by an incremental encoder may have to be moved to a fixed reference point to initialize the position measurement.

STEP 4: Construction & Working

One I have comes in this breakout board which already have the required pull-up resistors on it.

It has five pins - Clock, Data, Switch, Vcc, GND

Construction:

The knob which we rotate is internally connected to the disk(Pic - 2). If we rotate clockwise or counter-clockwise it will move accordingly. The grey portion is GND and Golden contact points are connected to Vcc. There are two contact point placed at a specific distance apart which are nothing but our CLOCK and DATA line.

Working:

As we rotate our encoder, the two output will change depending upon the position of the encoder. Which will generate two trains of pulses.

If you look closely those two signals will be 90 degrees out of the phase. If the encoder rotates clockwise then CLOCK will lead and if the encoder rotates counter-clockwise then DATA will lead.

And taking a look at state change for clockwise two signals will have opposite values and for counter-clockwise same values.

And now if we programme our Arduino accordingly we can get it to work with our project.

STEP 5: Programming

Example 1: (Counter.ino)

Now in this example, CLK is connected to pin 3, DT to 4 and SW to 5, then we have some variables to store data. In void setup section CLK, DT and SW are set as input and then started the serial command and here stored the current position in the last state

In void loop section first, we read the current state of the clock and using it else check it if it has changed.

If the state != last state means it has changed the position. And if data != state then CW else CCW which will increase or decrease counter accordingly.

Then using serial print printed the counter variable. And if the switch is pressed counter will reset to 0 and print it, in the end, put the state into the last state

Example 2: (LEDBrightness.ino)

In this example, I have put the counter value in analog write function and used constraint function to limit the rage between 0-255 in order for it to work

Note: it's better if you open the code in Arduino IDE and read it once

STEP 6: Thank You !

Check out JLCPCB
$2 PCB Prototype (10pcs,10*10cm): https://jlcpcb.com

That's it if you like my work

Feel free to check out my YouTube channel for more awesome stuff: https://www.youtube.com/c/Nematics_lab

You can also follow me on Facebook, Twitter etc for upcoming projects

https://www.facebook.com/NematicsLab/

https://www.instagram.com/nematic_yt/

https://twitter.com/Nematic_YT

9 Comments

I liked your explanation of how the encoder works, thankyou. My question is... Why is

"Laststate = digitalRead(CLK);" pin read, located in "setup" and your other variables wait until the loop, to do their first read?

An important tip: Bouncing.

Rotary encoders usually have the same issues than switch buttons having bouncing due to how they are made (unless they are magnetic/optical). I don't know if the Arduino library cares about it, but it's a must to remove any bouncing in contact (also the knob switch if the rotary encoder has one).

For a single rotary encoder, using an external RC filter is a good option to free the MCU from long delays or using interrupts to manage the bouncing (unless you make a more advanced non-blocking debouncing management, usually involving interrutps and timers).

Otherwise you cannot trust the readings and much less hope a correct behaviour of the rotary encoder, because the bouncing could be interpreted as correct transitions between detents.

Anyway there are some software solutions normally using finite state machines that have built in an intrinsic debouncing, regardless of the sampling speed of the rotary encoder.

And finally, the rotation speed is important, because faster rotations could miss detents depending on the quality of the rotary encoder (and once again, depending of the software).

You're right, bouncing can be a real issue, particularly with high speeds. I use the same software solution I developped hundred years ago. I decrement a counter while a state exists until it reaches 0, if the state change I restore the counter value. It's efficient and works with any kind of contact, I used it with 6800, 68000, 86 series and now Arduino. ( I told you hundred years ago !)

Me too. Not a hundred years ago but, say 30years ago. I used quite a similar approach as you, but with the ST62xx controller, written in assambler. Now I have translated it to Arduino for my own purposes. Look at my instr. "Hack the delay()". There in my subroutine I "build" up a "timegenerator", (not by using them "timer interrupts", I'll do that later on), but simply grabbing the millis() and use that to "build" up timebases to "what so ewer needed". It's easyly going up to minutes, 10's of minutes, hour's and so on to infinity :)

Feel free to ask me about my time-generator: kimmo.selin@pp.inet.fi. You may allso check out my other developements that are not instr. related, at: www.teksel.net

P.S. I could grab the microSec() as well, but for the moment I don't have a need of those speeds.

WOW, great instructions, thank you.

Actually, bouncing isn't a problem with the algorithm that Nemeen has chosen.

The wrong way to do it is to count on (say) the rising edge of the Clock: if Data is high, count up; if Data is low, count down. If the switch bounces then you'll get multiple counts.

The right way is to count on both the up and down edges of the Clock. If Data != Clock then count up, otherwise count down. Bouncing counts both up and down so cancels out.

However AFAIK, you really must sample both Clock and Data at the same instant. In Nemeen's code, the Clock is sampled then the Data several instructions later. That's wrong. The Data may have changed.

Instead, one should read a whole byte, e.g.
int i = PORTD;
then look at the individual bits of i.

I suspect it may be a bad idea to add RC filtering to the inputs - the filters may have different time-constants. You need to know what's happening in a single instant.

Peter

Cool Project, Im jelous of your coding skills :-)

Do you know how to use a motor with an encoder on the back?

thanks

An incremental encoder is an incremental encoder, it gives you direction and speed of the rotation. It' quite easy to use.