Encoder: upper and lower limit

Hi Forum:

I mixed two Rotary Encoder codes, to be able to use the spin, the button and the mix of button and spin. With an anti-bounce routine.

The problem. The limits are poorly described, when I go up to my upper limit (127) it goes to 0 and when I go down to the lower limit (0) it goes to 127 …

Something similar happens when the button is pressed and I turn, this increases the speed of steps . (in this case 5 steps at a time while the encoder is pressed and rotated) here the upper value goes from 127 to 4, and the lower one from 0 to 123 …

this should not happen, the minimum has to be 0 and the maximum 127 in all cases.

I have no idea how to code this …

this is the code:

//----------------------------MIX ENCODER--------------------------------------------------------------
 
 #include <MIDI.h>            // LIBRERIA MIDI
 #include "Controller.h"      // MIDI CONTROLLER
 MIDI_CREATE_DEFAULT_INSTANCE();
//--------------------------------------/ Declaro Encoder /--------------------------------------------

//-------------------------------------------------------------------------------------------ENTER START
int pinEnt = 4; 
bool E = false;
bool Ent = false;
//-------------------------------------------------------------------------------------------ENTER END

//-------------------------------------------------------------------------------------------ROTARY START
int A = 2;      //variable A a pin digital 2 (DT en modulo)
int B = 3;        //variable B a pin digital 4 (CLK en modulo)

int ANTERIOR = 63;    // almacena valor anterior de la variable POSICION

volatile int POSICION = 63; // variable POSICION con valor inicial de 50 y definida
        // como global al ser usada en loop e ISR (encoder)
//---------------------------------------/ ENCODER END/------------------------------------------------



void setup() {
  Serial.begin(115200);  // TO HAIRLESS MIDI 
//------------------------------------------------------/ SETUP - ENCODER /-------------------------------------------------------

//-------------------------------------------------------------------------------------------SETUP ENTER START
  pinMode (pinEnt,INPUT_PULLUP); // Enter
//-------------------------------------------------------------------------------------------SETUP ENTER END
  pinMode(A, INPUT_PULLUP);    // A como entrada
  pinMode(B, INPUT_PULLUP);    // B como entrada
  attachInterrupt(digitalPinToInterrupt(A), encoder, LOW);
                // interrupcion sobre pin A con
                // funcion ISR encoder y modo LOW
//---------------------------------------------------------------------------------------/ END SETUP - ENCODER
}
//--------------------------------------------------/ END VOID SETUP   /-----------------------------------------------------------


void loop() {
  //------------------------------------------------------------------------------------------/ LOOP - ENCODER 
if (POSICION != ANTERIOR) { // si el valor de POSICION es distinto de ANTERIOR
    ANTERIOR = POSICION; // asigna a ANTERIOR el valor actualizado de POSICION
  }

//--------------------------------------------------------------------------------------------/ END LOOP - ENCODER

//------------------------------------------------------------------ENTER - SW ENCODER----------(MIDI COMO BOTON***)
    // Enter
 if(E == false){
    if(digitalRead(pinEnt) == LOW){
      E = true;
    }
 }

  if(E == true){
    if(digitalRead(pinEnt) == HIGH){
      //Serial.println("Enter");
      E = false;
    }
  }
//-------------------------------------------------------------------------------------------/ END LOOP ENTER ENCODER 
}
//---------------------------------------------/ END VOID LOOP - ENCODER /-------------------------------------------------



//--------------------------------VOID ENCODER-----------------------------

void encoder()  {
  static unsigned long ultimaInterrupcion = 0;  // variable static con ultimo valor de
            // tiempo de interrupcion
  unsigned long tiempoInterrupcion = millis();  // variable almacena valor de func. millis

  if (tiempoInterrupcion - ultimaInterrupcion > 5) {  // rutina antirebote desestima
              // pulsos menores a 5 mseg.
   if(digitalRead(pinEnt) == LOW){ //SI SE PRESIONA BOTON Y SE GIRA                      //"PUSH & TURN" +5 STEPS
              if (digitalRead(B) == HIGH)     // si B es HIGH, sentido horario
                  {
                   POSICION = POSICION +5;  //POSICION++ ;         // incrementa POSICION en 5
                  }
              else {          // si B es LOW, senti anti horario
                    POSICION = POSICION -5; //POSICION-- ;        // decrementa POSICION en 5
                    }  
                                  }     
   else {
             if (digitalRead(B) == HIGH)     // si B es HIGH, sentido horario
                  {
                   POSICION++;  //POSICION++ ;        // incrementa POSICION en 1
                       }
              else {          // si B es LOW, senti anti horario
                      POSICION--; //POSICION-- ;        // decrementa POSICION en 1
                       }
         }
         MIDI.sendControlChange(6, POSICION, 1);
   }
   /*
    POSICION = min(126, max(1, POSICION));  // establece limite inferior de 0 y
            // superior de 100 para POSICION
    ultimaInterrupcion = tiempoInterrupcion;  // guarda valor actualizado del tiempo


    
    if (POSICION >127) {
        POSICION =127;
    }
    else{
        POSICION;
    }
    
    if (POSICION < 0) {
        POSICION =0;
    }
    else{
        POSICION;
    }
   */ 
   if (POSICION>127){// && ANTERIOR==127){
    POSICION=127;
    ANTERIOR=127;
   }
   if (POSICION<0){ //&& ANTERIOR==0){
    POSICION=0;
    ANTERIOR=0;
   }
    ultimaInterrupcion = tiempoInterrupcion; 
  }           // de la interrupcion en variable static

//-----------------------------END VOID ENCODER----------------------------
//}
attachInterrupt(digitalPinToInterrupt(A), encoder, LOW);

You should use edge triggering here not level triggering.

Are you expecting this code to keep the limits between 0 and 127?

if (POSICION >127) {
        POSICION =127;
    }
    else{
        POSICION;
    }
   
    if (POSICION < 0) {
        POSICION =0;
    }
    else{
        POSICION;
    }

If so don’t comment it out.

Why are you even trying to write your own code for this anyway? Why not use a library that does the reading of the encoder correctly, because yours does not?

I would recommend the one at http://www.pjrc.com/teensy/td_libs_Encoder.html

i don’t see the problem you describe.

i tested the code with buttons on A1 and A2 (a MultiFunction board) and since these are NOT interrupt pins, i added code to check for the “A” button going low, call encoder() and added a print when POSICION != ANTERIOR

i don’t see value exceeding 127 or going below 0

Grumpy_Mike:

attachInterrupt(digitalPinToInterrupt(A), encoder, LOW);

You should use edge triggering here not level triggering.

Are you expecting this code to keep the limits between 0 and 127?

if (POSICION >127) {

POSICION =127;
    }
    else{
        POSICION;
    }
 
    if (POSICION < 0) {
        POSICION =0;
    }
    else{
        POSICION;
    }




If so don't comment it out.

Why are you even trying to write your own code for this anyway? Why not use a library that does the reading of the encoder correctly, because yours does not?

I would recommend the one at http://www.pjrc.com/teensy/td_libs_Encoder.html

The limits are in

POSICION = min(127, max(0, POSICION)); // establece limite inferior de 0 y
// superior de 127 para POSICION

ultimaInterrupcion = tiempoInterrupcion; // guarda valor actualizado del tiempo

… but It does not work for me…

what kind of encoder do you use?

mechanical encoders do have problems with bouncing and with detecting "inbetween"-positions when you reverse the rotation-direction at the "wrong" position.

In my personal tests the interrupt-driven econoder-code performed less reliably than the non-interrupt-driven polling-code. But I don't know If a code that uses polling is suitable for your application.

here is a demo-code written by the german user argmue based on an article in a magazine, which takes care of all the possible "inbetween"-states of mechanical switches.

"inbetween" here means as you reverse rotating-direction you can do this with one contact still closed or with one contact already opened. If one contact is still closed when you start the reversed rotation,
to a simple code this looks like turning into the opposite direction because the sequence of how contact A and contact B turn HIGH-LOW is reveresed for the first switching".

const byte ENCODER_A_PIN = 3;
const byte ENCODER_B_PIN = 2;
const byte SWITCH_PIN = 4;

void setup() {
  Serial.begin(115200);
  Serial.println("\nStart");
  pinMode(ENCODER_A_PIN, INPUT);
  pinMode(ENCODER_B_PIN, INPUT);
  pinMode(SWITCH_PIN, INPUT);
}

void loop() {
  int8_t state = 0;
  if (rotaryEncoder(state)) {
    Serial.println("- SWITCH -");
  }
  if (state == -1) {
    Serial.println("<-- ");
  }
  if (state == 1) {
    Serial.println(" -->");
  }
}

bool rotaryEncoder(int8_t &delta) {
  delta = 0;
  enum {STATE_LOCKED, STATE_TURN_RIGHT_START, STATE_TURN_RIGHT_MIDDLE, STATE_TURN_RIGHT_END, STATE_TURN_LEFT_START, STATE_TURN_LEFT_MIDDLE, STATE_TURN_LEFT_END, STATE_UNDECIDED};
  static uint8_t encoderState = STATE_LOCKED;
  bool a = !digitalRead(ENCODER_A_PIN);
  bool b = !digitalRead(ENCODER_B_PIN);
  bool s = !digitalRead(SWITCH_PIN);
  static bool switchState = s;
  switch (encoderState) {
    case STATE_LOCKED:
      if (a && b) {
        encoderState = STATE_UNDECIDED;
      }
      else if (!a && b) {
        encoderState = STATE_TURN_LEFT_START;
      }
      else if (a && !b) {
        encoderState = STATE_TURN_RIGHT_START;
      }
      else {
        encoderState = STATE_LOCKED;
      };
      break;
    case STATE_TURN_RIGHT_START:
      if (a && b) {
        encoderState = STATE_TURN_RIGHT_MIDDLE;
      }
      else if (!a && b) {
        encoderState = STATE_TURN_RIGHT_END;
      }
      else if (a && !b) {
        encoderState = STATE_TURN_RIGHT_START;
      }
      else {
        encoderState = STATE_LOCKED;
      };
      break;
    case STATE_TURN_RIGHT_MIDDLE:
    case STATE_TURN_RIGHT_END:
      if (a && b) {
        encoderState = STATE_TURN_RIGHT_MIDDLE;
      }
      else if (!a && b) {
        encoderState = STATE_TURN_RIGHT_END;
      }
      else if (a && !b) {
        encoderState = STATE_TURN_RIGHT_START;
      }
      else {
        encoderState = STATE_LOCKED;
        delta = -1;
      };
      break;
    case STATE_TURN_LEFT_START:
      if (a && b) {
        encoderState = STATE_TURN_LEFT_MIDDLE;
      }
      else if (!a && b) {
        encoderState = STATE_TURN_LEFT_START;
      }
      else if (a && !b) {
        encoderState = STATE_TURN_LEFT_END;
      }
      else {
        encoderState = STATE_LOCKED;
      };
      break;
    case STATE_TURN_LEFT_MIDDLE:
    case STATE_TURN_LEFT_END:
      if (a && b) {
        encoderState = STATE_TURN_LEFT_MIDDLE;
      }
      else if (!a && b) {
        encoderState = STATE_TURN_LEFT_START;
      }
      else if (a && !b) {
        encoderState = STATE_TURN_LEFT_END;
      }
      else {
        encoderState = STATE_LOCKED;
        delta = 1;
      };
      break;
    case STATE_UNDECIDED:
      if (a && b) {
        encoderState = STATE_UNDECIDED;
      }
      else if (!a && b) {
        encoderState = STATE_TURN_RIGHT_END;
      }
      else if (a && !b) {
        encoderState = STATE_TURN_LEFT_END;
      }
      else {
        encoderState = STATE_LOCKED;
      };
      break;
  }

  uint32_t current_time = millis();
  static uint32_t switch_time = 0;
  const uint32_t bounce_time = 30;
  bool back = false;
  if (current_time - switch_time >= bounce_time) {
    if (switchState != s) {
      switch_time = current_time;
      back = s;
      switchState = s;
    }
  }
  return back;
}

best regards Stefan