Yes I haven't posted much on this for some time ... I've been busy with real work for a change, so there's been less time to devote to this
hobby obsession, much less write about it.
For those who've just tuned in, this project is about building a 737 Overhead Panel for my X-Plane Flight Simulator. You can read my earlier posts on this here.
a 737 Overhead Panel (it's at the top of the flightdeck cockpit, over the pilot's heads)
It's now time to give some thought to the electronics behind the overhead panel. Since this is a learning project, I planned to build the whole thing myself -- a decision I've often regretted when things got messy. Yes there are ready made interface boards you can buy, and still have enough fun wiring the thing up, but that isn't why I'm doing this ... it's also an opportunity to figure out what makes things tick. Also while I do have some money to throw into building this, it probably will not stretch to using ready built solutions, which can run into tens of thousands of Malaysian Ringgits. My budget is probably an order of magnitude lower.
The first decision is the choice of microcontroller. Although this is traditionally the domain of arduinos and similar microcontrollers, I decided to go with a Raspberry Pi as the control unit. Why? Because I'm a die-hard Linux geek and am a software person at heart. And also because I keep buying each new raspberry Pi that comes out without really using them for anything.
But seriously, looking at the requirements of the overhead panel, no microcontroller is going to come ready with all the interfaces we need. Regardless of the choice of control unit, there is going to be a need for a lot of additional external interface components that need to be added, and I believe the richer software environment on Linux will help manage the complexity as compared to having to design code that runs within a single event loop as is the case with most arduino-ish solutiions.
Or at least I'm hoping I can use my software skills will make up for my lack of hardware skills.
Interfacing with the Flight Simulator
The flight simulator can already handle USB HID (Human Interface Device -- for example joysticks, keyboards, mice) devices. The first few hardware addons you buy for your simulator such as the yoke, pedals and throttle levers all show up as USB HID devices, and therefore can be recognized by the simulator without any special driver software. A USB HID device "declares" how many buttons (switches that can be in an on-off state) it has and also how many axis (analog devices that report a value within a given range) and LED lights it has. X-Plane already has facilities to map these USB HID buttons to internal commands such as "Lower Landing Gear", and most add on aircraft also make available custom commands for their unique features.
It seems the easiest way to interface my overhead panel would be to have a microcontroller pretend to be a USB HID device, and then use X-Plane to integrate them into the aircraft model. This is the approach most ready-made solutions use. I found two devices that could do this ... the Teensy -- an arduino-like device that can pretend to be any USB device -- and the Raspberry Pi Zero -- which uses the Linux Kernel's USB Gadget interface to do the same over its USB On-the-Go (OTG) port (You can't do this with non-Zero Raspberry Pi's as the built-in USB hub gets in the way of pretending to be a USB device).
The problem I see with this approach is that you can only "map" buttons and switches to commands that are already mappable in X-Plane. It was also not clear to me where I could insert any additional logic processing ... for example, to control servos for the analog dials and so on. There are also no built-in facilities for controlling LED lights. It would probably require writing a custom X-Plane plugin ... but I was more interested in writing code for the Raspberry Pi.
Essentially the Raspberry Pi needs to be able to do two things:
- be aware of changes in the state of the flight simulator and
- be able to affect the state of the flight simulator.
And given that it sits "outside" of my PC and is connected only via the LAN, it needs to be able to accomplish this using a network connection.
In X-Plane, the "state" of things are represented internally using "datarefs". There are published "datarefs" which are values used by the simulator for representing everything in the simulated world. The APIs can read the values in the datarefs and also write values to the datarefs, which in turn will affect things in the simulation. There is probably more that can be done by manipulating X-Plane datarefs than calling X-Plane commands.
The x737 project (a freeware 737-800 add on aircraft for X-Plane) goes a step further in making it easy to build a real cockpit around it -- everything in the cockpit is exposed via a dataref. For example:
- The positions of toggle switches in the cockpit are stored in datarefs, with 0 for off and 1 (or thereabouts) for on. Changing the values in a dataref is the same as toggling the switch in the virtual cockpit.
- The status of annunciators and lights are stored in datarefs, when a light is off the value is 0 and when it is on the value is 1.
So interfacing the overhead panel to the flight simulator is essentially a task of monitoring and modifying dataref values. For example, when the cross feed knob on the overhead panel is turned, we have to change the value of a specific dataref (e.g. "x737/fuelPanel/crossFeed"). The flight simulator logic then reacts like how a 737 would when the knob is turned, and one of the results is that the "Valve Open" annunciator light comes on. This causes another dataref to change value ("x737/fuelPanel/crossFeed_annunc"), so if we monitor this, we know when to turn on the corresponding LED lights on the overhead panel. There are hundreds of datarefs available just for the x737 aircraft.
Side note: There is a useful developer's add-on plugin for X-Plane called DataRef Editor which lets you see (in real time) the values in a dataref, as you manipulate the controls within the aircraft. You can also change the values of a dataref from within this plugin, to see what effect it has on the aircraft.
Although there are many "standard" datarefs in X-Plane, each aircraft developer has extended the system with their own datarefs when there aren't any existing ones that suit the purpose. Therefore, I can't hard-code which datarefs the controls on my overhead panel manipulate, as the datarefs used on the x737 project 737-800 aircraft may be different from the IXEG 737-300 aircraft, for example. While there are some differences between a 737-800 and 737-300 overhead panel, they're functionally equivalent so to be tied to being able to use just one aircraft model would not be acceptable.
To work around this problem, I believe the controller needs to have the "mapping" between hardware switches and datarefs stored in a configuration file, and be able to switch to use a different set of mappings depending on which aircraft is being flown in the simulator. Fortunately, this is not too hard to do, as the model of the aircraft is also stored in a dataref which can be monitored by the controller. So whenever the aircraft model dataref changes, the controller needs to load in the appropriate mappings for the new aircraft. This means that while I'll have some work to do in figuring out the mappings for different aircraft, once its completed, the overhead panel will seamlessly work whenever I switch to a different aircraft.
I figured it would also be useful to have "default" mappings for unrecognized aircraft types (i.e. stuff I haven't written a mapping configuration for yet), where I map to standard x-plane datarefs. This would make common switches such as taxi lights and fuel pumps at least usable with other aircraft (although immersion level drops greatly then, after all a 737 has six fuel pump switches compared to a Cessna 172's single switch).
Keeping the mapping that controls the behavior of the program separate from the program itself in this case is a beneficial design approach, as it lets the program focus on the essentials of the job (the "core engine" so to speak) while leaving enough flexibility to handle different scenarios in the future.
To gain access to the datarefs and set values in them, requires writing an X-Plane plugin. These are written in C/C++ using X-Plane's SDK, although there are also bindings for Python and Lua. Since my Raspberry Pi is connected to the LAN, I needed a plugin that would allow access from the network and translate these into API calls.
Fortunately, there was already an existing plugin which did that, called ExtPlane. ExtPlane is a general plugin to allow networked software to have access to X-Plane's datarefs and commands.
The ExtPlane Plugin was developed to be used with ExtPlane-Panel, a software for creating external cockpit displays for X-Plane that run either on the same PC as X-Plane or on a separate PC. These gauges and elements need access to the datarefs in X-Plane and so the ExtPlane Plugin translates TCP socket network communications into X-Plane API calls. I don't need ExtPlane-Panel as I have a real hardware overhead panel, but the server end that allows some other software on the network to access datarefs and commands is exactly what I need.
A client of the ExtPlane Plugin can request to "subscribe" to a dataref, and it will receive a message over the TCP socket only when the dataref value changes. This can greatly help reduce the overhead of message traffic and processing on the client, as there is no need to continuously poll the flight sim to see all the current dataref values. It also means less processing is required on the Pi. Using the ExtPlane Plugin saves me from having to write my own X-Plane plugin, so that I can focus my efforts more on the integration to the hardware on the Raspberry Pi side of things.
At this point the general concept of the software I need to write on the Pi is starting to take shape. It needs to:
- Monitor the hardware switches and knobs, and whenever something changes, communicate with the ExtPlane Plugin to change a dataref or invoke a command.
- Tell the ExtPlane Plugin subscribe to datarefs of interest, and whenever there is a change in these datarefs, reflect the change on the actual output hardware (annunciators, dials, digit displays).
- Keep the mapping between the overhead panel's hardware and X-Plane's datarefs and commands in an external configuration file, where I can have different mapping for each Aircraft.
It is now an appropriate time to summarize this into a block diagram ...
In my next post I'll look a bit further into how the Raspberry Pi can be interfaced to all kinds of different hardware.