Tower / DroidPlanner revived — builds and runs on modern Android again

Hi everyone,

Tower (DroidPlanner) has been unmaintained since around 2016 and no longer installs or runs on current Android. We spent some time bringing it back and would like to share it with the community.

Repository: GitHub - nomar2/Tower: Tower is a Ground Control Station (GCS) Android app for UAVs running ArduPilot · GitHub
Debug APK: see the Releases page of the repo

It is a fork of DroidPlanner/Tower, still GPLv3, with the original credits preserved (Arthur Benemann, Fredia Huya-Kouadio and the Tower contributors, with DroneKit-Android underneath).

What we changed

Builds and runs on Android 8–16

  • Modern toolchain: Gradle 8 / AGP 8 / Kotlin 1.9 / Java 17 / AndroidX, compileSdk 36, targetSdk 34, ABI-universal APK.
  • Fixed the startup crashes and the touch/ANR freeze (notification channels, PendingIntent flags, foreground-service types, a main-thread location call that hung forever, runtime permissions, scoped storage).
  • Fixed the crashes on Connect and on flight-mode change.

MAVLink 2

  • The bundled MAVLink stack now parses 0xFD frames (incompat/compat flags, 3-byte message id, signature skipping) and auto-negotiates MAVLink 2 on transmit once the peer speaks it. MAVLink 1 links are unaffected.

Improved GUIDED follow-me

  • The stock follow forwarded raw GPS fixes as position-only GUIDED targets at ~1 Hz, so the copter braked at every point and trailed behind. Reworked: alpha/beta filter over position and ground velocity, setpoints streamed at 5 Hz with dead-reckoning between fixes, position and velocity sent, short lead-ahead prediction. Location acquisition moved to the modern FusedLocationProviderClient.

Other

  • “Reboot flight controller” action in the connected menu (slide-to-confirm).
  • Removed dead integrations: Baidu Maps, the Droneshare upload service and account UI, Google Analytics, the Weather Underground widget. The local TLog viewer is kept.

Full changelog and roadmap are in the repo.

Testing status — please read

  • Connects to a real vehicle over UDP and USB serial — tested, works.
  • Bluetooth (SiK-style radios) has not been re-checked yet.
  • Everything else is verified against a MAVLink simulator and on an Android 16 emulator — not yet flown end-to-end on hardware.
  • The follow-me is verified at the command-stream level; the flight tuning may need adjusting on a real copter.
  • MAVLink 2 signing: receive only (signature ignored), no signed transmit.
  • targetSdk is 34 for now.

So please fly-test it carefully. We would really appreciate feedback from anyone willing to try it — especially on real hardware, Bluetooth links, and follow-me behaviour.

Note: the released APK ships without a Google Maps key, so the map will not render. If you build from source, add your own key in local.properties (MAPS_API_KEY=...).

Thanks,
Ramón José Moreno & Alejandro Moreno

great work !
Is there a prebuild APK ?

Hi, thanks—yes:

Tower GCS v4.0.0.1

Build system

Gradle wrapper 8.7, Android Gradle Plugin 8.5.2, Kotlin 1.9.24, Java 17.
android.support.* migrated to AndroidX (+ Jetifier for legacy dependencies).
compileSdk 36, minSdk 24, targetSdk 34; namespace-based manifest.
Removed Fabric/Crashlytics and the dead jcenter/bintray/3DR repositories.
Baidu Maps SDK removed entirely (SDK, native libs, map provider, preferences).
LeakCanary updated to 2.x (debug only).
Google Maps key read from local.properties (MAPS_API_KEY) via a manifest
placeholder.

Android 8-16 runtime

Notification channels for all foreground-service and status-bar notifications.
PendingIntent FLAG_IMMUTABLE / FLAG_MUTABLE where required.
RECEIVER_NOT_EXPORTED on runtime-registered receivers.
foregroundServiceType="connectedDevice" and immediate startForeground().
Runtime permission requests (location, notifications, Bluetooth 12+).
Location acquisition moved off the main thread (FusedLocationProviderClient)
— fixes the touch/ANR freeze.
AppService is now a proper foreground service tied to the vehicle connection.
Scoped storage: missions/parameters stored under the app's external files dir;
storage permissions removed; content:// missions copied in before loading.

Connection / MAVLink

Fixed the crash on Connect (Wi-Fi / USB connection handlers).

Fixed the crash on flight-mode change (selection dialog rewritten as a
concrete class for AndroidX).
MAVLink 2: parser accepts 0xFD frames (incompat/compat flags, 3-byte
message id, 13-byte signature skipped); transmit auto-negotiates to MAVLink 2
once a v2 frame is received from the peer. MAVLink 1 links are unaffected.
Connecting to a real vehicle over UDP and USB serial was tested and
works.

Features

Reboot flight controller — menu action (shown when connected), confirmed
with a slide-to-unlock, sends MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN.
Improved GUIDED follow-me:
    alpha/beta filter over the operator's position and ground velocity;
    setpoints streamed at 5 Hz with dead-reckoning between GPS fixes
    (instead of ~1 Hz);
    position and velocity setpoints (SET_POSITION_TARGET_GLOBAL_INT,
    yaw / yaw-rate left uncontrolled);
    short lead-ahead prediction; clean decay to a hold when the operator stops.
    Leash and Above modes reworked onto the same path.

Removed dead integrations

Droneshare: background upload service, network client and account UI removed.
The local telemetry-log (TLog) viewer and its session database are kept.
Google Analytics (play-services-analytics) removed; the "usage statistics"
preference removed.
Weather Underground widget removed (API discontinued; widget was disabled).

Tower GCS v4.0.0.2 (Latest)

What has been added: Controller reset button a third button in the mission editor’s trash tool, alongside SELECTED and MISSION:
VEHICLE / DRONE — deletes the mission stored in the controller’s memory.
It is only active when a drone is connected (otherwise, it appears disabled or displays the message “Connect to a drone first”).

A power-off button has been added to disconnect from the application.

Fix: crash when rotating the phone in the editor — 49fd3c4
The “delete drone mission” button was only in the portrait layout; upon rotating to landscape (wide layout), the fragment looked for a non-existent button and crashed. Added the button to the landscape layout + null check. Tested: 5 consecutive rotations, no crash.

Mission send/receive reliability — 3a734d6
The .aar’s WaypointManager used a 15s watchdog with 3 retries → a lost packet caused a 15s block, and 3 failures (45s) caused an abort. Now set to 3s with 6 retries.
The app also retries the transfer once on its own and displays a clear warning if the link is down.
The “Download mission” button uses this same logic.
Verified against a packet-dropping simulator: download and upload recover from multiple consecutive interruptions.

Mission progress bar — abe32fb
Sending/downloading/deleting the drone mission now displays an indeterminate horizontal progress bar (“Sending mission to drone…”, etc.) for the duration of the operation.
It reappears automatically if the phone is rotated mid-operation; it closes upon completion or when retries are exhausted.
The raw MISSION_CLEAR_ALL call was moved from TrashToolsImpl to MissionProxy to unify the 3 paths.

Dronie button feedback — 9eadc77
Without a GPS signal, the button did nothing visible → now it warns “Dronie requires GPS signal (6+ satellites)” and “Drone not responding” if there is no reply within 8s.
The Dronie upload now uses the same progress bar and retry logic as a standard upload.
The confirmation text clarifies that the Dronie is only uploaded (arming and switching to Auto mode is still required).

I tried v4.0.0.2 and was not able to connect.
Neither via USB nor via UDP.

Hello,

Thank you for reporting this issue.

Following testing on our end using Android 10 and 16 across all standard telemetry interfaces—UDP, TCP, and USB-Serial (433 MHz)—we have confirmed full protocol compatibility with both MAVLink 1 and MAVLink 2. The application code and socket initialization routines operate as expected on this Android release.

Given that the build is functional under Android 16, the connection failure on your end is almost certainly related to OS-level network routing policies, runtime permission restrictions, or local hardware configuration.

To help us isolate and resolve the issue on your device, please execute the following diagnostic steps:

  1. Disable Mobile Data (Cellular Interface):

    • Temporarily turn off Mobile Data (4G/5G) on your Android device.

    • Connect to the drone’s Wi-Fi access point. If Android prompts that “This network has no internet access”, explicitly select Keep connection.

    • Launch Tower and attempt to initiate the UDP connection again. (Android OS policies often reroute unbound UDP socket traffic to the primary cellular interface when local Wi-Fi lacks WAN connectivity).

  2. Verify Runtime Network Permissions:

    • Navigate to Settings > Apps > Tower > Permissions.

    • Ensure that Location and Nearby Devices (or Local Network) permissions are explicitly set to “Allowed” or “Allowed only while using the app”. (Modern Android releases enforce strict permission checks for local socket binding and network discovery).

  3. Confirm IP Endpoint and Port Binding:

    • Verify that your Wi-Fi module (e.g., ESP8266/ESP32 or onboard router) is broadcasting to the correct UDP port (typically 14550 or 14555) and that your device’s assigned IP resides within the expected subnet.
  4. Cross-Application Benchmark:

    • If the issue persists, test the Wi-Fi link on the same device using an alternative Ardupilot.

Please advise on the outcome of these checks—specifically whether disabling mobile data or granting nearby device permissions restores communication.

Sincerely,

no success.
Mobile Data turned off
All permission granted to Tower app
Tested with 3 ArduPilots.
Connection with QGroundControl works.

​I’m really sorry, I’m not sure what might be happening. Given how much effort you put into trying out Tower, it’s a realgrief. The hardware I have on hand is the only one I can test with. Do you have the chance to try a different type of telemetry? Thank you so much for trying!

I have a few telemetry devices.
But I think the USB connection should work first.
There is no point in testing other connections before that.

Hi, what about the UDP server port configuration?

I have mine set to 14550—see if that’s it.