Vorgeschichte: Meine kleine Italienerin macht zwar leckeren Espresso, die Auswahl des Kaffees mit einem Drehimpulsgeber erinnert aber eher an ein Glücksspiel. Wenn Profis diese Technik nicht in den Griff bekommen, dann, so meine Schlußfolgerung, will ich das mit einem Arduino auch nicht probieren.
Eine Lösung, wie es scheint, habe ich nun in der c't 18/2020 in dem Artikel "Drehknopf bis 11" von Pia Merkert gefunden. Gerne würde ich aus dem Inhalt zitieren, aber da dürfte der Verlag wohl nicht einverstanden sein. Den Link zur Quelle darf ich hoffentlich ebenso wie den Link zum Repository weitergeben.
Meine Drehimpulsgeberplatinen schalten, anders als im Beitrag vorgeschlagen, nach GND. Zum Testen habe ich daher das Programm geringfügig verändert. Schon mal im Schwung habe ich dann noch Variablentypen angepaßt, enum verwendet, #define rausgeschmissen, den Taster entprellt und alles in eine Funktion mit Rückgabewerten verschoben:
const byte ENCODER_A_PIN = 3;
const byte ENCODER_B_PIN = 2;
const byte SWITCH_PIN = 4;
void setup() {
Serial.begin(115200);
Serial.println("\nStart");
pinMode(ENCODER_A_PIN, INPUT);
pinMode(ENCODER_B_PIN, INPUT);
pinMode(SWITCH_PIN, INPUT);
}
void loop() {
int8_t state = 0;
if (rotaryEncoder(state)) {
Serial.println("- SWITCH -");
}
if (state == -1) {
Serial.println("<-- ");
}
if (state == 1) {
Serial.println(" -->");
}
}
bool rotaryEncoder(int8_t &delta) {
delta = 0;
enum {STATE_LOCKED, STATE_TURN_RIGHT_START, STATE_TURN_RIGHT_MIDDLE, STATE_TURN_RIGHT_END, STATE_TURN_LEFT_START, STATE_TURN_LEFT_MIDDLE, STATE_TURN_LEFT_END, STATE_UNDECIDED};
static uint8_t encoderState = STATE_LOCKED;
bool a = !digitalRead(ENCODER_A_PIN);
bool b = !digitalRead(ENCODER_B_PIN);
bool s = !digitalRead(SWITCH_PIN);
static bool switchState = s;
switch (encoderState) {
case STATE_LOCKED:
if (a && b) {
encoderState = STATE_UNDECIDED;
}
else if (!a && b) {
encoderState = STATE_TURN_LEFT_START;
}
else if (a && !b) {
encoderState = STATE_TURN_RIGHT_START;
}
else {
encoderState = STATE_LOCKED;
};
break;
case STATE_TURN_RIGHT_START:
if (a && b) {
encoderState = STATE_TURN_RIGHT_MIDDLE;
}
else if (!a && b) {
encoderState = STATE_TURN_RIGHT_END;
}
else if (a && !b) {
encoderState = STATE_TURN_RIGHT_START;
}
else {
encoderState = STATE_LOCKED;
};
break;
case STATE_TURN_RIGHT_MIDDLE:
case STATE_TURN_RIGHT_END:
if (a && b) {
encoderState = STATE_TURN_RIGHT_MIDDLE;
}
else if (!a && b) {
encoderState = STATE_TURN_RIGHT_END;
}
else if (a && !b) {
encoderState = STATE_TURN_RIGHT_START;
}
else {
encoderState = STATE_LOCKED;
delta = -1;
};
break;
case STATE_TURN_LEFT_START:
if (a && b) {
encoderState = STATE_TURN_LEFT_MIDDLE;
}
else if (!a && b) {
encoderState = STATE_TURN_LEFT_START;
}
else if (a && !b) {
encoderState = STATE_TURN_LEFT_END;
}
else {
encoderState = STATE_LOCKED;
};
break;
case STATE_TURN_LEFT_MIDDLE:
case STATE_TURN_LEFT_END:
if (a && b) {
encoderState = STATE_TURN_LEFT_MIDDLE;
}
else if (!a && b) {
encoderState = STATE_TURN_LEFT_START;
}
else if (a && !b) {
encoderState = STATE_TURN_LEFT_END;
}
else {
encoderState = STATE_LOCKED;
delta = 1;
};
break;
case STATE_UNDECIDED:
if (a && b) {
encoderState = STATE_UNDECIDED;
}
else if (!a && b) {
encoderState = STATE_TURN_RIGHT_END;
}
else if (a && !b) {
encoderState = STATE_TURN_LEFT_END;
}
else {
encoderState = STATE_LOCKED;
};
break;
}
uint32_t current_time = millis();
static uint32_t switch_time = 0;
const uint32_t bounce_time = 30;
bool back = false;
if (current_time - switch_time >= bounce_time) {
if (switchState != s) {
switch_time = current_time;
back = s;
switchState = s;
}
}
return back;
}
Testergebnis: Mit einer Platine nur mit PullUp-Widerständen (Beispiel zur Veranschaulichung) erziele ich eine perfekte Funktionalität.