Slow Down Rotary Enoder from 360PPR

In my application I am using the script from the LCD "Hello World" page to display serial messages from my external device. I am using a optical rotary encoder fro Amazon. The application script works fine with the cheap mechanical encoders that are 15PPR. Is there a way to slow down the optical encoder so that it counts every other pulse or some other fraction?
Thanks

/* Read Quadrature Encoder
   Connect Encoder to Pins encoder0PinA, encoder0PinB, and +5V.

   Sketch by max wolf / www.meso.net
   v. 0.1 - very basic functions - mw 20061220

*/

int val;
int encoder0PinA = 3;
int encoder0PinB = 4;
int encoder0Pos = 0;
int encoder0PinALast = LOW;
int n = LOW;

void setup() {
  pinMode (encoder0PinA, INPUT);
  pinMode (encoder0PinB, INPUT);
  Serial.begin (9600);
}

void loop() {
  n = digitalRead(encoder0PinA);
  if ((encoder0PinALast == LOW) && (n == HIGH)) {
    if (digitalRead(encoder0PinB) == LOW) {
      encoder0Pos--;
    } else {
      encoder0Pos++;
    }
    Serial.print (encoder0Pos);
    Serial.print ("/");
  }
  encoder0PinALast = n;
}

You must have posted the wrong sketch by accident. There isn't even a single line of rotary encoder code in there.

Sure, just count the pulses in a global variable, and only do whatever you want to do everytime it goes past an even number, or a multiple of three.

Please post the name and specifications of the encoder. It's not enough to just say you got it from Amazon. They carry thousands of similar items.

i've found the turning a relatively inexpensive encoder by hand requires interrupts.

code i'm using to read encoder (i have 2)

typedef enum { ___, For, Rev, Skp, ActSize } Act_t;

const char* actStr [] = { "___", "For", "Rev", "Skp", "ActSize" };

Act_t  encAct [ActSize][ActSize] = {
// state  00   01   10   11      inputs
       { ___, Rev, For, Skp },  // 00
       { For, ___, Skp, Rev },  // 01
       { Rev, Skp, ___, For },  // 10
       { Skp, For, Rev, ___ },  // 11
};

int encAst  = 0;
int encBst  = 0;

// ------------------------------------------------
// read brake
inline void readEncoder (
    byte  dt,
    byte  clk,
    int & brkPos,
    int & encSt )
{
    byte  val = digitalRead (dt) << 1 | digitalRead (clk);

    switch (encAct [val][encSt])  {
    case For:
        brkPos++;
        break;

    case Rev:
        brkPos--;
        break;

    default:
        break;
    }

    encSt = val;

 // digitalWrite (LED, ! digitalRead (LED));
}

// -------------------------------------
void IRAM_ATTR isrEncA (void)
{
    readEncoder (Enc_A_Dt, Enc_A_Clk, encApos, encAst);
}

I did post the wrong code. Corrected now. Sorry.

You might have a better time if you used a library to deal with your RotEnc rather than roll your own, for example:

If your loop() has any logic that will take a while, like transmitting data over a 9600-baud UART interface, your program might just completely miss some pulses from a fast-moving rotary encoder. Using interrupts will help with that.

another encoder-library that works very reliably and has comfort-functions like
limiting values, set a start value is the NewEncoder-library
here is a demo-code with explaining.
(can't resist to ask a rhetoric question: which code will be used from a newcomer?
That code that is easiest to understand and based on understanding it to modify it)


#include "NewEncoder.h" //https://github.com/gfvalvo/NewEncoder

const byte EncChA_Pin = 2;
const byte EncChB_Pin = 3;
const int minVal = -10;
const int maxVal =  10;
const int startVal = 2;

// Pins 2 and 3 should work for many processors, including Uno. 
// See README https://github.com/gfvalvo/NewEncoder#readme 
// for meaning of constructor arguments.
// Use FULL_PULSE for encoders that produce one complete quadrature pulse per detnet, such as: https://www.adafruit.com/product/377
// Use HALF_PULSE for endoders that produce one complete quadrature pulse for every two detents, such as: https://www.mouser.com/ProductDetail/alps/ec11e15244g1/?qs=YMSFtX0bdJDiV4LBO61anw==&countrycode=US&currencycode=USD

NewEncoder myEncoderObject(EncChA_Pin, EncChB_Pin, minVal, maxVal, startVal, FULL_PULSE);

int16_t currentValue;
int16_t prevEncoderValue;

void setup() {
  // myEncState is a variable of type EncoderState
  // EncoderState is a structured variable that has two "simple" variables
  // .currentValue which is type int16_t
  // (16 bit signed integer valuerange -36767 to 36767)
  // currentValue counts up / down with each pulse created through rotating the encoder
  // and
  // .currentClick which is of type "EncoderClick"
  // the variable type "EncoderClick" can have just 3 values
  // NoClick, DownClick, UpClick where "click" means a "pulse" created through rotating the encoder
  NewEncoder::EncoderState myEncState;

  Serial.begin(115200);
  delay(2000);
  Serial.println("Starting");

  if (!myEncoderObject.begin()) {
    Serial.println("Encoder Failed to Start. Check pin assignments and available interrupts. Aborting.");
    while (1) {
      yield();
    }
  } else {
    // store values of currentValue and EncoderClick into variable myEncState
    myEncoderObject.getState(myEncState);
    Serial.print("Encoder Successfully Started at value = ");
    prevEncoderValue = myEncState.currentValue;
    Serial.println(prevEncoderValue);
  }
}

void loop() {
  NewEncoder::EncoderState myCurrentEncoderState;

  // store actual values into variable myCurrentEncoderState
  if (myEncoderObject.getState(myCurrentEncoderState)) {
    Serial.print("Encoder: ");
    currentValue = myCurrentEncoderState.currentValue;

    // if currentValue has REALLY changed print new currentValue
    if (currentValue != prevEncoderValue) {
      Serial.println(currentValue);
      prevEncoderValue = currentValue;


      // if currentValue stayed the same because the number is at upper/lower limit
      // check if encoder was rotated by using the UpClick / DownClick-values
    } else
      switch (myCurrentEncoderState.currentClick) {
        case NewEncoder::UpClick:
          Serial.println("at upper limit.");
          break;

        case NewEncoder::DownClick:
          Serial.println("at lower limit.");
          break;

        default:
          break;
      }
  }
}

best regards Stefan

Using a 300PPR optical encoder is a very poor choice for a manual menu selection. You can follow the advice of post #3, but you would do better to go back to the low resolution encoder with detents.
Why did you change?

I completely agree - the haptic feedback ('click') is important for precise operation and without it, your menu will just feel floaty.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.