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

Thread: Rotary encoders for flightsim panel

  1. #1

    Rotary encoders for flightsim panel

    I'm building a flight sim control panel (DCS) which incorporates rotary encoders, and I want the on-screen dial/knob rotation to mimic the rotation of the rotary encoder. A lot of the DCS key mapping allows for keypresses to increment a parameter up or down, such as cockpit instrument light brightness or heading indicator, using a rotary encoder each small step results in what the joystick controller recognises as a button depress. The result is for a knob on the game to turn one revolution you have to turn the rotary encoder about 20 revolutions. Is there a way I can get it to turn on 1:1 ratio?

  2. #2
    Senior Member
    Join Date
    Mar 2016
    Posts
    242
    So, you are mapping the turns from a quadrature encoder into button clicks if I understand your question. The obvious (and maybe wrong) answer is just multiply the number of pulses by 20 - if you see one click, send out 20 "keypresses". I say wrong because it will create choppy behavior with large jumps and you will, for example, only get headings in multiples of 20 but if that's ok, you can stop here. The cheap "arduino" encoders are typically 20 or 25 pulses per rev. You can get 100 ppr encoders but they are at least $20 or more. Note that most encoder libraries measure in state changes, not pulses, so a 20 ppr encoder is seen as having 80 ppr. Don't know if that changes your thinking.

    To make it smoother, you would need to be able to plan out the increase/decrease. Say you are turning one click every second then you would emit 20 (or 80, see above) pulses in a second to make it smooth. Though the issue of "lag" needs to be taken into account - you want the effect to stop when you stop turning the dial.

    I think there is some value in doing a simpler proportional algorithm where the multiplier changes based on the rotational rate - faster means a larger multiplier, slower means a smaller one. Maybe all that is needed is a simple 2 stage version of this where rates faster than say 500 mS/pulse are multiplied and slower than that aren't.

  3. #3
    Thanks for your input, PhilB, its only cheap encoders I'm using, the smoothness of them is not so important, its to turn non-critical switches and knobs.
    Code:
    //#include <Joystick.h>
    
    //SON OF BUTTON BOX 
    //USE w Teensy2.0
    //Tested in WIN10 + DCS World
    //ALSTUDIO
    //20.7.20
    
    #include <Keypad.h>
    
    #define ENABLE_PULLUPS
    #define NUMROTARIES 4
    #define NUMBUTTONS 20
    #define NUMROWS 4
    #define NUMCOLS 5
    
    
    byte buttons[NUMROWS][NUMCOLS] = {
      {1,2,3,4,5},
      {6,7,8,9,10},
      {11,12,13,14,15},
      {16,17,18,19,20},
    };
    
    struct rotariesdef {
      byte pin1;
      byte pin2;
      int ccwchar;
      int cwchar;
      volatile unsigned char state;
    };
    
    rotariesdef rotaries[NUMROTARIES] {//pins used by rotaries and corresponding button presses activated
      {3,5,21,22,0},
      {4,6,23,24,0},
      {7,9,25,26,0},
      {8,10,27,28 ,0},
    };
    
    #define DIR_CCW 0x10
    #define DIR_CW 0x20
    #define R_START 0x0
    
    #ifdef HALF_STEP
    #define R_CCW_BEGIN 0x1
    #define R_CW_BEGIN 0x2
    #define R_START_M 0x3
    #define R_CW_BEGIN_M 0x4
    #define R_CCW_BEGIN_M 0x5
    const unsigned char ttable[6][4] = {
      // R_START (00)
      {R_START_M,            R_CW_BEGIN,     R_CCW_BEGIN,  R_START},
      // R_CCW_BEGIN
      {R_START_M | DIR_CCW, R_START,        R_CCW_BEGIN,  R_START},
      // R_CW_BEGIN
      {R_START_M | DIR_CW,  R_CW_BEGIN,     R_START,      R_START},
      // R_START_M (11)
      {R_START_M,            R_CCW_BEGIN_M,  R_CW_BEGIN_M, R_START},
      // R_CW_BEGIN_M
      {R_START_M,            R_START_M,      R_CW_BEGIN_M, R_START | DIR_CW},
      // R_CCW_BEGIN_M
      {R_START_M,            R_CCW_BEGIN_M,  R_START_M,    R_START | DIR_CCW},
    };
    #else
    #define R_CW_FINAL 0x1
    #define R_CW_BEGIN 0x2
    #define R_CW_NEXT 0x3
    #define R_CCW_BEGIN 0x4
    #define R_CCW_FINAL 0x5
    #define R_CCW_NEXT 0x6
    
    const unsigned char ttable[7][4] = {
      // R_START
      {R_START,    R_CW_BEGIN,  R_CCW_BEGIN, R_START},
      // R_CW_FINAL
      {R_CW_NEXT,  R_START,     R_CW_FINAL,  R_START | DIR_CW},
      // R_CW_BEGIN
      {R_CW_NEXT,  R_CW_BEGIN,  R_START,     R_START},
      // R_CW_NEXT
      {R_CW_NEXT,  R_CW_BEGIN,  R_CW_FINAL,  R_START},
      // R_CCW_BEGIN
      {R_CCW_NEXT, R_START,     R_CCW_BEGIN, R_START},
      // R_CCW_FINAL
      {R_CCW_NEXT, R_CCW_FINAL, R_START,     R_START | DIR_CCW},
      // R_CCW_NEXT
      {R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START},
    };
    #endif
    
    byte rowPins[NUMROWS] = {20,19,18,17}; 
    byte colPins[NUMCOLS] = {12,13,14,15,16}; 
    
    Keypad buttbx = Keypad( makeKeymap(buttons), rowPins, colPins, NUMROWS, NUMCOLS); 
    
    //Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, 
    // JOYSTICK_TYPE_JOYSTICK, 32, 0,
     // false, false, false, false, false);
    
    void setup() {
    // Joystick.begin();
      rotary_init();}
    
    void loop() { 
    
      CheckAllEncoders();
      CheckAllButtons();
    
    }
    
    void CheckAllButtons(void) {
          if (buttbx.getKeys())
        {
           for (int i=0; i<LIST_MAX; i++)   //i++ is same as i=i+1
            {
               if ( buttbx.key[i].stateChanged )   
                {
                switch (buttbx.key[i].kstate) {  
                        case PRESSED:
                        case HOLD:
                                  Joystick.button(buttbx.key[i].kchar, 1);
                                  break;
                        case RELEASED:
                        case IDLE:
                                  Joystick.button(buttbx.key[i].kchar, 0);
                                  break;
                }
               }   
             }
         }
    }
    
    
    void rotary_init() {
      for (int i=0;i<NUMROTARIES;i++) {
        pinMode(rotaries[i].pin1, INPUT);
        pinMode(rotaries[i].pin2, INPUT);
        #ifdef ENABLE_PULLUPS
          digitalWrite(rotaries[i].pin1, HIGH); 
          digitalWrite(rotaries[i].pin2, HIGH); 
        #endif
      }
    }
    
    
    unsigned char rotary_process(int _i) {
       unsigned char pinstate = (digitalRead(rotaries[_i].pin2) << 1) | digitalRead(rotaries[_i].pin1);
      rotaries[_i].state = ttable[rotaries[_i].state & 0xf][pinstate];
      return (rotaries[_i].state & 0x30);
    }
    void CheckAllEncoders(void) {
      for (int i=0;i<NUMROTARIES;i++) {
        unsigned char result = rotary_process(i);
        if (result == DIR_CCW) {
          Joystick.button(rotaries[i].ccwchar, 1); delay(50); Joystick.button(rotaries[i].ccwchar, 0);
          };
        if (result == DIR_CW) {
          Joystick.button(rotaries[i].cwchar, 1); delay(50); Joystick.button(rotaries[i].cwchar, 0);
        };
      }
    }
    The rotary bit is right at the end, this is someone else's code I'm trying to modify to work with my Teensy2.0 board. I am a rookie when it comes to this code so not sure where to add the lines to send multiple pulses instead of just one.

Posting Permissions

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