Using rotary encoders can be daunting for beginners: they need two GPIO pins and are commonly managed with interrupts - which in themselves are a whole different can of worms that even experienced programmers often fear to open. Then there is the tricky quadrature encoding scheme of the two pins (usually called A and B) which needs to be translated into rotary motion in either a clockwise or anti-clockwise direction. All in all, its "non-trivial".
Furthermore there are dozens of quite different circuits for hardware debouncing and various opinions about which is best...in short, using Encoders can be difficult. But it doesn't need to be that way...
NODE-PINK solves the problem with two simple flows, either of which is declared in a single line of code. They require no external hardware components and perform both the debouncing and the quadrature decoding.
The simple encoder (h4pEncoder
) then sends an H4PE_EVENT
with msg
of +1 every time the encoder clicks clockwise or -1 every time its clicks anti-clockwise, it's that simple.
The automatic encoder (h4pEncoderAuto
) is a much cleverer beast, but just as easy to use. When you declare it you give it:
- A minimum value (default = 1)
- A maximum value (default = 100)
- An increment amount (default = 1)
- A percentage starting position (default = 50% , i.e. "centered")
Then whenever the user turns it, it sends an H4PE_EVENT
with msg
of whatever the current value just changed to. Using the above defaults and clicking once clockwise you get an H4PE_EVENT
with msg
= 51. And that's pretty much all there is to encoders in NODE-PINK!
N.B. The user never has to know about or deal with the "half encoders" - they are included in the diagram for completeness and to aid understanding.
uint8_t pA
// pin A gpio numberuint8_t pB
// pin B gpio numberuint8_t
m // The mode as per Arduino'spinMode
: eitherINPUT
orINPUT_PULLUP
H4PM_SENSE
s // WhetherACTIVE_HIGH
orACTIVE_LOW
Manages a rotary encoder, including all necessary debouncing and decoding. You get an event msg
of -1 for every anti-clockwise click or +1 for clockwise.
It also takes a binary parameter sord
to tell it to operate in "Single" or "Dual" mode, which is explained below. The default is "Single" (and is probably best left that way!)
Pipeline:
Pin A npROTARYDECODE{pA,pB,sord}
-> npPUBLISHVALUE
Pin B npPASSTHRU{pA}
// simply "forwards" all transitions to the pinA handler
h4pEncoder(uint8_t pA,uint8_t pB,uint8_t m,H4PM_SENSE s,bool sord=true);
In dual mode H4PE_GPIO
events are sent on both the PinA and PinB pins: the user can decide which one to listen for. This maintains certain internal values accurately on PinB for purist purposes and may be leveraged in the future. Setting it to single is recommended, in which case you will only ever see H4PE_GPIO
events on PinA and never any events at all on PinB. This is why for example the internal field sigE ("sigma Events") will not be correct for PinB - it will remain at zero, as will the delta value between events, and others...
None of this should be noticed by the user, nor make any real difference: it is included for the sake of completeness and to prevent erroneous support issues such as "PinB internal statistics aren't getting updated!!!" and saves me having to reply: "Well don't look at them then!".
Think of this as an "absolute" encoder or an h4pEncoder
"on steroids". At creation time, you provide a minimum (vMin
) and a maximum (vMax
) value and an increment amount (vInc
). By default the h4pEncoderAuto
will be set to a starting value of the mid-point between vMin
and vMax
, but you can set it anywhere you like as a percentage (vSet
) of the valid range between vMin
and vMax
.
Each time it is clicked in the clockwise direction it will add vInc
to the current value, and subtract vInc
when turned anticlockwise.
It has an additional parameter (bool wrap
) which determines the action on reaching the "end-stops" of vMin
or vMax
. If wrap
is true
then the value "wraps round", ie < vMin
becomes vMax
and > vMax
becomes vMin
. If wrap
is false
then trying to rotate the value past the end-stop has no effect: it simply "stops dead" and can only be reversed.
Either vMin
or vMax
. may be negative: you could have vMin
= -273 and vMax
=-1
Pipeline:
Pin A npROTARYDECODE{pA,pB,true}
->npLIMITER{this,vMin,vMax,vInc,vSet,wrap}
->npVALUEDIFF
->npPUBLISHVALUE
Pin B npPASSTHRU{pA}
// simply "forwards" all transitions to the pinA handler
h4pEncoderAuto(uint8_t pA,uint8_t pB,uint8_t m,H4PM_SENSE s,int vMin=0,int vMax=100,int vInc=1,int _vSet=50,bool wrap=false);
h4pEncoderAuto
Also provides the following methods.
void center(); // set value to 50% = (vmax - vmin) / 2
int getValue(); // return current value
void setPercent(int v) // set value to arbitrary percentage position between vmin and vmax
void setToMax(); // force to vmax ("fully clockwise")
void setToMin(){ // force to vmin ("fully clockwise")
void setValue(int v)// set arbitrary value: will be constrained such that vmin < v < vmax
- GPIO/NODE-PINK: a logical approach
- GPIO/NODE-PINK: basic flows
- 🏗️ GPIO/NODE-PINK: analog flows
- 🏗️ GPIO/NODE-PINK: advanced techniques
(c) 2021 Phil Bowles [email protected]