很多场景中,由于业务需要,模块需要保持正确的系统时钟,才能正常工作。但是模块上电后的初试时间戳是1338516000(即2012/06/01,10:00:00),所以同步时钟成为了开发者要解决的重要问题。
首先我们捋一下思路——模块要如何执行动作,执行何种动作,才可以同步时钟呢?基于这个疑问,我们先做一个定义:
只有当模块和外界以某种方式产生数据交互,且模块收到完整数据并正确解析时,才可以完成对应操作。
定义中指的“某种方式”是没有局限性的,也就是说开发者可以自由发挥想象力,只有你想不到,没有它做不到。接下来为大家介绍一些常见的和不常见的方式,方便开发者根据不同场景选择合适的方式。
(下文中,如无额外说明,均指Lua方式的二次开发)
一、常见且常用的NTP
NTP是大家众所周知的、广泛使用的、精确可靠的时钟同步方式。具有高效、准确、流量少,易实现的特点。NTP这种方式,就是使用“定义”中的蜂窝网络,和远端NTP服务器交互数据,收到服务器的回应并解析,重新设定系统时钟,完成同步。
我司的lua代码也提供了相应的基础库,开发者只需要简单的调用相应的函数,即可实现同步时钟:
luaScript:
require”ntp” —对,就这一行代码即可哦
luaTask:
require”ntp”
ntp.timeSync() —只同步一次
–ntp.timeSync(1) —每1小时同步一次
实际运行一下,看看效果如何:
需要注意的是,NTP使用的是多个免费公共的NTP服务器来同步时钟,所以不能保证任何时间任何地点都能百分百同步到正确的时间。如果开发者项目中的业务逻辑严格依赖于时钟同步功能,建议使用开发者自己的服务器进行同步。
二、精确到纳秒的GPS
如果开发者使用的是Air8xx系列模块,或者是其他挂载GPS模块的Air2xx系列产品,那么可以是用这个方式进行同步。
由于卫星定位系统的特性,每颗卫星都有精确到纳秒级的铯原子钟,才能实现全球的精确定位。故此,当开发者使用含GPS功能的模块时,可以使用GPS授时特性实现同步时钟。此种方式,就是使用“定义”中的GPS卫星信号,解析数据,设定模块系统时钟,完成同步。
实现原理并不复杂,只要解析GPS模块上报GGA即可:
格式:$–GGA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*hh
示例:$GPGGA,065545.789,2109.9551,N,12023.4047,E,1,9,0.85,18.1,M,8.0,M,,*5E
名称 | 样例 | 单位 | 描述 |
消息ID | $GPGGA | GGA 协议头 | |
UTC 时间 | 065545.789 | hhmmss.sss | |
纬度 | 2109.9551 | ddmm.mmmm | |
N/S | N | N=北,S=南 | |
经度 | 12023.4047 | dddmm.mmmm | |
E/W | W=西,E=东 | ||
定位指示 |
0:未定位 1:SPS 模式,定位有效 2:差分,SPS 模式,定位有效 3:PPS 模式,定位有效 |
||
卫星数目 | 9 | 范围 0 到 12 | |
HDOP | 0.85 | 水平精度 | |
MSL 幅度 | 18.1 | 米 | |
单位 | M | 米 | |
大地 | -2.2 | 米 | |
单位 | M | – | |
差分时间 | 8.0 | 秒 | 当没有 DGPS 时,无效 |
差分 ID | 0000 | ||
校验和 | *5E | ||
|
消息结束 |
当然,解析RMC亦可哦:
格式:$–RMC,hhmmss.ss,A,llll.ll,a,yyyyy.yy,a,x.x,x.x,xxxx,x.x,a*hh
例句:$GPRMC,100646.000,A,3109.9704,N,12123.4219,E,0.257,335.62,291216,,,A*59
名称 | 样例 | 单位 | 描述 |
消息ID | $GPRMC | RMC 协议头 | |
UTC 时间 | 100646.000 | hhmmss.sss | |
状态 | A | A=数据有效;V=数据无效 | |
纬度 | 2109.9704 | ddmm.mmmm | |
N/S | N | N=北,S=南 | |
经度 | 11123.4219 | dddmm.mmmm | |
E/W | W=西,E=东 | ||
地面速度 | 0.257 | Knot(节) | |
方位 | 335.62 | 度 | |
日期 | 291216 | ddmmyy | |
磁变量 | – | ||
校验和 | *59 | ||
|
消息结束 |
我司的lua代码也提供了相应的基础库,开发者只需要简单的调用相应的函数,即可实现同步时钟:
luaScript:
require”gps”
gps.init() —初始化
gps.setfixmode(0) –GPS+BD定位
gps.settimezone(gps.GPS_BEIJING_TIME) —设置时区为北京
gps.open(gps.DEFAULT,{cause=”time_sync”}) —打开gps
—剩下的gps.lua都会自动处理好了
luaTask:
require”gps”
require”sys”
require”misc”
gps.open(gps.DEFAULT,{tag=”time_sync”})
sys.timeLoopStart( function()
If gps.isFix then
misc.setClock(gps.getUtcTime())
end
end, 60000)
相对于使用NTP等方式,此方法不受蜂窝网络限制,即使无卡或者卡欠费,也可以准确校时。但是需要注意的是,此种方法略有局限性:
- 必须使用有GPS芯片的模块,或者挂在GPS模块,且必须可以搜到1颗以上卫星才能获取时间;
- 卫星播发的时间是UTC,所以开发者还需要根据经度判断时区做对应的加减才可以;
- 要注意定位数据的有效性。
三、薅基站羊毛的AT+CLTS
如果我告诉你,基站也有授时服务哦,惊不惊喜,意不意外?
模块首次搜到基站的时候,即可获得基站下发的时间信息。发AT+CLTS即可。
由于此方法通用性并不是非常强,所以仅贴出AT交互流程,有能力的开发者请自行改写lua代码吧:
需要注意的是,此方法存在一定局限性:1、不是所有的基站都会下发,存在返回是nil的情况(具体要看基站的配置);2、只有附着的那一刻是准确的,随后再发AT+CLTS都是恒定值(即附着时),除非进入飞行模式后再离开飞行模式,重新获取一次;3、需要有卡才能使用。
四、让运营商来给你打工的SMS
有什么办法让运营商来给你打工吗?当然啦,今天就让运营商来出一次血——利用运营商下行短信的时间同步模块时钟。
luaScript & luaTask:
require”sms”
local function
procnewsms(num, data, datetime)
print(“procnewsms”, num, data,
datetime)
end
sms.regnewsmscb(procnewsms)
sms.send(“10086”,
“hi, china mobile”)
—给10068发一条内容为“hi, china mobile”的短信,等他返回短信的时候,就自带datetime啦,开发者可以根据datetime同步时系统时钟。
此种方法基本上百试百灵,而且给运营商发短信时双向免费的哦;不过一定要注意使用的SIM卡是否又短信功能。如果没有,是无法使用这个方法同步时钟的。
结束语
不论是直接去访问time.123cha.com这种提时钟的网页,抓取关键数据;还是通过mqtt传输时间字符串;哪怕打电话给模块,通过DTMF设置时间,都是完全没问题的。
正如“定义”所言,只要能产生数据交互,一切就皆有可能。