工业物联网场景下的 4G 模组接入与应用

问题背景

在工业物联网设备里,4G 模组通常不是一个"插上就完事"的外设。它承担的是现场设备到云端平台、边缘服务器或远程运维系统之间的广域网链路,一旦链路不稳定,传感数据上报、远程配置、OTA、日志回传都会受影响。相比消费类设备,工业现场还会遇到弱信号、断电重启、SIM 卡欠费、基站切换、设备长期无人值守等问题,所以应用层必须把"能联网"进一步做成"可恢复、可观测、可维护的联网能力"。

这篇笔记偏嵌入式 Linux 应用开发视角,不展开 USB、PCIe、串口驱动怎么写,也不讨论内核协议栈细节。重点放在应用层如何识别模组、选择拨号方式、管理 AT 命令通道、建立数据连接,以及怎样做一个最小可用的 4G 联网守护流程。

4G 模组在系统中的位置

典型工业网关或采集终端里,4G 模组一般通过 USB、Mini PCIe、M.2 或 UART 接入主控。对嵌入式 Linux 应用来说,最常见能看到两类接口:

  • 控制通道:通常表现为 /dev/ttyUSBx/dev/ttyACMx 或 MBIM/QMI 控制节点,用来发送 AT 命令、查询 SIM 状态、信号强度、注册状态等。
  • 数据通道:可能表现为 ppp0wwan0usb0eth1 等网络接口,用来承载 IP 数据包。

应用层的核心任务不是"驱动这个硬件",而是把这些系统接口组织成可靠状态机。换句话说,业务程序不应该直接假设设备永远在线,而应该有一个连接管理层负责处理模组上电、SIM 检测、网络注册、拨号、路由、DNS、心跳检测和失败恢复。

常见联网方式的应用层取舍

PPP

PPP 是比较传统的方式,常见于串口模组或早期 USB 模组。应用上通常使用 pppd 配合 chat 脚本发送 AT 拨号命令,成功后生成 ppp0 接口。

它的优点是通用、依赖少,很多精简 Linux 系统也能跑。缺点是吞吐和稳定性通常不如 QMI/MBIM/NCM 这类方式,连接状态也更依赖脚本维护。在低速率数据采集、只做 MQTT/HTTP 小包上报的场景下,PPP 仍然可用;如果设备要传图片、日志包或 OTA 镜像,就应该优先考虑更现代的数据模式。

ECM、RNDIS、NCM

这类方式会把模组暴露成一个类似 USB 网卡的接口,例如 usb0eth1。应用层看起来最简单:系统拿到网口后,通过 DHCP 获取地址,然后配置默认路由即可。

它适合希望降低拨号复杂度的项目,但也要注意两个问题:一是不同模组默认 USB composition 不一样,量产时需要固定配置;二是应用层仍要检测接口是否真的能访问外网,不能只看网口是否 UP

QMI 和 MBIM

QMI/MBIM 更常见于较新的蜂窝模组。应用层可以使用 libqmilibmbimModemManager 或 NetworkManager 进行连接管理。它们对状态查询、拨号、断线恢复、IP 配置的支持更完整,也更适合嵌入式 Linux 网关这类长期在线设备。

如果系统资源允许,个人更倾向于使用 ModemManager 或 NetworkManager 把底层差异收敛掉。业务应用只关心"蜂窝网络是否可用",连接守护进程负责处理模组细节。对于极简 Buildroot 系统,则可以直接使用 qmiclimbimcli 或厂商提供的拨号工具,但需要自己补齐状态机和日志。

应用层应该管理哪些状态

4G 接入不要只做一个"启动时拨号"的脚本。更稳妥的做法是把它拆成几个状态:

  1. 设备存在:检查控制节点、网络接口或 ModemManager 中是否枚举到模组。
  2. SIM 就绪:确认 SIM 卡存在、PIN 状态正常,必要时识别 ICCID 以便排查卡槽或卡号问题。
  3. 网络注册:查询运营商注册状态和信号强度,避免还没注册就反复拨号。
  4. 数据连接:根据 APN 建立 PDP context,确认本地接口拿到 IP。
  5. 路由与 DNS:确认默认路由、DNS 可用,避免"接口有 IP 但业务出不去"。
  6. 业务可达:用 MQTT keepalive、HTTP 探测或 ICMP 心跳确认目标服务可达。
  7. 恢复动作:按失败等级执行重拨、重置模组、重启连接服务,最后才考虑整机重启。

这里有一个经验:不要把所有失败都归结为"重启设备"。工业现场的设备一旦进入反复整机重启,日志容易丢,问题也更难定位。应用层应该优先保存失败原因,然后做分级恢复。

AT 命令仍然很重要

即使用了 ModemManager,AT 命令也值得掌握,因为它是排查问题的基础工具。常见查询包括:

text 复制代码
AT                  // 模组是否响应
ATI                 // 查询模组信息
AT+CPIN?            // SIM 卡状态
AT+CSQ              // 信号强度
AT+CEREG?           // EPS 注册状态
AT+CGDCONT?         // PDP context 配置
AT+CGACT?           // PDP 激活状态

应用层不建议高频、无节制地直接占用 AT 串口。更好的方式是:连接管理进程统一访问控制通道,业务程序通过本地 IPC、状态文件或 D-Bus 获取联网状态。这样可以避免多个进程同时写 AT 口导致响应串行混乱。

实践:基于 ModemManager 的 4G 联网守护脚本

下面示例适用于带 systemd、ModemManager、mmcliiproute2ping 的嵌入式 Linux 系统。它不涉及驱动开发,只做应用层连接管理:查找模组、按 APN 连接、检测默认路由和外网可达性,失败后执行重连。

保存为 /usr/local/bin/wwan-keepalive.sh

sh 复制代码
#!/bin/sh
set -eu

APN="${APN:-internet}"
PING_HOST="${PING_HOST:-223.5.5.5}"
CHECK_INTERVAL="${CHECK_INTERVAL:-30}"
FAIL_LIMIT="${FAIL_LIMIT:-3}"

log() {
    logger -t wwan-keepalive "$*"
    echo "$*"
}

find_modem() {
    mmcli -L | awk -F/ '/Modem/ {print $NF; exit}' | awk '{print $1}'
}

connect_modem() {
    modem="$1"

    mmcli -m "$modem" --enable >/dev/null 2>&1 || true

    if mmcli -m "$modem" -K | grep -q 'modem.generic.state: connected'; then
        return 0
    fi

    log "connecting modem ${modem} with apn=${APN}"
    mmcli -m "$modem" --simple-connect="apn=${APN},ip-type=ipv4" >/dev/null
}

has_default_route() {
    ip route show default | grep -q .
}

network_ok() {
    has_default_route && ping -c 3 -W 3 "$PING_HOST" >/dev/null 2>&1
}

reset_connection() {
    modem="$1"
    log "network check failed, resetting modem connection"
    mmcli -m "$modem" --simple-disconnect >/dev/null 2>&1 || true
    sleep 3
    connect_modem "$modem"
}

fail_count=0

while true; do
    modem="$(find_modem || true)"

    if [ -z "$modem" ]; then
        log "no modem found"
        fail_count=$((fail_count + 1))
    else
        connect_modem "$modem" || fail_count=$((fail_count + 1))

        if network_ok; then
            fail_count=0
            log "network ok"
        else
            fail_count=$((fail_count + 1))
            log "network failed, count=${fail_count}"
        fi

        if [ "$fail_count" -ge "$FAIL_LIMIT" ]; then
            reset_connection "$modem" || true
            fail_count=0
        fi
    fi

    sleep "$CHECK_INTERVAL"
done

配套的 systemd service 可以这样写,保存为 /etc/systemd/system/wwan-keepalive.service

ini 复制代码
[Unit]
Description=4G WWAN keepalive service
After=ModemManager.service network-pre.target
Wants=ModemManager.service

[Service]
Type=simple
Environment=APN=internet
Environment=PING_HOST=223.5.5.5
Environment=CHECK_INTERVAL=30
ExecStart=/usr/local/bin/wwan-keepalive.sh
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

部署命令:

sh 复制代码
chmod +x /usr/local/bin/wwan-keepalive.sh
systemctl daemon-reload
systemctl enable --now wwan-keepalive.service
journalctl -u wwan-keepalive.service -f

这个脚本只是最小版本,真正项目里还可以继续补充:读取 SIM ICCID、记录信号强度、上报连接失败原因、限制重置频率、区分服务器不可达和蜂窝网络不可达等。

业务程序如何使用 4G 链路

在工业物联网应用里,4G 链路通常承载 MQTT、HTTPS、WebSocket 或自定义 TCP 协议。业务程序要注意三点:

第一,不要把"进程启动"和"网络已可用"绑定得太死。设备开机后,4G 注册和拨号可能需要几十秒,弱信号环境下更久。业务程序应该允许网络晚到,并支持断线重连。

第二,协议层要有本地缓存。采集数据可以先落盘或进入队列,网络恢复后再按时间顺序补发。这样即使 4G 短暂掉线,也不会丢关键数据。

第三,心跳要区分层次。ICMP 能通只能说明外网大概率可达,MQTT keepalive 或 HTTPS 探测才能说明业务服务可达。连接管理层可以检测公共网络,业务层则检测自己的服务端。

踩坑记录

4G 模组调试里最容易误判的是"有 IP 等于联网成功"。有时接口拿到了地址,但默认路由没切过去;有时 DNS 没配置好,ping IP 可以,访问域名失败;还有时公网可达,但目标平台因为证书时间错误、TLS 版本或防火墙策略导致连接失败。因此日志里最好同时记录接口 IP、默认路由、DNS、信号强度、注册状态和业务连接错误码。

另一个常见问题是 APN 配置。不同 SIM 卡、专网卡、物联网卡可能需要不同 APN、用户名、密码和鉴权方式。量产设备不要把 APN 写死在程序里,更适合放在配置文件、出厂参数区或远程配置中。

总结与延伸

在嵌入式 Linux 工业物联网设备中,4G 模组的难点不在于"调用一次拨号命令",而在于把蜂窝网络做成一个长期稳定的系统能力。应用层需要理解控制通道和数据通道的分工,选择合适的 PPP、ECM/NCM、QMI 或 MBIM 方案,并围绕 SIM、注册、拨号、路由、业务可达性建立状态机。后续可以继续深入两个方向:一是结合 MQTT/HTTP 做断点缓存和补传机制;二是把信号强度、重连次数、拨号失败原因纳入设备运维指标,让现场问题更容易被远程定位。