I bought a few KY-040 rotary encoders from China and tried several examples seen here and on other blogs. It didn't make sense that I needed to use Interrupts to make it work properly, but got very erratic results and threw them in a drawer for later and forgot about them. Last week I got a new digital oscilloscope and decided to put one on a board and hook it up to the scope for grins. I discovered a few very important things with my version of rotary encoder:
- Debouncing with a capacitor seemed to do nothing but mess up an otherwise fine looking pair of pulses.
- The pulses were slow enough that surely a 16mhz Arduino should be able to be used to count pulses reliably.
- The logic in most of the non-interrupt sketches posted here and elsewhere have one very important flaw (at least for my encoders) that makes the reading fail.
If you count a pulse every time aVal changes state, (LOW to HIGH and HIGH to LOW) and don't otherwise account for it, you will negate your counts and only count the spurious ones. Therefore use aVal only when it transitions from HIGH to LOW (or visa-versa, but this works, so I use it.) This seems to be the reason many have resorted to using Interrupts so they can use FALLING or RISING in their code. A change in the 'if' statement mimics the FALLING interrupt and made it all work with my encoders:
if ((aVal != aLast)&&(aVal==LOW)) {
The bold text above is the change. Now we use the datapoint only when transitioning from HIGH to LOW to indicate that the knob moved. And...
if(bVal == LOW){ encoderCount++; }
else { encoderCount--; } }
Gives direction. If bVal is also LOW, then the knob was turned CW, so lets increment our counter by one step. Else, if bVal was HIGH, CCW and let's decrement our counter.
This works from fairly slow up to a fairly fast knob rotation. And NO USE OF INTERRUPTS!
Full Code with Serial Print to Monitor:
//Constants
int pinA = 7; // Connected to CLK on KY-040
int pinB = 6; // Connected to DT on KY-040
int encoderCount = 0;
int aLast;
int aVal;
int bVal;
void setup() {
Serial.begin(9600);
pinMode (pinA, INPUT);
pinMode (pinB, INPUT);
aLast = digitalRead(pinA);
}
void loop() {
aVal = digitalRead(pinA);
bVal = digitalRead(pinB);
if ((aVal != aLast)&&(aVal==LOW)) { // Means the knob is rotating
if(bVal == LOW){ encoderCount++;} // If bVal is Low, too, CW ++
else {encoderCount--;} // else, CCW --
Serial.print("PinA: "); Serial.print(aVal); //Always 0 due to 'if'
Serial.print(" PinB: "); Serial.print(bVal); // Changes with Direction
Serial.print(" Counter: ");Serial.println(encoderCount);
}
aLast = aVal; //Don't forget to reset aLast
}
If the scope pictures came through, one is of the pulse when .47Uf cap is added to debounce. Looks like nothing good can come of that, and the other shows a typical pulse without debouncing.

