Need guidance on getting rotary encoders to work for my flight sim panel

Just a little background. I'm not very good with programming, but I like tinkering with electronics and doing flight sims. So I decided to build my first flight panel. It has multiple momentary switches, rotary switches and five rotary encoders with buttons. I intend to use the rotary encoders to control the altitude select, vertical speed, heading bug etc on Microsoft Flight Simulator 2020.

I basically copied codes from the net and was able to get it to work (well, most of it). Using the "joy.cpl" application on windows, I am able to verify that ALL the buttons work. LEDs work too!

My problem is that the rotary encoder movements (both clockwise and counter-clockwise) are not recognized by the computer. When I am on Arduino IDE, using the Serial Monitor, i can confirm that all the knobs work as intended. But outside Arduino IDE, i can't seem to get any response at all. Since there is no response using the joystick configuration app, MSFS also doesn't recognize the rotary encoder movements.

Currently, I am only able to use the buttons, which is a shame. I'd really like to make use of the rotary encoders. I must be missing something. Could anyone please assist? Apologies if this is a super noob query as I am still getting the hang of coding.

Thanks in advance!

FlightPanelCode

Why not just post the code instead of sending 'us' to a thingy that wants to download?

Oh, use code tags.

In this case you might as well let us know which micro controller you are using, post a schematic, and some images of your project.

Hi, thanks for the reply.

I was going to post the .ino code and some other files but I am currently not able to, as I am a new user. Apparently the forums require that I post more and earn some badges before I can attach files, so I resorted to uploading to a google drive. Apologies for that, too.

I'll work on the pics and schematic and upload them as soon as I am able to.

You can post the code inline.. just make sure you put it between the code tags (</>).

trying it out!

//How many buttons I'm using, must equal amount of values in following array #define NUM_BUTTONS 17

// Rotary Encoder Inputs
#define CRSinputCLK 10 // heading
#define CRSinputDT 11
#define HEADinputCLK 13 // course
#define HEADinputDT 16
#define FLCSPDinputCLK 32 // vspeed
#define FLCSPDinputDT 33
#define VSPEEDinputCLK 17 // alt select
#define VSPEEDinputDT 18
#define ALTSELinputCLK 19 // flc change
#define ALTSELinputDT 20

//Which pins I have attached to my buttons
int buttonList[NUM_BUTTONS] = {0,1,2,3,4,5,6,7,8,9,12,21,22,23,24,25,31};

int CRScounter = 0;
int CRScurrentStateCLK;
int CRSpreviousStateCLK;

int HEADcounter = 0;
int HEADcurrentStateCLK;
int HEADpreviousStateCLK;

int FLCSPDcounter = 0;
int FLCSPDcurrentStateCLK;
int FLCSPDpreviousStateCLK;

int VSPEEDcounter = 0;
int VSPEEDcurrentStateCLK;
int VSPEEDpreviousStateCLK;

int ALTSELcounter = 0;
int ALTSELcurrentStateCLK;
int ALTSELpreviousStateCLK;

String CRSencdir ="";
String HEADencdir ="";
String FLCSPDencdir ="";
String VSPEEDencdir ="";
String ALTSELencdir ="";

//Led intensity, so super bright LEDS aren't shining in our eyes
#define INTENSITY 200

void setup() {
//This makes it so the states are sent by us manually
Joystick.useManualSend(true);

//Declare button pins as input with the internal pullup resistor on
for (int i = 0; i < NUM_BUTTONS; i++) {
pinMode(buttonList[i], INPUT_PULLUP);
}

//Declare our LED pins as outputs
pinMode(26, OUTPUT);
pinMode(27, OUTPUT);
pinMode(28, OUTPUT);
pinMode(29, OUTPUT);
pinMode(30, OUTPUT);

// Set encoder pins as inputs
pinMode (CRSinputCLK,INPUT);
pinMode (CRSinputDT,INPUT);
pinMode (HEADinputCLK,INPUT);
pinMode (HEADinputDT,INPUT);
pinMode (FLCSPDinputCLK,INPUT);
pinMode (FLCSPDinputDT,INPUT);
pinMode (VSPEEDinputCLK,INPUT);
pinMode (VSPEEDinputDT,INPUT);
pinMode (ALTSELinputCLK,INPUT);
pinMode (ALTSELinputDT,INPUT);

// Setup Serial Monitor
Serial.begin (9600);

// Read the initial state of inputCLK
// Assign to previousStateCLK variable
CRSpreviousStateCLK = digitalRead(CRSinputCLK);
HEADpreviousStateCLK = digitalRead(HEADinputCLK);
FLCSPDpreviousStateCLK = digitalRead(FLCSPDinputCLK);
VSPEEDpreviousStateCLK = digitalRead(VSPEEDinputCLK);
ALTSELpreviousStateCLK = digitalRead(ALTSELinputCLK);

}

void loop() {
//Not applicable Read our analogue pots
//Removed because NA Remember that the analogue pin numbers are different than the digital ones!
//Removed because not applicable Joystick.sliderLeft(analogRead(7));
//Removed because not applicable Joystick.sliderRight(analogRead(8));

//Read our button states
for (int i = 0; i < NUM_BUTTONS; i++) {
if (digitalRead(buttonList[i]) == HIGH) { //Check to see if pin is HIGH
Joystick.button(i + 1, 0); //If pin is HIGH, button isn't pressed, so send 0
} else {
Joystick.button(i + 1, 1); //If pin is LOW, button is pressed, so send 1
}
}

//Special case for LED status lights
//Check status of button and change LED accordingly

if (digitalRead(21) == LOW) //Check if button is pressed/switch flipped
analogWrite(26, INTENSITY); //Set corresponding LED pin to intensity level
else
analogWrite(26, 0); //Otherwise turn off

if (digitalRead(22) == LOW) //Same for other pins
analogWrite(27, INTENSITY);
else
analogWrite(27, 0);

if (digitalRead(23) == LOW)
analogWrite(28, INTENSITY);
else
analogWrite(28, 0);

if (digitalRead(24) == LOW)
analogWrite(29, INTENSITY);
else
analogWrite(29, 0);

if (digitalRead(25) == LOW)
analogWrite(30, INTENSITY);
else
analogWrite(30, 0);

Joystick.send_now(); //Send control states
delay(5); //Slow things down a bit

// THE HEADING BUG
// Read the current state of CRSinputCLK
CRScurrentStateCLK = digitalRead(CRSinputCLK);

// If the previous and the current state of the inputCLK are different then a pulse has occured
if (CRScurrentStateCLK != CRSpreviousStateCLK){

 // If the inputDT state is different than the inputCLK state then 
 // the encoder is rotating counterclockwise
 if (digitalRead(CRSinputDT) != CRScurrentStateCLK) { 
   CRScounter --;
   CRSencdir ="CCW";
   
 } else {
   // Encoder is rotating clockwise
   CRScounter ++;
   CRSencdir ="CW";
   
 }
 Serial.print("Heading Direction: ");
 Serial.print(CRSencdir);
 Serial.print(" -- Value: ");
 Serial.println(CRScounter);

}
// Update previousStateCLK with the current state
CRSpreviousStateCLK = CRScurrentStateCLK;

// THE COURSE BUG
// Read the current state of HEADinputCLK
HEADcurrentStateCLK = digitalRead(HEADinputCLK);

// If the previous and the current state of the inputCLK are different then a pulse has occured
if (HEADcurrentStateCLK != HEADpreviousStateCLK){

 // If the inputDT state is different than the inputCLK state then 
 // the encoder is rotating counterclockwise
 if (digitalRead(HEADinputDT) != HEADcurrentStateCLK) { 
   HEADcounter ++;
   HEADencdir ="CW";
   
 } else {
   // Encoder is rotating clockwise
   HEADcounter --;
   HEADencdir ="CCW";
   
 }
 Serial.print("Course Direction: ");
 Serial.print(HEADencdir);
 Serial.print(" -- Value: ");
 Serial.println(HEADcounter);

}
// Update previousStateCLK with the current state
HEADpreviousStateCLK = HEADcurrentStateCLK;

// THE VSPEED BUG
// Read the current state of FLCSPDinputCLK
FLCSPDcurrentStateCLK = digitalRead(FLCSPDinputCLK);

// If the previous and the current state of the inputCLK are different then a pulse has occured
if (FLCSPDcurrentStateCLK != FLCSPDpreviousStateCLK){

 // If the inputDT state is different than the inputCLK state then 
 // the encoder is rotating counterclockwise
 if (digitalRead(FLCSPDinputDT) != FLCSPDcurrentStateCLK) { 
   FLCSPDcounter --;
   FLCSPDencdir ="CCW";
   
 } else {
   // Encoder is rotating clockwise
   FLCSPDcounter ++;
   FLCSPDencdir ="CW";
   
 }
 Serial.print("Vspeed Direction: ");
 Serial.print(FLCSPDencdir);
 Serial.print(" -- Value: ");
 Serial.println(FLCSPDcounter);

}
// Update previousStateCLK with the current state
FLCSPDpreviousStateCLK = FLCSPDcurrentStateCLK;

// THE ALTITUDE SELECT KNOB
// Read the current state of VSPEEDinputCLK
VSPEEDcurrentStateCLK = digitalRead(VSPEEDinputCLK);

// If the previous and the current state of the inputCLK are different then a pulse has occured
if (VSPEEDcurrentStateCLK != VSPEEDpreviousStateCLK){

 // If the inputDT state is different than the inputCLK state then 
 // the encoder is rotating counterclockwise
 if (digitalRead(VSPEEDinputDT) != VSPEEDcurrentStateCLK) { 
   VSPEEDcounter --;
   VSPEEDencdir ="CCW";
   
 } else {
   // Encoder is rotating clockwise
   VSPEEDcounter ++;
   VSPEEDencdir ="CW";
   
 }
 Serial.print("Alt Select Direction: ");
 Serial.print(VSPEEDencdir);
 Serial.print(" -- Value: ");
 Serial.println(VSPEEDcounter);

}
// Update previousStateCLK with the current state
VSPEEDpreviousStateCLK = VSPEEDcurrentStateCLK;

// THE FLCSPEED Knob
// Read the current state of ALTSELinputCLK
ALTSELcurrentStateCLK = digitalRead(ALTSELinputCLK);

// If the previous and the current state of the inputCLK are different then a pulse has occured
if (ALTSELcurrentStateCLK != ALTSELpreviousStateCLK){

 // If the inputDT state is different than the inputCLK state then 
 // the encoder is rotating counterclockwise
 if (digitalRead(ALTSELinputDT) != ALTSELcurrentStateCLK) { 
   ALTSELcounter --;
   ALTSELencdir ="CCW";
   
 } else {
   // Encoder is rotating clockwise
   ALTSELcounter ++;
   ALTSELencdir ="CW";
   
 }
 Serial.print("FLCSpeed Direction: ");
 Serial.print(ALTSELencdir);
 Serial.print(" -- Value: ");
 Serial.println(ALTSELcounter);

}
// Update previousStateCLK with the current state
ALTSELpreviousStateCLK = ALTSELcurrentStateCLK;

}

Realistically you need to understand the code , be able to code to some degree , you have and then google how to use an Encoder with Arduino - you will need to do the coding bit at some point .
On the forum people like to help rather than do it for you -so have a go and see how you get on , just write something for the encoder only to start with.

Other wise you will need to pay up to get someone to write your code .

Thanks for the reply, hammy.

I'm still trying to understand the language and I have been reading up (and watching videos). Not really asking to have someone write the code for me but hoping someone could maybe just guide me, I guess?

I've been reviewing the code and realized that the code is working on the serial monitor on Arduino IDE because the code just includes the "serial print" commands, nothing else. Windows does not recognize any other actions such as button presses because there's nothing else on the code for that.

I'm now in the process of researching for the correct code so that when the knob is turned clockwise, it will send a command that "x" button is pressed, and when knob is turned counterclockwise, "y" button is pressed. So if you keep turning the knob, there will be multiple button presses, which is exactly what I am aiming for :smiley:

Again, thanks everyone for the patience, and apologies if this post is too "noobish" bordering offensive :smiley:

I'm struggling a little to understand exactly what isn't working here.

Is the joystick connected to the Arduino?

Where does the FS software fit in the mix? Presume this is running on you PC and somehow you need to get the rotary encoder information sent from the Arduino to the PC?

Hi red_car.

Yes the buttonbox is connected to the arduino and both buttons and encoders work when testing them using the serial monitor of arduino ide and the joy.cpl (joystick configurator) app of windows.

But for the encoders, they are not recognized in the joy.cpl app, as well as in the simulator itself.

I think i may have figured it out. I think i need to add a joystick.button command somewhere. Still figuring it out. :slight_smile:

I am very close to solving this :wink:

I've decided to use another code just to focus on the rotary encoder. This new code now uses a library which, in my opinion, made the reading of the encoder rotations a LOT better.

After trying to understand the coding language, I was able to add the necessary codes to set the effects when the dial is turned CW and CCW. What I did was this: when the user turns the knob clockwise, say, twice, it will print a message saying the movement was clockwise, and also displays the value.. like this "Clockwise turn - new value 2". Then, I added a code so that it will press a joystick button corresponding to the clockwise pin on the micro controller (joystick.button)

My new problem is that.. once pressed, it remains pressed unless I turn the knob to a different direction (CCW, in the example). I'd like for the button to release after one press, then start from scratch again.

Here is the new code:

#include <Encoder.h>

// Change these two numbers to the pins connected to your encoder.
// Best Performance: both pins have interrupt capability
// Good Performance: only the first pin has interrupt capability
// Low Performance: neither pin has interrupt capability
Encoder myEnc(10, 11);
// avoid using pins with LEDs attached

void setup() {
Serial.begin(9600);
Joystick.useManualSend(true);
}

long oldPosition = 0;

void loop() {
long newPosition = myEnc.read();
if (newPosition != oldPosition) { // there was a rotation

    if (newPosition > oldPosition) {    // rotation is clockwise
    Serial.print("Heading Direction: ");
    Serial.print("Clockwise");
    Serial.print(" -- Value: ");
    Serial.println(newPosition);
    Joystick.button(10+1, 1);
    Joystick.button(11+1, 0);
    Joystick.send_now();
       
    } else {                            // rotation is counter-clockwise
    Serial.print("Heading Direction: ");
    Serial.print("Counter-clockwise");
    Serial.print(" -- Value: ");
    Serial.println(newPosition);
    Joystick.button(11+1, 1);
    Joystick.button(10+1, 0);
    Joystick.send_now();
    
    }

oldPosition = newPosition;

}

}

I would guess that you need to send to the joystick twice. Once to indicate that you pressed a button and then again to tell the PC that you released it.

Thanks, wildbill.

That's what I did! :slight_smile: For the succeeding loop, the code checks if there has been a change in values from the knobs. If there is no change, the command is to release it. This solved the problem for me.

Finally everything is working fine :slight_smile:

Thanks, everyone!