Heiß gewünschtes Thema in dieser Folge: Encoder. Wir gucken uns jetzt an wie die Hardware funktioniert und wie wir Encoder mit dem Arduino abfragen. Und natürlich erkläre ich euch das wieder so, dass es auch für Anfänger ganz easy verständlich ist!
Ganz normale Potis
Normale Potis erkennt man ganz einfach: Sie haben einen begrenzten Bereich in dem sie sich drehen lassen. Mit einem festen Start- und Endpunkt. Darum kann man die Position von einem Poti auch direkt abfragen: Einfach ein analog.read-Befehl, und schon kenne ich den Wert – die Position – von meinem Poti.
Arduino Encoder Tutorial – los geht’s
Ein Encoder funktioniert ganz anders: Erstmal gibt es keinen Start- oder Endpunkt. Dafür kann ich ihn endlos und unbegrenzt in beide Richtungen drehen. Ich nenne die Dinger darum auch Endlos-Encoder. Nur kann man die Position deswegen nicht einfach so abfragen – wie auch, der Encoder hat ja keine festen Positionen. Es gibt quasi unendlich viele Positionen die er haben kann. Und wie funktioniert das ganze dann? Man bekommt vom Encoder nur Signale wenn man daran dreht. Diese Signale auswerten, in einen Wert/eine Position umrechnen, überhaupt erst mal einen Bereich definieren in dem wir arbeiten wollen – das machen wir dann mit einem Arduino-Programm.
Bevor wir diese Signale mit dem Arduino auswerten, müssen wir erst mal wissen wie die Hardware aufgebaut ist und welche Signale ganz genau vom Encoder erzeugt werden.
Aufbau
Ein Encoder hat insgesamt 5 Beinchen. Natürlich gibt’s Ground und – Plus – Strom rein, genau wie beim normalen Poti die beiden äußeren Pins. Es gibt einen SW-Pin, das steht für „Switch“ und das ist der eingebaute Taster. Man kann also auf den Encoder auch draufdrücken. Hat nicht zwangsläufig jeder Encoder, dieser hier hat das allerdings , soll uns heute aber nicht weiter interessieren. Für den mittleren Pin des Potis hat der Encoder zwei Pins. Und diese beiden Pins sind für die Position zuständig.
Technisch gesehen machen diese beiden Pins erst mal genau das gleiche. Und um euch das zu zeigen hab ich mal einen dieser Pins und den GND Pin an mein Multimeter angeschlossen. Das misst jetzt Durchgang, d.h. jedes mal wenn diese beiden Leitungen elektrisch verbunden werden piepst das Multimeter. Im Video seht und hörst du, was dann passiert.
Der Encoder stellt mit jedem Schritt den ich drehe ganz kurz einen Kontakt her. Genau so, wie wenn man beispielsweise kurz auf einen Taster drückt. Hier seht ihr auch noch mal einen Encoder von innen: Man sieht ganz deutlich die einzelnen Kontaktflächen, die dann beim Drehen hier mit dem Schleifer einen Kontakt herstellen. Und weil’s ja zwei Ausgangspins gibt, gibt es halt auch zwei Schleifer.
Arduino Encoder Tutorial – Sketch 1
int messungPin1 = LOW; int messungPin1Alt = LOW; int encoderWert = 0; void setup() { pinMode(2, INPUT); Serial.begin(9600); } void loop() { messungPin1 = digitalRead(2); if ((messungPin1 == HIGH) && (messungPin1Alt == LOW)) { encoderWert++; Serial.println (encoderWert); } messungPin1Alt = messungPin1; }
Wenn der Encoder einfach nur einen kurzen Kontakt herstellt, dann kann man ihr ja auch genau so wie einen Taster abfragen. Angeschlossen hab ich mit der weißen und grauen Leitung Plus und Minus und hier mit dem grünen Kabel verbinde ich Pin 3 am Arduino mit einem der beiden Messpins am Encoder. Und wer sich jetzt fragt: Hey, wo ist denn bei dem Aufbau der Pulldown-Widerstand? Der ist bei meinem Encoder schon direkt draufgelötet – wie praktisch!
Wie beim Taster gibt’s zwei Variablen um immer nur einen einzelnen Wert pro Schritt am Encoder zu erfassen. Zusätzlich gibt’s eine Variable die meinen Wert, meine Position des Encoders zwischenspeichert. Das macht eben nicht der Encoder selbst, sondern unser Programm.
Der einzige Unterschied zu unserem Taster-Sketch: In der if-Bedingung steht encoderWert++ und den Befehl „variable mit plusplus“ kennt ihr auch schon: Es erhöht die Zahl in meiner Variable um eins.
So, dann wollen wir das ganze mal ausprobieren und im Serial Monitor angucken!
Allerdings gibt’s noch ein ziemlich großes Problem: Egal ob ich meinen Encoder vorwärts oder rückwärts drehe, steigt der Wert. Wir können nicht erkennen, in welche Richtung wir drehen, denn egal ob vorwärts oder rückwärts: In beide Richtungen gibt’s ja nur einen kurzen elektrischen Kontakt.
Was wir also brauchen ist eine Möglichkeit um zu unterscheiden, in welche Richtung wir drehen. Und – habt ihr euch sicher schon gedacht – das hat irgendwas mit dem zweiten Pin an unserem Encoder zu tun. Nur, wie kann das funktionieren, wenn der zweite Pin doch genau das gleiche macht wie der erste?
Am Encoder drehen würde auf einem Oszilloskop so aussehen: Mit jedem Schritt den ich weiterdrehe gibt’s nen elektrischen Kontakt und meine Spannungs-Kurve geht kurz nach oben. Und weil ich ja zwei Pins hab, gibt’s eben auch zwei Kurven.
Wenn man sich die Schleifer aber ganz genau anschaut, sieht man was sehr wichtiges: Beide liegen nebeneinander! Das bedeutet, dass der Kontakt nicht gleichzeitig hergestellt wird! Die beiden Kurven am Oszilloskop sind quasi zeitlich verschoben.
Und das ist das Geheimnis um die Drehrichtung zu erkennen: Die rote Linie ist die Position von meinem Encoder. Drehe ich vorwärts, messe ich an PIN 1 HIGH und an PIN 2 auch HIGH. Sind beide Pins High definiere in meinem Aduino-Sketch: Wert plus 1.
Drehe ich rückwärts, macht sich die Verschiebung der Kontaktflächen bemerkbar: wenn Pin 1 auf HIGH geht ist Pin 2 noch ist LOW: Dann verringere ich meine Wert im Arduino um 1.
Das machen wir also gleich in unserem Sketch: wir behalten Pin 1 im Auge. Wenn der auf HIGH geht, gucken wir nach, was macht Pin 2 und entscheiden danach, ob der Wert hoch oder runter geht.
Jetzt ist zwar die Darstellung mit den beiden verschobenen Recheckkurven irgendwie Standard, kann aber durchaus verwirren: Hä, wieso denn zeitlich verschoben? Vielleicht hilf euch diese Darstellung:
Blau ist der Encoder, daruf in weiß die Kontaktflächen, oben in schwarz die beiden Schleifer….
Arduino Encoder Tutorial – Sketch 2
int messungPin1 = LOW; int messungPin1Alt = LOW; int encoderWert = 0; void setup() { pinMode(3, INPUT); pinMode(4, INPUT); Serial.begin(9600); } void loop() { messungPin1 = digitalRead(3); if ((messungPin1 == HIGH) && (messungPin1Alt == LOW)) { if (digitalRead(4) == HIGH) { encoderWert++; } else { encoderWert--; } Serial.println (encoderWert); } messungPin1Alt = messungPin1; }
Und jetzt programmieren wir das Ganze: Im Setup fügen wir den Pin 4 als Input hinzu – dort ist dann der zweite Pin vom Encoder angeschlossen.
Und weil wir’s ja jetzt so einfach wie möglich programmieren wollen, checken wir den Zustand des zweiten Pins nur dann, wenn die if-Bedingung des ersten Pin serfüllt wurde. Wir wissen dann schon: der Encoder wurde gedreht, Pin zwei entscheidet über die Drehrichtung. Darum schreiben wir in die if-Bedingung eine zweite if-Bedingung rein. Wenn wir am Pin 4 ein HIGH messen, dann erhöhe die Variable um einen Schritt. Und „else“ bedeutet „in allen anderen Fällen“ – und der kann ja hier nur sein Pin 4 = LOW, dann senkst du die Variable um einen Schritt, und das schreibt man mit Variablen-Name und Minus, Minus.
Und das probieren wir aus: Ich drehe im Uhrzeigersinn: Der Wert meiner Variable geht nach oben. Ich drehe gegen den Uhrzeigersinn: Aha! Der Wert geht wieder nach unten! Und im Moment haben wir noch keinen maximalen Wertebereich definiert, darum geht das sogar in den Minusbereich. Und es geht natürlich auch, wenn man etwas schneller dreht.
ABMOD
Ihr seht also: Encoder arbeiten elektisch sehr einfach und lassen sich auch sehr einfach abfragen.
Es gibt natürlich Wege die Genauigkeit des Abfragens zu erhöhen oder z.B. mit einer Encoder-Library auch mit einer Beschleunigung arbeiten. Heute lasse ich das aber mal so stehen. Und, ihr könnt euch jetzt sicher schon denken, dass es von hier – wo ich den Encoder Wert als Zahl in ner Variable hab – kein Hexenwerk mehr ist mit dem Encoder z.B. unseren Cutoff am Monotron zu steuern.
Sehr anschaulich erklärt (wie immer 🙂 ) Gibt es einen besonderen Grund Endlos Encoder zu verwenden? Ich werde demnächst meine Variante eines MIDI Controllers in die Praxis umsetzen. Dort werde ich definitiv normale Potis verwenden. Mein Arturia Beatstep hat 16 Endlos Encoder, die reagieren recht zäh, man muss ganz schön kurbeln. Gerade beim Cutoff stört das. Die Testschaltung mit Poti nach dem Video gebaut ist da wesentlich griffiger.
@ „warum Encoder statt Potis“:
Stell dir vor du willst mit einem Drehregler die Lautstärken von Instrumenten einstellen. Angenommen du hast ein Projekt mit einer Spur für Vocals und einer für das Instrumental. Sagen wir z.B., dass du die Lautstärke der Vocals mit dem Poti auf 80 Einheiten gedreht hast. Als nächstes willst du die Lautstärke des Instrumentals einstellen. Also klickst du die Instrumentalspur an und merkst, dass sie mit 60 Einheiten zu leise ist. Du drehst also den Poti hoch während du die Instrumental-Spur angewählt hast und was passiert ist, dass der Wert direkt auf 81 hochspringt. Das liegt daran, dass der Poti sich ja den Wert, den du zuvor eingegeben hast (80) quasi merkt und sobald du ihn erhöhst, geht er auf 81 usw. Das ist ein echtes Problem, denn sobald du mit einem Drehregler mehr als rine Spur regeln willst, hast du das beschriebene Problem.
Rin Encoder dagegen übermittelt keine absoluten Werte wie 81 o.ä., sondern nur Veränderungen (wie z.B. erhöhe die Lautstärke um 4 Einheiten.
Gerade im Zusammenspiel mit dem VST oder der DAW finde ich endlos encoder besonders wertvoll,
das hat den Vorteil dass du den Wert via LED oder Display darstellen kannst und der encoder immer die „richtige“ Position hat,
Alternative hierzu währen motor potis die sich beim wechsel des plug-ins in die richtige Stellung drehen, aber das ist sehr teuer und aufwendig.
Ich habe den Sketch einmal ausprobiert und festgestellt, dass er falsch ist.
Es wird nur jeder zweite Schritt erfasst.
Ich habe aber eine gute Erklärung und eine funktionierende Lösung hier gefunden.
https://www.youtube.com/watch?v=bBhbynj6NYM
Von dort stammt auch der Sketch hier unten.
Gruß
Frank
volatile int zaehler = 0;
void setup()
{
Serial.begin(9600);
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(2), Encoder, CHANGE);
}
void loop()
{
}
void Encoder()
{
if(digitalRead(2) != digitalRead(3))
zaehler++;
else
zaehler–;
Serial.println(zaehler,DEC);
}
Hallo!! Dein Video und deine Seite haben mir sehr geholfen! Danke
Steh vor einer Herausforderung!
Möchte das wenn ich den Button drücke, der Wert auf 0 gesetzt wird.
Schaffe ich auch, aber wenn ich wieder drehe zählt er weiter wo ich aufgehört habe.
Zb.: 12,13,14,0,15,16 bzw.
12,13,14,0,13,12
Steh an und komm nicht weiter!!
Bin mir sicher das die Lösung ganz einfach ist. Leider bin ich noch nicht so fit beim programmieren!
Wäre spitzt wenn du mir weiterhelfen könntest.
Danke Alex
Hallo Alex,
poste mal deinen Sketch – darin muss ja irgendwo der Fehler liegen…
Danke für die schnelle Antwort!
Hab es aber mittlerweile selbst lösen können!! Danke!
Aber eine andere Frage:
Mit dem Code den ich verwende kam ich nun die Werte in beide Richtungen verändern und per Knopfdruck den Wert auf 0 zurücksetzen.
Besteht die Möglichkeit den Taster bei gedrückten Zustand eine andere Funktion zu geben?
Zum Beispiel:
Drehe ich im normalen Zustand erhöht oder verringert sich die Zahl jeweils um 1. ✅
Beim einmaligen drücken wird der Zählwert auf 0 gesetzt.✅
Jetzt möchte ich wenn der Knopf gedrückt bleibt und ich drehe sich der Zählwert in 10 Schritten ändert!!
Will keine fertige Lösung, sonst geht der Lernprozess verloren.
Meine Idee wäre den HIGH Zustand des Tasters mit einer zeitfunktion abzufragen.
currentTime = millis();
[…]
}
void loop() {
if (button == HIGH) && (millis()-currentTime < 1000)
{
……
}
Bin ich da am Holzweg??
Danke schon mal!!
Für Ideen und Hinweise wäre ich dankbar!!
Hi,
vorab dickes ein dickes Lob! .. deine Tutorials gehören zu den besten im ganzen Web!
Ich frage mich grade ob es möglich ist, Rotary-Encoder zu „muxen“, sprich, mit einem 74HC4051 abzufragen wie auch bei den Switches oder Potis ?
Hintergrund ist der, dass ich neben tausenden von Buttons noch eine ganze Reihe von Encodern brauche und da jeder Encoder 2 Pins braucht, gehen mir die Pins an meinem Teensy langsam aus.
Danke Richard!
Ja theoretisch schon, eleganter ist es aber mit einem „echten“ IO-Expander. Schau mal die Folge zum Stepsequencer (Geheimprojekt X) – da verwende ich einen MCP23016 bzw. 23017.
Wie schaffe ich es denn dass der Encoder als midi controller genutzt werden kann? Dafür müsste dieser doch auch in einem gewissen Wertebereich arbeiten. Ich habe es jetzt soweit , dass der Encoder wie bei dir hoch und runter zählt. Aber wie bekomme ich daraus jetzt das Midi Signal mit dem Controllwert zwischen 0-127?
Hallo, hast du den eine gute Seite oder einen guten Laden, wo man Potis, Encoder etc kaufen kann?