At the start of my own Arduino based Quad Drone project at home, I was looking for the best way to communicate between my remote and my Arduino based drone. In one of my earlier posts I gave a short description about using the PS3 SixAxis controller instead of a RC Remote, and this is also the plan for my Quad.

To be able to easy support and integrate functionality on a later stage of development, I was looking around the web for solutions on this type of communication. and there it was: MAVLink (Micro Air Vehicle Communication Protocol).

MAVlink is a light weight message marshaling library first developed back in 2009 by Lorenz Meier for use in micro air vehicles.

MAVLink has a set of predefined messages and structures for use between a “ground station” and the air vehicle. There are a few pre-generated data sets and message structures to be used, or it is possible to make your own by modifying or generating a set of XML files in the library. A description on how this can be done is found here, or more Arduino specific here.

To get started with the integration of MAVLink, you first need to get the correct .h (Header) files to use. There are some predefined headers generated from the last stable release on GitHub for direct download and ease of use.

I wanted to use my own set of supported messages, so I decided to use the MAVLink Generator to be able to make my own set of supported messages. For simplicity I will be using the same example as posted on the MAVLink homepage, integrating the HeartBeat message, but under my own MAVLink protocol alias JAF_QuadProject.

First of all there is no code to be written when to want to generate new messages, just .xml files that are used by the mavgenerate.py python script provided by the MAVLink repository. Details on this can be found here.

There is a .xml file allready created in the MVLink project that supports only the HeartBeat functionality. This is called minimal.xml. Copying the contents of minimal.xml into JAF_QuadProject.xml I have made the necessary preparations to get started.

After running mavgenerate.py directly from the project root folder i get a GUI with simple options.

Mavgenerate.py

This generator will create the necessary .h files for using MAVLink on the Arduino

Tree filestructure structure generated from mavgen.py

To actual be able to use MAVLink on the Arduino, you need to provide two .h files yourself. I wanted to use the supported MAVLINK_USE_CONVENIENCE_FUNCTIONS for easy later usage of any message type that will be supported. Usage is defined here.

mavlink_bridge_header.h

When using the Convenience Functions you need to create the following file in your project:

- mavlink_bridge_header.h

This header file will provide the necessary function description that the system needs to actually use the Convenience Functions generated MAVLink code

/* MAVLink adapter header \*/
#ifndef YOUR_MAVLINK_BRIDGE_HEADER_H
#define YOUR_MAVLINK_BRIDGE_HEADER_H

#define MAVLINK_USE_CONVENIENCE_FUNCTIONS

#include "mavlink\mavlink_types.h"

/* Struct that stores the communication settings of this system.
you can also define / alter these settings elsewhere, as long
as they're included BEFORE mavlink.h.
So you can set the

mavlink_system.sysid = 66; // System ID, 1-255
mavlink_system.compid = 44; // Component/Subsystem ID, 1-255

Lines also in your main.c, e.g. by reading these parameter from EEPROM.
\*/
mavlink_system_t mavlink_system = {66, 44};

\/**
\* @brief Send one char (uint8_t) over a comm channel
\*
\* @param chan MAVLink channel to use, usually MAVLINK_COMM_0 = UART0
\* @param ch Character to send
\*/
static inline void comm_send_ch(mavlink_channel_t chan, uint8_t ch)
{
	if (chan == MAVLINK_COMM_0)
	{
		Serial.write(ch);
	}
	if (chan == MAVLINK_COMM_1)
	{
		Serial1.write(ch);
	}

#if DEBUG
	Serial.print(ch);
#endif
}

#endif /* YOUR_MAVLINK_BRIDGE_HEADER_H \*/

Note:

  • This method will provide information on wich channel, or Serial resource to use on the Arduino.

  • MAVLINK_COMM_X are only internal communication definitions used inside MAVLink libraries to detach from the Hardware layer of the application.

  • the mavlink_bridge_header.h needs to be imported before mavlink.h, because mavlink.h uses the mavlink_system_t mavlink_system definition set in this file.

  • In this example the system is started with systemid 66 and componentid 44

mavlink_receive.h

To be able to receive MAVLink formated messages you need to provide a header file with the definitions on the hardware receive channels used in the application.

For the Arduino create this file:

- mavlink_receive.h

This header file will provide the handler for receiving and parsing MAVLink messages, before deciding on the action to take.

// mavlink_receive.h

#ifndef _MAVLINK_RECEIVE_h
#define _MAVLINK_RECEIVE_h

#include "mavlink\JAF_QuadProject\mavlink.h"

// Example variable, by declaring them static they're persistent
// and will thus track the system state
static int packet_drops = 0;
static int mode = MAV_STATE_UNINIT; /* Defined in mavlink_types.h, which is included by mavlink.h */

\/**
\* @brief Receive communication packets and handle them
\*
\* This function decodes packets on the protocol level and also handles
\* their value by calling the appropriate functions.
\*/
static void communication_receive(void)
{
	mavlink_message_t msg;
	mavlink_status_t status;

	// COMMUNICATION THROUGH EXTERNAL UART PORT (XBee serial)

	while (Serial.available())
	{
		uint8_t c = Serial.read();
		// Try to get a new message
		if (mavlink_parse_char(MAVLINK_COMM_0, c, &msg, &status)) {
			// Handle message

			switch (msg.msgid)
			{
			case MAVLINK_MSG_ID_HEARTBEAT:
			{
											 // E.g. read GCS heartbeat and go into
											 // comm lost mode if timer times out
			}
				break;
			default:
				//Do nothing
				break;
			}
		}

		// And get the next one
	}

	// Update global packet drops counter
	packet_drops += status.packet_rx_drop_count;

	// COMMUNICATION THROUGH SECOND UART

	while (Serial1.available())
	{
		uint8_t c = Serial1.read();
		// Try to get a new message
		if (mavlink_parse_char(MAVLINK_COMM_1, c, &msg, &status))
		{
			// Handle message the same way like in for UART0
			// you can also consider to write a handle function like
			// handle_mavlink(mavlink_channel_t chan, mavlink_message_t* msg)
			// Which handles the messages for both or more UARTS
		}

		// And get the next one
	}

	// Update global packet drops counter
	packet_drops += status.packet_rx_drop_count;
}
#endif

A working example

To make sure the system is working I wrote a small test program for the arduino:

/*
Name:		JAF_MavLink.ino
Created:	12/23/2015 22:30:32 PM
Author:	JohnF
Editor:	http://www.visualmicro.com
\*/


#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#endif

#define DEBUG 1

#include "mavlink_bridge_header.h"
#include "mavlink_receive.h"

MAV_AUTOPILOT mav_autopilot;
MAV_TYPE mav_type;
MAV_MODE_FLAG mav_mode_flag;
MAV_MODE_FLAG_DECODE_POSITION mav_mode_decode_position;
MAV_STATE mav_state;

void setup()
{

	/* add setup code here \*/

	Serial.begin(57600);
	Serial1.begin(57600);

	mav_autopilot = MAV_AUTOPILOT_INVALID;
	mav_type = MAV_TYPE_QUADROTOR;
	mav_mode_flag = MAV_MODE_FLAG_MANUAL_INPUT_ENABLED;
	mav_state = MAV_STATE_CALIBRATING;
}

void loop()
{

	/* add main program code here \*/

	_MAVLINK_RECEIVE_h communication_receive();

	delay(500);

#if DEBUG
	Serial.print("\n");
	mavlink_msg_heartbeat_send(MAVLINK_COMM_2, mav_type, mav_autopilot, mav_mode_flag, 0, mav_state);
#else
	mavlink_msg_heartbeat_send(MAVLINK_COMM_0, mav_type, mav_autopilot, mav_mode_flag, 0, mav_state);
#endif

The MAVLINK_COMM_2 channel used above is not implemented in mavlink_bridge_header.h but lets me print the characters out to a debug terminal, and not just the binary data. (remove the DEBUG definition if you want to send binary)

Summary

MAVLink can be a bit hard to get your head around at the beginning, and I see there are a lot of posts on the internet with people struggeling with implementing it for the Arduino. Hopefully this will provide you with a basic working example that you can continue working on. This example now only has the HeartBeat message implemented, but once you get the basic plumbing together and understand the connections, it should not be to hard to expand the MAVLink message support.

Note:

  • On other examples found for Arduino you will have to include FastSerial.h. This was only necessary before Arduino 1.0 when the Serial.write operations where blocking. Now they are not, but you can use Serial.flush() if for some reason you need to wait on the outgoing data buffer to empty before moving on. NOT RECOMENDED

  • MAVLink is now implemnted as a static header only library, but if you want to integrate the receive functionality without a static class you are free to leave the definition in the .h file and implement the class somewhere else (Preferably mavlink_receive.cpp)

Here is a picture of the completed file structure after putting it all together. (I used VisualMicro to create this sketch)

Working MAVLink tree-structure for Arduino

For the complete code examples, see my ArduinoLibary GitHub page.

I have been thinking about a remote controlled drone project for quite some time. Together with this I also have a small Arduino controlled car which I bought to get started from DealExtreme.

The problem with these remote controlled projects has always been getting a good remote control! My plan has always been NOT GETTING a RC remote. So there must be a different way!

How about using my PS3 SixAxis controller for this?

PlayStation PS3 SixAxis Controller

I tried different approaches using bluetooth and different strange program packages downloaded from different places, but none of these seemed to work.

Linux Devices

I have some experience in using python 2.7 on a linux platform, so I started out trying to find the actual device for the PS3 controller. And after connecting the USB cable to my computer it showed up as the following:

$ /dev/input/js0

Or even better: Using the symlink so that the device number does not matter.

$ /dev/input/by-id/usb-Sony_PLAYSTATION_R_3_Controller-joystick

This will work on most Linux systems, and has been tested on Ubuntu and RasperyPi (Raspbian). Not working in OS X

Reading binary data from the device

Python uses the binascii library to binary code and decode text. So using this to read from the device actually makes the data understandable.

from binascii import hexlify

Decoding the data

After finding a similar example on GitHub by @urbanzim i found that each message from the PS3 SixAxis controller is a 8 char long message with different types of data with a set definition for each of these characters.

Some of these characters are incrementing counters and things I did not care about at first, but i found that char 8 (string[7]) has the channel identifier, and that char 6 (string[5]) has the actual analog signal value from that channel.

After some research I got this list of analog signals amd digital signals:

Signal Reference Analog Binary
00 L-Stick LR A L3
01 LeftStick UD A Select
02 RightStisk LR A R3
03 RightStick UD A Start
04 Up Pad   B
05 Right Pad   B
06 Down Pad   B
07 Left Pad   B
08 Up Pad A L2
09 Right Pad A R2
0a Down Pad A L1
0b - - R1
0c L2 A -
0d R2 A -
0e L1 A -
0f R1 A -
10 Triangle A -
11 Rond A -
12 Cross A -
13 Square A -
17 Rotation Axis ? A -
18 Rotation Axis ? A -
19 Rotation Axis ? A -
1a Rotation Axis ? A -

This might seem a bit confusing, and it was but the same identifier was used for different buttons (binary and analog). In my python approach i focus only on the analog datastream, and filter out the digital (0 value) digital signals.

Putting the data to use

When looking at the data streaming from the PS3 controller, it is obvious that these are event based, and are only updated when there is a change to any one of the sensors. Because of this the datastream will be asynchronous.

Simple usage

When reading from any file or device it is importsnt that you are able to cleanly close the file or device after use. python has a nice solution to this using the keyword with()

from binascii import hexlify

path = "/dev/input/by-id/usb-Sony_PLAYSTATION_R_3_Controller-joystick"

sdata = []
num = []

with open(path, 'r') as filestream:

    signals = [0]*27

    while True:
        read_data = filestream.read(1)
        sdata += [hexlify(read_data)]

    if len(sdata) == 8:

        # Analog button signals
        if (int(sdata[7], 16) > 3) and (int(sdata[5], 16) != 0):
            signals[int(sdata[7], 16)] = int(sdata[5], 16) ^ 0x80

        # Stick Signals
        elif(int(sdata[7], 16) <= 3):
            signals[int(sdata[7], 16)] = (int(sdata[5], 16) ^ 0x80) - 128

    print signals

The resulting printout of this simple function will be a 27 char long list of analog values according to the table shown above. (All values have been normalized so that center stick, or no button press will have the default value 0).

The List values will only be updated when the actual signal is changing from the PS3 remote itself (All signals show up on first run, so the list will allways be complete after initiating).

This is still a work in progress, but I will make a python package for this in the future and host it on GitHub

Well. Lets get started.

My original idea was really to just start putting together bits and pieces for my ambitious drone project. This of course resulted in using GitHub and BitBucket for source control. Doing this i noticed that both of these services can host a free blog based static HTML page. Since I know nothing about writing HTML this was not really something I wanted to get into. But after looking a bit into GitHub Pages, Jekyll came to the rescue!!

And here we are!

I tested aroung with the basic Jekyl setup and was really surpriced on how easy it was to get started with a blog using jekyll. So why not try documenting some of my quests with a blog?

Now let’s choose a theme

I looked aroung for some theme for my blog, since I did not want to just use the default Jekyll theme. So i found the HPSTR Theme from jekkylthemes.com, and from there I must say it got to be a steap learning curve, but I really like this theme. And after struggeling a bit to put it together I finally have something that looks like a blog?!