Jump to content
  • entries
    3
  • comments
    34
  • views
    895

DIY - Custom Game Controller - 2 Dial HSI Course and Heading Knobs


zPztEG1.jpg

 I have a touch screen monitor below my main one when I fly in flight simulators like Falcon BMS or DCS or Microsoft Flight Simulator.  It's great for the MFD buttons, as opposed to the actual wired USB Thrustmaster MFD's physically attached to the screen like I used to have, and I can use the monitor for other things, too.  But one thing I would love to have attached to the screen would be the Course and Heading knobs of the HSI (horizontal situation indicator) .

It would need to be removable so I can store it when not in use. 

I've heard about a micro-suction tape that can stick to anything smooth and flat without leaving a residue or losing stickiness, so this is a good excuse to buy some and play around with sticking things to my monitors.
 

← On the left is the (green) Arduino Micro board I used to test the concept with a rotary encoder on a breakout board, where I wrote the initial sketch with the Joystick and Encoder libraries.  Later, because I didn't want to sacrifice my Arduino Micro to this controller, I bought a 3 pack of these much smaller Sparkfun Pro Micro clones for fifteen bucks (blue one in the middle).  That one is gonna stay there for future tests and such, they'll be the heart of many projects in the future since they cost so little and include so much!

 


 

My DIY USB Game Controller - Rotary Encoders as Course & Heading Knobs for Falcon BMS / DCS / FSX
*parts list with links at bottom


The initial goals I had for this project were:h5w7NMM.jpg

  • Two Rotary Encoders with Push Buttons recognized as USB Game Controller in Windows
  • Thin and flat, without taking up too much viewing space on the monitor
  • Can stick and restick to the monitor with micro-suction tape
  • USB connector can be removed so it can be boxed (NetDot magnetic)
  • Fine control for one-degree per detent and Fast Speeds for turning quickly
  • Encased in semi-rigid form like shrink wrap so it won't scratch the monitor
  • Total cost for one single unit less than $10

 

 

 

 

 

 


4Y01eI2.jpg 

 

First, I affixed the Rotary Encoders and the Pro Micro clone to an 8cm x 2cm prototype PCB with a couple header pins bent outwards and crimped down, and with the two blue solid copper switch wires soldered in place from the top. I also ran the 10k Ohm resistors for the switches now.

 

These are for the push buttons built into the rotary encoders, and although they only have two pegs on the actual component, they require three wires from the controller board (ground, 5 volt power, and the blue switch wire going to the Pro Micro). All this added plenty of structure. There is no wiggle or play, I'm not gonna try to break it, but it is very rigid now.

 

 

 

 

H89ZiWv.jpg 

 

Here's a close look at the first stage of soldering. (okay, don't look too close) I don't have the highest quality tools or solder, or even a lot of skill and experience at this, but it's good enough -- and good enough should always be good enough for a DIY project for personal use.  If I wanted to, I could order a custom circuit board if I was going to make a bunch of these, but I'm happy as it is.

 

It is so cheap to buy some of this stuff, if you messed up, you can always just buy another.  Those Pro Micro controller boards cost about as much as a value meal at a fast food joint, so you can literally afford to make mistakes as you learn even if you're on a very tight budget like I am.  

 

 

 


ru0rmph.jpg

 

 

A homemade carbon filter fume extractor, a simple soldering iron with a power switch and a temperature controller, some handy dandy helping hands and that brass soldering tip cleaner make little projects like this very easy, and all these items together costs less than the Logitech mouse to it's right. Good enough to play.

 

If anyone reading this has any idea to get into working with things like this, my advice is to jump right in, get the most basic stuff you can get, not the cheapest, but things with good reviews from some good online store like Amazon, Sparkfun, Adafruit, etc.

 

 

 

 

 

Pinxa02.jpg

 

Soldering the top side here, used one of the helping hands to hold a string of solder from the spool. When I'm dealing with tiny bits that are between 0.5mm-2mm, anything that helps is appreciated. One slip and this 225C soldering iron would burn through any one of those tiny chips or components on the board. I find myself holding my breath and doing one or two, then letting go for a sec. I had to redo a few, and I always wait for it to cool down so I don't transfer too much heat to this poor little board.

 

I had a few bits of solid copper wire with colored insulators, from a breadboard jumpers kit, so I cut some to length, but used others as is. This resulted in an odd looking crossover of the Orange and Yellow wires, but they are not taller than the micro USB port on the other side of the board and that's fine.

 

 

 

 

 


xzlxtFJ.jpg   fzzbWnc.jpgOnce I was all done, I tested every connection for continuity and unintentional bridges, and it all checked out, so I encased it in shrink wrap.  By some sort of miracle, it works, it looks decent, and I think that will do just great!  I connected it to the computer and uploaded the sketch, which I had to modify slightly since I used a few different pins for the push buttons on the rotary encoders, now at pins 15 and 6 instead of 6 and 7.

I am thoroughly hooked on these NetDot magnetic adapters.  When I looked at the reviews for Pro Micro boards, one of the only issues I saw that I'd be concerned with is accidentally popping the micro-USB port off the Pro Mini board if dropped or yanked too hard.  A magnetic connector completely eliminates this issue, as well as the standard wear and tear issues that plugging and unplugging creates over time.  And they're as cheap as regular braided USB cables, too.  Why not?

 

 


Ocsopzo.jpg   dzvZVUh.jpg   CustomHSIgamecontrollerProperties.PNG

Here it is connected to the touchscreen monitor. It is far more satisfying to have tactile dials for these knobs than trying to make little circles on the screen with my finger over the touchscreen dials. The micro-suction tape keeps it in place and actually has some grab when trying to pull it off the screen. I've started using a peeling action but I doubt the screen would ever have problems.  I might get a 3D printed case someday and maybe even a 90 degree angle connector so the cord goes back behind the monitor naturally instead of downwards, but for now, it's done and working great.

Turning the dial with the most basic rotary encoder sketch from that Arduino library results in a "one degree per detent" meaning one turn is one click, and one click is one degree. No matter how fast you turn it, with 20 detents per 360 degrees with these encoders, it would take FOREVER to turn the dial from heading 000 to 180!!

I wrote two separate jump speeds to detect how fast the dial is being turned, one jumps 18 degrees per click, and the other 30 per click. Now, it's easy to twist the dial fast to get it spinning fast on screen, while also being able to dial in one degree at a time when needed.  All in all, it was a fun project and it's made me think seriously about getting a 3D printer someday to make little plastic cases for things like this.
 

Here's the code if someone wants to use it (or modify it to make it better):
Rotary Encoders HSI Course and Heading Knobs.ino for Arduino IDE
https://pastebin.com/drUnCfKN

spoilerIMG2415905122018.PNG
/* Simple HSI Knobs Sketch for Falcon BMS / DCS / FSX
 *  for Arduino Micro/Leonardo / Sparkfun Pro Micro or equiv. clones
 * by SemlerPDX June2019
 * VETERANS-GAMING.COM
 *  
 *  Pins:
 *  Rotary Encoder 1 - (OUTA-OUTB-SW) = Arduino Pins (0,1,15)
 *  Rotary Encoder 2 - (OUTA-OUTB-SW) = Arduino Pins (2,3,6)
 *  
 *  Encoder Library
 * http://www.pjrc.com/teensy/td_libs_Encoder.html
 * 
 *  Joystick Library 
 * by Matthew Heironimus
 * https://github.com/MHeironimus/ArduinoJoystickLibrary
 */

#define ENCODER_USE_INTERRUPTS
#define ENCODER_OPTIMIZE_INTERRUPTS
#include <Encoder.h>
#include <Joystick.h>

//Tell the Encoder Library which pins have encoders
Encoder axisXRotation(0, 1);
Encoder axisYRotation(2, 3);

//Rotary Encoder Push Button Pins
int buttonArray[2] = {15, 6};

//Rotary Encoder Interrupt Pins
int EncoderPin0 = 0;
int EncoderPin1 = 1;
int EncoderPin2 = 2;
int EncoderPin3 = 3;

//Delay Time between loops
int debounceDelay = 260;

//Variables to compare current to old values
int oldX = 0;
int oldY = 0;
int RxAxis_Value = 1;
int RyAxis_Value = 1;

//Intervals for Jump/Warp Speed Rotations
int JumpSpeed = 18;
int WarpSpeed = 30;

//Set generic joystick with id 42 with 2 buttons and 2 axes
Joystick_ Joystick(0x42, 
  0x04, 2, 0,
  false, false, false, true, true, false,
  false, false, false, false, false);  


void setup() { 

  //Set Encoder Pins as Pullups
  pinMode(EncoderPin0, INPUT_PULLUP);
  pinMode(EncoderPin1, INPUT_PULLUP);
  pinMode(EncoderPin2, INPUT_PULLUP);
  pinMode(EncoderPin3, INPUT_PULLUP);

  //Loop through buttons and set them as Pullups
  for(int x = 0; x < sizeof(buttonArray); x++) {
    pinMode(buttonArray[x], INPUT_PULLUP);
  }

  //Set Range of custom Axes
  Joystick.setRxAxisRange(0, 359);
  Joystick.setRyAxisRange(0, 359);
  
  // Initialize Joystick Library
  Joystick.begin(false);

}


void loop() {

  // Loop through button pin values & set to Joystick
  for (int x = 0; x < sizeof(buttonArray); x++) {
    byte currentButtonState = !digitalRead(buttonArray[x]);
    Joystick.setButton(x, currentButtonState);
  }


  // Read "Heading" X Axis Rotation Encoder Knob
  int newX = axisXRotation.read();
  if (newX > oldX) {
    //Determine speed of increment & set output
    int difX = newX - oldX;
    RxAxis_Value = speedVal(difX, RxAxis_Value, 1);
    Joystick.setRxAxis(RxAxis_Value);
    axisXRotation.write(newX);
    oldX = newX;

  }else if (newX < oldX) {
    //Determine speed of decrement & set output
    int difX = oldX - newX;
    RxAxis_Value = speedVal(difX, RxAxis_Value, 0);
    Joystick.setRxAxis(RxAxis_Value);
    axisXRotation.write(newX);
    oldX = newX;
  }


  // Read "Course" Y Axis Rotation Encoder Knob
  int newY = axisYRotation.read();
  if (newY > oldY) {
    //Determine speed of increment & set output
    int difY = newY - oldY;
    RyAxis_Value = speedVal(difY, RyAxis_Value, 1);
    Joystick.setRyAxis(RyAxis_Value);
    axisYRotation.write(newY);
    oldY = newY;

  }else if (newY < oldY) {
    //Determine speed of decrement & set output
    int difY = oldY - newY;
    RyAxis_Value = speedVal(difY, RyAxis_Value, 0);
    Joystick.setRyAxis(RyAxis_Value);
    axisYRotation.write(newY);
    oldY = newY;
  }


  //Send Joystick info through USB
  Joystick.sendState();
  delay(debounceDelay);
}


//Function to set Rotation value adjusted for the turning speed
int speedVal(int dif, int val, int dir){
  if (dif >= WarpSpeed) {
    if (dir == 1) {
      val = val + WarpSpeed;
    }else{
      val = val - WarpSpeed;
    }
  }else if (dif >= JumpSpeed) {
    if (dir == 1) {
      val = val + JumpSpeed;
    }else{
      val = val - JumpSpeed;
    }
  }else{
    if (dir == 1) {
      val = val + 1;
    }else{
      val = val - 1;
    }
  }
  //Correct Rotation within 360 deg.
  if (val < 0) {
    val = val + 360;
  }else if (val >= 360) {        
    val = val - 360;
  }
  return val;
}

 

 


Here's a cost breakdown:

Pro Micro  (clone of Sparkfun Pro Micro board sold by KeeYees) (3 pack)  -  $15.99
https://www.amazon.com/gp/product/B07FXCTVQP/
$5.33 per board

PCB Prototype Board Kit  -  $15.99
https://www.amazon.com/gp/product/B07CK3RCKS/ 
$0.25 (just a guestimate - it's one part out of a huge kit)

360 Degree Rotary Encoders (5 pack w/knob caps)  -  $8.89
https://www.amazon.com/gp/product/B07DM2YMT4/
$3.56 for 2 dials

Microsuction Tape (25cm X 30cm Sheet)  -  $14.95
https://www.amazon.com/gp/product/B00M7FC1K8/
$0.12 (just a guestimate - used 1.5cm x 6cm strip of a huge sheet)

NetDot 5ft Braided Magnetic Tip USB Micro Cable (3 pack)  -  $13.90
https://www.amazon.com/gp/product/B074TB8XTL/
$4.64 for 1 cord

(Not taking into account things like double sided sticky tape, solder, wires, or shrink wrap tubing segments used because they cost less than a few pennies)

Total:
$13.65 (USD)

_________________________________

I spent a fair bit more money than I initially expected to (almost $55!), but much of that went towards components or materials that I'll be able to use for several projects in the future.  At about $14 bucks, it is twice what I thought it would be per unit, so that will help me to better gauge other ideas.  It seemed like it would be cheap as dirt, using many parts I already owned, but it all adds up - they don't sell less than a sheet of micro-suction tape, and buying control boards in bulk is the only wise way to do it (if you can call 3 units "bulk", that is).


In conclusion, it was great to have an idea, play around with some proof of concepts, and then make it into a reality within a few days.  Single game controllers today.... one day, a full cockpit of switches!  Not sure what my next project will be, but among other ideas, I've considered making a custom control board for Kerbal Space Program, or maybe some kind of wireless gear that can connect to a computer and translate into RF to control some DIY RC cars or whatever with my Xbox controller already attached to my PC.  Eventually, I want to get into wireless stuff, and even RC, but I might start with button boards that use USB cables just like this one. 

If anyone has any questions on this project, feel free to ask.


Thanks for reading! 



10 Comments


Recommended Comments

Hello sir!

i did your project and it’s working fine, thank you so much!!

I want to add another rotary for the QNH  adjusment and a three way switch (as master arm), but i am unable to modify the code because it’s very hard for me... can you help me to add this Z axis and three way switch?

  • Like 1

Share this comment


Link to comment

You're welcome!

Adding a third rotation axis is super simple, the joystick library actually has 3 rotation axes and we've only used 2 of them.  It's just a matter of adding in that Z axis in the same way we handle the X and Y, and wiring it up to a pair of Interrupt Pins, and it's button to the buttons pin array. Adding a 3-way toggle switch should work just fine as buttons, but I don't have an example I can mash in right now, and so I'll have to look into it tomorrow or this weekend, but I'm sure I can toss something together for you.

You will want to learn how to wire up that 3-way switch and run an example sketch to test it, here's a simple lesson for that - his pics could be better, but you'll get the idea - I can't help you with your wiring, so you'll need to work through this to learn where to wire the resistors, ground, etc. : http://www.lucadentella.it/en/2014/08/01/interruttore-a-tre-posizioni-e-arduino/


For now, I have an example for you with the 3rd rotary encoder (Z axis), look it over and you'll see what I did to add it, it's pretty easy even for a novice because it's literally a copy of X and Y.

It does matter what board you are using, this is working example with all three rotary encoders, but not the 3-way switch yet -- this sketch assumes that on your board the 9 & 10 pins are Interrupt pins (refer to the pinout for your board if this sketch doesn't work out of the box).  I didn't test it, so you can do that.

https://pastebin.com/gEWNgm83
 

spoilerIMG2415905122018.PNG
/* Modified HSI Knobs Sketch for Falcon BMS / DCS / FSX
 *  with additional 3rd Rotary Encoder (Z Axis)
 *    *(coming next: a 3-way Toggle Switch add-on)
 *  for Arduino Micro/Leonardo / Sparkfun Pro Micro or equiv. clones
 * by SemlerPDX Aug2019
 * VETERANS-GAMING.COM
 * ( in response to reply at:
 *    http://veterans-gaming.com/index.php?/blogs/entry/32-diy-custom-game-controller-2-dial-hsi-course-and-heading-knobs/ )
 *  
 *  Pins:
 *  Rotary Encoder 1 - (OUTA-OUTB-SW) = Arduino Pins (0,1,15)
 *  Rotary Encoder 2 - (OUTA-OUTB-SW) = Arduino Pins (2,3,6)
 *  Rotary Encoder 3 - (OUTA-OUTB-SW) = Arduino Pins (9,10,7)
 *  
 *  Encoder Library
 * http://www.pjrc.com/teensy/td_libs_Encoder.html
 * 
 *  Joystick Library 
 * by Matthew Heironimus
 * https://github.com/MHeironimus/ArduinoJoystickLibrary
 */

#define ENCODER_USE_INTERRUPTS
#define ENCODER_OPTIMIZE_INTERRUPTS
#include <Encoder.h>
#include <Joystick.h>

//Tell the Encoder Library which pins have encoders
Encoder axisXRotation(0, 1);
Encoder axisYRotation(2, 3);
Encoder axisZRotation(9, 10);

//Rotary Encoder Push Button Pins
int buttonArray[3] = {15, 6, 7};

//Rotary Encoder Interrupt Pins
int EncoderPin0 = 0;
int EncoderPin1 = 1;
int EncoderPin2 = 2;
int EncoderPin3 = 3;
int EncoderPin4 = 9;
int EncoderPin5 = 10;

//Delay Time between loops
int debounceDelay = 260;

//Variables to compare current to old values
int oldX = 0;
int oldY = 0;
int oldZ = 0;
int RxAxis_Value = 1;
int RyAxis_Value = 1;
int RzAxis_Value = 1;

//Intervals for Jump/Warp Speed Rotations
int JumpSpeed = 18;
int WarpSpeed = 30;

//Set generic joystick with id 42 with 3 buttons and 3 axes
Joystick_ Joystick(0x42, 
  0x04, 3, 0,
  false, false, false, true, true, true,
  false, false, false, false, false);  


void setup() { 

  //Set Encoder Pins as Pullups
  pinMode(EncoderPin0, INPUT_PULLUP);
  pinMode(EncoderPin1, INPUT_PULLUP);
  pinMode(EncoderPin2, INPUT_PULLUP);
  pinMode(EncoderPin3, INPUT_PULLUP);
  pinMode(EncoderPin4, INPUT_PULLUP);
  pinMode(EncoderPin5, INPUT_PULLUP);

  //Loop through buttons and set them as Pullups
  for(int x = 0; x < sizeof(buttonArray); x++) {
    pinMode(buttonArray[x], INPUT_PULLUP);
  }

  //Set Range of custom Axes
  Joystick.setRxAxisRange(0, 359);
  Joystick.setRyAxisRange(0, 359);
  Joystick.setRzAxisRange(0, 359);
  
  // Initialize Joystick Library
  Joystick.begin(false);

}


void loop() {

  // Loop through button pin values & set to Joystick
  for (int x = 0; x < sizeof(buttonArray); x++) {
    byte currentButtonState = !digitalRead(buttonArray[x]);
    Joystick.setButton(x, currentButtonState);
  }


  // Read "Heading" X Axis Rotation Encoder Knob
  int newX = axisXRotation.read();
  if (newX > oldX) {
    //Determine speed of increment & set output
    int difX = newX - oldX;
    RxAxis_Value = speedVal(difX, RxAxis_Value, 1);
    Joystick.setRxAxis(RxAxis_Value);
    axisXRotation.write(newX);
    oldX = newX;

  }else if (newX < oldX) {
    //Determine speed of decrement & set output
    int difX = oldX - newX;
    RxAxis_Value = speedVal(difX, RxAxis_Value, 0);
    Joystick.setRxAxis(RxAxis_Value);
    axisXRotation.write(newX);
    oldX = newX;
  }


  // Read "Course" Y Axis Rotation Encoder Knob
  int newY = axisYRotation.read();
  if (newY > oldY) {
    //Determine speed of increment & set output
    int difY = newY - oldY;
    RyAxis_Value = speedVal(difY, RyAxis_Value, 1);
    Joystick.setRyAxis(RyAxis_Value);
    axisYRotation.write(newY);
    oldY = newY;

  }else if (newY < oldY) {
    //Determine speed of decrement & set output
    int difY = oldY - newY;
    RyAxis_Value = speedVal(difY, RyAxis_Value, 0);
    Joystick.setRyAxis(RyAxis_Value);
    axisYRotation.write(newY);
    oldY = newY;
  }

 
  // Read "QNH" Z Axis Rotation Encoder Knob
  int newZ = axisZRotation.read();
  if (newZ > oldZ) {
    //Determine speed of increment & set output
    int difZ = newZ - oldZ;
    RzAxis_Value = speedVal(difZ, RzAxis_Value, 1);
    Joystick.setRzAxis(RzAxis_Value);
    axisZRotation.write(newZ);
    oldZ = newZ;

  }else if (newZ < oldZ) {
    //Determine speed of decrement & set output
    int difZ = oldZ - newZ;
    RzAxis_Value = speedVal(difZ, RzAxis_Value, 0);
    Joystick.setRzAxis(RzAxis_Value);
    axisZRotation.write(newZ);
    oldZ = newZ;
  }


  //Send Joystick info through USB
  Joystick.sendState();
  delay(debounceDelay);
}


//Function to set Rotation value adjusted for the turning speed
int speedVal(int dif, int val, int dir){
  if (dif >= WarpSpeed) {
    if (dir == 1) {
      val = val + WarpSpeed;
    }else{
      val = val - WarpSpeed;
    }
  }else if (dif >= JumpSpeed) {
    if (dir == 1) {
      val = val + JumpSpeed;
    }else{
      val = val - JumpSpeed;
    }
  }else{
    if (dir == 1) {
      val = val + 1;
    }else{
      val = val - 1;
    }
  }
  //Correct Rotation within 360 deg.
  if (val < 0) {
    val = val + 360;
  }else if (val >= 360) {        
    val = val - 360;
  }
  return val;
}

 


:hi: 

Edited by =VG= SemlerPDX

Share this comment


Link to comment

Semler, thank you so much for all the time you spent for me. I really appreciate!

I tried this sketch but a found some issues.

First, when i verified the code, the software triggered a problem with "'int buttonArray[2]'" Maybe because there are 3 buttons now. Anyway i replaced with 3 and no more messages.

Then, the 3rd axis is well recognized frow Window game controler but won't moving. The rotary is wired to pin 9 and pin 10 according to your sketch. I tried as well to swap rotaries to make sure, but still won't moving... In attached files 2 pictures of my project. To be more clear, i removed all switches. Only rotaries still connected.

Best regards.

Arnaud.image1.thumb.jpeg.d01146f510eb8726f9ebaf15a77c4d95.jpegIMG_3569.thumb.jpg.dec8d5a6427e07a4cb0e7dc5b222f763.jpg

  • Upvote 1

Share this comment


Link to comment

Thanks for the heads up on the button array - I literally forgot to increment the size of the array.  Didn't test (and it's late). I've edited my reply so we don't trip up the next guy who goes through all this. Cheers!

If the new rotary isn't working then those pins are not Interrupt pins on that board (looks like the generic Pro Micro like I used), I literally took a shot in the dark based on what I now assume was an incorrect pinout on google images.

I've looked it up proper now, and for the record, I was mistaken - there are only 5 interrupt pins on the pro-micro.  3 rotary encoders in the method I have used in my DIY would require 6 total.

Quote

The Pro Micro has five external interrupts, which allow you to instantly trigger a function when a pin goes either high or low (or both). If you attach an interrupt to an interrupt-enabled pin, you'll need to know the specific interrupt that pin triggers: pin 3 maps to interrupt 0 (INT0), pin 2 is interrupt 1 (INT1), pin 0 is interrupt 2 (INT2), pin 1 is interrupt 3 (INT3), and pin 7 is interrupt 4 (INT6).

(quoted from: https://learn.sparkfun.com/tutorials/pro-micro--fio-v3-hookup-guide/hardware-overview-pro-micro )

There are other equally affordable boards that have more, this Pro Micro is just designed for much smaller projects.  The name brand Arduino Micro shown in the top most image has 6 or 8 interrupts (off the top of my head), but is a bit pricey for a DIY at around $18.

I've found a slightly cheaper version here that has 8 interrupt pins and could handle your modified sketch.  Otherwise, if you bought a 3 pack of those just like I did in my DIY, you could get away with (if you have 2 usb ports open on your PC) doing 2 controllers to save money.  You'd just use the same sketch but increment the Joystick ID from 0x42 to 0x43.  But for $14, might as well do it right, it's up to you.  Just remember for future, you can hang on to old gear for new projects later, you never know.

https://www.amazon.com/Gowoops-Arduino-Leonardo-Controller-ATmega32u4/dp/B00J1X5B7Y/

*Leonardo would be required for the total of 3 rotary encoders, as it has enough interrupt pins for the task:
https://www.amazon.com/Solu-Leonardo-Compatible-Revision-Atmega32u4/dp/B00R237VGO/

Edited by =VG= SemlerPDX

Share this comment


Link to comment

*Now that I thought about it, I wonder if you need all the functions of the HSI buttons (Warp Speed/Jump Speed) and could go analog with the QNH, 1 step at a time, with a different method.  I'll think about it over the next few days.  It's a matter of talking to the encoder library or joystick library in some way to enact a change, and within those two libraries there may be functions to get around the interrupts, I just didn't do it for my method.

We'll see.
Some additional reading I'll review, you might too, on Rotary Encoders: https://dronebotworkshop.com/rotary-encoders-arduino/

Share this comment


Link to comment

@Arnaud  Just a quick update for you:  I have not had a lot of time to spend on this, I've been busy and I'm not really a pro at this stuff, so I'm far less helpful than posting these questions on one of the micro-controller discussion forums.  BUT I have read a bunch of them, and I went back to review a few.  There are others who've wanted to use tons of rotary encoders, even asking if it's possible to do them in a matrix array like a button board does.  In the end, I think the most helpful page I landed on discussed changing any pin into an interrupt. 

Now, I've not fully wrapped my head around it yet, as I am again busy and when I'm done, I just want to veg and play mindless games for now, but I wanted to share this with you in case you have the drive to chase it down yourself.  Aside from buying the proper board for the task, something with enough interrupt pins to work out of the box for the project, it might be possible to get creative, but it will require you to learn even more about this topic to make your idea work with your current gear.

Here's the link - best wishes and good luck! I may not be able to help you finish your project, but if you have any simple questions, or need to know something else about my method, feel free to ask!
https://playground.arduino.cc/Main/PinChangeInterrupt/

Share this comment


Link to comment

*For your three way toggle switch, set it up as buttons in the same way the encoder buttons are set - give each pole a pin, follow the circuit diagram for where to put resistors/ground/etc., and play around with it until each flip of the switch causes a single button press of whatever 3 buttons that is set to.  Read the instructions on the Joystick Library link, it shows the format for the VOID SETUP bit for setting a generic joystick with so many axes and so many buttons, and add three to the buttons count for your switch.  Other than that, you will need to play around and learn a bit to get it to work as I'll be unavailable and you'll not want to wait for me to work it out for you.  Tons of info on this on flight sim forums of cockpit builders, I'm not the best source for info on that.  Cheers!

Share this comment


Link to comment

Thank you so much of what you did for me, i really appreciate your endeavor! I keep in mind all advises that you teach to me.

Thanks a lot Semler,

Arnaud.

  • Like 1

Share this comment


Link to comment

I had a bit more time to spend on this, so I played around with adding in a 3-way toggle switch as buttons, and also using a rotary encoder without interrupt pins.  It's just not accurate enough, and that's probably why the interrupts are used for methods like this.

But I did get a working example of the 3-way Toggle Switch for you.  Works well, I've tested it.  There is a debounce delay for the toggle, increase it slightly if the keypress is too fast, but it works for me as is.

If you haven't already learned this, for the toggle you'll need to wire a voltage divider, long story short follow this image and you'll be good.  The red wire goes to Ground, the yellow wire goes to an Analog pin on your board, and also a 10k Ohm resistor connects it to voltage 3V/5V, whatever.  The blue wire goes through a 10k Ohm resistor to Ground.  (See Black ground wire on rail / white voltage wire on rail)

three_way_toggle_wiring_arduino.png

 

And here's the sketch with 3 Rotation Axes and the Toggle:
https://pastebin.com/sJQEAA8a

spoilerIMG2415905122018.PNG
/* Modified HSI Knobs Sketch for Falcon BMS / DCS / FSX
 *  with additional 3rd Rotary Encoder (Z Axis)
 *    also with a 3-way Toggle Switch as buttons
 *  for Arduino Leonardo or equiv. clones
 * by SemlerPDX Sep2019
 * VETERANS-GAMING.COM
 * ( in response to reply at:
 *    http://veterans-gaming.com/index.php?/blogs/entry/32-diy-custom-game-controller-2-dial-hsi-course-and-heading-knobs/ )
 *  
 *  Rotary Encoders need 2 Intterupt Pins Each - Choose a board that has enough like Arduino Leonardo (or clones)
 *    info on Interrupts: https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/
 *  
 *  Pins:
 *  Rotary Encoder 1 - (OUTA-OUTB-SW) = Arduino Pins (0,1,15)
 *  Rotary Encoder 2 - (OUTA-OUTB-SW) = Arduino Pins (2,3,6)
 *  Rotary Encoder 3 - (OUTA-OUTB-SW) = Arduino Pins (9,10,7)
 *  
 *  Three-Way Toggle - (Center Post)  = Analog Input (A1)
 *  
 *  Three-Way Toggle Switch Wiring Example:
 * http://veterans-gaming.com/uploads/monthly_2019_09/three_way_toggle_wiring_arduino.png.1524289e8f56690c7ff6d9f321904de9.png
 *  
 *  Encoder Library
 * http://www.pjrc.com/teensy/td_libs_Encoder.html
 *
 *  Joystick Library
 * by Matthew Heironimus
 * https://github.com/MHeironimus/ArduinoJoystickLibrary
 */
#define ENCODER_USE_INTERRUPTS
#define ENCODER_OPTIMIZE_INTERRUPTS
#include <Encoder.h>
#include <Joystick.h>

//Three Way Toggle Init
#define TOGGLE_PIN A1
int previousState;
bool aToggled;

//Tell the Encoder Library which pins have encoders
Encoder axisXRotation(0, 1);
Encoder axisYRotation(2, 3);
Encoder axisZRotation(9, 10);

//Rotary Encoder Push Button Pins
//  *further buttons added must increment after these (these are 0,1,2) (buttons 1,2 and 3 on controller)
int buttonArray[3] = {15, 6, 7};

//Set Toggle Positions as Buttons* (buttons start at 0,1,2,3,4,5 for 6 buttons)
int ToggleButton1 = 3; //button 4 on controller
int ToggleButton2 = 4; //button 5 on controller
int ToggleButton3 = 5; //button 6 on controller

//Rotary Encoder Interrupt Pins
int EncoderPin0 = 0;
int EncoderPin1 = 1;
int EncoderPin2 = 2;
int EncoderPin3 = 3;
int EncoderPin4 = 9;   //*Must have board with 6 total interrupt pins
int EncoderPin5 = 10;  //*Must have board with 6 total interrupt pins

//Delay Time between loops
int debounceDelay = 260;

//Delay Time before button release
int toggleDebounce = 10;

//Variables to compare current to old values
int oldX = 0;
int oldY = 0;
int oldZ = 0;
int RxAxis_Value = 1;
int RyAxis_Value = 1;
int RzAxis_Value = 1;

//Intervals for Jump/Warp Speed Rotations
int JumpSpeed = 18;
int WarpSpeed = 30;

//Set generic joystick with id 42 with 6 buttons and 3 axes
Joystick_ Joystick(0x42, 
  0x04, 6, 0,
  false, false, false, true, true, true,
  false, false, false, false, false);  


//Function to translate Three-way Toggle Analog Value
int getToggleState(int aVal) {
  if(aVal < 100) {
    aVal = ToggleButton1;
  }else if(aVal < 900) {
    aVal = ToggleButton3;
  }else {
    aVal = ToggleButton2;
  }
  return aVal;
}

//Function to set Rotation value adjusted for the turning speed
int speedVal(int dif, int val, int dir){
  if (dif >= WarpSpeed) {
    if (dir == 1) {
      val = val + WarpSpeed;
    }else{
      val = val - WarpSpeed;
    }
  }else if (dif >= JumpSpeed) {
    if (dir == 1) {
      val = val + JumpSpeed;
    }else{
      val = val - JumpSpeed;
    }
  }else{
    if (dir == 1) {
      val = val + 1;
    }else{
      val = val - 1;
    }
  }
  //Correct Rotation within 360 deg.
  if (val < 0) {
    val = val + 360;
  }else if (val >= 360) {        
    val = val - 360;
  }
  return val;
}


void setup() { 
  
  //Toggle Switch Setup
  previousState = 1000;
  aToggled = false;

  //Set Encoder Pins as Pullups
  pinMode(EncoderPin0, INPUT_PULLUP);
  pinMode(EncoderPin1, INPUT_PULLUP);
  pinMode(EncoderPin2, INPUT_PULLUP);
  pinMode(EncoderPin3, INPUT_PULLUP);
  pinMode(EncoderPin4, INPUT_PULLUP);
  pinMode(EncoderPin5, INPUT_PULLUP);

  //Loop through buttons and set them as Pullups
  for(int x = 0; x < sizeof(buttonArray); x++) {
    pinMode(buttonArray[x], INPUT_PULLUP);
  }

  //Set Range of custom Axes
  Joystick.setRxAxisRange(0, 359);
  Joystick.setRyAxisRange(0, 359);
  Joystick.setRzAxisRange(0, 359);
  
  // Initialize Joystick Library
  Joystick.begin(false);

}


void loop() {

  // Loop through button pin values & set to Joystick
  for (int x = 0; x < sizeof(buttonArray); x++) {
    byte currentButtonState = !digitalRead(buttonArray[x]);
    Joystick.setButton(x, currentButtonState);
  }


  //Read Three Way Toggle
  int analogValue = analogRead(TOGGLE_PIN);
  int actualState = getToggleState(analogValue);
  if(previousState != actualState) {
    //Set Toggle Switch input as Button Press
    Joystick.setButton(actualState, 1);
    previousState = actualState;
    aToggled = true;
    
  }else {
    //Reset button(s) to unpressed state
    if (aToggled) {
      aToggled = false;
      delay (toggleDebounce);
      for (int a = 3; a < 6; a++) {
        Joystick.setButton(a, 0);
      }
    }
  }
  

  // Read "Heading" X Axis Rotation Encoder Knob
  int newX = axisXRotation.read();
  if (newX > oldX) {
    //Determine speed of increment & set output
    int difX = newX - oldX;
    RxAxis_Value = speedVal(difX, RxAxis_Value, 1);
    Joystick.setRxAxis(RxAxis_Value);
    axisXRotation.write(newX);
    oldX = newX;

  }else if (newX < oldX) {
    //Determine speed of decrement & set output
    int difX = oldX - newX;
    RxAxis_Value = speedVal(difX, RxAxis_Value, 0);
    Joystick.setRxAxis(RxAxis_Value);
    axisXRotation.write(newX);
    oldX = newX;
  }


  // Read "Course" Y Axis Rotation Encoder Knob
  int newY = axisYRotation.read();
  if (newY > oldY) {
    //Determine speed of increment & set output
    int difY = newY - oldY;
    RyAxis_Value = speedVal(difY, RyAxis_Value, 1);
    Joystick.setRyAxis(RyAxis_Value);
    axisYRotation.write(newY);
    oldY = newY;

  }else if (newY < oldY) {
    //Determine speed of decrement & set output
    int difY = oldY - newY;
    RyAxis_Value = speedVal(difY, RyAxis_Value, 0);
    Joystick.setRyAxis(RyAxis_Value);
    axisYRotation.write(newY);
    oldY = newY;
  }

 
  // Read "QNH" Z Axis Rotation Encoder Knob
  int newZ = axisZRotation.read();
  if (newZ > oldZ) {
    //Determine speed of increment & set output
    int difZ = newZ - oldZ;
    RzAxis_Value = speedVal(difZ, RzAxis_Value, 1);
    Joystick.setRzAxis(RzAxis_Value);
    axisZRotation.write(newZ);
    oldZ = newZ;

  }else if (newZ < oldZ) {
    //Determine speed of decrement & set output
    int difZ = oldZ - newZ;
    RzAxis_Value = speedVal(difZ, RzAxis_Value, 0);
    Joystick.setRzAxis(RzAxis_Value);
    axisZRotation.write(newZ);
    oldZ = newZ;
  }


  //Send Joystick info through USB
  Joystick.sendState();
  delay(debounceDelay);
}

 

 

:hi:

  • Upvote 1

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×