How to decode 4 quadrature encoders using only 3 processor’s I/O

I would like to share my personal HW/SW solution that allow to decode 4 quadrature encoders using only 3 processor’s I/O: one external interrupt, one analog input and one GPIO.
It uses only a simple external HW circuit and it’s based on external interrupt routine (no polling), using only one external interrupt input.

This solution could be useful in case your project needs:

  • Small form factor;
  • Low budget;
  • I/O saving;
  • Computing resource saving;

The solution doesn’t use external library, serial communication protocols or complex data manipulation so it’s a valid alternative to the use of more complex solutions and save space and processor’s resources for integration of further functionalities.

External HW schematic: HERE

Complete breadboard based project: HERE

Here's the sketch:

/* This sketch, in combination with a few external components, reads 4 encoders using only 3 microprocessor inputs: one interrupt input,
 * one analog input and one GPIO (in this example digital pin n.4 is used, but an analog input could be used indifferently).
 * 
 * The encoders are connected in parallel through a voltage divider that provides interrupt signals at a specific voltage for each encoder.
 * Depending on the interrupt signal voltage the microprocessor identifies the "active" encoder.
 * After the voltage divider the signal is normalized again to 0-5V to be used as interrupt signal.
 * The system works only with encoders with detent (both encoder contacts normally open or closed).
 * For further details on the external circuit and system working limitations please refer to: https://massimoriggi.wordpress.com/2016/12/12/4-rotary-encoders/
 */

#define intpin 2 //rotary encoder interrupt pin
#define dirpin 4 //rotary encoder direction pin
#define analogreadpin A0 //pin for interrupt signal analog reading

/* The following lines declare:
 *  - variables for Rotary encoders values/position, from rotary encoder n.1 to rotary encoder n.4 (pot1 to pot4);
 *  - variable for analog reading of the interrupt signal (analogvalue);
 *  - variable for encoders direction reading (dir).
 */
volatile int pot1=500; 
volatile int pot2=500; 
volatile int pot3=500;
volatile int pot4=500;
volatile int analogvalue;
volatile int dir;

void setup() {
pinMode(intpin,INPUT);
pinMode(dirpin,INPUT);
pinMode(analogreadpin,INPUT);
attachInterrupt(digitalPinToInterrupt(intpin),readenc,FALLING);
Serial.begin(250000);
}

/* The main loop only prints encoder positions on terminal window. The ISR (readenc function) does all the job! */
void loop() {
  Serial.print(pot4); 
  Serial.print("\t");
  Serial.print(pot3);
  Serial.print("\t");
  Serial.print(pot2);
  Serial.print("\t");
  Serial.println(pot1);
}

void readenc(){
  dir=digitalRead(dirpin); //reads the direction pin state
  analogvalue=analogRead(analogreadpin); //reads the voltage of the interrupt

/* the following lines split the analog reading in 4 different voltage ranges and update (increment or decrement) only the variable of the corresponding encoder */
  if(analogvalue>300&&analogvalue<400){
    if(dir==HIGH){
    pot1++;
    }
  else{
    pot1--;
    }
  }
  if(analogvalue>500&&analogvalue<600){
    if(dir==HIGH){
    pot2++; 
    }
  else{
    pot2--;
    }
  }
  if(analogvalue>700&&analogvalue<800){
    if(dir==HIGH){
    pot3++; 
    }
  else{
    pot3--;
    }
  }
  if(analogvalue>900&&analogvalue<1023){
    if(dir==HIGH){
    pot4++; 
    }
  else{
    pot4--;
    }
  }
}

This is a video demo of the project: HERE

Comments, suggestions, remarks or corrections are really appreciated :slight_smile:

(sorry for my english, for my programming skills and sorry if I have posted it in the wrong section)

Regards.

Massimo