How to use encoders as pot

Status
Not open for further replies.

DanieleFromItaly

Active member
Hi guys, I want to use few encoders as pot, it's a midi controller so at the end, I need to read 0-127.

Encoders are cheap ALPS.
With the encoder lib, I get 4 values for each step (1 2 3 4 - 5 6 7 8...). Is there a way to increase/decrease a counter by 1 (or any fixed value) for each step ?

Do I need 2 interrupt for this use ?
Missing a step every once in a while, is tolerable, wrong turn detection or jumping values are not.

There are tons of sketch in the web, but none of them work in reliable way :(

Now I'm testing this one: https://dl.dropboxusercontent.com/u/20622434/RotaryEncoderInterrruptsLED.ino
Found here: https://www.youtube.com/watch?v=J9cDEef0IbQ

not reliable.

Thanks,
Daniele.
 
The Encoder library doesn't work well for mechanical Encoders. Well-defined step-filtering is impossible.

Here is an experimental MappedEncoder class that works very well with my mechanical encoders. It is intended for Teensy 3.x and LC. 2 counts per detent and 4 counts per detent mechanical encoders are supported. It maps encoder steps to a clamped value range. Encoder steps result in a configurable value change.

https://github.com/tni/teensy-samples/tree/master/experimental/mapped_encoder
 
The Encoder library doesn't work well for mechanical Encoders. Well-defined step-filtering is impossible.

Here is an experimental MappedEncoder class that works very well with my mechanical encoders. It is intended for Teensy 3.x and LC. 2 counts per detent and 4 counts per detent mechanical encoders are supported. It maps encoder steps to a clamped value range. Encoder steps result in a configurable value change.

https://github.com/tni/teensy-samples/tree/master/experimental/mapped_encoder

Thanks ! Example sketch with 4 counts default config works fine !

But I' have some trouble make it working in a real sketch. I would call read() inside loop() but with begin() inside setup(), there's a classic scope error. If I put begin() outside setup(): "specializing member 'MappedEncoder<3u, 4u, long int, MappedEncoderConf4cpd>::begin' requires 'template<>' syntaxspecializing member 'MappedEncoder<3u, 4u, long int, MappedEncoderConf4cpd>::begin' requires 'template<>' syntax"

Code:
#include "mapped_encoder.h"

auto& serial = Serial;


// Channel A: pin 3
// Channel B: pin 4
// Encoder value type: int32_t
// Use default 4 counts per detent configuration.
using enc1 = MappedEncoder<3, 4, int32_t, MappedEncoderConf4cpd>;
int32_t enc1_val = 0;
    

// Use value range of [0, 127] with 1 step size, start value is enc1_val
enc1::begin(enc1_val, 0, 127, 1);


void setup() {
    serial.begin(115200);
    delay(2000);

    //int32_t enc1_val = 0;
    

    // Use value range of [0, 127] with 1 step size, start value is enc1_val
    //enc1::begin(enc1_val, 0, 127, 1);
    

//    while(true) {
//        if(enc1_val != enc1::read()) {
//            enc1_val = enc1::read();
//            serial.printf("[%u] Enc val [1]: %i\n", millis(), enc1_val);
//        }
//        
//    }
}

void loop() {
  if(enc1_val != enc1::read()) {
            enc1_val = enc1::read();
            serial.printf("[%u] Enc val [1]: %i\n", millis(), enc1_val);
        }
  
  
  }

So, how to use in real Sketch ?

Thanks again,

Daniele.
 
What's wrong with calling begin() from setup()?

MappedEncoder::begin() is a static member function. Your 'enc1::begin(enc1_val, 0, 127, 1);' is at the global scope, so it's interpreted as a declaration. You can't have 'freestanding' function calls in the global scope.

Either call 'enc1::begin(enc1_val, 0, 127, 1);' from a function (like setup()) or use a hack like (this is a variable declaration with initialization, which can be placed in the global scope):
Code:
int dummy_variable = (enc1::begin(0, -500, 500, 10) , 1);

Edit:

There is no object instance. The entire class is static. 'enc1' is a type with static members.
 
Last edited:
What's wrong with calling begin() from setup()?

Sorry, my mistake. int32_t enc1_val = 0; needs to be outside setup(), otherwise there's a scope error in loop().

Last question, is there a way to rewrite the encoder value ? Something like enc::value(newvalue).

Daniele.
 
This is valid:
Code:
#include "mapped_encoder.h"

auto& serial = Serial;

// Channel A: pin 3
// Channel B: pin 4
// Encoder value type: int32_t
// Use default 2 counts per detent configuration.
using enc1 = MappedEncoder<3, 4, int32_t, MappedEncoderConf2cpd>;

int32_t enc1_val = 0;

void setup() {
    serial.begin(115200);
    delay(2000);

    // Use value range of [-500, 500] with 10 step size, start value is enc1_val
    enc1::begin(enc1_val, -500, 500, 10);
}

void loop() {
    if(enc1_val != enc1::read()) {
        enc1_val = enc1::read();
        serial.printf("[%u] Enc val [1]: %i\n", millis(), enc1_val);
    }
}


Last question, is there a way to rewrite the encoder value ? Something like enc::value(newvalue).
Not by itself. Use begin(newvalue, ...).
 
@tni: I really enjoy reading your clear and well structured code. Especially your use of the more modern features of c++ is always very instructive for me.

One question:
is there any particular reason of aliasing "value_type_" with "value_type" instead of directly naming it "value_type" in the template declaration?

Code:
...
template<uint8_t pin_A, uint8_t pin_B, typename value_type_, typename config> 
class MappedEncoder {
public:
    using value_type = value_type_;
...
 
@tni: I really enjoy reading your clear and well structured code. Especially your use of the more modern features of c++ is always very instructive for me.

One question:
is there any particular reason of aliasing "value_type_" with "value_type" instead of directly naming it "value_type" in the template declaration?

@tni: Your use of C++ templates makes me discover a whole new world, thank you so much!
And I'd be interested in understanding the "value_type" aliasing, too.
 
And I'd be interested in understanding the "value_type" aliasing, too.
The template parameter is not easily accessible. By introducing value_type, I can use it via enc1::value_type. The enc1_val declaration could be:
Code:
enc1::value_type enc1_val = 0;

In an ideal world, I would do it like this:
Code:
template<class T>
struct S {
    using T = T;
};

But I'm not allowed to reuse the name.
 
Status
Not open for further replies.
Back
Top