What's new
  • SNBForums Code of Conduct

    SNBForums is a community for everyone, no matter what their level of experience.

    Please be tolerant and patient of others, especially newcomers. We are all here to share and learn!

    The rules are simple: Be patient, be nice, be helpful or be gone!

Quectel RM520N-GL 5G modem – Dual WAN setup guide

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:
  • 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
    "
    I went this route since I don't need Entware for anything else.

Usage​

  • To open the modem console:
    Code:
    picocom /dev/ttyUSB2
    Note: the modem doesn't echo your input - just type the command and watch for OK or ERROR.
  • To exit picocom, press Ctrl+A, then Ctrl+X.

Configuration​

Enter picocom on /dev/ttyUSB2, then run the commands. You should get an OK for each.
If picocom drops the session, just reconnect and continue.
  1. Configure APN
    Code:
    AT+CGDCONT=1,"IPV4V6","internet" # replace "internet" with your carrier's APN
  2. 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
  3. 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.
    SCR-20251001-peoa.png
    SCR-20251001-peuo.png
  4. 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 the picocom 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
P.S. Quectel firmware updates aren't public, but getting the latest standard version is pretty painless if you follow the instructions on their forum.
 
Last edited:

Similar threads

Support SNBForums w/ Amazon

If you'd like to support SNBForums, just use this link and buy anything on Amazon. Thanks!

Sign Up For SNBForums Daily Digest

Get an update of what's new every day delivered to your mailbox. Sign up here!
Back
Top