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."
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.