06 NTP
作者:沈园园
一、NTP通信概述
SNTP是简化版的NTP,NTP(Network Time Protocol,网络时间协议)是用于同步系统时间的协议。它通过网络连接多个设备,确保这些设备的系统时钟保持一致。SNTP相比较NTP主要区别在于精确度和复杂性。SNTP通常用于不需要高精度同步的设备。而NTP则用于要求较高时间精度的场景。
虽然现在的程序名字使用的是SNTP,但现在底层的实现已经改成NTP了,只是名字还没改过来。对模组来说, 没多大区别。
二、演示功能概述
本demo将演示使用NTP授时服务来同步时间。
三、准备硬件环境
参考:硬件环境清单第二章节内容,准备以及组装好硬件环境。
四、软件环境
在开始实践本示例之前,先筹备一下软件环境:
1. Luatools工具;
2. 内核固件文件(底层core固件文件):LuatOS-SoC_V2002_Air780EP;参考项目使用的内核固件;
3. luatos需要的脚本和资源文件
脚本和资源文件点我,下载完整压缩文件包
lib脚本文件:使用Luatools烧录时,勾选 添加默认lib 选项,使用默认lib脚本文件;
准备好软件环境之后,接下来查看如何烧录项目文件到Air780EP核心板,将本篇文章中演示使用的项目文件烧录到Air780EP核心板中。
4. 本教程使用 api 接口为:ntp-api地址
五、代码示例介绍
5.1 使用NTP授时服务来同步时间
5.1.1 demo 介绍
-- LuaTools需要PROJECT和VERSION这两个信息
PROJECT = "sntpdemo"
VERSION = "1.0.0"
--[[
本demo需要mqtt库, 大部分能联网的设备都具有这个库
mqtt也是内置库, 无需require
]]
-- sys库是标配
_G.sys = require("sys")
--[[特别注意, 使用mqtt库需要下列语句]]
_G.sysplus = require("sysplus")
-- Air780E的AT固件默认会为开机键防抖, 导致部分用户刷机很麻烦
if rtos.bsp() == "EC618" and pm and pm.PWK_MODE then
pm.power(pm.PWK_MODE, false)
end
-- 统一联网函数
sys.taskInit(function()
local device_id = mcu.unique_id():toHex()
-----------------------------
-- 统一联网函数, 可自行删减
----------------------------
if wlan and wlan.connect then
-- wifi 联网, ESP32系列均支持
local ssid = "luatos1234"
local password = "12341234"
log.info("wifi", ssid, password)
-- TODO 改成自动配网
-- LED = gpio.setup(12, 0, gpio.PULLUP)
wlan.init()
wlan.setMode(wlan.STATION) -- 默认也是这个模式,不调用也可以
device_id = wlan.getMac()
wlan.connect(ssid, password, 1)
elseif mobile then
-- Air780E/Air600E系列
--mobile.simid(2) -- 自动切换SIM卡
-- LED = gpio.setup(27, 0, gpio.PULLUP)
device_id = mobile.imei()
elseif w5500 then
-- w5500 以太网, 当前仅Air105支持
w5500.init(spi.HSPI_0, 24000000, pin.PC14, pin.PC01, pin.PC00)
w5500.config() --默认是DHCP模式
w5500.bind(socket.ETH0)
-- LED = gpio.setup(62, 0, gpio.PULLUP)
elseif socket then
-- 适配的socket库也OK
-- 没有其他操作, 单纯给个注释说明
else
-- 其他不认识的bsp, 循环提示一下吧
while 1 do
sys.wait(1000)
log.info("bsp", "本bsp可能未适配网络层, 请查证")
end
end
-- 默认都等到联网成功
sys.waitUntil("IP_READY")
sys.publish("net_ready", device_id)
end)
sys.taskInit(function()
-- 等待联网
local ret, device_id = sys.waitUntil("net_ready")
sys.wait(1000)
-- 对于Cat.1模块, 移动/电信卡, 通常会下发基站时间, 那么sntp就不是必要的, 而联通卡通常不会下发, 就需要sntp了
-- 对应ESP32系列模块, 固件默认也会执行sntp, 所以手动调用sntp也是可选的
-- sntp内置了几个常用的ntp服务器, 也支持自选服务器
while 1 do
-- 使用内置的ntp服务器地址, 包括阿里ntp
log.info("开始执行SNTP")
socket.sntp()
-- 自定义ntp地址
-- socket.sntp("ntp.aliyun.com")
-- socket.sntp({"baidu.com", "abc.com", "ntp.air32.cn"})
-- 通常只需要几百毫秒就能成功,超时时间2s
local ret = sys.waitUntil("NTP_UPDATE", 5000)
if ret then
-- 以下是获取/打印时间的演示,注意时区问题
log.info("sntp", "时间同步成功", "本地时间", os.date())
log.info("sntp", "时间同步成功", "UTC时间", os.date("!%c"))
log.info("sntp", "时间同步成功", "RTC时钟(UTC时间)", json.encode(rtc.get()))
-- os.time(rtc.get()) 需要 2023.07.21 之后的版本, 因为月份的命名差异mon/month
-- log.info("sntp", "时间同步成功", "utc时间戳", os.time(rtc.get()))
log.info("sntp", "时间同步成功", "本地时间戳", os.time())
local t = os.date("*t")
log.info("sntp", "时间同步成功", "本地时间os.date() json格式", json.encode(t))
log.info("sntp", "时间同步成功", "本地时间os.date(os.time())", os.time(t))
-- log.info("sntp", "时间同步成功", "本地时间", os.time())
-- 正常使用, 一小时一次, 已经足够了, 甚至1天一次也可以
-- sys.wait(3600000)
-- 这里为了演示, 用5秒一次
sys.wait(5000)
else
log.info("sntp", "时间同步失败")
sys.wait(60000) -- 1分钟后重试
end
-- 时间戳, 精确到毫秒. 2023.11.15 新增
-- 注意, 至少成功完成2次sntp,该时间戳才比较准确
-- 如果仅完成了一次sntp, 时间戳比标准时间会慢一个网络延时的时长(10~500ms)不等
if socket.ntptm then
local tm = socket.ntptm()
log.info("tm数据", json.encode(tm))
log.info("时间戳", string.format("%u.%03d", tm.tsec, tm.tms))
sys.wait(5000)
end
end
end)
-- 订阅ntp同步时间失败的消息
sys.subscribe("NTP_ERROR", function()
log.info("socket", "sntp error")
-- socket.sntp()
end)
-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()
-- sys.run()之后后面不要加任何语句!!!!!
六、功能验证
七、总结
本 demo 介绍了如何使用NTP服务器同步时间。
常见问题
1、NTP同步时间后,这个clock精度高吗,需要多久同步一次
并不能保证任何时间任何地点都能百分百同步到正确的时间。 所以,如果用户项目中的业务逻辑严格依赖于时间同步功能 则不要使用本功能模块,建议使用自己的应用服务器来同步时间。
2、多长时间NTP同步一次
正常使用, 一小时一次, 已经足够了, 甚至 1 天一次也可以。
3、这个函数 socket.sntp()后每次在程序中调用 os.time 也是实时时间了吗?
是的,只要时间同步成功了,就是实时时间了。