If you follow this and something breaks, that's on you. I'm not responsible for a bricked router, a lost config, or whatever else goes wrong. When a step failed for me, the useful thing was to paste the exact error into an AI and ask what to check next. Guessing from a forum thread was slower.
Pages I kept open while doing this: the AX3200 wiki, the 23.05.5 MT7622 downloads, XMiR-Patcher, unlock_pwd.py, PassWall2 and its releases, PuTTY, and WinSCP.
I wanted the Xiaomi AX3200 off the stock MiWiFi firmware. I bought the router for about 20,000 LKR, around 65 to 67 dollars, so the house could go out through a VPS I already owned on DigitalOcean. If you are from Sri Lanka, you know why someone would do that. OpenWrt lets me set the firewall, the routes, and which domains skip the proxy. The ISP router stayed in place. The AX3200 sits behind it. This post is only the router side. I already had a VLESS server to point at. Building that server is a different job. After the flash, nothing got worse. It works.
International model, RB01. The label says produced 06/2024. Stock firmware was MiWiFi 1.0.83. CPU is a MediaTek MT7622B, 256 MB RAM, 128 MB NAND. The June 2024 date matters because the AX3200 wiki calls out newer international units and their bootloader. I flashed OpenWrt 23.05.5 in August 2025. Kernel on the status page was 5.15.167, target mediatek/mt7622, packages aarch64_cortex-a53. The PassWall2 and Xray builds from that month are older than what the release page shows now. Old asset URLs from this post 404. Download whatever the page lists for your architecture today, don't reuse a link I typed in 2025.
Cables, before touching software
While I was unlocking and flashing, the PC was on Ethernet into a Xiaomi LAN port. I left the Xiaomi powered the whole time. I didn't plug the Xiaomi WAN into the ISP router yet. The ISP router can stay up on its own. Wi-Fi on the PC just made the addresses harder to trust, so don't do the unlock over Wi-Fi.
After OpenWrt was on and the LAN address had moved, the cables were:
ISP router LAN -> AX3200 WAN
PC -> AX3200 LANInternet
|
ISP router 192.168.1.1
| LAN
AX3200 OpenWrt 192.168.50.1
| LAN / Wi-Fi
phone, laptop, TVnetmode 4, then XMiR
Stock management was http://192.168.31.1. There's no OpenWrt upload button on the stock page. I tried the wiki's xiaomi-enable-mesh-telnet.py path first. The script printed binary Response blocks and I thought it had died. It hadn't. The check that actually mattered was the stock API, using the stok from the admin session I was already logged into:
http://192.168.31.1/cgi-bin/luci/;stok=<CURRENT_STOK>/api/xqnetwork/get_netmodeIt came back {"netmode":4,"code":0}. So netmode 4 was on. stok is just a session token. Mine stays off this page. I also opened 192.168.32.1 once and got a 404. The router was on 192.168.31.1.

Telnet from there was a mess on Windows, so I switched to XMiR-Patcher. run.bat from Explorer flashed open and closed. I ran it from an Administrator PowerShell so the menu stayed open. Option 2, connect and install the exploit, IP 192.168.31.1. It asked for the stock web admin password. The screen showed device_name = RB01 and rom_version = 1.0.83 release, then #### SSH server are activated! #### on port 22. You don't need both tools if XMiR already gave you SSH. I ran netmode first because that was the wiki path. XMiR is what actually got me a shell.



The root password for that SSH login comes from the serial on the router label, not the MAC. I pulled unlock_pwd.py with the real curl binary, because PowerShell's curl is Invoke-WebRequest and it just rejects -L:
curl.exe -L -o unlock_pwd.py https://raw.githubusercontent.com/Freifunk-Dresden/AX6S-unlock/master/unlock_pwd.py
python .\unlock_pwd.py "YOUR_SERIAL_NUMBER"The script prints a password. That's what I used for root in PuTTY, host 192.168.31.1, port 22. Banner was Welcome to XiaoQiang!. Serial and password stay off this page.
Use the factory image, not sysupgrade
On the stock shell I set the NVRAM flags from the wiki's old bootloader path:
nvram set ssh_en=1
nvram set uart_en=1
nvram set boot_wait=on
nvram set flag_boot_success=1
nvram set flag_try_sys1_failed=0
nvram set flag_try_sys2_failed=0
nvram commitNewer bootloaders want different handling. Read the device page for the router in front of you before you type that. Mine was the old bootloader path.
First install uses the factory image. I didn't use the sysupgrade file. The wiki keeps those as separate steps, and the wrong one on a new AX3200 is how people brick them. The file was:
openwrt-23.05.5-mediatek-mt7622-xiaomi_redmi-router-ax6s-squashfs-factory.binI renamed it factory.bin so I wouldn't mistype that long name. WinSCP, SCP, root, and I dragged the file into /tmp. That directory is tmpfs. A reboot wipes it. Then:
cd /tmp
mtd -r write factory.bin firmwareI left it alone until it rebooted. Stock 192.168.31.1 was gone. OpenWrt came up at http://192.168.1.1. First boot had open Wi-Fi, so I set a root password and WPA before doing anything else. OpenWrt didn't make the radios reach farther. Same antennas.

Second router, different subnet
The fiber still ends on the ISP router. I moved the AX3200 LAN to 192.168.50.1 so it wouldn't sit on the same subnet as the ISP box at 192.168.1.1. Then I plugged ISP LAN into the Xiaomi WAN. WAN is a DHCP client. In the test it got 192.168.1.15/24, gateway 192.168.1.1. LuCI after the move is http://192.168.50.1.

Six reboots
The wiki says some newer international units soft-brick after six reboots. Mine is a June 2024 unit, so I did the workaround once OpenWrt was up. Check first:
fw_printenv | grep -e ^flag_try -e ^boot_fwfw_setenv boot_fw1 "run boot_rd_img;bootm"
fw_setenv flag_try_sys1_failed 8
fw_setenv flag_try_sys2_failed 8
fw_setenv flag_boot_rootfs 0
fw_setenv flag_boot_success 1
fw_setenv flag_last_success 1Reboot, then the same fw_printenv line. I got flag_try_sys1_failed=9 and flag_try_sys2_failed=8, and both boot_fw0 and boot_fw1 were run boot_rd_img;bootm. The 9 looked like I'd typo'd it. The wiki treats 8 or higher as the workaround holding, so I left it. If you need to debrick, that's the TFTP section on the device page. I'm not rewriting that here.
PassWall2, without filling /tmp
Overlay on that build had about 91 MB free. /tmp was about 119 MB of RAM. I unzipped the whole PassWall package zip into /tmp and the shell said write error (disk full?). So look at the zip first, don't dump the whole thing:
unzip -l passwall_packages_ipk_aarch64_cortex-a53.zip | egrep 'xray-core|sing-box'Mine listed:
sing-box_1.12.0-r1_aarch64_cortex-a53.ipk
xray-core_25.8.3-r1_aarch64_cortex-a53.ipkI only run Xray, so I only pulled that one file out:
unzip -j passwall_packages_ipk_aarch64_cortex-a53.zip 'xray-core_*aarch64_cortex-a53.ipk' -d .Hard-coded GitHub release URLs 404'd on me. I opened the release page in a browser, grabbed the ipks that matched 23.05, opkg, and aarch64_cortex-a53, and copied them with WinSCP. The LuCI package is *_all.ipk. The core and the helpers are the architecture ones.
luci-app-passwall2 still wouldn't install. It wanted tcping and geoview from that same package set. I installed those two, then Xray, then the LuCI app.

nftables mode then complained that dnsmasq was too old, or built without nftset. I thought it was the version. dnsmasq -v said 2.90, which is past the 2.87 in the warning, and it also said no-nftset. So the version was fine. The build flag was the miss. I swapped in dnsmasq-full from the 23.05 feed.
This is a client. It does not create the server
PassWall2 on this router dials a server I already had. It doesn't create one. My node was Xray, VLESS, RAW TCP, TLS on, REALITY off, flow disabled. That matched that server. Another server wants different TLS, REALITY, or transport settings, so don't copy my checkboxes. VLESS is the protocol. TLS is what encrypted this one. Address, port, UUID, and SNI have to come from your server config, not from my screenshot. Those fields are covered. The port and the node nickname on the screen are mine too. Leave them.

Direct for a few domains
Shunt rules are under Services, PassWall2, Rule Manage, the Sing-Box/Xray shunt editor. Most traffic goes through the node. A few domains go Direct out the WAN, for a bank, a local shop, a game, stuff that hates coming out of a datacenter. I typed pizzahut.lk into the name box and got Expecting: valid UCI identifier. That box is the config id. The domain is a different field.
Wrong
Name: pizzahut.lk
What I saved
Name: pizzahut
Domain:
domain:pizzahut.lk
domain:example-bank.lk
domain:example-shop.com
Outbound: DirectSeveral domains can sit in one rule, one per line. logread | grep -i passwall when something is going the wrong way. Check the exit IP from a phone or laptop. The router's own curl is not that client.


One speed check
Wi-Fi on the AX3200 was slower than the ISP router in the one test I wrote down: about 113 Mbps down and 117 up, against about 295 down and 150 up on the ISP box. That was one sitting, with the proxy in the path. I wouldn't call that the AX3200's limit. Channel width, the Ethernet link, flow offloading, the CPU, and the proxy were all in that number.
Don't copy these off the screenshots: my serial, the derived root password, the stok, the MAC, the VLESS address, UUID, port, and SNI. Those are this router and this server. Use the wiki and your own server config.