This site is curently being worked on. Some parts may be missing or not fully functional.
The address of this site has been changed from stefan.gofferje.net to www.gofferje.net. Please update your bookmarks.

I have been asked a few times now how I got my GCS basics up and running. MAVLink is a rather complicated protocol to implement. The documentation is rather thin and partially outdated... Not an easy thing, if you are no experienced developer...

I'm no pro developer by a long shot. I speak reasonably well C and I did some smaller projects in JAVA. All in all, I can hold my own but can't do nor understand really fancy stuff. Without the help of Pat Hickey, who answered many of my questions on the mailing list, I wouldn't have gotten very far, so first of all, thanks again to him and the others!

Now, how do we get a MAVLink interpreter going?

The first thing to do would be to snatch a copy of the GCS_MAVLink library from the Arducopter project. You will also need AP_Common, AP_Math and FastSerial for dependencies. FastSerial must be the first to be included in your sketch.

Then, go an grab the code of ArduStationII. When you have that, open 2 windows: One with the code of MAVLink.pde from ArduStation2, the other in the GCS_MAVLink library subdir include/mavlink/v1.0/common/

Then, in MAVLink.pde, look at void gcs_handleMessage(mavlink_message_t* msg). This function is called by gcs_update and does the main decoding work. Basically, it's a huge switch-case block which switches on the message id of the received package. Let's have a look at one case block:

case MAVLINK_MSG_ID_ATTITUDE:
  {
    // decode
    mavlink_attitude_t packet;
    mavlink_msg_attitude_decode(msg, &packet);
    pitch = toDeg(packet.pitch);
    yaw = toDeg(packet.yaw);
    roll = toDeg(packet.roll);
    break;
  }

That doesn't look so complicated, does it? ArduStationII works mainly with global variables for all the interesting parameters. So what they do is, for everything they want, they knit a case block, fishing for the right message id, then call the respective decode method and assign the values to their global variables.

Let's have a look at something that isn't included in ArduStationII. In the common directory, search for mavlink_msg_vfr_hud.h and open it in an editor.

The very first thing in this file is the definition of what's in a MAVLINK_MSG_VFR_HUD packet:

typedef struct __mavlink_vfr_hud_t
{
  float airspeed; ///< Current airspeed in m/s
  float groundspeed; ///< Current ground speed in m/s
  float alt; ///< Current altitude (MSL), in meters
  float climb; ///< Current climb rate in meters/second
  int16_t heading; ///< Current heading in degrees, in compass units (0..360, 0=north)
  uint16_t throttle; ///< Current throttle setting in integer percent, 0 to 100
} mavlink_vfr_hud_t;

Let's say, we want the heading... If you scroll down a bit, you find the decode function...

static inline void mavlink_msg_vfr_hud_decode(const mavlink_message_t* msg, mavlink_vfr_hud_t* vfr_hud)

Seems, like we have everything. Now you just need to define a global variable for the heading...

int heading;

and write the respective case block:

case MAVLINK_MSG_ID_VFR_HUD:
{
  mavlink_vfr_hud_t packet;
  mavlink_msg_vfr_hud_decode(msg, &packet);
  heading = packet.heading;
  break;
}

Now there is only one more thing to do. Normally, the APM sends the VFR_HUD message rather rarely. You have to tell it to do it more often. In MAVLink.pde, scroll down until you find the start_feeds function. VFR_HUD is a stream in the EXTRA2 category ;). You can find out, which message stream is in which category from the GCS_MAVLink.pde in the Arducopter project :).

Have fun coding!