How to install and use a Flight Mission#
Description#
A flight mission can be in the following states:
UNAVAILABLE: mission is installed but not available. Possible reasons are:
BROKEN: the mission will never be able to load , e.g. because firmware version is not supported.
LOAD_FAILED: the last load failed, e.g. because of an exception in python code.
UNLOADED: mission is not loaded.
IDLE: mission is loaded and can be activated.
ACTIVE: mission is active.

The flight mission is managed from the mission UI that is running on the controller. It can either be a smartphone application using GroundSDK (ex: OpenFlight) or an PC application using Olympe/framework.
The mission UI can list, install and exchange messages with the flight mission.
The mission UI is optional, a flight mission can be installed and setup to replace the default mission.
Listing and installing flight missions from mission UI#
From GroundSDK: MissionUpdater
From Olympe:
Todo
add link when available
Start a flight mission from mission UI#
Once installed, a flight mission shall first be loaded, then activated. Several flight missions can be loaded simultaneously but only one can be active. Activating a new flight mission automatically deactivate the previous one.
From GroundSDK: MissionManager
From Olympe: MissionManager
Exchanging messages between mission and mission UI#
You can define custom messages between your mission UI and your flight mission. Messages are written using Protobuf 3.
You need to comply with the following conditions for your custom protobuf message file: * A package declaration with “<your_company>.missions.” + mission name + “.airsdk.messages”
Two messages called Command and Event, each containing an union of all possible command or event using a oneof tag with the name id.
The following properties need to be set:
java_package: package name in the form “com.your_company.drone.missions.” + mission name + “.airsdk
java_outer_classname: mission name
We also recommend to follow those rules for consistency along all protobuf messages:
Enumerations should be put in their own separate files, use the name Enum and have a package name corresponding to the current service, followed by the enumeration name (ex: MyPackage.MyEnum). If this is not possible, add the enumeration name in the values (ex: enumeration MyEnum with value MY_ENUM_VALUE_ONE)
Message and Package names should be in snake_case
Type name should be in CamelCase
Minimal example:
syntax = "proto3";
package your_company.missions.mymission.airsdk.messages;
option java_package = "com.your_company.drone.missions.mymission.airsdk";
option java_outer_classname = "MyMission";
option (olympe_package) = "your_company.missions.mymission.airsdk";
import 'google/protobuf/empty.proto';
message Command {
oneof id {
google.protobuf.Empty my_empty_command = 1;
}
}
message Event {
oneof id {
uint32 my_event = 1;
}
}
On the drone side: Use the mission_environment given in the Mission constructor.
make_airsdk_service_pair(package): Create a communication channel with the Mission UI given a protobuf package.
From the returned service object, use the following methods to send and receive messages.
send(message): Send the given message to the mission UI.
observe(…): Register a callback for message received from mission UI.
import fsup.services.events as events
import your_company.missions.mymission.airsdk.messages_pb2 as messages_pb
class Mission(object):
def __init__(self, mission_environment):
# Setup service pair with protobuf description
self.ext_ui_msgs = self.env.make_airsdk_service_pair(messages_pb)
# Register for incoming messages (commands)
self.ext_ui_msgs_observer = self.ext_ui_msgs.cmd.observe_messages({
self.ext_ui_msgs.cmd.idx.my_empty_command: self._on_msg_my_empty_command
})
# Send a message (event)
self.ext_ui_msgs.evt.sender.my_event(42)
def _on_msg_my_empty_command(self, msg):
# 'msg' is directly the arguments of 'my_empty_command'
# (google.protobuf.Empty in this case)
pass
On the mobile application side: Use the MissionManager
peripheral
from Ground SDK.
Send the given message to the drone:
MissionManager - func send(message: MissionMessage)
message.uid: UID of the destination mission.
message.packageName: Package name of the protobuf message.
message.messageNumber: Number of the message (the value of the id oneof of the message).
message.payload: Message payload serialized from the protobuf message.
let missionUid = "MyMission"
/// Send message to my mission
func sendMessage(drone: Drone) {
/// Get mission manager
let missionManager = drone.getPeripheral(Peripherals.missionManager) { [unowned self] missionManager in
guard let missionManager = missionManager else {
return
}
// Set package name of my mission to mission manager.
// Mission manager will then be able to send messages and received events
missionManager.packageNames = [missionUid]
// Create mission command
var command = Your_Company_Missions_Mymissions_Airsdk_Messages_Command()
// Set command id
command.id = .myEmptyCommand(Google_Protobuf_Empty())
// Create paylaod
guard let payload = try? command.serializedData() else {
return
}
// Create mission message
let message = Message(messageNumber: 1, payload: payload)
// Send message
missionManager.send(message: message)
}
}
// Message class
class Message: MissionMessage {
var uid: String = "MyMission"
var messageNumber: UInt
var packageName: String = "your_company.missions.mymission.airsdk.messages"
var payload: Data
init(messageNumber: UInt, payload: Data) {
self.messageNumber = messageNumber
self.payload = payload
}
}
Read the latest message sent by the drone:
MissionManager - var latestMessage: MissionMessage
latestMessage.uid: UID of the destination mission.
latestMessage.packageName: Package name of the protobuf message.
latestMessage.messageNumber: Number of the message (the value of the id oneof of the message).
latestMessage.payload: Message payload serialized from the protobuf message.
var missionManager: Ref<MissionManager>?
/// Observer for my mission
func observeMission(drone: Drone) {
// Get mission manager
missionManager = drone.getPeripheral(Peripherals.missionManager) { [unowned self] missionManager in
// Get missions
guard let missions = missionManager?.missions else {
return
}
// Test if my mission is inside this array.
guard missions.keys.contains(missionUid) else {
return
}
// Get the latest message
guard let message = missionManager?.latestMessage else {
return
}
let uid = message.uid
let messageNumber = message.messageNumber
let packageName = message.packageName
let payload = message.payload
// Print mission parameters
print("uid: \(uid) messageNumber: \(messageNumber) packageName: \(packageName)")
// Decode mission message
do {
let event = try Your_Company_Missions_Mymissions_Airsdk_Messages_Event(serializedData: payload)
DispatchQueue.main.async {
print("Event: \(event.id)")
switch event.id {
case .event1:
break
case .event2:
break
}
}
} catch {
print("Failed to extract protobuf data from My mission message")
}
}
}
Mission web server REST API#
Instead of using the Ground SDK to manage flight missions, the drone web server REST API can also be used.
For more information see the WebAPI Module Mission.