Rotary encoder not increment perfectly

Hello

It's my first message and I need some help.

I use Arduino Mega 2560 (clone), 8*8 led display, and rotary encoder KY040.
Project is to display a digit on 8*8 led display and increment or decrement it with the rotary encoder.

The code (below) is very simple, but when I turn the rotary digits change not regulary. Sometimes not change, sometime skip a digit.
I turn slowly or quickly, never got a good incrementation.

I change rotary with another ref, same result.

An idea ?
Mayby I have to tru another biblioteca ?

Many thanks for your help.

JM

#include <LedControl.h>
#include <RotaryEncoder.h>

int CS =  8;
int DIN = 9;
int CLK = 10;

//Déclaration des pins du codeur rotatif
RotaryEncoder encoder(2, 3);  // (DT, CLK)

static int pos = 0;
static int compteur = 0;
int newPos;

// digits 0 to 9
byte chiffre[][8] ={ 
  {B00000000, B00111000, B01000100, B01001100, B01010100, B01100100, B01000100, B00111000},
  {B00000000, B00010000, B00110000, B00010000, B00010000, B00010000, B00010000, B00111000},
  {B00000000, B00111000, B01000100, B00000100, B00001000, B00010000, B00100000, B01111100},
  {B00000000, B01111100, B00001000, B00010000, B00001000, B00000100, B01000100, B00111000},
  {B00000000, B00001000, B00011000, B00101000, B01001000, B01111100, B00001000, B00001000},
  {B00000000, B01111100, B01000000, B01111000, B00000100, B00000100, B01000100, B00111000},
  {B00000000, B00011000, B00100000, B01000000, B01111000, B01000100, B01000100, B00111000},
  {B00000000, B01111100, B00000100, B00001000, B00010000, B00100000, B01000000, B01000000},
  {B00000000, B00111000, B01000100, B01000100, B00111000, B01000100, B01000100, B00111000},
  {B00000000, B00111000, B01000100, B01000100, B00111100, B00000100, B00001000, B00110000}
};

LedControl lc=LedControl(DIN,CLK,CS,0);

void setup(){
 lc.shutdown(0,false);       //The MAX72XX is in power-saving mode on startup
 lc.setIntensity(0,0);      // Set the brightness 
 lc.clearDisplay(0);         // and clear the display
}

void loop(){ 
   encoder.tick();
   newPos = encoder.getPosition();

   if (pos != newPos) {
    if (newPos>pos){
      if (compteur<9){    // maxi 9
          compteur ++;        
       }      
    }else{
      if (compteur>0){    // mini 0 
          compteur --;        
       }        
    }
    pos = newPos; 
   }

  // display the digit
  printByte(chiffre[compteur]);
}

// displaying on 8*8 led
void printByte(byte character []){
  int i = 0;
  for(i=0;i<8;i++)  {
    lc.setRow(0,i,character[i]);
  }
}



Hi @papybou

I had the same problem, trying to find an implementation that functioned correctly and didn't skip detents.

Here's the example that I found that works well:

/*******Interrupt-based Rotary Encoder Sketch*******
by Simon Merrett, based on insight from Oleg Mazurov, Nick Gammon, rt, Steve Spence
*/
 
static int pinA = 2; // Our first hardware interrupt pin is digital pin 2
static int pinB = 3; // Our second hardware interrupt pin is digital pin 3
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile int16_t encoderPos = 0; //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
volatile int16_t oldEncPos = 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 reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent
 
void initRotaryEncoder() 
{
  pinMode(pinA, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
  pinMode(pinB, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
  attachInterrupt(digitalPinToInterrupt(2), PinA, RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
  attachInterrupt(digitalPinToInterrupt(3), PinB, RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
}

void PinA()
{  
  //cli(); //stop interrupts happening before we read pin values
  reading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values
  if (reading == B00001100 && aFlag)  //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
  {
    encoderPos --; //decrement the encoder's position count
    bFlag = 0; //reset flags for the next turn
    aFlag = 0; //reset flags for the next turn
  }
  else if (reading == B00000100)
  {
    bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
  }
  //sei(); //restart interrupts
}
 
void PinB()
{
  //cli(); //stop interrupts happening before we read pin values
  reading = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values
  if (reading == B00001100 && bFlag)  //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
  {
    encoderPos ++; //increment the encoder's position count
    bFlag = 0; //reset flags for the next turn
    aFlag = 0; //reset flags for the next turn
  }
  else if (reading == B00001000) 
  {
    aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
  }
  //sei(); //restart interrupts
}
 
void setup() 
{
  Serial.begin(115200);
  //while(!Serial);
  initRotaryEncoder();
}

void loop()
{
  if(oldEncPos != encoderPos) 
  {
    Serial.println(encoderPos);
    oldEncPos = encoderPos;
  }
}
1 Like

I will try now and tell you.
Thanks !

Sorry,

Strictly paste the code.
CLK on digital 0
DT on digital 1

But nothing in serial console...

@papybou Oops, I missed Serial.begin(), I'll just correct the code above.

@papybou Sorry about that, I've just corrected and tested the new code above, it now works.

Thanks, but still nothing.
I past new code, of course.

But I never use serial monitor before ; maybe i miss something...
Windows displayed, but empty.

@papybou Have you turned the rotary encoder?

Of course :wink:

connexions are ok : I just tried with my code below ; same result as before : digits not incremented strictly.

Thanks, we will recover neurones tomorow...
:wink:

I'm not sure I understand. Is the example code working?

You're welcome to try this library I wrote. It's based on the State Table Approach. It does require use of pins that support external interrupts. On a Mega those are 2, 3, 18, 19, 21.

The library includes several examples showing how to use it:
https://github.com/gfvalvo/NewEncoder

static int pinA = 0; // Our first hardware interrupt pin is digital pin 0
static int pinB = 1; // Our second hardware interrupt pin is digital pin 1

attachInterrupt(digitalPinToInterrupt(0), PinA, RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
  attachInterrupt(digitalPinToInterrupt(1), PinB, RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)

On what Arduino? @papybou is on a Mega, and this code won't work on that platform.

Hi @cattledog

Thanks for the correction, in the example code I've moved the pins to 2 and 3 instead.

Thanks for info concerning pins to choose.
I try and tell you.

Your code don't worked yesterday for me, with pins 0,1.
But I will today test with good pin (2,3).
Just for fun, because I cannot choose this way for my final project : I have 5 rotaries, so we need 10 special pins. gfvalvo wrote Mega has only 5.

Sorry, the las script don't work for me (Mega 2560 clone, pins digital 2,3)...

As I told you, solution with interrupts will not be the good way for me, I have 5 rotaries to read ; not enought special pins...

Many thanks !

My first script works quite well ; but need sometimes to turn during several clics to increment counter by 1.

Hi Papybou,

if you post a description that gives an overview over your project so it becomes clear for what you are using your rotary-encoders maybe suggestions for different solutions can be made.
I guess you don't need to turn all five encoders at the same time. So switching between different ecnoders might be a solution too.

Interrupt-based detecting works very reliable but if you use a microcontroller with a very limited number of interrupts maybe and the encoders turn only slow maybe optimising the execution-time of your loop is an option.

If this not an option maybe using a small second microcontroller for the encoders might be an option.

Or using a library like this one

best regards Stefan

best regards Stefan

I've not tried it, nor have I checked if any of the suggested libraries use it already, but for a little while now I've been wondering about the utility of multiplying the increment of the encoder by a delta time. Similar to how movement is handled in a game.

Thanks.

Idea is to choose a 5 digits number, with 5 rotaries.
Each rotary drive a 8x8 led block, by increment (if possible 1 step on rotay = increment/decrement 1).
Like a safe box in a bank with 5 digits code...

I have a Mega 2560 (clone), that I don't know very well. Until today I used Arduino Uno...

For rotary encoders that a human will be turning, there’s no reason that well-written code couldn’t use a polling-based method instead of an interrupt-based one. I still recommend using the State Table Approached described Here. I recommend that you study it.

Alternately, I’d ask if there’s any reason you MUST use a Mega (clone). There are many boards in the Arduino ecosystem that use processors based on an ARM core. These typically support using interrupts on nearly all pins.

I am building 5 ou 6 objects, for xmas preasents.
And I have already Mega boards.
I choose Mega because this system (rotaries+display) are one of many modules, as parts of a multi-purpose box. I need many pins...

Thanks for the link ; I will read it carefully.