Close
0%
0%

Hacking DJI motion controller as regular joystick

I wanted to use my Avata Motion controller as a regular joystick

Similar projects worth following
1.4k views
0 followers

DJI Motion Controller

The DJI motion controller 1 was provided by DJI when I bought the drone pack.

This device is very cool and allows to pilot the drone even though you have never flown a drone... It's sooo simple !

I wanted to reuse this device to try to pilot RC planes connected to my TX16s buddy box.

It's easy to connect to motion controller to a PC or a smartphone and see it as a joystick.

But its outputs are quite misleading... So I decided to spend some time on this tricky challenge !

And you know what ? It works :-)

with a complex setup:

It's more or less like flying with an egg plant !

And works also with a much simpler setup :


Decoding the Motion Controller

I mounted the device on a tripod and connected it to my phone with a USB cable and entered the data into my phone using gamepad Tester App


It told me that this device has 4 buttons and 5 axis

  • X, Y and Rx are interresting rotations (yaw, pitch roll... almost !) (values -1 to +1)
  • Z is throttle (values -0.64 to + 0.64)
  • Ry is the gimbal motion (values -0.64, 0 and +0.64 three positions switch)

If I align the motion controller to the north values are

  • x = 0
  • Y = 0
  • Rx = 0

from this "zero" postion if I rotate one axis to point to top I get

  • x = 0
  • y = 0.5
  • Rx = 0

if I continue rotation on the same axis, pointing to north and down, I get:

  • x =0
  • y= -0.5
  • Rx = 0

if I rotate around the vertical axis from "zero" postion to point to "east" I get:

  • X = 0
  • Y = 0
  • Rx = 0.5

if I go on to point to south I get

  • X = 0
  • Y = 0
  • Rx = 1 (and if I go a little further to south west it jumps to -1)

now if I start from "zero" position and twist on the "roll" axis 90° I get

x=0
y= 0.5
Rx = 0


Preliminary conclusions 


The yaw axis is directly given by Rx value. It's an angle in the fixed reference frame of earth (values -1 to +1 for -PI to PI)
X and Y axis are given also in the earth reference frame (so they move together when you rotate around the vertical axis).
If we want them in motion controller ref frame (as any joystick or RC controller) then we have to apply rotation transformations from this fixed frame to the moving one (your hand)


Finally Rx gives an absolute angle. We want an angular velocity to control the rudder of our plane. So we need to evaluate the variation of Rx angle during time...


fixed frame to hand frame

Heading angle

heading =   Rx * pi  (expressed in radians, absolute value 0 = North)

Pitch angle

Pitch= -( Y*cos(heading) - X*sin(heading))

Roll angle

{Roll= - ( X*cos(heading) + Y*sin(heading))


With these formulae we have pitch and roll expressed into your "hand" ref frame. But heading is still espressed as an absolute value relative to north... To convert heading into yaw motion we need "a trick"

I used the "gimbal rotation" axis to latch the current heading (position of your hand relative to north) and then to compute variation of heading from this position.

If I release the gimbal switch then yaw is reset to zero until you press again this button.

I did try other solutions (introducing a "dead band" closed to yaw = 0 and detecting motion only if at a certain speed. It worked but was prone to some confusion.... So this simple "press and yaw" solution seemed to be better !


Test application

I wrote a small Basic for Android Application, starting from a working gamepad decoder, and implementing the formulae above.

result can be seen into this video


Current status is that it is quite easy to decode this motion controller and to compute "classical" Yaw/Pitch/Roll values that we could into into our RC radio Rudder/Elevator/Ailerons.


Testing with over engineered solution

To test this system "in flight" I wanted first to port the equations into an ESP32-S3 using ESP32Beans  USB host library. But it crashed... Details into this log

So I tried a different "over engineered approach"... go on with the APP and try to...

Read more »

  • ESP32-S3 fails to read DJI motion Controller

    JP Gleyzes12/21/2025 at 17:49 0 comments

    ESP32-S3 fails to read DJI motion Controller

    I tried to read the HID joystick from DJImotion Controller with my ESP32-S3 board.

    It was connected to the ESP32Beans/USBhostHID library .

    But It fails at startup with this error message :

    E (419) HCD DWC: EP MPS (512) exceeds supported limit (128)
    
    E (420) USBH: EP Alloc error: ESP_ERR_NOT_SUPPORTED
    
    E (420) USB HOST: EP allocation error ESP_ERR_NOT_SUPPORTED
    
    E (424) USB HOST: Claiming interface error: ESP_ERR_NOT_SUPPORTED
    
    ESP_ERROR_CHECK failed: esp_err_t 0x106 (ESP_ERR_NOT_SUPPORTED) at 0x420025e8

    Investigating with the help of Gemini gave me no hope... 

    The error you are seeing is a hardware limitation of the ESP32-S2/S3 USB peripheral. It is not a bug in your code, but a physical constraint of how the ESP32 handles USB packets.

    So we tried with another board MAX3421E. This chip is used in the "USB Host Shield" and can handle larger buffers than the ESP32's built-in peripheral. 

    But result was also a failure, and the final conclusion was :

    The Verdict: A Hardware "Hard Limit"
    This confirms why you see USB State: 90 (the device is connected and recognized) but no data arrives.
    
    The DJI Controller: It is a "High-Speed" device. It is telling the USB Host, "I will only send data in chunks of 512 bytes."
    
    The MAX3421E: This chip is a "Full-Speed" controller. It has a physical internal memory (FIFO) of only 64 bytes.
    
    The Conflict: When the DJI controller tries to send its 512-byte HID report, the MAX3421E physically cannot fit it into its memory. The hardware simply ignores the packet or throws an internal overflow error that the library doesn't display.

    At this point I stopped investigating !

    FInally all this is confirmed by Claude AI: 

    ESP32-S3 USB Host cannot support DJI Motion Controller's 512-byte endpoints

    The DJI Motion Controller's 512-byte Maximum Packet Size (MPS) is fundamentally incompatible with ESP32-S3 USB Host hardware, which limits periodic endpoint MPS to 128 bytes. This is a hardware constraint of the DWC_OTG controller's internal FIFO sizing—not a software-configurable limitation. However, several workarounds exist depending on whether the device's descriptors are misconfigured or genuinely require High-Speed USB.

    The root cause is Full-Speed versus High-Speed USB incompatibility

    ESP32-S3's USB Host controller only supports USB Full-Speed (12 Mbps) and Low-Speed (1.5 Mbps)—it lacks the High-Speed PHY interface entirely. The chip uses Synopsys DesignWare DWC_OTG controller configured with OTG_HSPHY_INTERFACE = 0, permanently disabling High-Speed capability. Only the newer ESP32-P4 supports USB High-Speed (480 Mbps).

    Per USB 2.0 specifications, endpoint MPS limits differ dramatically by speed class:

    USB SpeedBulk MPS MaxInterrupt MPS MaxESP32-S3 Support
    Full-Speed (12 Mbps)64 bytes64 bytes✓ Supported
    High-Speed (480 Mbps)512 bytes1024 bytes✗ Not supported

    When the DJI Motion Controller reports 512-byte MPS for its interrupt endpoint, it's operating with High-Speed descriptors. A USB 2.0 compliant device should fall back to Full-Speed operation when connected to a Full-Speed-only host, presenting descriptors with ≤64-byte MPS. The DJI Motion Controller appears to violate this requirement by only offering High-Speed configuration.

    ESP32-S3 hardware imposes fixed MPS ceilings

    The error "EP MPS (512) exceeds supported limit (128)" originates from ESP-IDF's hcd_dwc.c driver, which enforces hard limits based on the DWC_OTG controller's 1024-byte total FIFO partitioned between three buffers:

    Transfer TypeMaximum MPSFIFO Allocation
    IN endpoints (all types)408 bytesRX FIFO shared by all IN
    Non-periodic OUT (bulk/control)256 bytesNPTX FIFO
    Periodic OUT (interrupt/iso)128 bytesPTX FIFO

    The 128-byte limit for periodic transfers directly blocks the DJI Motion Controller's 512-byte interrupt endpoint. ESP-IDF's CONFIG_USB_HOST_HW_BUFFER_BIAS_* Kconfig options theoretically allow rebalancing these FIFOs, but they are non-functional...

    Read more »

View project log

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates