Hier ein kleiner Versuchsaufbau mit einem Drehimpulsregler KY-040 auch als Rotary Encoder bekannt.
Dieser Drehregler hat spürbar beim drehen kurze Wege bis zur nächsten Einrastung. Durch Zählen dieser Impulse kann der Grad der Rotation ermittelt werden. Der Encoder gibt das Signal für die Drehung an Pin „CLK“ und „DT“ wieder. Dabei ist entscheidend, welcher Pin zuerst den Zustand wechselt. Bei Rotation im Uhrzeigersinn wechselt zuerst „CLK“ den Zustand. Bei Rotation gegen den Uhrzeigersinn wechselt „DT“ zuerst den Zustand.
Er kann auch gedrückt werden, so das eine Art Multifunktionsauswahl stattfinden kann.
Er benötigt zwischen 3,3 Volt bis 5 Volt und hat den Pin DT und CLK für digitalen A und B -Kanal, sowie SW als Auswahlbutton

KY-040 Drehgeber-Datenblatt: https://www.rcscomponents.kiev.ua/datasheets/ky-040-datasheet.pdf
Encoder steuert LEDs
Im ersten Teil wird die Ansteuerung einzelner LEDs gezeigt.
Weiter unten im zweiten Teil, ein Wert, zum Beispiel für Lautstärke, auf einem OLED Display angezeigt.
Verdrahtung:
Verdrahtung des Drehreglers an einen Arduino Uno:
Pin 2 mit CLK
Pin 3 mit DT
Pin 4 mit SW,
5V mit + oder VCC
GND an GND.
Die 5 LED Anode an Vorwiderstand 220 Ohm, sowie die jeweilige Katode an den Arduino Pin 5, 6, 7, 8, 9.

Bibliothek:
Arduino Drehgeber-Bibliothek: https://www.arduino.cc/reference/en/libraries/encoder/
Quellcode 1 :
Mit dieser Lösung kann jede LED einzeln ausgewählt und zusätzlich geschaltet werden und zur Kontrolle ist eine Ausgabe im Seriellen Monitor mit dabei.
#include <Encoder.h>
// Encoder Pins
#define ENCODER_PIN_A 2 //CLK
#define ENCODER_PIN_B 3 //DT
// Button Pin
#define BUTTON_PIN 4 // SW
// LED Pins
const int ledPins[] = {5, 6, 7, 8, 9};
int ledCount = sizeof(ledPins) / sizeof(ledPins[0]);
// Create Encoder object
Encoder myEnc(ENCODER_PIN_A, ENCODER_PIN_B);
long oldPosition = -999;
int selectedLed = 0;
bool ledStates[5] = {false, false, false, false, false}; // LED-Zustände speichern
void setup() {
Serial.begin(9600);
// Setup LED pins
for (int i = 0; i < ledCount; i++) {
pinMode(ledPins[i], OUTPUT);
digitalWrite(ledPins[i], LOW);
}
// Setup button pin
pinMode(BUTTON_PIN, INPUT_PULLUP); // Taster mit Pullup-Widerstand
}
void loop() {
long newPosition = myEnc.read() / 4; //Einstellung für 4 Stufen pro Rastung
if (newPosition != oldPosition) {
oldPosition = newPosition;
// Wrap the encoder value
selectedLed = newPosition % ledCount;
if (selectedLed < 0) {
selectedLed += ledCount;
}
// Turn off all LEDs
for (int i = 0; i < ledCount; i++) {
digitalWrite(ledPins[i], LOW);
}
// Turn on the selected LED
digitalWrite(ledPins[selectedLed], ledStates[selectedLed] ? HIGH : LOW);
Serial.print("Selected LED: ");
Serial.println(selectedLed + 1);
}
// Check if the button is pressed
if (digitalRead(BUTTON_PIN) == LOW) {
delay(50); // Debounce delay
if (digitalRead(BUTTON_PIN) == LOW) { // Confirm button press
ledStates[selectedLed] = !ledStates[selectedLed]; // Toggle LED state
digitalWrite(ledPins[selectedLed], ledStates[selectedLed] ? HIGH : LOW);
Serial.print("Toggled LED: ");
Serial.println(selectedLed + 1);
// Wait for button release
while (digitalRead(BUTTON_PIN) == LOW) {
delay(10);
}
}
}
}

Quellcode 2:
Hier ist ein Beispiel, wie der Sketch mit Interrupts für den Encoder und den Taster arbeitet. Da die Interrupts die Ereignisse sofort behandeln, muss der Hauptprogrammzyklus (loop()
) nicht kontinuierlich auf Encoder- und Taster-Eingaben prüfen. Dies kann die CPU-Auslastung reduzieren und ermöglicht es der CPU, andere Aufgaben effizienter zu erledigen.
#include <Encoder.h>
// Encoder Pins
#define ENCODER_PIN_A 2 // CLK
#define ENCODER_PIN_B 3 // DT
#define BUTTON_PIN 4 // SW
// LED Pins
const int ledPins[] = {5, 6, 7, 8, 9};
const int ledCount = sizeof(ledPins) / sizeof(ledPins[0]);
// Create Encoder object
Encoder myEnc(ENCODER_PIN_A, ENCODER_PIN_B);
volatile long encoderValue = 0; // volatile for ISR
volatile bool buttonPressed = false; // volatile for ISR
void setup() {
Serial.begin(9600);
// Setup LED pins
for (int i = 0; i < ledCount; i++) {
pinMode(ledPins[i], OUTPUT);
digitalWrite(ledPins[i], LOW);
}
// Setup button pin
pinMode(BUTTON_PIN, INPUT_PULLUP); // Taster mit Pullup-Widerstand
// Attach interrupts
attachInterrupt(digitalPinToInterrupt(ENCODER_PIN_A), encoderISR, CHANGE);
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonISR, FALLING);
Serial.println("Setup complete.");
}
void loop() {
static long lastEncoderValue = -999;
static bool ledStates[ledCount] = {false};
static int selectedLed = 0;
// Check if encoder value has changed
if (encoderValue != lastEncoderValue) {
lastEncoderValue = encoderValue;
// Wrap the encoder value
selectedLed = encoderValue % ledCount;
if (selectedLed < 0) selectedLed += ledCount;
// Turn off all LEDs
for (int i = 0; i < ledCount; i++) digitalWrite(ledPins[i], LOW);
// Turn on the selected LED
digitalWrite(ledPins[selectedLed], HIGH); // Ensure the selected LED is turned on
Serial.print("Selected LED: ");
Serial.println(selectedLed + 1);
}
// Check if the button was pressed
if (buttonPressed) {
buttonPressed = false; // reset the flag
// Toggle the state of the selected LED
ledStates[selectedLed] = !ledStates[selectedLed];
digitalWrite(ledPins[selectedLed], ledStates[selectedLed] ? HIGH : LOW);
Serial.print("Toggled LED: ");
Serial.println(selectedLed + 1);
}
}
// Interrupt service routine for the encoder
void encoderISR() {
encoderValue = myEnc.read() / 4;
}
// Interrupt service routine for the button
void buttonISR() {
buttonPressed = true;
}
3D-Druck:
Habe mir diesen Drehknopf ausgedruckt. Er passt perfekt und macht die Bedienung deutlich einfacher.
Rotary Encoder Knob by Nitorix – Thingiverse
Encoder mit Display
Jetzt mal mit Display der den Stellwert anzeigt, sowie auch als Balkengrafik.
Verdrahtung:
Dazu werden die OLED Display Pins:
VCC –> 3,3 Volt
GND –> GND
SDA –> A4
SCL –> A5
angeschlossen.

Quellcode:
#include <Wire.h> //für I2C-Kommunikation
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display Breite
#define SCREEN_HEIGHT 32 // OLED display Hoehe
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
const int encoderPin1 = 2; // Encoder Pin CLK
const int encoderPin2 = 3; // Encoder Pin DT
const int buttonPin = 4; // Button Pin SW
int encoderValue = 0;
int lastEncoded = 0;
void setup() {
pinMode(encoderPin1, INPUT);
pinMode(encoderPin2, INPUT);
pinMode(buttonPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(encoderPin1), updateEncoder, CHANGE);
attachInterrupt(digitalPinToInterrupt(encoderPin2), updateEncoder, CHANGE);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Initialisiere I2C mit Addr 0x3C
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
}
void loop() {
if (digitalRead(buttonPin) == LOW) {
encoderValue = 50;
}
encoderValue = constrain(encoderValue, 0, 100);
display.clearDisplay();
display.setCursor(0, 0);
display.print("Stellwert: ");
display.println(encoderValue);
display.drawRect(10, 20, encoderValue, 10, WHITE);
display.display();
delay(100);
}
void updateEncoder() {
int MSB = digitalRead(encoderPin1); //MSB = Bit mit dem höchsten Stellenwert
int LSB = digitalRead(encoderPin2); //LSB = Bit mit dem niedrigsten Stellenwert
int encoded = (MSB << 1) | LSB; //Umwandlung des 2-Pin-Wertes in eine einzelne Zahl
int sum = (lastEncoded << 2) | encoded; //Hinzufügung zum vorherigen kodierten Wert
if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue ++;
if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue --;
lastEncoded = encoded; //diesen Wert für das nächste Mal speichern
}

Rechts drehen – Wert wird erhöht, Links drehen – Wert wird kleiner, drücken setzt Standardwert auf 50
Weiteres Beispiel, bei diesem Webradio wurde es als Lautstärke Regler und zur Stationswahl eingesetzt: