Rotary encoder code

Hi guys, Im in need of a little help, Im just starting out with rotary encoders and Ive been playing around with some exaples and what not, but so far I have been unable to implement this into my project.

I am using GUISlice for my project and this library allows input mapping from raw events. Unfortunately the rotary encoder libraries are either over complicated, or give a 0, 1, or 2 depending on stationary, clockwise, or anticlockwise.

I believe what I need are some callbacks where I get only a 0 or 1 for both clockwise and anticlockwise, so I can feed them into guislice as raw input events for next and previous buttons.

I am thinking a modified version of SimpleRotary might be an easy solution, I am just a bit stuck. I took a look but I got the typical beginners block, and was wondrring if someone could give me a few hints just to set me off on the right path? Just a quick look and a few tips, once I know roughly what is needed to be done I usually get into a flow and figure it out.

Thanks :slight_smile:

Unfortunately the rotary encoder libraries are either over complicated, or give a 0, 1, or 2 depending on stationary, clockwise, or anticlockwise.

So, put a simple filter function between these unfortunate libraries and your code, that convert these results into your required values.

Thats a nice idea, I will keep that in mind for plan b. I have come somewhat further wit the modification to the library, Unfortunately there are some small issues. Sometimes left is shown when turning right and vice versa.

Here is what I have so far:

#include "Arduino.h"
#include "SimpleRotary.h"

SimpleRotary::SimpleRotary(byte pinA, byte pinB, byte pinS)
{
  _pinA = pinA;
  _pinB = pinB;
  _pinS = pinS;
  _currentTime = millis();
  _debounceRTime = _currentTime;
  _debounceSTime = _currentTime;
  _errorTime = _currentTime;
  
  _setInputPins();
}




/**
	SET TRIGGER
	Sets the trigger for rotations / button press either to high or low.
	If set to HIGH then the library will automaticall enable pull up resisters for the rotaty
	encoder's pins.
	
	@since v0.1;
**/
void SimpleRotary::setTrigger(byte i)
{
  _trigger = i;
  _setInputPins();
}


/**
	SET DEBOUNCE DELAY
	Sets the debounce delay time in milliseconds.
	
	This number shold be set as small as possible. Higher values can result in missing input pulses.
	Finding this value will vary from one rotary encoder to another and can be found simply with
	trial and error.
	
	You can turn this feature off by setting the delay value to 0.
	
	@since v0.1;
**/
void SimpleRotary::setDebounceDelay(int i)
{
  _debounceRDelay = i;
}


/**
	SET ERROR CORRECTION DELAY
	Sets the error correction delay delay time in milliseconds.
	
	Setting a higher number here will result in smoother direction values at the cost of being able to quickly change directions. For example, setting a value of 250ms will require a the user to pause 1/4 second between changing direction of the rotary encoder. It takes most users longer then 250ms to reorient their fingers to the new direction so in most cases this would be fine. If a user was to change direction of the rotary encoder faster then 1/4 of a second then the output value would be the previous direction. This number should be set to whatever delay works best for your application.
	
	You can turn this feature off by setting the delay value to 0.
	
	@since v0.1;
**/
void SimpleRotary::setErrorDelay(int i)
{
  _errorDelay = i;
}


/**
	GET ROTARY DIRECTION
	Gets the direction the rotary encoder is turned.
	
	0x00 = Not turned.
	0x01 = Clockwise;
	0x02 = Counter-Clockwise
	
	@since v0.1;
**/
byte SimpleRotary::Right()
{
  byte _dir = 0x00;
  _updateTime();
  
  if( _currentTime >= ( _debounceRTime + _debounceRDelay ) ) {

    _statusA = ( digitalRead(_pinA) == _trigger ? true : false);
    _statusB = ( digitalRead(_pinB) == _trigger ? true : false);
	 
    if( !_statusA && _statusA_prev ){

      if ( _statusB != _statusA ) {
        _dir = 0x01;
      } 

      if ( _currentTime < (_errorTime + _errorDelay) ){
        _dir = _errorLast;
      } else {
		_errorLast = _dir;
	  }

      _errorTime = _currentTime;
	  
    }
    _statusA_prev = _statusA;
    _debounceRTime = _currentTime;
  }

  return _dir;

}

byte SimpleRotary::Left()
{
  byte _dir = 0x00;
  _updateTime();
  
  if( _currentTime >= ( _debounceRTime + _debounceRDelay ) ) {

    _statusA = ( digitalRead(_pinA) == _trigger ? true : false);
    _statusB = ( digitalRead(_pinB) == _trigger ? true : false);
	 
    if( !_statusA && _statusA_prev ){

      if ( _statusB == _statusA ) {
        _dir = 0x01;
      } 

      if ( _currentTime > (_errorTime + _errorDelay) ){
        _dir = _errorLast;
      } else {
		_errorLast = _dir;
	  }

      _errorTime = _currentTime;
	  
    }
    _statusA_prev = _statusA;
    _debounceRTime = _currentTime;
  }

  return _dir;

}

/**
	SET INPUT PINS
	Sets the input pins and pinmode based on the defined pins and _trigger value.
	
	@see setTrigger();
	
	@since v0.2;
**/
void SimpleRotary::_setInputPins(){
	if ( _trigger == HIGH ) {
		pinMode(_pinA, INPUT_PULLUP);
		pinMode(_pinB, INPUT_PULLUP);
		pinMode(_pinS, INPUT_PULLUP);
	} else {
		pinMode(_pinA, INPUT);
		pinMode(_pinB, INPUT);
		pinMode(_pinS, INPUT);
	}
}


/**
	UPDATE THE TIME
	Updates the _currentTime value to the current time in milliseconds.
	
	@since v0.1;
**/
void SimpleRotary::_updateTime()
{
  _currentTime = millis();
}

That's not what I would call a "simple"-Rotary-code.

The code you have posted does not include a setup() and a loop()-function

Does your rotary-econder have mechanical switches?
If yes - all those "simple"-rotary-codes work unreliable with bouncing mechanical switches.

I recommend using the NewEncoder-library from user gfvavlo

You can setup a min-value and a max-value for the counting
And you have two functions that return true one time for each change in clockwise / counterclockwise step

best regards Stefan

By far the best rotary encoder technique I've seen is the State Table approach described here: Buxtronix: Rotary encoders, done properly. It handles contact bounce automatically without delays, millis() timing, external capacitors, or other hacks.

You can take a look at my implementation of this technique here: GitHub - gfvalvo/NewEncoder: Rotary Encoder Library. Check out the upClick() and downClick() member functions.

@Stefan, no it doesnt, because I am discussing a library not a sketch. And Simple Rotary is the name of the original library, the code may not be simple but it allows for simple implementation inside the sketch. As far as I know my encoder is not the mechanical "momantary switch" type. Otherwise I would just pass the digitalread() straight into the input mapping in GUISlice. A library "inbetween" is needed to convert the encoder signal into either 1 or 0 for clockwise and the same for ccw. This particuar library that I am attemting to modify almost does that, it gives 0 for stationary, 1 for clockwise and 2 for ccw when .rotation is called. I am trying to modify it so I can call .left or .right to get a 0 or a 1 depending on if the encoder is turning or not. Then i can use this to mimic a raw pin event and feed it into the input mapping function in GUISlice.

@gfvalvo I will take a look shortly and let you know if it could work

@gfvalvo could you please provide an example of the ESP32 interrupt code that serial prints upclick() and downclick() inside void loop? It would provide me with the framework to implement your library in my project. I havent been able to figure it out yet.
Thanks in advance :slight_smile:

I don't have such a code to post. upclick() and downclick() deliver a true only once on the first call
a second, third, etc. call delivers false

until a real hardware change of the encoder has occurred

best regards Stefan

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