user31526: I'm using an Arduino Uno and two (3-pin) rotary encoders for a project. Since the loop takes a bit long and accuracy is important, I'd like to use interrupts to read the values from the rotary encoders. Also, pins 2 and 3 are occupied so I can't use hardware interrupts.
I'm using the Rotary library (github link) for the rotary encoders and the EnableInterrupt library (github link) for external interrupts. The Rotary library includes an interrupt example, but I couldn't manage to modify it to work with pin change interrupts. This is my current state (test code):
#include <EnableInterrupt.h>
#include <Rotary.h>

Rotary r = Rotary(2, 3);

void setup() {
  Serial.begin(9600);

  /* Original interrupt setup:
  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();
  */

  enableInterrupt(2, interruptFunction, CHANGE);
  enableInterrupt(3, interruptFunction, CHANGE);
}

void loop() {

}

void interruptFunction() {

  Serial.println("Int. called");

  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");
  }
}

Everytime I turn the encoder for one step, the interruptFunction gets called a bunch of times (about 2~4) but the r.process() call never returns a value.
My current guess is that this has something to do with the rotary encoder outputting gray code, but I'm not quite sure how my approach differs from the Rotary interrupt example using hardware interrupts.
Hope you can help!

user35068: I too had this problem. I chased my tail in SW for hours. I was making the ISRs non-reentrant (assumed they accidentally were), I was doing SW debouncing etc. I even looked at the signal on a scope but did not see any significant bounces.
I eventually added 0.1uF capacitors to the inputs and that solved all my problems with the ISR.
So don't trust the encoder manufacturers, there WILL be signal bounce and your Arduino WILL detect it and throw multiple interrupts.
Good luck with your project!
LDP

user37540: 
Everytime I turn the encoder for one step, the interruptFunction gets
  called a bunch of times (about 2~4)

This is due to contact bounces. You should either implement a hardware debouncing circuit (a simple R-C filter could be enough. Do not connect instead large capacitors directly in parallel to your contacts, as when the contact closes, the energy stored on the capacitor will damage over time the contacts. Using large capacitances on an input can also lead to problems when power is removed), or better a good software debouncing strategy.
For instance, when the interrupt is called the first time, disable the pin-change interrupts. Then set up a timer, which will trigger an interrupt when it expires (the time should be larger than the bounce time, usually several ms or more). Then in the timer interrupt, first put your code:
  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");
  }

end re-enable the pin-change interrupts.
  enableInterrupt(2, interruptFunction, CHANGE);
  enableInterrupt(3, interruptFunction, CHANGE);


but the r.process() call never returns a value.

r.process() will ALWAYS returns a value. Maybe you did mean that it returns DIR_NONE ?  

user30005: check the library to see if it debounces the input.
if not, write your own code. usually state machine-based approaches are very resistant to switch bounces.

user69247: Code Correction 7th October - Tidy code as per comments
Code Correction 7th October - proper reading of rotary encoder
Uses the Pin Change interrupts to allow increment and decrement of the 2 rotary encoders within an interrupt service routine using arduino (in my case nano) with ATMEGA328P chip. Need to add 2 x 104 (0.1uF caps) at the rotary encoder between each pin and the centre ground pin to stop the debounce, doesnt work without the capacitors added. Does not need any library. Watched the Tutorial: Pin Change Interrupts on the Arduino by makecourse which helped understand the Pin change interrupts, will help to watch this if have a different version of arduino so know what digital pins relate to what interrupts.
          //*******************************************  rotary encoder ************************************************************
          #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))    //shortcut way to clear a bit in a register
          #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))     //shortcut way to set a bit in a register
          
          
          #define Encoder1pin 3 //corresponds to PCINT19
          #define Encoder2pin 4 //corresponds to PCINT20
          #define Encoder3pin 5 //corresponds to PCINT21
          #define Encoder4pin 6 //corresponds to PCINT22
          #define Encoder5pin 7 //corresponds to PCINT23

          unsigned long encoderPos1 = 100;
          unsigned long oldEncPos1 = 0; 
          unsigned long encoderPos2 = 100; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
          unsigned long oldEncPos2 = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
          
          volatile byte portDstatus;
          
          volatile bool readingEncoder1pin = 0;
          volatile bool readingEncoder2pin = 0;
          volatile bool readingEncoder3pin = 0;
          volatile bool readingEncoder4pin = 0;
          volatile bool lastreadingEncoder1pin = 0;
          volatile bool lastreadingEncoder2pin = 0;
          volatile bool lastreadingEncoder3pin = 0;
          volatile bool lastreadingEncoder4pin = 0;
          
          void setup() {
          
            Serial.begin(9600);
          
          
          //*************************************************** rotary encoder ******************************************************************************
            pinMode(Encoder1pin, INPUT);//define pin as INPUT  
            digitalWrite(Encoder1pin, HIGH);//connect the internal pull-up resistor - this will yield a HIGH readout if the switch is open.
            pinMode(Encoder2pin, INPUT);
            digitalWrite(Encoder2pin, HIGH);
            pinMode(Encoder3pin, INPUT);
            digitalWrite(Encoder3pin, HIGH);
            pinMode(Encoder4pin, INPUT);
            digitalWrite(Encoder4pin, HIGH);
            pinMode(Encoder5pin, INPUT);
            digitalWrite(Encoder5pin, HIGH);
          
          
            //setting up the interrupt pins:   //I needed to set all of these otherwise the serial monitor does not read the byte correctly -
                                               // and it doesnt work.
          
            sbi (PCICR, PCIE2); // enable interrupt PCI2);
            sbi (PCMSK2,PCINT23); // set the interrupt control bits for pins 7-3. These bits control whether the pins can trigger the interrupt or not. 
            sbi (PCMSK2, PCINT22);
            sbi (PCMSK2, PCINT21);
            sbi (PCMSK2, PCINT20);
            sbi (PCMSK2, PCINT19); // we do not want to set interrupt control for pins 0 or 1 since this is serial reading .
          
          //******************************************************************* LED *********************************************************************
          }
          
          void loop() 
          
              {
               }
          
           
          
          
          ISR(PCINT2_vect) // Interrupt Service Routine. This code is executed when a state change occurs on a pin 
          {
          
            
          
                portDstatus=PIND;
               readingEncoder1pin = bitRead(portDstatus,3);
               readingEncoder2pin = bitRead(portDstatus,4);
             if(lastreadingEncoder1pin == 1 && lastreadingEncoder2pin == 0 && readingEncoder1pin == 1 && readingEncoder2pin == 1)
              {
                  encoderPos1 ++;
                   lastreadingEncoder1pin = readingEncoder1pin;
                   lastreadingEncoder2pin = readingEncoder2pin;
              }
          
              else if(lastreadingEncoder1pin == 0 && lastreadingEncoder2pin == 1 && readingEncoder1pin == 1 && readingEncoder2pin == 1)
              {
                encoderPos1 --;
                  lastreadingEncoder1pin = readingEncoder1pin;
                  lastreadingEncoder2pin = readingEncoder2pin;;
              }
              else
                {
                   lastreadingEncoder1pin = readingEncoder1pin;
                   lastreadingEncoder2pin = readingEncoder2pin;
                }
          
          
    //  reading the second encoder:       
          
          
          
               readingEncoder3pin = bitRead(portDstatus,5);
               readingEncoder4pin = bitRead(portDstatus,6);
          
             if(lastreadingEncoder3pin == 1 && lastreadingEncoder4pin == 0 && readingEncoder3pin == 1 && readingEncoder4pin == 1)
              {
                  
                  encoderPos2 ++;
                   lastreadingEncoder3pin = readingEncoder3pin;
                   lastreadingEncoder4pin = readingEncoder4pin;
              }
          
              else if(lastreadingEncoder3pin == 0 && lastreadingEncoder4pin == 1  && readingEncoder3pin == 1 && readingEncoder4pin == 1)
              {
                encoderPos2 --;
                  lastreadingEncoder3pin = readingEncoder3pin;
                  lastreadingEncoder4pin = readingEncoder4pin;;
              }
              else
                {
          
                   lastreadingEncoder3pin = readingEncoder3pin;
                   lastreadingEncoder4pin = readingEncoder4pin;
                }
             // serial print for testing only then remove from ISR  
          Serial.print(encoderPos1);
          Serial.println(encoderPos2);
        
             }


31526: <p>I'm using an Arduino Uno and two (3-pin) rotary encoders for a project. Since the loop takes a bit long and accuracy is important, I'd like to use interrupts to read the values from the rotary encoders. Also, pins 2 and 3 are occupied so I can't use hardware interrupts.</p>

<p>I'm using the Rotary library (<a href="https://github.com/brianlow/Rotary" rel="nofollow noreferrer">github link</a>) for the rotary encoders and the EnableInterrupt library (<a href="https://github.com/GreyGnome/EnableInterrupt" rel="nofollow noreferrer">github link</a>) for external interrupts. The Rotary library includes an interrupt example, but I couldn't manage to modify it to work with pin change interrupts. This is my current state (test code):</p>

<pre><code>#include &lt;EnableInterrupt.h&gt;
#include &lt;Rotary.h&gt;

Rotary r = Rotary(2, 3);

void setup() {
  Serial.begin(9600);

  /* Original interrupt setup:
  PCICR |= (1 &lt;&lt; PCIE2);
  PCMSK2 |= (1 &lt;&lt; PCINT18) | (1 &lt;&lt; PCINT19);
  sei();
  */

  enableInterrupt(2, interruptFunction, CHANGE);
  enableInterrupt(3, interruptFunction, CHANGE);
}

void loop() {

}

void interruptFunction() {

  Serial.println("Int. called");

  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");
  }
}
</code></pre>

<p>Everytime I turn the encoder for one step, the interruptFunction gets called a bunch of times (about 2~4) but the r.process() call never returns a value.</p>

<p>My current guess is that this has something to do with the rotary encoder outputting gray code, but I'm not quite sure how my approach differs from the Rotary interrupt example using hardware interrupts.</p>

<p>Hope you can help!</p>
 35068: <p>I too had this problem. I chased my tail in SW for hours. I was making the ISRs non-reentrant (assumed they accidentally were), I was doing SW debouncing etc. I even looked at the signal on a scope but did not see any significant bounces.
I eventually added 0.1uF capacitors to the inputs and that solved all my problems with the ISR.
So don't trust the encoder manufacturers, there WILL be signal bounce and your Arduino WILL detect it and throw multiple interrupts.
Good luck with your project!
LDP</p>
 37540: <blockquote>
  <p>Everytime I turn the encoder for one step, the interruptFunction gets
  called a bunch of times (about 2~4)</p>
</blockquote>

<p>This is due to contact bounces. You should either implement a hardware debouncing circuit (a simple R-C filter could be enough. Do not connect instead large capacitors directly in parallel to your contacts, as when the contact closes, the energy stored on the capacitor will damage over time the contacts. Using large capacitances on an input can also lead to problems when power is removed), or better a good software debouncing strategy.</p>

<p>For instance, when the interrupt is called the first time, disable the pin-change interrupts. Then set up a timer, which will trigger an interrupt when it expires (the time should be larger than the bounce time, usually several ms or more). Then in the timer interrupt, first put your code:</p>

<pre><code>  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");
  }
</code></pre>

<p>end re-enable the pin-change interrupts.</p>

<pre><code>  enableInterrupt(2, interruptFunction, CHANGE);
  enableInterrupt(3, interruptFunction, CHANGE);
</code></pre>

<blockquote>
  <p>but the r.process() call never returns a value.</p>
</blockquote>

<p>r.process() will ALWAYS returns a value. Maybe you did mean that it returns DIR_NONE ?  </p>
 30005: <p>check the library to see if it debounces the input.</p>

<p>if not, write your own code. usually state machine-based approaches are very resistant to switch bounces.</p>
 69247: <p>Code Correction 7th October - Tidy code as per comments
Code Correction 7th October - proper reading of rotary encoder</p>
<p>Uses the Pin Change interrupts to allow increment and decrement of the 2 rotary encoders within an interrupt service routine using arduino (in my case nano) with ATMEGA328P chip. Need to add 2 x 104 (0.1uF caps) at the rotary encoder between each pin and the centre ground pin to stop the debounce, doesnt work without the capacitors added. Does not need any library. Watched the Tutorial: Pin Change Interrupts on the Arduino by makecourse which helped understand the Pin change interrupts, will help to watch this if have a different version of arduino so know what digital pins relate to what interrupts.</p>
<pre><code>          //*******************************************  rotary encoder ************************************************************
          #define cbi(sfr, bit) (_SFR_BYTE(sfr) &amp;= ~_BV(bit))    //shortcut way to clear a bit in a register
          #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))     //shortcut way to set a bit in a register
          
          
          #define Encoder1pin 3 //corresponds to PCINT19
          #define Encoder2pin 4 //corresponds to PCINT20
          #define Encoder3pin 5 //corresponds to PCINT21
          #define Encoder4pin 6 //corresponds to PCINT22
          #define Encoder5pin 7 //corresponds to PCINT23

          unsigned long encoderPos1 = 100;
          unsigned long oldEncPos1 = 0; 
          unsigned long encoderPos2 = 100; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
          unsigned long oldEncPos2 = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
          
          volatile byte portDstatus;
          
          volatile bool readingEncoder1pin = 0;
          volatile bool readingEncoder2pin = 0;
          volatile bool readingEncoder3pin = 0;
          volatile bool readingEncoder4pin = 0;
          volatile bool lastreadingEncoder1pin = 0;
          volatile bool lastreadingEncoder2pin = 0;
          volatile bool lastreadingEncoder3pin = 0;
          volatile bool lastreadingEncoder4pin = 0;
          
          void setup() {
          
            Serial.begin(9600);
          
          
          //*************************************************** rotary encoder ******************************************************************************
            pinMode(Encoder1pin, INPUT);//define pin as INPUT  
            digitalWrite(Encoder1pin, HIGH);//connect the internal pull-up resistor - this will yield a HIGH readout if the switch is open.
            pinMode(Encoder2pin, INPUT);
            digitalWrite(Encoder2pin, HIGH);
            pinMode(Encoder3pin, INPUT);
            digitalWrite(Encoder3pin, HIGH);
            pinMode(Encoder4pin, INPUT);
            digitalWrite(Encoder4pin, HIGH);
            pinMode(Encoder5pin, INPUT);
            digitalWrite(Encoder5pin, HIGH);
          
          
            //setting up the interrupt pins:   //I needed to set all of these otherwise the serial monitor does not read the byte correctly -
                                               // and it doesnt work.
          
            sbi (PCICR, PCIE2); // enable interrupt PCI2);
            sbi (PCMSK2,PCINT23); // set the interrupt control bits for pins 7-3. These bits control whether the pins can trigger the interrupt or not. 
            sbi (PCMSK2, PCINT22);
            sbi (PCMSK2, PCINT21);
            sbi (PCMSK2, PCINT20);
            sbi (PCMSK2, PCINT19); // we do not want to set interrupt control for pins 0 or 1 since this is serial reading .
          
          //******************************************************************* LED *********************************************************************
          }
          
          void loop() 
          
              {
               }
          
           
          
          
          ISR(PCINT2_vect) // Interrupt Service Routine. This code is executed when a state change occurs on a pin 
          {
          
            
          
                portDstatus=PIND;
               readingEncoder1pin = bitRead(portDstatus,3);
               readingEncoder2pin = bitRead(portDstatus,4);
             if(lastreadingEncoder1pin == 1 &amp;&amp; lastreadingEncoder2pin == 0 &amp;&amp; readingEncoder1pin == 1 &amp;&amp; readingEncoder2pin == 1)
              {
                  encoderPos1 ++;
                   lastreadingEncoder1pin = readingEncoder1pin;
                   lastreadingEncoder2pin = readingEncoder2pin;
              }
          
              else if(lastreadingEncoder1pin == 0 &amp;&amp; lastreadingEncoder2pin == 1 &amp;&amp; readingEncoder1pin == 1 &amp;&amp; readingEncoder2pin == 1)
              {
                encoderPos1 --;
                  lastreadingEncoder1pin = readingEncoder1pin;
                  lastreadingEncoder2pin = readingEncoder2pin;;
              }
              else
                {
                   lastreadingEncoder1pin = readingEncoder1pin;
                   lastreadingEncoder2pin = readingEncoder2pin;
                }
          
          
    //  reading the second encoder:       
          
          
          
               readingEncoder3pin = bitRead(portDstatus,5);
               readingEncoder4pin = bitRead(portDstatus,6);
          
             if(lastreadingEncoder3pin == 1 &amp;&amp; lastreadingEncoder4pin == 0 &amp;&amp; readingEncoder3pin == 1 &amp;&amp; readingEncoder4pin == 1)
              {
                  
                  encoderPos2 ++;
                   lastreadingEncoder3pin = readingEncoder3pin;
                   lastreadingEncoder4pin = readingEncoder4pin;
              }
          
              else if(lastreadingEncoder3pin == 0 &amp;&amp; lastreadingEncoder4pin == 1  &amp;&amp; readingEncoder3pin == 1 &amp;&amp; readingEncoder4pin == 1)
              {
                encoderPos2 --;
                  lastreadingEncoder3pin = readingEncoder3pin;
                  lastreadingEncoder4pin = readingEncoder4pin;;
              }
              else
                {
          
                   lastreadingEncoder3pin = readingEncoder3pin;
                   lastreadingEncoder4pin = readingEncoder4pin;
                }
             // serial print for testing only then remove from ISR  
          Serial.print(encoderPos1);
          Serial.println(encoderPos2);
        
             }
</code></pre>
