This project enables the DJI FPV Remote Controller 3 to work as a standard gamepad in macOS simulators like Liftoff, Velocidrone, Uncrashed, DCL, and any other game that supports game controllers.
- ✅ Full macOS compatibility: Works with ALL games (SDL2, Unity, native, etc.)
- ✅ Controller detection: DJI RC3 recognized as "DJI Virtual Joystick"
- ✅ Analog sticks: Both sticks mapped correctly (Mode 2 layout)
- ✅ 24 buttons: Available for in-game binding
- ✅ Dial/scroll wheel: Mapped to gamepad triggers
- ✅ No launcher scripts needed: Appears as native USB gamepad
- macOS 14.0+ (Sonoma or later)
- DJI FPV Remote Controller 3 (firmware 01.00.00 or later)
- USB-C cable to connect controller to Mac
- Apple Developer Program membership (for code signing)
- SIP (System Integrity Protection) disabled OR user approval in System Settings
- SIP disabled for development/testing
- SDL2 library (via Homebrew:
brew install sdl2) - Games that use SDL2 input
This project provides three different approaches to enable the DJI RC3:
Best for: All games, permanent installation, production use
Pros: Works with every game, no scripts needed, system-level driver
Cons: Requires Apple Developer account, more complex installation
The DriverKit extension intercepts the DJI USB device at the kernel level and presents it with a corrected HID descriptor that macOS recognizes as a standard gamepad.
Quick Install:
-
Build the project:
# Generate Xcode project xcodegen generate # Open and build open DJIGamepad.xcodeproj # Build both DJIGamepadDriver and DJIGamepadLoader targets
-
Install the driver extension:
Method A - Using the Loader app (Recommended):
# Build the app, then run it open build/Debug/DJIGamepadLoader.app # Click "Install Driver" in the GUI # Approve in System Settings > Privacy & Security
Method B - Manual installation (requires SIP disabled):
# Ensure the dext is built bin/install-dext.sh # Verify installation systemextensionsctl list | grep DJI
-
Connect your controller:
# Power on DJI RC3 and connect via USB-C # Verify it's detected: ioreg -p IOUSB | grep "DJI Virtual Joystick"
Uninstall:
# Method A - Using the Loader app
open build/Debug/DJIGamepadLoader.app
# Click "Uninstall Driver"
# Method B - Manual uninstallation
sudo rm -rf /Library/SystemExtensions/*/DJIGamepadDriver.dext
sudo killall sysextd
systemextensionsctl reset # If neededBest for: Development, testing, when DriverKit entitlements unavailable
Pros: Easier to build, good for debugging
Cons: Requires SIP disabled, must stay running in background
The Bridge creates a virtual HID device in user space, reads raw reports from the DJI controller, and forwards remapped data.
Quick Install:
-
Build the bridge:
# Compile the bridge swiftc DJIGamepadBridge/main.swift -framework IOKit -framework CoreFoundation -o dji-gamepad-bridge # Or use the Makefile target (if available) make bridge
-
Run the bridge:
# Start the bridge (keep terminal open) ./dji-gamepad-bridge # You should see: # "DJI FPV RC3 -> Virtual Gamepad Bridge" # "Virtual gamepad created: 'DJI FPV RC3 Gamepad'"
-
Connect your controller:
- Power on DJI RC3 and connect via USB-C
- The bridge will detect it automatically
- Verify in any game controller settings
Uninstall:
# Simply stop the bridge process
Ctrl+C in the terminal running dji-gamepad-bridge
# Or kill it if running in background
pkill -f dji-gamepad-bridgeRequirements:
- SIP must be disabled, OR
com.apple.developer.hid.virtual.deviceentitlement (requires Apple Developer account)
Best for: SDL2-based games only, no installation needed
Pros: Simple, works without drivers, good for quick testing
Cons: Only works with SDL2 games, requires launcher scripts or environment variables
This approach uses SDL2's game controller mapping database to tell SDL2-based games how to interpret the DJI controller's axes and buttons.
Quick Install:
-
Install SDL2:
brew install sdl2
-
Build the tools:
make
-
Install system-wide mapping:
bin/dji-gamepad-launch --install
-
Launch games:
# For supported simulators: bin/dji-liftoff # Launch Liftoff bin/dji-uncrashed # Launch Uncrashed bin/dji-dcl # Launch DCL The Game # For other SDL2 games: bin/dji-gamepad-launch "Game Name"
Uninstall:
# Remove the system-wide mapping
rm ~/.config/SDL-GameControllerDB/gamecontrollerdb.txt
# Or remove just the DJI entry:
sed -i '' '/DJI FPV RC3/d' ~/.config/SDL-GameControllerDB/gamecontrollerdb.txtLimitations:
- Only works with games that use SDL2 for input (Unity games on macOS typically do)
- Does NOT work with: Velocidrone, native macOS games using GCController directly
| Feature | DriverKit ⭐ | Bridge | SDL2 |
|---|---|---|---|
| Works with all games | ✅ Yes | ✅ Yes | ❌ SDL2 only |
| No background process | ✅ Yes | ❌ Must stay running | ✅ Yes |
| Works with SIP enabled | ✅ After approval | ❌ No | ✅ Yes |
| Easy to install | ✅ Easy | ✅ Easy | |
| Apple Developer account | ✅ Required | ❌ Not needed* | ❌ Not needed |
| Permanent solution | ✅ Yes | ❌ Temporary | ✅ Yes |
* Bridge requires SIP disabled unless you have the com.apple.developer.hid.virtual.device entitlement
Recommendation:
- New users with Developer account: Start with DriverKit (Option 1)
- Testing/development: Use Bridge (Option 2)
- Quick test with SDL2 games only: Use SDL2 mapping (Option 3)
The DJI FPV RC3 uses Mode 2 controls (standard for FPV):
| Physical Control | Gamepad Mapping | Notes |
|---|---|---|
| Left Stick L/R (Yaw) | Left Thumbstick X | - |
| Left Stick U/D (Throttle) | Left Thumbstick Y | Inverted (up = negative) |
| Right Stick L/R (Roll) | Right Thumbstick X | - |
| Right Stick U/D (Pitch) | Right Thumbstick Y | Inverted (up = negative) |
| Scroll Wheel/Dial | Left/Right Trigger | -660 to +660 range |
In-game, bind:
- Left stick to yaw (rotation) and throttle
- Right stick to roll and pitch
# Check if extension is loaded
systemextensionsctl list | grep DJI
# Should show: io.maido.DJIGamepadLoader.DJIGamepadDriver [activated enabled]
# Check USB devices
ioreg -p IOUSB | grep "DJI Virtual Joystick"# Build and run the test tool
make
./sdl_gc_test-
Check USB connection:
ioreg -p IOUSB | grep -i djiShould show both "DJI Virtual Joystick" (from our driver) and the raw device.
-
Verify extension status:
systemextensionsctl list
Look for
io.maido.DJIGamepadLoader.DJIGamepadDriverand ensure it says[activated enabled] -
Re-activate if needed:
# Open System Settings > Privacy & Security # Look for "System Extension" approval button # Click "Allow" and restart
The extension needs user approval:
- Open System Settings > Privacy & Security
- Scroll to Security section
- Look for message about "DJIGamepadDriver"
- Click Allow
- Reconnect the controller
For SDL2 games only:
# Check if game uses SDL2
otool -L "/Applications/Game.app/Contents/MacOS/Game" | grep -i sdl
# If no SDL2, use DriverKit or Bridge approach insteadFor all games:
- Ensure DriverKit extension is
[activated enabled] - Try reconnecting the USB cable
- Check in macOS System Settings > Game Controllers
The mapping has been verified with multiple simulators. If it feels wrong:
-
Check your controller mode:
- DJI RC3 should be in simulator mode (automatic when USB connected)
- Verify: Hold power button 3 seconds to turn off, reconnect USB
-
Use in-game calibration:
- Most simulators have controller calibration in settings
- Bind axes manually if needed
-
For SDL2 approach only - Edit the mapping:
# Edit gamecontrollerdb.txt # Invert an axis: lefty:a2 -> lefty:-a2 # Swap axes: Change the number after 'a:'
"Failed to create virtual HID gamepad"
- SIP is enabled: Disable SIP or use DriverKit approach
- Missing entitlement: Requires
com.apple.developer.hid.virtual.device
"No DJI device found"
- Ensure controller is powered on
- Try different USB-C cable/port
- Check with
ioreg -p IOUSB | grep DJI
# For all approaches:
xcode-select --install
# For SDL2 approach:
brew install sdl2
# For Xcode project generation:
brew install xcodegen# Build SDL2 tools
make
# Generate Xcode project for DriverKit/Bridge
xcodegen generate
# Build DriverKit extension
xcodebuild -scheme DJIGamepadDriver -configuration Debug
# Build Loader app
xcodebuild -scheme DJIGamepadLoader -configuration Debug
# Build Bridge
swiftc DJIGamepadBridge/main.swift -framework IOKit -framework CoreFoundation -o dji-gamepad-bridgeIMPORTANT: Before building for distribution, update the signing identity:
-
Edit
project.yml:settings: base: DEVELOPMENT_TEAM: "YOUR_TEAM_ID_HERE" # Replace with your Team ID targets: DJIGamepadLoader: settings: base: CODE_SIGN_IDENTITY: "Apple Development: your@email.com (YOUR_ID)"
-
Regenerate Xcode project:
xcodegen generate
The DJI FPV RC3 presents itself as a USB HID device with:
- Vendor ID: 0x2CA3 (DJI)
- Product ID: 0x1021
- HID Descriptor: Uses X/Y/Z/Rx/Ry usages
The issue: Apple's GCController framework expects standard gamepad axes (X/Y for left stick, Z/Rz for right stick). Since DJI uses Rx/Ry for the right stick instead of Rz, GCController doesn't recognize it as a gamepad.
DriverKit Approach: Intercepts the USB device at the kernel level and presents a corrected HID descriptor that uses proper gamepad usages (Z/Rz instead of Rx/Ry). This makes it appear as a native gamepad to ALL macOS apps.
Bridge Approach: Creates a virtual HID device in user space, reads raw reports from DJI, remaps the data to standard gamepad format, and forwards to the virtual device.
SDL2 Approach: Provides a mapping file that tells SDL2-based games how to interpret DJI's raw axes. Only works with SDL2 games.
| File | Description | Used By |
|---|---|---|
DJIGamepadDriver/DJIGamepadDriver.cpp |
DriverKit extension source | DriverKit |
DJIGamepadDriver/GamepadDescriptor.h |
Corrected HID descriptor | DriverKit |
DJIGamepadBridge/main.swift |
Virtual HID bridge | Bridge |
DJIGamepadLoader/ |
macOS app for managing dext | DriverKit |
gamecontrollerdb.txt |
SDL2 mapping database | SDL2 |
bin/dji-gamepad-launch |
SDL2 game launcher | SDL2 |
sdl_gc_test |
SDL2 controller test | All |
- DJI FPV Remote Controller 3 USB protocol reverse-engineered via IOKit inspection
- SDL2 game controller mapping based on SDL_GameControllerDB format
- Inspired by Karabiner-DriverKit-VirtualHIDDevice for virtual HID approach
MIT License - See LICENSE file
Contributions welcome! Areas for improvement:
- Support for other DJI controllers (Goggles, Avata, etc.)
- Button mapping profiles for specific simulators
- Dead zone and curve configuration
- GUI configuration tool
- Issues: GitHub Issues
- Discussions: FPV community forums
Note: This is an unofficial community project. Not affiliated with DJI.