Hi,
I'm playing around with Rotary Encoders and after some research and tinkering I've settled on this test sketch here based on the work of Marko Pinteric (MP Electronic Devices: Yet another algorithm for rotary encoder control) . I modified it to handle two Rotary Encoders (RE)
Here is the code:
#include <Arduino.h>
#include <FastLED.h> // Just included to use beatsin8 to give Arduino busy-work in line 170. Otherwise not needed in this sketch
// RE 1
#define RE1_CLK_PIN 2
#define RE1_DT_PIN 3
#define RE1_SW_PIN 6
// RE 2
#define RE2_CLK_PIN 8
#define RE2_DT_PIN 9
#define RE2_SW_PIN 10
int RE1[3] = {RE1_CLK_PIN, RE1_DT_PIN, 0}; // Pin, Pin, Rotery Endoder number
int RE2[3] = {RE2_CLK_PIN, RE2_DT_PIN, 1}; // Pin, Pin, Rotery Endoder number
// A turn counter for the rotary encoder (negative = anti-clockwise)
int rotationCounter1 = 0;
int rotationCounter2 = 0;
// Flag from interrupt routine (moved=true)
volatile bool rotaryEncoder1 = false;
volatile bool rotaryEncoder2 = false;
// Interrupt routine just sets a flag when rotation is detected
void rotary1()
{ rotaryEncoder1 = true; }
void rotary2() // Currently not needed. Only if there were 2 additionl interrupt pins
{ rotaryEncoder2 = true; }
// Rotary encoder has moved (interrupt tells us) but what happened? See https://www.pinteric.com/rotary.html
int8_t checkRotaryEncoder(int RE[])
{
// Reset the flag that brought us here (from ISR)
if (RE[2] == 0) { rotaryEncoder1 = false; }
if (RE[2] == 1) { rotaryEncoder2 = false; } // Currently not needed. Only if there were 2 additionl interrupt pins
static uint8_t lrmem[2] = {3,3};
static int lrsum[2] = {0,0};
static int8_t TRANS[] = {0, -1, 1, 14, 1, 0, 14, -1, -1, 14, 0, 1, 14, 1, -1, 0};
// Read BOTH pin states to deterimine validity of rotation (ie not just switch bounce)
int8_t l = digitalRead(RE[0]);
int8_t r = digitalRead(RE[1]);
// Move previous value 2 bits to the left and add in our new values
lrmem[RE[2]] = ((lrmem[RE[2]] & 0x03) << 2) + 2 * l + r;
// Convert the bit pattern to a movement indicator (14 = impossible, ie switch bounce)
lrsum[RE[2]] += TRANS[lrmem[RE[2]]];
/* encoder not in the neutral (detent) state */
if (lrsum[RE[2]] % 4 != 0)
{
return 0;
}
/* encoder in the neutral state - clockwise rotation*/
if (lrsum[RE[2]] == 4)
{
lrsum[RE[2]] = 0;
return 1;
}
/* encoder in the neutral state - anti-clockwise rotation*/
if (lrsum[RE[2]] == -4)
{
lrsum[RE[2]] = 0;
return -1;
}
// An impossible rotation has been detected - ignore the movement
lrsum[RE[2]] = 0;
return 0;
}
void setup()
{
Serial.begin(2000000);
// The module already has pullup resistors on board
pinMode(RE1_CLK_PIN, INPUT);
pinMode(RE1_DT_PIN, INPUT);
pinMode(RE2_CLK_PIN, INPUT);
pinMode(RE2_DT_PIN, INPUT);
// But not for the push switch
pinMode(RE1_SW_PIN, INPUT_PULLUP);
pinMode(RE2_SW_PIN, INPUT_PULLUP);
// // We need to monitor both pins, rising and falling for all states
attachInterrupt(digitalPinToInterrupt(RE1_CLK_PIN), rotary1, CHANGE);
attachInterrupt(digitalPinToInterrupt(RE1_DT_PIN), rotary1, CHANGE);
// attachInterrupt(digitalPinToInterrupt(RE2_CLK_PIN), rotary2, CHANGE); // Nano only has 2 intterup pins
// attachInterrupt(digitalPinToInterrupt(RE2_DT_PIN), rotary2, CHANGE);
Serial.println("Setup completed");
}
void loop()
{
// Has rotary encoder moved?
if (rotaryEncoder1 == 1)
{
// Get the movement (if valid)
int8_t rotationValue1 = checkRotaryEncoder(RE1);
// If valid movement, do something
if (rotationValue1 != 0 )
{
rotationCounter1 += rotationValue1 * 5;
Serial.print(rotationValue1 < 1 ? "L1 " : "R1 ");
Serial.println(rotationCounter1);
}
}
if (rotaryEncoder2 == 0)
{
// Get the movement (if valid)
int8_t rotationValue2 = checkRotaryEncoder(RE2);
// If valid movement, do something
if (rotationValue2 != 0 )
{
rotationCounter2 += rotationValue2 * 5;
Serial.print(rotationValue2 < 1 ? "L2 " : "R2 ");
Serial.println(rotationCounter2);
}
}
if (digitalRead(RE1_SW_PIN) == LOW)
{
rotationCounter1 = 0;
Serial.print("X1");
Serial.println(rotationCounter1);
// Wait until button released (demo only! Blocking call!)
while (digitalRead(RE1_SW_PIN) == LOW)
{
delay(100);
}
}
if (digitalRead(RE2_SW_PIN) == LOW)
{
rotationCounter2 = 0;
Serial.print("X2");
Serial.println(rotationCounter2);
// Wait until button released (demo only! Blocking call!)
while (digitalRead(RE2_SW_PIN) == LOW)
{
delay(100);
}
}
// for (int i = 0; i < 100; i++) {
// int value = beatsin8(random(2,20), 10*i, 255, 0 ,0);
// }
}
The Nano only has two interrupt pins (2, 3), which I'm using for RE #1 and pins 8 and 9 for RE #2.
With the lines 168 - 171 disabled, the code runs beautifully fast. Both REs record the movement perfectly even when spun very fast. So far so good.
However, when I enable lines 168 - 171 (to give the Arduino some busy work) both REs start lagging, especially when spun fast and especially the non interrupt pin RE.
Is this to be expected with the Arduino Nano? Are there ways around this issue with the Nano, especially when spinning a RE fast?
Would a different chip like the esp32 work better because of faster CPU freqs?
I appreciate your time and insight. Thank you for any help.