MAVLink ground station basics

MAVLink ground station basics

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!