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 expose a Bluettoth Low Energy joystick from the Android App... But I didn't succeed... So i decided to add an ESP32 in the middle; Android and ESP exchanging UDP messages and ESP32 exposing the BLE joystick...
Well quite long chain but it works and it even works with no perceptible latency !
here is the flowchart :
- control : DJI MotionController (IMU high accuracy)
- connection : USB host
- process : Android (Raw data conversion).
- network : UDP Unicast with broadcast discovery (zero config, minimal latency).
- Payload : JSON (compacted)
- Output : ESP32 BLE Gamepad
- decoding : PC USB HID virtual joystick (details here : https://hackaday.io/project/204638-a-bluetooth-joystick-for-tx16s-radio-or-others)
- running: PhoenixRC launcher
- flying : Phoenix RC controlled by the eggplant !
And here is the result
and it works. As you can see motion is smooth and learning curve is rather fast (at least on a simulator).
So next step will be to try to embed this code into a ESP32-C3 MCU and to connect all this to my Buddy box
in order to fly a real RC plane
But before that here is a much simpler approach: a plugin for UCR
DJI MotionController plugin for Universal Control Remapper
UCR (Universal Control Remapper) is a nice open source solution to create virtual Joysticks. I already used it to allow Phoenix RC simulator to run with Bluetooth joysticks (see here end of page).
UCR alllows to use "plugins" to remap the axies, buttons and so on to other virtual joystick.
However a plugin for a DJI motionController didn't exist yet... So I coded it !
you can find here on my gitub project : DJI motion controller UCR plugin
How to use it ?
Before installing UCR you need first to install the ViGem driver for emulation of DS4 and XBox360 controllers
ViGemBus driver
ViGem Currently supports emulation of the following USB Gamepads:
- Microsoft Xbox 360 Controller
- Sony DualShock 4 Controller
It can be found on this site : ViGEmBus driver – Virtual Gamepad Emulation Framework (ViGEm)
Download it and install it
UCR configuration
Configuring UCR is finally not complex at all !
download it, Install it.
Then locate the installation folder and copy my UCRplugins.dll into the plugins folder of UCR
Then create a new "profile"
then you will name it as you want (DJI_USB on my side !) and you will have to select the DJImotionController joystick into the list of existing joysticks and remap it to a "virtual" DS4 joystick

Now you will have to remap your DJI motion controller axis to the virtual one.
Double click on the profile name (DJI_USB) it will open a new window

There you can click on "DJI MotionController" plugin to add a new "mixer" that you will have to fill (use the 3 dots menu on the right to bind each axis of the BLE joystick to the one of the DS4 controller

select the input axis one by one, then map the Yaw/Pitch/roll axis of the output to the virtual joystick ones

And you are good to go: your motion controller is now a regular joystick !
Running the virtual joystick
Once remmaping is done, using UCR virtual joystick is fairly simple :
select the profile and click the "play" button !

Now yo should see a new "Wireless Controller" under windows "game controllers"

You can select it and see that you DJI MotionController is now seenas a joystick with "normal behavior"

Bear however in mind that the yaw axis has to be triggered when your hand is in "stable" position. To do this, simply pull up the "gimbal" slider. To desactivate : pull it down !
See it working here :
Enjoy
If you don't like UCR, another handy method does exist :JoystickGremlin
A DJI Motion Controller plugin for JoystickGremlin
JoystickGremlin installation
You will need to install Vjoy, select thelatest install from this site. This will install Vjoy and the needed driver.
Now install JoystickGremlin from the download tab.
Copy the DjiMotionController plugin into the plugin folder of JoystickGremlin
You are ready to configure joystickGremlin with your DJI controller.
The idea is to remap the DJI into a viratual Vjoy joystick using the plugin to perform all the "maths"
Start to select your HID Joystick (the DJI one) from the main screen of Gremlin.
You should see 5 axis and a lot of buttons.
The axis MUST NOT be directly remapped to Vjoy as we want to use the plugin for this. So you will have to add a "NoOp" remapper (something which does nothing). Do this for the 5 axis.
You can (if you want ) remap the 4 first button to Vjoy ones directly as they are not handled by the plugin.

Now go the plugin tab and
- add the DJI motion Controller plugin file.
- click the gear button
- assign each axis in the order of the picture

click on the gamecontroller icon and it will become Green (activated)
go to the "tools/Input Viewer" menu and select both the HID joystick and the Vjoy one.
You will see the "magic" the DJI axis (on top) are mapped to Yaw / pitch /roll joystick. Its sensitivity is perfect for a wrist rotation, south gap is no longer present on Yaw.

Your Vjoy DJI controller works !
Do Not forget to save this configuration for next time (menu Files/save Profile). And do not forget to click on the green gamecontroller to launch the virtual joystick.
Most of the flight simulators will accept Vjoy joystick to run. But not Phoenix... So if you want to use it a next step is required (simple one)
Xoutput virtual Xbox gamecontroller
Phoenix requires Xbox like game controller to be launced with the "SimEmu.exe" launcher.
Downlod Xoutput from this site. Install it as well as VigemBus driver
Launch it and select your Vjoy joystick (it must be "green" in joystickGremlin) and clcik on Add controller


And you are good to go.
Check in Windows game controller (joy.cpl) that you see a XBOX 360 controller. click on "advanced" and selct it as prefered joystick (to be in the first row!)

Launch Phoenix SimEmu.exe and select the first joystick proposed calibrate and fly !
JP Gleyzes