Quadrature rotary encoders, also known as rotary pulse generators, are popular input devices for embedded platforms, including Arduino. Several rotary encoder code examples are posted on Arduino site and elsewhere, however, they treat encoder as a pair of switches, adding decoding/debouncing overhead. For many years, I used an algorithm based on the fact that quadrature encoder is a Gray code generator and if treated as such, can be read reliably in 3 straight step without need for debouncing. As a result, the code I’m using is very fast and simple, works very well with cheap low-quality encoders, but is somewhat cryptic and difficult to understand. Soon after posting one of my projects where I used rotary encoder to set motor speed i started receiving e-mails asking to explain the code. This article is a summary of my replies – I’m presenting small example written for the purpose of illustrating my method. I’m also going through the code highlighting important parts.
The hardware setup can be seen on title picture. The encoder from Sparkfun is connected to a vintage Atmega168-based Arduino Pro. Common pin of the encoder is connected to ground, pins A and B are connected to pins 14 and 15, AKA Analog pins 0 and 1, configured as digital inputs. We also need a serial connection to the PC to receive power, program the Arduino, and send program output to the terminal. For this purpose, Sparkfun FTDI basic breakout is used.
Connecting encoder pins to pins 0 and 1 of 8-bit MCU port makes encoder reading code very simple. If analog pins are needed for something else, it is possible to move encoder to digital pins 8,9 or 0,1 (losing serial port) with no modification of code logic. While technically using any two consecutive port pins is possible with a bit of tweaking, using non-consecutive pins for encoder input with this method is not recommended. Lastly, it sometimes hard to determine which encoder pin is A and which is B; it is easier to connect them at random and if direction is wrong, swap the pins.
Example code is posted below. It is complete sketch – you can copy it from this page and paste into Arduino IDE window, compile, upload and run. The result of rotating the encoder can be seen in terminal window. Note that serial speed in the sketch is set to 115200, you will need to set your PC terminal to that speed as well. The explanation of the code is given after the listing.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | /* Rotary encoder read example */ #define ENC_A 14 #define ENC_B 15 #define ENC_PORT PINC void setup() { /* Setup encoder pins as inputs */ pinMode(ENC_A, INPUT); digitalWrite(ENC_A, HIGH); pinMode(ENC_B, INPUT); digitalWrite(ENC_B, HIGH); Serial.begin (115200); Serial.println("Start"); } void loop() { static uint8_t counter = 0; //this variable will be changed by encoder input int8_t tmpdata; /**/ tmpdata = read_encoder(); if( tmpdata ) { Serial.print("Counter value: "); Serial.println(counter, DEC); counter += tmpdata; } } /* returns change in encoder state (-1,0,1) */ int8_t read_encoder() { static int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0}; static uint8_t old_AB = 0; /**/ old_AB <<= 2; //remember previous state old_AB |= ( ENC_PORT & 0x03 ); //add current state return ( enc_states[( old_AB & 0x0f )]); } |
First three #defines set names for port pins. This way, if you want to use different pins for your encoder, you can do it here without sifting through code looking for pin names. If you’d like to move encoder pins to Arduino pins 8 and 9, ENC_PORT shall be defined as PINB, and for pins 0,1 as PIND.
In setup() function hardware is initialized. pinMode sets the pin as input so we can read it and digitalWrite turns on pull-up resistor on the pin so it won’t dangle in the air when switch is open. Finally, the serial port is initialized and message is printed as an indicator that port is working – if you can’t see “Start” on your terminal screen, check PC port settings.
Let’s go down to line 30, where read_encoder() function starts. enc_states[] array is a look-up table; it is pre-filled with encoder states, with “-1” or “1” being valid states and “0” being invalid. We know that there can be only two valid combination of previous and current readings of the encoder – one for the step in a clockwise direction, another one for counterclockwise. Anything else, whether it’s encoder that didn’t move between reads or an incorrect combination due to switch bouncing, is reported as zero.
Note that old_AB is declared static. This means that it’s value will be retained between function calls therefore previous encoder reading will be preserved. When function is called, old_AB gets shifted left two times (line 36) saving previous reading and setting two lower bits to “0” so the current reading can be correctly ORed here. Then ENC_PORT & 0x03 reads the port to which encoder is connected and sets all but two lower bits to zero so when you OR it with old_AB bits 2-7 would stay intact. Then it gets ORed with old_AB (line 37, old_AB |= ( ENC_PORT & 0x03 )). At this point, we have previous reading of encoder pins in bits 2,3 of old_AB, current readings in bits 0,1, and together they form index of (AKA pointer to) enc_states[] array element containing current state – either increment, decrement, or no change. All that is left to do is return this state to the calling function, and line 38 does just that. Upper half of old_AB gets zeroed by old_AB & 0x0f – if we don’t do this, we will be reading memory past enc_states[] array.
The above paragraph was pretty long explanation for mere 5 lines of code (counting two declarations); if you are reading this, 98% of work is done. The rest of the code is very easy. We are now moving up to line 17, where loop() function is residing. counter variable is the one which is modified by encoder and tmpdata is used to hold the reading. On line 22, encoder is read. If encoder state has changed, i.e., read_encoder() returned non-zero, current value of counter variable is printed and then counter is updated with tmpdata. This is done within conditional statement on lines 22-27. The loop then ends and starts again.
Now let’s briefly talk about real-life applications of this method. If you’re using Sparkfun encoder, you’ll notice that it gives 4 increments per click and it’s impossible to make it hold position between clicks. Therefore, to count clicks you will need to divide counter variable by 4. Also, if counter larger than 255 is necessary, the declaration for it would have to be changed to uint16_t or longer. Another nice modification, left out to keep code simple, would be to move enc_states[] array to program memory using PROGMEM type saving several bytes of precious RAM.
In order for this method to work well, read_encoder() function needs to be called fairly often . To see what happens when loop is slow, lower serial port speed – go to line 13, change “115200” to “9600”, recompile the sketch and run (don’t forget to reconfigure the terminal on PC side). Picture on the left shows the result. Note that encoder goes down from 237 to 229, then jumps up to 230, then continues going down. Sometimes it counts correctly but gives 2 or 3 states per click instead of 4. Instabilities like this are good indication of slow loop. If encoder is used to things like setting motor speed or sound volume, this behavior won’t do much harm because encoder will never skip back more than one state and overall counting would still be in the right direction. However, if encoder is used for interfacing with display, glitches like that are very annoying. Therefore, for user interface applications, loop time should be kept short and in some cases it may be even necessary to convert encoder reading function into interrupt service routine. Given the size and speed of encoder read function, such conversion can be done without much difficulty.
To summarize: I presented a method to convert quadrature rotary encoder output into direction information using look-up table. The method makes debouncing unnecessary, and as a result, conversion function implemented with this method is very short, fast, and works very well on small 8-bit MCU platforms, such as Arduino.
Oleg.
[EDIT] I posted a new article describing reading an encoder from an ISR.
I’m confused by the use of ENC_PORT, and new to Arduino. I also have an UNO so that may not be helping. I assume that ENC_PORT looks at the state of the pins without the use of digitalRead(). would the following be about correct for line 37?
old_AB |= (digitalRead(ENC_A) * 2) + dititalRead(ENC_B);
p.s. my issue is that I may want to use 2 pins that are not next to each other, or actually I don’t like to update 3 variables if I change something when I can get away with only changing 2 🙂
If I can get this working I can get rid of 3 switches and 3 resistors as well as solving my debounce and long hold issues 😀
Thanks
Making two reads of the encoder is not correct. Think of what happens if an encoder changes state between reads.
This code won’t work with non-adjacent pins.
Ok, I can’t get this working and I’m a lot stuck. My understanding is that the pins should cycle… for example…
00, 01, 11, 10 –> and repeat going one way
and
00, 10, 11, 01 –> and repeat going the other way.
I understand about digitalRead being bad for for testing I’d expect it to be ok. I have the same physical setup as you and this sketch.
The issue is that it output 11 a whole lot. If I rotate the spindle one click, most of the time I get 10, and then back to 11. If I go the other way I get 01 briefly, then 11 again. Sometimes if I go a bit nuts I have managed to get 00. This behaviour is the same if I print PINC & 0x03 with 1, 2 and 3s being output.
Is there anything else I can look at? Is my Encoder busted? Is my brain busted? 🙂
Thanks.
Many encoders go through 4 states between clicks. Some of them read 00 at detent, others (like yours) read 11. Move your encoder between clicks slowly and you shall see all four states.
BTW, this is the purpose of initialization on line 34 of the example – for your encoder you need to set old_AB to 0x03 if you want it to work correctly at power-up.
Thanks for the nice code. There is one point I’m still stuck on. In the explanation, you say: “If you’re using Sparkfun encoder, you’ll notice that it gives 4 increments per click and it’s impossible to make it hold position between clicks. Therefore, to count clicks you will need to divide counter variable by 4.”
The code probably needs to be more sophisticated than simply inserting a:
counter = counter/4; after line 26. There are still 4 values returned from the read_encoder(). Would it be possible for you to post an example that would only increment the counter once per click? … and update the serial output once per click as well?
Hello, Me again 🙂 I found the exact same encoder as you, the sparkfun one, and its much better.. Other encoders don’t seem to have the same workings. Another thing I found – 1 “click” on the rotor increments the counter by 4 because the code detects each change in both pulses, so I use a long counter (for negative values) and then round(counter/4.0) in the output. Now awesome… now to work out the interrupts and I’m done 🙂
One other question I have – why do you not have any resistors on the pins? Are they not needed? There is nothing in the datasheet that mentions internal loads.
Thanks
Nigel
I’m using internal pull-up resistors of the Atmega.
So I’m having some issues.. not sure if it’s hardware or an issue with the code. I’m using the following encoder.
http://www.sparkfun.com/products/9117
When I use the code provided above I’m always getting 0,1 0,1 in the output through serial. This happens regardless of the direction I turn the encoder.
Kevin
This usually happens when one of encoder outputs is disconnected – double-check your connections with an ohmmeter.
Thanks for the reply.. Really appreciate it.
Just wanted to say that was exactly the issue. Bad solder connection.
– Oleg
Hi, I am currently trying to get two sparkfun rotary encoders to work side by side on the analogue pins. But I’m not entirely sure how to go about it. Im relatively new to Arduino programing (but have used processing to make a clock, timer and a simple space invaders game) as struggle with pins etc.
I would prefer to use A0,A1,A4&A5 if possible.
Thanks
The pins look good. I’d start with making first one work, this would give you good understanding of the algorithm. Then make a variant with second set of pins. Then combine both into one program.
Thanks for the quick reply.
I was able to get A0 and A1 working correctly. But have got the problem where changing just the following (to work on A4 & A5), outputs only dec 0 and dec 1.
2// #define ENC_A 14 >> #define ENC_A 18
3// #define ENC_B 15 >> #define ENC_A 19
So I changed the end of the code as well to;
37// old_AB |= ( ENC_PORT & 0x03 ); (binary 11000000 L2R)
>> old_AB |= ( ENC_PORT & 0x30 ); (binary 00001100 L2R)
38// return ( enc_states[( old_AB & 0x0f )]); (binary 11110000 L2R)
>> return ( enc_states[( old_AB & 0xf0 )]); (binary 00001111 L2R)
but the serial monitor just spits out random numbers.
Am I changing too much or too little.
(I am still just trying to get 1 encoder to work)
Thanks
You move encoder 4 bits up and you move the mask only two bits up. I can’t understand the reason behind second modification. Additionally, since now your table index is contained in 4 high bits of old_AB, you need to shift it 4 bits down.
I only half understand what you mean.
Im moving the encoder from bits 0 & 1 to 4 & 5
I thought I was moving the mask from bits 0 & 1 (0x03) to 4 & 5 (0x30) or am I wrong?
Do I not need the 2nd modification (line 38) ?
Where do I need to shift the table?
Can you show me the changes I need to make
Thanks
You’re correct, I overlooked it. Don’t number bits from left to right, do it like if you were expanding hex, i.e, 0x41 = (0100)(0001).
old_AB for the first encoder uses two lower bits 0000 00xx, when you shift it to produce the index, you use next two bits, i.e. 0000 xxxx. The same is true for the second encoder, but at this time you’re using upper nibble – 00xx 0000, xxxx 0000 – this is too large for an index unless you want to expand the lookup table to 256 members and only use first and last 16. This is what you’re trying to do in line 38, however, since your lookup table has only 16 members, you’re reading past it picking up some random bytes from the memory.
Instead, if after shifting it to the left you shift it again to the right 4 times, the index will be small again and work exactly like one from the article. BTW, encoders must use separate old_ABs.
I noticed my mistake in the original post. I wrote it out how I was looking at the arduino board (hence the L2R).
so the code will be
old_AB <>= 4
or can I write it like this
old_AB >>= 2 <<= 4
You need to preserve old_AB for the next reading, so something like this is necessary:
-Oleg,
Thanks for your help. Moving it right 4 times worked a treat, and now have both encoders working along side each other.
Hi Paul!
Sorry i’m a new Bee on Arduino, and i started to control 2 dc motors with to ALPS encoders across interrupts, without success, a lot of noise, and the debounce not work at all!
I saw this blog, a i try the Oleg code with success, for one encoder, but i need, like use a second, and i’m not understand what is necessary to change on the original code?
In other words can you show me the code?
Thanks for the help
With my best regards, Braulio Vieira-Portugal
Hello Oleg – that is nice code you’ve got there and over my head, BUT it does run nicely for me. My encoder counts up and down just as it should. I get one increment per click – not sure what encoder I’ve got, just one in box.
I desire to use the encoder to enter diameter in a project I’m working on. I would like to enter from 0.1 to 10 (this will be inches of diameter to be used in a calculation. My encoder with your code reads from 0 to 255, then starts over. Is there any way I could get this to read from 1 to 100 only? I could divide that by 10 to have my .1 to 10 value for calculation. I would like to do this without using interrupts – I have a Atmega168 and I understand it only has 2 interrupts? Those I’m hoping to use to read an IR sensor for a Tachometer.
Thank you for any help,
Ken H>
KenH – To have it count from 1 to 100 you would initialize counter to 1 in line 19. Then every time through the loop as counter is updated in line 26, check the value to see if it is > 100. If so, then set it to 1 again, etc. as shown below.
19 static uint8_t counter = 1; //this variable will be changed by encoder input
26 counter += tmpdata;
if( counter > 100 ){
counter = 1;
}
else if( counter < 0 ){
counter += 100;
}
Thank you Rich – I’ll try that- looks good to me, but I’m very much a novice at this stuff.
Thank you again,
Ken H>
Just yesterday I had to solve this problem and my method is Finite State Machine. We have a set of states and transitions between them.
Square – set of input data, circle – states, double circle – recognized state.
My code:
#include
int sv1,sv2,q = 0;
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void setup()
{
lcd.begin(16, 2);
pinMode(9, INPUT);
pinMode(8, INPUT);
}
void doL() {
lcd.scrollDisplayLeft();
}
void doR() {
lcd.scrollDisplayRight();
}
void loop()
{
// read the value from the sensor:
sv1 = digitalRead(9);
sv2 = digitalRead(8);
if ( q == 0 ) {
if( sv1 == 0 & sv2 == 0 ) q = 1;
if( sv1 == 1 & sv2 == 1 ) q = 7;
}
if ( q == 1 & sv1 == 0 & sv2 == 0 ) { q = 1; }
else if ( q == 1 & sv1 == 0 & sv2 == 1 ) { q = 2; }
else if ( q == 1 & sv1 == 1 & sv2 == 0 ) { q = 3; }
else if ( q == 2 & sv1 == 0 & sv2 == 0 ) { q = 1; }
else if ( q == 2 & sv1 == 0 & sv2 == 1 ) { q = 2; }
else if ( q == 2 & sv1 == 1 & sv2 == 1 ) { q = 4; }
else if ( q == 3 & sv1 == 0 & sv2 == 0 ) { q = 1; }
else if ( q == 3 & sv1 == 1 & sv2 == 0 ) { q = 3; }
else if ( q == 3 & sv1 == 1 & sv2 == 1 ) { q = 5; }
else if ( q == 6 & sv1 == 1 & sv2 == 1 ) { q = 6; }
else if ( q == 6 & sv1 == 1 & sv2 == 0 ) { q = 7; }
else if ( q == 6 & sv1 == 0 & sv2 == 1 ) { q = 8; }
else if ( q == 7 & sv1 == 0 & sv2 == 0 ) { q = 9; }
else if ( q == 7 & sv1 == 1 & sv2 == 0 ) { q = 7; }
else if ( q == 7 & sv1 == 1 & sv2 == 1 ) { q = 6; }
else if ( q == 8 & sv1 == 0 & sv2 == 0 ) { q = 10; }
else if ( q == 8 & sv1 == 0 & sv2 == 1 ) { q = 8; }
else if ( q == 8 & sv1 == 1 & sv2 == 1 ) { q = 6; }
if ( q == 4) { doR(); q = 7; }
else if ( q == 5) { doL(); q = 7; }
else if ( q == 9) { doR(); q = 1; }
else if ( q == 10) { doL(); q = 1; }
lcd.setCursor(0, 0);
lcd.print("X");
}
It more longer but take 2902 bytes of memory, uses fewer variables, not need to remember the previous states.
PS sorry for my English:)
Hi, I have question. But first I need to thank you for posting this helpful code!
I have setup of Arduino Mega with sample code from Sparkfun(http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Components/Switches/Rotary_Encoder_LED_Ring_Example.pde) and rotary encoder with LED ring.
On Mega it seems that only A0&A1 on port F pins working for recieve signal from rotrary.Why ?
And second question is how can I connect two encoders to it?
Will the program work for an encoder plugged into pins 15 and 16 (analog pins 2 and 3)? We tried plugging it into these pins and it wasn’t working.
Our ultimate project requires us to use 3 encoders. At first, we are trying to make it work for 2 encoders. This is the program we wrote up. I assume the first problem is that the pins 15 and 16 aren’t working properly with the program. Is there anything else that we must fix?
Here’s our program for 2 encoders:
#define ENC1_A 14
#define ENC1_B 15
#define ENC2_A 16
#define ENC2_B 17
#define ENC_PORT PINC
void setup()
{
pinMode(ENC1_A, INPUT);
digitalWrite(ENC1_A, HIGH);
pinMode(ENC1_B, INPUT);
digitalWrite(ENC1_B, HIGH);
pinMode(ENC2_A, INPUT);
digitalWrite(ENC2_A, HIGH);
pinMode(ENC2_B, INPUT);
digitalWrite(ENC2_B, HIGH);
Serial.begin (9600);
Serial.println(“Start”);
}
void loop()
{
static unsigned int counter1 = 0;
int tmpdata1 = read_encoder1();
if( tmpdata1 ) {
Serial.print(“Counter value of Encoder 1: “);
Serial.println(counter1, DEC);
counter1 += tmpdata1;
}
static unsigned int counter2 = 0;
int tmpdata2 = read_encoder2();
if( tmpdata2 ) {
Serial.print(“Counter value of Encoder 2: “);
Serial.println(counter2, DEC);
counter2 += tmpdata2;
}
}
const int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
/* returns change in encoder state (-1,0,1) */
int read_encoder1()
{
static uint8_t old_AB = 0;
old_AB <<= 2; //remember previous state
old_AB |= ( ENC_PORT & 0x03 ); //add current state
return ( enc_states[( old_AB & 0x0f )]);
}
/* returns change in encoder state (-1,0,1) */
int read_encoder2()
{
static uint8_t old_AB = 0;
old_AB <<= 2; //remember previous state
old_AB |= ( ENC_PORT & 0x03 ); //add current state
return ( enc_states[( old_AB & 0x0f )]);
}
Great Code. I am very new to both the arduino and the rotary encoder. Your code is working great for me, but I would like to set an alarm if the encoder stops turning. I set pinMode on 13 for output and I wrote this if statement:
if ( old_AB == uint8_t )
delay(5000);
digitalWrite(13, HIGH);
This is not working for me. Could you explain what I am doing wrong? Any help would be greatly appreciated. Thank you.
@franey61 – There’s no way to tell if it is your only problem, but you are trying to compare old_AB to an integer *type*… not a variable or constant. That will never work… Good luck!
Thanks so much for the great code, it was very helpful.
I’m just wondering if it would be possible to make this work using one pin from port D and another from port B. I don’t quite understand why they would have to be adjacent pins.
They don’t need to be adjacent but they do need to be on the same port. The issue is that when you read one port (or one pin) and then read the next port or pin the value of that could change or bounce giving you an incorrect reading. You need to take a snapshot of the pins/state of both pins at the same time.
So I’ve set mine up to not be adjacent but on the same port. Then my routine reads the port and then does all the needed bit shifting to get it adjacent to plug into Oleg’s routine.
Could you please explain that lookup table?
int read_encoder2()
{
static uint8_t old_AB = 0;
old_AB <<= 2; //remember previous state
old_AB |= ( ENC_PORT & 0×03 ); //add current state
return ( enc_states[( old_AB & 0x0f )]);
}
won't work as described because old_AB will only have a value of 0 -4. It starts out initialized as 0(zero), is then shifted 2 bits, and is then anded with a new value in bits 0 & 1, and then anded with ox0F which still means tha bits 0 & 1 have teh current reading, and bits 2 & 3 are still 0(zero)
I have one encoder working on the original sketch and am trying to get
#define ENC_A 0
#define ENC_B 1
#define ENC_PORT PIND to work .I get serial read numbers (lots off reads)
it will print same number line after line and will change up or down with movement of encoder but is always reading not just on change state .
I have had a few goes at putting 2 encoders in to one sketch ,I’m not able to ,can some one put up a sketch with 2 encoders please
thanks for the codes,
I am new to arduino and encoders.
I have one question.
I am supposed to use the encoder to determine the position of a motor. basically, I need to tell the rotor how much to rotate.
How can I use the output of the codes ? and how can I determine the speed of the shaft from the output of these codes?
I really appreciate if you help me guys
rotary encoder will give u a value use that figure to set servo motor position ..or as speed of rotation for motor ,where 0 is stop and 255 full voltage / speed
my last comment was how to set position or speed not to read what the motor is doing
thank you craig,
so I have two phases, A and B and the conditions are 0,0 – 0,1 -1,1 – 1,1
how can I find the position ? should count these numbers?
imagine I have coupled the motors to 4 wheels of a robots. and I want to tall the robot to go 4 feet.
how can I do it with phase A and B of my encoder ?
your reading should be a number from 1 – 255 and will go up or down as u turn encoder therefor an encoder gives forward or back (count up or count down)
i can get one rotary encoder working only . so if i send go = 1 foot per sec for 4 sec i get 4 foot
if i have rotary encoder (with a wheel on it ) read travel . if wheel is 3 inch around > then 4 turns per foot ,16 turns and your there
i do not know the code . but if encoder is set to read each number up and not reset 255 then 1 …just count up to (16 X 255) if encoder reads 4080 .stop
not sure if I am helping here .
thanks a million craig
one more question.
I am using Arduino UNO. but I don’t know which pins I should connect the phase A and B of my encoder /
can anyone help me plz .
I connected the encoder to the analog pins of A0 and A1,
but I get some unknown letters on serial monitor as can be seen in this picture :
http://s18.postimage.org/x7wb6zwhz/Screen_Shot_2012_08_02_at_5_39_44_PM.png
can anyone help me please ?
I fixed that problem,
and I get the counting .
thank you
However, I still have a problem,
look at my serial monitor.
http://s7.postimage.org/oley8fc2x/Screen_Shot_2012_08_02_at_5_58_15_PM.png
it doesn’t count continuously, it seems that i bounces . what’s the problem ?
one of the encoder outputs is not changing, either bad contact or dead port pin.
thank you oleg for your awesome code.
How can I make sure that my arduino is working properly because I have seen some weird things.
for example ;
I made the switch circuit with 2 wires and with a 10K resistor . so when I connect the wires I should see 1 on the serial monitor and when I disconnect them 0 . but it’s always high. !
when I a put a lead inside the pin it goes to 0 but it plays between 0 and 1 all the time !!!!
this the simple program that I upload to test it :
//
#define enc 8
#define led 13
int count = 0 ;
void setup()
{
pinMode(enc,INPUT);
pinMode(led, OUTPUT);
Serial.begin(9600);
}
void loop()
{
int val = digitalRead(enc);
if (val ==HIGH)
{
digitalWrite(led,HIGH);
Serial.println(val);
}
else{
digitalWrite(led,LOW);
}
}
i thought maybe it’s because of bouncing. because it was like this since the first day that I got the Arduino.
is there any other way to check the pins ? it’s UNO btw.
Connect your resistor to the Arduino pin, other end of resistor goes to ground. The pin should read low. Try it on several different pins.
tnx Oleg, I got a little bit confused and I am a little newbie in Arduino world 😉
Can you tell me the solution more clearly ?
which resistor ? and which program should I upload on it .
I really appreciate your invaluable help.
The 10K resistor you have. The program is the one you posted in your previous comment.
It read low .
but when I connect to 5V. it start to act weird!!
I changed the program to this :
int button = 12;
void setup()
{
pinMode(button,INPUT);
Serial.begin(9600);
}
void loop()
{
int val = digitalRead(button);
Serial.println(val);
}
but here is another weird thing.
when I start the program it reads zero which is normal. but when I put the wire which is connected to nowhere to the pin 12 it starts to bounces between 0 and 1 continuously.
He could try changing INPUT to INPUT_PULLUP
I thinks that’s why it bounces 🙂
finally worked.
Thank you Oleg.
let me tell you the problem.
I am using one of the FaulhaberGearmotors with haul effect rotarry encoder.
(http://www.robotroom.com/FaulhaberGearmotor.html )
I was powering the encoder using the same source that I was powering the motor. It was 7 volts and I not regulated.
when I powered the sensor using the 5v of the Arduino itself I got it worked.
the counts are flawless and awesome. It doesn’t miss any single step. so accurate. even in highspeed ( maximum speed) of the motor.
thank you very much
thank you very much.
The code works great on Arduino UNO, but I cannot get it to work with MEGA 2560. I connect the wires to A0 & A1 and set ENC_A to A0 and ENC_B to A1. I assume that the problem is with ENC_PORT… but no idea how to solve it. Any suggestions?
Thanks
The ports are different. Check board schematics at arduino.cc.
Hi Oleg,
Thank you for your suggestion.
I opened up the schematics for Uno and Mega 2560, but I have no idea what I am looking at/for. (14 & 15 seem to be labeled the same on both boards)
Please let me know if there are two ports that on the mega that would work and what changes I would have make to the code. Sorry for taking up your time.
Cheers,
Hi Oleg,
I am trying to drive 4 motors with encoders. your codes work perfectly , but I want to make it as function to use it much more organized in my sketch. my code is so messy right now.
I made this change. but it doesn’t work.
can you please take a look to see where the problem is .
thank you
[code]
/* Rotary encoder read example */
#define ENC_A A0
#define ENC_B A1
#define ENC_PORT PINC
void setup()
{
/* Setup encoder pins as inputs */
pinMode(ENC_A, INPUT);
digitalWrite(ENC_A, HIGH);
pinMode(ENC_B, INPUT);
digitalWrite(ENC_B, HIGH);
Serial.begin (115200);
Serial.println(“Start”);
}
void loop ()
{
long val = encoding();
Serial.println(val);
}
void encoding()
{
static uint32_t counter = 0; //this variable will be changed by encoder input
int32_t tmpdata;
/**/
tmpdata = read_encoder();
if( tmpdata ) {
counter += tmpdata;
return counter;
}
}
/* returns change in encoder state (-1,0,1) */
int16_t read_encoder()
{
static int32_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
static uint32_t old_AB = 0;
/**/
old_AB <<= 2; //remember previous state
old_AB |= ( ENC_PORT & 0x03 ); //add current state
return ( enc_states[( old_AB & 0x0f )]);
}
[/code]
I want to use the “val” later in the main loop.
but always gives me 0 !
btw,
instead of void encoding() I used uint32_t encoding() and didn’t work.
is there anyway to use more than two encoders with this code ?
Hi Nathan
It should be possable to use 3 encoders with an arduino UNO
or 6 with a mega .
Hi Oleg
I have changed your sketch so i can start to combine 2 encoders in to one sketch. Im not good at this .
[quote]
[color=#7E7E7E]/* Rotary encoder read example */[/color]
#define ENC_C 0
#define ENC_D 1
#define ENC_PORT PIND
[color=#CC6600]void[/color] [color=#CC6600][b]setup[/b][/color]()
{
[color=#7E7E7E]/* Setup encoder pins as inputs */[/color]
[color=#CC6600]pinMode[/color](ENC_C, [color=#006699]INPUT[/color]);
[color=#CC6600]digitalWrite[/color](ENC_C, [color=#006699]HIGH[/color]);
[color=#CC6600]pinMode[/color](ENC_D, [color=#006699]INPUT[/color]);
[color=#CC6600]digitalWrite[/color](ENC_D, [color=#006699]HIGH[/color]);
[color=#CC6600][b]Serial[/b][/color].[color=#CC6600]begin[/color] (115200);
[color=#CC6600][b]Serial[/b][/color].[color=#CC6600]println[/color]([color=#006699]”Start”[/color]);
}
[color=#CC6600]void[/color] [color=#CC6600][b]loop[/b][/color]()
{
[color=#CC6600]static[/color] uint8_t counterleft = 0; [color=#7E7E7E]//this variable will be changed by encoder input[/color]
int8_t tmpdataleft;
[color=#7E7E7E]/**/[/color]
tmpdataleft = read_encoder();
[color=#CC6600]if[/color]( tmpdataleft ) {
[color=#CC6600][b]Serial[/b][/color].[color=#CC6600]print[/color]([color=#006699]”Counter value: “[/color]);
[color=#CC6600][b]Serial[/b][/color].[color=#CC6600]println[/color](counterleft, [color=#006699]DEC[/color]);
counterleft += tmpdataleft;
}
}
[color=#7E7E7E]/* returns change in encoder state (-1,0,1) */[/color]
int8_t read_encoder()
{
[color=#CC6600]static[/color] int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
[color=#CC6600]static[/color] uint8_t old_ABL = 0;
[color=#7E7E7E]/**/[/color]
old_ABL <<= 2; [color=#7E7E7E]//remember previous state[/color]
old_ABL |= ( ENC_PORT & 0x03 ); [color=#7E7E7E]//add current state[/color]
[color=#CC6600]return[/color] ( enc_states[( old_ABL & 0x0f )]);
}
[/quote]
this gives goon numbers but so fast so many .
Is there a mistake ?
Sorry i didnt know it would look that bad on here
/* Rotary encoder read example */
#define ENC_C 0
#define ENC_D 1
#define ENC_PORT PIND
void setup()
{
/* Setup encoder pins as inputs */
pinMode(ENC_C, INPUT);
digitalWrite(ENC_C, HIGH);
pinMode(ENC_D, INPUT);
digitalWrite(ENC_D, HIGH);
Serial.begin (115200);
Serial.println(“Start”);
}
void loop()
{
static uint8_t counterleft = 0; //this variable will be changed by encoder input
int8_t tmpdataleft;
/**/
tmpdataleft = read_encoder();
if( tmpdataleft ) {
Serial.print(“Counter value: “);
Serial.println(counterleft, DEC);
counterleft += tmpdataleft;
}
}
/* returns change in encoder state (-1,0,1) */
int8_t read_encoder()
{
static int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
static uint8_t old_ABL = 0;
/**/
old_ABL <<= 2; //remember previous state
old_ABL |= ( ENC_PORT & 0x03 ); //add current state
return ( enc_states[( old_ABL & 0x0f )]);
}
Hi Oleg
I have made the sketch to this far ,but it is stopping when i verify ,with
read_encoder was not declared .
#define ENC_C 0
#define ENC_D 1
#define ENC_PORT PIND
#define ENC_A 14
#define ENC_B 15
#define ENC_PORT PINC
void setup()
{
/* Setup encoder pins as inputs */
pinMode(ENC_C, INPUT);
digitalWrite(ENC_C, HIGH);
pinMode(ENC_D, INPUT);
digitalWrite(ENC_D, HIGH);
pinMode(ENC_A, INPUT);
digitalWrite(ENC_A, HIGH);
pinMode(ENC_B, INPUT);
digitalWrite(ENC_B, HIGH);
Serial.begin (115200);
Serial.println(“Start”);
}
void loop()
{
static uint8_t counterleft = 0; //this variable will be changed by encoder input
int8_t tmpdataleft;
/**/
tmpdataleft = read_encoder();
if( tmpdataleft ) {
Serial.print(“leftside value: “);
Serial.println(counterleft, DEC);
counterleft += tmpdataleft;
static uint8_t counterright = 0; //this variable will be changed by encoder input
int8_t tmpdataright;
/**/
tmpdataright = read_encoder();
if( tmpdataright ) {
Serial.print(“rightside value: “);
Serial.println(counterright, DEC);
counterright += tmpdataright;
}
}
/* returns change in encoder state (-1,0,1) */
int8_t read_encoder()
static int8_t enc_states[] = {
0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0 };
static uint8_t old_ABL = 0;
/**/
old_ABL <<= 2; //remember previous state
old_ABL |= ( ENC_PORT & 0x03 ); //add current state
return ( enc_states[( old_ABL & 0x0f )]);
int8_t read_encoder()
static int8_t enc_states[] = {
0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0 };
static uint8_t old_ABR = 0;
/**/
old_ABR <<= 2; //remember previous state
old_ABR |= ( ENC_PORT & 0x03 ); //add current state
return ( enc_states[( old_ABR & 0x0f )]);
}
I have 2 encoders but how do I get the arduino to read the one i call left or the one i call right ?
regards craig
Your function definition is incorrect. Take a look at basic C language tutorial on the web.
Hi Oleg,
Thanks for the great code !! It works very well on my Arduino, however when I am trying to upload to the ATTiny85, I got this error :
ROTARY_ENCODER.cpp: In function ‘int8_t read_encoder()’:
ROTARY_ENCODER:32: error: ‘PINC’ was not declared in this scope
This line causes the problem: old_AB |= ( ENC_PORT & 0x03 ); //add current state
I wonder how can I fix this ? Do you have any idea ?
This is the pin-out of ATtiny85:
http://hlt.media.mit.edu/wp-content/uploads/2011/10/ATtiny45-85.png
Thanks !
PINC is a name of input buffer of Atmega328 port C. Apparently, ATtiny has different names for port buffers – look them up in the datasheet.
Thanks, changed it to PINB and worked !
Hi Oleg, me again. This time I want to set the common pin of the encoder to V++ (in order to use the interrupt), instead of Gnd like in your tutorial.
I changed the code to:
pinMode(ENC_A, INPUT);
digitalWrite(ENC_A, LOW);
pinMode(ENC_B, INPUT);
digitalWrite(ENC_B, LOW);
However the value returned is not correct (jumping).
Could you help please.
Thanks.
What are you trying to achieve?
Hi, I want to use the Rotary encoder to wake up the attiny85.
What is
digitalWrite(ENC_A, LOW);
meant to achieve?I am using an Arduino Uno board for a university project. Is the code provided compatible with my micro-controller??
Also, i see that you didn’t give directions as to how to connect it to the robot itself. It would be nice if you could give a few links to address this issue.
🙂
Hi
What do you mean with:
“Connecting encoder pins to pins 0 and 1 of 8-bit MCU port makes encoder reading code very simple. If analog pins are needed for something else, it is possible to move encoder to digital pins 8,9 or 0,1 (losing serial port)”
More exact that you are lossing serial port if you change port of the encorder?
Best
I mean this: “You will lose serial port if you connect an encoder to pins 0,1”.
Hi
I mean what do you loose in function witch the lost of serial port?
Best Fredrik
Hi,
Your sketch works great with my UNO and rotery encoder reading to my serial monitor. I would like to read the encoder on my Adafruit RGB LCD shield which is conected to Analog 4 and 5 on the UNO. Any suggestions would be greatly appreciated. Thanks.
Hi,
Neat solution.. 🙂 Only problem is polling would be an issue for me. Could you not use pins 2 & 3 for an interrupt driven solution? On each change of input read_encoder would be called…
This is one of the most elegant way to read an encoder. I have borrowed the code and tailored it to my AVR Mu, It’s amazing how the code is so compact where my original code was more than 15 lines. Thank you very much.
Hello,
Thank you for this great post.
Did you make any kind of test or simulation to find how fast your can go with this code without loosing pulses? If not, how fast would you guess?
Thank you,
Igor.
It’s going to depend on polling rate, I guess.
hey – I am trying to change to use pins 2,3 on port B (10,11 on arduino). So I tried to change the code at the top”
#define ENC_A 2
#define ENC_B 3
#define ENC_PORT PINB
and then at the bottom:
to:
old_AB |= ( (PIND >> 2) & 0x03 ); //add current state
from:
old_AB |= ( ENC_PORT & 0x03 ); //add current state
But it isn’t working(i’m not getting any readings when using pins other than 1 and 2 of the defined port, so I’m doing something wrong in my logic in the read_encoder() function but not sure what – I’m wondering if you know? Any help would be appreciated. Thanks a lot
I’ve been trying for a
See the code in its entirety that i was trying:
Hey – just an addendum – I also tried
#define ENC_A 10
#define ENC_B 11
#define ENC_PORT PINB
But it gave the same results. I think its in the bottom function I’m doing something not right…
Thank you so much in advance.
Hey –
I’m sorry – I see how you had answered someone prior, and it worked for me by doing:
Thanks anyway because this is really helpful –
does this mean that I dont have to use the interrupt pins? (this is maybe what I fundamentally don’t understand – why does this code work without the interrupt pins – I understand that I can use an encode without the interrupt pins but am sacrificing accuracy. But here you said I dont even need to debounce? I am wondering also up to what frequency this will work (I’m waiting for my real encoders in the mail) …
Thanks so much
old_AB <> 4;
return ( enc_states[( new_AB & 0x0f )]);
There are two ways to read a digital pin. You either poll it or use an interrupt on change. When you poll it, you need to do it often enough in order to catch every change. This code is no different, you need to call it often. The debouncing here uses the properties of Gray code plus the fact that only one channel can possibly be bouncing at each transition. In Gray code channels A and B change states in turn – channel A from 0 to 1, then channel B from 0 to 1, then channel A from 1 to 0, then channel B from 1 to 0. The cycle then repeats. When a channel changes state, first change is accepted as valid. All other changes of this are rejected (zeroes in a lookup table) until a second channel changes state. This works correctly until you start rotating your encoder so fast that change rate becomes comparable to bounce rate.