Android Tutorial: Getting data from our GHOSTDRONE

Overview

Good job on getting the G-Box bluetooth address! Can we build an App to fly now? Probably not. For safety and other concerns, we have to make sure we do everything right before putting a drone in the air. We are four steps away from building your own drone controller App

  1. Our GHOSTDRONE's sensors have to be functioning properly.
  2. Our GHOSTDRONE's flight control system has to be functioning well.
  3. Our phone has to be functioning well.
  4. We have to find a way to transform phone sensory data to drone control command.

In this tutorial, we tackle the first step. We will build an Android App to connect to the drone and fetch data for diagnostic purpose. We will be able to read sensor feedback from GPS, Gyro sensors, barometer and battery, with which, to conduct basic diagnostics by ourselves.

Setting up the Android project

First let's create an empty project named GhostDiagnostics and add bluetooth permissions as we have done in DiscoverMyGBox.

In order to connect to the drone, EHand SDK library needs to be imported. Copy the jar file into /app/lib under your project folder.

Go back to Android Studio, switch the navigation panel to Project Files. Locate the jar file under /app/libs/, right click it and select "Add as library".

Now replace the files AndroidManifest.xml, activity_main.xml and MainActivity.xml with the ones created by Android Studio and copy App.java to the same folder of MainActivity.java.

main_activity.xml handles the UI setup and is pretty straightforward. We'll skip elaborating.

In AndroidManifest.xml a few special settings need to pay attention to.

  • We add bluetooth and bluetooth admin permissions to enable turning on bluetooth and connection to G-Box.
  • Vibration permission will be used when we are controlling a drone in avatar mode.
  • Write external storage permission is also required to store flight data for future analysis.
  • SDK needs to use cellphone fine location for follow me mode.
  • android:name=".App" specifies the application entry point to App.java where CopterControl initialization needs to be done in the very beginning.
  • android:screenOrientation="portrait" fixes the phone orientation and avoid switching out of the current Activity.

Now open App.java, only a single line catches our eyes. This is where we initialize the CopterControl instance which will be reused through the entire App life cycle.

//App.java CopterControl.getInstance().init(this);

MainActivity.java includes all control logics of our Android application. OnCreate() is the entry of the Activity. The first two lines are seen in every Android app and we don't bother to go into detail.

//MainActivity.java @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initCopterEventListener(); initView(); setupHandler(); }

initCopterEventListener() sets up two listeners to handle bluetooth and drone connections. It provides us a chance to handle situations that we lose connection to G-Box or the drone. Feel free to power on/off the G-Box and drone to see what message is prompted. Additionally, we set up a mode change listener to handle callback when the flight mode changes.

//MainActivity.java public void initCopterEventListener(){ //add listener to bluetooth connection CopterControl.getInstance().getConnection().addGboxConnectionListener(new ConnectionListener() { @Override public void onConnect() { Toast.makeText(getApplicationContext(), "Bluetooth Connected", Toast.LENGTH_SHORT).show(); } @Override public void onDisconnect() { Toast.makeText(getApplicationContext(), "Bluetooth Disconnected", Toast.LENGTH_SHORT).show(); } }); //add listener to copter connection CopterControl.getInstance().getConnection().addCopterConnectionListener(new ConnectionListener() { @Override public void onConnect() { Toast.makeText(getApplicationContext(), "Copter Connected", Toast.LENGTH_SHORT).show(); } @Override public void onDisconnect() { Toast.makeText(getApplicationContext(), "Copter Disconnected", Toast.LENGTH_SHORT).show(); } }); CopterControl.getInstance().setOnModeChangeListener(new CopterControl.OnModeChangeListener() { @Override public void onChange(FlightMode flightMode) { Toast.makeText(getApplicationContext(), "Changed to " + flightMode.name(), Toast.LENGTH_SHORT).show(); } }); }

initView() connects the TextView objects with what we have defined in layout and binds the onClick events of three buttons to the current Activity.

//MainActivity.java public void initView() { tv_drone_status = (TextView) findViewById(R.id.tv_DroneStatus); tv_GPS_Lat = (TextView) findViewById(R.id.tv_GPSLat); tv_GPS_Lng = (TextView) findViewById(R.id.tv_GPSLng); tv_GPS_Sat = (TextView) findViewById(R.id.tv_GPSSat); tv_drone_pitch = (TextView) findViewById(R.id.tv_Pitch); tv_drone_roll = (TextView) findViewById(R.id.tv_Roll); tv_drone_yaw = (TextView) findViewById(R.id.tv_Yaw); tv_drone_altitude = (TextView) findViewById(R.id.tv_Altitude); tv_battery_percentage = (TextView) findViewById(R.id.tv_BatteryPercentage); findViewById(R.id.bt_Connect).setOnClickListener(this); findViewById(R.id.bt_Bind).setOnClickListener(this); findViewById(R.id.bt_Disconnect).setOnClickListener(this); }

setupHandler() enables us to actively check drone status periodically. In this example, we utilize getCurrentMode(), getGpsStatus(), getAttitude, getHUDSystem() and getRemainingPower() to read and display critical flight data on GUI.

//MainActivity.java public void setupHandler() { handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what){ case HEARTBEAT_MSG: if (CopterControl.getInstance().isCopterConnected()){ //Get Flight mode and ARM status boolean armed = CopterControl.getInstance().isArmed(); String status_text = CopterControl.getInstance().getCurrentMode().name(); if (armed) { status_text = status_text + " Armed"; } else { status_text = status_text + " Disarmed"; } tv_drone_status.setText(status_text); //Get GPS status Double latitude, longitude; int satellites; if (CopterControl.getInstance().getGpsStatus()!=null) { latitude = CopterControl.getInstance().getGpsStatus().latitude; longitude = CopterControl.getInstance().getGpsStatus().longitude; satellites = CopterControl.getInstance().getGpsStatus().NumAvailableSatellites; tv_GPS_Lat.setText(Float.toString(new BigDecimal(latitude).setScale(6, BigDecimal.ROUND_HALF_UP).floatValue())); tv_GPS_Lng.setText(Float.toString(new BigDecimal(longitude).setScale(6, BigDecimal.ROUND_HALF_UP).floatValue())); tv_GPS_Sat.setText(Integer.toString(satellites)); } //Get HUD status / altitude Float altitude; int heading; if (CopterControl.getInstance().getHudSystem()!=null) { altitude = CopterControl.getInstance().getHudSystem().altitude; heading = CopterControl.getInstance().getHudSystem().heading; tv_drone_yaw.setText(Integer.toString(heading)); tv_drone_altitude.setText(Float.toString(new BigDecimal(altitude).setScale(3, BigDecimal.ROUND_HALF_UP).floatValue())); } //Get attitude Float roll, pitch, yaw; if (CopterControl.getInstance().getAttitude()!=null) { roll = CopterControl.getInstance().getAttitude().roll; pitch = CopterControl.getInstance().getAttitude().pitch; tv_drone_roll.setText(Float.toString(roll)); tv_drone_pitch.setText(Float.toString(pitch)); } //Get Battery int remainingBattery; remainingBattery = CopterControl.getInstance().getRemainingBattery(); tv_battery_percentage.setText(Integer.toString(remainingBattery)); //Schedule the next check handler.sendEmptyMessageDelayed(HEARTBEAT_MSG, STATUS_REFRESH_INTERNAL); } break; } } }; }
Now we take a look at onClick(). When the connect button is clicked, we try to connect to a G-Box with the specified MAC address, then call initStatusCheckTimer() to start checking copter data periodically. When bind button is clicked, a binding process is initiated just like in EHang play.
//MainActivity.java @Override public void onClick(View v){ switch(v.getId()) { case R.id.bt_Connect: CopterControl.getInstance().getConnection().connect(MAC, new OnConnectionListener() { @Override public void onSuccess() { Toast.makeText(getApplicationContext(), "Connection Succeeds", Toast.LENGTH_SHORT).show(); initStatusCheckTimer(); } @Override public void onFailure() { Toast.makeText(getApplicationContext(), "Connection Fails", Toast.LENGTH_SHORT).show(); } }); break; case R.id.bt_Bind: CopterControl.getInstance().stopPair(); CopterControl.getInstance().startPair(new OnSendListener() { @Override public void onSuccess() { Toast.makeText(getApplicationContext(), "Pairing Succeeds", Toast.LENGTH_SHORT).show(); } @Override public void onFailure() { Toast.makeText(getApplicationContext(), "Pairing Fails", Toast.LENGTH_SHORT).show(); } }); break; case R.id.bt_Disconnect: if (CopterControl.getInstance().isCopterConnected()) { CopterControl.getInstance().getConnection().disconnect(); } break; } }

Testing the App

We have implemented the App we saw in the beginning and it鈥檚 high time to test it out.

  • Drone Status: what flight mode the drone is currently in.
  • GPS Lat/Lng: drone coordinates based on GPS signal. Put your drone outdoor or near the window to see if you have the latitude/longitude that matches your current location.
  • # of satellites: the more satellites we have the less likely we lose track of the drone.
  • Roll/Pitch: lay your drone flat, it鈥檚 expected to see near zero values of roll and pitch. Tilting your drone leftward/rightward changes the Roll value and forward/backward changes the Pitch value. It鈥檚 expected to receive symmetric values when the drone is tilted to the same angle on both sides. Otherwise, we need further inspection on sensors.
  • Altitude: grab your drone up and down we can easily check if the barometer is working well.
  • Battery %: compare the values with the ones displayed on your battery to see if battery reading is correct.

More data to process?

Congratulations on getting data from the drone successfully. Even as a tutorial App, it is very useful to run it regularly before taking off. Note that by the time we write this tutorial, EHang SDK only exposes a few key sensor readings from the drone and there are definitely a lot more sensor feedback for you to explore. Tell us what data you want to get in the forum and help us improve the SDK.

Project files