The first FCC unlock built natively for iPhone. No server, no account, no tracking. Everything runs on device.
An app by Andrea Piani. Project page and FAQ: andreapiani.com/dji-fcc-unlock-ios.
For educational and research purposes. Modifying radio transmission power or altitude limits may violate the law where you are. In most places, transmitting above the power permitted for your region, or flying above the permitted altitude, requires authorisation from the regulator. You are solely responsible for compliance. If you are not sure whether this is legal where you live, do not use it.
Not affiliated with, endorsed by, or sponsored by DJI. Using this may void your warranty and DJI Care Refresh coverage.
| Feature | |
|---|---|
| πΆ | FCC unlock. Switches the radio from CE to FCC: 33 dBm (about 2 W) instead of 20 dBm (about 100 mW) on 2.4 GHz, DJI's own limits for the RC-N3, for more channels and more range. |
| π°οΈ | Altitude research. Requests 500m; the tested DJI Neo stores 120m. A working altitude unlock is still under investigation (#1). |
| π | Native MFi. Talks to the controller over the same certified channel DJI Fly uses, no jailbreak, no desktop, no second device. |
| π | Readable. Every byte sent is a plain JSON profile you can inspect on the Profile tab. |
| π | Offline. No server contact, no account, no tracking, ever. |
| π | Auto-hold. Re-applies while the app is backgrounded, so FCC survives DJI Fly reconnecting. |
| FCC active | Activity log | Command profile | About |
![]() |
![]() |
![]() |
![]() |
The FCC and Log captures predate the fifth tab and show four tabs; Profile and About show five. The green connection states require real hardware to recapture.
There is a fifth tab, Experimental, with the reverse-engineering probes the issues below refer to: green buttons only read, the amber one writes altitude limits, the red one writes flight-control parameters and is flight-test territory.
Tested on real hardware, a DJI RC-N3 controller cabled to an iPhone with a DJI Neo aircraft: FCC power reached, verified on the DJI Fly Transmission graph with the signal extending well past the 1km reference. Not a simulator or a protocol mock, an actual controller and an actual drone in the air. Three things had to be right, and finding them was the work:
- π The channel is
com.dji.logiclink, one of the two MFi protocol strings DJI does not publish. A build declaring only the three documented strings never sees the controller at all, because iOS hides an accessory whose protocols you did not declare. The string was read off the hardware. - πΊπΈ The region is set by country code
US. The command is the WiFi Set Country Code the controller already accepts; the country it carries is what decides CE or FCC. - π The aircraft must be linked when you apply, not just powered on. The frames reach the controller and stop there until it is relaying to a drone. The app shows a green link line with the aircraft serial once the drone is there.
One honest limit: this firmware answers no region-read command, so the app cannot read the mode back. The DJI Fly Transmission graph is the confirmation.
Every pair anyone has reported, working or not. A failure is as useful as a success here, so both get a row.
| Controller | Aircraft | FCC | Reported by | What happened |
|---|---|---|---|---|
| DJI RC-N3 | DJI Neo | β confirmed | @andreapianidev | FW v00.05.00.12. Verified on the DJI Fly Transmission graph, signal well past the 1km reference. |
| DJI RC-N3 | DJI Mini 5 Pro | β no response | @theboyroberts | Link healthy, RCLink framing correct, aircraft linked, and zero acks on all six paths. Under investigation in #5. |
| DJI RC-N1 | any | β untested | Wanted. The protocol should be identical, but nobody has run it. | |
| DJI RC-N2 | any | β untested | Wanted. |
Add a row by filing a hardware report. It takes a cable, five minutes and no coding, and it is the single most useful thing anyone can contribute to this project right now.
Requirements: Xcode 26+, iOS 17+ target, an Apple ID in Xcode.
brew install xcodegen # once
xcodegen generate
open FreeFCC.xcodeprojPick your iPhone as the destination and Run. The project signs with the team wildcard profile, so no App Store Connect setup is needed. This is a sideload for your own hardware, not an App Store build: it opens DJI's MFi protocol strings and changes a regulatory radio setting.
Every push builds an unsigned .ipa in CI, so reporting a hardware log does
not require owning a Mac. Open the Build workflow,
pick the latest green run and download the FreeFCC-<version>-build<n>-unsigned
artifact.
Be clear about what that file is: it carries no signature and no provisioning profile, so an iPhone will refuse it as it comes out of CI. It has to be re-signed with your own Apple ID first, with Sideloadly or AltStore on Windows, Linux or macOS. A free Apple ID signs it for 7 days; a paid developer account, for a year.
The MFi protocol strings the app needs (com.dji.logiclink and the rest) live in
Info.plist and survive re-signing, so a re-signed build talks to the controller
exactly like one built in Xcode.
If a run fails on your fork, open an issue with the job log rather than patching around it; the workflow is meant to work for everyone.
The order matters. The controller only relays commands to the aircraft once DJI Fly has woken that link, and the link stays warm for a while after DJI Fly closes. That warm window is what the app needs.
- Power on the drone and the controller, wait for them to pair.
- Open DJI Fly first and wait until it shows the drone connected with a live camera feed. This is the step that wakes the controller-to-aircraft link. Skipping it is why an apply gets 0 responses even with the drone detected.
- Close DJI Fly (swipe it away).
- Cable the iPhone to the TOP USB port of the controller, the phone-cradle port.
- Open FCC Unlock, tap Connect, wait for the green line with the aircraft
serial, then tap Enable FCC Mode and let the sweep finish. A line like
profile@02/RCLink: 38 responsesmeans the aircraft answered. - Do not reopen DJI Fly straight away. Reopening it right after the unlock drops the radio back to CE every time (see the sequence below).
Reopening DJI Fly immediately after the unlock loses FCC: DJI Fly renegotiates the region on connect and the radio falls back to CE. The sequence that holds, observed on hardware:
- Start DJI Fly, let the drone link, then run the unlock in FCC Unlock (Connect, then Enable FCC Mode).
- Close DJI Fly.
- Power the controller off, then the drone off.
- Power the controller back on, with the iPhone still cabled and FCC Unlock still holding the link.
- Power the drone back on and let it relink.
- Open DJI Fly, Transmission tab. FCC power is there and holds, matching the signal-graph screenshot in this repo.
FCC is RAM-based and the app re-applies on an interval while it holds the link, which is what survives the power cycle here. If it ever reverts, repeat from step 1.
Altitude, what the hardware actually reports. The app writes the aircraft's
flying_limit.max_heightto 500 and the flight controller acknowledges it with status OK, but reading the value back the drone reports 120, not 500: the0xF9reply carries00 8A 23 71 03 78 00, where78 00is 120. So the 120m ceiling is enforced drone-side too, not only inside DJI Fly, andmax_heightalone does not lift it. The parameter that actually governs a real 500m unlock is still being reverse engineered (issue #1). FCC power is a separate matter and works.
The app opens an EASession on the controller's MFi protocol and speaks the DUML
command protocol over the stream: it builds each frame with its CRC-8 and CRC-16,
wraps it in the link envelope, keeps the session alive with a keepalive, and
parses the aircraft's replies out of a stream that also carries the video feed.
Each apply sweeps several sender and framing combinations and counts the
responses, so the Log tab names the path your hardware answered on. The region is
set with the country code and the altitude ceiling with the flight controller's
max_height parameter, both inside one service-mode window.
Everything beyond FCC power lives on the Experimental tab: the 0xF7/0xF8 and 0xFB reads, the 500m-gate probe, the OSD telemetry decode, the 30-second flight recorder and the staged Sport-speed boost. Section 17 of the technical documentation describes each one and what the hardware said.
Deep dive. The full walkthrough, protocol and frames, transport, the service-mode window, the sweep, and every hardware finding is in docs/TECHNICAL-DOCUMENTATION.md (English) or docs/DOCUMENTAZIONE-TECNICA.md (Italian).
FreeFCC/
Core/ frame builder + CRC, link envelope + stream parser, profile loader,
MFi transport, controller (sweep, hold, region, altitude, diagnostics,
experimental probes)
App/ SwiftUI screens (FCC, Log, Profile, Experimental, About) and design system
Resources/profiles/ fcc.json (FCC + 500m), ce_restore.json
FreeFCCTests/ frames, parser, profiles and OSD speed regression checks
docs/TECHNICAL-DOCUMENTATION.md full protocol + architecture writeup (English)
docs/DOCUMENTAZIONE-TECNICA.md the same writeup in Italian
docs/screenshots/ hardware captures; Profile/About can use -initialTab N
This is the free, source-available answer to the paid FCC/altitude tools. FCC power is done and confirmed; the rest is open reverse engineering, and it moves faster with more hands and more hardware. Everything still to do is written up as detailed issues:
- π°οΈ #1 Unlock 500m altitude and #3 unlock ~60 km/h speed, the headline features, both drone-side RE. The gate probe is on the Experimental tab. The Sport boost was rewritten in v1.8, after the log showed the old one wrote parameters the Neo does not have: it now targets the Sport config block and needs a ground run, a recorded flight and the log.
- π #2 Get the config-table read answering, the tool that unblocks both of the above. 0xF7/0xF8 are dead on this firmware, the 0xF9 echo reads limits only, and the 0xFB read is shipped and waiting for a result.
- β‘ #4 Drop the "open DJI Fly first" step by initialising the link ourselves. The warmth gate already tells a cold link from a wrong write; the DJI Fly init sequence is what is left to capture.
- π§ͺ #5 Testers wanted on RC-N1 / RC-N2 and other aircraft, no coding needed, just a device and a log.
- π‘ #6 Read the region back for a real in-app CE/FCC indicator.
Each issue lists what is known, the exact parameter hashes and commands, and the next concrete step. Pick one, open a PR, or just run the app on your hardware and post your log. Findings from real devices are as valuable as code.
A write-up and a call for testers will go up on Reddit (r/dji and friends) so owners of other DJI gear can help map the parameters across models.
Free for noncommercial use under the PolyForm Noncommercial License 1.0.0. See LICENSE and NOTICE.md. You may use, study, modify and share it for any noncommercial purpose, with attribution. Commercial use is not permitted without a separate license from the author. The DUML protocol the app implements is publicly documented by the dji-firmware-tools project; the iOS app and its logic are original work.
Β© 2026 Andrea Piani Β· NIE Z2331796-S Β· Tijarafe, Santa Cruz de Tenerife Β· Islas Canarias





