Introduction to Payload SDK for DJI Drones

| 20 min read
Author: masayuki-kono masayuki-konoの画像
Information

To reach a broader audience, this article has been translated from Japanese.
You can find the original version here.

Introduction

#

At Mamezou, we are working on the development of a solar panel cleaning robot system.

This system consists of a robot that cleans solar panels and a drone that transports the robot. In this article, we introduce the Payload SDK (https://developer.dji.com/doc/payload-sdk-tutorial/en/tutorial-map.html), which is the development technology on the drone side.

Project Overview

#

To keep the power generation efficiency of solar panels at its maximum, it is essential to regularly remove the dust and dirt that accumulate on their surfaces.
Cleaning by hand may be possible for small panels for household use, but for large-scale power generation facilities such as mega-solar plants, manual cleaning operations are not realistic in terms of efficiency and cost.

Image of a mega-solar power plant

At our company, we are developing an autonomous cleaning robot for such power generation facilities.

Cleaning Robot System Configuration

#

In power plants, linked solar panels are installed at positions that are separated from each other. The solar panels are installed at a height of more than 2 m above the ground, and it is difficult to manually transport the robot between panels.

Therefore, this system adopts a method of using a drone to transport the robot.

Prototype transport scene

The main components are as follows.

Cleaning Robot

#

An in-house developed AMR that autonomously navigates while cleaning solar panel surfaces with brushes. It is the payload for the drone and is designed so that its total weight is within the drone's payload capacity. It was also exhibited at the 2025 International Robot Exhibition.

Cleaning robot on display

Drone

#

We use the DJI FlyCart 30. Its payload capacity is 30 kg in dual-battery mode (maximum flight time 18 minutes) and 40 kg in single-battery mode (maximum flight time 9 minutes).

The remote controller runs an application called DJI Pilot 2 on a DJI RC Plus.

DJI RC Plus

Payload Device

#

The standard cargo case included with the FlyCart 30 has internal dimensions of 573×416×305 mm, which cannot accommodate the robot. Therefore, we are developing a device to secure the robot to the drone.

Standard cargo case

Configuration of the Payload Device

#

The main role of the payload device in this system is to secure the robot to the drone.

The lock mechanism is controlled via the drone's remote controller operations to secure the robot. A single-board computer (SBC) inside the payload device handles the lock mechanism control and provides widgets to the remote controller.

DJI provides an SDK for payload device development called the Payload SDK (https://developer.dji.com/doc/payload-sdk-tutorial/en/tutorial-map.html), and the payload device runs an application developed with this SDK on the SBC.

For the FlyCart 30, the following interfaces are provided for payload devices.

  • E-Port Lite
    • USB Type-C maintenance port
    • Can be connected directly via USB Type-C cable to a PC with DJI Assistant 2 installed for firmware updates and log collection
    • For platforms like the FlyCart 30 that do not provide an E-Port, E-Port Lite can be connected to an SBC via a USB to TTL serial module and used as an expansion port
  • Payload Port
    • Power supply port for payload devices
    • Rated voltage is 51.2 V

Since the lock mechanism of this system is still under development, details are omitted. The following is a conceptual diagram of the configuration when using a CAN-compatible servo. In this case, power to the servo is supplied from the Payload Port, and the SBC communicates with the aircraft via E-Port Lite to control the servo.

Payload device configuration image

Various Expansion Ports Provided by the Aircraft

#

The above configuration is an example for the FlyCart 30. Some aircraft models provide expansion ports other than E-Port Lite.

A widely supported expansion port that provides power, UART, and USB. By connecting a custom payload via the E-Port Development Kit, UART and USB communications become possible.

E-Port Lite has limitations in obtaining camera images, but with E-Port and the Development Kit, USB is extended, allowing the use of many functions.

Connection setup via Development Kit

An expansion port provided on the Matrice 400 (announced June 2025). While aircraft typically have one E-Port, E-Port V2 has four ports on the underside of the M400, each capable of supplying 120 W of power. The power output can be adjusted in three levels: 13.6 V, 17 V, and 24 V. It supports USB 3.0, allowing simultaneous acquisition of 4K streams and radar point cloud data. Custom payloads can connect via the E-Port V2 Development Kit.

A standard interface equipped on the gimbals of M300 RTK (announced May 2020) and M350 RTK (announced May 2023), also called PSDK Port. It is an interface for connecting DJI gimbal payloads (cameras, sensors, etc.) represented by the Zenmuse series.

Zenmuse H20

For third parties, a Payload SDK Development Board Kit 2.0 is provided, which can be used as an intermediary to connect custom payloads.

An older interface currently provided only by the M300 RTK. It predates the E-Port and uses the Onboard SDK (OSDK), but its final release was 2021-02-02 (OSDK 4.1.0), and no new features have been added since.

The OSDK Port supports not only the OSDK Expansion Module but also connection with the E-Port Development Kit, allowing the use of the Payload SDK (PSDK). According to OSDK Version Support Information (dated May 9, 2023), all OSDK 4.x features have been migrated to PSDK V3. For new development, migration to PSDK V3 is recommended.

Expansion Ports by Aircraft Model

#

Below is a list of expansion ports provided by current aircraft models, excerpted from Standard Hardware Port Introduction.

Aircraft Port Name Supports App Binding
FlyCart 100 E-Port Lite
FlyCart 30 E-Port Lite
Matrice 4D/4TD E-Port, E-Port Lite
Matrice 4E/4T E-Port, E-Port Lite
Matrice 3D/3TD E-Port, E-Port Lite
Matrice 30/30T E-Port
Mavic 3E/3T E-Port
M400 E-Port V2
M350 RTK E-Port
M350 RTK Gimbal Port
M300 RTK OSDK Port
M300 RTK Gimbal Port

Aircraft with a check in the Supports App Binding column require the Application Binding procedure. We plan to introduce this procedure in a separate article.

Supported Functions per Port Type

#

The table below is a selection of supported functions by port type for some aircraft, extracted from the Aircraft Type Function Difference list.

Because supported functions vary by aircraft even within the same port type, you cannot determine functionality solely by port type (for example, “If it has an E-Port, you can use this feature”). For instance, Hoisting Control is only supported on the E-Port Lite of the FlyCart 100.

Function Name Function Level FlyCart 30 E-Port Lite Matrice 4E/4T E-Port Matrice 400 E-Port V2 Matrice 350 RTK Gimbal Port
Log Management basic
Data Subscription basic
Basic Camera Function basic
Basic Camera Management advanced
Gimbal Function basic
Gimbal Management advanced
Power Management basic
Flight Control advanced
Custom Widget basic
Custom HMS basic
HMS Manager advanced
Time Synchronization basic
Low-speed Data Transmission basic
Camera Video Stream basic
Playback Download basic
X-Port Function basic
Camera Stream Liveview advanced
Local Upgrade basic
High-speed Data Transmission basic
Positioning basic
SDK Interconnection basic
Waypoint Mission advanced
Speaker basic
Hoisting Control basic
Access Internet advanced
Network RTK advanced

Serial Communication Specifications

#

The Payload SDK uses UART and USB serial communication.

In USB communication, USB Gadget (a mechanism for making a Linux device behave as a USB device) is used, and two types of communication modes are utilized:

  • Bulk (USB Bulk Transfer)
    • Bidirectional raw data communication between the device and the host
  • RNDIS (Remote Network Driver Interface Spec)
    • A specification that emulates Ethernet over USB
    • IP communication between the device and the host

The support status of each port is shown in the table below.

Port Only UART UART+Bulk UART+RNDIS Only Bulk Only RNDIS
E-Port Lite
E-Port
E-Port V2
Gimbal Port
Mode Description
Only UART Communicate with the aircraft using UART only
UART+Bulk Communicate with the aircraft using both UART and Bulk
UART+RNDIS Communicate with the aircraft using both UART and RNDIS
Only Bulk Communicate with the aircraft using Bulk only
Only RNDIS Communicate with the aircraft using RNDIS only

Only E-Port V2 can communicate using only Bulk or only RNDIS; UART is required for all other ports.

For more details, refer to the following DJI Developer Support pages (in Chinese). A DJI Developer Center account registration is required to view any of them.

Since any port can communicate via UART only, it is recommended to initially connect only via UART and start by verifying the operation of the application developed with the Payload SDK.

Payload SDK API Specifications

#

The Payload SDK API Reference appears to list API specifications automatically generated from the SDK’s source code headers. However, there is very little descriptive text, and it mainly focuses on lists of functions and types. Therefore, understanding the API specifications requires referring to sample code and verifying actual operation on hardware.

Payload SDK Sample Applications

#

The DJI Payload-SDK repository publishes the Payload SDK libraries and sample applications that use them.

Directory Structure

#

The repository directory structure is as follows:

├── psdk_lib
│   ├── include
│   └── lib
│       ├── aarch64-linux-gnu-gcc
│       ├── arm-linux-gnueabi-gcc
│       ├── arm-linux-gnueabihf-gcc
│       ├── armcc_cortex-m4
│       └── x86_64-linux-gnu-gcc
├── samples
│   ├── sample_c
│   │   ├── module_sample
│   │   │   ├── camera_emu
│   │   │   ├── camera_manager
│   │   │   ├── cloud_api
│   │   │   ├── data_transmission
│   │   │   ├── fc_subscription
│   │   │   ├── flight_control
│   │   │   ├── gimbal_emu
│   │   │   ├── gimbal_manager
│   │   │   ├── hms
│   │   │   ├── interest_point
│   │   │   ├── liveview
│   │   │   ├── mop_channel
│   │   │   ├── payload_collaboration
│   │   │   ├── perception
│   │   │   ├── positioning
│   │   │   ├── power_management
│   │   │   ├── tethered_battery
│   │   │   ├── time_sync
│   │   │   ├── upgrade
│   │   │   ├── utils
│   │   │   ├── waypoint_v2
│   │   │   ├── waypoint_v3
│   │   │   ├── widget
│   │   │   ├── widget_interaction_test
│   │   │   └── xport
│   │   └── platform
│   │       ├── linux
│   │       │   ├── common
│   │       │   │   ├── 3rdparty
│   │       │   │   ├── monitor
│   │       │   │   ├── osal
│   │       │   │   └── upgrade_platform_opt
│   │       │   ├── manifold2
│   │       │   │   ├── application
│   │       │   │   └── hal
│   │       │   ├── manifold3
│   │       │   │   ├── app_json
│   │       │   │   ├── application
│   │       │   │   └── hal
│   │       │   ├── nvidia_jetson
│   │       │   │   ├── application
│   │       │   │   └── hal
│   │       │   └── raspberry_pi
│   │       │       ├── application
│   │       │       └── hal
│   │       └── rtos_freertos
│   │           ├── common
│   │           │   └── osal
│   │           ├── gd32f527_development_board
│   │           │   ├── application
│   │           │   ├── bootloader
│   │           │   ├── drivers
│   │           │   ├── hal
│   │           │   ├── middlewares
│   │           │   └── project
│   │           └── stm32f4_discovery
│   │               ├── application
│   │               ├── bootloader
│   │               ├── drivers
│   │               ├── hal
│   │               ├── middlewares
│   │               └── project
│   └── sample_c++
│       ├── module_sample
│       │   ├── camera_manager
│       │   ├── flight_controller
│       │   ├── gimbal
│       │   ├── hms_manager
│       │   ├── liveview
│       │   ├── perception
│       │   ├── positioning
│       │   └── widget_manager
│       └── platform
│           └── linux
│               ├── common
│               │   ├── 3rdparty
│               │   └── osal
│               ├── manifold2
│               │   ├── application
│               │   └── hal
│               ├── manifold3
│               │   ├── application
│               │   └── hal
│               ├── nvidia_jetson
│               │   ├── application
│               │   └── hal
│               └── raspberry_pi
│                   ├── application
│                   └── hal

psdk_lib

#

Platform-specific static libraries are placed here. For each toolchain, refer to Using third-party development platforms. In the case of Raspberry Pi or Jetson, use the aarch64-linux-gnu-gcc/libpayloadsdk.a in this directory. If you need a toolchain not available here, you can request it from SDK Technical Support (dev@dji.com), and they will provide a static library for that toolchain. See also PSDK platform static library link.

samples

#

module_sample

Sample code for each feature provided by the SDK is located here. Samples are prepared for each feature, such as camera management, flight control, gimbal, live view, HMS, positioning, and widgets.

platform

Platform-dependent code is organized here. The source code placed in hal/ is the implementation of the hardware abstraction layer (HAL) (e.g., network, UART, USB Bulk, I2C).

The entry point of the sample applications is application/main.cpp. From there, application/application.cpp is called, and the registration of HAL handlers and the setup of the SDK via DjiCore_Init are performed. Which of UART, Bulk, or RNDIS is used is determined by the combination of HAL handlers registered.

Below is an excerpt of the HAL handler registration section (branching based on CONFIG_HARDWARE_CONNECTION).

Payload-SDK/samples/sample_c++/platform/linux/raspberry_pi/application/application.cpp

    returnCode = DjiPlatform_RegHalI2cHandler(&i2CHandler);
    if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) {
        throw std::runtime_error("register hal i2c handler error");
    }

#if (CONFIG_HARDWARE_CONNECTION == DJI_USE_UART_AND_USB_BULK_DEVICE)
    returnCode = DjiPlatform_RegHalUartHandler(&uartHandler);
    if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) {
        throw std::runtime_error("Register hal uart handler error.");
    }

    returnCode = DjiPlatform_RegHalUsbBulkHandler(&usbBulkHandler);
    if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) {
        throw std::runtime_error("Register hal usb bulk handler error.");
    }
#elif (CONFIG_HARDWARE_CONNECTION == DJI_USE_UART_AND_NETWORK_DEVICE)
    returnCode = DjiPlatform_RegHalUartHandler(&uartHandler);
    if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) {
        throw std::runtime_error("Register hal uart handler error.");
    }

    returnCode = DjiPlatform_RegHalNetworkHandler(&networkHandler);
    if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) {
        throw std::runtime_error("Register hal network handler error");
    }
#elif (CONFIG_HARDWARE_CONNECTION == DJI_USE_ONLY_USB_BULK_DEVICE)
    returnCode = DjiPlatform_RegHalUsbBulkHandler(&usbBulkHandler);
    if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) {
        throw std::runtime_error("Register hal usb bulk handler error.");
    }

#elif (CONFIG_HARDWARE_CONNECTION == DJI_USE_ONLY_NETWORK_DEVICE)
    returnCode = DjiPlatform_RegHalNetworkHandler(&networkHandler);
    if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) {
        throw std::runtime_error("Register hal network handler error");
    }

    //Attention: if you want to use camera stream view function, please uncomment it.
    returnCode = DjiPlatform_RegSocketHandler(&socketHandler);
    if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) {
        throw std::runtime_error("register osal socket handler error");
    }
#elif (CONFIG_HARDWARE_CONNECTION == DJI_USE_ONLY_UART)
    /*!< Attention: Only use uart hardware connection.
     */
    returnCode = DjiPlatform_RegHalUartHandler(&uartHandler);
    if (returnCode != DJI_ERROR_SYSTEM_MODULE_CODE_SUCCESS) {
        throw std::runtime_error("Register hal uart handler error.");
    }
#endif

The following is an illustrative diagram of the application's layer structure.

flowchart LR
  subgraph App["Entry Point"]
    A["application.cpp<br/>HAL Registration & DjiCore_Init"]
  end
  subgraph SDK["SDK Core"]
    B["DjiCore_Init, etc."]
  end
  subgraph HAL["HAL Handler"]
    H["Network / UART /<br/>USB Bulk / I2C"]
  end
  A --> B --> H

At runtime, when DjiCore needs to perform operations such as writing to UART, the pre-registered HAL handlers are called back. The flow is as follows:

sequenceDiagram
  participant App as application.cpp
  participant Core as DjiCore
  participant HAL as HAL Handler

  App->>Core: Register HAL handler
  App->>Core: DjiCore_Init()
  Note over Core: Initialization complete
  Note over Core,HAL: At runtime (when UART write is needed)
  Core->>HAL: Callback invocation<br/>(e.g., HalUart_WriteData)
  HAL-->>Core: Result

In the diagram, application.cpp and the HAL handlers are included in the sample code, and DjiCore is provided as a static library in psdk_lib.

At first glance, you may feel that the amount of code in the sample seems large, but the platform-dependent code can basically be used as is. However, even on the same platform, there may be cases where it does not work with the latest OS or accompanying libraries, so maintenance by the developer is necessary.

Verification Environment for the Payload SDK

#

Unfortunately, verifying the operation of a Payload SDK application requires an actual aircraft.

DJI Assistant 2 has a feature to simulate flights, but it can only be used when connected to a vehicle paired with the transmitter. Additionally, Payload SDK communications also assume an actual aircraft.

You need to acquire or rent a model equipped with the same ports (E-Port Lite or E-Port) as the aircraft used in production operations.

At our company, we use the FlyCart 30 in production operations, but because it is expensive, we develop the application on a different model (e.g., Matrice 4E) equipped with E-Port Lite for development and debugging.

Conclusion

#

In this article, we introduced the Payload SDK used for drone-side development in a system that transports solar panel cleaning robots by drone.

Even when searching on the Internet, materials related to the Payload SDK are limited to those provided by DJI, and practical procedures are mainly centered on DJI Developer Support’s Chinese pages. It took us considerable effort to get our application up and running. We hope that this article will serve as a starting point for those who are also working on developing custom payloads.

Practical topics of the Payload SDK such as obtaining flight status, widget integration, and the Application Binding procedure will be covered in detail in upcoming articles. We appreciate your continued interest.

豆蔵では共に高め合う仲間を募集しています!

recruit

具体的な採用情報はこちらからご覧いただけます。