Problems using newSettings method in newEncoder library

Hi,

I’ve been using @gfvalvo’s excellent newEncoder library with some success for a project I am working on. I now need to be able to reset my encoder parameters for various menu levels as part of an LCD menu interface.

I note that the library provides a newSettings function that allows one to dynamically reset the min / max encoder values.

 bool newSettings(int16_t newMin, int16_t newMax, int16_t newCurrent, EncoderState &state); 

However, I’m having trouble applying the fourth argument, EncoderState &state, which I understand is a variable of the type NewEncoder::EncoderState that is being passed by reference.

The newSettings function is also discussed in How to Change Encoder Values - #5 by cattledog where another person was also having trouble with this method but clear instructions on its use were not provided in this post.

Consider the example library sketch, CustomEncoder.ino, which I’ve used to try to figure out how to apply the newSettings method. CustomEncoder.ino creates a custom Encoder object using inheritance where the encoder “wraps around” when it hits the max / min limits - which is how I want my encoders to behave. For newSettings testing purposes, I’ve just added a flag that I manually set when I want to reset the encoder values and the main loop simply tries to apply the newSettings function if the flag is set.

I’ve not been able to get this sketch to successfully compile and consistently get the error

 'EncoderState' was not declared in this scope 

I’ve been researching pass by reference and this error for several days but can’t seem to overcome this error, hence this post. I'm not sure if it is a namespace / scope issue or if I'm just neglecting to declare something necessary.

Please be gentle with me as I’m new to C++ and a novice programmer.

Full sketch and error below. Note that I use Visual Code / PlatformIO rather than the Arduino IDE.

Thanks in advance!

Sketch:

#include "Arduino.h"
#include "NewEncoder.h"

// CustomEncoder.ino example
//  ref: https://github.com/gfvalvo/NewEncoder/blob/master/examples/CustomEncoder/CustomEncoder.ino

// Demonstrate creation of custom Encoder using inheritance.
// This encoder "wraps around" when it hits the min or max limit

class CustomEncoder : public NewEncoder {
   public:
    CustomEncoder() : NewEncoder() {
    }
    CustomEncoder(uint8_t aPin, uint8_t bPin, int16_t minValue, int16_t maxValue, int16_t initalValue, uint8_t type = FULL_PULSE) : NewEncoder(aPin, bPin, minValue, maxValue, initalValue, type) {
    }
    virtual ~CustomEncoder() {
    }

   protected:
    virtual void updateValue(uint8_t updatedState);
};

void ESP_ISR CustomEncoder::updateValue(uint8_t updatedState) {
    if ((updatedState & DELTA_MASK) == INCREMENT_DELTA) {
        liveState.currentClick = UpClick;
        liveState.currentValue++;
        if (liveState.currentValue > _maxValue) {
            liveState.currentValue = _minValue;
        }
    } else if ((updatedState & DELTA_MASK) == DECREMENT_DELTA) {
        liveState.currentClick = DownClick;
        liveState.currentValue--;
        if (liveState.currentValue < _minValue) {
            liveState.currentValue = _maxValue;
        }
    }
    stateChanged = true;
}

// Pins 2 and 3 should work for many processors, including Uno. See 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
CustomEncoder encoder(2, 3, 0, 10, 5, FULL_PULSE);

int16_t prevEncoderValue;

// flag for when use in loop when I want the program to apply the newSettings function
bool resetEncoder = 1;

void setup() {
    CustomEncoder::EncoderState state;

    Serial.begin(74880);
    delay(2000);
    Serial.println("Starting");
    if (!encoder.begin()) {
        Serial.println("Encoder Failed to Start. Check pin assignments and available interrupts. Aborting.");
        while (1) {
            yield();
        }
    } else {
        encoder.getState(state);
        Serial.print("Encoder Successfully Started at value = ");
        prevEncoderValue = state.currentValue;
        Serial.println(prevEncoderValue);
    }
}

void loop() {
    int16_t currentValue;
    CustomEncoder::EncoderState currentEncoderState;

    if (resetEncoder == 1) {
        encoder.newSettings(0, 8, 4, EncoderState & state);
    }

    if (encoder.getState(currentEncoderState)) {
        currentValue = currentEncoderState.currentValue;
        if (currentValue != prevEncoderValue) {
            Serial.print("Encoder: ");
            Serial.println(currentValue);
            prevEncoderValue = currentValue;
        }
    }
}

Compiler Error:

Compiling .pio/build/uno/src/main.cpp.o
src/main.cpp: In function 'void loop()':
src/main.cpp:73:38: error: 'EncoderState' was not declared in this scope
         encoder.newSettings(0, 8, 4, EncoderState & state);
                                      ^~~~~~~~~~~~
src/main.cpp:73:38: note: suggested alternative: 'encoder'
         encoder.newSettings(0, 8, 4, EncoderState & state);
                                      ^~~~~~~~~~~~
                                      encoder
*** [.pio/build/uno/src/main.cpp.o] Error 1
==================== [FAILED] Took 3.89 seconds=====

Try this:

    if (resetEncoder == 1) {
        encoder.newSettings(0, 8, 4, currentEncoderState);
    }

Also, note that your 'resetEncoder' global variable is initialized to '1' and never changed. That means you'll perform that newSettings() operation EVERY TIME your loop function iterates. That's probably not what you want.

Thank you for responding @gfvalvo. I really appreciate your help. Making the changes you suggest, the code now successfully compiles but the newSettings() method fails to reset the encoder values to the new max / min values.

Original encoder settings

CustomEncoder encoder(2, 3, 0, 10, 5, FULL_PULSE);

newSettings configuration

encoder.newSettings(0, 8, 4, currentEncoderState);

serial monitor results when I run the revised sketch and rotate the encoder past the max value (which then wraps). You can see that the encoder is still on the initial settings and the min/max and initial value settings are not changed to the new settings .

--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
Starting
Encoder Successfully Started at value = 5
Encoder range reset
Encoder: 6
Encoder: 7
Encoder: 8
Encoder: 9
Encoder: 10
Encoder: 0
Encoder: 1
Encoder: 2
Encoder: 3

Following is what I changed on my code in the void loop. Good pick up that the newSettings method would trigger every loop iteration so I added a reset to the flag plus a serial message to check that the code execution flow.

<code fragment showing changes >
void loop() {
    int16_t currentValue;
    CustomEncoder::EncoderState currentEncoderState;

    if (resetEncoder == 1) {
        encoder.newSettings(0, 8, 4, currentEncoderState);
        Serial.println("Encoder range reset");
        resetEncoder = 0;
    }
<end code fragment>

Your library readme and previous discussion says that the last argument EncoderState &state should be passed by reference. Could the problem be that if I use currentEncoderState as the final argument, I am just passing the instantiated object by value?

Anyhow, I am still at a loss at how to get this working correctly. Thanks in advance for your help.

Please post your complete new code.

Found an error in the library code. Until it's updated on GitHub, replace your downloaded copy of NewEncoder.cpp with the attached. Here's the code I just tested it with:

#include "Arduino.h"
#include "NewEncoder.h"

// Pins 2 and 3 should work for many processors, including Uno. See 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 encoder(2, 3, -20, 20, 0, FULL_PULSE);
int16_t prevEncoderValue;

void setup() {
  NewEncoder::EncoderState state;

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

  if (!encoder.begin()) {
    Serial.println("Encoder Failed to Start. Check pin assignments and available interrupts. Aborting.");
    while (1) {
      yield();
    }
  } else {
    encoder.getState(state);
    Serial.print("Encoder Successfully Started at value = ");
    prevEncoderValue = state.currentValue;
    Serial.println(prevEncoderValue);
  }
}

void loop() {
  int16_t currentValue;
  NewEncoder::EncoderState currentEncoderState;

  if (encoder.getState(currentEncoderState)) {
    Serial.print("Encoder: ");
    currentValue = currentEncoderState.currentValue;
    if (currentValue != prevEncoderValue) {
      Serial.println(currentValue);
      prevEncoderValue = currentValue;

      if (currentValue > 15) {
        Serial.println("Changing Encoder Settings.");
        encoder.newSettings(-10, 10, 5, currentEncoderState);
        prevEncoderValue = currentEncoderState.currentValue;
        Serial.print("Starting Value: ");
        Serial.println(prevEncoderValue);
      }

    } else
      switch (currentEncoderState.currentClick) {
        case NewEncoder::UpClick:
          Serial.println("at upper limit.");
          break;

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

        default:
          break;
      }
  }
}

NewEncoder.cpp (26.2 KB)

Thanks! Appreciate the library fix. My test sketch is working.