Ein Potentiometer (kurz Poti) an einem Arduino zu betreiben ist nicht schwierig, ein Rotary Encoder (Drehimpulsgeber) hingegen erfordert ein wenig mehr Aufwand, denn so ein “Drehknopf” ist nicht zu unterschätzen. Um es für euch einfacher zu machen, habe ich drei bekannte Rotary Encoder getestet und ein HowTo erstellt.
Was ist ein Rotary Encoder?
Er sieht so aus wie ein Poti hat aber den Vorteil, dass der Rotary Encoder endlos in jede Richtung gedreht werden kann und dabei ein Gray-Code-Signal erzeugt. Damit kann bei jeder Drehbewegung die Winkelveränderung und die Drehrichtung präzise errechnet werden. Bei einem Potentiometer hingegen ist irgendwann immer der Start- (0%) oder Endwert (100%) erreicht.
Die Testkandidaten
Zuerst habe ich den Sparkfun (COM-10596) getestet, da dieser kein Gewinde zur Befestigung besitzt, war er für mein Projekt ungeeignet. Der Sparkfun (COM-10982) ist mein Favorit da er ein Gewinde und eine RGB LED besitzt. Der robuste Keyes KY-040 besitzt zwar ein Gewinde, hat dafür aber keine LEDs.
Hier die wichtigsten Daten auf einen Blick:
Verdrahtung
Normalerweise haben Rotary Encoder die Pins A, B und C für GND. Die Pins A und B müssen auf dem Arduino mit Digitalen Pins verbunden werden die Interrupts unterstützen. Weitere Infos hierzu erhaltet ihr auf der Arduino – AttachInterrupt Webseite. Zusätzlich können aber auch Pins für Taster und mehrfarbige LEDs vorhanden sein.
Der Keyes KY-040 hat die Pins CLK (A), DT (B), SW, +, and GND wobei hier SW für den Taster vorgesehen ist. Da auf dem KY-040 jeweils Widerstände von A und B zu VCC verbaut sind, sollte auch + verbunden werden da es sonst zu Problemen bei der Auswertung kommt.
Für die SparkFun Rotary Encoder kann ich zum basteln die SparkFun Rotary Encoder Breakout Board (BOB-11722) empfehlen. Hier sieht man auch sehr gut das die Pinbelegung der beiden sehr unterschiedlich ist! Beim COM-10982 laufen die 3 LEDs und der Taster über ein gemeinsames +, beim COM-10596 ist es GND. Wichtig! Nicht vergessen vor den LEDs einen Widerstand zu schalten.
Entweder kann ich den Schaltplan nicht lesen oder dieser ist falsch?! Es ist scheinbar nicht möglich alle LEDs (egal welcher Sparkfun Rotary Encoder) gleichzeitig zu betreiben. Die LEDs haben scheinbar eine Priorität. Die rote LED hat die höchste Priorität gefolgt von der blauen LED und die niedrigste Priorität hat die grüne LED.
Arduino Sketch
Nachdem ich gefühlt jeden Sketch und jede Library ausprobiert habe, bin ich auf der Arduino Playground Webseite fündig geworden. Es werden alle drei Rotary Encoder richtig auswertet und zudem wird keine Library benötigt. Die Beschreibung dort finde ich sehr passend: “Another Interrupt Library THAT REALLY WORKS (the Encoder interrupts the processor and debounces like there is no tomorrow) by rafbuff”
Aktuell ist die Ausgabe auf den Serial Monitor beschränkt. Aktuell wird nur eine Zahl 0-65535 ausgegeben.
Der Rotary Encoder sollte an einem Arduino Uno mit Pin 2 und 3 verbunden werden da diese Interrupts unterstützen. Wenn die Drehrichtung nicht passen sollte, einfach A und B bzw. CLK und DT tauschen. Ich habe den Sketch auf das wesentliche gekürzt damit man den Aufbau besser verstehen kann.
#define encoderPinA 2 #define encoderPinB 3 volatile unsigned int encoderPos = 0; // a counter for the dial unsigned int lastReportedPos = 1; // change management static boolean rotating=false; // debounce management // interrupt service routine vars boolean A_set = false; boolean B_set = false; void setup() { pinMode(encoderPinA, INPUT); pinMode(encoderPinB, INPUT); digitalWrite(encoderPinA, HIGH); // turn on pullup resistors digitalWrite(encoderPinB, HIGH); // turn on pullup resistors attachInterrupt(0, doEncoderA, CHANGE); // encoder pin on interrupt 0 (pin 2) attachInterrupt(1, doEncoderB, CHANGE); // encoder pin on interrupt 1 (pin 3) Serial.begin(9600); // output } void loop() { rotating = true; // reset the debouncer if (lastReportedPos != encoderPos) { Serial.print("Index:"); Serial.println(encoderPos, DEC); lastReportedPos = encoderPos; } } // Interrupt on A changing state void doEncoderA() { if ( rotating ) delay (1); // wait a little until the bouncing is done if( digitalRead(encoderPinA) != A_set ) { // debounce once more A_set = !A_set; // adjust counter + if A leads B if ( A_set && !B_set ) encoderPos += 1; rotating = false; // no more debouncing until loop() hits again } } // Interrupt on B changing state, same as A above void doEncoderB(){ if ( rotating ) delay (1); if( digitalRead(encoderPinB) != B_set ) { B_set = !B_set; // adjust counter - 1 if B leads A if( B_set && !A_set ) encoderPos -= 1; rotating = false; } }
Wie geht es weiter?
Im nächste Schritt bekommt das Arduino Info Display einen Rotary Encoder. Ich werde dazu den Sparkfun Rotary Encoder (COM-10982) als “digitaler Endlos-Poti” zur Eingabe von Werten bzw. zur Menüführung verwenden. Zusätzlich werden dort die LEDs zur Statusanzeige und der Taster für die Navigation eingesetzt. Mal schauen ob das so klappt wie ich es mir vorstelle. 🙂
Hi,
ich versuche den Rotary encoder meiner alten DX6i mit dem Arduino auszuwerten. Habe schon einige Sketches probiert. Deines funktioniert soweit ganz gut nur leider wird nur jede zweite drehung erkannt.
hast du eine Idee wo man hier ansetzen könnte?
grüße
tobias
Vielen Dank für diese tolle kurze Zusammenfassung. Der Code hat bei mir auf Anhieb funktioniert und erspart mir eine Menge Arbeit. Der Drehencoder soll bei mir ein Lautstärkeregler sein.
OK Danke sehr funktioniert.
werde das auch einsetzen mit paar Änderungen. Für den Azimut Antennen Rotor.
Das ist die SOLL Einstellung für den Azimith. Mit dem BUTTON drücken startet dann die Drehbewegung bis zur gelesenen Position vom Positionsmelder. Der Positionsmelder ist eine Scheibe mit 36 Reed Kontakten. Dann eine Widerstandsreihe die analog ausgewertet wird. Auflösung ist 10° (absolut genügend – minimal müsste ich 30° haben – die Antenne hat einen Öffnungswinkel von 30°).
TNX Erich
Hier ne leicht abgeänderte Version ohne interupts, falls man dieses für andere dinge braucht 😉
#define encoderPinA 2
#define encoderPinB 3
volatile unsigned int encoderPos = 0; // a counter for the dial
unsigned int lastReportedPos = 1; // change management
// interrupt service routine vars
boolean A_set = false;
boolean B_set = false;
void setup() {
pinMode(encoderPinA, INPUT);
pinMode(encoderPinB, INPUT);
digitalWrite(encoderPinA, HIGH); // turn on pullup resistors
digitalWrite(encoderPinB, HIGH); // turn on pullup resistors
Serial.begin(9600); // output
}
void loop()
{
if (lastReportedPos != encoderPos)
{
Serial.print(“Index:”);
Serial.println(encoderPos, DEC);
lastReportedPos = encoderPos;
}
if( digitalRead(encoderPinA) != A_set ) { // debounce once more
A_set = !A_set;
// adjust counter + if A leads B
if ( A_set && !B_set )
encoderPos += 1;
}
if( digitalRead(encoderPinB) != B_set ) {
B_set = !B_set;
// adjust counter – 1 if B leads A
if( B_set && !A_set )
encoderPos -= 1;
}
}
Hallo,
zuerst einmal Danke an Stefan. Aber auch einen Dank an Patrick. Ich habe erst wenig Ahnung vom Arduino und habe in meinem Leichtsinn die Einstellung der IDE auf meinen MicroBit geändert. Damit musste ich dann erkennen, dass scheinbar dieser MicroBit ohne Interrupts auskommen muss. So war ich begeistert die modifizierte Version von Patrick zu sehen. Ich habe sie mal ausprobiert. Dabei bekam ich einen Syntaxfehler angezeigt beim Serial.print(„Index:“); was schnell in “Index” geändert war. So konnte ich dann den Code auf den MicroBit aufspielen. Dabei viel mir auf das beim “links drehen” hochgezählt wurde und beim “rechts drehen” runtergezählt. Das werde ich dann noch für mich ändern. Außerdem scheint mein Rotary Encoder zu prellen, was ich dann auch noch irgendwie lösen möchte. Danke nochmal an Alle und noch viel Spaß beim Coden. Viele Grüße, Michael
Hallo,
Danke, Stefan hat mir auf der Suche nach einen brauchbaren Sketch weitergeholfen.
Tolle Arbeit!
Und wie es meistens ist – jetzt kommt das Aber.
Ich habe mehrere Drehencoder, wo auf der Rückseite drei Widerstände waagerecht
liegen.
Folgendes Fehlverhalten tritt nur nach dem Einschalten auf:
Beim drehen im Uhrzeigersinn ist die erste Zahl nach der Null 65535.
Dann zählt er natürlich weiter. Im Uhrzeigersinn mit Null eins … und
gegen den Uhrzeigersinn 65534 usw..
Ich konnte den Fehler schon eingrenzen. Es muß etwas mit dem Aufbau des
Encoder’s zu tun haben.
Teste es einmal selbst und gehe wie folgt vor:
Teste jeweils nur einen Schritt im Uhrzeigersinn. Es erscheint Index: 0
drehe nun den Encoder einen Schritt nach rechts und es erscheint eine 1.
Starte nun den Arduino neu oder beim UNO durch betätigen des Tasters.
Drehe nun den Encoder erneut nach rechts. Jetzt sollte der Fehler auftreten und
65535 statt einer 1 angezeigt werden.
Es muß etwas damit zusammen hängen, das die Kontakte AB auf der Kontaktscheibe
ein kleinwenig versetzt sind.
Wenn Du den Fehler nachvollziehen kannst, währe es schön wenn du eine Lösung
vorschlagen könntest.
PS: Der Fehler tritt nicht auf wenn Du zwischen dem ersten und zweitem test den
Encoder einen Schritt weiter drehst. Also darauf achten das wirklich nur
eine Schrittdrehung gemacht wird. Ich hoffe ich habe mich verständlich
ausgedrückt und du kannst es nachvollziehen.
Bernd