Results 1 to 3 of 3
  1. #1
    10+ Posting Member
    Join Date
    May 2015
    Location
    internets
    Posts
    10
    Contribute If you enjoy reading the
    content here, click the below
    image to support MyCockpit site.
    Click Here To Contribute To Our Site

    link2fs radiopanel adjustment.

    can someone help me whit edit.
    the code of radiopanel.

    i cut out the rotary switch.

    i been adjusting the code to only display:
    com1
    nav1
    nav2
    xponder

    i have 2 dual encoders shipping in soon (so 4 encoders)
    and i have a singel encoder liggen.

    can someone help me adjust the basic link2fs code to acomidate 5 encoders instead of 1 ?

    many thanks to bushpilot. who was helping me alot getting the lcd to work

  2. #2
    150+ Forum Groupie BuzziBi's Avatar
    Join Date
    Mar 2013
    Location
    Norway
    Posts
    275
    Contribute If you enjoy reading the
    content here, click the below
    image to support MyCockpit site.
    Click Here To Contribute To Our Site

    Re: link2fs radiopanel adjustment.

    If you look at the "Multi_Keys_RotaryEncoders" in the Demo INOs from Jim, you find that there is used 3 encoders. Just add 2 more
    Life was hard, but then came Windows 7.
    Now we can fly! --------
    --------

  3. #3
    10+ Posting Member
    Join Date
    Jan 2013
    Location
    UK
    Posts
    18
    Contribute If you enjoy reading the
    content here, click the below
    image to support MyCockpit site.
    Click Here To Contribute To Our Site

    Re: link2fs radiopanel adjustment.

    Here is my implementation of five rotary encoders used in my FSX Radio Stack (see http://www.mycockpit.org/forums/showthread.php?t=29966).

    1. define a rotary encoder structure...
    Code:
    /*==============================================================================
     * ROTARY ENCODER structure
     * ---------------------------------------------------------------------------*/
    typedef struct {
      byte pinA;
      byte pinB;
      int detent;
      byte states;
      int counter;
      volatile int value;
    } encoder_t;
    2. initialise an array of rotary encoder structures as a global variable...
    Code:
    #define NBR_ENCS 5
    /*==============================================================================
     * mechanical rotary encoders usually have a detent position (click point)
     * where the rotary movement settles.  however, between each detent position, 
     * it is possible that either one, two or four state transitions may take place.  
     * thus we need to define the type of rotary encoder we are dealing with and
     * count the number of transitions between each detent to enable it to be
     * updated correctly for each detent position.  The encoder variable 'detent'
     * allows us to signal 1, 2 or 4 state transitions per detent.
     *
     * note: in my implementation, Leo Bodnar's dual encoders output 2 state
     * transitions per detent, whereas, the cheap (chinese sourced) rotary 
     * encoders output four state transitions per detent.
     * 
     * structure variables:      
     * A=pinA, B=pinB, dt=detent, st=states, ct=counter, val=value
     *                                   A   B dt st ct val
     * ---------------------------------------------------------------------------*/
    encoder_t encoders[NBR_ENCS] =  { { 22, 23, 2, 0, 0, 0 },     // COM MHz
                                      { 24, 25, 2, 0, 0, 0 },     // COM kHz
                                      { 26, 27, 4, 0, 0, 0 },     // BAR
                                      { 28, 29, 4, 0, 0, 0 },     // OBS
                                      { 30, 31, 4, 0, 0, 0 } };   // BAR
    3. initialise a rotary encoder state state machine as a global variable...
    Code:
    /*==============================================================================
     * mechanical rotary encoders use grey encoding in which the binary output
     * changes by a single bit for each state transition.  This program uses a state 
     * machine to interpret the rotational direction of encoders: i.e. clockwise 
     * or anti-clockwise.  If rotary encoder is turning clockwise it outputs:
     * AB    AB    AB    AB    AB (A and B represent the two pins of the encoder)
     * 00 -> 10 -> 11 -> 01 -> 00... etc
     * anti-clockwise movement will output:
     * AB    AB    AB    AB    AB
     * 00 -> 01 -> 11 -> 10 -> 00... i.e. the reverse of the above.
     * 
     * keeping track of both current and previous AB states then clockwise motion
     * results in:  
     *    BABA = prevB, prevA, currB, currA
     *    0010  ( 2 in decimal)
     *    1011  (11 in decimal)
     *    1101  (13 in decimal)
     *    0100  ( 4 in decimal)
     *    0010  repeats...
     * so at offset index positions 2, 4, 11 and 13 we store 1
     * On the other hand, anti-clockwise motion results in:
     *    BABA = prevB, prevA, currB, currA
     *    0001  ( 1 in decimal)
     *    0111  ( 7 in decimal)
     *    1110  (14 in decimal)
     *    1000  ( 8 in decimal)
     *    0001  repeats...
     * so at offset index positions 1, 7, 8 and 14 we store -1.
     * any other combination is invalid thus 0, 3, 5, 6, 9, 10, 12 and 15 = 0.
     * 
     *                         index into int array offsets[]
     * Hence... 0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15 
     * ---------------------------------------------------------------------------*/
    const int offsets[] = 
              { 0, -1,  1,  0,  1,  0,  0, -1, -1,  0,  0,  1,  0,  1, -1,  0 };
    4. in setup() routine create a timer interrupt and initialise the encoder pins...
    (for some reason if I put this inside [CODE] labels, it does not display!!??)

    cli(); // disable interrupts
    TIMSK2 |= (1<<0C1E2A); // set timer2 interrupt period to 1ms
    sei(); // enable interrupts

    Code:
      /*============================================================================
       * Initialise ROTARY ENCODERS...
       * -------------------------------------------------------------------------*/
      for (byte i = 0; i < NBR_ENCS; i++) {       // for each encoder...
        pinMode(encoders[i].pinA, INPUT_PULLUP);  // initialise encoder pins
        pinMode(encoders[i].pinB, INPUT_PULLUP);
      } //  end of for loop
    5. create an timer interrupt service routine (ISR)...
    Code:
    /*==============================================================================
     * ROTARY ENCODERS...
     * a timer interrupt is used to read rotary encoder movement
     * ---------------------------------------------------------------------------*/
    /*==============================================================================
     * this ISR reads each encoder to determine movement.  the process moves the 
     * previous AB states into bit positions 3 and 2, then stores the current AB 
     * states into bits 1 and 0.  the resultant states then form an index into the 
     * offsets[] array to determine if movement is positive (1), negative (-1) or 
     * none/invalid (0).  the number of state transitions is counted and this is 
     * compared with 'detent' (holding the number of transitions output between each 
     * detent) to determine when to increment/decrement encoder value.
     * ---------------------------------------------------------------------------*/
    ISR( TIMER2_COMPA_vect ) {
      for (byte i = 0; i < NBR_ENCS; i++ ) {
        encoders[i].states <<= 2; // shift previous states and read new states
        encoders[i].states |= ((digitalRead(encoders[i].pinB)) ? (1<<1) : 0) |
                              ((digitalRead(encoders[i].pinA)) ? (1<<0) : 0);
        encoders[i].counter += offsets[(encoders[i].states & 0x0F)];  // add offset
        if (encoders[i].counter >= encoders[i].detent) {  // detent?
          encoders[i].value += 1;                         // positive movement
          encoders[i].counter = 0;                        // reset counter
        } // end of if (counter >= detent)
        if (encoders[i].counter <= -encoders[i].detent) { // detent?
          encoders[i].value -= 1;                         // negative movement
          encoders[i].counter = 0;                        // reset counter
        } // end of if (counter <= -detent)
      } // end of for loop
    } // end of ISR()
    6. in loop() routine, check for rotary encoder movement...
    Code:
        /*==========================================================================
        * check for encoder movement
        * ------------------------------------------------------------------------*/
        for (byte i = 0; i < NBR_ENCS; i++ ) {  // for each encoder...
          int value;
          cli();                                // disable interrupts
          value = encoders[i].value;            // get encoder value
          encoders[i].value = 0;                // reset encoder value
          sei();                                // enable interrupts
          if (value) {                          // movement detected?
            processEncoder(i, value);           // if so, process it
          }  // end of if
        } // end of for loop
    7. process rotary encoder movement...
    Code:
    /*==============================================================================
     * based on the parameter 'enc' this calls the relevant change routine 
     * and passes 'value' to it.
     * ---------------------------------------------------------------------------*/
    void processEncoder(byte enc, int value) {
      switch (enc) {
        case 0:     // COM radio MHz
        case 1: {   // COM radio kHz
          changeCOMRadios(enc, value);
          break;      
        } // end of case 1:
        case 2: {   // Heading bug
          changeHeadingBug(value);
          break;
        } // end of case 2:
        case 3: {   // OBR
          changeNavOBS(value);
          break;
        } // end of case 3:
        case 4: {   // barometric pressure setting (millibars)
          changeKollsmanWindow(value);
          break;
        } // end of case 4:
      } // end of switch
    } //  end of processEncoder()
    8. Here is an example update routine. This one changes the heading bug...
    Code:
    /*==============================================================================
     * 'value' is used to increment/decrement the heading bug.
     * if the encoder is turned quickly then a multiplier is introduced to update
     * the heading more quickly.  The new heading is then sent to FSX
     * NOTE: FUSIPC offset (0x07CC) for AP heading value = degrees * 65536 / 360
     * ---------------------------------------------------------------------------*/
    void changeHeadingBug(int value) {
      const int interval = 100;                     // accelerate interval (msec)
      static unsigned long timeThen = 0;            // last time routine was called
      unsigned long timeNow = millis();             // get current time
      int bug = int(atoi(HDG_BUG)*65536/360);       // get current heading
      int stepval = value * 65536 / 360;            // format value for Link2fs
      if ((timeNow - timeThen) < interval)          // if faster than interval
        stepval *= 10;                              // then accelerate movement
      bug += stepval;                               // incr/decr heading bug
      if (bug > 32768)  bug -= 65535;               // wrap heading bug value
      if (bug < -32768) bug += 65536;
      Serial.print("#a");                           // update FSX
      Serial.println(bug);
      timeThen = timeNow;                           // reset timer
    }  // end of changeHeadingBug()
    Hope this makes sense and is useful
    Dave