The rotary portion of the logic seems to work fine, but the responsiveness of the button has been rather frustrating. It often takes 3/4/5 clicks of the button before it responds. It happens with short, medium, and long clicks. I created a bare sketch with only the logic for the encoder to eliminate interference from the main app I’ve written, but the problem still occurs. I added an LED that flashes whenever the button is recognized to make it easy to tell when the button has been acknowledged.
I’m using pins 25 & 26 for the rotary, and 27 for the button. The “FLOATING” option is used with setEncoderType as the rotary encoder is a bare encoder.
Any suggestions?
/**
* ESP32RotaryEncoder: BasicRotaryEncoder.ino
*
* This is a basic example of how to instantiate a single Rotary Encoder.
*
* Turning the knob will increment/decrement a value between 1 and 10 and
* print it to the serial console.
*
* Pressing the button will output "boop!" to the serial console.
*
* Created 3 October 2023
* Updated 1 November 2023
* By Matthew Clark
*/
#include <ESP32RotaryEncoder.h>
// Change these to the actual pin numbers that
// you've connected your rotary encoder to
const uint8_t DI_ENCODER_A = 26;
const uint8_t DI_ENCODER_B = 25;
const int8_t DI_ENCODER_SW = 27;
// const int8_t DO_ENCODER_VCC = 13;
RotaryEncoder rotaryEncoder(DI_ENCODER_A, DI_ENCODER_B, DI_ENCODER_SW);
void knobCallback(long value) {
Serial.printf("Value: %ld\n", value);
}
void buttonCallback(unsigned long duration) {
Serial.printf("boop! button was down for %lu ms\n", duration);
}
void setup() {
Serial.begin(115200);
// This tells the library that the encoder has its own pull-up resistors
rotaryEncoder.setEncoderType(EncoderType::HAS_PULLUP);
// Range of values to be returned by the encoder: minimum is 1, maximum is 10
// The third argument specifies whether turning past the minimum/maximum will
// wrap around to the other side:
// - true = turn past 10, wrap to 1; turn past 1, wrap to 10
// - false = turn past 10, stay on 10; turn past 1, stay on 1
rotaryEncoder.setBoundaries(1, 10, true);
// The function specified here will be called every time the knob is turned
// and the current value will be passed to it
rotaryEncoder.onTurned(&knobCallback);
// The function specified here will be called every time the button is pushed and
// the duration (in milliseconds) that the button was down will be passed to it
rotaryEncoder.onPressed(&buttonCallback);
// This is where the inputs are configured and the interrupts get attached
rotaryEncoder.begin();
}
void loop() {
// Your stuff here
}
It’s the “BasicRotaryEncoder” example. Didn’t see the need to post it, but it’s included now.
I even added this piece of code so that there would be absolutely nothing else being executed while it was waiting. Still experienced the same problem.
Just gave the “EncoderButton” a whirl using the “Basic” example included with the library. It acknowledges the button being pressed, but not the rotary being turned. If this is using “interrupts”, I’m curious why it requires the “eb1.update()” function to be executed to obtain the values from the encoder. From several articles that I’ve read about the ESP32, pins 25/26/27 support interrupts.
/**
* A basic example of using the EncoderButton library.
*/
#include <EncoderButton.h>
/**
* Instatiate an EncoderButton.
* For Arduino Uno, the hardware interrupts are pins 2 & 3
* For Teensy, you can use any digital pin.
* Probably better to pick a more meaningful name than 'eb1'...
* Encoder+button:
* EncoderButton(byte encoderPin1, byte encoderPin2, byte switchPin);
* Encoder only:
* EncoderButton(byte encoderPin1, byte encoderPin2);
* Button only:
* EncoderButton(byte switchPin);
*/
EncoderButton eb1(26, 25, 27);
/**
* A function to handle the 'clicked' event
* Can be called anything but requires EncoderButton&
* as its only parameter.
* I tend to prefix with 'on' and suffix with the
* event type.
*/
void onEb1Clicked(EncoderButton& eb) {
Serial.print("eb1 clickCount: ");
Serial.println(eb.clickCount());
}
/**
* A function to handle the 'encoder' event
*/
void onEb1Encoder(EncoderButton& eb) {
Serial.print("eb1 incremented by: ");
Serial.println(eb.increment());
Serial.print("eb1 position is: ");
Serial.println(eb.position());
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
delay(500);
Serial.println("EncoderButton Basic Example");
//Link the event(s) to your function
eb1.setClickHandler(onEb1Clicked);
eb1.setEncoderHandler(onEb1Encoder);
}
void loop() {
// put your main code here, to run repeatedly:
// You must call update() for every defined EncoderButton.
// This will update the state of the encoder & button and
// fire the appropriat events.
eb1.update();
}
The encode in use is a “bare” unit without pullup resisters or a power source. When I used with with a Mega 2560 and interrupts, I set the pinMode to “INPUT_PULLUP” on all three pins. I wrote my own interrupt routines and everything worked flawlessly. It was a different story when I ported the same code to the ESP32. I’m guessing the speed of the ESP32 plays into it. This is why I switched to using the “ESP32RotaryEncoder.h” library as it’s written for the ESP32.
That “INPUT_PULLUP” was on the Mega2560 with no library being used. That logic is not in use on the ESP32. My apologies for not being more clear.
Using the “BasicRotaryEncoder” example included with the library produced the same issue. The left/right rotary logic works extremely well. The button often takes multiple clicks before it responds. Just strange that two out of three pins are being recognized with no problems.
I have two other ESP32 MCU’s I’m going to try to see if the issue is tied to the particular ESP32 I’m using.
Topic says ESP32. Sketch says ESP32. Error pointed out in first sketch (after a struggle). Second sketch introduced. Now it's not a ESP32. Moving target. Everything is a guessing game.
I merely explained what I’ve tried. I’m sorry if I’m not the best at getting my thoughts out into printed text. If something is not clear, please ask for clarification instead of beating someone down who is asking for help. I’ll will attempt to do better in future posts.
I switched from using a Hosyond ESP32-32S to a Lonely Binary ESP32-S3 with the “PinPulse” shield that has led lights attached to the pins. The frequency of the rotary button not being acknowledged is far lower with the ESP32-S3. The LED light on the shield allowed me to see that when the button is not acknowledged by the software, the LED light is going off and then back on, which tells me it’s not a mechanical issue with the button built into the rotary encoder. The shield will not be included in the final product, so I’ve added an LED on pin 48 that will flash when the rotary button is acknowledged by the software. That will save some frustration as certain button clicks do things like change the input increment from “10x” to “1x”, where nothing changes on the screen. At least I’ll see when the button click has been accepted.
I spent some time watching the LED’s on the shield when rotating the knob, and I never saw that miss a beat. Weird that the button function doesn’t work the same. The duration of the button click doesn’t matter. It misses long clicks just as much as the short clicks.
I finally took some time this afternoon to revisit the “rotary button” issue. While staring at the ISR routine that’s specified in the attachInterrupt statement (buttonISR), a thought occurred to me: What if the interrupt is being triggered while the ISR is being executed? The switches built into the rotary encoder can’t be of very high quality, and from past experience, dirty contacts can cause multiple “on/off” states with one press of the button.
I first experimented with using “noInterrupts()” and “interrupts()” in the ISR function with some sort of delay between them, but the ESP32-S3 rebooted when the button was pressed. It also doesn’t like a delay() statement in the ISR.
I then tried detaching and re-attaching the interrupt with a delay in between. Instead of the delay() statement, I incorporated a “while” statement using millis(), and “Bingo”, everything worked perfectly! I can click the button 10 times quickly, and the counter increments exactly 10.
I also thought that by detaching/reattaching the interrupt, it would clear out any buffer in case interrupts incorporate them in some fashion. Wow! This eliminates a lot of frustration!