Skip to content
This repository was archived by the owner on Mar 1, 2026. It is now read-only.

v3rm0n/dji-fpv3

Repository files navigation

DJI FPV Remote Controller 3 - macOS Driver

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.

What Works

  • 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

Requirements

  • 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

For DriverKit Installation (Recommended)

  • Apple Developer Program membership (for code signing)
  • SIP (System Integrity Protection) disabled OR user approval in System Settings

For Bridge Installation (Alternative)

  • SIP disabled for development/testing

For SDL2 Installation (Legacy)

  • SDL2 library (via Homebrew: brew install sdl2)
  • Games that use SDL2 input

Installation Options

This project provides three different approaches to enable the DJI RC3:

Option 1: DriverKit Extension (Recommended) ⭐

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:

  1. Build the project:

    # Generate Xcode project
    xcodegen generate
    
    # Open and build
    open DJIGamepad.xcodeproj
    # Build both DJIGamepadDriver and DJIGamepadLoader targets
  2. 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
  3. 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 needed

Option 2: Virtual HID Bridge (User-Space)

Best 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:

  1. 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
  2. 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'"
  3. 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-bridge

Requirements:

  • SIP must be disabled, OR
  • com.apple.developer.hid.virtual.device entitlement (requires Apple Developer account)

Option 3: SDL2 Game Controller Mapping (Legacy)

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:

  1. Install SDL2:

    brew install sdl2
  2. Build the tools:

    make
  3. Install system-wide mapping:

    bin/dji-gamepad-launch --install
  4. 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.txt

Limitations:

  • 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

Which Option Should I Choose?

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 ⚠️ Medium ✅ 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)

Axis Mapping

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

Testing Your Installation

Verify DriverKit Extension is Active

# 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"

Test Controller Input

# Build and run the test tool
make
./sdl_gc_test

Troubleshooting

"DJI Virtual Joystick" not appearing

  1. Check USB connection:

    ioreg -p IOUSB | grep -i dji

    Should show both "DJI Virtual Joystick" (from our driver) and the raw device.

  2. Verify extension status:

    systemextensionsctl list

    Look for io.maido.DJIGamepadLoader.DJIGamepadDriver and ensure it says [activated enabled]

  3. Re-activate if needed:

    # Open System Settings > Privacy & Security
    # Look for "System Extension" approval button
    # Click "Allow" and restart

Extension shows [activated disabled]

The extension needs user approval:

  1. Open System Settings > Privacy & Security
  2. Scroll to Security section
  3. Look for message about "DJIGamepadDriver"
  4. Click Allow
  5. Reconnect the controller

Game doesn't see 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 instead

For all games:

  • Ensure DriverKit extension is [activated enabled]
  • Try reconnecting the USB cable
  • Check in macOS System Settings > Game Controllers

Sticks are inverted or wrong

The mapping has been verified with multiple simulators. If it feels wrong:

  1. 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
  2. Use in-game calibration:

    • Most simulators have controller calibration in settings
    • Bind axes manually if needed
  3. For SDL2 approach only - Edit the mapping:

    # Edit gamecontrollerdb.txt
    # Invert an axis: lefty:a2 -> lefty:-a2
    # Swap axes: Change the number after 'a:'

Bridge approach errors

"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

Building from Source

Prerequisites

# For all approaches:
xcode-select --install

# For SDL2 approach:
brew install sdl2

# For Xcode project generation:
brew install xcodegen

Build Everything

# 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-bridge

Development Team Configuration

IMPORTANT: Before building for distribution, update the signing identity:

  1. 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)"
  2. Regenerate Xcode project:

    xcodegen generate

How It Works

The Problem

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.

The Solution

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.

Files Reference

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

Credits

License

MIT License - See LICENSE file

Contributing

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

Support


Note: This is an unofficial community project. Not affiliated with DJI.

About

No description or website provided.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors