madhephaestus / esp32encoder Goto Github PK
View Code? Open in Web Editor NEWA Quadrature and half quadrature PCNT peripheral driven encoder library supporting 8 encoders
License: Other
A Quadrature and half quadrature PCNT peripheral driven encoder library supporting 8 encoders
License: Other
Hi,
thank's for your library! Works fine with PlatformIO Arduino 1.0.6!
Switching to 2.0.1-RC1 i get a compiler error, PNCT undefined.
Fix:
Insert this line to ESP32Encoder.cpp:
#include <soc/pcnt_struct.h>
Now i can compile both 1.0.6 and 2.0.1-RC
Best
Dirk
Hi
Trying out your library with a pretty simple code and the very common KEYES Rotary Encoder with a switch (KY-040) with a basic "ESP 32 Dev Module"
here is the test code:
#include <ESP32Encoder.h>
ESP32Encoder encoder;
const uint8_t encoderCLKPin = 13; // encoder CLK
const uint8_t encoderDTPin = 14; // encoder DT
int32_t encoderCntr = 0L;
void setup() {
Serial.begin(115200);
ESP32Encoder::useInternalWeakPullResistors = true; // Enable the weak pull down resistors
encoder.clearCount();
encoder.attachHalfQuad(encoderDTPin,encoderCLKPin); // Attach pins for use as encoder pins
}
void loop()
{
int32_t currentEncoderCntr = encoder.getCount();
if (currentEncoderCntr != encoderCntr) {
if (currentEncoderCntr > encoderCntr) {
Serial.print(F("tick + = "));
} else {
Serial.print(F("tick - = "));
}
encoderCntr = currentEncoderCntr;
Serial.println(currentEncoderCntr);
}
}
I occasionally get tick+ or tick- that is unwanted - that's probably because my module is a poor quality $0.50 component bought from the Far East
but I also get at boot the following error message:
E (33) pcnt: pcnt_counter_clear(158): PCNT UNIT ERROR
I'm curious what this error is and how to get rid of it ?
I am using external pullup resistors. so hardcoded internal pulldown is not wise.
can you make it optional pulldown/pullup/normal
I get this message after I updated the library and trying to read the encoder:
E (21084) pcnt: _pcnt_get_counter_value(98): PCNT driver error
Hi Kevin, sorry to bother you.
I'm just an hobbyist, trying to understand how to control position of an N20 Micro Encoder Gear Motor
Can you help me understand if I can use your library and how?
I made a simple arduino code (just googling around and copying some code), but I have very unstable result. As of now I don't understand if it's a software or an hardware problem :/
Thanks a lot in advance, Luca (from Italy)
I have been using the library to read my full quadrature encoders with very good results.
My current project is a custom closed loop stepper motion control. The esp32 will receive step/dir inputs for two motors, output to the drivers, and monitor the two related encoders for closed loop control. There are other tasks and features the system will be performing that are above what a stock closed loop controller would provide.
Since I am only reading two encoders, I thought maybe a tweak to the library for a new encoder mode would make it possible to also read step and direction inputs?
Thanks.
I use you library for read encoder, and library FastAccelStepper for step drivers, but they are not compatible.
Author library FastAccelStepper, wrote this:
Solution: ESP32Encoder author should not take over the PCNT-module interrupt. Espressif offers a cooperative solution: use pcnt_isr_service_install() and with pcnt_isr_handler_add() routines ca be registered to manage one channel's interrupt request.
Hi !
First thanks for your work !
I'd like to use your library interfacing it with analog/digital inputs of ADS1115, due to I have not more pins available on ESP32
Is it possibile in any way ?
many thanks
peppe
I'm going to use it on a very fast motor (12000rpm) and the encoder has a 12 bit resolution, therefore I will have an interrupt in about 1us. Do you think it will be possible to run a 500 lines of code with your library?
MADHELPHAESTUS Answer:
this lib doesn't use pin change interrupts, it uses the PCNT module which is a hardware counting peripheral, so all you need to be concerned with is that the runt pulse filter is short enough so as not to filter your counts as runts.
https://github.com/madhephaestus/ESP32Encoder/blob/master/src/ESP32Encoder.cpp#L154
250 there means 250 clock cycles must pass between edge transitions to be detected as a count pulse. Do the math based on the RPM to get your ticks per second, then compute the smallest seconds per tick, and compute how many clock cycles at 240mhz that is. If your number is less that 50% more than the runt time, then it will work. If it comes in less than that number, you can try to change that line of code in your copy of the library to see if you can make it work.
Also as a note, there are interrupts, but they only fire when a 16bit count buffer overflows.
Short answer, its unlikely that you have an encoder fast enough to cause the PCNT module issues, but it does have known limits.
Hi, Nice work!
I want use this in my speedo project, is it possible to attach a single pin encoder? I only need one direction.
I didn't find related ways to do it in document.
Thanks!
Still trying to track this down, but I seem to be getting random (xQueueGenericReceive)- assert failed! errors when I am spinning my rotary dial. Works for a lot of clicks then dies. Here is the error. I do not, as of yet, have a way to reproduce in a consistent way.
/home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/queue.c:1446 (xQueueGenericReceive)- assert failed!
abort() was called at PC 0x4008880b on core 0
Backtrace: 0x4008c334:0x3ffbe030 0x4008c561:0x3ffbe050 0x4008880b:0x3ffbe070 0x400d45b9:0x3ffbe0b0 0x400d3761:0x3ffbe0d0 0x400d39ae:0x3ffbe0f0 0x40080ee1:0x3ffbe190 0x40080fdd:0x3ffbe1b0 0x40084ebd:0x3ffbe1d0 0x4000bfed:0x3ffbd090 0x40089c15:0x3ffbd0a0 0x40089231:0x3ffbd0c0 0x40089295:0x3ffbd0e0 0x4008afca:0x3ffbd100 0x4008b0ab:0x3ffbd140 0x40088ae5:0x3ffbd170
Rebooting...
COMMIT:
git log | head
commit a6f47f8
Merge: 3752002 aa647e5
Author: Kevin Harrington [email protected]
Date: Thu Jul 23 18:05:09 2020 -0700
Merge pull request #26 from thijstriemstra/patch-2
fix typos
SERIAL MONITOR:
�⸮$⸮⸮⸮⸮⸮⸮⸮��b�9�⸮ : 0x00000000 LCOUNT : 0x00000000
Backtrace: 0x400e8db4:0x3ffb1ee0 0x400d1068:0x3ffb1f10 0x400d1156:0x3ffb1f40 0x400d0da9:0x3ffb1f60 0x400d1a53:0x3ffb1fb0 0x40088299:0x3ffb1fd0
Rebooting...
ets Jun 8 2016 00:22:57
rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 188777542, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1044
load:0x40078000,len:8896
load:0x40080400,len:5816
entry 0x400806ac
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e8db0: ffffffff ffffffff ffffffff
Core 1 register dump:
PC : 0x400e8db4 PS : 0x00060f30 A0 : 0x800d106b A1 : 0x3ffb1ee0
A2 : 0x00000011 A3 : 0x00000000 A4 : 0x00000000 A5 : 0x3ffc0170
A6 : 0x00000013 A7 : 0x00000000 A8 : 0x800e7ae9 A9 : 0x00000001
A10 : 0x00000011 A11 : 0x3ffb8058 A12 : 0x00000001 A13 : 0x00000001
A14 : 0x00060320 A15 : 0x00000000 SAR : 0x00000010 EXCCAUSE: 0x00000000
EXCVADDR: 0x00000000 LBEG : 0x00000000 LEND : 0x00000000 LCOUNT : 0x00000000
Backtrace: 0x400e8db4:0x3ffb1ee0 0x400d1068:0x3ffb1f10 0x400d1156:0x3ffb1f40 0x400d0da9:0x3ffb1f60 0x400d1a53:0x3ffb1fb0 0x40088299:0x3ffb1fd0
[...]
Many thanks for this excellent library! The examples worked immediately with no effort, the code is clear and well documented and I learned quite a bit reading it.
My only question is regarding the if statement in the attach method. That check seems to be undoing everything that was done from lines 110 to just before the if statement. There's also an else
which similarly undoes everything before it.
What's even stranger is that there's a ternary operator a few lines above that accomplishes the same thing as the if/else
.
I am not an embedded engineer but I have been writing software for a number of years. I am missing something?
hi !
finally the first library without bouncing problems !!!
will you also implement an internal routine that allows you to recognize the switch without having to build something external (digital state of any pin)?
something like : encoder.switchState()
Hi Madhephaestus,
I'm trying to build a event driven / interupt driven piece of code, I love your library and would like an example of how to get your code to fire on interupt , the only example uses the loop() method and that's not going to work for me.
Thanks
John
please add rotary encoder acceleration (fast count) support when rotating the encoder fastly.
Hi,
I'm trying to understand what type of rotary encoder I have and what the differance is between the attach methods. I'm using https://wiki.keyestudio.com/Ks0013_keyestudio_Rotary_Encoder_Module and it works with your library , but is best method attachFullQuad or attachHalfQuad?
Thanks
John
Can you add a section to the README about connecting encoders to the ESP32?
I see in the code:
gpio_pulldown_en(aPinNumber);
gpio_pulldown_en(bPinNumber);
So, I assume I need to connect common to +V.
However, a wee note in README and/or the example would be helpful to others.
Thanks for the nice library!
Is it possible to count on falling edge instead of rising?
r_enc_config.pos_mode = fullQuad ? PCNT_COUNT_DEC : PCNT_COUNT_DIS; //Count Only On Rising-Edges
Hello,
do you have an example how to implement the push button functionality on encoders like KY-040?
Thank you,
Andrej
Hello, thanks for this great library. Hard to find an encoder library that uses interrupts or the PCNT hardware peripheral, and also supports registering a callback function.
However, there's a bug here (or at least a documentation bug) regarding the pointer passed to the callback function.
Per the docs:
https://github.com/madhephaestus/ESP32Encoder/blob/master/src/ESP32Encoder.h#L32
"@param enc_isr_cb callback executed on every encoder ISR, gets a pointer to the ESP32Encoder instance as an argument"
Per the example:
https://github.com/madhephaestus/ESP32Encoder/blob/master/examples/Encoder_interrupt_display/Encoder_interrupt_display.ino#L8
The ISR here casts the void* v
to ESP32Encoder*
, but this is always a null pointer.
The instantiation of the ESP32Encoder class here:
https://github.com/madhephaestus/ESP32Encoder/blob/master/examples/Encoder_interrupt_display/Encoder_interrupt_display.ino#L18
ESP32Encoder encoder(true, enc_cb);
does not provide a third parameter here, hence the null pointer passed into the callback function.
We could fix this by changing this line:
https://github.com/madhephaestus/ESP32Encoder/blob/master/src/ESP32Encoder.cpp#L88
esp32enc->_enc_isr_cb(esp32enc->_enc_isr_cb_data);
to something like
esp32enc->_enc_isr_cb(esp32enc->_enc_isr_cb_data ? esp32enc->_enc_isr_cb_data : esp32enc);
where it could return the constructor-set _enc_isr_cb_data
if one was provided at construction time, otherwise it would return a pointer to the ESP32Encoder instance.
I can prepare a PR for this if you like. Thanks!
In the Readme, it is specified that the library supports upto 10 encoders simultaneously. But in the example sketch the encoder2 is paused before encoder1 is being read. Is this necessary?
Hi,
I want to change encoder direction. I know it is possible to change this in software(SETUP) but i want to change on the fly in the void loop by attaching the pins reversed "encoderX.attachHalfQuad(2, 3)" to encoderX.attachHalfQuad(3, 2) When a button is pressed. This works BUT
when you do this a couple times in the loop then it gives on the serial monitor this error "Too many encoders, FAIL!" So when this error occurs there is no possibility to change the encoder directions anymore.
Is there a possibility to erase the ATTACHED encoders or to reset the encoder attached count? a funtion like "encoder_detach"?
Thanks!
Hi,
I'm trying to have 9 encoders and the last one does'nt work.
There is my code :
#include <ESP32Encoder.h> // https://github.com/madhephaestus/ESP32Encoder/
#include <BleGamepad.h> // https://github.com/lemmingDev/ESP32-BLE-Gamepad
BleGamepad bleGamepad("SimRacing Wheel", "test", 100);
/*********************
* JOYSTICK SETTINGS *
*********************/
#define numOfButtons 40
#define numOfHatSwitches 0
#define enableX false
#define enableY false
#define enableZ false
#define enableRZ false
#define enableRX false
#define enableRY false
#define enableSlider1 false
#define enableSlider2 false
#define enableRudder false
#define enableThrottle false
#define enableAccelerator false
#define enableBrake false
#define enableSteering false
#define MAXENC 9
uint8_t uppPin[MAXENC] = {36,34,32,25,27,22,03,17,04};
uint8_t dwnPin[MAXENC] = {39,35,33,26,14,01,21,16,00};
uint8_t encoderUpp[MAXENC] = {17,19,21,23,25,27,29,33,31};
uint8_t encoderDwn[MAXENC] = {18,20,22,24,26,28,30,34,32};
ESP32Encoder encoder[MAXENC];
unsigned long holdoff[MAXENC] = {0,0,0,0,0,0,0,0,0};
int32_t prevenccntr[MAXENC] = {0,0,0,0,0,0,0,0,0};
bool prevprs[MAXENC] = {0,0,0,0,0,0,0,0,0};
#define HOLDOFFTIME 150 // TO PREVENT MULTIPLE ROTATE "CLICKS" WITH CHEAP ENCODERS WHEN ONLY ONE CLICK IS INTENDED
////////////////////////////////////////////////////////////////////////////////////////
void sendKey(uint8_t key) {
Serial.print("pulse\t");
Serial.println(key);
if(bleGamepad.isConnected()) {
bleGamepad.press(key);
delay(150);
bleGamepad.release(key);
}
}
void pressKey(uint8_t key) {
Serial.print("hold\t");
Serial.println(key);
if(bleGamepad.isConnected()) {
bleGamepad.press(key);
}
}
void releaseKey(uint8_t key) {
Serial.print("release\t");
Serial.println(key);
if(bleGamepad.isConnected()) {
bleGamepad.release(key);
}
}
////////////////////////////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(115200);
for (uint8_t i=0; i<MAXENC; i++) {
encoder[i].clearCount();
encoder[i].attachSingleEdge(dwnPin[i], uppPin[i]);
}
bleGamepad.setControllerType(CONTROLLER_TYPE_GAMEPAD); //CONTROLLER_TYPE_JOYSTICK, CONTROLLER_TYPE_GAMEPAD (DEFAULT), CONTROLLER_TYPE_MULTI_AXIS
bleGamepad.begin(numOfButtons,numOfHatSwitches,enableX,enableY,enableZ,enableRZ,enableRX,enableRY,enableSlider1,enableSlider2,enableRudder,enableThrottle,enableAccelerator,enableBrake,enableSteering);
Serial.println("Booted!");
}
////////////////////////////////////////////////////////////////////////////////////////
void loop() {
unsigned long now = millis();
// -- ROTARY ENCODERS : ROTATION -- //
for (uint8_t i=0; i<MAXENC; i++) {
int32_t cntr = encoder[i].getCount();
if (cntr!=prevenccntr[i]) {
if (!holdoff[i]) {
if (cntr>prevenccntr[i]) { sendKey(encoderUpp[i]); }
if (cntr<prevenccntr[i]) { sendKey(encoderDwn[i]); }
holdoff[i] = now;
if (holdoff[i]==0) holdoff[i] = 1; // SAFEGUARD WRAP AROUND OF millis() (WHICH IS TO 0) SINCE holdoff[i]==0 HAS A SPECIAL MEANING ABOVE
}
else if (now-holdoff[i] > HOLDOFFTIME) {
prevenccntr[i] = encoder[i].getCount();
holdoff[i] = 0;
}
}
}
delay(10);
}
The last defined encoder never work.
It's not a pin issue because if I change the order of my pin on uppPin
and dwnPin
, the encoder with the last pin does'nt work.
I've make lot of test and check lot of things but I don't see where is the problem.
Have you an idea please ?
Thank's
Hi, I use this library a lot and works quite well, only with mechanical encoders I still have issues with debouncing the inputs. I made a small change to the attach function to add a debounce value which is now fixed in the library. Maybe a valuable change.
// In the include file have debounce assigned default value to keep library compatibility
void attachHalfQuad(int aPintNumber, int bPinNumber, int debounce = 250);
void attachFullQuad(int aPintNumber, int bPinNumber, int debounce = 250);
void attachSingleEdge(int aPintNumber, int bPinNumber, int debounce = 250);
void ESP32Encoder::attach(int a, int b, enum encType et, int debounce)
in the attach function
// Filter out bounces and noise
pcnt_set_filter_value(unit, debounce); // Filter Runt Pulses pcnt_set_filter_value(unit, 250);
void ESP32Encoder::attachHalfQuad(int aPintNumber, int bPinNumber, int debounce) {
attach(aPintNumber, bPinNumber, half, debounce);
}
void ESP32Encoder::attachSingleEdge(int aPintNumber, int bPinNumber, int debounce) {
attach(aPintNumber, bPinNumber, single, debounce);
}
void ESP32Encoder::attachFullQuad(int aPintNumber, int bPinNumber, int debounce) {
attach(aPintNumber, bPinNumber, full, debounce);
}
At the top of my main.cpp, I have defined two ISR's:
static IRAM_ATTR void volume_encoder_cb(void* arg) {
volume_interrupt_status = true;
}
static IRAM_ATTR void respiration_encoder_cb(void* arg) {
respiration_interrupt_status = true;
}
Then I instatiate two ESP32Encoders:
ESP32Encoder volume_encoder(true, volume_encoder_cb);
ESP32Encoder respiration_encoder(true, respiration_encoder_cb);
Then, in setup(), I attached them:
volume_encoder.attachSingleEdge(23, 18);
respiration_encoder.attachSingleEdge(17, 16);
If I attach volume_encoder
first, its ISR fires and works great, but the respiration_encoder
ISR never fires.
If I attach respiration_encoder
first, its ISR fires and works great, but the volume_encoder
ISR never fires.
With massive help from @jkoz, I will submit a PR that fixes this issue.
I'm using some KY-040 similar encoders and have been having issues with not getting consistent values from getCount(). As I turn the knob, sometimes the count will jump by several values with a single 'click', sometimes the values will switch direction even though I'm still turning the same direction.
I noticed in README.md that I need to add "electrical debouncing in the range of 0.1 - 0.22 uf per encoder line to ground." I wasn't sure which lines needed the capacitor so I added 0.22 uf capacitors to all three (clk, dt, sw). Before I build a custom PCB for my project, it would be nice to know if all 3 are necessary or if there are other changes I should make.
I also read "to setFilter() to 1024 as well after attaching in order to get the maximum hardware debouncing." I noticed that when I did this, pcnt_set_filter_value() returned an 'out of range' error. Looking at the code, a comment says "filter_val is a 10-bit value, so the maximum filter_val should be limited to 1023." I switched to 1023 and the error went away. I suggest updating README.md to recommend the correct '1023' value.
After adding the capacitors and calling setFilter(1023) things are improved but I still occasionally don't get notifications for when I rotate the knob and non-sequential values from getCount(). It's usable now but disconcerting when things don't change in response to a clear 'click' when rotating the knobs. If there is more I can do to improve it, I'd love to hear any additional suggestions.
I noticed that you recently added an interrupt based encoder but I didn't see any docs or examples on how to use it. When I was using a Teensy, it had interrupt based encoders that worked flawlessly - is this a better option for me to be using? Any special requirements for using it: pins that are supported/not supported, etc?
Dear Sir,
You said that you have used pcnt interrupt to prevent the 16 bit counter from overflowing. In my project I want to fire an interrupt when a certain value is reached for example 20000. In the ISR I want to reset the encoder count and give an output. Is it possible?
Secondly I wanted to read the step direction signal from a position controller using your library. For that I used it in "single" mode. It worked but I was only able to do it accurately for frequency<40 kHz. Higher than that and the pulses were missed. On the other hand I was able to accurately count an encoder at a frequency>300 kHz in "quad" mode. What may be the reason for less frequency in "single" mode?
Hi, Great library, seems to work flawlessly on 2 channels, compiles but does nothing if I add a third. Is this a limitation on the library or is there something else I have to do for 3 or more encoders? I tried reading up on PCNT from link, and looking through the library, but can't see what resource difference is between Full or Half. I'm also struggling to see how to use channel 2. Is this magically done in the library ?
Can I have 10 FullQuad encoders, or does type limit max?
ESP32Encoder xchan; // pin21,22
ESP32Encoder ychan; //18,19
ESP32Encoder zchan; // 16,17
Then is setup
xchan.attachFullQuad(XApin,XBpin);
etc
It compiles without complaint if I use all 3 but only first 2 set work. this is true if I use xy, xz, yz
Any suggestions?
Thanks
Steve
Hello,
thank you for this library. Is there a way to set limits on the encoders?
A nice piece of Code THX, but how to use the interrupt procedure ?????
when using attachSingelEdge() I get erroneous count-up by wiggling the encoder shaft.
Using attachHalfQuad I get correct half step counts.
This is most likely due to the incorrect edge triggering of the ESP32. (but don't quote me on that)
To ignore singel count changes I wrote following code in my loop:
int32_t encoder_position = 0;
void loop() {
uint16_t enc_delta = abs(encoder.getCountRaw() - (encoder_position*2)) / 2;
if(encoder.getCountRaw() > (encoder_position*2)){
encoder_position += enc_delta;
}
else{
encoder_position -= enc_delta;
}
}
I'm working on a self balancing robot and it will have encoders on motors (two of them) and my choice is between a 13 and 500 cpr encoder and the motors will spin at a max of 20000rpm.
So I'm expecting up to 250000 pulses per motor to the ESP32. Can the library handle the 500cpr encoder or is that too fast?
Not really an issue but is it possible to output direction rather than count (getCount or getCountRaw)?
I am very new to programming, so bear with me if I don't know some terms or basic concepts.
I have a program to read 5 encoders and output some values over serial. It works great as-is, but I want to improve it. Functional code here
I am trying to rewrite parts to use arrays, as it is obviously more efficient and flexible (increase or decrease the number of encoders). My attempt at this is here, but it is not working.
I tried to figure out what happens on the ESP32, as it is clearly not running the second program successfully. I got this decoded stack trace: Decoding stack results 0x40086e70: vTaskSwitchContext at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/tasks.c line 2820
.
If there's something I am missing or a better way to achieve the same thing I am all ears.
I updated the ESP32 board definition and updated ESP32Encoder to the latest value. Now the encoder doesn't work right, showing 0 as a count or -1, but never counting up or down like I used to.
I've experimented with various pull-up and pull-down pin settings, and have had no improvement.
I'm using Pins 21 and 22 on an ESP32 Dev Module, with the center pin connected to ground. Similar to the setup used in this issue:
That confuses me slightly, as this issue seems to indicate that the built in pull is down not up: #7
Any ideas?
Is it possible using the current 0.3.6 library to switch the count direction without having to reverse the pin numbers using the attach function?
The scenario here is where an encoder is mounted 'reversed' depending on the application.
I currently have a flag that is set if the encoder is "inverted" and therefore I could address this on startup with the attach function but would prefer the ability to change it on the fly.
I used this library to create a DRO (Digital Readout ) for a lathe. It worked great, but I ran into one problem and wanted to share this in hopes of saving people debug time. It might be good to mention this in the library documentation.
I was using a 600 step rotary encoder in quad mode. This produced 2400 steps per revolution. As my lathe bed has about 36" of travel, I would have many revolutions of the encoder from one end to the other. I ran into the problem that as I was moving, my DRO would reset to zero. I finally tracked this down to the encoder output. The library uses a 32 bit signed integer and this can overflow (sort of). When the encoder count reaches the max positive or negative number it rolls to zero. I had to add a function in my code to count the roll overs so that I could keep my position accurate.
/The encoder will rollover to zero and continue in positive or negative direction
-> 0, 1, ... 32766, 0, 1, ...
-> 0, -1, ... -32767, 0, -1,...
Great library!
I've been using your library and I think it's the best way to handle encoders on an ESP32. Before I include your library in the public version of my project, can you put a license on this, so I'm sure that I can legally use it and what restrictions I have.
Thank you!
I've attached an KY040 rotary encoder to a knob that has a stop. (I've used printed gears on both the knob and the encoder.)
I want to track knob position with zero at the knob stop, but I can tell some skipping is occurring as the rotary encoder turns. To test the skip, I zero the counter at the stop, turn the knob a number of rotations, and turn it back to the stop. If all works right, the number should be 0 again. Here are some numbers I gathered in test, moving slow and fast, and changing encoder mode just to see if it would help:
at stop, middle value, back at stop.
half quad
0 620 -30 slowly
0 603 -90 fast
full quad
0 1204 -210 slowly
0 1222 -60 fast
single edge
0 1318 -141 slow (number jumped around)
0 1172 897 fast
Any ideas how I debug this? I don't think it's just speed, as the problem isn't significantly decreased at slow speeds. I don't have a scope to check the wire signals. The sketch is pretty simple, with one other interrupt that I'm careful with. No wifi or bluetooth being used.
I was reading the encoder count from an ISR running via 1Mhz timer and it was working fine but it seems since some refactoring I have done I get some very big jumps between values. I store the previous value and crash if it is few ticks more than the previous value. The jumps are sometimes in the wrong direction. I saw in another issue that you recommend using a task to read the value via getCount(). Since I need to make calculations on every encoder tick I wonder if you have any thoughts on how best to notify a task that the encoder has moved? This doesn't seem possible with the current public methods and I'm guessing maybe pcnt isn't the right approach for me?
Hello.
It would be great to have a min and a max function.
thank you.
Marc.
Thanks for a great library. My apologies in advance if these questions should be elsewhere.
My implementation involves two IR photo-interrupters reading 2 windows in a flange mounted on the chuck of a drill. The two windows provide sufficient accuracy as long as no counts are lost. An accurate neutral, for example, is important. The size of the photo-interrupters necessitate windows of order 10mm long in a circumference of order 160mm. When changing direction, the chuck can easily stop part way through a quadrature cycle - ie with a window over one or both detectors. Using the full quadrature implementation seems to work OK. A single count per window would be easier. But will a single edge implementation be accurate? Would it, for example, count on the way in as well as on the way out of the window if it stopped "inside". For example, if the single edge counts rising edges, it might count one direction on the way in but nothing on the way out since there would be no rising edge?
I've had some success with these photo-interrupters with pull down resistors. Do you recommend setting useInternalWeakPullResistors=DOWN; and not using any hardware pull downs?
Thanks in advance.
I am using an open collector NPN rotary encoder that will require pullups instead of pulldowns. I want to be sure that this library will work properly. I have two questions:
I see that I can set the "useInternalWeakPullResistors" to false, but how do I use the internal pullup resistor. It looks like if I set the pin to "INPUT_PULLUP" in my sketch, your attach method will override it. Will I need to use external pullups and forget trying to use the internal pullups or can I redefine the pin after initializing the encoder?
#define A_PIN 16
#define B_PIN 17
ESP32Encoder::useInternalWeakPullResistors=false;
encoder.attachFullQuad(A_PIN, B_PIN);
pinMode(A_PIN, INPUT_PULLUP);
pinMode(B_PIN, INPUT_PULLUP);
Since your code is based on a pulldown, will the pulse detection be reversed or will it work correctly using a pullups? I don't know if this has any ramifications.
It would be nice to be able to specify using internal pullup as an option.
Thanks,
I loaded the example onto a ESP32 DevKit V1 and it crashed. execution stalls at around line 132-133 of ESP32Encoder.cpp (I didn't debug any deeper than this level.)
This is the log:
Rebooting...
ets Jun 8 2016 00:22:57
rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:1044
load:0x40078000,len:8896
load:0x40080400,len:5816
entry 0x400806ac
Guru Meditation Error: Core 1 panic'ed (LoadProhibited). Exception was unhandled.
Core 1 register dump:
PC : 0x400e7e08 PS : 0x00060f30 A0 : 0x800d0ff5 A1 : 0x3ffb1ee0
A2 : 0x00000006 A3 : 0x00000001 A4 : 0x00000000 A5 : 0x3ffc015c
A6 : 0x0000000c A7 : 0x00000000 A8 : 0xffffffff A9 : 0xfffffffe
A10 : 0x00000000 A11 : 0x00000000 A12 : 0x40080e70 A13 : 0x00000000
A14 : 0x3ffc015c A15 : 0x00000000 SAR : 0x0000001c EXCCAUSE: 0x0000001c
EXCVADDR: 0xffffffff LBEG : 0x00000000 LEND : 0x00000000 LCOUNT : 0x00000000
Backtrace: 0x400e7e08:0x3ffb1ee0 0x400d0ff2:0x3ffb1f10 0x400d10f6:0x3ffb1f40 0x400d0da1:0x3ffb1f60 0x400d1423:0x3ffb1fb0 0x40088279:0x3ffb1fd0
With a bit of debug and tracing, I found that if I used almost any GPIO other than 6 (which is part of encoder 2 in your example), it compiles, loads and appears to run OK. I don't know what's magic about GPIO6, and obviously there's an easy work-around. But it may be a bug with other implications that I haven't seen.
Apologies if I have not followed the correct protocol - I am totally new to this.
Compiling this library on the arduino-esp32 framework for ESP32-S2 models fails, since a few variables have been renamed.
.pio/libdeps/esp32-s2_otg/ESP32Encoder/src/ESP32Encoder.cpp:50:27: error: 'volatile union pcnt_un_status_reg_t' has no member named 'h_lim_lat'
if(PCNT.status_unit[i].h_lim_lat){
^~~~~~~~~
.pio/libdeps/esp32-s2_otg/ESP32Encoder/src/ESP32Encoder.cpp:53:27: error: 'volatile union pcnt_un_status_reg_t' has no member named 'l_lim_lat'
if(PCNT.status_unit[i].l_lim_lat){
^~~~~~~~~
My current workaround is to use a preprocessor directive to globally rename the new variable name to the old one.
It would be however more elegant to directly fix this within this library by checking for the ESP32-S2 model and using the correct variable names when running on the ESP32-S2 platform.
Hi. It would be very helpful to allow setting custom filter value (preferably in seconds or microseconds) to deal with noisy encoders. Right now it seems to be fixed at 250 ticks of APB_CLK which is 3.125 uS. In my case I could use a thousand times larger one.
Does not compile on Arduino IDE 1.8.20 in Windows 11
However did compile without issues on IDE 1.8.13 on my Macbook Air
Errors on 1.8.20
C:\Users\Stefan\Documents\Arduino\libraries\TouchDRO_Interface-master\ESP32_Quad_Encoder.cpp: In function 'void pcnt_isr(void*)': C:\Users\Stefan\Documents\Arduino\libraries\TouchDRO_Interface-master\ESP32_Quad_Encoder.cpp:42:20: error: 'PCNT' was not declared in this scope int intr_status = PCNT.int_st.val; ^ At global scope: cc1plus.exe: warning: unrecognized command line option '-Wno-frame-address' exit status 1 Error compiling for board DOIT ESP32 DEVKIT V1.
When running the example the encoder counts twice for every rotation of the encoder.
Using encoder: https://www.sparkfun.com/products/15141
With attachHalfQuad
ex"
Encoder count = 8
Encoder count = 8
Encoder count = 8
Encoder count = 8
Encoder count = 8
Encoder count = 10
Encoder count = 10
Encoder count = 11 ->when spinning slowly I can see the half turn signal move before skipping to 12
Encoder count = 12
Encoder count = 12
Encoder count = 13
Encoder count = 14
Encoder count = 14
Encoder count = 15
Encoder count = 16
Encoder count = 16
Encoder count = 18
Encoder count = 18
Encoder count = 18
Encoder count = 18
Encoder count = 18
Encoder count = 18
Encoder count = 20
"
ESP32Encoder.h uses #include <Arduino.h>
However i would like to use it without the Arduino framework,.
It does not seem the include serves any purpose AFAICS (except maybe using boolean
instead of bool
)
Would you be open to a PR where the Arduino include is removed?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.