I had so much fun playing around with DroneKit Python over the last month or so, that I thought I would try my hand at DroneKit Android. Specifically, I was interested in building a customized version of Tower. As always, this blog post is cross posted on my blog with a little nicer formatting here.
The DIY Drones community is probably well aware of Tower’s expansive capabilities as a mobile ground control station, specifically with regards the built in autonomous mapping and scanning modes. Some tens of thousands of users are currently leveraging Tower to control their Pixhawk-powered drones, many of these for small business or large enterprise. However, these commercial users are stuck with a multitool when all they need is a knife. Tower has so many features and functions that it can be quite overwhelming for first time users to create even simple survey. Furthermore, it is fairly easy for even advanced users to make simple mistakes like accidentally pushing the drone into ACRO using the drop down menu up top.
For my first DroneKit Android project, I set out to create an Agribotix branded GCS for Solo AGCO Edition. This isn’t any kind of official AGCO product, but just a fun project I thought would help me learn about DK-Android.
Key features I wanted to explore in DroneKit Android were how to:
Change styling, colors, and logos
Remove unnecessary flight modes
Fix camera settings
Remove hobby oriented settings
Force a UDP connection
While this project is obviously mostly an exercise in deletion, it served as a great introduction to what functions are available in DroneKit Android, how code is split between Tower and 3DR Services, and how a basic Android app is structured. Prior to this undertaking, I had never touched a line of Java or Kotlin or worked with an Android app, so I will assume the reader hails from a similar background.
Step 1: Get ready
If you aren’t already familiar with SITL, DroneKit, git, and the various developer tools I’ll mention here, head over to my Idiot’s Guide to DK-Python: A Journey to WHOZ CHILLIN and give it a quick read. You will be using many of the tools and concepts discussed previously to debug and test your custom version of Tower.
Note: DK-Python is fairly simple and well documented. DK-Android is not. If you are interested in an easier journey to a custom GCS, it may make sense to work in Python.
Next, download Android Studio. Google has built an incredible Integrated Development Environment (IDE) for building Android apps and you will be using it to do all of your work on Tower.
The Tower source code comes next. Because Tower is open source, all of its source code can be downloaded from the DroidPlanner GitHub account. Run
from the terminal in the directory where you’d like to work on your version of Tower. Git will automatically download all of the files you will need.
Next open Tower in Android Studio. Alternatively you can use the VCS ⇒ Checkout from Version Control ⇒ GitHub flow to automatically import Tower into Android Studio.
Android Studio will then attempt to build the Gradle, which must succeed before you can do anything. If you just installed Android Studio, it will fail because you will need to install both the latest Android SDK and BuildTools. Wait for the Gradle build to fail and follow Android Studio’s instructions to get both of these items installed.
Finally, it’s possible to test your app on either a simulated device or a real one. On my computer, the simulated device runs very slowly, so I prefer to debug using a real tablet. To do this, you must plug your tablet into your computer’s USB and set it to developer mode. Briefly, find the Android Build Number section in settings and tap it seven times. After the seventh tap, you will see Developer Mode enabled and you will be able to run your code on the connected tablet.
Step 2: Build Tower
Now that you’re ready to roll, try building Tower yourself. First, click Run ‘Android’ under the Run menu. Assuming you’ve followed all the instructions in Step 1 successfully, Android Studio should compile the Tower source code and spin up a copy of Tower on your connected device. Cool! Now you never need to download compiled .apks of open source projects. You can just compile them yourself.
Go ahead and start up an instance of SITL and connect your compiled version of Tower, which should be called Tower debug on your tablet. You should be able to control your virtual copter just like the you can using the production version of Tower available in the app store.
Running your program is great for quickly testing your changes, but you will need to compile your own .apk for distribution. Try clicking Build APK under the Build menu, building your own version of Tower, and installing it on an Android device. Easy!
Step 3: Android app basics
I am definitely not the most qualified person to write this section and likely will have some errors in my assumptions, but I think my basic understanding is generally sound. If any of the Android wizards out here have improvements or corrections, I would love to roll them in. However, having spent some time bumbling through Tower’s structure without any background in Android applications, I hope I can provide a more accessible view into how to start digging in.
When you open Tower in Android Studio, you’ll notice two high level menus: Android and Gradle Scripts. Android is where most of the meat lives, but some of the files found within Gradle Scripts are responsible for very high level functions like the app name.
Within Android, you’ll see manifests, java, and res. manifests dictates what types of devices are compatible with our app. When you open the xml file within manifests, you’ll notice most of the file is concerned with various hardware configurations. For this project, we don’t care about hardware, but you might imagine that for your custom version of Tower, you might want to restrict users to connecting over Bluetooth and thus would like to throw up an error if a user tried to run your app on a tablet that did not have Bluetooth capability.
The java folder contains, not surprisingly, all of the java files that provide the structure of the app. However, in an Android application, the java files in many cases only serve as a general framework into which content is populated from the xml files found in the res folder. This will become abundantly clear as we walk through the tutorial, but Tower is a delicate dance between the java files in the java folder and the xml files in the res folder.
Take a minute to scan through the java and res folders with Tower open by your side and see what you recognize. I certainly was pretty amazed by how much I was able to understand by just generally scanning the code while poking through Tower. If there is a specific feature you are interested in, try Find in Path, Shift+Command+F on a Mac, and you generally can see where in the code base a specific feature resides.
Step 4: Let’s try some easy stuff
So you think Tower is a boring name? Me, too. Within Android Studio, open the Gradle Scripts/build.gradle (Module Android) file. Head down to lines 140, 148, and 154. You’ll notice that you can replace Tower with a name of your choosing. I used Agribotix for my example, but let your creative juices flow.
Don’t like the Tower logo? Replace the ic_launcher.png logos found in res/drawable/ic_launcher with your own. The different files are for different resolution icons that are displayed on different size devices. If you want to be really fancy, you can use an online tool like this one to automatically generate all of these files from a vector image.
Want to establish some branding within the app? The sidebar seems like a good place to start. Head to res/layout/nav_header_main.xml. You’ll see something like the image below. The screen is split between Android Studio’s best guess as to how nav_header_main.xml will appear on an device and the actual .xml file. If you CMD + click on some of the elements, you can see how they are linked to the rest of the code. Try it and see. After playing around for a few minutes, it should be fairly obvious what’s going on inside this .xml file.
Now, let’s get to your custom branding. You can either do things the hard way or the easy way. The hard way is scan through the text and identify which lines of code likely correspond to which design element. Here, is is fairly obvious that android:text= specifies what the text in the header will say, but that’s not always to case. However, Android Studio is pretty amazing. You can simply click on the item of interest, say the text, and Android Studio will highlight what code is responsible for that element. Go ahead and change the background color, logo, and text to match your branding.
Great, now you should understand the basics of how Tower, and Android apps in general for that matter, is structured from a design perspective. I just identified a few specific touch points here, but with some pretty straightforward digging, you should now be able to completely own the style and design of Tower.
After you’ve tinkered to your heart’s content, go ahead and Run ‘Android’ or build an APK and see how it looks.
Step 5: Let’s dive a little deeper
Now that you own the design, let’s start to own the functionality. One of my biggest issues with Tower, especially when showing it off to a commercial user interested in mapping, is the 15 flight modes available from the telemetry bar. It is far too easy for a user interested in autonomous flight to accidentally kick the copter into ACRO mode and cause a crash. Let’s fix that.
This is the first time where we will be working at the intersection of Tower and 3DR Services. You’ll notice that if you navigate res/layout/fragment_action_bar_telem.xml, you’ll find the design responsible for this feature. However, what you will not find is the ability to add or remove menu items using a simple .xml file. Rather, you will see a reference to android:id="@+id/bar_flight_mode" when you click on the flight mode piece of the telemetry bar. If you search your app for that string, you will find a java file called ActionBarTelemFragment.java.
If you stare at this file for long enough, you will realize that the menu of options in the telemetry drop down is populated by FlightModeAdapter.
If you CMD + click on FlightModeAdapter, Android Studio will take you to a file called FlightModeAdapter.kt, which is written in a language called Kotlin, which is similar to Java. It may be worthwhile to stop at this point and download the Kotlin plugin for Android Studio to make your code look a little more beautiful. Inside FlightModeAdapter.kt, you will see that there is another layer to the onion.
Tower is pulling the flightModes from a variable called VehicleMode. CMD + click on VehicleMode and you’ll notice that Android Studio takes you to the locked VehicleMode.java. This file is locked because it is not actually part of Tower, but rather 3DR Services, which provides many of the functions called by Tower and DroneKit-Android apps. While you certainly could modify that file and compile a new version of 3DR Services, it is easier to step out a layer and try to address all of our changes from the Tower level.
To do this, reopen FlightModeAdapter.kt and put on your Java hat. flightModes is simply an array populated by 3DR Service’s VehicleMode variable. To remove elements from the array, we use the Java remove command. For example, to remove ACRO from your version of Tower, include
underneath flightModes. For my app, I removed every flight mode aside from RTL, Auto, and Guided.
Great. Now build your app and make sure everything still works.
Step 6: Like Solo? Let’s make Tower connect automatically over WiFi (UDP).
Solo AGCO Edition users obviously are only interested in flying Solo. Bluetooth, USB, and TCP connections are painful for the uninitiated to understand and totally unnecessary. To force a UDP connection, open java/DroidPlannerApp.java. You can see that this file handles all kinds of connectivity-related functionality.
Around line 300, you’ll see
final int connectionType = dpPrefs.getConnectionParameterType();
If you look at the code carefully, you’ll see that we can cheat and short circuit the whole connection type selection process by just hard coding in a UDP connection. You’ll notice that the connectionType variable in an integer. If you do a little CMD + click digging, you’ll learn that that integer is 0 for USB, 1 for UDP, 2 for TCP, and 3 for Bluetooth. To force a UDP connection, we can just go behind all of the logic and menus and replace
This cheat will not remove any of the UI, but no matter what connection type you specific, Tower will look for a vehicle over UDP. Which brings us to...
Step 7: Remove unnecessary menus
Now that we’ve hardcoded a UDP connection, we should remove all of the connectivity menus. And, while we’re at it, let’s get rid of everything else we find distracting. Personally, I don’t like the Flight History or Checklist top level menus and think the user is exposed to way too many settings.
Go ahead and open up res/menu/navigation_drawer_items.xml to clean up the navigation drawer and res/xml/preferences.xml to get rid of the preferences you don’t like.
You may find that with some of the xml items that you remove, you may have to find the associated Java reference and comment it out as well. This should be fairly straightforward to work through, but feel free to look to the Agribotix app for specific examples.
In this general vein, I also removed Dronie, Follow, and Land from the top level menus and everything aside from Spline Survey from the Editor screen, because the Solo AGCO Edition owners are essentially only interested in building maps. You can see how I did this by looking at what I commented out in java/org.droidplanner.android/fragments/control/CopterFlightControlFragment.java and /res/layout/fragment_editor_tools.xml
Step 8: Simplify the Editor screen for the optimal survey
Solo AGCO Edition customers use the editor screen to do only one thing: spline survey. They also user only one camera, GoPro, and should never have to play with sidelap or overlap. The mapping workflow can be dramatically simplified by hardcoding all of these values in, which is possible in java/org.droidplanner.android/proxy/mission/item/fragments/MissionSurveyFragment.java.
I started to get a little sloppy here (you’ll notice that I didn’t change the camera UI, although the camera is hardcoded to GoPro in the backend), but I hardcoded the camera to Hero 4 Black, the sidelap to 70%, and the overlap to 85%.
Step 9: Profit?
Whew! That was a lot of work, but in the end we created a custom version of Tower that is built to address specific user needs and reinforce a platform partner’s branding. Tower is a very extensible base for a huge variety of custom mobile GCSs, so I hope this guide makes modification a little more accessible for all of the 3DR platform partners out there doing great things with Pixhawk.