In the meantime I googled a bit about rotary encoder debouncing and found several links, here are just a few::
https://forum.arduino.cc/t/debounce-rotary-encoder/967691
https://garrysblog.com/2021/03/20/reliably-debouncing-rotary-encoders-with-arduino-and-esp32/
https://www.best-microcontroller-projects.com/rotary-encoder.html
From the latter I have taken the function to read the encoder and very slightly modified it to fit into the existing sketch from above. It should work with most "nervous" rotary encoders ...
//#define WOKWI // For use with keyboard.h just make this line a comment -> //#define WOKWI
#ifdef WOKWI
#define KEY_TAB "KEY_TAB"
#define KEY_LEFT_SHIFT "KEY_LEFT_SHIFT"
#define KEY_RETURN "KEY_RETURN"
struct kbt {
void begin();
void write(String aKey);
void press(String aKey);
void release(String aKey);
} Keyboard;
void kbt::begin() {}
void kbt::write(String aKey) {
Serial.println(aKey + "_press&released");
}
void kbt::press(String aKey) {
Serial.println(aKey + "_pressed");
}
void kbt::release(String aKey) {
Serial.println(aKey + "_released");
}
#else
#include <Keyboard.h>
#endif
const int encoderClk = 2; // Rotary encoder pin 1
const int encoderDT = 3; // Rotary encoder pin 2
const int buttonPin = 4; // Rotary encoder button pin
int section = 0;
const int numSections = 7;
const char* sectionNames[] = {"Text Entry", "Types", "Instruments", "Styles", "Banks", "User", "Blank Section"};
enum Direction {NONE, LEFT, RIGHT};
struct encoderType {
byte buttonPin;
byte encoderClk;
byte encoderDT;
boolean buttonReleased();
Direction turned();
void init(byte bPin, byte ClkPin, byte DTPin);
};
void encoderType::init(byte bPin, byte ClkPin, byte DTPin) {
encoderType::encoderClk = ClkPin;
encoderType::encoderDT = DTPin;
encoderType::buttonPin = bPin;
pinMode(ClkPin, INPUT_PULLUP);
pinMode(DTPin, INPUT_PULLUP);
pinMode(bPin, INPUT_PULLUP);
}
boolean encoderType::buttonReleased() {
static unsigned lastChange = 0;
static byte state = HIGH;
static byte lastState = HIGH;
byte actState = digitalRead(encoderType::buttonPin);
if (actState != lastState) {
lastChange = millis();
lastState = actState;
}
if (state != lastState && millis() - lastChange > 30) {
state = lastState;
if (state) return true;
}
return false;
}
// The following function was derived from https://www.best-microcontroller-projects.com/rotary-encoder.html
Direction encoderType::turned() {
static int8_t rot_enc_table[] = {0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0};
static uint8_t prevNextCode = 0;
static uint16_t store=0;
prevNextCode <<= 2;
if (digitalRead(encoderType::encoderDT)) prevNextCode |= 0x02;
if (digitalRead(encoderType::encoderClk)) prevNextCode |= 0x01;
prevNextCode &= 0x0f;
// If valid then store as 16 bit data.
if (rot_enc_table[prevNextCode] ) {
store <<= 4;
store |= prevNextCode;
if ((store&0xff)==0x2b) return LEFT;
if ((store&0xff)==0x17) return RIGHT;
}
return NONE;
}
encoderType myEncoder;
void setup() {
Serial.begin(115200);
myEncoder.init(buttonPin, encoderClk, encoderDT);
Keyboard.begin();
}
void loop() {
Direction d = myEncoder.turned();
if (d == LEFT) DecreaseSection();
if (d == RIGHT) IncreaseSection();
if (myEncoder.buttonReleased()) SendReturn();
}
void SendReturn() {
Keyboard.write(KEY_RETURN);
}
void IncreaseSection() {
section++;
if (section >= numSections) {
section = 0;
}
Serial.println("section++\t" + String(section));
if (section == 6) {
// Send 5 tabs for Blank Section
for (int i = 0; i < 5; i++) {
Keyboard.write(KEY_TAB);
}
} else {
Keyboard.write(KEY_TAB);
}
Serial.print("Section: ");
Serial.println(sectionNames[section]);
}
void DecreaseSection() {
section--;
if (section < 0) {
section = numSections - 1;
}
Serial.println("section--\t" + String(section));
if (section == 6) {
// Send 5 shift+tabs for Blank Section
Keyboard.press(KEY_LEFT_SHIFT);
for (int i = 0; i < 5; i++) {
Keyboard.write(KEY_TAB);
}
Keyboard.release(KEY_LEFT_SHIFT);
} else {
Keyboard.press(KEY_LEFT_SHIFT);
Keyboard.write(KEY_TAB);
Keyboard.release(KEY_LEFT_SHIFT);
}
Serial.print("Section: ");
Serial.println(sectionNames[section]);
}
The test version to be found at https://wokwi.com/projects/361719624996514817
Please make sure that the CLK pin of the encoder is connected to pin 2 and the DT pin is connected to pin 3 of your board!
State Machines are absolutely useful if you have an application that stays in different states and leaves theses states when certain events take place. In this specific case the application is generally idling around in loop() waiting for one of three events:
- Right turn
- Left turn
- Button pressed
to take a specific action. It returns immediately after the action to loop(). That can be handled without a state machine ... Anyway it is worth to learn how that concept works!
Good luck!