Help with some Rotary Encoder stuff

Hi,

I'm new to Arduino.

I am trying to improve some existing code that did not work very well with a no-detent encoder. I have removed that code completely and replaced it with the code mentioned here:Rotary encoder decode for Arduino

Here is the code I have in an encoders() function

void encoders () {
static long encoder_pos = 0, last_encoder_pos = 0, encoder_change;  // will hold state of pins last time encoder was read, will hold any movement deduced from comparing old and new pins states from an encoder
  
//These two lines are for the other two encoders
static long encoder2_pos = 0, last_encoder2_pos = 0;  long encoder2_change;
static long encoder3_pos = 0, last_encoder3_pos = 0;  long encoder3_change;
  
unsigned long long old_freq;

encoder_pos = (digitalRead(enc1pinA)<<1|digitalRead(enc1pinB)); 
if (encoder_pos != last_encoder_pos) {
  
  Serial.print(last_encoder_pos); Serial.println(encoder_pos);
  encoder_change = RotaryDecodeTable[last_encoder_pos][encoder_pos]; // used RotaryDecodeTable to decide movement, if any
  last_encoder_pos = encoder_pos;

  if (encoder_change == B01){ // if combined transitions result was move right (CW), increment counter
    counter++; }
  if (encoder_change == B10){ // if combined transitions result was move left (anti-CW), decrement counter
    counter--; }
  delay(10); // wait generous debounce period before next read

  /// Other code here
  }

This is the output I get from the code when I turn the encoder.

17:20:01.258 -> 02
17:20:01.976 -> 23
17:20:02.804 -> 31
17:20:03.604 -> 10
17:20:05.487 -> 01
17:20:06.184 -> 13
17:20:07.109 -> 32
17:20:07.884 -> 20

That's from 4 turns CW and 4 from CCW.

The counter (temp variable for now) is supposed to be the frequency to be displayed and possibly other calculations, however with this code counter seems to count up/down slower than expected.

What would be the best way to improve the speed to ensure that the transitions that are happening increment/decrement the counter variable correctly.

If I turn the encoder too quickly, I get the following

17:48:13.518 -> 23
17:48:13.620 -> 30
17:48:13.723 -> 02
17:48:13.823 -> 21
17:48:13.926 -> 13
17:48:14.028 -> 31
17:48:14.094 -> 13
17:48:14.330 -> 31
17:48:14.500 -> 12

Many thanks

..StarKnight

If you have your serial port at 9600 that can slow it down. Increase the serial port to 115200. If the serial buffer fills the print calls block until buffer space becomes available. At 9600 it takes about 1ms to print a character.

You might even consider buffering the counters and only printing them when the encoder is not moving. That will give a more accurate picture. With a 10ms delay you will never capture more than 100 increments per second.

I've been at it for some time now, and modified the matrix to a single line

The new code is here

void encoders () {
static long encoder_pos = 0, last_encoder_pos = 0, encoder_change=0;  // will hold state of pins last time encoder was read, will hold any movement deduced from comparing old and new pins states from an encoder
  
//These two lines are for the other two encoders
static long encoder2_pos = 0, last_encoder2_pos = 0;  long encoder2_change;
static long encoder3_pos = 0, last_encoder3_pos = 0;  long encoder3_change;
  
unsigned long long old_freq;

last_encoder_pos = encoder_pos;
encoder_pos = (digitalRead(enc1pinA)*2 + digitalRead(enc1pinB)); 
if (encoder_pos != last_encoder_pos) {
  encoder_change = RotaryDecodeTable[last_encoder_pos *  4 + encoder_pos]; // used RotaryDecodeTable to decide movement, if any
  
  if (encoder_change == 1){ // if combined transitions result was move right (CW), increment counter
    encoder_change = 4 * encoder_change;
   }
  else if (encoder_change == -1){ // if combined transitions result was move left (anti-CW), decrement counter
    encoder_change = 4 * encoder_change;
    }
   
  delay(1); // wait generous debounce period before next read
  /// Other code here
  }

I'm still getting false readings, I'm thinking since I'm using a non-detent encoder may be that is cause?

Hi,

Came across this link: https://www.best-microcontroller-projects.com/rotary-encoder.html

A test seems to work, now to integrate and check

..StarKnight

The best approach that I've found for rotary encoders is the State Table approach. It's fast, simple, and handles debounce automatically.

i used an interrupt triggered on the RISING edge of one encoder input and simply checked the other encoder input to increment or decrement a count. doesn't count every encoder event and doesn't increment or decrement on opposite events.

but i've also had problems where knowing rotation was in a constant direction, i saw interrupts occurring earlier than expected and the "other" encoder input indicating motion in the opposite direction. my workaround was to ignore events occurring much sooner (< half) than the previous event.

i think, but haven't tested, that adding hysterisis may correct (?) the problem. I hope to try passing each encoder output through the trigger/threshold of a 555.

why do you have a 10 msec delay? optical input don't bounce like mechanical switches

Which encoder?

Hi,

Thanks for all the replies.

I'm currently using a PEC11R-4020F-S0024 from RS: https://uk.rs-online.com/web/cp/1674648,7377732,7377732P/?pst=PEC11R-4020F-S0024&sra=p

I'm trying to get an optical encoder for 3.3V such as this https://uk.rs-online.com/web/p/rotary-encoders/1674551/
But they seem scarce.

Many thanks for your help.

..StarKnight

StarKnight:
Hi,

Came across this link: Rotary Encoder: Immediately Tame your Noisy Encoder!

A test seems to work, now to integrate and check

..StarKnight

I have tried this code it works very well.

StarKnight:
Hi,

Came across this link: Rotary Encoder: Immediately Tame your Noisy Encoder!

A test seems to work, now to integrate and check

..StarKnight

Integration with the main section of the code that consists of a number of DSP stuff and screen refreshes did not go too well.

There was a considerable and severe lag on turning the tuning knob which for some reason would only update the counter upwards, CCW turns had no effect. The original code was very responsive in CW and CCW.

I made the follow change to the line

if (state==0xfff0){

to

if (state > 0xf03c && state < 0xffff){

.

Any reduction to the lower value F03C automatically started the frequency count to increment.

Any ideas would be appreciated, I would really like to get the tuning sorted, especially to get it to be responsive as the original code.

Many thanks for your time.

..StarKnight

StarKnight:
Any ideas would be appreciated,

Reply #4.

gfvalvo:
The best approach that I've found for rotary encoders is the State Table approach. It's fast, simple, and handles debounce automatically.

I've tried the state table approach, such as here Code For Improved Table Decode Rotary Encoder: Immediately Tame your Noisy Encoder! .

Both snippets of code works fine if it is just being used as a test, but if I add it to the main program, the large amount of code that is running in a loop() maybe causes a delay and does not give a correct response.

Thus, my question above on what could be done?

Thanks.

..StarKnight

Hi,

I found this link:

I have integrated this in the main code and it seems to work well. The responsiveness is dramatic.

I will complete the integration later tomorrow and report back.

Cheers

..StarKnight

So far you have posted enough code for me to tell what pins or Arduino board you're using. Complete code and board specification should have been in your very first post.

Regardless, you might try this library that implements the state table approach: GitHub - gfvalvo/NewEncoder: Rotary Encoder Library.

But, it only works on pins that support external interrupts. On an Uno, that would be 2 & 3. Again, that's why it's important to know what board you're using.

Hi,
Yes, Sorry. I should have said what the environment was about. I did not realise that the large DSP code would affect the running of the tuning encoder.

I am using the Arduino environment to modify the Teensy code (I'm using Teensy 3.6), so I came here for help. I'm using pins 16 and 17 on the Teensy 3.6 for the encoder.

The full code is here Home · DD4WH/Teensy-ConvolutionSDR Wiki · GitHub .

Apologies if this was the incorrect forum or if I did not post enough about the environment, I should have done so.

There are 2 other encoders that are detent-ed and those work fine since they control a limited set of functions, but I might try and work on them if this seemingly works well.

..StarKnight

That's a fairly complex project. How well do you understand the code to be attempting to modify it? It already uses the PJRC Encoder library which will be interrupt driven on a T3.6 as all its GPIO pins are interrupt-capable. I see no reason why it wouldn't work with your particular encoder. I'd dig into that anomaly before trying to role your own encoder handler.

The library at the GitHub link I posted will also work. But, I wouldn't use it at the same time as the PJRC library. Since its method calls are different, you'd have to change the handling of all 3 encoders.

Hi gfvalvo,

I've done PIC programming and I have a decent knowledge of C, so I guess I should be ok.

On the modification of the code, I guess I have to edit the encoders() section only, which I have done and I have removed the code for the Tuning encoder and used automaton.h

I have therefore included the 2 or 3 lines to drive just this encoder using the .OnChange line and have managed to insert the +4/-4 that the code requires to calculate the correct frequency.

The encoder works well and is fully responsive in CW and CCW directions, the frequency is also being updated correctly based on the step value.

I have kept the other two encoders as-is since they are working fine with the existing code (these are both detent-ed ones).

With all the earlier code that I used the tuning encoder was failing on CCW and was causing the frequency to jump large steps in the other (CW) direction.

Using the code from Rotary encoder decode for Arduino , or anything else caused the encoder to jump when used in the full version, however the sample code would work well in a test program where just the encoder snippet was used.

I've now tried to spin the tuning as fast as possible both CW and CCW and it works fine.

Maybe some further investigation on why the earlier snippets of code was not working with the same encoder is something that I must look into.

Thanks.
..StarKnight