Otočný ovladač zná asi každý. Najdeme ho na rádiích, zesilovačích, měřicích přístrojích, 3D tiskárnách nebo v různých menu elektronických zařízení. Na první pohled může rotační enkodér vypadat trochu jako obyčejný potenciometr, ale funguje úplně jinak.
Potenciometr má začátek, konec a podle natočení mění odpor. Rotační enkodér se může otáčet stále dokola a neposílá Arduinu přímo žádnou analogovou hodnotu. Místo toho při otáčení vytváří impulzy, ze kterých si Arduino dokáže spočítat, jestli se ovladačem otáčí doprava nebo doleva.
V tomto článku se podíváme na jednoduchý mechanický rotační enkodér s integrovaným tlačítkem. Nebude to hotový modul na DPS, ale samotná součástka. To je důležité, protože takový enkodér nemá vývody VCC, GND, CLK, DT a SW jako hotové moduly. Má jen kontakty samotného enkodéru a zvlášť kontakty tlačítka.

Co je rotační enkodér
Rotační enkodér je součástka, která převádí otáčení hřídele na elektrické signály. U běžného mechanického enkodéru jsou uvnitř kontakty, které se při otáčení postupně spínají a rozpínají.
Výhodou proti potenciometru je, že enkodér nemá pevný začátek a konec. Můžeme s ním otáčet stále dokola. Program si sám počítá, o kolik kroků jsme se posunuli.
Typické použití:
- pohyb v menu,
- nastavení hodnoty,
- ovládání hlasitosti,
- nastavení jasu displeje,
- nastavení času,
- ovládání různých přístrojů.
Velká výhoda je i integrované tlačítko. Ovladačem tedy můžeme otáčet a zároveň ho stisknout. To se hodí třeba pro potvrzení položky v menu.
Jak enkodér vypadá
Použitý enkodér nemá vlastní destičku s elektronikou. Je to pouze samotná součástka.
Na jedné straně jsou dva vývody tlačítka. Ty fungují jako obyčejný spínač. Když se osa enkodéru stiskne, tyto dva kontakty se propojí.
Na druhé straně jsou tři vývody samotného enkodéru. Jeden z nich je společný kontakt a zbylé dva jsou signály enkodéru. Ty se často označují jako A, B a C.
Většinou bývá prostřední vývod společný, ale není dobré na to slepě spoléhat. U neznámého enkodéru je lepší si vývody ověřit multimetrem.


Jak enkodér funguje
Při otáčení enkodéru se uvnitř postupně spínají kontakty. Na dvou signálových výstupech vznikají impulzy. Tyto dva signály nejsou stejné, ale jsou vůči sobě posunuté.
Právě podle toho, který signál se změní jako první, poznáme směr otáčení.
Zjednodušeně:
- při otočení jedním směrem přijde nejdřív signál A,
- při otočení druhým směrem přijde nejdřív signál B.

Arduino sleduje oba signály a podle jejich pořadí zvyšuje nebo snižuje počítanou hodnotu.
Tlačítko je úplně samostatná část. Chová se jako obyčejné tlačítko, které se sepne při zatlačení na hřídel enkodéru.
Rozdíl proti potenciometru
Tohle je důležité hlavně pro začátečníky.
Potenciometr vrací nějakou okamžitou hodnotu. Například Arduino na analogovém vstupu přečte číslo podle toho, jak je potenciometr natočený.
Rotační enkodér ale žádnou absolutní polohu nezná. Po zapnutí programu nevíme, kde „je“. Program si jen počítá kroky od výchozí hodnoty.
Příklad:
Po spuštění programu nastavíme hodnotu na 0.
Otočíme enkodérem doprava o 5 kroků.
Program zvýší hodnotu na 5.
Otočíme doleva o 2 kroky.
Program sníží hodnotu na 3.
Enkodér tedy neříká „jsem na poloze 3“, ale jen „otočil jsem se o krok doprava“ nebo „otočil jsem se o krok doleva“.
Zapojení k Arduino MEGA 2560
Protože používáme samotný enkodér bez DPS, nepřipojujeme k němu napájení 5 V.
V programu použijeme interní pull-up odpory v Arduinu. To znamená, že vstupy jsou v klidu ve stavu HIGH a při sepnutí kontaktu se připojí na GND.
Zapojení samotného enkodéru:
| Enkodér | Arduino MEGA 2560 |
|---|---|
| společný kontakt enkodéru | GND |
| signál A enkodéru | D2 |
| signál B enkodéru | D3 |
| jeden kontakt tlačítka | GND |
| druhý kontakt tlačítka | D4 |
Do enkodéru samotného se tedy nepřivádí 5 V. Připojuje se jen mezi vstupy Arduina a zem.
V programu budou vstupy nastavené takto:
pinMode(pinA, INPUT_PULLUP);
pinMode(pinB, INPUT_PULLUP);
pinMode(pinSW, INPUT_PULLUP);
Díky tomu nepotřebujeme žádné externí odpory.

První testovací program
Nejdřív si uděláme jednoduchý test. Program bude do sériového monitoru vypisovat aktuální hodnotu.
Otočení jedním směrem hodnotu zvýší.
Otočení druhým směrem hodnotu sníží.
Stisk tlačítka hodnotu vynuluje.
// Rotační enkodér s tlačítkem
// Test pro Arduino MEGA 2560
// Enkodér je samotná součástka bez DPS
const int pinA = 2; // signál A enkodéru
const int pinB = 3; // signál B enkodéru
const int pinSW = 4; // tlačítko
int hodnota = 0;
int posledniA;
bool posledniCteniTlacitka = HIGH;
bool stabilniStavTlacitka = HIGH;
unsigned long posledniZmenaTlacitka = 0;
const unsigned long debounceTlacitka = 50;
void setup() {
Serial.begin(9600);
pinMode(pinA, INPUT_PULLUP);
pinMode(pinB, INPUT_PULLUP);
pinMode(pinSW, INPUT_PULLUP);
posledniA = digitalRead(pinA);
Serial.println("Test rotacniho enkoderu");
Serial.println("Otacej enkoderem, stisk tlacitka vynuluje hodnotu.");
Serial.print("Hodnota: ");
Serial.println(hodnota);
}
void loop() {
// Čtení otáčení enkodéru
int aktualniA = digitalRead(pinA);
if (aktualniA != posledniA) {
// Reagujeme jen na jednu hranu signálu,
// aby se jedna poloha nepočítala dvakrát.
if (aktualniA == LOW) {
if (digitalRead(pinB) == HIGH) {
hodnota++;
} else {
hodnota--;
}
Serial.print("Hodnota: ");
Serial.println(hodnota);
}
posledniA = aktualniA;
}
// Čtení tlačítka s jednoduchým odrušením zákmitů
bool cteniTlacitka = digitalRead(pinSW);
if (cteniTlacitka != posledniCteniTlacitka) {
posledniZmenaTlacitka = millis();
posledniCteniTlacitka = cteniTlacitka;
}
if ((millis() - posledniZmenaTlacitka) > debounceTlacitka) {
if (cteniTlacitka != stabilniStavTlacitka) {
stabilniStavTlacitka = cteniTlacitka;
if (stabilniStavTlacitka == LOW) {
hodnota = 0;
Serial.println("Tlacitko stisknuto - hodnota vynulovana");
Serial.print("Hodnota: ");
Serial.println(hodnota);
}
}
}
}
Po nahrání programu otevřeme sériový monitor a nastavíme rychlost 9600 baud.
Při otáčení enkodérem by se měla hodnota zvyšovat nebo snižovat. Po stisknutí tlačítka se hodnota vrátí na nulu.
Když jde směr opačně
U enkodéru se může stát, že se hodnota při otáčení doprava snižuje a při otáčení doleva zvyšuje. To není chyba.
Nejjednodušší možnost je prohodit vodiče signálů A a B.
Po úpravě bude směr opačný.
Mechanické zákmity kontaktů
Mechanický enkodér není ideální elektronická součástka. Uvnitř má kontakty a ty při sepnutí nemusí přepnout úplně čistě. Může vzniknout několik velmi rychlých sepnutí za sebou. Tomu se říká zákmit kontaktů.
U tlačítka je v programu jednoduché odrušení pomocí času. Program čeká, jestli se stav tlačítka po krátké době ustálí.
U samotného otáčení může u některých enkodérů občas dojít k tomu, že hodnota přeskočí víckrát nebo se jeden krok nezapočítá. Pro jednoduché pokusy to většinou nevadí. Pokud by měl enkodér ovládat něco důležitějšího, je možné čtení řešit přes knihovnu nebo přes propracovanější program.
Pokud by hodnota při otáčení občas poskočila o více kroků, nemusí být chyba v zapojení. Může jít právě o zákmit mechanických kontaktů.
Praktický příklad: nastavení jasu LED
Samotné vypisování do sériového monitoru je dobré pro první test, ale zkusíme i něco viditelnějšího. Enkodérem budeme nastavovat jas LED.
LED připojíme na PWM pin Arduina. U Arduino MEGA 2560 použijeme pin D9.
Zapojení LED:
| Součástka | Arduino MEGA 2560 |
|---|---|
| anoda LED přes odpor 220 Ω | D9 |
| katoda LED | GND |
Jas budeme nastavovat v rozsahu 0 až 255. To odpovídá rozsahu funkce analogWrite().
// Rotační enkodér jako regulace jasu LED
// Test pro Arduino MEGA 2560
// Enkodér je samotná součástka bez DPS
const int pinA = 2; // signál A enkodéru
const int pinB = 3; // signál B enkodéru
const int pinSW = 4; // tlačítko
const int pinLED = 9; // PWM výstup pro LED
int jas = 0;
int posledniA;
bool posledniCteniTlacitka = HIGH;
bool stabilniStavTlacitka = HIGH;
unsigned long posledniZmenaTlacitka = 0;
const unsigned long debounceTlacitka = 50;
void setup() {
Serial.begin(9600);
pinMode(pinA, INPUT_PULLUP);
pinMode(pinB, INPUT_PULLUP);
pinMode(pinSW, INPUT_PULLUP);
pinMode(pinLED, OUTPUT);
posledniA = digitalRead(pinA);
analogWrite(pinLED, jas);
Serial.println("Rizeni jasu LED pomoci rotacniho enkoderu");
Serial.print("Jas: ");
Serial.println(jas);
}
void loop() {
// Čtení otáčení enkodéru
int aktualniA = digitalRead(pinA);
if (aktualniA != posledniA) {
if (aktualniA == LOW) {
if (digitalRead(pinB) == HIGH) {
jas += 5;
} else {
jas -= 5;
}
// Omezení rozsahu hodnot
if (jas < 0) {
jas = 0;
}
if (jas > 255) {
jas = 255;
}
analogWrite(pinLED, jas);
Serial.print("Jas: ");
Serial.println(jas);
}
posledniA = aktualniA;
}
// Čtení tlačítka s jednoduchým odrušením zákmitů
bool cteniTlacitka = digitalRead(pinSW);
if (cteniTlacitka != posledniCteniTlacitka) {
posledniZmenaTlacitka = millis();
posledniCteniTlacitka = cteniTlacitka;
}
if ((millis() - posledniZmenaTlacitka) > debounceTlacitka) {
if (cteniTlacitka != stabilniStavTlacitka) {
stabilniStavTlacitka = cteniTlacitka;
if (stabilniStavTlacitka == LOW) {
jas = 0;
analogWrite(pinLED, jas);
Serial.println("Tlacitko stisknuto - LED vypnuta");
Serial.print("Jas: ");
Serial.println(jas);
}
}
}
}
Teď už enkodér opravdu něco ovládá. Otáčením se jas LED zvyšuje nebo snižuje a stisknutím tlačítka se LED vypne.
K čemu se dá enkodér použít
Rotační enkodér je jednoduchý, ale velmi užitečný ovládací prvek. Hodí se všude tam, kde chceme něco nastavovat, ale nechceme používat spoustu tlačítek.
Například:
- nastavení hlasitosti,
- pohyb v menu na displeji,
- nastavení času,
- nastavení teploty,
- regulace jasu,
- výběr položek,
- ovládání jednoduchého přístroje.
Velmi pěkně se kombinuje s displejem. Otáčením vybíráme položku a stiskem ji potvrdíme. Přesně tak funguje ovládání u spousty hotových zařízení.
Na co si dát pozor
Při práci s rotačním enkodérem je dobré myslet na pár věcí.
První věc je, že samotný enkodér bez DPS nemá napájecí piny. Nepřipojuje se na 5 V. Připojuje se jen proti zemi a v Arduinu se zapnou interní pull-up odpory.
Druhá věc je, že enkodér není potenciometr. Po zapnutí programu neznáme jeho absolutní polohu. Program si pouze počítá kroky od výchozí hodnoty.
Třetí věc je směr otáčení. Pokud vychází opačně, stačí prohodit signály A a B, nebo změnit program.
Čtvrtá věc je zákmit kontaktů. U jednoduchého pokusu to většinou nebude velký problém, ale u přesnějšího ovládání se vyplatí použít lepší čtení nebo hotovou knihovnu.
Pátá věc je omezení rozsahu hodnot. Když enkodérem nastavujeme třeba jas LED, musíme hodnotu v programu omezit. Jinak by mohla utéct pod nulu nebo nad maximální hodnotu.
Závěr
Rotační enkodér s tlačítkem je jednoduchá součástka, ale dokáže výrazně vylepšit ovládání různých Arduino projektů. Na rozdíl od potenciometru se může otáčet stále dokola a program si sám počítá jednotlivé kroky.
V článku jsme použili samotný enkodér bez DPS, takže jsme ho nepřipojovali k napájení 5 V. Místo toho jsme využili interní pull-up odpory v Arduinu MEGA 2560. Díky tomu je zapojení velmi jednoduché a stačí jen samotný enkodér, pár vodičů a Arduino.
V prvním pokusu jsme si vyzkoušeli čtení směru otáčení a stisku tlačítka v sériovém monitoru. V druhém příkladu jsme enkodérem nastavovali jas LED.
Na tak malou součástku je to překvapivě praktický ovládací prvek. Dá se použít pro menu, nastavení hodnot, ovládání displeje nebo třeba u vlastních hodin, zdrojů a měřicích přístrojů.
Tento enkodér zakoupíte na Laskakit.cz přímý odkaz je ZDE.

Uvedený kód má opravdu směšné začátečnické nedostatky.
Do integeru přiřazuje bool hodnota – int posledniA; Správně by mělo být bool posledniA;
Konstanty HIGH a LOW jsou Arduino názvy pro true a false. Dejme tomu, ale…
Podmínka if (digitalRead(pinB) == HIGH) { může být samozřejmě napsaná pouze jako if (digitalRead(pinB)){ protože výstup funkce digitalRead už je hodnota true/false. Je to asi jako bych napsal if(10 = 10){. Opačná podmínka se vyhodnocuje jako if (!digitalRead(pinB)){ Vypadá to líp a není potřeba tam psát true/false (HIGH/LOW).
Nejjednodušší změna směru čítání samozřejmě není přepájením, ale pouhou změnou podmínky, nebo přehozením jas++ a jas–.
A hlavně – vyhodnocení enkodéru je ideální zpracovávat v přerušení. Pak se neztrácejí pulzy, když je třeba procesor zaneprázdněn.
volatile int jas = 0;
#define PIN_A 2
#define PIN_B 3
setup(){
pinMode(PIN_A, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(PIN_A), encoderEvent, RISING );
}
encoderEvent(){
if(digitaleRead(PIN_B)){
jas++;}
else{
jas–;}
if(jas 255) jas = 255;
}
A v loopu už si v klidu můžu číst aktualizovanou hodnotu.
Mimochodem u tohoto enkodéru žádné vysoké frekvence nelze očekávat, takže i tvůj k´ód bude fungovat (maximálně semtam něco vynechá). Ale pokud by to bylo odměřování, kde je na jednu otočku řekněme 1024 pulzů, tak to Arduino tvým kódem neodchytí. Díky interuptu není potřeba rozlišovat jestli je to pomalej, nebo rychlej enkodér, ale je krásně univerzální, jednoduchej a efektivní kus kódu. Snejně by se to řešilo třeba u čítače v podstatě čehokoliv.