Application based on the DJI Mobile SDK for streaming synchronized video data and flight data using websockets to a custom-made server. It provides basic control functionality, such as in the DJI Go 4 / DJI Fly apps. The app should be compatible with most of the DJI drones (tested on Mavic Mini 1, Mavic Pro, Mavic 2 Dual Enterprise).
- Implement a WebSocket server, here is our Unity example.
- In the drone GUI, click the Server IP button and fill in the IP address of the server and port (make sure that the port is open).
- The drone, as a WebSocket client, tries to establish the connection.
- If successful, the drone sends a handshake message, with the information about the drone type (ctype 0 means the client is a drone):
{ "type":"hello", "data":{ "ctype":0, "drone_name":"MavicPro", "serial":"12345" } } - The server needs to implement a handshake reply. The drone expects a reply message in the following format (client_id is handled by the server, can be any identification string, for instance, an ID of the websocket client):
{ "type":"hello_resp", "data":{ "client_id":"41f28cb6", } } - When successfully exchanged handshake messages, the drone is ready to start sending its data. To start, press the Live button.
- The drone starts sending its data in binary format, encoded as:
Binary data is split into a JSON message containing the flight data and JPEG bytes containing the current frame of the drone video feed. The first 4 bytes tell you the length of the JSON message, and the 4 bytes starting from (4 + length of JSON message) tell you the length of the JPEG image.
| First 4 bytes == JSON bytes length | JSON bytes | 4 bytes + JSON bytes length + 4 bytes == JPEG image bytes length | JPEG bytes | |------------------------------------|------------|------------------------------------------------------------------|------------|
- When decoding the JSON bytes to a string using UTF-8, the following structure of the flight data should be received (filled with example data):
Where:
{ "type":"data_broadcast", "data":{ "client_id":"41f28cb6", "altitude":234.6, "relative_altitude":10, "gps":{ "latitude":49.24031521243185, "longitude":16.613207555321484 }, "aircraft_orientation":{ "pitch":-4.0, "roll":-1.2, "yaw":81.2, "compass":81.2 }, "aircraft_velocity":{ "velocity_x":0.1, "velocity_y":1.0, "velocity_z":0.1 }, "gimbal_orientation":{ "pitch":0.0, "roll":0.0, "yaw":81.1, "yaw_relative":0.0 }, "satellite_count":16, "gps_signal_level":3, "battery":60, "sticks":{ "left_stick": { "x":0 "y":0 } "right_stick": { "x":0 "y":0 } } "timestamp":"2025-06-27 14:45:06.843" } }- altitude is AMSL (height above mean sea level), to work, the takeoff altitude needs to be manually set using the button Altitude.
- relative_altitude stands for AGL (above ground level) altitude.
- sticks report the current configuration of the physical remote controller. Values are in the range of [-660, 660]. Zero means the sticks are centered, in the default position.
- JPEG byte array can be directly decoded and displayed in the remote GUI.
- To create some level of autonomy, virtual sticks functionality can be used. To enable virtual stick mode, send from the server following message:
{ "type":"enable_control", "data":{ "enable":true, } } - The drone starts listening for command messages with the velocities of individual drone axes, in the following format:
Where:
{ "type":"control_command", "data":{ "pitch":0, "roll":0, "yaw":0, "throttle":0, "gimbal_pitch":0 } }- pitch (forward / backward), roll (left / right), throttle (up / down) are velocities in m/s. For slow speed, use values around 0.1.
- yaw is velocity in deg/s. For slow speed, use values around 10.
- gimbal_pitch is velocity in deg/s for tilting the gimbal up/down. For slow speed, use values around 10.
