Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 12 of 12

Thread: How to use encoders as pot

  1. #1

    How to use encoders as pot

    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/...rrruptsLED.ino
    Found here: https://www.youtube.com/watch?v=J9cDEef0IbQ

    not reliable.

    Thanks,
    Daniele.

  2. #2
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,401
    Have you read this: https://www.pjrc.com/teensy/td_libs_Encoder.html

    (also... you can make it easier people looking to help. .ino files can be hard to open on tablets and some networks block dropbox; better to paste your code here using 'go advanced' and the 'code' tags.)
    Last edited by oddson; 05-11-2017 at 07:06 PM.

  3. #3
    Quote Originally Posted by oddson View Post
    What exactly ? As I wrote, with Encoder lib, I get 4x count (as expected..). Do I need QuadDecode lib from linked thread ?
    Or is about polling vs interrupt ?

    Thanks,
    Daniele.

  4. #4
    Senior Member
    Join Date
    Jan 2013
    Posts
    843
    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-sample...mapped_encoder

  5. #5
    Quote Originally Posted by tni View Post
    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-sample...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.

  6. #6
    Senior Member
    Join Date
    Jan 2013
    Posts
    843
    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 by tni; 05-11-2017 at 09:38 PM.

  7. #7
    Quote Originally Posted by tni View Post
    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.

  8. #8
    Senior Member
    Join Date
    Jan 2013
    Posts
    843
    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);
        }
    }

    Quote Originally Posted by DanieleFromItaly View Post
    Last question, is there a way to rewrite the encoder value ? Something like enc::value(newvalue).
    Not by itself. Use begin(newvalue, ...).

  9. #9
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,431
    @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_;
    ...

  10. #10
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    2,600
    Quote Originally Posted by luni View Post
    @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.

  11. #11
    Senior Member
    Join Date
    Jan 2013
    Posts
    843
    Quote Originally Posted by Theremingenieur View Post
    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.

  12. #12
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,431
    Got it, thanks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •