Rotary Encoder

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.
Rotary Encoder

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:

Sparkfun (COM-10982) Sparkfun (COM-10596) Keyes KY-040
Rotary Encoder - Sparkfun COM-10982 Rotary Encoder - Sparkfun COM-10596 Rotary Encoder - Keyes KY-040
24 Impulse
LED (RGB)
Taster
Geriffelte transparente Achse
Mit Rastung
M9 Gewinde zur Befestigung
24 Impulse
LED (Red/Green)
Taster
Geriffelte transparente Achse
Ohne Rastung
Kein Gewinde zur Befestigung
20 Impulse
Keine LED
Taster
Abgeflachte und glatte Achse
Mit Rastung
M7 Gewinde zur Befestigung

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.
Rotary Encoder - Arduino Serial Monitor

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. 🙂

6 Gedanken zu „Rotary Encoder“

  1. 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

  2. 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.

  3. 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

  4. 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;
    }

    }

  5. 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

  6. 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

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert