Donkey’s cockpit is further improving with the addition of a Ka-50 Autopilot panel with LEDS to display their status.
The project is based on the usual firmware I usually use as base (you can find it in the Download page) that provides the necessary code to control a button matrix and rotary encoders. From there, I expanded it to include DCS-BIOS.
DCS-BIOS allows Arduino to retrieve the status of the in-game cockpit, and the obtained variables can be used in a number of ways. For instance, I used DCS-BIOS for my Ka-50 TFT; a fellow pilot from the 132nd is using it to build an A-10C cockpit.
Why both HID and DCS-BIOS?
That’s a good question. DCS-BIOS can both receive and send commands to DCS so why bother using a more expensive board (Arduino Uno, for instance, is cheaper than Leonardo) and two means of sending data to the PC? Simple answer: flexibility.
HID means that the panel can be used with any game and DCS module. The LEDs won’t work but the buttons will do. Also, more modules can be supported by DCS-BIOS at the same time with a bit of black magic (as some friends call “coding” 🙂 ). Therefore, for instance, the LEDs can represent the status of the flaps in the F/A-18C then, by changing slots and without even leaving the mission or server, they can immediately work as the Autopilots in the Ka-50. Thanks to HID, the buttons can be assigned to any function of DCS rapidly and hassle-free.
The Project
This panel uses an Arduino Leonardo, as usual based on ATmega32U4. The wiring diagram is extremely simple and similar to other previous projects you can find in my Articles page.
The button matrix is 3×2 in order to control 5 pushbuttons. Each button has an integrated 5V LED, powered and controlled by the board.
This is a sketch of the wiring diagram. For ease of drawing, some busses look like as if they were parallel but they are actually series. You can find more details about how the button matrix work in my step-by-step guide.

Resistor are not represented in the diagram but Donkey has used 220Ω resistors in series with the diodes.
Light-Emitting Diodes (LED)
Donkey is using 5V LEDs for this project and they will be powered directly by the board. For more complex project an external power source is recommended.
There are infinite tutorials about how to use LEDs with Arduino so I leave it to Google. The point I want to stress though is that LEDs may look symmetrical, but there are not! LEDs have anode and cathode and the polarity must be respected in order to avoid potential damage to the LED and the board.
Take a look at this diagram from Arduino’s website:

Do you see the LED? It’s not symmetrical, there is a protuberance on one side. Look at the inside of the LED, again, it’s not symmetrical. Lastly, one “leg” is usually longer than the other and it’s usually represented as bent. All these three features indicate how the LED should be connected: as a rule of thumb, the part with more features indicates the “+” or anode, less features indicate the “-” or cathode. I know, this is another of my silly rules to remember things like CCRP/CCIP in the RIO seat but hey, usually they work 🙂
Coding with DCS-BIOS. First steps
Installing DCS-BIOS and including the necessary libraries is easy and well explained in the dev guide therefore I am not covering this topic.
To start developing with DCS-BIOS we need to include its library first:
#include "DcsBios.h"
Then add in loop and setup:
void setup(){
DcsBios::setup();
/*[..]*/
}
void loop() {
DcsBios::loop();
/*[..]*/
}
These two methods initialize and update DCS-BIOS. DCS-BIOS doesn’t work (the source will not even compile) or fails to update if any of these parts is missing.
Almost done! IRQ vs DEFAULT
We have to define if Arduino is either using IRQ or DEFAULT serial. From the User Guide:
Before including DcsBios.h, you have to define a preprocessor macro that tells the DCS-BIOS Arduino Library what mode it should operate in. If you are using an Arduino board that has either an ATMega328 or an ATMega2560 chip, such as the Uno, Pro Mini, Nano or Mega 2650 boards, use DCSBIOS_IRQ_SERIAL. If your board has a different microcontroller, you can use DCSBIOS_DEFAULT_SERIAL instead, which should work with any Arduino-compatible board but can cause problems if your sketch spends a long time updating outputs, either because you are outputting to something “slow” like displays or you are using a lot of outputs (such as trying to run a Caution Lights Panel with 48 instances of DcsBios::LED).
In my TFT project I use an Arduino Uno, hence I use IRQ_SERIAL. In Donkey’s project we are using DEFAULT_SERIAL because the board is an Arduino Leonardo based on the ATmega32u4. Long updates are not really an issue because we are simply displaying the status of the AP channels.
Therefore, let’s add DCSBIOS_DEFAULT_SERIAL along other preprocessor directives:
#define DCSBIOS_IRQ_SERIAL
This is how the full code looks like; this is a screenshot from the example “DefaultSerial” (Arduino IDE: Files→Examples→DCS-BIOS).

Finally some coding!
You couldn’t wait, don’t you?!
Let’s start by defining the pins used by the LEDs:
#define LED_ALTHOLD 2
#define LED_BANKHOLD 3
#define LED_HDGHLD 4
#define LED_PITCHHLD 5
#define LED_FLIGHTDIR 6
#define is a simple yet powerful tool, it allows to define a macro straight into the preprocessor therefore saving memory. Having worked for years on ancient 8bit microcontrollers, I always consider memory as a luxury (although I admit we have plenty here so I try to don’t be paranoid over it) so I love the preprocessor and I prefer #define over const. But I’m going Off Topic here.
Next step is telling the board that the pins used by the LEDs are now OUTPUTs, whereas, for instance, the button matrix buttons’ are INPUTs:
void setup() {
DcsBios::setup();
Joystick.begin();
rotary_init();
pinMode(LED_ALTHOLD, OUTPUT);
pinMode(LED_BANKHOLD, OUTPUT);
pinMode(LED_HDGHLD, OUTPUT);
pinMode(LED_PITCHHLD, OUTPUT);
pinMode(LED_FLIGHTDIR, OUTPUT);
}
Finally, we can include the actual DCS-BIOS methods. Since we are working directly on a pin, we can use the following methods:
DcsBios::LED apAltHoldLed(0x1936, 0x8000, LED_ALTHOLD);
DcsBios::LED apBankHoldLed(0x1936, 0x0200, LED_BANKHOLD);
DcsBios::LED apFdLed(0x1938, 0x0200, LED_FLIGHTDIR);
DcsBios::LED apHdgHoldLed(0x1936, 0x0800, LED_HDGHLD);
DcsBios::LED apPitchHoldLed(0x1936, 0x2000, LED_PITCHHLD);
My TFT Project required instead to draw and update the screen, I had to use the alternative method. For instance, from the guide sample:
void onApPitchHoldBtnChange(unsigned int newValue) {
/* your code here */
}
DcsBios::IntegerBuffer apPitchHoldBtnBuffer(0x1936, 0x1000, 12, onApPitchHoldBtnChange);
The complete list of methods and function is available here. This is unfortunately obsolete, last update is DCS-BIOS 0.7.0. More recent versions are available and support more modules, even the recent F-14B.
This is a video of the first test of panel. It makes passing from Forward Flight to Holding Pattern to Hover much, much easier because the AP channels are easy to operate and their status is clearly visible. For instance, at ~11″ Donkey is about to establish a left Holding Pattern: operating the in-game buttons would require much longer and distract him from flying the helicopter.
That’s all, folks!
I have not covered the necessary steps to adjust the button matrix in this article because it is a topic I have already discussed it multiple times.
The firmware is available in the Download section (Please note that I kept parts of code relative to other features such as rotary encoders in order to make further upgrades easier).
Download it and feel free to adjust it to your needs. Happy coding!