This code was developed from an idea by Oleg Mazurov and is presented here as a complete class that can be added as a tab in an Arduino sketch. The code will detect forward and reverse movement and will detect if the encoder is turned more quickly.
Hopefully, the code is self explanatory with the possible exception of anti-bounce approach taken. Oleg Mazurov has described his work at www.circuitsathome.com. The scaleReduction
parameter is a means to control the sensitivity of the rotation, i.e., a setting of 8 would require 8 clockwise rotation changes before output was reported as a single clockwise rotation.
An example of using this code is shown below with the header and code files shown further below. This particular example was taken from a larger project and used a raw pointer to access the class, there is no requirement to use pointers.
const uint8_t SLOW_INCREMENT = 10;
const uint8_t FAST_INCREMENT = 100;
RotaryEncoder * rotaryEncoder;
double rotValue = 0;
void setup()
{
rotaryEncoder = new RotaryEncoder(
ROTARY_ENCODER_SW_PIN,
ROTARY_ENCODER_CLK_PIN,
ROTARY_ENCODER_DT_PIN);
rotaryEncoder->scaleReduction = 2;
}
void loop()
{
RotaryEncoderState rData = rotaryEncoder->getState();
int adjustment = SLOW_INCREMENT;
if (rData.isFast) {
adjustment = FAST_INCREMENT;
}
if (rData.rotation == RotaryEncoderRotationClockwise) {
rotValue += adjustment;
}
else if (rData.rotation == RotaryEncoderRotationAntiClockwise) {
rotValue -= adjustment;
}
if (rData.rotation != RotaryEncoderRotationStationary) {
}
}
RotaryEncoder.h
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <arduino.h>
#include <stdint.h>
enum RotaryEncoderRotation {
RotaryEncoderRotationStationary,
RotaryEncoderRotationClockwise,
RotaryEncoderRotationAntiClockwise
};
class RotaryEncoderState {
public:
RotaryEncoderRotation rotation;
bool buttonPressed;
bool isFast;
};
class RotaryEncoder {
public:
RotaryEncoder (int swPin, int clkPin, int dtPin);
RotaryEncoderState getState();
int scaleReduction;
private:
int _swPin;
int _clkPin;
int _dtPin;
uint8_t history = 0;
int clockwiseScaleCounter = 0;
int antiClockwiseScaleCounter = 0;
unsigned long _timeStamp = 0;
RotaryEncoderState _previousState;
bool isBounce();
bool isFast();
};
RotaryEncoder.cpp
#include "RotaryEncoder.h"
const byte ENC_STATES[] PROGMEM = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
const int BOUNCE_MICROS = 5000;
const long SLOW_MICROS = 80000;
RotaryEncoder::RotaryEncoder(int swPin, int clkPin, int dtPin) {
_swPin = swPin;
_clkPin = clkPin;
_dtPin = dtPin;
pinMode(_swPin, INPUT);
digitalWrite(_swPin, HIGH);
pinMode(_clkPin, INPUT);
digitalWrite(_clkPin, HIGH);
pinMode(_dtPin, INPUT);
digitalWrite(_dtPin, HIGH);
_timeStamp = micros();
}
RotaryEncoderState RotaryEncoder::getState()
{
RotaryEncoderState result;
uint8_t clk = digitalRead(_clkPin);
uint8_t dt = digitalRead(_dtPin);
clk <<= 1;
uint8_t clkDt = dt | clk;
history <<= 2;
history |= ( clkDt & 0x03 );
int rotation = pgm_read_byte_near(ENC_STATES + (history & 0x0f));
result.rotation = RotaryEncoderRotationStationary;
if ((int8_t)rotation > 0 && !isBounce()) {
clockwiseScaleCounter++;
antiClockwiseScaleCounter = 0;
if (clockwiseScaleCounter > scaleReduction) {
result.rotation = RotaryEncoderRotationClockwise;
result.isFast = isFast();
clockwiseScaleCounter = 0;
}
}
if ((int8_t)rotation < 0 && !isBounce()) {
antiClockwiseScaleCounter++;
clockwiseScaleCounter = 0;
if (antiClockwiseScaleCounter > scaleReduction) {
result.rotation = RotaryEncoderRotationAntiClockwise;
result.isFast = isFast();
antiClockwiseScaleCounter = 0;
}
}
result.buttonPressed = !digitalRead(_swPin);
_previousState = result;
return result;
}
bool RotaryEncoder::isBounce(){
return micros() - _timeStamp < BOUNCE_MICROS;
}
bool RotaryEncoder::isFast(){
bool result = micros() - _timeStamp < SLOW_MICROS;
_timeStamp = micros();
return result;
}