Tplink MR6400 routers lte hangs

the lte works for sometime then after it stops (hangs) but then i check then wwan interface is up and the simcard has a working data connection

what could be the cause and how to fix it

some work for some hours then go off some work for days then go off

they still have power LAN works but lte doesnt work

tried pinging something (from the router) outside your LAN to check if the LTE connection is up ?

yes after booting up router internet works well u can ping anything on internet but after sometime traffic going through lte stops and wont work untill u restart

the ping is obviously only relevant when internet's unavailable.

Should be "the modem"

Is it the same interval all the time? For how long it normally stays up?

i have a fleet of about 12
but at the moment only 6 are up
the other 6 went off one by one and untill they restart from power they wont come up
not same interval and actually some have not gone off
but all are same version both software and hardware

time of 1day 3days some even went of after 5hrs

btw this is an installation script i use to set then up for dynamic whitelisting and reporting
i dont know if something here contributes to that

#!/bin/sh

CONFIG_FILE="/root/mqtt-router.conf"

log() {
  echo "[*] $1"
}

fail() {
  echo "[x] $1" >&2
  exit 1
}

escape_for_double_quotes() {
  printf '%s' "$1" | sed 's/\\/\\\\/g;s/"/\\"/g'
}

write_config() {
  umask 077
  tmp="${CONFIG_FILE}.tmp.$$"
  {
    printf 'BROKER="%s"\n' "$(escape_for_double_quotes "$BROKER")"
    printf 'PORT="%s"\n' "$(escape_for_double_quotes "$PORT")"
    printf 'SERIAL="%s"\n' "$(escape_for_double_quotes "$SERIAL")"
    printf 'PASSWORD="%s"\n' "$(escape_for_double_quotes "$PASSWORD")"
  } > "$tmp" || fail "Could not write temporary config"
  mv "$tmp" "$CONFIG_FILE" || fail "Could not install config at $CONFIG_FILE"
  chmod 600 "$CONFIG_FILE" 2>/dev/null || true
}

prompt_mqtt_settings() {
  printf "MQTT broker (IPv4): "
  read BROKER
  [ -n "$BROKER" ] || fail "BROKER cannot be empty"

  printf "MQTT port (e.g. 1883): "
  read PORT
  [ -n "$PORT" ] || fail "PORT cannot be empty"

  printf "Serial number: "
  read SERIAL
  [ -n "$SERIAL" ] || fail "SERIAL cannot be empty"

  printf "Password: "
  read PASSWORD
  [ -n "$PASSWORD" ] || fail "PASSWORD cannot be empty"
}

setup_simcard() {
  log "Setting up SIM card network interface..."

  uci batch <<'EOF' || fail "Failed to commit network.wwan UCI (check uci / flash)"
set network.wwan=interface
set network.wwan.proto='qmi'
set network.wwan.device='/dev/cdc-wdm0'
set network.wwan.apn='internet'
set network.wwan.pdptype='ip'
set network.wwan.dhcp='0'
commit network
EOF

  /etc/init.d/network restart || fail "network restart failed"
  log "SIM card network interface setup complete."
  log "Waiting for simcard to be up and have internet connectivity..."

  # Wait for WWAN0 to be up and pingable (max 60 attempts)
  for i in $(seq 1 60); do
    STATUS=$(ifstatus wwan 2>/dev/null)
    UP=$(echo "$STATUS" | grep -q '"up": true' && echo yes || echo no)
    AVAILABLE=$(echo "$STATUS" | grep -q '"available": true' && echo yes || echo no)

    if [ "$UP" = "yes" ] && [ "$AVAILABLE" = "yes" ]; then
      if ping -I wwan0 -c 1 8.8.8.8 >/dev/null 2>&1; then
        log "SIMCARD is up and has internet connectivity."
        log "Waiting for 60 seconds to ensure SIMCARD is ready..."
        break
      else
        log "SIMCARD up but no internet yet, retrying..."
      fi
    else
      log "SIMCARD not ready yet, waiting..."
    fi

    sleep 1
  done

  # Final check
  if ! ping -I wwan0 -c 1 8.8.8.8 >/dev/null 2>&1; then
    fail "SIMCARD did not get internet connectivity after timeout"
  fi
  sleep 60
  log "SIMCARD ready, continuing setup..."
}

# Prefer SIM for SMS read/write via modem AT port when ttyUSB2 is present.
setup_sms_sim_storage() {
  SMS_AT_TTY="/dev/ttyUSB2"
  OPTION_NEW_ID="/sys/bus/usb-serial/drivers/option1/new_id"

  if [ ! -c "$SMS_AT_TTY" ]; then
    touch /etc/medx-option-new-id
    log "$SMS_AT_TTY not found — registering modem with option1 (05c6 9025 ff)..."
    if [ -e "$OPTION_NEW_ID" ]; then
      if echo "05c6 9025 ff" >"$OPTION_NEW_ID" 2>/dev/null; then
        log "Wrote to $OPTION_NEW_ID; waiting for USB serial nodes..."
        sleep 3
      else
        log "Could not write to $OPTION_NEW_ID (need root or driver busy)."
      fi
    else
      log "Missing $OPTION_NEW_ID — is kmod-usb-serial-option loaded?"
    fi
  fi

  if [ ! -c "$SMS_AT_TTY" ]; then
    log "SMS AT port $SMS_AT_TTY still missing — skipping AT+CPMS."
    return 0
  fi

  log "Configuring SMS storage on SIM via $SMS_AT_TTY (AT+CPMS=\"SM\"...)..."

  if ! echo -e "AT\r\n" >"$SMS_AT_TTY" 2>/dev/null; then
    log "Could not write to $SMS_AT_TTY (busy or permission) — skipping CPMS."
    return 0
  fi
  sleep 1

  if ! echo -e 'AT+CPMS="SM","SM","SM"\r\n' >"$SMS_AT_TTY" 2>/dev/null; then
    log "Could not send AT+CPMS — skipping."
    return 0
  fi
  sleep 1

  log "AT+CPMS sent: receive/send/preferred storage set to SM (SIM card)."
}

install_deps() {
  opkg update || apk update || fail failed to update
  opkg install mosquitto-client grep jq || apk add mosquitto-client grep jq || fail failed to install dependencies
}

setup_routing() {
  POLICY_TABLE_ID=100
  WWAN_IF="wwan0"
  MARK_ID=0x64

  log "Setting up policy routing (whitelist IPs via MQTT / get_whitelists.sh)..."

  ip route flush table "$POLICY_TABLE_ID" 2>/dev/null
  ip rule add fwmark "$MARK_ID" table "$POLICY_TABLE_ID" priority 100

  WWAN_IP=$(ip -4 addr show dev "$WWAN_IF" | awk '/inet / {print $2}' | cut -d/ -f1)
  WWAN_GW=$(ubus call network.interface.wwan status | jsonfilter -e '@["route"][0]["nexthop"]')
  [ -n "$WWAN_IP" ] || fail "No IPv4 address on $WWAN_IF"
  [ -n "$WWAN_GW" ] || fail "No WWAN default gateway (check ubus network.interface.wwan)"

  ip route add default via "$WWAN_GW" dev "$WWAN_IF" table "$POLICY_TABLE_ID"
  ip route add "$WWAN_IP" dev "$WWAN_IF" table "$POLICY_TABLE_ID"

  nft delete table inet mangle 2>/dev/null
  nft add table inet mangle
  nft add chain inet mangle prerouting { type filter hook prerouting priority mangle\; }
  nft add chain inet mangle output { type route hook output priority mangle\; }

  [ -n "$BROKER" ] || fail "BROKER not set"
  nft add rule inet mangle prerouting ip daddr "$BROKER" counter mark set $MARK_ID
  nft add rule inet mangle output ip daddr "$BROKER" counter mark set $MARK_ID
  log "Routing table $POLICY_TABLE_ID ready; broker $BROKER marked for WWAN (table 100)."
}

setup_hotplug() {
  cat << EOF > /etc/hotplug.d/iface/99-wwan-reroute
#!/bin/sh

# Only run for wwan interface coming up
[ "\$INTERFACE" = "wwan" ] || exit 0
[ "\$ACTION" = "ifup" ] || exit 0

logger -t hotplug "WWAN interface is up. Updating routing table."

WWAN_IF="wwan0"
ROUTING_TABLE_ID=100
MARK_ID=0x64

# Get current IP of wwan0
WWAN_IP=\$(ip -4 addr show dev \$WWAN_IF | awk '/inet / {print \$2}' | cut -d/ -f1)
# Get gateway from network status (OpenWrt-specific)
WWAN_GW=\$(ubus call network.interface.wwan status | jsonfilter -e '@["route"][0]["nexthop"]')

# Exit if we don't have a gateway or IP
[ -z "\$WWAN_IP" ] && logger -t hotplug "No IP found on \$WWAN_IF" && exit 1
[ -z "\$WWAN_GW" ] && logger -t hotplug "No gateway found on \$WWAN_IF" && exit 1

# Ensure rule exists
ip rule add fwmark \$MARK_ID table \$ROUTING_TABLE_ID 2>/dev/null

# Update routes
ip route replace default via \$WWAN_GW dev \$WWAN_IF table \$ROUTING_TABLE_ID
ip route replace \$WWAN_IP dev \$WWAN_IF table \$ROUTING_TABLE_ID

logger -t hotplug "Routing for \$WWAN_IF updated: default via \$WWAN_GW, local IP \$WWAN_IP"
EOF

  chmod +x /etc/hotplug.d/iface/99-wwan-reroute
  log "Hotplug script installed and made executable."
}

# Write MQTT client scripts to /usr/bin (heredocs — no repo files on device) and wire boot + cron.
set_persistance() {
  log "Writing /usr/bin/get_whitelists.sh"
  cat <<'H_EOF_GETWHITELISTS' > /usr/bin/get_whitelists.sh
#!/bin/sh

MQTT_CONFIG_FILE="/root/mqtt-router.conf"
if [ ! -f "$MQTT_CONFIG_FILE" ]; then
  echo "Missing MQTT config: $MQTT_CONFIG_FILE" >&2
  exit 1
fi
. "$MQTT_CONFIG_FILE"

USERNAME="$SERIAL"
[ -n "$BROKER" ] && [ -n "$PORT" ] && [ -n "$USERNAME" ] && [ -n "$PASSWORD" ] || {
  echo "Invalid config: need BROKER, PORT, SERIAL (or USERNAME), PASSWORD" >&2
  exit 1
}

MARK_ID=0x64
MQTT_RETRY_SEC="${MQTT_RETRY_SEC:-5}"

GLOBAL_FILE="/tmp/global_ips.cache"
DEVICE_FILE="/tmp/device_ips.cache"
FINAL_FILE="/tmp/final_ips.cache"

TMP_FILE="/tmp/final_ips.tmp"

touch "$GLOBAL_FILE" "$DEVICE_FILE" "$FINAL_FILE" 2>/dev/null || true

apply_nft() {

    logger -t nft-update "Applying nftables rules..."

    nft flush table inet mangle 2>/dev/null
    nft add table inet mangle 2>/dev/null

    nft add chain inet mangle prerouting { type filter hook prerouting priority mangle\; } 2>/dev/null
    nft add chain inet mangle output { type route hook output priority mangle\; } 2>/dev/null

    nft add rule inet mangle prerouting ip daddr "$BROKER" counter mark set $MARK_ID
    nft add rule inet mangle output ip daddr "$BROKER" counter mark set $MARK_ID

    while read -r IP; do
        [ -z "$IP" ] && continue
        [ "$IP" = "$BROKER" ] && continue
        nft add rule inet mangle prerouting ip daddr "$IP" counter mark set $MARK_ID
        nft add rule inet mangle output ip daddr "$IP" counter mark set $MARK_ID
    done < "$FINAL_FILE"

    logger -t nft-update "nftables rules updated."
}

apply_nft

rebuild_final() {
    # DEVICE overrides GLOBAL
    if [ -s "$DEVICE_FILE" ]; then
        cp "$DEVICE_FILE" "$TMP_FILE"
    else
        cp "$GLOBAL_FILE" "$TMP_FILE"
    fi

    # Only apply if changed
    if ! cmp -s "$TMP_FILE" "$FINAL_FILE"; then
        mv "$TMP_FILE" "$FINAL_FILE"
        apply_nft
    else
        rm "$TMP_FILE"
    fi
}

set_global() {
    echo "$1" | tr ' ' '\n' > "$GLOBAL_FILE"
    logger -t nft-update "Updated GLOBAL whitelist"
    rebuild_final
}

set_device() {
    echo "$1" | tr ' ' '\n' > "$DEVICE_FILE"
    logger -t nft-update "Updated DEVICE whitelist"
    rebuild_final
}

# -------------------------
# Subscribe to global lists (reconnect forever if broker unreachable)
# -------------------------
(
  while true; do
    logger -t mqtt-whitelist "routers/all/whitelists: connecting to $BROKER:$PORT"
    mosquitto_sub -h "$BROKER" -p "$PORT" -u "$USERNAME" -P "$PASSWORD" \
        -i "$USERNAME-global" -t "routers/all/whitelists" -q 1 | while IFS= read -r payload; do
        set_global "$payload"
    done
    logger -t mqtt-whitelist "routers/all/whitelists: disconnected, retry in ${MQTT_RETRY_SEC}s"
    sleep "$MQTT_RETRY_SEC"
  done
) &

# -------------------------
# Subscribe to device lists
# -------------------------
(
  while true; do
    logger -t mqtt-whitelist "routers/$USERNAME/whitelists: connecting to $BROKER:$PORT"
    mosquitto_sub -h "$BROKER" -p "$PORT" -u "$USERNAME" -P "$PASSWORD" \
        -i "$USERNAME-device" -t "routers/$USERNAME/whitelists" -q 1 | while IFS= read -r payload; do
        set_device "$payload"
    done
    logger -t mqtt-whitelist "routers/$USERNAME/whitelists: disconnected, retry in ${MQTT_RETRY_SEC}s"
    sleep "$MQTT_RETRY_SEC"
  done
) &

wait
H_EOF_GETWHITELISTS
  chmod 755 /usr/bin/get_whitelists.sh || fail "chmod get_whitelists.sh"

  log "Writing /usr/bin/mqtt_cmd_dispatch.sh"
  cat <<'H_EOF_MQTT_DISPATCH' > /usr/bin/mqtt_cmd_dispatch.sh
#!/bin/sh
# One JSON command: parse, run, publish ACK (separate process — no subshell function bugs).
#
# Optional in /root/mqtt-router.conf:
#   MQTT_CMD_TIMEOUT_SEC=300   Wall-clock limit (seconds) for sh -c "$command"; then SIGTERM/KILL.
#   MQTT_CMD_TIMEOUT_SEC=0     No timeout (long jobs OK; use with async dispatch in run_commands.sh).

MQTT_CONFIG_FILE="/root/mqtt-router.conf"
if [ ! -f "$MQTT_CONFIG_FILE" ]; then
  echo "Missing MQTT config: $MQTT_CONFIG_FILE" >&2
  exit 1
fi
# shellcheck disable=SC1090
. "$MQTT_CONFIG_FILE"

USERNAME="$SERIAL"
MQTT_RETRY_SEC="${MQTT_RETRY_SEC:-5}"

PATH=/usr/sbin:/usr/bin:/sbin:/bin
export PATH
HOME=/root
export HOME

_json="$1"
[ -n "$_json" ] || exit 0

echo "[$(date)] Received command: $_json"

_cmd_id=$(printf '%s\n' "$_json" | jq -r '.id // empty')
_command=$(printf '%s\n' "$_json" | jq -r '.command // empty')
_reply_to=$(printf '%s\n' "$_json" | jq -r '.replyTo // empty')

if [ -z "$_command" ]; then
  echo "Failed to parse command"
  exit 0
fi

echo "Executing: $_command"

CMD_TO="${MQTT_CMD_TIMEOUT_SEC:-300}"
[ -z "$CMD_TO" ] && CMD_TO=300
case "$CMD_TO" in *[!0-9]*) CMD_TO=300 ;; esac

_outf=$(mktemp /tmp/mqtt_cmd.XXXXXX 2>/dev/null) || _outf="/tmp/mqtt_cmd_$$.out"
( cd /root 2>/dev/null || cd /
  sh -c "$_command"
) >"$_outf" 2>&1 &
_pid=$!

if [ "$CMD_TO" -gt 0 ]; then
  (
    sleep "$CMD_TO"
    if kill -0 "$_pid" 2>/dev/null; then
      logger -t mqtt-cmd "command timed out after ${CMD_TO}s (id=$_cmd_id), sending TERM"
      kill -TERM "$_pid" 2>/dev/null
      sleep 2
      kill -0 "$_pid" 2>/dev/null && kill -KILL "$_pid" 2>/dev/null
    fi
  ) &
  _wd=$!
  wait "$_pid"
  _exit=$?
  kill "$_wd" 2>/dev/null
  wait "$_wd" 2>/dev/null
else
  wait "$_pid"
  _exit=$?
fi

_output=$(cat "$_outf")
rm -f "$_outf"

_esc=$(printf '%s\n' "$_output" | jq -Rs .) || _esc='""'
_ts=$(date +%s)
_ack="{\"commandId\":\"$_cmd_id\",\"routerId\":\"$USERNAME\",\"status\":\"completed\",\"result\":{\"exit_code\":$_exit,\"output\":$_esc},\"timestamp\":$_ts}"

if [ -n "$_reply_to" ] && [ -n "$_cmd_id" ]; then
  _n=0
  while [ "$_n" -lt 60 ]; do
    if mosquitto_pub -h "$BROKER" -p "$PORT" \
        -u "$USERNAME" -P "$PASSWORD" \
        -t "$_reply_to" \
        -m "$_ack" \
        -q 1; then
      echo "Sent ACK for $_cmd_id"
      break
    fi
    _n=$((_n + 1))
    logger -t mqtt-cmd "ACK publish failed (attempt $_n), retry in ${MQTT_RETRY_SEC}s"
    sleep "$MQTT_RETRY_SEC"
  done
fi
H_EOF_MQTT_DISPATCH
  chmod 755 /usr/bin/mqtt_cmd_dispatch.sh || fail "chmod mqtt_cmd_dispatch.sh"

  log "Writing /usr/bin/run_commands.sh"
  cat <<'H_EOF_RUNCOMMANDS' > /usr/bin/run_commands.sh
#!/bin/sh

MQTT_CONFIG_FILE="/root/mqtt-router.conf"
if [ ! -f "$MQTT_CONFIG_FILE" ]; then
  echo "Missing MQTT config: $MQTT_CONFIG_FILE" >&2
  exit 1
fi
# shellcheck disable=SC1090
. "$MQTT_CONFIG_FILE"

USERNAME="$SERIAL"
CLIENT_ID="$SERIAL-cmd"
MQTT_RETRY_SEC="${MQTT_RETRY_SEC:-5}"

echo "Starting MQTT command listener for $USERNAME..."

# -------------------------
# Listen for commands (reconnect forever)
# FIFO + external mqtt_cmd_dispatch.sh — no functions inside subshells.
# -------------------------
(
  while true; do
    BUFFER=""
    logger -t mqtt-cmd "routers/$USERNAME/cmd: connecting to $BROKER:$PORT"
    FIFO=$(mktemp -u /tmp/mqtt_fifo.XXXXXX 2>/dev/null) || FIFO="/tmp/mqtt_fifo.$$"
    rm -f "$FIFO"
    if ! mkfifo "$FIFO" 2>/dev/null; then
      logger -t mqtt-cmd "mkfifo $FIFO failed, retry in ${MQTT_RETRY_SEC}s"
      sleep "$MQTT_RETRY_SEC"
      continue
    fi
    mosquitto_sub -h "$BROKER" -p "$PORT" \
        -u "$USERNAME" -P "$PASSWORD" \
        -i "$CLIENT_ID" \
        -t "routers/$USERNAME/cmd" \
        -c -q 1 >"$FIFO" &
    _M_PID=$!
    while IFS= read -r line <"$FIFO"; do
      BUFFER="${BUFFER}${line}"
      if echo "$BUFFER" | grep -q '}$'; then
        # Async: a long/hung command must not block handling the next MQTT message.
        sh /usr/bin/mqtt_cmd_dispatch.sh "$BUFFER" &
        BUFFER=""
      fi
    done
    kill "$_M_PID" 2>/dev/null
    wait "$_M_PID" 2>/dev/null
    rm -f "$FIFO"
    logger -t mqtt-cmd "cmd subscriber exited, retry in ${MQTT_RETRY_SEC}s"
    sleep "$MQTT_RETRY_SEC"
  done
) &

wait
H_EOF_RUNCOMMANDS
  chmod 755 /usr/bin/run_commands.sh || fail "chmod run_commands.sh"

  log "Writing /usr/bin/send_logs.sh"
  cat <<'H_EOF_SENDLOGS' > /usr/bin/send_logs.sh
#!/bin/sh

MQTT_CONFIG_FILE="/root/mqtt-router.conf"
. "$MQTT_CONFIG_FILE"

USERNAME="$SERIAL"

INTERFACE="wwan0"
TOPIC="routers/$USERNAME/logs"
CHAIN="prerouting"
TABLE="inet mangle"

run_with_timeout() {
  # usage: run_with_timeout <seconds> <outfile> -- command args...
  _sec="$1"; shift
  _out="$1"; shift
  [ "$1" = "--" ] && shift

  "$@" >"$_out" 2>/tmp/uqmi.err &
  _pid=$!

  (
    sleep "$_sec"
    kill -0 "$_pid" 2>/dev/null && kill -TERM "$_pid" 2>/dev/null
    sleep 1
    kill -0 "$_pid" 2>/dev/null && kill -KILL "$_pid" 2>/dev/null
  ) &
  _wd=$!

  wait "$_pid"
  _rc=$?
  kill "$_wd" 2>/dev/null
  wait "$_wd" 2>/dev/null
  return "$_rc"
}

uqmi_safe() {
  DEVICE="${DEVICE:-/dev/cdc-wdm0}"
  LOCKDIR="/tmp/uqmi.lock"
  TIMEOUT_SEC="${UQMI_TIMEOUT_SEC:-12}"
  RETRIES="${UQMI_RETRIES:-3}"

  i=1
  while [ "$i" -le "$RETRIES" ]; do
    if mkdir "$LOCKDIR" 2>/dev/null; then
      TMP="/tmp/uqmi.$$.$i.out"
      run_with_timeout "$TIMEOUT_SEC" "$TMP" -- uqmi -d "$DEVICE" "$@"
      RC=$?
      OUT="$(cat "$TMP" 2>/dev/null)"
      rm -f "$TMP"
      rmdir "$LOCKDIR" 2>/dev/null

      [ "$RC" -eq 0 ] && { printf '%s\n' "$OUT"; return 0; }
    else
      sleep 1
    fi

    sleep "$i"
    i=$((i + 1))
  done

  return 1
}

TMP_FILE="/tmp/nft_output.txt"
# --- READ STATS ---
TX_BYTES=$(cat /sys/class/net/$INTERFACE/statistics/tx_bytes)
RX_BYTES=$(cat /sys/class/net/$INTERFACE/statistics/rx_bytes)
TIMESTAMP=$(date -u +"%s")
SIGNAL_JSON_RAW=$(uqmi_safe --get-signal-info 2>/dev/null)
TYPE=""
RSSI=0
RSRP=0
RSRQ=0
SINR=0

if [ -n "$SIGNAL_JSON_RAW" ]; then
    RSSI=$(echo "$SIGNAL_JSON_RAW" | grep -o '"rssi":[^,]*' | cut -d: -f2)
    RSRP=$(echo "$SIGNAL_JSON_RAW" | grep -o '"rsrp":[^,]*' | cut -d: -f2)
    RSRQ=$(echo "$SIGNAL_JSON_RAW" | grep -o '"rsrq":[^,]*' | cut -d: -f2)
    SINR=$(echo "$SIGNAL_JSON_RAW" | grep -o '"sinr":[^,}]*' | cut -d: -f2)
    TYPE=$(echo "$SIGNAL_JSON_RAW" | grep -o '"type":[[:space:]]*"[^"]*' | cut -d'"' -f4)

    RSSI=${RSSI:-0}
    RSRP=${RSRP:-0}
    RSRQ=${RSRQ:-0}
    SINR=${SINR:-0}
fi

# --- GET NFTABLES COUNTERS ---
nft list chain "$TABLE" "$CHAIN" 2>/dev/null | grep 'ip daddr' > "$TMP_FILE" || {
    echo "Error: Failed to list nftables chain '$CHAIN' in table '$TABLE'" >&2
    exit 1
}

# --- BUILD SERVICES JSON ---
SERVICES_JSON=""
FIRST=1

while IFS= read -r line; do
    IP=$(echo "$line" | awk '{print $3}')
    BYTES=$(echo "$line" | awk '{for (i=1;i<=NF;i++) if ($i=="bytes") print $(i+1)}')
    PACKETS=$(echo "$line" | awk '{for (i=1;i<=NF;i++) if ($i=="packets") print $(i+1)}')

    [ -z "$BYTES" ] && BYTES=0
    [ -z "$PACKETS" ] && PACKETS=0

    [ -n "$IP" ] || continue

    ENTRY="{\"ip\":\"$IP\",\"bytes\":$BYTES,\"packets\":$PACKETS}"
    if [ "$FIRST" -eq 1 ]; then
        SERVICES_JSON="$ENTRY"
        FIRST=0
    else
        SERVICES_JSON="$SERVICES_JSON,$ENTRY"
    fi
done < "$TMP_FILE"

FINAL_JSON=$(cat <<EOF
{
  "timestamp": $TIMESTAMP,
  "tx_bytes": $TX_BYTES,
  "rx_bytes": $RX_BYTES,
  "signal": {
    "type": "$TYPE",
    "rssi": $RSSI,
    "rsrp": $RSRP,
    "rsrq": $RSRQ,
    "sinr": $SINR
  },
  "services": [
    $SERVICES_JSON
  ]
}
EOF
)

mosquitto_pub -h "$BROKER" -p "$PORT" \
    -u "$USERNAME" -P "$PASSWORD" \
    -i "$USERNAME" \
    -t "$TOPIC" \
    -m "$FINAL_JSON" \
    -q 1

if [ $? -eq 0 ]; then
    echo "Log published successfully"
else
    echo "Failed to publish log!" >&2
fi
H_EOF_SENDLOGS
  chmod 755 /usr/bin/send_logs.sh || fail "chmod send_logs.sh"

  log "Writing /usr/bin/sms.sh"
  cat <<'H_EOF_SMS' > /usr/bin/sms.sh
#!/bin/sh

MQTT_CONFIG_FILE="/root/mqtt-router.conf"
. "$MQTT_CONFIG_FILE"

USERNAME="$SERIAL"

DEVICE="/dev/cdc-wdm0"
TOPIC="routers/$USERNAME/sms"
POLL_INTERVAL=5

run_with_timeout() {
  # usage: run_with_timeout <seconds> <outfile> -- command args...
  _sec="$1"; shift
  _out="$1"; shift
  [ "$1" = "--" ] && shift

  "$@" >"$_out" 2>/tmp/uqmi.err &
  _pid=$!

  (
    sleep "$_sec"
    kill -0 "$_pid" 2>/dev/null && kill -TERM "$_pid" 2>/dev/null
    sleep 1
    kill -0 "$_pid" 2>/dev/null && kill -KILL "$_pid" 2>/dev/null
  ) &
  _wd=$!

  wait "$_pid"
  _rc=$?
  kill "$_wd" 2>/dev/null
  wait "$_wd" 2>/dev/null
  return "$_rc"
}

uqmi_safe() {
  DEVICE="${DEVICE:-/dev/cdc-wdm0}"
  LOCKDIR="/tmp/uqmi.lock"
  TIMEOUT_SEC="${UQMI_TIMEOUT_SEC:-12}"
  RETRIES="${UQMI_RETRIES:-3}"

  i=1
  while [ "$i" -le "$RETRIES" ]; do
    if mkdir "$LOCKDIR" 2>/dev/null; then
      TMP="/tmp/uqmi.$$.$i.out"
      run_with_timeout "$TIMEOUT_SEC" "$TMP" -- uqmi -d "$DEVICE" "$@"
      RC=$?
      OUT="$(cat "$TMP" 2>/dev/null)"
      rm -f "$TMP"
      rmdir "$LOCKDIR" 2>/dev/null

      [ "$RC" -eq 0 ] && { printf '%s\n' "$OUT"; return 0; }
    else
      sleep 1
    fi

    sleep "$i"
    i=$((i + 1))
  done

  return 1
}

while true; do
    MSG_IDS=$(uqmi_safe --list-messages 2>/dev/null | tr -d '[],')

    for ID in $MSG_IDS; do
        [ -z "$ID" ] && continue

        RAW=$(uqmi_safe --get-message "$ID" 2>/dev/null)

        SENDER=$(echo "$RAW" | grep -o '"sender"[[:space:]]*:[[:space:]]*"[^"]*' | cut -d'"' -f4)
        TEXT=$(echo "$RAW" | grep -o '"text"[[:space:]]*:[[:space:]]*"[^"]*' | cut -d'"' -f4)
        MSG_TS=$(echo "$RAW" | grep -o '"timestamp"[[:space:]]*:[[:space:]]*"[^"]*' | cut -d'"' -f4)

        SENDER=${SENDER:-"unknown"}
        TEXT=${TEXT:-""}
        MSG_TS=${MSG_TS:-""}

        JSON=$(cat <<EOF
{
  "sender": "$SENDER",
  "timestamp": "$MSG_TS",
  "text": "$TEXT"
}
EOF
)

        mosquitto_pub -h "$BROKER" -p "$PORT" \
            -u "$USERNAME" -P "$PASSWORD" \
            -i "$USERNAME" \
            -t "$TOPIC" \
            -m "$JSON" \
            -q 1

        if [ $? -eq 0 ]; then

            uqmi_safe --delete-message "$ID"
        else
            echo "Failed to publish SMS, keeping message" >&2
        fi
    done

    sleep $POLL_INTERVAL
done
H_EOF_SMS
  chmod 755 /usr/bin/sms.sh || fail "chmod sms.sh"

  RC_LOCAL="/etc/rc.local"
  marker="# medx-mqtt-router"
  opt_tag="medx-option-new-id"
  if [ ! -f "$RC_LOCAL" ]; then
    log "Creating minimal $RC_LOCAL"
    printf '%s\n' '#!/bin/sh' '' 'exit 0' > "$RC_LOCAL"
    chmod +x "$RC_LOCAL" 2>/dev/null || true
  fi

  # On boot: re-apply USB serial option id only if install created /etc/medx-option-new-id (no ttyUSB2 then).
  if [ -f /etc/medx-option-new-id ]; then
    if ! grep -qF "$opt_tag" "$RC_LOCAL" 2>/dev/null; then
      log "Adding USB option new_id boot hook to $RC_LOCAL"
      tmp="${RC_LOCAL}.new.$$"
      if grep -qF "$marker" "$RC_LOCAL" 2>/dev/null; then
        awk -v mqtt="$marker" '
          $0 == mqtt && !done {
            print "[ -f /etc/medx-option-new-id ] && echo \"05c6 9025 ff\" > /sys/bus/usb-serial/drivers/option1/new_id 2>/dev/null # medx-option-new-id"
            print ""
            done=1
          }
          { print }
        ' "$RC_LOCAL" >"$tmp" && mv "$tmp" "$RC_LOCAL" || fail "Failed to add option new_id to $RC_LOCAL"
      else
        awk '
          /^exit 0$/ && !done {
            print "[ -f /etc/medx-option-new-id ] && echo \"05c6 9025 ff\" > /sys/bus/usb-serial/drivers/option1/new_id 2>/dev/null # medx-option-new-id"
            print ""
            done=1
          }
          { print }
        ' "$RC_LOCAL" >"$tmp" && mv "$tmp" "$RC_LOCAL" || fail "Failed to add option new_id to $RC_LOCAL"
      fi
    else
      log "rc.local already contains $opt_tag hook"
    fi
  fi

  if grep -qF "$marker" "$RC_LOCAL" 2>/dev/null; then
    log "rc.local already contains $marker"
  else
    log "Adding MQTT services to $RC_LOCAL"
    tmp="${RC_LOCAL}.new.$$"
    awk -v m="$marker" '
      /^exit 0$/ && !done {
        print m
        print "ip rule add fwmark 0x64 lookup 100 priority 100"
        print "/usr/bin/get_whitelists.sh >/dev/null 2>&1 &"
        print "/usr/bin/run_commands.sh >/dev/null 2>&1 &"
        print "/usr/bin/sms.sh >/dev/null 2>&1 &"
        done=1
      }
      { print }
    ' "$RC_LOCAL" > "$tmp" && mv "$tmp" "$RC_LOCAL" || fail "Failed to update $RC_LOCAL"
  fi

  log "Installing crontab for send_logs.sh (every 5 minutes)"
  ( crontab -l 2>/dev/null | grep -vF "/usr/bin/send_logs.sh"; echo "*/5 * * * * /usr/bin/send_logs.sh" ) | crontab - || fail "Failed to install crontab"

  log "Persistence installed (scripts use $CONFIG_FILE)."
}

# Attach wwan to the same firewall zone as wan and ensure masquerading (dual-uplink NAT).
setup_firewall_nat() {
  log "Configuring firewall/NAT for wan + wwan..."
  wan_zone=""
  idx=0
  while uci -q get "firewall.@zone[$idx]" >/dev/null; do
    if [ "$(uci -q get "firewall.@zone[$idx].name")" = "wan" ]; then
      wan_zone="@zone[$idx]"
      break
    fi
    idx=$((idx + 1))
  done
  [ -n "$wan_zone" ] || fail "No firewall zone named 'wan' (check /etc/config/firewall)"

  uci set "firewall.${wan_zone}.masq"='1'
  uci set "firewall.${wan_zone}.input"='REJECT'
  uci set "firewall.${wan_zone}.output"='ACCEPT'
  uci set "firewall.${wan_zone}.forward"='REJECT'

  # Put wwan in wan zone so lan->wan forwards and MASQ apply to cellular too.
  w_found=0
  for n in $(uci -q get "firewall.${wan_zone}.network"); do
    [ "$n" = "wwan" ] && w_found=1 && break
  done
  if [ "$w_found" -eq 0 ]; then
    uci add_list "firewall.${wan_zone}.network"='wwan'
  fi

  # Ensure LAN can forward to wan zone (covers wan + wwan).
  have_lan_wan=0
  idx=0
  while uci -q get "firewall.@forwarding[$idx]" >/dev/null; do
    src=$(uci -q get "firewall.@forwarding[$idx].src")
    dest=$(uci -q get "firewall.@forwarding[$idx].dest")
    [ "$src" = "lan" ] && [ "$dest" = "wan" ] && have_lan_wan=1 && break
    idx=$((idx + 1))
  done
  if [ "$have_lan_wan" -eq 0 ]; then
    uci add firewall forwarding
    uci set firewall.@forwarding[-1].src='lan'
    uci set firewall.@forwarding[-1].dest='wan'
  fi

  uci commit firewall || fail "UCI commit firewall failed"
}

setup_lan() {
  LAN_IF="br-lan"
  LAN_IP="192.168.1.1"
  NETMASK="255.255.255.0"
  LAN_GATEWAY="192.168.1.2"

  log "Configuring LAN $LAN_IP/$NETMASK with default gateway $LAN_GATEWAY..."

  uci set network.lan.proto='static'
  uci set network.lan.ipaddr="$LAN_IP"
  uci set network.lan.netmask="$NETMASK"
  uci set network.lan.gateway="$LAN_GATEWAY"

  uci set network.lan.metric='10'
  uci set network.wwan.metric='200'

  uci set dhcp.lan.ignore='1'

  setup_firewall_nat

  uci commit dhcp || fail "UCI commit dhcp failed"
  uci commit network || fail "UCI commit network failed"
  /etc/init.d/dnsmasq restart
  /etc/init.d/network restart
  /etc/init.d/firewall restart

  log "LAN ready; NAT/masquerade on wan zone (wan + wwan)."
}

# --- main (step 1) ---
log "MQTT broker configuration"
prompt_mqtt_settings
write_config
log "Saved configuration to $CONFIG_FILE"

log "Setting up SIM card..."
setup_simcard
log "SIM card setup complete."

log "SMS on SIM storage (AT)..."
setup_sms_sim_storage

log "Installing dependencies..."
install_deps
log "Dependencies installed."

log "Setting up policy routing..."
setup_routing
log "Policy routing ready."

log "Setting up hotplug script..."
setup_hotplug
log "Hotplug script ready."

log "Installing MQTT client scripts and persistence..."
set_persistance
log "Persistence ready."

log "Setting up LAN..."
setup_lan
log "LAN ready. Setup complete."

Guys any advice to this

Without you answering the questions, why would we ?

wic question
i didnt get it

was pinging from inside the router (ssh in router) and in lan

Probably the posts containing a ?...

We're interested in the results, not your thoughts.

have answered the question
do u have an idea how i can solve it

In my experience, LTE connections are unreliable by nature, you cannot expect for it to just work. I use ModemManager to restart the connection when it fails, plus some watchcat scripts to reboot the router as a last resource.

That aligns with a common scenario where the provider initiates a disconnect due to inactivity or simply because their policy dictates it.
Search the forum for watchcat.

It is still not explicitly clear to me, whether "no traffic" is caused by malfunction of modem (hanging) or disconnection from provider. It might help, to know type of modem (use AT, qmicli) . Using qmicli or AT, in state of "no traffic", is modem still responding to AT or qmicli, i.e providing connection status or SIM status ? What is connection status, SIM-status then, and are other config settings, like APN, profile still OK? And to get LTE connection again, is a soft boot good enough, or is power cycle required ? If possible, provide system log covering the instant of time, when connection is lost. Log to start few minutes before disconnection. You might anonymize MACs or IPs.

Worst case, I have seen, were real modem hangs with Quectels EC25, only to be recovered by power cycle. Luckily, this was also possibe programmatically.