Problem with Encoder (Lost Steps)

Hi, i am new in this forum, i dont write english perfectly but i can try to improve mi english :) :)

I am making a weather station, this proyect display the info trough LCD 20*4, and manage the diferent menus with rotary encoder, same that:

I look for the rutine to manage encoder that better work in my proyect, i dont want to lost steps so i used this rutine:

/* interrupt routine for Rotary Encoders
   tested with Noble RE0124PVB 17.7FINB-24 http://www.nobleusa.com/pdf/xre.pdf - available at pollin.de
   and a few others, seems pretty universal

   The average rotary encoder has three pins, seen from front: A C B
   Clockwise rotation A(on)->B(on)->A(off)->B(off)
   CounterCW rotation B(on)->A(on)->B(off)->A(off)

   and may be a push switch with another two pins, pulled low at pin 8 in this case
   raf@synapps.de 20120107

*/

// usually the rotary encoders three pins have the ground pin in the middle
enum PinAssignments {
  encoderPinA = 2,   // rigth
  encoderPinB = 3,   // left
  clearButton = 8    // another two pins
};

volatile unsigned int encoderPos = 0;  // a counter for the dial
unsigned int lastReportedPos = 1;   // change management
static boolean rotating=false;      // debounce management

// interrupt service routine vars
boolean A_set = false;              
boolean B_set = false;


void setup() {

  pinMode(encoderPinA, INPUT); 
  pinMode(encoderPinB, INPUT); 
  pinMode(clearButton, INPUT);
 // turn on pullup resistors
  digitalWrite(encoderPinA, HIGH);
  digitalWrite(encoderPinB, HIGH);
  digitalWrite(clearButton, HIGH);

// encoder pin on interrupt 0 (pin 2)
  attachInterrupt(0, doEncoderA, CHANGE);
// encoder pin on interrupt 1 (pin 3)
  attachInterrupt(1, doEncoderB, CHANGE);

  Serial.begin(9600);  // output
}

// main loop, work is done by interrupt service routines, this one only prints stuff
void loop() { 
  rotating = true;  // reset the debouncer

  if (lastReportedPos != encoderPos) {
    Serial.print("Index:");
    Serial.println(encoderPos, DEC);
    lastReportedPos = encoderPos;
  }
  if (digitalRead(clearButton) == LOW )  {
    encoderPos = 0;
  }
}

// Interrupt on A changing state
void doEncoderA(){
  // debounce
  if ( rotating ) delay (1);  // wait a little until the bouncing is done

  // Test transition, did things really change? 
  if( digitalRead(encoderPinA) != A_set ) {  // debounce once more
    A_set = !A_set;

    // adjust counter + if A leads B
    if ( A_set && !B_set ) 
      encoderPos += 1;

    rotating = false;  // no more debouncing until loop() hits again
  }
}

// Interrupt on B changing state, same as A above
void doEncoderB(){
  if ( rotating ) delay (1);
  if( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if( B_set && !A_set ) 
      encoderPos -= 1;

    rotating = false;
  }
}

When i implement this code in my script, dont work properly, when i move bewteen the diferent menus and submenus, lost steps and sometimes moves two steps instead of one.

I think that the encoder quality its too bad, because the code should work without problems...

I can try increase the debounce time, but didnt work. I dont like to use delay() at long code because delay() pause other executions.

This is my code:

//Libreriaswgw
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <DS1307RTC.h>
#include <Time.h>
#include <Bounce.h>


LiquidCrystal_I2C lcd(0x20,20,4);  // set the LCD address to 0x20 for a 20 chars and 4 line display

//Caracteres
byte flecha_dech[8] = { 0x0,0x8,0xc,0xe,0xc,0x8,0x0,0x0 };
//ENCODER+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// usually the rotary encoders three pins have the ground pin in the middle
enum PinAssignments {
  encoderPinA = 2,   // right (labeled DT on our decoder, yellow wire)
  encoderPinB = 3,   // left (labeled CLK on our decoder, green wire)
// connect the +5v and gnd appropriately
};

const int encoderSW = 8;

volatile int menu = 1;              // a counter for the dial
int lastmenu = 0;
static boolean rotating=false;      // debounce management

boolean salir = false;

// interrupt service routine vars
boolean A_set = false;              
boolean B_set = false;
//Asignamos el pin
Bounce encoderSW_bounce = Bounce(8,100); 
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void setup() {
	lcd.init();
  	lcd.backlight(); 					//Iniciamos la retroiluminacion
  	lcd.setCursor(6,0);				//Situamos el cursor al inicio
  	lcd.print("Estacion");		//Imprimimos mensaje inicial
  	lcd.setCursor(3,1);
  	lcd.print("Meteorologica");
  	lcd.setCursor(7,3);				//		
  	lcd.print("L1llo5");				//
  	delay(2000);						//Esperamos 2 segundos con el mensaje
  	lcd.clear();						//Limpiamos la pantalla
	//SETUP ENCODER+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    pinMode(encoderPinA, INPUT_PULLUP);
  	pinMode(encoderPinB, INPUT_PULLUP); 
    pinMode(encoderSW, INPUT_PULLUP);
    //Antirebote 
  	// encoder pin on interrupt 0 (pin 2)
  	attachInterrupt(0, doEncoderA, CHANGE);
	// encoder pin on interrupt 1 (pin 3)
  	attachInterrupt(1, doEncoderB, CHANGE);
  	//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  //Caracteres, creacion
  lcd.createChar(1,flecha_dech);
}

void loop() {
	rotating = true;  // reset the debouncer
	if(menu != lastmenu){
		lcd.clear();
		lastmenu=menu;
	}

  	if (menu>4) {
  		menu=1;
  		lcd.clear();
  		}
  	if (menu<1) {
  		menu=4;
  		lcd.clear();
  		}
  lcd.setCursor(0,0);
  lcd.print(menu);
	switch (menu) {
		case 1:
		lcd.setCursor(6,0);
		lcd.print("Reloj");
    printhour();
		break;
		case 2:
		lcd.setCursor(6,0);
		lcd.print("Tiempo");
		break;
		case 3:
		lcd.setCursor(6,0);
		lcd.print("Opciones");
    lcd.setCursor(1,1);
    lcd.print("Ajustar Hora");
    lcd.setCursor(1,2);
    lcd.print("Ajustar Fecha");
    encoderSW_bounce.update();
    if (encoderSW_bounce.risingEdge()){
      salir = false ;
      menu= 1 ;
      lastmenu = 0;
      while ( salir == false ){
        rotating = true;  // reset the debouncer
        if(menu != lastmenu){
          lastmenu=menu;
          delete_cursor();
        }
        if (menu > 4){
          lcd.clear();
          lcd.setCursor(1,0);
          lcd.print("Ajuste Alarmas");
          lcd.setCursor(1,1);
          lcd.print("Salir");
        }
        else if (menu >5){
          menu = 1;
          lcd.clear();
          lcd.setCursor(1,1);
          lcd.print("Ajustar Hora");
          lcd.setCursor(1,2);
          lcd.print("Ajustar Fecha");
        }
        else if (menu < 1){
          menu = 5;
          lcd.clear();
          lcd.setCursor(0,0);
          lcd.print("Ajuste Alarmas");
          lcd.setCursor(0,1);
          lcd.print("Salir");
        }
      switch (menu) {
        case 1:
        lcd.setCursor(0,1);
        lcd.write(1);
        break;
        case 2:
        lcd.setCursor(0,2);
        lcd.write(1);
        break;
        case 3:
        lcd.setCursor(0,3);
        lcd.write(1);
        break;
        case 4:
        lcd.setCursor(0,0);
        lcd.write(1);
        break;
        case 5:
        lcd.setCursor(0,1);
        lcd.write(1);
      }
      }
    }
		break;
		case 4:
		lcd.setCursor(6,0);
		lcd.print("Alarmas");
		break;
	}
}


//FUNCIONES++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// Interrupt on A changing state
void doEncoderA(){
  // debounce
  if ( rotating ) delay (1);  // wait a little until the bouncing is done
  // Test transition, did things really change
    if( digitalRead(encoderPinA) != A_set ) {  // debounce once more
    A_set = !A_set;
    // adjust counter + if A leads B
    if ( A_set && !B_set ) 
      menu ++;
    rotating = false;  // no more debouncing until loop() hits again
  }
}

// Interrupt on B changing state, same as A above
void doEncoderB(){
  if ( rotating ) delay (1);
  if( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if( B_set && !A_set ) 
      menu --;
    rotating = false;
  }
}
//Printing the time on LCD
void printhour(){
    tmElements_t tm;
  if (RTC.read(tm)) {
    lcd.setCursor(0,1);
    if(tm.Hour >= 0 && tm.Hour < 10){
      lcd.print('0');
    }
    lcd.print(tm.Hour);
    lcd.print(':');
    if(tm.Minute >= 0 && tm.Minute < 10){
      lcd.print('0');
    }
    lcd.print(tm.Minute);
    lcd.print(':');
    if(tm.Second >= 0 && tm.Second < 10){
      lcd.print('0');
    }
    lcd.print(tm.Second);
    lcd.setCursor(0,2);
    lcd.print(tmYearToCalendar(tm.Year));
  }
}
//Borrar cursores
void delete_cursor(){
  lcd.setCursor(0,0);
  lcd.print(" ");
  lcd.setCursor(0,1);
  lcd.print(" ");
  lcd.setCursor(0,2);
  lcd.print(" ");
  lcd.setCursor(0,3);
  lcd.print(" ");
}

Someone, see any solution to the problem?

If i cant solve this, i will implement buttons instead the encoder, but the encoder its more useful

Thanks and i can try to improve my writting

:slight_smile: :slight_smile:

EDIT:

I have been testing the Encoder.h library, and i had problems too. The encoder rutine alone work fine, but when insert the rutine in my code, dont work fine.

I think, the comunication with DS1307, cause problems with encoder rutine, because only have problems when i move between menus “Reloj”, “Time”…when dont display anything on LCD i havent got problems…

Can be use buttons the better solution…

It seems likely that contact bounce (multiple switch closures and openings as you advance the knob) is the problem and that the software is not taking care of it correctly. How do you have the encoder wired to the Arduino? Some of those encoders require external resistors and capacitors to do the debouncing. Google "knob encoder debouncing" for lots of ideas.