Jump to content
  • entries
    6
  • comments
    97
  • views
    13,353

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


=VG= SemlerPDX

18,720 views


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! 

42 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
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
 

(click to reveal)
/* 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: 

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
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/

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/

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/

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!

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

(click to reveal)
/* 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:
 *    https://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:
 * https://veterans-gaming.com/uploads/monthly_2019_09/three_way_toggle_wiring_arduino.png.1524289e8f56690c7ff6d9f321904de9.png
 *  
 *  Encoder Library
 * https://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;

//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

//Tell the Encoder Library which pins have encoders
Encoder axisXRotation(EncoderPin0, EncoderPin1);
Encoder axisYRotation(EncoderPin2, EncoderPin3);
Encoder axisZRotation(EncoderPin4, EncoderPin5);

//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){
  int increment = 1;
  if (dif >= WarpSpeed) {
    increment = WarpSpeed;
  }else if (dif >= JumpSpeed) {
    increment = JumpSpeed;
  }
  if (dir == 1) {
    val = val + increment;
  }else{
    val = val - increment;
  }
  //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
Link to comment

Great Job! I want to do a dual concentric rotary encoder, connected to a pro micro making it a HID/USB device like the “Knobster” created by Russ Barlow below.  It looks like your two single switched encoders would work almost “out of the box”!
Do you have or have a link to a wiring diagram?

Thanks!

Chas. 

 

  • Like 1
Link to comment
9 hours ago, Windblow said:

I just discover this interesting project Semler. I need to relearn some basics  with Arduino, but it seems to be accessible for me.

I'm gonna redo this at some point, when I can create a small frame for the rotary encoders with a single bundle of wires coming out, and going to another small unit inline on the cord to hold the Arduino.  This will keep the profile of the on-screen unit as small as it can be.  Running the cord out at a "T" intersection like that using a magnetic adapter can cause it to disconnect if the keyboard gets pushed into it, so I'll be revisiting this project sometime this year.

Link to comment
On 4/18/2021 at 2:44 AM, Windblow said:

I made it, first try today and it seems to work as intended

Thanks a lot for sharing  your work and sketch. I need now to make a nice box and maybe try to add a third one for the altimeter

😃

5552967F-B2DA-41C9-8748-1723090162A4.jpg

B9F49AD7-462D-425F-ADD6-09BCFF8A94F6.jpg

Somehow missed this reply until just now...


That's very awesome, man!! Well done!  From the outside, some of this stuff seems very hard to get into, but literally just diving in and playing around with the bits and some tutorials / videos is the best way!

(Stuff is cheap as hell, too - if something fried, it's like losing a lunch, not a monthly rent payment like our computer parts ;) )

 :drinks: 

  • Like 1
  • Upvote 1
Link to comment
10 minutes ago, Hakan GÖKMEN said:

How can i see project code? 

Link is broken. Can u re share 

It's the image that used to be part of the 'spoiler tag' which is broken.  If you click the caret to its left it will still reveal - also the link directly above these is valid, goes to my pastebin where this code is still displayed:

Original: https://pastebin.com/drUnCfKN

Alternate (3 rotary encoders + 3-way toggle switch): https://pastebin.com/sJQEAA8a

Link to comment

Hello and Good day to everyone .

i Found this post while searching for ways to add HSI knobs and a QNH knob to my devices to control Falcon BMS.

I was very excited to see that the method employed Arduino hardware as I have recently begun to learn some basic coding in hopes of making devices for my desktop cockpit.

I purchased a Leonardo board and some rotary encoders ( the ones mounted on PCBs with resistors ) I did a test run first  with the 2 encoder sketch and it worked flawlessly. I tested it in BMS 4.36 U3 and It was perfect.

Next I set up for a test using the sketch titled "Modified HSI Knobs Sketch for Falcon BMS / DCS / FSX  with additional 3rd Rotary encoder (Z Axis) and this is where my issue came to light.

With this setup all 3 push buttons are working as are the X and Y axes but the Z axis is behaving differently. With the Z axis knob It will not warp speed jump as do the X and Y axes and will only move in the positive direction (adding numbers)

no matter which direction the knob is turned. This encoder for the Y axis is connected to leonardo Pins 9,10 and 7.  I have swapped out encoders an have the same result. I have swapped the CLK (A) and DT (B) wires and that changed just the direction but likewise  all of the outputs are descending no matter whether the knob is turned clockwise or counter clockwise.  The test in BMS was the same HDG and CRS inputs are functioning  but the ALT (QNH) knob only adds and will not dial down.

Before I started this project I, updated the Arduino IDE to 1.819 on my window 7 64 desktop Pc.  Downloaded and installed the listed Encoder and joystick library s and read all of the linked posts and information in the above post.  i have also recopied the code to be certain I did not use the wrong sketch or compromise it in some way. I would like to eventually include the 3 way toggle switch but I cant continue until I get the Z axis up and stable.

Here are my questions:

With the info I have provided does anyone have any ideas what I can check next?  or what should be changed ? What am I missing here? 

 I saw something about Paypal at the donation page but it is not a link I can click. Can I donate through paypal? 

 Thank you for this post and all the work to share this with people. This is very exciting to me.

 

Any help with this will be greatly appreciated.

Thank You

 

 

Link to comment
3 hours ago, Kaymon said:

Hello and Good day to everyone .

i Found this post while searching for ways to add HSI knobs and a QNH knob to my devices to control Falcon BMS.

I was very excited to see that the method employed Arduino hardware as I have recently begun to learn some basic coding in hopes of making devices for my desktop cockpit.

I purchased a Leonardo board and some rotary encoders ( the ones mounted on PCBs with resistors ) I did a test run first  with the 2 encoder sketch and it worked flawlessly. I tested it in BMS 4.36 U3 and It was perfect.

Next I set up for a test using the sketch titled "Modified HSI Knobs Sketch for Falcon BMS / DCS / FSX  with additional 3rd Rotary encoder (Z Axis) and this is where my issue came to light.

With this setup all 3 push buttons are working as are the X and Y axes but the Z axis is behaving differently. With the Z axis knob It will not warp speed jump as do the X and Y axes and will only move in the positive direction (adding numbers)

no matter which direction the knob is turned. This encoder for the Y axis is connected to leonardo Pins 9,10 and 7.  I have swapped out encoders an have the same result. I have swapped the CLK (A) and DT (B) wires and that changed just the direction but likewise  all of the outputs are descending no matter whether the knob is turned clockwise or counter clockwise.  The test in BMS was the same HDG and CRS inputs are functioning  but the ALT (QNH) knob only adds and will not dial down.

Before I started this project I, updated the Arduino IDE to 1.819 on my window 7 64 desktop Pc.  Downloaded and installed the listed Encoder and joystick library s and read all of the linked posts and information in the above post.  i have also recopied the code to be certain I did not use the wrong sketch or compromise it in some way. I would like to eventually include the 3 way toggle switch but I cant continue until I get the Z axis up and stable.

Here are my questions:

With the info I have provided does anyone have any ideas what I can check next?  or what should be changed ? What am I missing here? 

 I saw something about Paypal at the donation page but it is not a link I can click. Can I donate through paypal? 

 Thank you for this post and all the work to share this with people. This is very exciting to me.

 

Any help with this will be greatly appreciated.

Thank You

 

 

I would first need to review the code you are using.  I realize you got it from *my* sources here, but there could be an earlier version floating around, and I'd need to eliminate such variables to troubleshoot your issue.  Can you please link to the exact code you are using, or use a code block in a reply here to paste it that way?  Once I can get eyes on what you're using, I can begin asking other important questions.  I don't own a Leonardo, so there's only so much I can try to replicate.  :coffee:

-Sem

  • Thanks 1
Link to comment
On 9/6/2019 at 3:41 AM, =VG= SemlerPDX said:

 


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, 
https://pastebin.com/gEWNgm83
 

(click to reve


:hi: 

Yes Thank you Sem for the rapid response. The sketch I am using was downloaded from the link I have quoted above. I am thankful for the link and your work on it. 

 

Link to comment
4 hours ago, Kaymon said:

Yes Thank you Sem for the rapid response. The sketch I am using was downloaded from the link I have quoted above. I am thankful for the link and your work on it. 

Can you try changing the pins for the 3rd rotary encoder from 9 & 10 to pins 18 & 19 for a test?  Again, I don't have a Leonardo, so I'm taking shots in the dark that perhaps these need to be the same kind of interrupt pins as the rest...  can't hurt to try.  You'd need to adjust the code in your sketch as well as the pins, to correspond, of course - at line 31 and lines 41 & 42

Try that and let me know if that gets the 3rd encoder working correctly on Leonardo :hi: 

Link to comment

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

Terms of Use and Privacy Policy