前段时间做完一个是视频数据传输终端,使用4G模块拨号,接上摄像头,当一个路由器来传输视频信号;以及本身会带一些远程管理功能;当然这不是本文的重点;在此仅重点介绍4G拨号相关的内容,因为从开发到上线到量产过程中,拨号稳定性一直是一个考验;
设备使用的是openwrt系统,Ulong9300 LTE模块,MiniPcie接口,其实就是usb;
首先介绍几个相关的工具:
comgt:主要是发送AT指令以及读取结果,非常轻量级,一个.c文件,支持多种语法,可以自行编写配置脚本
pppd:3g ppp 协议拨号 工具,使用配置文件配置拨号参数:如APN,用户名,断线检测周期等等
usbserial: usb转tty驱动
option: usb转tty驱动
Ndis: LTE拨号专有驱动,与usbserial虚拟成串口不同,ndis虚拟成网口,可以获得更高的速率
下面根据使用流程一个一个做相关详细介绍:
驱动:如果使用usbserial驱动,Linux Kernel自带,无需配置,直接install即可;Option驱动同样;
使用ndis驱动,需要LTE模块厂家提供相关的驱动包,以及应用层拨号程序
pppd参数:主要是APN和Dialnumber,用户名和密码一般不需要;APN及Dialnumber 可以咨询运营商,每个运营商固定的,不过地区不同也可能存在差别,尤其是现在IOT的M2M卡,专用卡等等。
可以通过SIM卡ICCID来判断运营商类别,然后设定相关的APN和Dialnumber。参考comgt脚本如下:
:cimi
waitquiet 1 0.2
send "AT+CIMI^m"
waitfor 3 "46000" "46002" "46007" "46001" "46006" "46003" "46005" "46011"
if % = -1 goto err
if % = 0 goto cmnet
if % = 1 goto cmnet
if % = 2 goto cmnet
if % = 3 goto net3g
if % = 4 goto net3g
if % = 5 goto ctlte
if % = 6 goto ctlte
if % = 7 goto ctlte
# default
goto webset
:err
exit 1
:cmnet
send "AT+CGDCONT=1,\"IP\",\"CMNET\"^m"
print "CMNET\n"
goto waitret
:net3g
send "AT+CGDCONT=1,\"IP\",\"3GNET\"^m"
print "3GNET\n"
goto waitret
:ctlte
send "AT+CGDCONT=1,\"IP\",\"CTLTE\"^m"
print "CTLTE\n"
:waitret
#!/bin/sh
[ -n "$INCLUDE_ONLY" ] || {
NOT_INCLUDED=1
INCLUDE_ONLY=1
. ../netifd-proto.sh
. ./ppp.sh
init_proto "$@"
}
proto_3g_init_config() {
no_device=1
available=1
ppp_generic_init_config
proto_config_add_string "device:device"
proto_config_add_string "apn"
proto_config_add_string "service"
proto_config_add_string "pincode"
proto_config_add_string "dialnumber"
proto_config_add_string "username"
proto_config_add_string "password"
proto_config_add_string "at_port"
proto_config_add_string "private_dial"
}
proto_3g_setup() {
local interface="$1"
local chatcfg
。。。发送一些AT指令,如查询SIM卡,信号质量,注册的网络等等,反正目的是为了查询保证拨号的条件 。。。
ppp_generic_setup "$interface" \
noaccomp \
${username:+user "$username" password "$password"} \
nopcomp \
novj \
nobsdcomp \
noauth \
lock \
modem \
crtscts \
115200 "$device"
return 0
}
proto_3g_teardown() {
proto_kill_command "$interface"
}
[ -z "NOT_INCLUDED" ] || add_protocol 3g
#!/bin/sh
PPP_IPPARAM="$6"
. /lib/netifd/netifd-proto.sh
echo -n > /tmp/dialcount
touch /tmp/dialok
proto_init_update "$IFNAME" 1 1
proto_set_keep 1
[ -n "$PPP_IPPARAM" ] && {
[ -n "$IPLOCAL" ] && proto_add_ipv4_address "$IPLOCAL" 32 "" "${IPREMOTE:-2.2.2.2}"
[ -n "$IPREMOTE" ] && proto_add_ipv4_route 0.0.0.0 0 "$IPREMOTE"
[ -n "$LLLOCAL" ] && proto_add_ipv6_address "$LLLOCAL" 128
[ -n "$DNS1" ] && proto_add_dns_server "$DNS1"
[ -n "$DNS2" -a "$DNS1" != "$DNS2" ] && proto_add_dns_server "$DNS2"
}
proto_send_update "$PPP_IPPARAM"
[ -d /etc/ppp/ip-up.d ] && {
for SCRIPT in /etc/ppp/ip-up.d/*
do
[ -x "$SCRIPT" ] && "$SCRIPT" "$@"
done
}
if [ -n "$AUTOIPV6" ]; then
json_init
json_add_string name "${PPP_IPPARAM}_6"
json_add_string ifname "@$PPP_IPPARAM"
json_add_string proto "dhcpv6"
json_close_object
ubus call network add_dynamic "$(json_dump)"
fi
主要是设定主路由表并通知相关模块;可以自行创建dialok等文件表示拨号成功,不需要再特别去查询;这样简单方便;当然失败时需要自行删除文件。#!/bin/sh
PPP_IPPARAM="$6"
. /lib/netifd/netifd-proto.sh
rm -f /tmp/dialok
echo "$(date)- ppp down ###" >> /tmp/at_failed_ret
proto_init_update "$IFNAME" 0
proto_send_update "$PPP_IPPARAM"
[ -d /etc/ppp/ip-down.d ] && {
for SCRIPT in /etc/ppp/ip-down.d/*
do
[ -x "$SCRIPT" ] && "$SCRIPT" "$@"
done
}
以上为一次拨号流程;那么如何保证24小时在线?就需要另外一套机制来保证;最简单的就是轮询了
openwrt 自带crond,PC上使用广泛的任务轮询调度模块,可以配置在几点几分周几,间隔多久运行什么程序,非常灵活;
本文设定每5分钟检测一次;检测脚本如下:
#!/bin/sh
resetmodule()
{
echo Init > /tmp/sim-info
echo 0 > /tmp/sig
echo 1 > /sys/class/leds/modrst/brightness
echo "$(date)-repower-4g-module" >> /tmp/repower-4g-module
echo "$(date)-repower-4g-module" >> /tmp/at_failed_ret
sleep 5
echo 0 > /sys/class/leds/modrst/brightness
}
checkat()
{
# other process has do at-cmd
local num="$(ps |grep /etc/gcom |wc -l)"
[ $num -gt 1 ] && return 1
gcom -d "$1" -s /etc/gcom/checkpin.gcom > /tmp/sim-info
num="$(ps |grep /etc/gcom |wc -l)"
[ $num -gt 1 ] && return 1
gcom -d "$1" -s /etc/gcom/getstrength.gcom > /tmp/sig
num="$(ps |grep /etc/gcom |wc -l)"
[ $num -gt 1 ] && return 1
gcom -d "$1" -s /etc/gcom/check_status.gcom > /tmp/module_status_file
if cat /tmp/sim-info | grep -qi 'Ready'; then
[ ! -e "/tmp/sim_ready" ] && touch /tmp/sim_ready
# no dialing ; no 3g-ppp; sim-card ok
ifconfig 3g-ppp | grep -qi 'inet addr:' || /sbin/ifup ppp
elif ifconfig 3g-ppp | grep -qi 'inet addr:'; then
# no dialing; 3g-ppp ok' ; sim-card error; detecting error
echo Ready > /tmp/sim-info
[ ! -e "/tmp/sim_ready" ] && touch /tmp/sim_ready
else
logger -t ltecheck "Check:SIM-error and ppp failed"
echo "$(date)-SIM-error and ppp dial failed" >> /tmp/at_failed_ret
cat /tmp/module_status_file >> /tmp/at_failed_ret
rm -f /tmp/dialok
fi
}
rm -rf /tmp/gcom*
# ttyUSBx is not exist
[ ! -e "$1" ] && return 1
[ ! -e "/tmp/cfuncount" ] && echo -n > /tmp/cfuncount
[ ! -e "/tmp/dialcount" ] && echo -n > /tmp/dialcount
[ ! -e "/tmp/at_failed_ret" ] && echo -n > /tmp/at_failed_ret
num="$(cat /tmp/at_failed_ret | wc -l)"
if [ $num -ge 800 ]; then
echo -n > /tmp/at_failed_ret
fi
if [ -e "/tmp/dialok" ]; then
checkat "$1"
echo -n > /tmp/dialcount
echo -n > /tmp/cfuncount
else
ifdown ppp
sleep 5
num="$(cat /tmp/cfuncount | wc -l)"
if [ $num -ge 3 ]; then
logger -t ltecheck "cfuncount>3,to reset module"
#cfun=0
gcom -d "$1" -s /etc/gcom/clearcfun.gcom
echo -n > /tmp/dialcount
echo -n > /tmp/cfuncount
resetmodule
sleep 20
ifup ppp
else
num="$(cat /tmp/dialcount | wc -l)"
if [ $num -ge 5 ]; then
logger -t ltecheck "dialcount>5,to reset cfun"
gcom -d "$1" -s /etc/gcom/clearcfun.gcom
gcom -d "$1" -s /etc/gcom/setcfun.gcom
echo "$(date)-cfun-reset" >> /tmp/cfuncount
echo "$(date)-cfun-reset" >> /tmp/at_failed_ret
echo -n > /tmp/dialcount
ifup ppp
else
logger -t ltecheck "$(date) ppp redial"
echo "$(date) ppp redial" >> /tmp/at_failed_ret
cat /tmp/module_status_file >> /tmp/at_failed_ret
ifup ppp
fi
fi
fi
主要流程为:
首先检测dialok文件(由ppp-up创建)在不在,再继续查询AT,当然AT有可能查询失败,AT结果写入tmp文件
然后判断文件或文件内容,此处文件不一定可靠,因为是人为创建的,如果考虑的不全,文件该清除的时候没清楚,该创建的时候没创建的情况也是有的;因此文件不对时,再查询一次系统实际情况,如ifconfig等,然后比较,即可判断出实际情况;
关于resetmodule,为什么要reset模块,因此模块里面也是系统也有程序,也是人写的;而且网络环境非常复杂,尤其在中国,因此模块也有一定几率出错,表现情况就是:AT一直失败,拨号一直不成功等;此时,就需要reset模块;一般来说有两种方法,软复位:设置CFUN,所有模块通用;硬复位:通过控制模块复位脚,需要硬件预先设计;
复位之后,模块重启,openwrt会自动检测到模块接入,然后执行拨号流程,如果没有,那么crond轮询程序在超时后也会主动检测一次;
理论上,crond可以保证永远拨号;crond自身做的工作很少,其稳定性可以保证;
到此,基本介绍完毕;
后续可能的优化:不使用hotplug机制,不使用procd自动调用机制,纯粹依靠crond来轮询执行;虽然拨号过程慢一些,但稳定性更加的得到提升;