Encoder macht zuviele Schritte

Guten Tag,

bin gerade dabei mich in das Gebiet Encoder einzuarbeiten. Habe mir bereits dutzende Foren durchgelesen und Videos angeschaut. Bin dann soweit gekommen das ich mir 3Libaries gedownloadet habe und habe alles soweit zum laufen gebracht mit Encoder und Display.

Jetzt mein Problem: Da ich ein Menu erarbeiten wollte ist mir der Encoder zurzeit zu ungenau manchmal macht er einen Schritt manchmal vier - wie bekomme ich das weg und soweit ich aus dem Code lesen kann soll er etwas anderes anzeigen wenn man den button drückt wird jedoch geschieht nichts.

Verwende einen Arduino Mega 2560 R3

Freue mich auch über ein wenig Erklärung der Libary.

Die Libary:

// ----------------------------------------------------------------------------
// Rotary Encoder Driver with Acceleration
// Supports Click, DoubleClick, Long Click
//
// (c) 2010 karl@pitrich.com
// (c) 2014 karl@pitrich.com
// 
// Timer-based rotary encoder logic by Peter Dannegger
// http://www.mikrocontroller.net/articles/Drehgeber
// ----------------------------------------------------------------------------

#include "ClickEncoder.h"

// ----------------------------------------------------------------------------
// Button configuration (values for 1ms timer service calls)
//
#define ENC_BUTTONINTERVAL    10  // check button every x milliseconds, also debouce time
#define ENC_DOUBLECLICKTIME  600  // second click within 600ms
#define ENC_HOLDTIME        1200  // report held button after 1.2s

// ----------------------------------------------------------------------------
// Acceleration configuration (for 1000Hz calls to ::service())
//
#define ENC_ACCEL_TOP      3072   // max. acceleration: *12 (val >> 8)
#define ENC_ACCEL_INC        12
#define ENC_ACCEL_DEC         8

// ----------------------------------------------------------------------------

#if ENC_DECODER != ENC_NORMAL
#  ifdef ENC_HALFSTEP (1 << 2)
     // decoding table for hardware with flaky notch (half resolution)
     const int8_t ClickEncoder::table[16] __attribute__((__progmem__)) = { 
       0, 0, -1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, -1, 0, 0 
     };    
#  else
     // decoding table for normal hardware
     const int8_t ClickEncoder::table[16] __attribute__((__progmem__)) = { 
       0, 1, -1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, -1, 1, 0 
     };    
#  endif
#endif

// ----------------------------------------------------------------------------

ClickEncoder::ClickEncoder(uint8_t A, uint8_t B, uint8_t BTN, uint8_t stepsPerNotch, bool active)
  : doubleClickEnabled(true), accelerationEnabled(false),
    delta(0), last(0), acceleration(0),
    button(Open), steps(stepsPerNotch),
    pinA(A), pinB(B), pinBTN(BTN), pinsActive(active)
{
  uint8_t configType = (pinsActive == LOW) ? INPUT_PULLUP : INPUT;
  pinMode(pinA, configType);
  pinMode(pinB, configType);
  pinMode(pinBTN, configType);
  
  if (digitalRead(pinA) == pinsActive) {
    last = 3;
  }

  if (digitalRead(pinB) == pinsActive) {
    last ^=1;
  }
}

// ----------------------------------------------------------------------------
// call this every 1 millisecond via timer ISR
//
void ClickEncoder::service(void)
{
  bool moved = false;
  unsigned long now = millis();

  if (accelerationEnabled) { // decelerate every tick
    acceleration -= ENC_ACCEL_DEC;
    if (acceleration & 0x8000) { // handle overflow of MSB is set
      acceleration = 0;
    }
  }

#if ENC_DECODER == ENC_FLAKY
  last = (last << 2) & 0x0F;

  if (digitalRead(pinA) == pinsActive) {
    last |= 2;
  }

  if (digitalRead(pinB) == pinsActive) {
    last |= 1;
  }

  uint8_t tbl = pgm_read_byte(&table[last]); 
  if (tbl) {
    delta += tbl;
    moved = true;
  }
#elif ENC_DECODER == ENC_NORMAL
  int8_t curr = 0;

  if (digitalRead(pinA) == pinsActive) {
    curr = 3;
  }

  if (digitalRead(pinB) == pinsActive) {
    curr ^= 1;
  }
  
  int8_t diff = last - curr;

  if (diff & 1) {            // bit 0 = step
    last = curr;
    delta += (diff & 2) - 1; // bit 1 = direction (+/-)
    moved = true;    
  }
#else
# error "Error: define ENC_DECODER to ENC_NORMAL or ENC_FLAKY"
#endif

  if (accelerationEnabled && moved) {
    // increment accelerator if encoder has been moved
    if (acceleration <= (ENC_ACCEL_TOP - ENC_ACCEL_INC)) {
      acceleration += ENC_ACCEL_INC;
    }
  }

  // handle button
  //
#ifndef WITHOUT_BUTTON
  static uint16_t keyDownTicks = 0;
  static uint8_t doubleClickTicks = 0;
  static unsigned long lastButtonCheck = 0;

  if (pinBTN > 0 // check button only, if a pin has been provided
      && (now - lastButtonCheck) >= ENC_BUTTONINTERVAL) // checking button is sufficient every 10-30ms
  { 
    lastButtonCheck = now;
    
    if (digitalRead(pinBTN) == pinsActive) { // key is down
      keyDownTicks++;
      if (keyDownTicks > (ENC_HOLDTIME / ENC_BUTTONINTERVAL)) {
        button = Held;
      }
    }

    if (digitalRead(pinBTN) == !pinsActive) { // key is now up
      if (keyDownTicks /*> ENC_BUTTONINTERVAL*/) {
        if (button == Held) {
          button = Released;
          doubleClickTicks = 0;
        }
        else {
          #define ENC_SINGLECLICKONLY 1
          if (doubleClickTicks > ENC_SINGLECLICKONLY) {   // prevent trigger in single click mode
            if (doubleClickTicks < (ENC_DOUBLECLICKTIME / ENC_BUTTONINTERVAL)) {
              button = DoubleClicked;
              doubleClickTicks = 0;
            }
          }
          else {
            doubleClickTicks = (doubleClickEnabled) ? (ENC_DOUBLECLICKTIME / ENC_BUTTONINTERVAL) : ENC_SINGLECLICKONLY;
          }
        }
      }

      keyDownTicks = 0;
    }
  
    if (doubleClickTicks > 0) {
      doubleClickTicks--;
      if (--doubleClickTicks == 0) {
        button = Clicked;
      }
    }
  }
#endif // WITHOUT_BUTTON

}

// ----------------------------------------------------------------------------

int16_t ClickEncoder::getValue(void)
{
  int16_t val;
  
  cli();
  val = delta;

  if (steps == 2) delta = val & 1;
  else if (steps == 4) delta = val & 3;
  else delta = 0; // default to 1 step per notch

  sei();
  
  if (steps == 4) val >>= 2;
  if (steps == 2) val >>= 1;

  int16_t r = 0;
  int16_t accel = ((accelerationEnabled) ? (acceleration >> 8) : 0);

  if (val < 0) {
    r -= 1 + accel;
  }
  else if (val > 0) {
    r += 1 + accel;
  }

  return r;
}

// ----------------------------------------------------------------------------

#ifndef WITHOUT_BUTTON
ClickEncoder::Button ClickEncoder::getButton(void)
{
  ClickEncoder::Button ret = button;
  if (button != ClickEncoder::Held) {
    button = ClickEncoder::Open; // reset
  }
  return ret;
}
#endif

Der Code:

#define WITH_LCD 1

#include <ClickEncoder.h>
#include <TimerOne.h>

#ifdef WITH_LCD
#include <LiquidCrystal.h>

#define LCD_RS       8
#define LCD_RW       9
#define LCD_EN      10
#define LCD_D4       4
#define LCD_D5       5
#define LCD_D6       6
#define LCD_D7       7

#define LCD_CHARS   20
#define LCD_LINES    4

LiquidCrystal lcd(LCD_RS, LCD_RW, LCD_EN, LCD_D4, LCD_D5, LCD_D6, LCD_D7);
#endif

ClickEncoder *encoder;
int16_t last, value;

void timerIsr() {
  encoder->service();
}

#ifdef WITH_LCD
void displayAccelerationStatus() {
  lcd.setCursor(0, 1);  
  lcd.print("Acceleration ");
  lcd.print(encoder->getAccelerationEnabled() ? "on " : "off");
}
#endif

void setup() {
  Serial.begin(9600);
  encoder = new ClickEncoder(26, 28, 31);

#ifdef WITH_LCD
  lcd.begin(LCD_CHARS, LCD_LINES);
  lcd.clear();
  displayAccelerationStatus();
#endif

  Timer1.initialize(1000);
  Timer1.attachInterrupt(timerIsr); 
  
  last = -1;
}

void loop() {  
  value += encoder->getValue();
  
  if (value != last) {
    last = value;
    Serial.print("Encoder Value: ");
    Serial.println(value);
#ifdef WITH_LCD
    lcd.setCursor(0, 0);
    lcd.print("         ");
    lcd.setCursor(0, 0);
    lcd.print(value);
#endif
  }
  
  ClickEncoder::Button b = encoder->getButton();
  if (b != ClickEncoder::Open) {
    Serial.print("Button: ");
    #define VERBOSECASE(label) case label: Serial.println(#label); break;
    switch (b) {
      VERBOSECASE(ClickEncoder::Pressed);
      VERBOSECASE(ClickEncoder::Held)
      VERBOSECASE(ClickEncoder::Released)
      VERBOSECASE(ClickEncoder::Clicked)
      case ClickEncoder::DoubleClicked:
          Serial.println("ClickEncoder::DoubleClicked");
          encoder->setAccelerationEnabled(!encoder->getAccelerationEnabled());
          Serial.print("  Acceleration is ");
          Serial.println((encoder->getAccelerationEnabled()) ? "enabled" : "disabled");
#ifdef WITH_LCD
          displayAccelerationStatus();
#endif
        break;
    }
  }    
}

Das Problem hatte ich auch.

Abhilfe haben, zusätzlich zu den Widerständen, 100nF Kondensatoren geschaffen (3Stück gegen Masse- A/B/Taste).

Ich habe gänzlich auf libs verzichtet und das mit Hilfe von millis() gelöst. Das Ergebnis ist brauchbar, egal welche Qualität der Decoder hatte.

void loop()
{
	
	if (menue == false) {TIMER_MENUE(pin1_1,1,5);} //Aufruf der Taste
......

}


/* Timer_Menue *******************************************/
void TIMER_MENUE (int pin, int menue_z, int laenge)
{
	time_menue = millis();
	
	if(time_menue >= (loop_time + laenge))
		{
      // alle 5ms Teste den encoder --> 200Hz 
       
      taste1 = digitalRead(pin); // Read Taste 1 Encoder 1/2
      
      if((!taste1) && (taste1_1))
      {	
      	// Menue einschalten   
        if (menue_z == 1)	
        	{ 
        		menue = true;
        		MENUE_START(); //Menuebild aufbauen
        	}  
        	
        // *****************
        if (menue_z == 2) {	menue_2 = true;}	
        .........

      }      
      taste1_1 = taste1; 
      loop_time = time_menue;  // Updates loopTime
    }
	
}

Damit ist die Taste bei mir sauber entprellt. (Ich benutze diese Taste mehrfach im Menue)

Und hier die A/B Decoderauswertung.

void loop()
{
....
if (menue == true)
		{
			do
			{
				
				TIME_MENUE_D (pin1_2, pin1_3, 5);
				
				......
                                TIMER_MENUE (pin1_1,2,5); // Änderung von menue_2
				
			}
			while(menue_2 == false); // Taste gedrückt
						
		}	
}


/* Timer_Decoder1 ****************************************/
void TIME_MENUE_D (int pin_A, int pin_B, int laenge)
{
  time_menue_d = millis();
  if(time_menue_d >= (loop_time_d + laenge))
  	{
    	// alle 5ms --> 200Hz  

    	encoder_A = digitalRead(pin_A);    // lese encoder pins
    	encoder_B = digitalRead(pin_B);   
    	if((!encoder_A) && (encoder_A_prev))
    	{
        // A Übergang high to low 
        if(encoder_B) 
      	{
          // B ist high --> Uhrzeigersinn
          // increase the counter
          goingUp = true;               
      	}   
        else 
      	{
          // B ist low --> entgegen Uhrzeigersinn      
          // decrease the counter
          goingDown = true; 
      	}   

    	}   
    	encoder_A_prev = encoder_A;     // Zustand von A für die nächste Schleife    
       
    	loop_time_d = time_menue_d;  // Updates loopTime
 	  }	
}

Jeder zweite Rasterpunkt am Decoder bringt ein sauberes Ergebnis.
Und ich verzichte auf Interrups.
Ich habe es so gemacht, dass das Menu per Tastendruck geöffnet und dann erst der Decoder A/B abgefragt wird.
Zeitlich ist es auch in Ordnung und ich kann so nebenbei ein Pegel optisch darstellen.

Gibt es für den korrekten Anschluß sowas wie einen Schaltplan?

Gruß

@Jortox
ich kann leider nicht erkennen, an welchem Pin des Arduino du den Button angeschlossen hast.
Damit du eine Aktion ausführen kannst, muss dieser auch ausgewertet werden.

Und hier findest du einen Code der hervorragend funktioniert (Dank an Serenifly).

Und auch richtig ohne prellen zählt.

HotSystems:
Pins am Drehregler (Rotary Encoder)

Was man allerdings verbessern sollte ist dass man den Encoder-Wert atomar ausliest:

int getEncoder()
{
   noInterrupts();
   int ret = encoderWert;
   interrupts();
   return ret;
}

Das ist ein 16 Bit Wert!

Danke sehr.

Ich gucke mir die Seite mal an.
Bezieht sich aber, sofern ich das richtig verstanden habe, nicht auf
diese Libary GitHub - 0xPIT/encoder at arduino

Trotzdem die Frage zum richtigen Anschluß. Hier wird ja erwähnt einen
Widerstand und einen Kondensator zur verwenden. Habe dieses
Schaltbild gefunden, kann man das üebrnehmen?

Gruß

@noxx2
Für den im Link genannten Sketch benötigst du keine zusätzliche Library und auch keine externen Bauteile zum Entprellen. Das erledigt alles der Code im Sketch.

Wenn du unbedingt mit anderem Code arbeiten willst, dann kann es durchaus sein, das du deinen DrehEncoder entprellen musst und dann kannst du das so wie in deinem Schaltbild gezeigt machen.

Ok, danke dir.

Werde dann zuhause in Ruhe mal mit dem Code rumspielen und den Encoder
direkt anklemmen.
Hatte die Libary bevorzugt, weil dort auch der Button mit aufgenommen wurde,
den viele Encoder auch mit drauf haben. Un leider habe ich keinen Beitrag gefunden,
der sich auf die 0xPIT Libary bezieht, nur das halt: GitHub - 0xPIT/encoder at arduino

Leider wird dort oft auf Quellen von www.meinDUINO.de verwiesen, diese
Seite scheint inzwischen tot zu sein.

Ja, das mit "www.meinDUINO.de" habe ich auch schon gesehen.
Die Soft im Link ist auch daher.

Aber die läuft sicher und gut und man brauch keine Lib.

Das mit dem zusätzlichen Button kann man ja auch noch nachbauen. Der hat ja erst mal nichts mit dem Encoder zu tun und ist auch separat rausgeführt.

Drehencoder per externem Interrupt auszulesen ist nicht die beste Lösung. Mit eine Timer Interrupt regelmäßig Pollen geht am besten.

Wenn man die Kontakte entprellen muss dann am besten mit in Hardware mit einem RC-Glied

nur das halt: GitHub - 0xPIT/encoder at arduino

Da steht auch dass es mit einem Timer geht, also gut.

Man kann es aber auch selbst machen. Was die Lib zusätzlich hat ist die Beschleunigung des Drehens zu berücksichtigen und die Auswertung des Knopfdrückens.

Da ist aber auch ein Beispiel dabei. Wo ist also das Problem?

Leider wird dort oft auf Quellen von www.meinDUINO.de verwiesen, diese

Da gibt in der Tat eine sehr, sehr gute Erklärung wie das geht. Der Code in dem Link oben ist aber der gleiche. Beides basiert auf Code der wahrscheinlich von mikrocontroller.net kommt

Habs nun am laufen, zumindest mit einem Encoder.
Was ich nicht brauchte, habe ich gelöscht, war dann
einfacher zu begreifen.

#include <Keyboard.h>

#include <ClickEncoder.h>
#include <TimerOne.h>

ClickEncoder *encoder;
int16_t last, value;

void timerIsr() 
{
  encoder->service();
}



void setup() 
{
  Serial.begin(9600);
  encoder = new ClickEncoder(A1, A0, A2, 4);  // Encoder Pin an A0 und A1, Button Pin an A2; Restlichen Pins direkt an GND ### 4 = stepsPerNotch

  Timer1.initialize(1000);
  Timer1.attachInterrupt(timerIsr); 
  
  last = -1;
}

void loop() 
{  
  value += encoder->getValue();
  
  if (value != last) 
  {
    if (value < last)
    {
      Serial.print(value);
      Serial.print("\n01: Right \n");
 
    }
    if (value > last)
    {
      Serial.print(value);
      Serial.print("\n01: Left \n");

    }
    last = value;
  }
  
  ClickEncoder::Button b = encoder->getButton();
  if (b != ClickEncoder::Open) 
  {
    switch (b) 
    {
      case ClickEncoder::Pressed:
        Serial.print("01: Pressed \n");
        break;
      case ClickEncoder::Held:
        Serial.print("01: Held \n");
        break;
      case ClickEncoder::Released:
        Serial.print("01: Released \n");
        break;
      case ClickEncoder::Clicked:
        Serial.print("01: Clicked \n");
        break;
      case ClickEncoder::DoubleClicked:
        Serial.print("01: DoubleClicked \n");
        break;
    }
  }    
}

Jetzt brauche ich das Ding für 2 Encoder.
Macht es hier Sinn, hinter allen Variablen einfach
ein 01 (oder 02) zu schreiben und das ganze dann 2x ?

#include <Keyboard.h>

#include <ClickEncoder.h>
#include <TimerOne.h>

ClickEncoder *encoder01;
ClickEncoder *encoder01;
int16_t last01 last02, value01, value02;
...

Ja, das geht. Aber wieso dynamischer Speicher? Das geht auch als ganz normale statische Variable

Und wie?

Mir fehlt es leider etwas an Erfahrung

ClickEncoder encoder(A1, A0, A2, 4);

Dann kann man auf die Methoden mit . statt -> zugreifen

Wobei diese Lib ganz nett ist aber nicht sauber programmiert ist. z.B. wollte der Autor ein paar Methoden const machen und hat dann const vor den Rückgabewert in der Signatur geschrieben. Das kommt aber ans Ende. Und im Konstruktor ist die Reihenfolge der Initialisierungen durcheinander.
Na ja, macht für die Funktion keinen Unterschied. Aber produziert nervige Warnungen wenn man sie aktiviert hat :slight_smile:

Was allerdings nicht so ohne weiteres geht es die Buttons von mehreren Encodern zu verwenden:

Da er nicht beachtet hat jedem Objekt seine eigenen Variablen zu spendieren

Danke für den Link. Ich hoffe ich kann das als Lösung des Problems betrachten,
ich probiere es heute Abend mal aus.

Muss sagen, für einen Encoder mit PushButton läuft das Ding gut. Die serielle Aushabe
ist sauber.

Für die Einbindung des zweiten hatte ich das gestern so getestet:
(ging aber in die Hose, keine Reaktion des zweiten Encodern)

#include <Keyboard.h>
#include <ClickEncoder.h>
#include <TimerOne.h>

ClickEncoder *encoder01;
ClickEncoder *encoder02;
int16_t last01, last02, value01, value02;

void timerIsr() 
{
  encoder01->service();
  encoder02->service();  
}



void setup() 
{
  Serial.begin(9600);
  encoder01 = new ClickEncoder(A1, A0, A2, 4);  // Encoder Pin an A0 und A1, Button Pin an A2; Restlichen Pins direkt an GND ### 4 = stepsPerNotch
  encoder02 = new ClickEncoder(A4, A3, A5, 4);  // Encoder Pin an A3 und A4, Button Pin an A5; Restlichen Pins direkt an GND ### 4 = stepsPerNotch
  
  Timer1.initialize(1000);
  Timer1.attachInterrupt(timerIsr); 
  
  last01 = -1;
  last02 = -1;  
}

void loop() 
{  
  // ENCODER EINS
  value01 += encoder01->getValue();
  
  if (value01 != last01) 
  {
    if (value01 < last01)
    {
      Serial.print(value01);
      Serial.print("\nEncoder 01: Right \n");
 
    }
    if (value01 > last01)
    {
      Serial.print(value01);
      Serial.print("\nEncoder 01: Left \n");

    }
    last01 = value01;
  }
  
  ClickEncoder::Button b = encoder01->getButton();
  if (b != ClickEncoder::Open) 
  {
    switch (b) 
    {
      case ClickEncoder::Pressed:
        Serial.print("Encoder 01: Pressed \n");
        break;
      case ClickEncoder::Held:
        Serial.print("Encoder 01: Held \n");
        break;
      case ClickEncoder::Released:
        Serial.print("Encoder 01: Released \n");
        break;
      case ClickEncoder::Clicked:
        Serial.print("Encoder 01: Clicked \n");
        break;
      case ClickEncoder::DoubleClicked:
        Serial.print("Encoder 01: DoubleClicked \n");
        break;
    }
  } 

  // ENCODER ZWEI
  value02 += encoder02->getValue();
  
  if (value02 != last02) 
  {
    if (value02 < last02)
    {
      Serial.print(value02);
      Serial.print("\nEncoder 02: Right \n");
 
    }
    if (value02 > last02)
    {
      Serial.print(value02);
      Serial.print("\nEncoder 02: Left \n");

    }
    last02 = value02;
  }
  
  ClickEncoder::Button c = encoder01->getButton();
  if (c != ClickEncoder::Open) 
  {
    switch (c) 
    {
      case ClickEncoder::Pressed:
        Serial.print("Encoder 02: Pressed \n");
        break;
      case ClickEncoder::Held:
        Serial.print("Encoder 02: Held \n");
        break;
      case ClickEncoder::Released:
        Serial.print("Encoder 02: Released \n");
        break;
      case ClickEncoder::Clicked:
        Serial.print("Encoder 02: Clicked \n");
        break;
      case ClickEncoder::DoubleClicked:
        Serial.print("Encoder 02: DoubleClicked \n");
        break;
    }
  }  
}

Hallo,
habe die *.h und *.cpp Dateien geändert. Der Code sieht nun so aus, aber
der Arduino zeigt nun keine Reaktion mehr, auf keinem der beiden Encoder

#include <Keyboard.h>

#include <ClickEncoder.h>
#include <TimerOne.h>

ClickEncoder *encoder01;
ClickEncoder *encoder02;

int16_t last01, value01;
int16_t last02, value02;

void timerIsr01() 
{
  encoder01->service();
}

void timerIsr02() 
{
  encoder02->service();
}

void setup() 
{
  Serial.begin(9600);
  encoder01 = new ClickEncoder(A1, A0, A2, 4);  // Encoder Pin an A0 und A1, Button Pin an A2; Restlichen Pins direkt an GND ### 4 = stepsPerNotch
  encoder02 = new ClickEncoder(A4, A3, A5, 4);  // Encoder Pin an A0 und A1, Button Pin an A2; Restlichen Pins direkt an GND ### 4 = stepsPerNotch


  Timer1.initialize(1000);
  Timer1.attachInterrupt(timerIsr01); 
  Timer1.attachInterrupt(timerIsr02);
  
  last01 = -1;
  last02 = -1;
}

void loop() 
{  
  value01 += encoder01->getValue();
  
  if (value01 != last01) 
  {
    if (value01 < last01)
    {
      Serial.print(value01);
      Serial.print("\n01: Right \n");
 
    }
    if (value01 > last01)
    {
      Serial.print(value01);
      Serial.print("\n01: Left \n");

    }
    last01 = value01;
  }
  
  ClickEncoder::Button b = encoder01->getButton();
  if (b != ClickEncoder::Open) 
  {
    switch (b) 
    {
      case ClickEncoder::Pressed:
        Serial.print("01: Pressed \n");
        break;
      case ClickEncoder::Held:
        Serial.print("01: Held \n");
        break;
      case ClickEncoder::Released:
        Serial.print("01: Released \n");
        break;
      case ClickEncoder::Clicked:
        Serial.print("01: Clicked \n");
        break;
      case ClickEncoder::DoubleClicked:
        Serial.print("01: DoubleClicked \n");
        break;
    }

  // E N C O D E R 2

  value02 += encoder02->getValue();
  
  if (value02 != last02) 
  {
    if (value02 < last02)
    {
      Serial.print(value02);
      Serial.print("\n01: Right \n");
 
    }
    if (value02 > last02)
    {
      Serial.print(value02);
      Serial.print("\n02: Left \n");

    }
    last02 = value02;
  }
  
  ClickEncoder::Button c = encoder02->getButton();
  if (c != ClickEncoder::Open) 
  {
    switch (c) 
    {
      case ClickEncoder::Pressed:
        Serial.print("02: Pressed \n");
        break;
      case ClickEncoder::Held:
        Serial.print("02: Held \n");
        break;
      case ClickEncoder::Released:
        Serial.print("02: Released \n");
        break;
      case ClickEncoder::Clicked:
        Serial.print("02: Clicked \n");
        break;
      case ClickEncoder::DoubleClicked:
        Serial.print("02: DoubleClicked \n");
        break;
    }
  }
      
  }    
}

Weiß nicht, ob es daran liegt, das ich Timer1 2x eingebunden habe.

Habs nun mit 1x Timer1 und 1x Timer3 probiert.

Nun reagiert zumindest der erste wieder (Timer1), aber der 2te ist
Mausetod

#include <TimerThree.h>
#include <Keyboard.h>
#include <ClickEncoder.h>
#include <TimerOne.h>


ClickEncoder *encoder01;
ClickEncoder *encoder02;

int16_t last01, value01;
int16_t last02, value02;

void timerIsr01() 
{
  encoder01->service();
}

void timerIsr02() 
{
  encoder02->service();
}

void setup() 
{
  Serial.begin(9600);
  encoder01 = new ClickEncoder(A1, A0, A2, 4);  // Encoder Pin an A0 und A1, Button Pin an A2; Restlichen Pins direkt an GND ### 4 = stepsPerNotch
  encoder02 = new ClickEncoder(A4, A3, A5, 4);  // Encoder Pin an A0 und A1, Button Pin an A2; Restlichen Pins direkt an GND ### 4 = stepsPerNotch


  Timer1.initialize(1000);
  Timer1.attachInterrupt(timerIsr01); 
  
  Timer3.initialize(1000);  
  Timer3.attachInterrupt(timerIsr02);
  
  last01 = -1;
  last02 = -1;
}

void loop() 
{  
  value01 += encoder01->getValue();
  
  if (value01 != last01) 
  {
    if (value01 < last01)
    {
      Serial.print(value01);
      Serial.print("\n01: Right \n");
 
    }
    if (value01 > last01)
    {
      Serial.print(value01);
      Serial.print("\n01: Left \n");

    }
    last01 = value01;
  }
  
  ClickEncoder::Button b = encoder01->getButton();
  if (b != ClickEncoder::Open) 
  {
    switch (b) 
    {
      case ClickEncoder::Pressed:
        Serial.print("01: Pressed \n");
        break;
      case ClickEncoder::Held:
        Serial.print("01: Held \n");
        break;
      case ClickEncoder::Released:
        Serial.print("01: Released \n");
        break;
      case ClickEncoder::Clicked:
        Serial.print("01: Clicked \n");
        break;
      case ClickEncoder::DoubleClicked:
        Serial.print("01: DoubleClicked \n");
        break;
    }

  // E N C O D E R 2

  value02 += encoder02->getValue();
  
  if (value02 != last02) 
  {
    if (value02 < last02)
    {
      Serial.print(value02);
      Serial.print("\n01: Right \n");
 
    }
    if (value02 > last02)
    {
      Serial.print(value02);
      Serial.print("\n02: Left \n");

    }
    last02 = value02;
  }
  
  ClickEncoder::Button c = encoder02->getButton();
  if (c != ClickEncoder::Open) 
  {
    switch (c) 
    {
      case ClickEncoder::Pressed:
        Serial.print("02: Pressed \n");
        break;
      case ClickEncoder::Held:
        Serial.print("02: Held \n");
        break;
      case ClickEncoder::Released:
        Serial.print("02: Released \n");
        break;
      case ClickEncoder::Clicked:
        Serial.print("02: Clicked \n");
        break;
      case ClickEncoder::DoubleClicked:
        Serial.print("02: DoubleClicked \n");
        break;
    }
  }
      
  }    
}

HotSystems:
@Jortox
ich kann leider nicht erkennen, an welchem Pin des Arduino du den Button angeschlossen hast.
Damit du eine Aktion ausführen kannst, muss dieser auch ausgewertet werden.

Und hier findest du einen Code der hervorragend funktioniert (Dank an Serenifly).

Pins am Drehregler (Rotary Encoder)

Und auch richtig ohne prellen zählt.

Habe ich nun auch probiert, läuft aber nicht. Passiert nichts.

Nunja, evtl gehts nur mit einem 2tem Arduino....

Bin leider ratlos.

Gruß

Du brauchst doch keine zwei Timer! Du kannst beider Encoder in der gleichen ISR behandeln!

Oder mach es per Hand. Dann weißt du wenigstens genau was abläuft. Der Code wurde schon verlinkt. Da legt man sich dann zwei altAB und encoderWert Variablen an und dupliziert einfach den Code in der ISR. Der Rest bleibt.

Den anderen Code bekomme ich leider nicht zum laufen.