Периодически пропадает связь через LTE модем

LTE watchdog для WBC2-4G на Wiren Board: рабочий вариант

Как и обещал, выкладываю рабочий вариант LTE watchdog, который я сейчас использую на контроллерах Wiren Board с модемом WBC2-4G.

Исходная проблема была такая: LTE-связь периодически пропадала, при этом по dmesg было видно, что это не просто обрыв сотовой сети, а модем переподнимался как USB-устройство. После такого отвала связь сама стабильно не восстанавливалась, помогала только перезагрузка контроллера.

После установки watchdog проблема не повторяется уже больше 10 дней. Сейчас это решение используется на нескольких контроллерах.

Что делает watchdog

Скрипт запускается через systemd timer каждые 2 минуты.

Логика восстановления такая:

  1. Проверить наличие активного GSM-профиля.

  2. Проверить наличие /dev/ttyUSB*.

  3. Проверить доступность интернета через ping.

  4. Если связи нет — переподнять активный GSM-профиль.

  5. Если не помогло — попробовать wb-gsm-sim1 и wb-gsm-sim2.

  6. Если не помогло — перезапустить ModemManager.

  7. Если не помогло — выполнить power-cycle LTE-модема через wb-gsm.

  8. Если несколько циклов подряд не дали результата — перезагрузить контроллер.

1. Установка скрипта

Создать файл:

nano /usr/local/sbin/lte-watchdog.sh

Содержимое:

#!/bin/bash

set -u

LOGTAG="lte-watchdog"
FAIL_FILE="/var/tmp/lte-watchdog-fails"

SIM1_CONN="wb-gsm-sim1"
SIM2_CONN="wb-gsm-sim2"

MAX_FAILS_BEFORE_REBOOT=5

PING_TARGETS=("1.1.1.1" "8.8.8.8")

log() {
    logger -t "$LOGTAG" "$1"
    echo "$(date '+%F %T') $1"
}

get_active_gsm_connection() {
    nmcli -t -f DEVICE,TYPE,STATE,CONNECTION dev status \
        | awk -F: '$2=="gsm" && $3=="connected" {print $4; exit}'
}

get_available_gsm_connections() {
    nmcli -t -f NAME,TYPE con show \
        | awk -F: '$2=="gsm" {print $1}'
}

internet_ok() {
    for target in "${PING_TARGETS[@]}"; do
        if ping -c 2 -W 5 "$target" >/dev/null 2>&1; then
            return 0
        fi
    done

    return 1
}

reset_fails() {
    echo 0 > "$FAIL_FILE"
}

inc_fails() {
    local n=0

    if [ -f "$FAIL_FILE" ]; then
        n=$(cat "$FAIL_FILE" 2>/dev/null || echo 0)
    fi

    n=$((n + 1))
    echo "$n" > "$FAIL_FILE"
    echo "$n"
}

connection_exists() {
    nmcli con show "$1" >/dev/null 2>&1
}

restart_connection() {
    local conn="$1"

    if ! connection_exists "$conn"; then
        log "Connection $conn does not exist, skipping"
        return 1
    fi

    log "Restarting GSM connection: $conn"

    nmcli con down "$conn" >/dev/null 2>&1
    sleep 10
    nmcli con up "$conn" >/dev/null 2>&1
    sleep 45

    if internet_ok; then
        log "Internet restored using $conn"
        reset_fails
        return 0
    fi

    log "Connection $conn did not restore internet"
    return 1
}

try_all_gsm_connections() {
    local active_conn="$1"

    # Сначала пробуем активный профиль, если он известен
    if [ -n "$active_conn" ]; then
        restart_connection "$active_conn" && return 0
    fi

    # Затем пробуем стандартные профили SIM1/SIM2
    for conn in "$SIM1_CONN" "$SIM2_CONN"; do
        if [ "$conn" != "$active_conn" ]; then
            restart_connection "$conn" && return 0
        fi
    done

    # Затем пробуем любые другие GSM-профили, если они есть
    while read -r conn; do
        [ -z "$conn" ] && continue
        [ "$conn" = "$SIM1_CONN" ] && continue
        [ "$conn" = "$SIM2_CONN" ] && continue
        [ "$conn" = "$active_conn" ] && continue

        restart_connection "$conn" && return 0
    done < <(get_available_gsm_connections)

    return 1
}

modemmanager_recovery() {
    local active_conn="$1"

    log "Restarting ModemManager"

    systemctl restart ModemManager
    sleep 35

    if [ -n "$active_conn" ]; then
        nmcli con up "$active_conn" >/dev/null 2>&1
        sleep 45

        if internet_ok; then
            log "Internet restored after ModemManager restart using $active_conn"
            reset_fails
            return 0
        fi
    fi

    try_all_gsm_connections "$active_conn" && return 0

    return 1
}

wbgsm_power_cycle() {
    local active_conn="$1"

    log "Power-cycling LTE modem via wb-gsm"

    systemctl stop wb-gsm
    sleep 25
    systemctl start wb-gsm
    sleep 80

    if [ -n "$active_conn" ]; then
        nmcli con up "$active_conn" >/dev/null 2>&1
        sleep 45

        if internet_ok; then
            log "Internet restored after wb-gsm power cycle using $active_conn"
            reset_fails
            return 0
        fi
    fi

    try_all_gsm_connections "$active_conn" && return 0

    return 1
}

check_ttyusb_presence() {
    if ls /dev/ttyUSB* >/dev/null 2>&1; then
        return 0
    fi

    log "No /dev/ttyUSB* devices found. Restarting ModemManager and wb-gsm"

    systemctl restart ModemManager
    sleep 25

    systemctl restart wb-gsm
    sleep 80

    return 1
}

main() {
    local active_conn
    local fails

    active_conn="$(get_active_gsm_connection || true)"

    if [ -n "$active_conn" ]; then
        log "Active GSM connection: $active_conn"
    else
        log "No active GSM connection detected"
    fi

    check_ttyusb_presence

    if internet_ok; then
        reset_fails
        exit 0
    fi

    log "Internet check failed"

    if try_all_gsm_connections "$active_conn"; then
        exit 0
    fi

    if modemmanager_recovery "$active_conn"; then
        exit 0
    fi

    if wbgsm_power_cycle "$active_conn"; then
        exit 0
    fi

    fails=$(inc_fails)
    log "LTE still unavailable after full recovery cycle. Consecutive fails: $fails"

    if [ "$fails" -ge "$MAX_FAILS_BEFORE_REBOOT" ]; then
        log "Too many LTE failures. Rebooting controller"
        reboot
    fi

    exit 1
}

main "$@"

Сделать исполняемым:

chmod +x /usr/local/sbin/lte-watchdog.sh

2. systemd service

Создать файл:

nano /etc/systemd/system/lte-watchdog.service

Содержимое:

[Unit]
Description=LTE watchdog for WBC2-4G with SIM1/SIM2 support
After=NetworkManager.service ModemManager.service wb-gsm.service
Wants=NetworkManager.service ModemManager.service wb-gsm.service

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/lte-watchdog.sh
Nice=10

3. systemd timer

Создать файл:

nano /etc/systemd/system/lte-watchdog.timer

Содержимое:

[Unit]
Description=Run LTE watchdog every 2 minutes

[Timer]
OnBootSec=5min
OnUnitActiveSec=2min
AccuracySec=30s
Persistent=true
Unit=lte-watchdog.service

[Install]
WantedBy=timers.target

4. Включить watchdog

systemctl daemon-reload
systemctl enable --now lte-watchdog.timer

Проверить timer:

systemctl status lte-watchdog.timer --no-pager
systemctl list-timers | grep lte-watchdog

Ручной запуск watchdog:

systemctl start lte-watchdog.service

Посмотреть логи:

journalctl -t lte-watchdog --since "1 hour ago" --no-pager

или:

journalctl -u lte-watchdog.service -n 100 --no-pager

5. Настройка под свои профили

В скрипте используются стандартные имена GSM-профилей:

SIM1_CONN="wb-gsm-sim1"
SIM2_CONN="wb-gsm-sim2"

Если у вас профили называются иначе, нужно поменять эти переменные.

Проверить профили:

nmcli con show | grep -E 'gsm|wb-gsm'

Проверить активное GSM-устройство:

nmcli dev status

6. Диагностика модема

Полезные команды:

mmcli -m wbc || mmcli -m any
nmcli dev status
nmcli con show
systemctl status ModemManager --no-pager
systemctl status wb-gsm --no-pager

Посмотреть USB-события:

dmesg -T | grep -Ei "usb|ttyUSB|option|rndis|wbc|modem|disconnect|reset|timeout" | tail -100

7. Что можно изменить

Интервал запуска задаётся в timer:

OnUnitActiveSec=2min

Количество неудачных циклов перед reboot:

MAX_FAILS_BEFORE_REBOOT=5

Адреса проверки интернета:

PING_TARGETS=("1.1.1.1" "8.8.8.8")

Если reboot как крайняя мера не нужен, можно закомментировать строку:

reboot

или сильно увеличить:

MAX_FAILS_BEFORE_REBOOT=20

8. Результат

На моих объектах это решение работает больше 10 дней. До установки watchdog LTE-связь на части контроллеров могла пропадать почти ежедневно и требовала ручной перезагрузки WB. После установки watchdog ручное вмешательство больше не требовалось.

Понимаю, что если ядро теряет USB-соединение с модемом, первопричина может быть на уровне питания, USB-хоста, модема, прошивки или драйвера. Но для удалённых объектов, где LTE является единственным каналом связи, такой watchdog оказался практичным способом автоматического восстановления доступности контроллера.