logo elektroda
logo elektroda
X
logo elektroda

How to use a pulser (pulse encoder) connected to an Arduino

ghost666  20 19419 Cool? (+14)
📢 Listen (AI):

TL;DR

  • A rotary incremental encoder (pulser) connects to an Arduino for menu control, counting rotations and using the built-in push switch for reset.
  • The sketch reads Clock and Data phase shifts to detect clockwise and counterclockwise turns, then updates a counter or PWM LED brightness.
  • The example wiring uses clock pin 3, data pin 4, and switch pin 5, and the PWM value is limited to 0–255.
  • The button resets the counter, but the encoder does not track absolute position, so some uses need a known starting point.
Generated by the language model.
Rotary encoder - pulser - is an excellent input device for controlling e.g. menus in the device interface. In the following tutorial, we will describe how to use a typical pulser in conjunction with the Arduino module.

Step 1. Watch the movie

If you do not want to read the entire text below, and the English language is not a problem for you, watch the video below - it will certainly help you understand how to connect the pulser and program the Arduino so that the whole thing works together.





Step 2. Introduction

Such an encoder is an electromechanical device that translates the angular position or rotation of an axis into a digital or analog output signal. There are two types of rotary encoders:

1) Absolute - The output state of this encoder corresponds exactly to the absolute position of the axis. Thus it is an angle converter.
2) Incremental - the signals at the output of this encoder correspond to the axis movements, so the position calculated on their basis is relative. Processing these signals into data on position, rotational speed, etc. is always carried out remotely.

Step 3. What is an incremental rotary encoder

The incremental encoder gives information about changing the position and its direction (clockwise and counterclockwise rotation). In many applications this functionality is essential, however, this element does not track the absolute position. Therefore, in some encoder applications, it may be necessary to initialize the encoder by moving it to a known, fixed position.

Step 4. Construction and principle of operation

An exemplary encoder that we will use in the tutorial has all the necessary resistors that pull the I / O lines. This element has five output pins:

Clock, Data, Switch, Vcc, GND.

Construction

The knob we turn is internally connected to the disk inside the encoder. It will move clockwise or counterclockwise, just like the knob that the user turns. On the surface of this disk there are fields connected to the power line. The remainder of the disk is at ground potential. Above the disk there are two contacts shifted relative to each other by a certain distance - these are the outputs of the Clock and Data.

Principle of operation

When we turn the encoder, the state of the outputs will change with the position of the disk in the encoder. In such a situation, we can observe sequences of impulses on the Clock and Data outputs.

If we look at these impulses, we can see that they are out of phase by 90 degrees. If the encoder turns clockwise, the clock output pulses will be first, and if the encoder turns counter-clockwise, the first pulse will be shown at the data output.

By analyzing the state change on these two outputs, you can immediately judge which direction the encoder knob is turning. Now let's try to implement this analysis programmatically in Arduino.

Step 5. Software

Example 1: (Counter.ino)

Code: C / C++
Log in, to see the code


In this example the clock pin is connected to pin 3 and the data pin is connected to pin 4. We have connected a switch to pin 5 which activates when we press the encoder.

In the above program, we first defined a series of variables and set the pins to which the encoder was connected as input pins. Finally, a serial port was launched, which we will use for communication and data transmission.

Then the main program loop is started. This is where we read the state of the encoder inputs. The current state is written to the variable state. If state! = State in any iteration, it means the encoder has turned. Then if data! = State it means the encoder turned clockwise and vice versa if the variables are equal. These encoder pulses will increment and decrement the counter accordingly.

The current counter value is then transferred via the serial interface. If the button is pressed, the counter will be reset and the new value will also be sent via the serial port.

At the end of the loop, the new state is written to the variable state and the cycle can start all over again.

Example 2: (LEDBrightness.ino)

Code: C / C++
Log in, to see the code


In this case, being a modification of the first program, the counter value is used to control the brightness of the LED via PWM. The counter value is directed to the analogWrite function instead of the UART interface. You should only limit the value of the counter to a value from 0 to 255, because such values are taken by the variable controlling the PWM filling.

And that's all you need to easily handle the rotary encoder - the pulser in our device. Do you use such an element in your projects? Show off your implementations and devices, not necessarily based on Arduino modules.

Source: https://www.instructables.com/id/How-to-Use-an-Rotary-Encoder-With-Arduino/

About Author
ghost666
ghost666 wrote 11961 posts with rating 10262 , helped 157 times. Live in city Warszawa. Been with us since 2003 year.

Comments

noel200 24 Jul 2018 06:08

I used the pulser several times. A graceful device. Small, and several functions can be realized on it. I only use the rotary.h library [Read more]

Slawek K. 24 Jul 2018 06:42

With the encoder, the interrupt reading definitely stops, with the extended code in the polling mode (as above) it simply stops working. Greetings [Read more]

Anonymous 24 Jul 2018 08:18

The examples from the first post should be moved to the "How not to write programs" section. The program will work relatively correctly at a low uC clock frequency (no elimination of contact tremors) or... [Read more]

Paweł Es. 24 Jul 2018 12:46

Incremental encoder, this is an incremental encoder in Polish, what did everyone insist on this encoder type? The original text is written in Polish as the teacher speaks English ;) , besides, there... [Read more]

Anonymous 24 Jul 2018 13:43

remote controller -> remote. - The manipulator's clap - ??? - Male overhang - ??? - Night overhang - ??? [Read more]

Paweł Es. 24 Jul 2018 14:17

Double clap, it's probably only with Jan Bielecki ;) Let us take care of our native language where possible. "- Male overhang - ??? - Night overhang - ??? "- commercial dictionary? (Aerator... [Read more]

SylwekK 24 Jul 2018 15:05

These algorithms from the article, unfortunately, are bad and would turn out to be a fraction of what's below ... In my project in C on Atmega32 clocked at 16MHz as of today (because the project is... [Read more]

simw 24 Jul 2018 15:49

I have been using the library from Sylwek for a long time, of course the old one, probably one of the first versions. This is a great piece of code. I also use STM, because why not :) I used to try to... [Read more]

Anonymous 24 Jul 2018 19:21

Encoder read only by pooling. For the simple reason that it is deterministic. In the case of vibrating contacts on the interrupts, you suddenly get 10x this frequency of calling the function. I have... [Read more]

SylwekK 24 Jul 2018 19:40

@ grafan999, nothing more to add :) [Read more]

wniedzie 24 Jul 2018 21:49

This algorithm waits for only one pulser output to be changed ("Clock"). Analyzing both outputs would double the resolution, only then the algorithm would get complicated. [Read more]

Rayan19 25 Jul 2018 08:25

Useful article, thank you :) [Read more]

Anonymous 25 Jul 2018 09:42

Amateurs (Aduinists) think so. They don't know what a timer is. [Read more]

SylwekK 25 Jul 2018 10:05

Can you develop a thought? I am not an Arduiner, I use timer interrupts for reading, not external interrupts, so I do not understand your remark too much. [Read more]

Paweł Es. 25 Jul 2018 18:11

In professional devices, a preliminary filter is often used to remove the effects of vibration of the rotary encoder contacts, e.g. something like this: https://obrazki.elektroda.pl/3951185100_1532534740_thumb.jpeg... [Read more]

SylwekK 25 Jul 2018 18:33

Any system with capacitors at the encoder is lost at the start for me, because it limits the speed of its reading. Maybe in less demanding applications it works well, but certainly not in PROFESSIONAL... [Read more]

Paweł Es. 25 Jul 2018 21:40

In particular, there are 40 encoders in the device (for setting various parameters) as in the digital console from which this chip comes. This is not for drives, but for manually rotated encoders. ;) [Read more]

SylwekK 25 Jul 2018 22:35

Nevertheless, handling with capacitors, where the software part is usually neglected, irritates me, because it is enough to wear the encoder a little or a little dirt (which increases the vibrations of... [Read more]

Janusz_kk 26 Jul 2018 12:46

Well, do something for the world and show off this algorithm :) [Read more]

FAQ

TL;DR: A properly debounced incremental rotary encoder on Arduino can track 30 000 pulses / s, and “polling keeps it deterministic” [Elektroda, grafan999, post #17347604] Use timer-driven polling or filtered interrupts for clean counts.

Why it matters: Clean decoding prevents missed steps, menu jumps, and motor misalignment.

Quick Facts

• Typical mechanical encoder: 24 pulses / rev, 5 V logic, ≤ 100 kΩ contact resistance [Instructables]. • Contact bounce lasts 1–5 ms; software debounce ≥ 2 ms recommended [TI, 2015]. • Arduino UNO interrupt latency ≈ 4 µs at 16 MHz [AVR datasheet]. • Hobby encoders cost US $0.3–1.5 online (Digi-Key search). • With timer polling at 30 k Hz, users reported zero lost counts in 8-hour runs [Elektroda, grafan999, post #17347604]

What is the difference between incremental and absolute rotary encoders?

Incremental encoders output pulses that indicate movement and direction but never absolute position. Absolute encoders report a unique code for every shaft angle, so position is known after power-up [Elektroda, ghost666, post #17346304]

How do I wire a 5-pin mechanical encoder to an Arduino?

Connect CLK to any digital pin, DT to another, SW (push-button) to a third, and VCC/GND to 5 V/GND. Enable internal pull-ups or use the module’s onboard resistors [Elektroda, ghost666, post #17346304]

Why does contact bounce cause miscounts and how can I debounce it?

When the wipers touch, they chatter for 1–5 ms, creating multiple false edges that your code may interpret as extra steps. Debounce in software by sampling each channel every 1–2 ms or in hardware with RC filters (e.g., 10 nF + 10 kΩ) [Elektroda, Paweł Es., post #17349266]

How fast can an Arduino reliably read pulses from a mechanical encoder?

Using a 16 MHz AVR with timer polling, forum members handled 20–30 k pulses / s without loss [Elektroda, grafan999, post #17347604] That equals ≈ 625 RPM for a 48 PPR wheel.

How do I reset an on-screen counter with the encoder’s push switch?

Read the SW pin; when it goes LOW, set your counter variable to zero and update the display. The Counter.ino example does exactly this [Elektroda, ghost666, post #17346304]

Can you show a minimal 3-step debounced reading routine?

  1. Set up Timer1 to trigger every 100 µs; read CLK and DT inside the ISR.
  2. Store last state; if changed, decode direction with (CLK ^ DT).
  3. Increment or decrement a volatile counter, then exit ISR in <20 µs. This keeps processing below 20 % CPU at 10 k Hz sample rate.

How can I double resolution by reading both channels?

Count every transition, not just rising edges on CLK. Decode the 4-state Gray sequence (00-01-11-10). This quadrature method gives 4× edge resolution, so a 24 PPR knob feels like 96 steps [Elektroda, wniedzie, post #17347888]

Do hardware RC filters hurt high-speed performance?

Yes. A 10 nF/10 kΩ network forms a 1.6 kHz low-pass filter, limiting reliable rotation to ≈ 400 RPM for 24 PPR units. “Any system with capacitors … limits speed” [Elektroda, SylwekK, post #17349296]

How do I drive LED brightness with the encoder?

Map the encoder counter to 0–255 and pass it to analogWrite() on a PWM pin, as in LEDBrightness.ino [Elektroda, ghost666, post #17346304] This yields 256 brightness levels without flicker.

What common mistakes lead to missed counts?

  1. No pull-ups, so floating lines add random edges.
  2. Long ISRs that disable interrupts.
  3. Ignoring DT channel, giving half resolution.
  4. Using delay() in the main loop, blocking sampling [Elektroda, Anonymous, post #17346514]

Edge case: What if my encoder shaft is damaged or dirty?

Severe wear increases bounce duration beyond debounce window, causing skipped or reversed counts. Advanced algorithms that monitor edge timing can halt counting when patterns deviate, a technique shared by users of custom libraries [Elektroda, SylwekK, post #17349805]

Is there a ready-made Arduino library that handles debouncing?

Yes. The Rotary.h library manages state tables and debouncing in under 1 kB flash [Elektroda, noel200, post #17346454] Import with: #include <Rotary.h> then call .process() inside your loop.

Can I read multiple encoders efficiently?

Shift-registers like 74HCT165 let you poll four debounced encoder channels over SPI, cutting I/O usage. This scheme supports 40 knobs in digital mixers [Elektroda, Paweł Es., post #17349266]

Expert tip for high-PPR optical encoders?

Use hardware quadrature decoders (e.g., LS7366) or microcontrollers with built-in QEI modules; they handle >1 M pulses / s with zero CPU load [Microchip, 2020].
Generated by the language model.
%}