mavleash: track and control your drone via 3G cellular

One of the nagging worries we have when flying a drone is that it will, despite our best efforts, fly away into the wild blue yonder.

If you’re lucky, and the drone lands within radio range of your handheld controller, you might be able to find the drone on a map, and go pick it up.  If you’re unlucky, and the drone flies out of range of the controller, you may never see it again.

Recently I decided to hack together a solution for tracking my drones, even if they fly out of range of my ground controller.  Fortunately I live and fly in areas that have good 3G cellular coverage, and the folks at Particle have recently released a very compact 3G cellular data board called the Electron.  The Electron includes a u-blox cellular data modem as well as a low-cost monthly data plan.

Particle also makes a cheaper WiFi version of their board called the Photon.  To avoid burning up my Electron data plan, I decided to start by prototyping using the Photon.  Fortunately the Particle API is nearly identical for both the Photon and the Electron, so I could be assured of a smooth transition from one to the other.

Initial components
Initial components

There are just a few basic components required: a Particle board (the Photon or Electron), a GPS receiver, and mavlink-compatible autopilot. The GPS receiver is required to enable the autopilot to know where it is. For the autopilot I used a trusty Pixhawk v2.4.6, which is available in many different flavors and form factors from numerous vendors on eBay and elsewhere.

What’s mavlink?

The mavlink protocol is a communications protocol used by numerous drone autopilots and accessories.  A typical use is that an Arducopter or similar autopilot will send its telemetry in mavlink format via a telemetry radio (such as the very common SiK telemetry radio) to a ground control station (GCS) such as QGroundControl.  In this case, I setup my Pixhawk using QGroundControl,  including flashing the Pixhawk with the latest Stable release of the PX4 autopilot software.


The wiring between the Particle board and the Pixhawk was straightforward.  I took the serial UART output from the Pixhawk’s TELEM1 port and built a cable to connect to the Particle’s Serial1 port (which attaches to the RX and TX pins) as well as the Particle’s VIN and GND pins. Fortunately the TELEM1 port provides +5V power that is perfect for the Particle’s voltage input.

Photon proto wiring
Photon proto wiring

Tip: I accidentally reversed the TX and RX pins multiple times while working on this project.  If you find that you are not apparently receiving any mavlink input to the Particle application, try reversing the TX and RX lines from the autopilot.


For reference, I’ve made the mavleash code available on github.

Because I was eventually going to send telemetry data and commands via 3G cellular connection, and I was paying by the megabyte for data, I knew I didn’t want to send raw mavlink from the autopilot to the Particle cloud.  This would work fine with the Photon’s wifi connection, but it was expensive and overkill for the Electron’s 3G connection.  Instead I decided to listen for a few key mavlink messages and forward summary data to the Particle cloud on a periodic timer.

I started by generating the common mavlink 1.0 headers using the mavgenerate tool.  Then I created a new Particle app project and quickly discovered that, by default, Particle projects don’t currently support descending into the directory hierarchy that mavlink requires.  So I needed to create a particle.include file that would pull in all of the mavlink headers as well as the sample app files.  This would limit me to using the Particle command line tools for builds instead of being able to use the otherwise great Particle Build web-based IDE.

Then I built a serial port reader (readMavlinkMsg) and mavlink message handler (handleMavlinkMsg).  The serial reader reads data from the Serial1 port (which is attached to the TX and RX pins on the Electron and Photon), and the message handler handles a few key mavlink messages, ignoring all others.  I won’t dive into why I chose these particular mavlink messages, but briefly I wanted to obtain sufficient data to find a drone if it flew away.

I will also point at that I used one of the mavlink messages to limit the rate at which I send data to the Particle cloud.  Mavlink provides a handy MAVLINK_MSG_ID_HEARTBEAT message that is sent by the autopilot at almost exactly 1Hz (once per second).  By counting the number of heartbeat messages I received on the Particle board I could decide how often to send updates to the ground– in this case I decided that a bit less than 0.3Hz was adequate.

Sending Data to the Particle Cloud

At first I experimented with using the Particle.variable methods to make individual bits of telemetry available in the Particle cloud (you’ll see this in registerCloudVariables).  This seems like a nice API for making one or two variables available for reading in the cloud — however, there’s not much device-side control over when these variables are published to the cloud, and there is a nontrivial amount of data overhead to each publication.  Also, reading these variables from a cloud client side requires RESTful polling (see eg the various setInterval functions in the mavleash.html file).

Next I experimented with Particle.publish, which allows you to publish a named event along with some payload data.  The advantage of this is that events can be published at a moment of your choosing, and you can carefully control what payload data is sent.   The first payload data format I tried was simple JSON (see publishStateAsJson ), where summary telemetry data was sent as name-value pairs. One big advantage of using JSON as the payload format is that it’s human-readable.  Another big advantage is that there are numerous JSON parsing libraries available for decoding the JSON (it’s almost trivial to read into javascript, as you can see from the statejson_eventSource callback function).

Publishing with Particle.publish and JSON worked fine, but I still wanted my payloads to be smaller. So next I moved to using CSV (comma separated values) as the payload format (see publishStateAsCSV).  The advantage and disadvantage of CSV is that there are no variable/parameter names– it’s just an ordered series of values, and you need to always send every parameter so that the order is maintained.  In practice, with just seven parameters sent in the payload, I saw about a 40% savings in payload size.

Reading Data from the Particle Cloud

As a proof-of-concept, I created a mavleash.html page that accessed the Particle event API using javascript.  I’ve left the code with both JSON and CSV readers enabled, and commented-out the Particle.variable polling code, so you can see how those can be used.

For flight testing, I loaded the mavleash.html file locally onto my iPhone using Coda, and entered my Electron’s specific device ID and access token.

iOS Mobile client
iOS Mobile client

In addition I created an IFTTT recipe to log events sent from the Electron in a Google Drive spreadsheet:  Each time the Electron publishes its position (as a ‘statecsv’ event), it creates another row in a spreadsheet.  This can help you figure out where your drone went even if you don’t have mavleash loaded on your iPhone.

Sending Commands

In case of flyaway emergency, I wanted to be able to send a few commands to the drone from the mavleash app:

  • Land.  Cause the drone to land now, wherever it is
  • RTL.  Cause the drone to return to where it was launched (un-flyaway)
  • Reset. Cause the Electron itself to reset, for testing purposes.

I implemented support for this in the handleCommand methods.  Since Particle.function only supports four functions currently,  I decided to make the actual command a string parameter. (See landCommand for an example of sending the commands using the cloud API.)

Mounting on the Drone

Here’s an overview of the components you need to mount on the drone:

Electron components to mount
Electron components to mount

Your drone might be different.

For flight tests, I mounted the required components on an F450 quadcopter:

Electron mounted on F450 arm
Electron mounted on F450 arm

There are probably better ways to mount the Electron, but Blenderm tape works just fine. In this configuration I mounted the Electron’s taoglas antenna with the backing tape attached to one of the quad’s ESCs– this means the antenna traces were facing the ground.

From testing I also decided that I didn’t need to use the external battery supplied with the Electron– it appeared that the power supplied by the Pixhawk’s +5V line was sufficient.

Flight Testing

The next step was to try this in the real world.  I took the F450 out to a local flying field:

Electron in field
Electron in field

After powering up, I waited a few minutes for the Electron to connect to the 3G network– however it never obtained a connection.  Thinking that this might be due to the Electron’s antenna facing the ground, I held the quad aloft for about a minute after power-cycling the quad, and sure enough the Electron started “breathing cyan”, indicating it had a 3G connection.

The first flight I placed the vehicle into position hold mode, then moved it around a bit as I watched the position indicator on the mavleash app. Sure enough, the tracking was working.

The next few flights, I tested sending commands to the vehicle.  I flew the vehicle in position hold mode, turned off my RC transmitter (to simulate going out-of-range during a flyaway) then sent a series of commands to the vehicle using the buttons on the mavleash app:

  • Land.  This worked perfectly.  Within a second after pressing the Land button the mavleash.html app, the vehicle started an automated landing.
  • RTL.  This did nothing, as my ground tests indicated it might.  The “native PX4” software stack I installed on the Pixhawk doesn’t have a simple RTL handler, and I think that in position hold (POSCTL) mode, it disallows RTL.  Arducopter apparently does support the simpler RTL command, but I haven’t tried it.
  • Reset.  I tried remotely resetting the Electron itself, and this worked as well. Telemetry stopped flowing from the vehicle for about a minute, then started flowing again.

Next Steps

Some folks may wish to run both mavleash as well as a more traditional telemetry radio such as the SiK 915MHz/433MHz, or monitor their mavlink telemetry using MinimOSD.
You can readily split the output line of the autopilot mavlink port and supply it to multiple readers (SiK, MinimOSD, and Electron).  This will provide you with a telemetry stream from the autopilot.  You can configure the wiring so that one of the radios can write commands back to the autopilot’s input pin. For example you could use the MAVBoard Lite to simplify the wiring.
However, if you wish to send commands to the autopilot from multiple sources, this is tricky.  One option would be to use a digital multiplexer (eg NC7SZ157P6X) to switch between, say, the SiK and the Electron.  You could output a mux control signal from one of the Electron’s digital output pins so that when the Electron is sending commands, it owns the autopilot’s input pin.

One thought on “mavleash: track and control your drone via 3G cellular

Leave a Reply