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 LAN
Internet
   |
ISP router  192.168.1.1
   | LAN
AX3200 OpenWrt  192.168.50.1
   | LAN / Wi-Fi
phone, laptop, TV

netmode 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_netmode

It 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.

Browser showing netmode 4 and code 0. The session URL is covered.
netmode 4. The session URL is covered.

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.

Stock Xiaomi status page for an international AX3200 on MiWiFi 1.0.83.
Stock page. RB01, MiWiFi 1.0.83.
XMiR-Patcher text menu on Windows.
XMiR-Patcher menu. Option 2 installs the exploit.
XMiR-Patcher reporting that the SSH server is activated. Password and local username covered.
SSH activated. The web password and the Windows username are covered.

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 commit

Newer 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.bin

I 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 firmware

I 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.

OpenWrt 23.05.5 status page on the AX3200.
First OpenWrt boot. 23.05.5.

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.

OpenWrt WAN interface with a DHCP address from the upstream router.
WAN lease from the ISP router.

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_fw
fw_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 1

Reboot, 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.ipk

I 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.

Terminal during the PassWall2 package install.
Local ipks. The release URL I had saved was already dead.

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.

PassWall2 node screen for an Xray VLESS profile. Address, UUID, and SNI are covered.
VLESS node. Server fields covered.

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

Several 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.

PassWall2 error: Expecting a valid UCI identifier, after a domain was typed into the rule name.
Domain in the name field.
PassWall2 shunt rule editor with separate fields for the rule name and the domain list.
Name and domain are different boxes.

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.