Improved Arduino Rotary Encoder Reading

212,488

260

169

Introduction: Improved Arduino Rotary Encoder Reading

Rotary encoders are great input devices for electronics projects - hopefully this Instructable will inspire and help you use one in your next project.

Why write rotary encoder code?

I wanted to use a low cost rotary encoder as an input mechanism for one of my upcoming projects and was initially bewildered by the code options available to take readings from the rotary encoder and determine how many "detents" or cycles the encoder had clicked past and in what direction. I think my main sketch will need to use most of my Arduino's memory so I am avoiding the various available encoder libraries, which seemed to be difficult to make work when I tried a couple of them. They also appear to use far more of the code budget than the sketch-based code approaches discussed from here on.

If you just want to bypass the thinking behind my approach and get straight into the Instructable, feel free to skip ahead to Step 1!

Other Approaches

Several of the main sketch-based (i.e. they don't use a library) approaches are discussed in rt's blog post where they write rotary encoder code that makes the cheapest encoders usable as Arduino inputs. They also have a good example of they logic signal that the encoder produces. rt found that a timer interrupt system worked best for them but I'm concerned that the polling frequency would detract from screen update speed in the main loop of my project sketch. Given that the rotary encoder will be moving for a tiny proportion of the time I want the screen to be updating, this seems a poor match for my application.

I chose to start off using Steve Spence's code here, which was fine on it's own but appeared to really slow down when I incorporated the rest of my sketch code (which involves writing display updates to a small TFT screen). Initially I wondered if it could be because the main loop contains a debounce statement.

I then read Oleg's rotary encoder article on an interrupt service routine version of his previous post, I also thought it might be a good idea to use direct port manipulation to read both pins simultaneously and as soon as the interrupt fires. His code can be used on any input pin if the port manipulation code is rewritten. In contrast, I decided to use only the hardware interrupts on digital pins 2 and 3, so we can set interrupts to only fire on a rising edge of the pin voltage, rather than on pin voltage change, which includes falling edges. This reduces the number of times the ISR is called, distracting from the main loop.

Oleg's code uses a lookup table to reduce compiled code size to a really small size but I couldn't get reliable results which would catch very slow rotation as well as reasonably fast rotation. Bear in mind that hardware debouncing (see Step 2) can help a lot with reliability of readings but I was after a software solution to simplify the hardware build and be as portable to other hardware applications as possible.

This concludes the introduction of my challenge and considerations. In Step 2 we'll take a look at the encoder hardware, terminology and some practical considerations when you want to integrate a rotary encoder into your project.

Step 1: A Bit About Rotary Encoders

Why are rotary encoders so cool?

  1. Unlike a variable resistor/potentiometer they have infinite travel in any direction and because they produce a digital "Gray code" you can scale their readings to whatever range you like.
  2. The dual direction makes them useful for increasing or decreasing a value within a variable or navigating menus.
  3. Finally, many of these rotary encoders come with a centre push button, which can be used to select menu items, reset a counter or do anything you can think of that might suit a momentary push button.

Terms

  1. PPR: pulses per rotation - typically 12, 20 or 24. You might also see specifications for maximum rotation in rpm etc. This is probably determined by the encoder's propensity to "bounce" contacts - see below.
  2. Detent: the little click of the action as it springs to a natural rest point between pulses. There may be one detent per pulse/cycle (not equal to a rotation of the shaft) or two.
  3. Bounce: mechanical contacts inside the encoder literally bounce enough to jump off and back on a contact when rotating, potentially leading to too many readings attributed to that phase of the travel between detents.
  4. Debounce: This can be either done in hardware, perhaps with a low value ceramic capacitor between each pin and Ground, or in software, perhaps with a delay. In either case, the aim is to create a system which ignores bouncing contacts.

Tips

  1. Look out for a threaded section near the base of the shaft and a matching nut if you want to mount your encoder in a panel or enclosure.
  2. Many knobs are available for rotary encoders, with the most easily available coming in 6mm diameter shafts.
  3. Pay attention to whether your encoder shaft uses a flat face or splines to achieve a proper fit with the knob.
  4. The body of the rotary encoder may also come with a raised pin/stub, intended to mate with a small indent/hole in your panel (probably hidden by your knob) and prevent your encoder from rotating when you turn the knob. You might find you want to remove this if you can create enough friction to prevent encoder body rotation using the mounting bolt to screw the encoder in the panel or enclosure.
  5. Make sure you find out where the detent state is for your encoder and adapt your code accordingly. My example uses an encoder whose pins are both disconnected from ground and are pulled high by their respective input pullup resistors. This drives my selection of a RISING interrupt. If both pins were connected to ground when at detent, they would need code which was looking for FALLING pin voltage.

Step 2: The Circuit

The circuit is so simple. You will need:



• An ATMEGA328P based Arduino, such as the Uno, Pro Mini or Nano.
• A mechanical (as opposed to optical) quadrature rotary encoder - this is the most common kind so don't worry too much if it isn't specified. eBay and Aliexpress listings will often mention Arduino in the description and this is a good indicator that one is suitable.
• Hook-up wire/jumper leads.
• Optional: a prototyping breadboard.


First of all, look for a collection of three pins on one side of the encoder. These are the three for measuring rotation with our code. If there are two pins together on another side, these are likely to be for the centre push button. We'll ignore these for now.

Out of the three pins together, the encoder ground pin is connected to Arduino ground pin. Either of the other two pins is connected to digital pin 2 and the remaining on is connected to digital pin 3. If your direction of rotation isn't the way you'd like, just swap the two non-ground pins over.

Pins 2 and 3 are important because on the ATMEGA328P-based Arduinos they are the only pins which have the ability to detect RISING and FALLING pin change interrupts. The MEGA 2560 boards etc. have other hardware interrupt pins which can do this.

Note: In the diagram the ground pin is one of the end pins. In reality, the ground pin is often the centre pin but this is not always the case so read the datasheet or test your encoder to find out which pin is ground.

Another note: ArneTR made a good comment about not having a separately wired connection to the logic positive voltage (e.g. 5V or 3.3V) for the rotary encoder circuit shown. The Arduino can't read the rotary encoder without both a ground signal (which we have connected a wire to) and the logic voltage (sometimes annotated as Vcc or Vdd), so how can the Arduino read the logic from this encoder without a positive voltage wire? The answer is that the ATMEGA328P chip in the Arduino has a special mode you can set on the digital pins (which we are using) where a pin is automatically pulled "high" to the logic voltage by an internal resistor. Look in the code for "pinMode(pinX, INPUT_PULLUP)" to see us telling the Arduino that we want to take advantage of this mode. Once set, we only need to provide the encoder with a ground wire as the sensing wires from the digital pins are already providing the logic voltage.

ONE MORE THING... Githyuk found that a particular branded encoder didn't work with this way of doing things (ie the code below). Please see the comments section for details but in general, trying a different encoder would be a good debugging step when you have exhausted the easier/faster/cheaper steps.

Step 3: The Code

If you are not familiar with programming Arduinos, please get up to speed with this resource from Arduino themselves.

This code is free for your use (as in no cost and to be modified as you please), please attribute where you should.


/*******Interrupt-based Rotary Encoder Sketch*******
by Simon Merrett, based on insight from Oleg Mazurov, Nick Gammon, rt, Steve Spence
*/

static int pinA = 2; // Our first hardware interrupt pin is digital pin 2
static int pinB = 3; // Our second hardware interrupt pin is digital pin 3
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile byte encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile byte oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent

void setup() {
pinMode(pinA, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
pinMode(pinB, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
attachInterrupt(0,PinA,RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
attachInterrupt(1,PinB,RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
Serial.begin(115200); // start the serial monitor link
}

void PinA(){
cli(); //stop interrupts happening before we read pin values
reading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values
if(reading == B00001100 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos --; //decrement the encoder's position count
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
}
else if (reading == B00000100) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
sei(); //restart interrupts
}

void PinB(){
cli(); //stop interrupts happening before we read pin values
reading = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values
if (reading == B00001100 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos ++; //increment the encoder's position count
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
}
else if (reading == B00001000) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
sei(); //restart interrupts
}

void loop(){
if(oldEncPos != encoderPos) {
Serial.println(encoderPos);
oldEncPos = encoderPos;
}
}

That's it!

Step 4: Conclusion

I hope you find this code useful for your next project which uses a rotary encoder or that it has inspired you to consider a rotary encoder as an input for your next project.

Summary of the Aims

I have tried to write some code which achieves a good balance of:

  • Portability (port manipulation code is the compromise when moving to other chips)
  • Speed (port manipulation really helps)
  • Low compiled code size (port manipulation and bitmath helps)
  • Reliably records slow and fast manual rotation
  • Reduced nugatory interrupt service routine calls (using RISING interrupt and temporarily disabling interrupts)

Caveats and Ideas for Improvement

This code isn't perfect by any means and you might like to change it to use other pins. I tested this code with the sketch which was causing the most delay and least reliable readings with the other approaches discussed - I certainly haven't compared it with timers to see whose code produces fewer nugatory interrupt service routines, takes the least time to execute or filters out the highest percentage of contact bounces. Perhaps someone might like to do a benchmark test against the other approaches out there.

3 People Made This Project!

Recommendations

  • Teach With Tinkercad Contest

    Teach With Tinkercad Contest
  • Back to School: Student Design Challenge

    Back to School: Student Design Challenge
  • Halloween Contest

    Halloween Contest

169 Comments

0
Dr_McFish
Dr_McFish

4 weeks ago

Thanks so much, man! This code works so well compared to other stuff out there online, that's either annoyingly unreliable or simply doesn't work

0
gastonrubiolo1986
gastonrubiolo1986

4 months ago

Hello to all, I´d love to share 2 things I´ve manage to do using the great code of Simon...

First of all, I´ll put the code that works on ATMEGA2560, understanding the pins changes was not easy for me since there were not in order like ARDUINO UNO... But a good friend of mine helps me to understand how to use the B00110000 and the PINE & 0x30 instead of the ones used by Simon.

Here is the CODE for using in ARDUINO MEGA with one encoder in pin 2 and 3:

/*******Interrupt-based Rotary Encoder Menu Sketch*******
* by Simon Merrett, based on insight from Oleg Mazurov, Nick Gammon, rt and Steve Spence, and code from Nick Gammon
* To use in ATMEGA2560 (arduino MEGA), changes in code by Gastón Rubiolo, great help in mapping pins from ONE to MEGA from Lucio Campanini
*/
static int pinA = 2; // Our first hardware interrupt pin is digital pin 2
static int pinB = 3; // Our second hardware interrupt pin is digital pin 3
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile byte encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile byte oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent
void setup() {
pinMode(pinA, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
pinMode(pinB, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
attachInterrupt(0,PinA,RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
attachInterrupt(1,PinB,RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
Serial.begin(9600); // start the serial monitor link
}
void PinA(){
cli(); //stop interrupts happening before we read pin values
reading = PINE & 0x30; // read all eight pin values then strip away all but pinA and pinB's values
if(reading == B00110000 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos --; //decrement the encoder's position count
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
}
else if (reading == B00010000) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void PinB(){
cli(); //stop interrupts happening before we read pin values
reading = PINE & 0x30; //read all eight pin values then strip away all but pinA and pinB's values
if (reading == B00110000 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos ++; //increment the encoder's position count
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
}
else if (reading == B00100000) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void loop(){
if(oldEncPos != encoderPos) {
Serial.println(encoderPos);
oldEncPos = encoderPos;
}
}

In adition I´d love to share the code I´ve write base on Simon´s, but using 2 encoders in Arduino MEGA, the same conection but adding a new encoder in pins 18 and 19 (interrupts 5 and 4)... I leave the code, I hope you find usefull in projects with two encoders instead of one!!!

static int pinA = 2; // Our first hardware interrupt pin is digital pin 2
static int pinB = 3; // Our second hardware interrupt pin is digital pin 3
static int pinC = 18; // Our first hardware interrupt pin is digital pin 2
static int pinD = 19; // Our second hardware interrupt pin is digital pin 3
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile byte cFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte dFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile byte encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile byte oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent
volatile byte encoderPos2 = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile byte oldEncPos2 = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading2 = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent
void setup() {
pinMode(pinA, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
pinMode(pinB, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
pinMode(pinC, INPUT_PULLUP); // set pinC as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
pinMode(pinD, INPUT_PULLUP); // set pinD as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
attachInterrupt(0,PinA,RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
attachInterrupt(1,PinB,RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
attachInterrupt(5,PinC,RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
attachInterrupt(4,PinD,RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
Serial.begin(9600); // start the serial monitor link
}
void PinA(){
cli(); //stop interrupts happening before we read pin values
reading = PINE & 0x30; // read all eight pin values then strip away all but pinA and pinB's values
if(reading == B00110000 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos --; //decrement the encoder's position count
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
}
else if (reading == B00010000) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void PinB(){
cli(); //stop interrupts happening before we read pin values
reading = PINE & 0x30; //read all eight pin values then strip away all but pinA and pinB's values
if (reading == B00110000 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos ++; //increment the encoder's position count
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
}
else if (reading == B00100000) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void PinC(){
cli(); //stop interrupts happening before we read pin values
reading2 = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values
if(reading2 == B00001100 && cFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos2 --; //decrement the encoder's position count
dFlag = 0; //reset flags for the next turn
cFlag = 0; //reset flags for the next turn
}
else if (reading2 == B00000100) dFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void PinD(){
cli(); //stop interrupts happening before we read pin values
reading2 = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values
if (reading2 == B00001100 && dFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos2 ++; //increment the encoder's position count
dFlag = 0; //reset flags for the next turn
cFlag = 0; //reset flags for the next turn
}
else if (reading2 == B00001000) cFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void loop(){
if(oldEncPos != encoderPos) {
Serial.print("Encoder 1: ");
Serial.println(encoderPos);
oldEncPos = encoderPos;
};
if(oldEncPos2 != encoderPos2) {
Serial.print("Encoder 2 (x10): ");
Serial.println(10*encoderPos2); //to make easier to know which encoder I´m using I´ve make it move from 10 to 10
oldEncPos2 = encoderPos2;
}
}


Thanks for reading, I hope somebody can get some use of it !!!

2encoder.jpeg
0
SimonM83
SimonM83

Reply 4 months ago

gastonrubiolo1986 thanks for posting. Your previous comments seemed to disappear for me, so I couldn't reply to them. Well done for getting it working!

0
gastonrubiolo1986
gastonrubiolo1986

Reply 4 months ago

Dear Simon, the first comments were not usefull, I hope this last comment could be of help for somebody who needs to use encoder in ATMEGA 2650. A big hug, thanks for your sharing... I´ve made one program for 2 encoders in ATMEGA 2650 because was something I need for my project, if somebody want that I´ll have no problem in sharing... that´s the hole idea of ARDUINO I think...

0
Robfm
Robfm

Question 11 months ago

Hi I am very new to Arduino and found your article very helpful especially the code. As yet I haven't tried it as I am waiting my encoder. I have uploaded the code to my board which it accepted without a hitch however it is a Leonardo board will it actually work. I noted the comments about the types of boards and the input pull up. Does Leonardo have this function and if so will I need to change pins.

Thanks in advance.

0
Robfm
Robfm

Answer 11 months ago

I found this seems to suggest it will work. What is your view Simon:
Input and Output

Each of the 20 digital i/o pins on the Arduino Leonardo can be used
as an input or output, using pinMode(), digitalWrite(), and
digitalRead() functions. They operate at 5 volts. Each pin can provide
or receive a maximum of 40 mA and has an internal pull-up resistor
(disconnected by default) of 20-50 kOhms.

In addition, some pins have specialized functions:

0
SimonM83
SimonM83

Reply 5 months ago

Hi Robfm, you have probably already worked this out but the choice to favour performance over portability (now not necessarily as big a choice you need to make as discussed in other comments) means the direct port reads will need adapting (at the very least) to the ATMEGA32U4 chip on the Leonardo boards. You should be able to work out the changes needed from comparing the datasheets for the ATMEGA32U4 and the ATMEGA328P I used in this example.

0
DavidP684
DavidP684

Question 5 months ago

Hi,
I notice that the encoder I am using requires two 'clicks' in each direction to register a change on the serial monitor. Is this by design and can it change to one?

BTW, best instructable and code that I have ever seen on this site.

0
SimonM83
SimonM83

Answer 5 months ago

Hi David, if you can give more details (photo link/ part number) of your encoder it would help. Sometimes they are designed with a different number of detents - if you look through the comments I think at least one other person has had the same observations and managed to get it sorted so that would be worth skimming through the comments to try and see what they did.

0
Grant_FM
Grant_FM

Question 1 year ago on Step 3

This worked perfectly... but how do I add a button click from the encoder?

1
DrN8PhD
DrN8PhD

1 year ago on Step 4

Side note: I now adapted this for the SAMD51 chips...and it's really a piece of cake. Key is to make the variables 32 bit instead of 8.

volatile uint32_t reading = 0; // somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent
uint32_t maskA;
uint32_t maskB;
uint32_t maskAB;
volatile uint32_t *port;
bool approve;

void setup() {
Serial.begin(9600);
pinMode(pinA, INPUT_PULLUP);
pinMode(pinB, INPUT_PULLUP);
attachInterrupt(pinA, fPinA, RISING);
attachInterrupt(pinB, fPinB, RISING);
maskA = digitalPinToBitMask(pinA);
maskB = digitalPinToBitMask(pinB);
maskAB = maskA | maskB;
port = portInputRegister(digitalPinToPort(pinA));
}

void fPinA() {
noInterrupts();
reading = *port & maskAB;
if((reading == maskAB) && aFlag) {
encoderPos --;
approve = !approve;
bFlag = 0;
aFlag = 0;
} else if (reading == maskB) bFlag = 1;
interrupts();

void fPinB() {
noInterrupts();
reading = *port & maskAB;
if (reading == maskAB && bFlag) {
encoderPos ++;
approve = !approve;
bFlag = 0;
aFlag = 0;
} else if (reading == maskA) aFlag = 1;
interrupts();

You can ignore the bool, that is for my code specifically. Thanks again to you wonderful people that made this happen! SimonM83 and hahrg, y'all are a dang treasure!
0
SimonM83
SimonM83

Reply 1 year ago

Thanks for posting your ported code and the kind words.

0
DrN8PhD
DrN8PhD

Question 1 year ago

Has anyone been able to adapt this for ESP32 or ESP32s2 chips? I really love how well this works in my menus outside of the void loops, which was a problem for me with the encoder libraries. But I am struggling to get this work with the fancy new-ish chip.

0
DrN8PhD
DrN8PhD

Reply 1 year ago

I did, but I was having trouble adapting to the pinout for the Metro ESP32-s2 pinout I have only my Adafruit board. I could not for the life of me get it to work by using the assigned PIN numbers printed on the board -- so I figured I would ask you before I went down the old, "brute force" method of subbing numbers until it worked.

0
DrN8PhD
DrN8PhD

Reply 1 year ago

Well, that'll do it! I'll try that. I have no idea how I missed that -- thanks! It's hell spending all these years on the AVR chips, getting used to the nuances....and then having to forget it all. (c:

0
evanlw85
evanlw85

Question 1 year ago on Step 4

Hi SimonM83,
I followed an instructional video on youtube for creating a buttonbox using an arduino leonardo. Here is a link to the tutorial:
I'm a complete arduino noob, but I can follow instructions.

It's all set up, but I find that the rotary encoder I'm using only registers once every 2 clicks.
(this is the datasheet: https://www.jaycar.com.au/medias/sys_master/images...

It says it has 15 pulses per 360 degrees. But with 30 Detents.
Does this simply mean that I have to ignore every second click? or is there a way to register that missing click via code?