Introduction: Tutorial of Rotary Encoder With Arduino
Rotary encoder is an electronic component capable of monitoring movement and position when rotating. Rotary encoder utilizes optical sensors that can generate pulses when the rotary encoder rotates. Application of the rotary encoder usually as a mechanical or robotic motion monitor can also be used for menu selection on the display. Rotary encoder has two outputs so that it can distinguish between negative (CW) and positive (CCW) rotation and also has a single button.
Step 1: Pulse Flow of Rotary Encoder
The pulse flow generated by the following rotary encoder is like the picture above.
Step 2: Pinout of Rotary Encoder
Explanation:
- GND --> GND
- + --> +5V
- SW --> button of rotary encoder when pressed
- DT --> Data
- CLK --> Data 2
One of the DT or CLK pins must be connected to the interrupt foot of Arduino Uno, or both of the DT and CLK are connected to the interrupt pin.
Step 3: Schematic
- GND à GND Arduino Uno
- + à +5V Arduino Uno
- SW à PIN 4 Arduino Uno
- DT à PIN 3 Arduino Uno
- CLK à PIN2 Arduino Uno
Step 4: Code
In the following tutorial, which will be used as an interrupt is PIN 2 of Arduino Uno, while PIN 3 is only used as a regular input.
#define encoder0PinA 2
#define encoder0PinB 3 #define encoder0Btn 4 int encoder0Pos = 0; void setup() { Serial.begin(9600); pinMode(encoder0PinA, INPUT_PULLUP); pinMode(encoder0PinB, INPUT_PULLUP); pinMode(encoder0Btn, INPUT_PULLUP); attachInterrupt(0, doEncoder, CHANGE); } int valRotary,lastValRotary; void loop() { int btn = digitalRead(encoder0Btn); Serial.print(btn); Serial.print(" "); Serial.print(valRotary); if(valRotary>lastValRotary) { Serial.print(" CW"); } if(valRotary {Serial.print(" CCW"); } lastValRotary = valRotary; Serial.println(" "); delay(250); } void doEncoder() { if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) { encoder0Pos++; } else { encoder0Pos--; } valRotary = encoder0Pos/2.5; }
In line 10 of the sketch above is used to enable the interrupt of pin 2 Arduino Uno. In the "doEncoder" function is calculated from the rotary encoder. If the value of DT and CLK (pin interrupt of Arduino Uno) is the same, then the "encoder0Pos" variable will be incremented / added, in addition to that condition, the "encoder0Pos" variable is decremented.
Step 5: Explanation
ValRotary value is the value of the number of steps that have been running. ValRotary value is obtained from rotary sensor encoder reading value divided by 2.5. A value of 2.5 is obtained from the test, since one step of the rotary encoder may exceed 1, so divide by 2.5 for its value according to the perstep and also the addition of the read delay.
While on line 19 - 25 is a program to determine whether rotary rotary encoder CW or CCW. The explanation of lines 19 - 25 is when the current rotary encoder readout is greater than the previous rotary data then expressed as CW. Whereas if the current reading is smaller than the previous reading then it is stated as CCW.
Step 6: Output
1 = the start button value of the rotary when it has not been pressed
7 Comments
9 months ago
Most (especially the inexpensive ones) rotary encoders do not use optical sensing; they're strictly mechanical devices.
9 months ago
I found that to get accurate tics for a continuous rotary encoder, I had to set attachInterrupt(0, doEncoder, RISING); // Not CHANGE
I added bits to the generous code provided above to work well with a continuous rotary encoder. I have tested it and it appears to work well. I have a 600 tic/rotation encoder and the output rate was too fast for my downstream acquisition system so I only send a signal every xx tics. It outputs a comma separated value file if you save the com output...
#define encoderPinA 2 // Needs to be the interrupt pin on the Arduino
#define encoderPinB 3 // Second signal line from encoder
#define outPinTic 4 // Sends a signal for each Tic
#define outPinDirection 5 // 1 if CW, 0 if CCW
#define outPinFullTic 6 // Sends a pulse for each full rotation.
#define TTL_DELAY_MS 2 // Duration the TTL is high. Be sure you data acquisition system can handle the delay.
#define TICS_PER_SIGNAL 20 // Send a signal every xx Tics. Many acquisition systems have a hard time keeping up with the full tic rate (up to 5000 tics/second) so we only send a signal every xx tics.
#define TICS_PER_ROTATION 600 // Tics for a full 360degrees rotation.
long encoderPos = 0;
int tempEncoderPos = 0;
int tempEncoderPosFullRot = 0;
// int ticCountCW = 0;
// int ticCountCCW = 0;
void setup() {
Serial.begin(115200); // Be sure your com port is set to receive this. The high rate helps if spinning fast perhaps.
pinMode(encoderPinA, INPUT_PULLUP);
pinMode(encoderPinB, INPUT_PULLUP);
pinMode(outPinTic, OUTPUT);
pinMode(outPinDirection, OUTPUT);
digitalWrite(outPinTic, 0);
digitalWrite(outPinDirection, 0);
attachInterrupt(0, doEncoder, RISING); // Must be rising
}
long lastValRotary;
int send_rotary_signal = 0;
int send_full_rot_signal = 0;
unsigned long currentMillis = 0;
void loop() {
if (send_rotary_signal > 0 ) {
currentMillis = millis();
send_rotary_signal = 0;
Serial.print("R,"); // for parsing the file later, this can be used to parse out the rows for the rotary encoder in case this code is added to a larger set of code that has different serial outputs interleaved with this output.
Serial.print(currentMillis);
digitalWrite(outPinTic, HIGH);
if (encoderPos > lastValRotary) {
digitalWrite(outPinDirection, HIGH);
Serial.print(",1");
}else if (encoderPos < lastValRotary) {
digitalWrite(outPinDirection, LOW);
Serial.print(",-1");
}else {
// This should rarely if ever happen.
Serial.print(",0");
}
lastValRotary = encoderPos;
delay(TTL_DELAY_MS); // A possible alternative would be to do this asynchronously.
digitalWrite(outPinTic, LOW); // ensures the direction signal and tic overlap.
Serial.print(",");
Serial.print(encoderPos);
if (send_full_rot_signal == 1) {
send_full_rot_signal = 0;
digitalWrite(outPinFullTic, HIGH);
delay(TTL_DELAY_MS);
digitalWrite(outPinFullTic, LOW);
Serial.print(",1");
}else{
Serial.print(",0");
}
Serial.println();
}
}
void doEncoder()
{ // Only runs on rising edge
if (digitalRead(encoderPinA) != digitalRead(encoderPinB))
{
encoderPos++;
tempEncoderPos++;
tempEncoderPosFullRot++;
//ticCountCW++; // Count contiguous rotations in one direction. This might be useful in the future for identifying jitter from a shaky hand/paw
//ticCountCCW = 0;
} else {
encoderPos--;
tempEncoderPos--;
tempEncoderPosFullRot--;
// ticCountCCW++;
// ticCountCW = 0;
}
if (abs(tempEncoderPos) >= TICS_PER_SIGNAL) {
tempEncoderPos = 0;
send_rotary_signal = 1;
}
if (abs(tempEncoderPosFullRot) >= TICS_PER_ROTATION) {
tempEncoderPosFullRot = 0;
send_full_rot_signal = 1;
}
}
3 years ago
#define encoderDT 2
#define encoderCLK 3 // Interrupt
#define encoderSW 4
int previousDT;
int previousCLK;
int previousSW;
void setup()
{
Serial.begin(9600);
pinMode(encoderDT, INPUT_PULLUP);
pinMode(encoderCLK, INPUT_PULLUP);
pinMode(encoderSW, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(encoderCLK), doEncoder, CHANGE);
previousDT = digitalRead(encoderDT);
previousCLK = digitalRead(encoderCLK);
previousSW = digitalRead(encoderSW);
}
int encoderPos = 0;
int previousEncoderPos = 0;
void loop()
{
int actualSW = digitalRead(encoderSW); // Without debouncing
if (actualSW != previousSW)
{
Serial.print("SW= ");
Serial.println(actualSW);
previousSW = actualSW;
}
if(encoderPos > previousEncoderPos)
{
Serial.print(actualSW);
Serial.print(" ");
Serial.print(encoderPos);
Serial.println(" CW");
}
if(encoderPos < previousEncoderPos)
{
Serial.print(actualSW);
Serial.print(" ");
Serial.print(encoderPos);
Serial.println(" CCW");
}
previousEncoderPos = encoderPos;
// delay(1000);
delay(40);
}
void doEncoder()
{
int actualCLK = digitalRead(encoderCLK);
int actualEncoderDT = digitalRead(encoderDT);
if ((actualCLK == 1) and (previousCLK == 0))
{
if (actualEncoderDT == 1)
encoderPos--;
else
encoderPos++;
}
if ((actualCLK == 0) and (previousCLK == 1))
{
if (actualEncoderDT == 1)
encoderPos++;
else
encoderPos--;
}
previousCLK = actualCLK;
}
Question 5 years ago on Step 5
Can you explain where 2.5 come from?
Question 5 years ago on Step 5
You state "ValRotary value is the value of the number of steps that have been running". What does "Step" mean?
Regard,
Question 5 years ago on Step 6
if(valRotary { Serial.print(" CCW");
}
the code is not working it shows some error
Answer 5 years ago
It should be:
if(valRotary<lastValRotary)
{
Serial.print(" CCW");
}