2 Rotary Encoders with Arduino? - Page 1
Forum section: electronics / general

author: Thane of Cawdor
author_rank: Regular Contributor
timestamp: March 02, 2014, 04:25:15 am
content: Hi,I am a little confused on how to use two rotary encoders to control the brightness of two different LED's. I have managed to get one rotary encoder to work using the code from this website:http://www.hobbytronics.co.uk/arduino-tutorial6-rotary-encoder.How can I use this code so that I can use two encoders with two analog output pins (PWM).Many Thanks.

author: JoeN
author_rank: Frequent Contributor
timestamp: March 02, 2014, 04:45:33 am
content: Why would you want to waste two PWM pins for what is a simple digital input application? This code uses pins 11 and 12 but any pins will do. You would put the second encoder on another two pins and just double up the code, at least that is the most straightforward way. If you had lots of encoders, you could refactor it out into a function, of course. If you can to do that I will leave it up to you.Assuming the code you provided works with one encoder and one LED, something like this should work for two encoders and two LEDs. I haven't tested this nor even compiled it, but, pin 10 is the second LED, pin 7 and 8 are the A and B for encoder #2. You can change these.Code:int brightness1 = 120; // how bright the LED is, start at half brightnessint brightness2 = 120; // how bright the LED is, start at half brightnessint fadeAmount = 10; // how many points to fade the LED byunsigned long currentTime;unsigned long loopTime;const int pin_A1 = 12; // pin 12const int pin_B1 = 11; // pin 11const int pin_A2 = 7; // pin 7const int pin_B2 = 8; // pin 8unsigned char encoder_A1;unsigned char encoder_B1;unsigned char encoder_A1_prev=0;unsigned char encoder_A2;unsigned char encoder_B2;unsigned char encoder_A2_prev=0;void setup() {// declare pin 9 to be an output:pinMode(9, OUTPUT);pinMode(10, OUTPUT);pinMode(pin_A1, INPUT);pinMode(pin_B1, INPUT);pinMode(pin_A2, INPUT);pinMode(pin_B2, INPUT);currentTime = millis();loopTime = currentTime;}void loop() {// get the current elapsed timecurrentTime = millis();if(currentTime >= (loopTime + 5)){// 5ms since last check of encoder = 200Hzencoder_A1 = digitalRead(pin_A1); // Read encoder pinsencoder_B1 = digitalRead(pin_B1);if((!encoder_A1) && (encoder_A1_prev)){// A has gone from high to lowif(encoder_B1) {// B is high so clockwise// increase the brightness, dont go over 255if(brightness1 + fadeAmount <= 255) brightness1 += fadeAmount;}else {// B is low so counter-clockwise// decrease the brightness, dont go below 0if(brightness1 - fadeAmount >= 0) brightness1 -= fadeAmount;}}encoder_A1_prev = encoder_A1; // Store value of A for next time// set the brightness of pin 9:analogWrite(9, brightness1);// 5ms since last check of encoder = 200Hzencoder_A2 = digitalRead(pin_A2); // Read encoder pinsencoder_B2 = digitalRead(pin_B2);if((!encoder_A2) && (encoder_A2_prev)){// A has gone from high to lowif(encoder_B2) {// B is high so clockwise// increase the brightness, dont go over 255if(brightness2 + fadeAmount <= 255) brightness2 += fadeAmount;}else {// B is low so counter-clockwise// decrease the brightness, dont go below 0if(brightness2 - fadeAmount >= 0) brightness2 -= fadeAmount;}}encoder_A2_prev = encoder_A2; // Store value of A for next time// set the brightness of pin 10:analogWrite(10, brightness2);}// Other processing can be done here}

author: Thane of Cawdor
author_rank: Regular Contributor
timestamp: March 02, 2014, 05:01:58 am
content: HiThanks for the help. It worked!Just one question, what do you mean by refactoring into a function?Thanks again!

author: JoeN
author_rank: Frequent Contributor
timestamp: March 02, 2014, 08:04:24 am
content: Quote from: Thane of Cawdor on March 02, 2014, 05:01:58 amIt's a little complex to explain, but refactoring code means changing the way that it is written to make the code more readable or maintainable (or some other positive purpose) without actually functionally changing the code. The code I created really just duplicated code in order to track that second rotary encoder. A better way would be to make a function that could track any rotary encoder on any two pins and adjust any brightness variable. That way, when you wanted to go to three or four encoders (subject to actual pin availability, of course) the code wouldn't expand further, no more than making a function call at least. At three copies of the code it would start getting ugly, any change you make to the routine has to be made in three places, not one.http://en.wikipedia.org/wiki/Refactor_code

author: SL4P
author_rank: Super Contributor
timestamp: March 02, 2014, 10:27:25 am
content: You can use the same code strategy as above (I've done it on PICs several times) - by using pin-change interrupts - that way you can eliminate most of the code -= and it's as fast as can be.Simply detect the change on the A-pin, and determine where the B-pin is. There are only four combinations, boiled down to two possible results - count up, or count down.

author: dannyf
author_rank: Super Contributor
timestamp: March 02, 2014, 11:32:04 am
content: My suggestion:1) Put it in a routine so your code is easier to read and can be reused later;2) Use a state machine for each routine - far simpler code than what you have now;3) Use interrupt so it runs in the background and no missing clicks.

author: linux-works
author_rank: Super Contributor
timestamp: March 02, 2014, 05:57:26 pm
content: look for interrupt based rotary reading. arduino online examples have lots.if you don't use an ISR you will miss clicks as you turn the knob too fast. doing it inside the main routine is a non-starter. I suggest abandoning that approach right away unless you know the knob will turn very very slowly and dropped events are ok for your app.I also suggest using both pins on each encoder as state-change pins on the arduino. arduino 328 supports only 2 pins doing that but the 328 CHIP supports many more, so you may have to go outside the standard arduino lib to do this (not a big deal) or use a mega chip which has more pin-change events you can map to.

author: zapta
author_rank: Super Contributor
timestamp: March 02, 2014, 07:35:46 pm
content: I would formulate the code as a state machine for simplicity. Something along these lines below. If you want two just copy/paste the code twice and rename to avoid conflict between the two (yes, I know, one can use classes or templates to avoid source code duplication but I try to keep it simple). Just make sure your loop() spins fast enough. BTW, digitalRead() has some unnecessary overhead, you can use something like library to make it fasterhttps://github.com/zapta/linbus/blob/master/beeper/arduino/io_pins.h// Returns encoder state as bits <a, b>.inline byte readEncoderState() {return (readDigital(pinA1) ? 0b10 : 0b00 ) + (readDigital(pinA2) ? 0b01 : 0b00);}// Last state of this encoderbyte last_state;// in main setup():last_state = readEncoderState();// in the main loop()const byte new_state = readEncoderState();if (last_state == 0b10) {if (new_state == 0b11) {// do increment up with limit enforcement.} else if (new_state == 0b00) {// do increment down with limit enforcement.}}last_state == new_state;

author: dannyf
author_rank: Super Contributor
timestamp: March 02, 2014, 07:47:38 pm
content: All those if statements cab be easily implemented via a look up table.3 or so statements in total.