In hobbyist applications, they are handy for all sorts of things - potentiometer replacements, up/down switching, etc. Using them with AVRs, PICs and Arduinos is quite common and they are easily available.
Their operation is quite simple, using a careful arrangement of conductive tracks. There are 3 pins on the most common ones - two for the "bit" outputs, and a common (wiper). As the encoder turns, the bit pins change according to a Gray Code sequence as follows:
Position | Bit A | Bit B |
---|---|---|
0 | 0 | 0 |
1/4 | 1 | 0 |
1/2 | 1 | 1 |
3/4 | 0 | 1 |
1 | 0 | 0 |
The basic way of decoding these is to watch for which bits change. For example, a change from "00" to "10" indicates one direction, whereas a change from "00" to "01" indicates the other direction. The video below shows these changes:
I looked around for an AVR/Arduino decoder implementation for an application I had in mind. There were a lot of code samples around, and even the official Arduino wiki has a lot of code options.
I looked around for an AVR/Arduino decoder implementation for an application I had in mind. There were a lot of code samples around, and even the official Arduino wiki has a lot of code options.
Unfortunately, most of them suck.
The vast majority of them suffer from one or more of the following flaws:
The Gray code actually follows a simple state machine, and at any given state, it can only change to one of two other values. Let's take a look at the state table again:
For example at position 1/4, the only valid next states are either 00 at position 0, or 11 at position 1/2. Any other state is invalid and should be ignored. The algorithm should know this.
Next, when there is switch bounce, the switch output will alternate many times between states, eg from 1/4 to 1/2 to 1/4 to 1/2 to 1/4 to finally settling on 1/2. The algorithm should not generate spurious events in this case, and must also still recognise the final 1/2 state.
The code I wrote honours all of these. It enforces valid state changes, deals with switch bounce, handles direction change, yes the entire logic is four lines of code. The rest of the code is port setup, and a static array holding the state machine table.
My code also includes a #define, which sets it to either emit a turn event after a full step (ie complete position changes between 00-10-11-01-00), or to support half-step mode, where it emits an event at both 00 and 11 positions. Both are equally useful, but the full-step code is handy for devices that give a physical 'bump' only at the 00 positions.
Unnecessary code is excluded. For example, its up to you to trigger a poll of the encoder input pins. This is done with a single call, which returns a code indicating a clockwise, anticlockwise, or no change. A change is triggered when the state goes back to 00 (with full step), or at both 00 and 11 (half step).
You can trigger the poll either from a loop, or even from a pin change interrupt handler (sample code included).
As you can see in the video below, the algorithm works reliably at both slow and high rotation speeds.
Get it from the Github repository.
The vast majority of them suffer from one or more of the following flaws:
- Debounce handling. Mechanical switches are imperfect, and bounce on and off during transitions, over a few milliseconds, which is enough to be sensed as discrete changes by the MCU. Many implementations simply detect a change from 00 to 01 and signal it. But with switch bounce, it could be detected as many events. Thus, implementations often include debounce routines. but these add additional code, and also at high rotational rates, they filter out the events and break.
- Direction changes. Some implementations use algorithms that expect direction changes to occur at the '00' bit position. If a change occurs mid-step, they get confused and return too many events, or a spurious one in the wrong direction.
- Complexity. Many implementations are plagued by complicated conditionals and loops. They have unwieldly long if-then statements that are impossible to debug, yet can still suffer from the above problems. Complex code also leads to consumption of valuable code space.
- Weak algorithms. Far too many implementations simply look for a single transition from one state to another, rather than following and checking for valid states. This leads to odd quirks and bugs.
The Gray code actually follows a simple state machine, and at any given state, it can only change to one of two other values. Let's take a look at the state table again:
Position | Bit A | Bit B |
---|---|---|
0 | 0 | 0 |
1/4 | 1 | 0 |
1/2 | 1 | 1 |
3/4 | 0 | 1 |
1 | 0 | 0 |
For example at position 1/4, the only valid next states are either 00 at position 0, or 11 at position 1/2. Any other state is invalid and should be ignored. The algorithm should know this.
Next, when there is switch bounce, the switch output will alternate many times between states, eg from 1/4 to 1/2 to 1/4 to 1/2 to 1/4 to finally settling on 1/2. The algorithm should not generate spurious events in this case, and must also still recognise the final 1/2 state.
The code I wrote honours all of these. It enforces valid state changes, deals with switch bounce, handles direction change, yes the entire logic is four lines of code. The rest of the code is port setup, and a static array holding the state machine table.
My code also includes a #define, which sets it to either emit a turn event after a full step (ie complete position changes between 00-10-11-01-00), or to support half-step mode, where it emits an event at both 00 and 11 positions. Both are equally useful, but the full-step code is handy for devices that give a physical 'bump' only at the 00 positions.
Unnecessary code is excluded. For example, its up to you to trigger a poll of the encoder input pins. This is done with a single call, which returns a code indicating a clockwise, anticlockwise, or no change. A change is triggered when the state goes back to 00 (with full step), or at both 00 and 11 (half step).
You can trigger the poll either from a loop, or even from a pin change interrupt handler (sample code included).
As you can see in the video below, the algorithm works reliably at both slow and high rotation speeds.
Get it from the Github repository.
This is the best rotary encoder solution. It uses the same idea as this post: http://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino.
ReplyDeleteHere's a bit of code that utilizes your library to enable the Arduino to read two rotary encoders:
Rotary r = Rotary(2, 3);
Rotary r2 = Rotary(4, 5);
void setup() {
Serial.begin(115200);
PCICR |= (1 << PCIE2) | (1 << PCIE1);
PCMSK2 |= (1 << PCINT18) | (1 << PCINT19) | (1 << PCINT20) | (1 << PCINT21);
sei();
}
void loop() {
}
ISR(PCINT2_vect) {
char result = r.process();
char result2 = r2.process();
if (result) {
Serial.println(result == DIR_CW ? "1Right" : "1Left");
}
if (result2) {
Serial.println(result2 == DIR_CW ? "2Right" : "2Left");
}
}
I've just expanded the AVR native interrupt code because the Arduino only supports two interrupt pins.
This comment has been removed by a blog administrator.
DeleteHi, Great thanks this looks so much strait forward.
ReplyDeleteBut i have trouble with the sample3 in the lib folder.
If I #define FULL_STEP or comment the #define HALF_STEP out I can not compile any more - get funny error I don't understand. I use Arduino 1.0.
could You pls check and also upgrade your lib Rotary to Arduino 1.0.
thanks in advance ( I go in circles ;-) )
This comment has been removed by a blog administrator.
DeleteIt would be great if this was updated--I agree that all the other examples suck. Thanks in advance-
ReplyDeleteThis comment has been removed by a blog administrator.
DeleteDear Marco, I am Unknown. No one wants to talk to you. Thank you.
DeleteDefintively the best code for encoders:
ReplyDeleteI have just a problem with transfering the result in a variable. Which are the two possible outcomes? if (result ==0x40) is left, but what is right?
My Code just won't work
void loop() {
char result = rotary_process();
if (result ==0x40) {{
x++;};
if (x==40) {
x=0;};
if (result !=0x80) {
x--;};
if (x==-1) {
x=39;};void loop() {
char result = rotary_process();
if (result ==0x40) {{
x++;};
if (x==40) {
x=0;};
if (result !=0x80) {
x--;};
if (x==-1) {
x=39;};
This comment has been removed by a blog administrator.
DeleteThere is a slight problem due to the rotary_process returning a char, when in fact it should be unsigned.
ReplyDeleteI will post a fix shortly, but in the meantime, just change all instances of "char" in the library files to "unsigned char".
For the outcomes, you should compare against the constants DIR_CW and DIR_CCW.
This comment has been removed by a blog administrator.
DeleteHi Ben,
ReplyDeleteI just did he changes (in rotary.h and rotary.cpp and in the sketch itself) - but it didn't do the trick. I can still only count clockwise. Any suggestions what I'm doing wrong? (PS: In your example with the serial output it works perfectly.)
Sascha
void loop() {
}
ISR(PCINT2_vect) {
unsigned char result = r.process();
if (result) {
if (result == DIR_CW) {{
x++;};
if (x==40) {
x=0;};
if (result == DIR_CCW) {
x--;};
if (x==-1) {
x=39;};
I've updated the library here to Arduino 1.0, as well as fixed a couple of problems relating to signed variables. Please give it a go!
ReplyDeleteThank you very much. WIth the new Version and a programming mistake I made fixed it works now.
ReplyDeleteJust another question. Did you also make some changes in the HALF_STEP programming?
I ask because i noticed, that it counts with the libray just every other step ( if i insert "#define HALF_STEP" or not makes no difference.
However, if I use the code without the library, it works just perfect.
Ok, my mistake.
ReplyDeleteI got it. I didn't think, that i had to activate the feature WITHIN the library ;-)
Just forget my last comment. Everything but the big, fat THANK YOU, of course
I'm using this library, but having some strangeness when I try to use the rotary3 example without half stepping. Whenever I comment out the #define HALF_STEP I can't compile my sketch anymore. Any insight on how to use the rotary3 sketch without half stepping?
ReplyDeleteadditionally, when I do get it to compile and upload without using half step (don't know why one sketch compiles and the other doesn't), but when I do get it uploaded then turning the knob in either direction on registers as LEFT, though it is doing single steps.
DeleteThis is with the latest version?
Deleterotary3 contains a typo in the table for FULL_STEP operation.
DeleteTo get it to compile under 1.0.1, without HALF_STEP defined, I found I had to remove all references to HALF_STEP but then found it only went LEFT.
If you look at the defs, CCW = 0x10 and CW = 0x20, but the table for FULL_STEP has both entered as 0x10.
Changing the table to as shown below means the code does FULL_STEP correctly in both directions:
const unsigned char ttable[7][4] = {
{0x0, 0x2, 0x4, 0x0}, {0x3, 0x0, 0x1, 0x10},
{0x3, 0x2, 0x0, 0x0}, {0x3, 0x2, 0x1, 0x0},
{0x6, 0x0, 0x4, 0x0}, {0x6, 0x5, 0x0, 0x20},
{0x6, 0x5, 0x4, 0x0},
};
I'm Marco from a rotary encoder factory in China, it would be great if we can have a talk thank you.
DeleteI was having trouble with rotary encoders as UI input in my project and this library sorted everything out. This should be the first listing under http://arduino.cc/playground/Main/RotaryEncoders as the other libs there don't work. Thanks for taking the time and effort to make this library!
ReplyDeleteThis comment has been removed by a blog administrator.
DeleteRegards from Spain, great job yours.. I have one project with two rotary encoders but they don´t work properly, can you see the arduino sketch to test those troubles??? and xplain me how solve it???
ReplyDeletehttps://dl.dropbox.com/u/58726056/sketch_sep06c/panel_inigo_02/panel_inigo_02.ino
The idea it´s increase Frecz when turn right or decrease when turn left, amounts of 10 mhz each pulse, and when we turns faster increase 100 Mhz units till 999,999 limits.
This comment has been removed by a blog administrator.
DeleteSorry my encoders are 2-bit gray code, perhaps there is the trouble???
ReplyDeleteRegards I have installed into arduino rotary3.ino file but each click gives me via serial port two signals right or when turn left two signals left... I think there is the trouble perhaps my encoders have different steep table??? in the script you indicate there is two tipes half and full steep tables isn´t??? Thx for your support.
ReplyDeleteGetting two signals per click is pretty normal. If setting it to full step is giving this, then your program will probably have to only take actions on two steps, rather than one.
DeleteHello Ben
ReplyDeletei am new at programming , i bought an arduino a few months ago and i have done amazing projects so far. my next challange involves a rotary encoder as you demostrated. your code is exactly what i ma looking for . however may you please suggest a way on how i can get a variable accordingly to the direction . for example , count the number of left and right turns or switch on a led if the encoder only turns right . thank you for all your help. i really appricate and admire your work.
michael
This comment has been removed by a blog administrator.
Deleteplease ignore my last comment, i had managed it rather well. although my biggest issue with a rotary encoder is that i cannot count once per direction. i would like to measure oscillations so the rotary encoder will change dirctions to the oscilattion. i have managed to count the half pulses in one direction and reverse the count if the encoder rotated in the opposite direction. this is fairly simple but now i battling , please help with any suggestions and help?
ReplyDeleteReorganized for Arduino 1.x IDE, added syntax highlighting, added polling example.
ReplyDeletehttps://github.com/brianlow/Rotary
I love your explanation but I'm having trouble following your code. I'm in the process of learning ASM for the AVR 8-bit microcontroller and haven't quite deciphered the C language yet. Do you have any suggestions or links that could help me learn or translate your code to ASM?
ReplyDeleteThis comment has been removed by a blog administrator.
DeleteWhat really confuses me are the hexadecimal values.. Where do you get some of the values in the table? On the Gray chart above, I see that the highest value in the table is 0x03? In your .imo document, I see hexadicimal values going up to 0x20!
ReplyDeleteThe values in the code go higher as I return not just the next state for the state machine, but also a couple of the high nibble bits are used to indicate whether a clockwise or anticlockwise rotation is signalled. This is more efficient than maintaining separate variables.
DeleteYour algorithm is excellent. I have spent the afternoon trying to have more complex libraries work with no success. It took me some time to have yours running as I'm still beginner, and my lcd has some serious problems displaying informations, but it now does work pretty fine, no servo jittering at all. Thank you very much.
ReplyDeleteI would like to see the code for running a servo. I have been hunting everywhere for some samples of rotary encoders and servos.
DeleteThanks,
This comment has been removed by a blog administrator.
DeleteHi, I tried your algorithm, but in my case it doesn't work as expected.
ReplyDeleteWhen I look at the table (below), I think it outputs a result at 11 (R_START | DIR_CW) in stead of 00 (second row). Am I correct or am i overlooking something?
Kind regards,
Piet
const unsigned char ttable[7][4] = {
// R_START
{R_START, R_CW_BEGIN, R_CCW_BEGIN, R_START},
// R_CW_FINAL
{R_CW_NEXT, R_START, R_CW_FINAL, R_START | DIR_CW},
// R_CW_BEGIN
{R_CW_NEXT, R_CW_BEGIN, R_START, R_START},
// R_CW_NEXT
{R_CW_NEXT, R_CW_BEGIN, R_CW_FINAL, R_START},
// R_CCW_BEGIN
{R_CCW_NEXT, R_START, R_CCW_BEGIN, R_START},
// R_CCW_FINAL
{R_CCW_NEXT, R_CCW_FINAL, R_START, R_START | DIR_CCW},
// R_CCW_NEXT
{R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START},
};
This comment has been removed by a blog administrator.
DeleteI'll try turning the table around.
ReplyDelete@Ben Buxton: Is it possible to post the rc5/manchester algorithm? Would really be appreciated.
Kind regards,
Piet
The algorithm I used for RC5 is http://www.clearwater.com.au/rc5 and I used that as a starting point for the rotary decoder. A read through that page should clarify things somewhat.
Deletewould it be possible to identify the rotation direction for a simple analog pot .
ReplyDeleteHi Ben, Thanks for this great info! I'm a neophyte and got a single encoder working immediately. I, however am looking to have two encoders in my project, and am quickly floundering. Is it possible two get two working? The first post implies it can be done but jumps into code that is over my head. Any hints?
ReplyDeleteThis comment has been removed by a blog administrator.
DeleteWhat is your application? the example in the first response is using interrupts, like the rotary sketch in the zip. Doing it like rotary3 does, with polling, may be responsive enough and easier to get your head around. I'm going to be playing with multiple encoders tomorrow, I started modifying the code to support more encoders. I'm working on a 5 zone/room audio amplifier so each zone needs its own volume control, etc. thus five rotary encoders with push buttons for mute.
ReplyDeleteThis comment has been removed by a blog administrator.
DeleteExcellent, thank you very much for this! Really high quality stuff, works absolutely reliable without any hardware debouncing, I didn't think, that this could be done. I have had quite a hard time to port it to basic because C code is so incomprehensible to me, but it definitely worth the time. I reworked the library to very general state and now I have few encoders running at the same time and still have damned effective and small code. Much better than my simple software solution with heavy hardware filtering and still not 100% reliable. Thank you one more time.
ReplyDeleteThis comment has been removed by a blog administrator.
DeleteWould you mind sharing your multi encoder implementation? seems that a lot of people (myself included) are interested in doing that.
ReplyDeleteThis comment has been removed by the author.
DeleteIdea is quite simple, I have excluded data collecting and processing outside the library towards user responsibility and also variable holding last encoder state. Routine in library just take actual encoder state (pre-processed to 000000XX binary form by user) and last encoder state as parameters and return event as byte - CCW, CW or nothing. So user can define and use whatever number of encoders, he just need to create parameters for each one and pass them to library routine for processing. Here is the code:
Deletemodule Rotary_encoder
dim encoder_state as byte[2] volatile
'Values returned by 'function Rotary'
' No complete step yet.
const DIR_NONE = 0x0
' Clockwise step.
const DIR_CW = 0x10
' Anti-clockwise step.
const DIR_CCW = 0x20
sub function Rotary(dim pinstate, encoder_number as byte) as byte
implements
const R_START = 0x0
const R_CW_FINAL = 0x1
const R_CW_BEGIN = 0x2
const R_CW_NEXT = 0x3
const R_CCW_BEGIN = 0x4
const R_CCW_FINAL = 0x5
const R_CCW_NEXT = 0x6
const ttable as byte[7][4] = (
' R_START
(R_START, R_CW_BEGIN, R_CCW_BEGIN, R_START),
' R_CW_FINAL
(R_CW_NEXT, R_START, R_CW_FINAL, R_START OR DIR_CW),
' R_CW_BEGIN
(R_CW_NEXT, R_CW_BEGIN, R_START, R_START),
' R_CW_NEXT
(R_CW_NEXT, R_CW_BEGIN, R_CW_FINAL, R_START),
' R_CCW_BEGIN
(R_CCW_NEXT, R_START, R_CCW_BEGIN, R_START),
' R_CCW_FINAL
(R_CCW_NEXT, R_CCW_FINAL, R_START, R_START OR DIR_CCW),
' R_CCW_NEXT
(R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START)
)
sub function Rotary(dim pinstate, encoder_number as byte) as byte
' Determine new state from the pins and state table.
encoder_state[encoder_number] = ttable[encoder_state[encoder_number] AND 0xf][pinstate]
' Return emit bits, ie the generated event.
result = encoder_state[encoder_number] AND 0x30
end sub
end.
So far tested on two encoders at once served in single interrupt routine - uses almost no resources, but works like a charm. Here is my interrupt routine:
sub procedure PinChangeInterrupt() iv IVT_ADDR_PCINT1 ics ICS_AUTO
dim i_, temp, output, current_state as byte
temp = PINB
for i_ = 0 to 1
current_state = temp AND 000011
output = Rotary(current_state, i_)
if output = DIR_CW then
if selector[i_] = 0 then
Inc(setpointV[i_])
else
Inc(setpointA[i_])
end if
else
if output = DIR_CCW then
if selector[i_] = 0 then
if setpointV[i_] > 0 then
Dec(setpointV[i_])
end if
else
if setpointA[i_] > 0 then
Dec(setpointA[i_])
end if
end if
end if
end if
temp = temp >> 2
next i_
end sub
It is a part of bigger project, so some things may not give sense (this is only small snippet) but it should give you a small look how it works if you are able to read mikroBasic code.
In the full step code, in the 3rd row in the table:
ReplyDeleteR_CW_BEGIN
(R_CW_NEXT, R_CW_BEGIN, R_START, R_START),
I don't understand why if we receive the 0-0 encoder output, we have to go to R_CW_NEXT. As far as i can understand the code, the previous state was 1-0, so we only can go to 1-1 (NEXT) or 0-0. 0-0 should mean START... but the table has R_CW_NEXT.
could you explain why these values are so?
This comment has been removed by a blog administrator.
DeleteHi Ben!
ReplyDeleteI have used your algorithm in a project: http://atoomnet.net/countdown-timer-with-random-intervals/. It works beautifully.
At first I did not understand how it worked, but later I did and it is really elegant.
The rotary encoder is the only input. You can use is to configure settings on the lcd screen for the countdown timer program.
Thanks.
Nice project! Glad the code works well - this one-knob input is exactly what I intended it to be for.
DeleteHi Ben,
ReplyDeleteAfther 2 weeks trying to get the Rotary working in my project i have stopped it and try to write a help signal to u.
I need some little help with your amazing Rotary project.
I have written a project to control a television/radio tuner with I2C. To control (select) the frequency i use a PCF with buttons Up and Down. this project is working exelent.
i Have find on the www your lib & example code. I want to inplement your Rotary code that i have a choice to set the frequency or with the buttons, or with the Rotary. just both wanted to be active in the project.
This is the PCF button part of the code,and below that the control for the Rorary.
{
Wire.requestFrom(PCF1_BUTTON, 1);
if(Wire.available()) // If the request is available from the PCF1_BUTTON
{
PCF_1=Wire.read(); // Receive the data from the PCF1_BUTTON
}
if(PCF_1<255) // If the button data is less than 255
{
if (PCF_1==254) { StepDR2--; }; // P0 = 0xFE Decrease DR2 with 1 step
if (PCF_1==253) { StepDR2++; }; // P1 = 0xFD Increase DR2 with 1 step
if (PCF_1==251) { Xtal=Xtal_a; }; // P2 = 0xFB Xtal 4.0 MHz (depending device = stepsize = 62.5 kHz)
if (PCF_1==247) { Xtal=Xtal_b; }; // P3 = 0xE7 Xtal 3.2 MHz (depending device = stepsize = 50 kHz)
if (PCF_1==239) { Band=HB; }; // P4 =
if (PCF_1==223) { RELAY_Output(Relay_2, HIGH); }; //Mono
if (PCF_1==191) { RELAY_Output(Relay_2, LOW); }; //Stereo
if (PCF_1==127) { RELAY_Output(Relay_1, HIGH); }; // P7 Spare (for testing)
}
{
unsigned char result = r.process();
if (result == DIR_NONE)
{
}
else if (result == DIR_CW)
{
StepDR2++;
}
else if (result == DIR_CCW)
{
StepDR2--;
{
The Problem i have in my project, it stuck now for more that 2 weeks :(
When i put this (above) or the complete Rotary example in my code, is dont react at all.
what i am doing wrong??
The complete project code i have written is to long to put here, if u want to have it, i can send u, no problem.
hopefully u can help me.
Best regard's
Edwin
pd2ebh@hotmail.com
the Netherlands
This comment has been removed by a blog administrator.
DeleteWhen i put this code in the project:
ReplyDelete{
Wire.requestFrom(PCF1_BUTTON, 1);
if(Wire.available()) // If the request is available from the PCF1_BUTTON
{
PCF_1=Wire.read(); // Receive the data from the PCF1_BUTTON
}
if(PCF_1<255) // If the button data is less than 255
{
if (PCF_1==254) { StepDR2--; }; // P0 = 0xFE Decrease DR2 with 1 step
if (PCF_1==253) { StepDR2++; }; // P1 = 0xFD Increase DR2 with 1 step
if (PCF_1==251) { Xtal=Xtal_a; }; // P2 = 0xFB Xtal 4.0 MHz (depending device = stepsize = 62.5 kHz)
if (PCF_1==247) { Xtal=Xtal_b; }; // P3 = 0xE7 Xtal 3.2 MHz (depending device = stepsize = 50 kHz)
if (PCF_1==239) { Band=HB; }; // P4 = 0xEF Low band = 0xA1
if (PCF_1==223) { RELAY_Output(Relay_2, HIGH); };
if (PCF_1==191) { RELAY_Output(Relay_2, LOW); };
if (PCF_1==127) { RELAY_Output(Relay_1, HIGH); }; // P7 Spare (for testing)
}
ISR(PCINT2_vect)
{
unsigned char result = r.process();
if (result == DIR_NONE) {
// do nothing
}
else if (result == DIR_CW) {
Serial.println("ClockWise");
}
else if (result == DIR_CCW) {
Serial.println("CounterClockWise");
}
}
i get this problem;
Universal_controller_met_Rotary_test:228: error: expected unqualified-id before string constant
Universal_controller_met_Rotary_test:229: error: a function-definition is not allowed here before '{' token
line 228 >> ISR(PCINT2_vect)
any help will be appreciate.
Edwin
Could it be that your microcontroller doesnt support PCINT2? I get the impression that the compiler is not finding its definition. try changing the PCINT to one supported by your micro, or use polling mode instead.
DeleteIf I comment out the #define HALF_STEP in the rotary3 example, I get a bunch of CRAZY compiler errors but why!
ReplyDeleterotary3.ino: In function 'void rotary_init()':
rotary3:49: error: 'INPUT' was not declared in this scope
rotary3:49: error: 'pinMode' was not declared in this scope
rotary3:52: error: 'HIGH' was not declared in this scope
rotary3:52: error: 'digitalWrite' was not declared in this scope
rotary3.ino: In function 'unsigned char rotary_process()':
rotary3:63: error: 'digitalRead' was not declared in this scope
rotary3.ino: In function 'void setup()':
rotary3:69: error: 'Serial' was not declared in this scope
rotary3.ino: In function 'void loop()':
rotary3:76: error: 'Serial' was not declared in this scope
If, instead I delete all the code setting up the half step table, it compiles ok but then it only goes in one direction, as Adrian found out a while back, due to a typo in the full step table
Regards,
Philip
This comment has been removed by a blog administrator.
DeleteHi Ben,
ReplyDeleteThanks for providing the rotary library. My project uses a rotary encoder on pins D2 and D3 of a Uno and I can get the 'rotary' example to work fine. However I also need to use the SoftwareSerial library to implement a MIDI in and Out on pins D11 and D12. When I add this I get a compiler error:
SoftwareSerial\SoftwareSerial.cpp.o: In function `__vector_5':
C:\Documents and Settings\Philip\My Documents\Arduino\libraries\SoftwareSerial/SoftwareSerial.cpp:319: multiple definition of `__vector_5'
rotary_demo.cpp.o:C:\Program Files\Arduino/rotary_demo.ino:35: first defined here
The __vector_5 error seems to be related to the PCINT2_vect.
A simple sketch (extension of the 'rotary' example) to show this problem is:
// Comment out the next line to remove the MIDI handling code
#define _MIDI
#ifdef _MIDI
// Handling the MIDI stuff
#include
#define MIDI_RXpin 12
#define MIDI_TXpin 11
SoftwareSerial mySerial(MIDI_RXpin,MIDI_TXpin); // RX, TX
byte incomingByte;
byte note;
byte chan;
byte command;
#endif
#include
Rotary r = Rotary(2, 3);
void setup() {
Serial.begin(9600);
PCICR |= (1 << PCIE2);
PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
sei();
}
void loop() {
}
ISR(PCINT2_vect) {
unsigned char result = r.process();
if (result) {
Serial.println(result == DIR_CW ? "Right" : "Left");
}
}
Any suggestions or is it impossible to use rotary lib and Software Serial lib together?
Thanks,
Philip
It's possible that the SoftwareSerial library also has a handler for PCINT2, which would give the conflict here.
DeleteWays to address it:
- Remove the handler from SoftwareSerial.
- Use a different pin (and thus PCINT) for rotary.
- Use polling for rotary instead.
I cant give exact details, but that should give a starting point.
Thanks for your reply Ben. I'll have a look in SoftwareSerial. I know already that it does have a handler for PCINT2 (line 319 in the compiler error message). I will try if I can remove that line and see if it works. I am running the MIDI on pins 11 and 12 so maybe SoftwareSerial doesn't need PCINT2??
DeleteAlso I have managed to poll the rotary but it misses if the rotary is spun too fast but I may be able to get away with it as is.
Will let you know how I get on :-)
Phil.
in SoftwareSerial.cpp there is a line that looks like this:
DeleteISR(PCINT2_vect, ISR_ALIASOF(PCINT0_vect));
comment that out. wOOt!
Oops - for some reason the #includes didn't paste??
ReplyDeletethe first is
#include '<'SoftwareSerial.h'>'
and the second is
#include '<'rotary.h'>'
(without the quotes)
Good news!
ReplyDeleteBy commenting out lines 318-323 in SoftwareSerial.cpp my script now compiles and, thankfully the MIDI input (which uses SoftwareSerial) still works :-)
Thanks for your help Ben and hopefully this fix may help others who need to use your rotary lib and the SoftwareSerial lib.
Phil.
this is really old, I know, like me, but Phil or anyone, I need the SoftwareSerial.cpp that you used as I too need it, but can't compile with the standard SoftwareSerial.cpp. Thanks!
DeleteCan this code be adapted to Arduino 0023 - if so, how? (with 1.0.x I get very poor performance when I use pulseIn() )
ReplyDeleteFor people like me, that know little to nothing about software I changed the code from displaying 'left' & 'right' to showing numbers 0-10 and counting up or down, depending on the direction. If you go over 10 it starts at 0, and below 0 will jump to 10.
int x;
void setup() {
/*
code from example here, unchanged
*/
int x = 5;
}
void loop() {
}
ISR(PCINT2_vect) {
unsigned char result = r.process();
if(result) {
if(result == 16) {x--; }
if(result == 32) {x++; }
if(x < 0 ) {x = 10; }
if(x > 10) {x = 0 ; }
Serial.println(x);
}
}
This comment has been removed by a blog administrator.
Delete//this called from external interrupt for falling and
ReplyDelete//rising edges
void Rotary_InterrupCallback(void)
{
static uint8_t state_left=0,state_right=0;
BOOL a,b;
uint8_t data;
a = GPIO_ReadInputDataBit(ROTARY_A_PORT, ROTARY_A_PIN);
b = GPIO_ReadInputDataBit(ROTARY_B_PORT, ROTARY_B_PIN);
data = (a << 1) | b;
switch(data)
{
case 0x00:
state_left = 0;
break;
case 0x02:
if(state_left == 0)
{
state_left=1;
}
break;
case 0x03:
if(state_left == 1)
{
state_left=2;
Rotary_Decrement();
return;
}
break;
case 0x01:
if(state_left == 2)
{
state_left=3;
Rotary_Decrement();
return;
}
break;
}
switch(data)
{
case 0x00:
state_right = 0;
break;
case 0x01:
if(state_right == 0)
{
state_right=1;
}
break;
case 0x03:
if(state_right == 1)
{
state_right=2;
Rotary_Increment();
}
break;
case 0x02:
if(state_right == 2)
{
state_right=3;
Rotary_Increment();
}
break;
}
Thanks for this. I've adapated your code to work with a couple of encoders on a Raspberry Pi wifi radio project (flickr : http://www.flickr.com/photos/limpfish/sets/72157634474295977/with/9208712190/ ) Code I had been using previously was awful. Thanks again.
ReplyDeleteThis comment has been removed by a blog administrator.
DeleteI just started using Arduino and I am not thrilled with much of the stuff it trows out as errors.
ReplyDeleteI have given up on using the Rotary header / library for few reasons.
This snippet throws out something "does not name type"
So what does that means? t
#include
Rotary r = Rotary(2,3); .
Second - the rotary.cpp constructor is
Rotary(char,char)
Should that be int's?
Third
How does one keep track of verisons? How do I know I AM using the latest / gratest?
I guess I use your code and forget to rotary library for now.
73 Vaclav AA7EJ
This comment has been removed by a blog administrator.
DeleteHello, I have some issue with this code (1.1). Namely, when I turn rotary encoder left-right-left-right...etc.it does not emit event, and I have to turn two click, and after that it emits events conitunuosly(turnig encoder in one direction), when I change direction again it emits event after second click.
ReplyDeleteHere is my simple code:
unsigned char rot_state = 0;
int8_t i=5;
const unsigned char ttable[7][4] = {
// R_START
{R_START, R_CW_BEGIN, R_CCW_BEGIN, R_START},
// R_CW_FINAL
{R_CW_NEXT, R_START, R_CW_FINAL, R_START | DIR_CW},
// R_CW_BEGIN
{R_CW_NEXT, R_CW_BEGIN, R_START, R_START},
// R_CW_NEXT
{R_CW_NEXT, R_CW_BEGIN, R_CW_FINAL, R_START},
// R_CCW_BEGIN
{R_CCW_NEXT, R_START, R_CCW_BEGIN, R_START},
// R_CCW_FINAL
{R_CCW_NEXT, R_CCW_FINAL, R_START, R_START | DIR_CCW},
// R_CCW_NEXT
{R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN , R_START},
};
while(1){
//Rotary encoder, PC4, PC5.
unsigned char pinstate = (PINC & _BV(PC5)) >> 5 | (PINC & _BV(PC4)) >> 3; //PC5 op bit0, PC4 op bit1
rot_state = ttable[rot_state & 0x0f][pinstate];
if ((rot_state & 0x30) == DIR_CW ) {i++; if (i>9) i=9;}
if ((rot_state & 0x30) == DIR_CCW ) {i--; if (i<0) i=0;}
PORTB=x[i];
}
I solved this issue.
DeleteRotary encoder I bought have different sequence(encoder is http://www.alps.com/WebObjects/catalog.woa/E/HTML/Encoder/Incremental/EC12E/EC12E1220405.html). it starts(when no finger action applied) with 10,11,01,00,10. It starts and ends with 10, and your code starts with 00 and ends with 00. Because I dont have enough a programmer/digital skills to understand this code(state machine), I bought another encoder (http://www.song-huei.com.tw/pdfimages/ED1112.pdf) to try, and it has sequence you wrote this table(00,10,11,01,00).
Some tips to use this library on MEGA2560?
ReplyDeleteThanks in advance,
Mimmo
Hi Erminio I'm Marco from a rotary encoder factory in China, it would be great if we can have a talk thank you.
DeleteWhere is the power coming from to light up the leds??? is the centre pin on 5v?
ReplyDeleteI assume you mean for the first video? The LEDS are on +5v, then the center of the encoder is at GND.
DeleteThis comment has been removed by a blog administrator.
Deletehow does ths work can someone please help me understand why pin2 has a left shift of 1 bit and then ORed with pin one.
ReplyDeleteunsigned char pinstate = (digitalRead(pin2) << 1) | digitalRead(pin1);
This comment has been removed by the author.
DeleteIt's so they can be joined in a single variable, pinstate, for further manipulation. For example, If pin 1 is low and pin 2 is high, pinstate will be 10. Pin 1 high and pin 2 low, pinstate is 01, etc.
DeleteThis is an awesome library. Worked with attiny85 without changing a single line.
thanks ben for this code!!! i hope it works for what im trying to do..
ReplyDeletei cant try the code that i have edited at the moment but i have added a 2nd encoder and have it output as button presses on midi. (using teensy 3.1 so it is usbMIDI)
i have edited out all the parts to the half parts as i kept getting errors
[CODE]
/* Rotary encoder handler for arduino.
*
* Copyright 2011 Ben Buxton. Licenced under the GNU GPL Version 3.
* Contact: bb@cactii.net
*
* Quick implementation of rotary encoder routine.
*
* More info: http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.html
*
*/
const byte ROTARY0_PIN1 = 2;
const byte ROTARY0_PIN2 = 3;
const byte ROTARY1_PIN1 = 4;
const byte ROTARY1_PIN2 = 5;
int DIR_CCW0 = 0x10;
int DIR_CW0 = 0x20;
int DIR_CCW1 = 0x10;
int DIR_CW1 = 0x20;
const unsigned char ttable[7][4] = {
{0x0, 0x2, 0x4, 0x0}, {0x3, 0x0, 0x1, 0x10},
{0x3, 0x2, 0x0, 0x0}, {0x3, 0x2, 0x1, 0x0},
{0x6, 0x0, 0x4, 0x0}, {0x6, 0x5, 0x0, 0x10},
{0x6, 0x5, 0x4, 0x0},
};
volatile unsigned char state0 = 0;
volatile unsigned char state1 = 0;
/* Call this once in setup(). */
void rotary0_init() {
pinMode(ROTARY0_PIN1, INPUT);
pinMode(ROTARY0_PIN2, INPUT);
}
void rotary1_init(){
pinMode(ROTARY1_PIN1, INPUT);
pinMode(ROTARY1_PIN2, INPUT);
}
/* Read input pins and process for events. Call this either from a
* loop or an interrupt (eg pin change or timer).
*
* Returns 0 on no event, otherwise 0x80 or 0x40 depending on the direction.
*/
unsigned char rotary0_process() {
unsigned char pinstate = (digitalRead(ROTARY0_PIN2) << 1) | digitalRead(ROTARY0_PIN1);
state0 = ttable[state0 & 0xf][pinstate];
return (state0 & 0x30);
}
unsigned char rotary1_process() {
unsigned char pinstate = (digitalRead(ROTARY1_PIN2) << 1) | digitalRead(ROTARY1_PIN1);
state1 = ttable[state1 & 0xf][pinstate];
return (state1 & 0x30);
}
void setup() {
rotary0_init();
rotary1_init();
}
void loop() {
unsigned char result0 = rotary0_process();
if (result0) {
result0 == DIR_CCW0 ? "LEFT" : "RIGHT";
if("LEFT"){
usbMIDI.sendNoteOn(1,127,5);
delayMicroseconds(5);
usbMIDI.sendNoteOff(1,0,5);
}
if("RIGHT"){
usbMIDI.sendNoteOn(2,127,5);
delayMicroseconds(5);
usbMIDI.sendNoteOff(2,0,5);
}
}
unsigned char result1 = rotary1_process();
if (result1) {
Serial.println(result1 == DIR_CCW1 ? "LEFT" : "RIGHT");
if("LEFT"){
usbMIDI.sendNoteOn(3,127,5);
delayMicroseconds(5);
usbMIDI.sendNoteOff(3,0,5);
}
if("RIGHT"){
usbMIDI.sendNoteOn(4,127,5);
delayMicroseconds(5);
usbMIDI.sendNoteOff(4,0,5);
}
}
}
[/CODE]
again thanks heaps
This comment has been removed by a blog administrator.
DeleteIs there any explanation how to fill lookup table for diferent sequence ? For this one 10,11,01,00,10 for example? I am not sure why some rotary encoders have diferent sequences.
ReplyDeleteHi Ben,
ReplyDeleteI used your library for a program that uses an si5351 as a vfo/bfo for ham radio purposes.
Thanks.
http://ak2b.blogspot.com/
This comment has been removed by a blog administrator.
DeleteHi Ben,
ReplyDeleteI'm loving this code. Have you ever considered adding a velocity section to switch between 1/4, HALF and FULL mode based on how fast the encoder is rotated? I'm just getting started and am trying to build something like that based on your superior code. Any hints would be most appreciated!
thanks,
Daniel
This comment has been removed by a blog administrator.
DeleteI'm trying to compile the original file ( RotaryLib.zip ) , but every time I try , it returns me the same error:
ReplyDeleteArduino: 1.0.6 (Windows 7), Board: "Arduino Leonardo"
rotary.ino: In function 'void setup()':
rotary:7: error: 'PCIE2' was not declared in this scope
rotary:8: error: 'PCINT18' was not declared in this scope
rotary:8: error: 'PCINT19' was not declared in this scope
What i´m doing wrong?
Hi Ben!
ReplyDeleteLove this library - exactly what I was looking for. I wanted something that didn't require interrupts, and I didn't want to re-write the example from Adafruit or try to adapt something from the Arduino playground.
I made some modifications to the library to support push-button encoders, and posted the changes up on Bitbucket. Please feel free to grab it, and let me know what you think!
https://bitbucket.org/Dershum/rotary_button/src
This comment has been removed by a blog administrator.
ReplyDeleteHello, Ben... Thank you for posting this code! I am a complete Noob. I would like to use digital encoders to control digital resistors in a DIY power supply. I am using a Microchip brand PIC18F45K50 and their MCP4361-103 digital resistor. I am using MPLAB X for coding in C, not C++. Will your C++ code compile as C code? If not, where could I learn how to port it to C? What else might need to be changed to use the 18F chip, instead of the Arduino. Thank you for your help!
ReplyDeleteWas there a fix for when you remm out the half step define like this:
ReplyDelete// #define HALF_STEP
So you can run the code in full step?
BTW I remmed out all of the half step lines to get it to run in full step and no matter which way I turn the knob it displays Left Left Left !!
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteAdrian's post from Aug 2012 still applies: there is a bug in the FULL_STEP table that causes the code to only see turning left. Change the 10 to a 20 as indicated and the problem is solved.
ReplyDeletehi ben & brianlow. thx a lot for your tutorial. your libs help me a lot.
ReplyDeleteThanks for the library, an elegant solution!
ReplyDeleteAfter all these years, still one of the better solutions. Any chance I could get some help with 3 encoders on a Mega? Ports 2,3,18,19,20,21.
ReplyDeleteHi Ben,
ReplyDeleteThanks for this solution - certainly elegant :)
I have a question about the likelihood of multiple debounces - not sure
if this is seen in real life (I guess I should experiment...), but the state machine only handles a single debounce, right?
What happens if you've got flaky hardware and the below is the signal
coming from the pins:
00000101010111111131313133333333232322222020200000
?
I guess it could also depends on the polling speed, but have you seen
something like this happen?
Cheers,
Bonny
Bonny,
DeleteCheap encoders are miserable to work with. I bought 5 and managed to get 3 good ones that don't go crazy just by touching them. I imagine there are "good" encoders out there, but I don't know where to get them.
Hello everyone,
ReplyDeleteSomeone knows how to change the Rotary library so you can use with the Mega 2560?
I need more pins than the UNO RV3. so I need to use the Mega.
Thank you.
Daniel
Daniel,
DeleteI was in the same boat. I needed three encoders. I ended up doing away with the Rotary library and just using attachInterrupt like so:
attachInterrupt(digitalPinToInterrupt(2), interrupt, CHANGE);
attachInterrupt(digitalPinToInterrupt(3), interrupt, CHANGE);
attachInterrupt(digitalPinToInterrupt(20), interrupt, CHANGE);
attachInterrupt(digitalPinToInterrupt(21), interrupt, CHANGE);
attachInterrupt(digitalPinToInterrupt(18), interrupt, CHANGE);
attachInterrupt(digitalPinToInterrupt(19), interrupt, CHANGE);
All the interrupts dump into this function:
Deletevoid interrupt()
{
encode = 1;
}
Once encode is set to 1, the rest is handled in the main loop.
Thanks for the info, I need one encoder, the digital pins are for control relays.
ReplyDeleteThe problem with the proposed program at the beginning, is I can not recall the contents of result.
I need :
ISR(PCINT0_vect)
{
unsigned char result = r.process();
if (result) {
if (result == DIR_CW){rx=rx+increment;}
else {rx=rx-increment;};
}
}
about to get started with rotary encoders, had no idea about Direction change issues
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteHi, excelent blog. How about 1/4 cycle rotary?. would you write code for this rotary. Thanks.
ReplyDeleteI am arduino newbee.
Ben, I ported your code to a PIC18F1330 and I have it working nicely to handle four encoders with switches. There is a bug in your code which prevents it from working correctly in full step mode. The file is rotary3.ino, on line 33 you need to change the 0x10 to 0x20. People using your Arduino library will not see this- the problem is in the code in the 'rotary3' folder which does not use the library.
ReplyDeleteTHANK YOU!!! Finally a library that works. Much appreciated.
ReplyDeleteHello Ben,
ReplyDeleteThanks to your article, I could correct errors in a Proton PIC Basic program that reads a 600 ppr optical rotary encoder. Below you can see a part of the program. Thanks for your help. Regards, Franz
Device 16F874A 'The PIC is PIC16F874A.
Xtal = 16 'I am Using a 16 MHz Xtal
Config HS_OSC, WDTE_OFF , PWRTE_ON , BOREN_OFF ,_
CP_OFF
All_Digital = true 'All I/O are digital
On_Hardware_Interrupt Encoder 'On falling edge of A goto Encoder:
TRISB = %10000011
OPTION_REG.7 = 0 'Enable pull-up resistors
OPTION_REG.6 = 0 'Interruption at falling edge
INTCON.7 = 1 'Enable global interrutions
INTCON.4 =1 'Enable RB0/INT interruptions
Dim Counter As Word
Dim OldA As Bit 'To be used in the interruption routine
Dim OldB As Bit
PORTB = 0
Symbol A = PORTB.0 'Encoder's signal A
Symbol B = PORTB.1 'Encoder's signal B
'*********** INTERRUPTION *************
Encoder:
Context Save 'Save registry contents
'Checking the Gray code sequence mentioned by Ben:
'If A and B change at the same time, Counter is not affected.
If A <> OldA And B <> OldB Then NoChange
If INTCON.1 = 1 Then
If B = 1 Then Inc Counter
If B = 0 Then Dec Counter
OldA = A
OldB = B
EndIf
NoChange:
INTCON.1 = 0 'Clear RB0/INT flag
Context Restore 'Restore former registy values.
Hi Ben, Thanks for library. After many years it is still my favourite.
ReplyDeleteIs it possible to add (or)change code for "quad step"? (4x mode like in "pjrc" library).
MAc
ok - I've spent too many hours trying to understand this state table and finally I am giving up and asking for help. I looked back through all the comments and there was one fellow that asked pretty much the same thing as I am trying to figure out without getting a response so I will copy his question here -
ReplyDelete-----
In the full step code, in the 3rd row in the table:
R_CW_BEGIN
(R_CW_NEXT, R_CW_BEGIN, R_START, R_START),
I don't understand why if we receive the 0-0 encoder output, we have to go to R_CW_NEXT. As far as i can understand the code, the previous state was 1-0, so we only can go to 1-1 (NEXT) or 0-0. 0-0 should mean START... but the table has R_CW_NEXT.
could you explain why these values are so?
--------------------------------
Hold the press ! I figured it out with the help of a youtube video where a fellow implements a similar approach and in fact references this site. The key to understanding this (at least for me) was the "state diagram" that he draws out on the whiteboard. You can see the paths much easier and prevent confusion.
Deletehttps://www.youtube.com/watch?v=BJHftzjNjkw
@Lewis: I have seen the youtube and understood the state diagram, however, I'm still confused about having an output (from the encoder) of 00 and state jumping to 11 (NEXT). Any hint will be greatly appreciated...
DeleteNow I understand…
DeleteI was puzzled as of why, in the 3rd line (R_CW_BEGIN), an output of 00, rather than 11, took us to state R_CW_NEXT. If you follow the states, you will find that, for a CW, the sequence is 00>01>00>10>11>00….so it still works! Although, through a less efficient route…
I have re written the state table so that it follows the required sequence: 00>10>11>01>00
const unsigned char ttable[7][4] = {
// R_START
{R_START, R_CCW_BEGIN, R_CW_BEGIN, R_START},
// R_CW_FINAL
{R_START | DIR_CW, R_CW_FINAL, R_START, R_CW_NEXT},
// R_CW_BEGIN
{R_START, R_START, R_CW_BEGIN, R_CW_NEXT},
// R_CW_NEXT
{R_START, R_CW_FINAL, R_CW_BEGIN, R_CW_NEXT},
// R_CCW_BEGIN
{R_START, R_CCW_BEGIN, R_START, R_CCW_NEXT },
// R_CCW_FINAL
{R_START | DIR_CCW, R_START, R_CCW_FINAL, R_CCW_NEXT},
// R_CCW_NEXT
{R_START, R_CCW_BEGIN, R_CCW_FINAL, R_CCW_NEXT},
};
I have tested it and it works like a charm! I hope it helps others...
Thank you very much for your work, and for sharing!!!
ReplyDeleteAnother thanks from me :)
ReplyDeleteExcellent work! thanks
ReplyDeleteYOU ARE MY HERO
ReplyDeleteWith your code, my encoder is running smoothly. Thank you very much!
ReplyDeleteMy encoder has half-turn detents, in 11 and 00, then I have to use the half step table.
I added some code to activate acceleration. Although it is running right until now, I am relatively new to these things, and I don't know yet if the code is well writen or if the code is bullet proff.
The acceleration depends of maximum multiplier and the gap time. Bigger multiplier permits bigger increments, while bigger gap time, permits activate the acceleration with less turning speed.
In practice it works like this: on slowly turning the encoder, each step will change the value 1 by 1, as the speed of rotation increases, it will change the value by 2 in 2, more speed by 3 in 3, and so, until the maximum set in the multiplier was hit. How quickly the acceleration will be active depends on the gap time.
unsigned char Rotary::process()
{
unsigned char pinstate = (digitalRead(pin2) << 1) | digitalRead(pin1);
state = ttable[state & 0xf][pinstate];
//instead returning directly, the result is passed to variable "direction"
int direction = encoderState_ & 0x30;
int factor = 1; //factor used to multiple to output, default is 1
if ((direction != 0) && accIsOn_) //if direction has changed and acc is on (accIsOn_ is a bool flag to acceleration)
{
//elapsed time between actual turning and last turning
unsigned long turnInterval = (millis() - lastReadTimeMs_);
if (turnInterval < accGapTimeMs_) //if elapsed time is smaller than gap time
{
// Here the turn interval will be subtracted from the acceleration gap time
// This returns a value that will be larger as the rotation is faster
int z = (accGapTimeMs_ - turnInterval);
// And here the value will be remaped from: minimum = 1, maximum: acc gap time
// to: minimum = 1, maximum = (acc multiplier +1). This +1 is because without it
// the acceleration, even at the maximum, will reach only the maximum - 1.
factor = map(z, 1, accGapTimeMs_, 1, (accMaxMultiplier_ + 1));
}
lastReadTimeMs_ = millis(); // store when the last turn happened
}
// The return of function will be
// 0 = no movement
// positive number = clock wise turning
// negative number = counter clock wise turning
if (direction == DIR_CW) //if direction is CW
{
int x = (1 * factor); //multiply the factor by 1
return x; //return the value
}
else if (direction == DIR_CCW) //if direction is CCW
{
int x = (-1 * factor); //multiply the factor by -1
return x; //return the value
}
else
// if has no movement
return 0; //return 0
}
I have been unsuccessful using your code. Can you help by putting it in an .ino for me.
DeleteSomeone did: https://forum.arduino.cc/index.php?topic=242356.msg2308019#msg2308019
DeleteHello and thank you for the elegantly simple code to bypass all the nasty debounce routines that I have been seeing all over the web. I am interested in modifying the .h file and .cpp files so that I can use an encoder that was a third input, the SW button when pushing down on the shaft. Would this even be possible?
ReplyDeleteI want to control and navigate a menu on an LCD screen using a single rotary encoder and its push button functionality.
Thank you in advance for taking the time to read and respond.
Hi there...I have included in the following link the rotary.h and .cpp libraries with a brief explanation of a couple of modifications that I've made. I hope it helps!
Deletehttps://carlossilesblog.wordpress.com/2019/07/27/rotary-encoder/
I need a rotary encoder just as good as this one but in Python (M5Stack|Core). Anyone that has one?
ReplyDeleteHey, I just wanted to thank you for writing and sharing this library.
ReplyDeletehi.
ReplyDeletei'm trying to use one RE for many jobs. is this the right way to do it?
Rotary Step1Rotary = Rotary(rotarypin1, rotarypin2);
Rotary Step2Rotary = Rotary(rotarypin1, rotarypin2);
Rotary Step3Rotary = Rotary(rotarypin1, rotarypin2);
Rotary Step4Rotary = Rotary(rotarypin1, rotarypin2);
RotaryProcessThing = &Step1Rotary;
Then change what RotaryProcessThing refers to each time I change what I'm using the encoder for?
I'm assuming the last state is then preserved for each?
i'm calling RotaryProcessThing.process() in a later function to increment or reduce variables
DeleteThe example doesn't compile on windows 10 with arduino 1.8.11.
ReplyDeleteSo far I only spent countless hours getting a decent rotary encoder routine to work. This project highlights the issues I've had, so I would hope it would get working.
C:\Users\me\AppData\Local\Temp\cc8tAXOq.ltrans0.ltrans.o: In function `rotate':
D:\buxtronix-encoder\interrupt/interrupt.ino:29: undefined reference to `Rotary::process()'
C:\Users\me\AppData\Local\Temp\cc8tAXOq.ltrans0.ltrans.o: In function `global constructors keyed to 65535_0_interrupt.ino.cpp.o.1755':
:(.text.startup+0x60): undefined reference to `Rotary::Rotary(char, char)'
collect2.exe: error: ld returned 1 exit status
exit status 1
Error compiling for board Arduino Uno.
https://getdailybook.com/ for top selling books
ReplyDeletehttp://alltopc.com/ for softwares
ReplyDeletehttps://hashmiipc.com/adobe-premiere-pro-cc-2015-free-download
ReplyDeleteSimple, fast and easy to port to my PIC-controller, using MPLAB IDE with CX8 C-compiler.
ReplyDeleteBefore, I lost several days trying to solve some strange behavior of my Encoders. It took me some while to realize that it was caused by bouncing. Realizing that using a state-machine was the way to go, this post crossed my path, and made me win back several days instead of re-inventing the wheel!
Thanks a lot!!!
FYI Ben, as of June 2021: This is still the nutz. Thanks for sharing.
ReplyDelete