FlightSimInstrument

Flight Sim Instrument Project

Intro

The aim of this project is to basically create an "airplane instrument" (such as an altimeter) for a flight simulator.

/media/uploads/mblokzijl/_scaled_img_0638.jpg

I used a Text LCD display since that was a bit easier than creating a dial, but if you're up for it feel free to create an analog edition! (if you do, post it in the comments :-) )

Essentially, the way this will work is that we'll extract some data (such as the air speed and the altitude) from a flight sim, and display them on a Text LCD display using the mbed. While this would theoretically be possible using the USB to Serial connection provided by the "mbed magic" chip, we'll give the new USB libraries a go! (that's more elegant and less 1969-style, too (as a figure of speech) :-) ).

Now, while some might argue, that Microsoft flight sims provide the best experience, extracting data out of those will be a pain (DLL hooks, anyone?), and beyond the scope of this project. So, we'll pick FlightGear instead, since it's open source, so worst case we can compile the necessary capabilities into the Flight Sim straight away, but as we'll see later, this won't be necessary.

From a "project management" point of view, my aim is to first get a simple prototype up and running quickly, and then expand the features until it's where I want it to be. I'll describe the process of developing the project rather than just stating "here's the end result" (if you want that now though, scroll down to the summary). I'll be updating this notebook as I go along.

This part will be written quite verbosely in a step-by-step way, apologies to the veterans who already know most of the stuff.

Disclaimer

Follow these instructions at your own risk, I've tried to be as careful as I could, but can't be held responsible if you blow something up.

Prerequisites

  • mbed (I'm using the LPC11U24)
  • USB connector - I used a breakout board + standard USB cable.
  • Text LCD display - I'm using the following one
  • FlightGear - you might as well start downloading it now; if you're on linux, check your package manager for 'flightgear', Win/Mac users should check the website for binaries.

You can of course adapt the project as you wish to suit the stuff you've got lying around. I'll be using mostly Linux for this part, but will include some notes for Windows users, too. I think things should work similarly on Mac as on Linux.

Making sure everything works

Wiring things up & testing

First, grab the datasheet for your LCD display (you'll usually find it on the website where you bought it from) and/or use the excellent Text-LCD cookbook page as a guide on how to wire it up.

Connecting the USB port is a bit easier, just connect the USB's D+ & D- to the mbed's D+ and D-, GND to GND and the USB VCC to mbed's Vin.

Next, I imported the USBMouse program and checked the USB wiring, as well as the Text-LCD Hello World program; the latter required me to delete the "mbed" library and import the "m0-beta" library. To do that, you click on "Import" and under "source URL" enter "http://mbed.org/projects/libraries-testing/svn/m0-beta" (this will probably change when the Cortex M0 goes into production).

Once everything's cabled up and tested, we can move on!

/media/uploads/mblokzijl/_scaled_img_0632.jpg

Sending some text from the PC to the mbed

Iteration 1: USBSerial : mbed

Aim: We'll want to send some dummy data from the PC to the mbed, and display it on the LCD screen.

Ok, using USBSerial is only slightly less hacky than using the USB-serial connection provided by the mbed magic chip (see the bottom of your board), but if you don't have an mbed magic chip this might be of interest to you.

The USBDevice library can be found here, and the following is the USBSerial class we'll be using for now:

Import library

Public Member Functions

USBSerial (uint16_t vendor_id=0x1f00, uint16_t product_id=0x2012, uint16_t product_release=0x0001)
Constructor.
virtual int _putc (int c)
Send a character.
virtual int _getc ()
Read a character: blocking.
uint8_t available ()
Check the number of bytes available.
bool writeBlock (uint8_t *buf, uint16_t size)
Write a block of data.
template<typename T >
void attach (T *tptr, void(T::*mptr)(void))
Attach a member function to call when a packet is received.
void attach (void(*fn)(void))
Attach a callback called when a packet is received.

The USBSerial "port" on my computer ended up being the following one:

/dev/serial/by-id/usb-mbed.org_CDC_DEVICE_0123456789-if00

So we'll be using that throughout this project. On Windows, it is COM3 (after installing the driver, as explained on the USBSerial handbook page).

Now, let's create a project, and import the libraries (and of course the m0-beta library):

Import libraryTextLCD

TextLCD library for controlling various LCD panels based on the HD44780 4-bit interface

and

Import libraryUSBDevice

USBDevice stack

Next, let's write some mbed code!

Import programFlightSimInstrument-1

This project is about creating an airplane instrument (such as an altimeter) for a flight simulator running on a PC. The code will read in two space-separated \"words\" containing the altitude and airspeed, with units, from a \"USBSerial port\". See my notebook page for details.

main.cpp

#include "mbed.h"
#include "USBSerial.h"
#include "TextLCD.h"

//an LED to display activity
DigitalOut serial_activity_led(LED2);
//Virtual serial port over USB
USBSerial serial;
//The TextLCD
TextLCD lcd(p21, p22, p17, p18, p19, p20); // rs, e, d4-d7

int main() {
    //initialise the LCD
    lcd.printf("FlightSimInstru\nready!");
    
    //setup some buffers - could use ints, too, but this way we can let the PC decide on the units, use floats etc.
    uint8_t ias[128];
    uint8_t alt[128];
    //clear them
    memset(ias,0,sizeof(ias)*sizeof(*ias));
    memset(alt,0,sizeof(alt)*sizeof(*alt));
    
    //toggle an LED to show us, that the mbed is alive.
    while(1) {
        //read two space separated strings
        serial.scanf("%s %s", ias, alt);
        //write them on the LCD
        lcd.cls();
        lcd.printf("Speed: %s\nAlt: %s", ias, alt);
        //toggle activity LED
        serial_activity_led = !serial_activity_led;
    }
}

This will read two space-separated words from the USBSerial port, display them on the LCD screen next to "Speed" and "Alt", and toggle LED2 every time you send something over the USBSerial port so that you know your mbed is alive. You can test it by executing e.g. the following in your terminal:

% echo "123kt 456ft" > /dev/serial/by-id/usb-mbed.org_CDC_DEVICE_0123456789-if00

On Windows, just connect to the mbed using your favourite terminal program (e.g. Putty). See the SerialPC page for more details. You may not see any characters on the screen as you type them, depending on your settings, but if you type "123kt 456ft" (quotes for clarity) and hit enter, you should see the data on the mbed.

The results should look something like this:

/media/uploads/mblokzijl/_scaled_img_0637.jpg

Now, all we need to do for the simple prototype, is get this data dynamically from something more exciting than a terminal!

Iteration 1: USBSerial : PC - writing data directly to the mbed

FlightGear has a quite nice feature, that allows users to define custom, generic protocols. We'll tell FlightGear to write the speed and altitude in the format that our mbed expects, and pass it the USBSerial port as "destination file" for the data. That way, it will send the data directly to the mbed.

1. Save the following file in $FG_ROOT/Protocol/speedalt.xml ($FG_ROOT is the directory that FlightGear was installed in - for me it's /usr/share/flightgear/data on Linux and C:\Program Files (x86)\FlightGear 2.4.0\data\Protocol on Windows.):

speedalt.xml

<?xml version="1.0"?>
<PropertyList>
 <generic>
   <output>
     <binary_mode>false</binary_mode>
     <var_separator> </var_separator>
     <line_separator>\n</line_separator>
     <preamble></preamble>
     <postamble></postamble>

     <chunk>
       <node>/velocities/airspeed-kt</node>
       <type>int</type>
       <format>%dkt</format>
     </chunk>

     <chunk>
       <node>/position/altitude-ft</node>
       <type>int</type>
       <format>%dft</format>
     </chunk>

   </output>
</generic>
</PropertyList>

It's essentially an XML file, instructing FlightGear to output "<speed in knots>kt <altitude in feet>ft\n" to whatever file you specify on the command line. For more information, see generic protocols.

Now, start Flightgear:

fgfs --generic=file,out,10,/dev/serial/by-id/usb-mbed.org_CDC_DEVICE_0123456789-if00,speedalt

The '10' in the comma separated list specifies that the data should be written at 10Hz. You can of course change this if you like (but I don't think there's much point in sending this kind of data at 100Hz for display purposes ;-) )

On Windows, just launch it from the Start menu, enter the details of your FlightGear installation (see below), follow the wizard's prompts and pick an aircraft and an airport, and at the end click on Advanced..., choose Input/Output, click new and adjust the settings so that they match the following screenshot:

/media/uploads/mblokzijl/_scaled_fg_launch.png /media/uploads/mblokzijl/_scaled_fg_serial_settings.png

And check out your FlightSimInstrument!

/media/uploads/mblokzijl/_scaled_img_0638.jpg

(You might need to use the knob next to the altimeter in FlightGear to initialise it while you're on the runway before you take off)

Iteration 1 summary

Here's the program:

Import programFlightSimInstrument-1

This project is about creating an airplane instrument (such as an altimeter) for a flight simulator running on a PC. The code will read in two space-separated \"words\" containing the altitude and airspeed, with units, from a \"USBSerial port\". See my notebook page for details.

And here's your flightgear command:

fgfs --generic=file,out,10,/dev/serial/by-id/usb-mbed.org_CDC_DEVICE_0123456789-if00,speedalt

or serial equivalent (see windows screenshot)/use wizard.

Iteration 1.5: USBSerial : PC - getting data from a self-written program

Using Python's PySerial, the following code will send some data to the mbed:

import serial
ser = serial.Serial("/dev/serial/by-id/usb-mbed.org_CDC_DEVICE_0123456789-if00")
ser.write("200kt 4500ft\n")
ser.close()

So, adding a UDP server around that:

#!/usr/bin/env python

# Slightly hacky Python script to forward data from FlightGear to the mbed.
import SocketServer
import serial

class FlightSimInstrumentUDPHandler(SocketServer.BaseRequestHandler):
    """
    This class will handle UDP packets sent to a SocketServer, forwarding
    the data to the mbed for display.
    If you need to, you could call XML parsers etc here to transform the data
    such that the mbed understands it (do this in the handle method).
    """

    def handle(self):
        global serial_port
        data = self.request[0].strip()
        socket = self.request[1]
        print "{} wrote:".format(self.client_address[0])
        print data
        serial_port.write(data + "\n")


if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    SERIAL_DEVICE = "/dev/serial/by-id/usb-mbed.org_CDC_DEVICE_0123456789-if00"
    # Open serial port
    global serial_port
    serial_port = None
    try:
        serial_port = serial.Serial(SERIAL_DEVICE)
    except serial.SerialException as e:
        print e
        exit(1)

    # Start UDP server
    server = SocketServer.UDPServer((HOST, PORT),
                                    FlightSimInstrumentUDPHandler)
    try:
        print "Server ready."
        server.serve_forever()
    except KeyboardInterrupt:
        print "Keyboard interrupt received, shutting down..."
        serial_port.close()

... and you can now send the data from FlightGear in the same format over UDP to the Python program, and it will be displayed on the mbed. To launch FlightGear in a way that it sends data via UDP, use the following command (or adapt the Input/Output screen in the Windows wizard); if you need more details, check the generic protocol FG wiki page:

fgfs --generic=socket,out,10,127.0.0.1,9999,udp,speedalt

And of course execute your python script, too.

Why would you want to do this? Well, most programs won't have convenient "export data to serial" capabilities... Here, it's probably not really necessary, but I showed it just in case you want to expand on this.

I originally intended to make a version of this project, that would be using the USB HID interface, but decided to try that from a Crysis mod instead.

Feel free to post any feedback in the comments!


Please log in to post comments.