4G 模组开机自连 systemd 服务方案:基于灵眸科技EAI1106--Core-T

4G 模组开机自连 systemd 服务方案

适用:带 USB 4G 模组(EC800K-CN 等)的 Linux 板(Easy EAI / 灵眸等),把"上电后手动 ifconfig → 发 AT → dhclient"改成 systemd 开机自动跑。

1. 适用场景

  • 背景:板子每次冷启动后,4G 网络都不会自动连。需要登录 → 手动拉起 USB 网卡 → 手动发 AT 指令让模组拨号 → 手动 dhclient 拿 IP。
  • 目标:插上电,等上 15~30 秒,网卡自动 UP、IP 自动拿到、能直接 ping 通外网。后续 ssh / 业务程序起来就能用。
  • 适用型号 :
    • 模组侧:Quectel EC800K-CN USB 模组--- 使用用 AT+qnetdevctl=1,1,1 触发 PDP 拨号。
    • 板子侧:任何使用 systemd 启动的 Linux 板(EasyEAI、灵眸、Rockchip 系等)。

2. 前置准备

2.1 硬件

  • 4G 模组(Quectel 系)已插上板子的 USB 口,SIM 卡已装好
  • 天线已接,信号格不为 0
  • 板子已经烧好系统,开机能进 shell
  • 手动拉起 USB 网卡,让模组拨号,dhclient 拿 IP后可以上网

2.2 设备识别

插好模组,板子启动后跑:

复制代码
# 看 USB 串口(模组一般会出来 ttyUSB0~ttyUSB2)
ls -l /dev/ttyUSB* 2>/dev/null
#   期望至少看到 ttyUSB0 / ttyUSB1 / ttyUSB2(分别对应 DM / AT / PPP)
#   看到 ttyUSB1 才能正常发 AT 指令

# 看 USB 网卡(systemd 命名后是 enxMAC 形式)
ip -br link | grep -E 'enx|eth|usb'
#   期望看到 enxae0c29a39b6d 这种(enx 前缀 + MAC 后 6 字节)
#   如果用 lsusb 也能确认模组是否枚举成功:
lsusb | grep -i quectel
#   期望看到 Quectel Wireless Solutions Co., Ltd. ... 一行

💡 记录你的实际值 :ttyUSB1 和 enxae0c29a39b6d 是后续脚本默认值。如果你板子上不是这两个(比如接口名是 wwan0 或 tty 是 ttyUSB2),需要替换脚本里的 IFACE / MODEM,或者在调用时用环境变量覆盖。

2.3 权限

整个流程都需要 CAP_NET_ADMIN(拉网卡)+ 写 /var/log 权限,所以必须以 root 跑:

复制代码
# 切换 root
sudo su
# 或当前用户有 sudo 权限,跑命令前加 sudo
# 推荐直接 su 到 root,后面少敲

2.5 验证手动流程能跑通

这条最重要 :先把"手动的三步"自己跑一遍确认能联网,再上 systemd。免得 service 跑不起来时不知道是脚本问题还是网络问题。

复制代码
sudo ifconfig enxae0c29a39b6d up
sudo stty -F /dev/ttyUSB1 -opost
sudo printf "AT+qnetdevctl=1,1,1\r\n" > /dev/ttyUSB1
sleep 10
sudo dhclient enxae0c29a39b6d
ip -4 addr show enxae0c29a39b6d   # 期望看到 inet 10.x.x.x 这种地址
ping -c 2 8.8.8.8                 # 期望有回应

全过才能进下一步。哪个不过就先解决哪个。

3. 工作流程

复制代码
开机
 └─ systemd 启动 /etc/systemd/system/auto-4g-connect.service
     └─ 跑 /usr/local/bin/auto-4g-connect.sh
         ├─ 1. 等 enxae0c29a39b6d 网卡出现(≤30s)
         ├─ 2. ifconfig enxae0c29a39b6d up
         ├─ 3. 等 /dev/ttyUSB1 出现(≤60s)
         ├─ 4. stty 关 tty 输出处理,避免 \n 双重转 \r\n
         ├─ 5. 发 AT+qnetdevctl=1,1,1 触发 PDP 拨号
         ├─ 6. sleep 10s 等模组注册 + PDP 激活
         └─ 7. dhclient enxae0c29a39b6d(失败重试 5 次)

典型 15~30 秒 完成,最坏情况约 2.5 分钟(设备枚举慢 + 信号差 + DHCP 多次 retry)。

4. 关键设计

设计 为什么这样
IFACE / MODEM / AUTO_4G_LOG 环境变量覆盖 换板/换模组不动脚本;调试时改日志路径省得跟权限打架
自动 exec sudo 兜底 atom 用户手敲也能跑;systemd 跑时直接是 root,不进 sudo 分支
After=local-fs.target,不在 network.target 之后 自己先拉起网卡,避免和 NetworkManager 抢同一个网口
Type=oneshot + RemainAfterExit=yes 跑完视为成功;systemd status 显示 active (exited)
Restart=on-failure + 15s 重试 + 5 次封顶 开机早期 /dev/ttyUSB1 还没就绪时自动 retry;信号差时 DHCP 失败也能重试
stty -F /dev/ttyUSB1 -opost 关 tty 的 ONLCR 输出处理,避免 \n → \r\n 跟我们的 \r\n 打架
DHCP 重试 5 次 模组拨号成功但 IP 还没下发到内核网卡时,给点容忍度

5. 文件清单

文件 作用 权限 谁读谁写
/usr/local/bin/auto-4g-connect.sh 主逻辑脚本 755 (rwxr-xr-x) root
/etc/systemd/system/auto-4g-connect.service systemd 单元 644 (rw-r--r--) root
/var/log/auto-4g-connect.log 脚本自己的日志 644 root 写
/etc/systemd/system/multi-user.target.wants/auto-4g-connect.service enable 后自动生成的软链 --- systemd 读

📁 为什么脚本放 /usr/local/bin/、服务放 /etc/systemd/system/?

  • /usr/local/ 是给"系统包管理器不管的、本地装的"软件用的。第三方脚本放这里最规范。
  • /etc/systemd/system/ 优先级高于 /lib/systemd/system/,放这里相当于"覆盖发行版默认",不会升级时被冲掉。

6. 完整脚本与配置

6.1 /usr/local/bin/auto-4g-connect.sh

复制代码
#!/bin/bash
# auto-4g-connect.sh --- 上电自启:拉网卡 -> 触发模组拨号 -> DHCP 拿 IP
# 用法: sudo /usr/local/bin/auto-4g-connect.sh
# 可覆盖环境变量: IFACE / MODEM / AUTO_4G_LOG

set -u   # 抓未定义变量,不要乱吞错

# ========== 配置区 ==========
IFACE="${IFACE:-enxae0c29a39b6d}"   # 模组的 USB 网卡(systemd 命名,enx + MAC)
MODEM="${MODEM:-/dev/ttyUSB1}"      # 模组 AT 指令口(Quectel 模组通常是 ttyUSB1)
LOG="${AUTO_4G_LOG:-/var/log/auto-4g-connect.log}"

# ========== 工具函数 ==========
log() { echo "$(date '+%F %T') $*" | tee -a "$LOG"; }

# ========== 权限兜底 ==========
# 必须 root:拉网卡要 CAP_NET_ADMIN,写 /var/log 要 root
if [ "$(id -u)" -ne 0 ]; then
    echo "$(date '+%F %T') 非 root,转 sudo: $0 $*" >> "$LOG" 2>/dev/null
    echo "[!] 需要 root,请用 sudo $0 $*" >&2
    exec sudo "$0" "$@"   # exec 替换当前进程,sudo 续上
fi

log "===== auto-connect start (pid=$$ iface=$IFACE modem=$MODEM) ====="

# ========== 1) 等网卡出现 ==========
# USB 设备枚举可能比 systemd 启动晚几秒
for i in $(seq 1 30); do
    if ip link show dev "$IFACE" >/dev/null 2>&1; then
        log "iface $IFACE 出现 (${i}s)"; break
    fi
    sleep 1
done
ip link show dev "$IFACE" >/dev/null 2>&1 || { log "ERROR: $IFACE not found"; exit 1; }

# ========== 2) 拉起网卡 ==========
# 优先 ifconfig(老习惯);没有就 fallback 到 ip 命令
if command -v ifconfig >/dev/null 2>&1; then
    ifconfig "$IFACE" up 2>>"$LOG"
else
    ip link set "$IFACE" up
fi
log "iface $IFACE up"

# ========== 3) 等模组 tty 出现 ==========
# ttyUSB* 出现可能比网卡再晚一点
for i in $(seq 1 60); do
    [ -e "$MODEM" ] && { log "modem $MODEM 出现 (${i}s)"; break; }
    sleep 1
done
[ -e "$MODEM" ] || { log "ERROR: $MODEM not present"; exit 1; }

# ========== 4) 关 tty 输出处理 ==========
# 默认 tty 会把输出里的 \n 转成 \r\n,跟我们的 \r\n 撞车会变成 \r\r\n
stty -F "$MODEM" -opost 2>/dev/null
sleep 2   # 给模组一秒钟稳一下

# ========== 5) 触发 PDP 拨号 ==========
# AT+qnetdevctl=1,1,1 是 Quectel 私有指令:1=start,1=cid,1=rat(自动选 4G/3G/2G)
printf "AT+qnetdevctl=1,1,1\r\n" > "$MODEM"
log "AT+qnetdevctl sent"

# ========== 6) 等模组注册 + PDP 激活 ==========
# 看 SIM/信号/网络,2~15s 都正常,冷启动可能更久
sleep 10

# ========== 7) DHCP,带重试 ==========
# dhclient / dhcpcd 二选一,都没有就报错退出
for i in 1 2 3 4 5; do
    if command -v dhclient >/dev/null 2>&1; then
        if dhclient "$IFACE" 2>>"$LOG"; then
            log "dhclient OK (try $i)"; break
        fi
    elif command -v dhcpcd >/dev/null 2>&1; then
        if dhcpcd "$IFACE" 2>>"$LOG"; then
            log "dhcpcd OK (try $i)"; break
        fi
    else
        log "ERROR: 没装 dhclient 也没装 dhcpcd,装一个: sudo apt install isc-dhcp-client"; exit 1
    fi
    log "DHCP try $i failed, sleep 5s & retry"
    sleep 5
done

log "===== auto-connect end ====="
exit 0

6.2 /etc/systemd/system/auto-4g-connect.service

复制代码
[Unit]
Description=Auto bring up 4G modem (Quectel AT+qnetdevctl) and DHCP
# local-fs 是几乎所有服务都依赖的"文件系统就绪"标记,最早期的同步点
# 这里不在 network.target 之后是因为我们要自己拉网卡,不能等别人先动
After=local-fs.target
Wants=local-fs.target

[Service]
Type=oneshot                # 跑一次就退,不是常驻进程
RemainAfterExit=yes          # 脚本退出后仍视为 active,这样 systemctl status 看着干净
ExecStart=/usr/local/bin/auto-4g-connect.sh
Restart=on-failure           # 失败才重启(避免成功跑完被反复重启)
RestartSec=15                # 失败后 15 秒再试
StartLimitBurst=5            # 5 分钟内最多 5 次
StartLimitIntervalSec=300

[Install]
WantedBy=multi-user.target   # 多用户模式启动时拉起(就是普通启动的常规 target)

7. 安装

7.1 一键安装(推荐)

SSH 上板后,整块复制粘贴到终端直接回车:

复制代码
sudo bash <<'SETUP_EOF'
set -e

# 1) 写主脚本
cat > /usr/local/bin/auto-4g-connect.sh <<'AUTO_4G_SH'
#!/bin/bash
set -u

IFACE="${IFACE:-enxae0c29a39b6d}"
MODEM="${MODEM:-/dev/ttyUSB1}"
LOG="${AUTO_4G_LOG:-/var/log/auto-4g-connect.log}"

log() { echo "$(date '+%F %T') $*" | tee -a "$LOG"; }

if [ "$(id -u)" -ne 0 ]; then
    echo "$(date '+%F %T') 非 root,转 sudo: $0 $*" >> "$LOG" 2>/dev/null
    echo "[!] 需要 root,请用 sudo $0 $*" >&2
    exec sudo "$0" "$@"
fi

log "===== auto-connect start (pid=$$ iface=$IFACE modem=$MODEM) ====="

for i in $(seq 1 30); do
    ip link show dev "$IFACE" >/dev/null 2>&1 && { log "iface $IFACE 出现 (${i}s)"; break; }
    sleep 1
done
ip link show dev "$IFACE" >/dev/null 2>&1 || { log "ERROR: $IFACE not found"; exit 1; }

if command -v ifconfig >/dev/null 2>&1; then
    ifconfig "$IFACE" up 2>>"$LOG"
else
    ip link set "$IFACE" up
fi
log "iface $IFACE up"

for i in $(seq 1 60); do
    [ -e "$MODEM" ] && { log "modem $MODEM 出现 (${i}s)"; break; }
    sleep 1
done
[ -e "$MODEM" ] || { log "ERROR: $MODEM not present"; exit 1; }

stty -F "$MODEM" -opost 2>/dev/null
sleep 2

printf "AT+qnetdevctl=1,1,1\r\n" > "$MODEM"
log "AT+qnetdevctl sent"

sleep 10

for i in 1 2 3 4 5; do
    if command -v dhclient >/dev/null 2>&1; then
        if dhclient "$IFACE" 2>>"$LOG"; then log "dhclient OK (try $i)"; break; fi
    elif command -v dhcpcd >/dev/null 2>&1; then
        if dhcpcd "$IFACE" 2>>"$LOG"; then log "dhcpcd OK (try $i)"; break; fi
    fi
    log "DHCP try $i failed, sleep 5s & retry"
    sleep 5
done

log "===== auto-connect end ====="
exit 0
AUTO_4G_SH

# 2) 写 systemd 单元
cat > /etc/systemd/system/auto-4g-connect.service <<'AUTO_4G_UNIT'
[Unit]
Description=Auto bring up 4G modem (Quectel AT+qnetdevctl) and DHCP
After=local-fs.target
Wants=local-fs.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/bin/auto-4g-connect.sh
Restart=on-failure
RestartSec=15
StartLimitBurst=5
StartLimitIntervalSec=300

[Install]
WantedBy=multi-user.target
AUTO_4G_UNIT

# 3) 上权限 + 注册服务 + 启动
chmod +x /usr/local/bin/auto-4g-connect.sh
systemctl daemon-reload
systemctl enable --now auto-4g-connect.service

# 4) 报告
echo ""
echo "================ 安装完成 ================"
ls -l /usr/local/bin/auto-4g-connect.sh /etc/systemd/system/auto-4g-connect.service
echo "-------------------------------------------"
systemctl status auto-4g-connect.service --no-pager -l || true
echo "-------------------------------------------"
echo "日志:   journalctl -u auto-4g-connect.service -f"
echo "       tail -f /var/log/auto-4g-connect.log"
echo "卸载:   sudo systemctl disable --now auto-4g-connect.service && sudo rm /usr/local/bin/auto-4g-connect.sh /etc/systemd/system/auto-4g-connect.service"
echo "==========================================="
SETUP_EOF

7.2 分步安装

如果不想粘贴大块,可以先把两个文件用 vim 在板子上手敲(文件内容见 §6),然后跑:

复制代码
# 1) 上权限
sudo chmod +x /usr/local/bin/auto-4g-connect.sh

# 2) systemd 重载新单元
sudo systemctl daemon-reload

# 3) 启用 + 立刻跑一次
sudo systemctl enable --now auto-4g-connect.service
#    --now = enable + start 一起;以后只想启动用: sudo systemctl start auto-4g-connect.service

🔍 daemon-reload 干啥:让 systemd 重新扫一遍 /etc/systemd/system/ 看有没有新加的 .service 文件,不加这条 systemctl enable 会报 unit not found。

相关推荐
TMT星球1 小时前
云从科技获评广东省制造业单项冠军企业
科技
天远数科2 小时前
零信任架构实战:基于天远企业股权变更构建自动化商业银行对公授信存续期股权异动与资产抽逃预警网关
运维·人工智能·架构·自动化
qq_401700412 小时前
Linux设备树详解
linux·运维·服务器
奇树谦3 小时前
Windows CMD 下 mc 运维指南:SeaweedFS 安装验收与 MinIO 数据迁移
运维·windows
刚入门的大一新生3 小时前
Linux-信号
linux·运维·服务器
车软学堂3 小时前
CANoe自动化:跑起第一个测试用例
运维·车载系统·自动化·汽车·测试用例
L0208245 小时前
Kubernetes Ingress|CNI 网络模型|Calico 网络策略|Pod 调度器完整实战教程
运维·网络·云原生·容器·kubernetes
考拉讲支付7 小时前
延迟分账 / 核销分账的技术实现方案:服务完成后再分钱,到底怎么设计?
大数据·运维·人工智能·微信小程序·小程序·系统架构