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.
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
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.
@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