kuchkovsky
Regular Contributor
I've been running Dual WAN on Asuswrt-Merlin for a while. My first setup used one of the cheap Cat 4 LTE USB sticks. They were fine for basic failover, but speeds were capped and there was no carrier aggregation. Eventually I wanted something faster, so I went looking for a better option and ended up with a Quectel RM520N-GL in the Waveshare 5G M.2 to Gigabit Ethernet enclosure (Amazon).
So far, it's been a great upgrade. The modem works perfectly as a secondary WAN both over USB (CDC-ECM) and over Ethernet (via the built-in RTL8125 chip). On plain LTE I consistently see ~220 Mbps+ in both modes. If you're in a region with mmWave 5G, you may want to look at the slightly more expensive Quectel RM530N-GL (modem + enclosure on Amazon), which works with the same Waveshare enclosure but adds mmWave support that the RM520N-GL doesn't have.
Couple of notes from my testing:
If
So far, it's been a great upgrade. The modem works perfectly as a secondary WAN both over USB (CDC-ECM) and over Ethernet (via the built-in RTL8125 chip). On plain LTE I consistently see ~220 Mbps+ in both modes. If you're in a region with mmWave 5G, you may want to look at the slightly more expensive Quectel RM530N-GL (modem + enclosure on Amazon), which works with the same Waveshare enclosure but adds mmWave support that the RM520N-GL doesn't have.
Couple of notes from my testing:
- USB mode is capped - ASUS routers seem to only negotiate USB 2.0 speeds with modems, so expect a ceiling around ~480 Mbps even on 5G. For LTE, that's not an issue. I can't test 5G yet since it's not currently available in my country.
- IPv6 limitations - the modem itself fully supports IPv6, but the ASUS Dual WAN implementation is IPv4-only. If you need IPv6, you'd have to use some custom scripts to make it work end-to-end.
Setup Guide
Here's a step-by-step setup tutorial for anyone who wants to try this modem with Asuswrt-Merlin.picocom prerequisites
To configure the modem, you'll need picocom for sending AT commands.Installation
- Option A (easy if you already use Entware)
Code:opkg update opkg install picocom
- Option B (self-contained, no Entware needed)
This produces and uploads a tiny static binary (~135 KB) to your router. Docker is required.
Code:docker run --rm -it --platform=linux/arm64 -v "$PWD"/out:/out alpine:3.20 sh -lc ' apk add --no-cache build-base git git clone --depth=1 https://github.com/npat-efault/picocom.git cd picocom make CC=gcc CFLAGS="-Os -DNO_CUSTOM_BAUD" LDFLAGS="-static" strip picocom mkdir -p /out/bin cp -r picocom /out/bin/picocom ' # Replace "router" with your SSH router alias below scp -O -r out/bin/ router:/jffs/ ssh router " chmod 755 /jffs/bin/picocom && \ grep -qxF 'export PATH=\$PATH:/jffs/bin' /jffs/configs/profile.add 2>/dev/null || \ echo 'export PATH=\$PATH:/jffs/bin' >> /jffs/configs/profile.add "
Usage
- To open the modem console:
Code:picocom /dev/ttyUSB2
OK
orERROR
. - To exit
picocom
, press Ctrl+A, then Ctrl+X.
Configuration
Enterpicocom
on /dev/ttyUSB2
, then run the commands. You should get an OK
for each.If
picocom
drops the session, just reconnect and continue.- Configure APN
Code:AT+CGDCONT=1,"IPV4V6","internet" # replace "internet" with your carrier's APN
- Choose mode
- Option A: USB (CDC-ECM)
Code:AT+QCFG="data_interface",0,0 # switch data path to USB AT+QMAP="MPDN_rule",0 # clear the existing MPDN_rule AT+QMAP="MPDN_rule",0,1,0,0,1 # enable NAT and autoconnect for the APN AT+QMAPWAC=1 # enable global autoconnect AT+QMAP="DHCPV4DNS","enable" # enable DNS proxy for IPv4 AT+QCFG="usbnet",1 # set the USB mode to CDC AT+CFUN=1,1 # reboot
- Option B: Ethernet (RTL8125)
Code:AT+QCFG="data_interface",1,0 # switch data path to PCIe (enables onboard RTL8125) AT+QCFG="pcie/mode",1 # switch PCIe to RC mode AT+QETH="eth_driver","r8125" # select Realtek 8125 Ethernet driver AT+QMAP="MPDN_rule",0 # clear the existing MPDN_rule AT+QMAP="MPDN_rule",0,1,0,1,1,"FF:FF:FF:FF:FF:FF" # enable IP passthrough and autoconnect for the APN AT+QMAPWAC=1 # enable global autoconnect AT+QMAP="DHCPV4DNS","disable" # disable DNS proxy for IPv4 AT+CFUN=1,1 # reboot
- Option A: USB (CDC-ECM)
- Configure Dual WAN in the router Web UI, based on your settings (either USB mode or one of the router's Ethernet ports). For the USB mode, go to the WAN settings -> USB, and make sure the APN settings match the ones required by your carrier (that wasn't the case for me by default - the router was using the wrong ones for some reason). You can switch to the manual APN configuration mode and change any field that you need. The built-in Dual WAN feature has been rock-solid for me in this setup.
- Bonus: automatically toggle the modem's radio based on the primary WAN state.
Works in both USB and Ethernet data modes, but the modem's USB control port must remain connected to the router (AT commands go over USB even in Ethernet mode). This uses a modified version of my WAN failover script that enables email notifications - follow the setup steps in my repo, then replace the repo's script with the one below. Change the ISP names in the script, and update thepicocom
path if you installed it using Entware.
Code:set -euo pipefail PRIMARY_WAN_ID=0 SECONDARY_WAN_ID=1 PRIMARY_WAN_ISP="PrimaryISP" # <-- edit: name of your main / primary WAN provider SECONDARY_WAN_ISP="SecondaryISP" # <-- edit: name of your failover / secondary provider HOSTS_TO_BLOCK=' ' MIN_BOOT_TIME=180 MAIL_DELAY=60 EMAIL_SENDER='/jffs/scripts/utils/send_email.sh' SECONDARY_WAN_FLAG='/tmp/secondary_wan.enabled' PICO='/jffs/bin/picocom' # replace with '/opt/bin/picocom' if you installed it from Entware AT_DEV='/dev/ttyUSB2' WAN_ID="$1" EVENT="$2" [ "$1" = "$(nvram get wan${SECONDARY_WAN_ID}_ifname)" ] && WAN_ID="$SECONDARY_WAN_ID" . /jffs/scripts/utils/common.sh . /jffs/scripts/utils/firewall.sh send_at() { local cmd="$1" out ok=1 # Retry up to 3 times in cases of a failure for _ in 1 2 3; do # Run the command and get the result out="$(printf '%s\r' "$cmd" | "$PICO" -q --nolock --exit-after 1000 "$AT_DEV")" || out="" # Check if any line is exactly "OK" (ignore CRs and surrounding spaces) if printf '%s\n' "$out" | tr -d '\r' | grep -Eq '^[[:space:]]*OK[[:space:]]*$'; then ok=0 break fi # Brief pause between tries sleep 1 done return "$ok" } set_modem_radio() { local state="${1:-1}" cmd case "$state" in 0) cmd='AT+CFUN=4' ;; # RF OFF: flight mode *) cmd='AT+CFUN=1' ;; # RF ON : normal mode esac send_at "$cmd" & return 0 } notify_when_settled() { local subject="$1" # first arg = subject shift # $@ now contains the whole body, word-for-word awk -v min="$MIN_BOOT_TIME" '{ exit ($1 < min) }' /proc/uptime || return 0 (sleep "$MAIL_DELAY"; "$EMAIL_SENDER" "$subject" "$@") & } apply_host_action() { local action="$1" host case "$action" in block|allow) ;; *) log -l err "apply_host_action: invalid action '$action'" \ "(expected 'block' or 'allow')" return 1 ;; esac for host in $(strip_comments "$HOSTS_TO_BLOCK"); do "${action}_wan_for_host" "$host" "$SECONDARY_WAN_ID" || true done } PRIMARY_WAN_ACTIVE="$(nvram get wan${PRIMARY_WAN_ID}_primary)" SECONDARY_WAN_ACTIVE="$(nvram get wan${SECONDARY_WAN_ID}_primary)" case "$WAN_ID" in 0) case "$EVENT" in disconnected) # primary WAN down -> using backup set_modem_radio 1 ;; connected) # primary WAN restored set_modem_radio 0 ;; esac ;; 1) case "$EVENT" in connected) # primary WAN down -> using backup if [ "$PRIMARY_WAN_ACTIVE" = 0 ] && [ "$SECONDARY_WAN_ACTIVE" = 1 ]; then touch "$SECONDARY_WAN_FLAG" apply_host_action block notify_when_settled "🔴 WAN State Notification" \ "$PRIMARY_WAN_ISP connection is not available." \ "I switched to $SECONDARY_WAN_ISP." fi ;; fw_started) # firewall (re)started while secondary is active if [ -f "$SECONDARY_WAN_FLAG" ]; then apply_host_action block fi ;; disconnected) # primary WAN restored if [ "$PRIMARY_WAN_ACTIVE" = 1 ] && [ "$SECONDARY_WAN_ACTIVE" = 0 ]; then rm -f "$SECONDARY_WAN_FLAG" apply_host_action allow notify_when_settled "🟢 WAN State Notification" \ "$PRIMARY_WAN_ISP connection is available again." \ "I switched back from $SECONDARY_WAN_ISP." fi ;; esac ;; esac
Last edited: