init: Air780EPM 官方 LuatOS 项目代码基线
- 来源: 合宙 LuatOS 官方仓库 air780epm 模块完整代码 - 路径: luatos/air780epm/module/Air780EPM/demo 含官方 demo(含 mqtt/mqtts/socket/uart 等) - 后续: 基于 demo 开发 UART<->MQTT 数据上报功能
This commit is contained in:
@@ -0,0 +1,289 @@
|
||||
--[[
|
||||
@module mqtts_ca_main
|
||||
@summary mqtts ca client 主应用功能模块
|
||||
@version 1.0
|
||||
@date 2025.07.28
|
||||
@author 马梦阳
|
||||
@usage
|
||||
本文件为mqtts ca client 主应用功能模块,核心业务逻辑为:
|
||||
1、创建一个mqtts ca client,连接server;
|
||||
2、处理连接/订阅/取消订阅/异常逻辑,出现异常后执行重连动作;
|
||||
3、调用mqtts_ca_receiver的外部接口mqtts_ca_receiver.proc,对接收到的publish数据进行处理;
|
||||
4、调用sys.sendMsg接口,发送"CONNECT OK"、"PUBLISH OK"和"DISCONNECTED"三种类型的"MQTT_EVENT"消息到mqtts_ca_sender的task,控制publish数据发送逻辑;
|
||||
5、收到MQTT心跳应答后,执行sys.publish("FEED_NETWORK_WATCHDOG") 对网络环境检测看门狗功能模块进行喂狗;
|
||||
|
||||
本文件没有对外接口,直接在main.lua中require "mqtts_ca_main"就可以加载运行;
|
||||
]]
|
||||
|
||||
-- 加载sntp时间同步应用功能模块(证书校验的mqtt ssl需要时间同步功能)
|
||||
require "sntp_app"
|
||||
|
||||
-- 加载mqtts ca client数据接收功能模块
|
||||
local mqtts_ca_receiver = require "mqtts_ca_receiver"
|
||||
-- 加载mqtts ca client数据发送功能模块
|
||||
local mqtts_ca_sender = require "mqtts_ca_sender"
|
||||
|
||||
-- mqtts ca服务器地址和端口
|
||||
-- 这里使用的地址和端口,会不定期重启或维护,仅能用作测试用途,不可商用,说不定哪一天就关闭了
|
||||
-- 用户开发项目时,替换为自己的商用服务器地址和端口
|
||||
local SERVER_ADDR = "airtest.openluat.com"
|
||||
local SERVER_PORT = 8888
|
||||
|
||||
-- mqtt连接时的用户名和密码
|
||||
-- 必须填写,否则会连接失败
|
||||
local USERNAME = "mqtt_hz_test_2"
|
||||
local PASSWORD = "3bEKUb"
|
||||
|
||||
-- mqtts_ca_main的任务名
|
||||
local TASK_NAME = mqtts_ca_sender.TASK_NAME_PREFIX.."main"
|
||||
|
||||
-- mqtt主题的前缀:IMEI号
|
||||
local TOPIC_PREFIX = mobile.imei()
|
||||
|
||||
-- mqtts ca client的事件回调函数
|
||||
local function mqtts_ca_client_event_cbfunc(mqtt_client, event, data, payload, metas)
|
||||
log.info("mqtts_ca_client_event_cbfunc", mqtt_client, event, data, payload, json.encode(metas))
|
||||
|
||||
-- mqtt连接成功
|
||||
if event == "conack" then
|
||||
sys.sendMsg(TASK_NAME, "MQTT_EVENT", "CONNECT", true)
|
||||
-- 订阅单主题
|
||||
-- 第二个参数表示qos,取值范围为0,1,2,如果不设置,默认为0
|
||||
if not mqtt_client:subscribe(TOPIC_PREFIX .. "/down") then
|
||||
sys.sendMsg(TASK_NAME, "MQTT_EVENT", "SUBSCRIBE", false, -1)
|
||||
end
|
||||
-- 订阅多主题,如果有需要,打开注释
|
||||
-- 表中的每一个订阅主题的格式为[topic]=qos
|
||||
-- if not mqtt_client:subscribe(
|
||||
-- {
|
||||
-- [(TOPIC_PREFIX .. "/data"]=0,
|
||||
-- [(TOPIC_PREFIX .. "/cmd"]=1
|
||||
-- }
|
||||
-- ) then
|
||||
-- sys.sendMsg(TASK_NAME, "MQTT_EVENT", "SUBSCRIBE", false, -1)
|
||||
-- end
|
||||
|
||||
-- 订阅结果
|
||||
-- data:订阅应答结果,true为成功,false为失败
|
||||
-- payload:number类型;成功时表示qos,取值范围为0,1,2;失败时表示失败码,一般是0x80
|
||||
elseif event == "suback" then
|
||||
-- 发送消息通知 mqtts ca main task
|
||||
sys.sendMsg(TASK_NAME, "MQTT_EVENT", "SUBSCRIBE", data, payload)
|
||||
|
||||
-- 取消订阅成功
|
||||
elseif event == "unsuback" then
|
||||
-- 发送消息通知 mqtts ca main task
|
||||
sys.sendMsg(TASK_NAME, "MQTT_EVENT", "UNSUBSCRIBE", true)
|
||||
|
||||
-- 接收到服务器下发的publish数据
|
||||
-- data:string类型,表示topic
|
||||
-- payload:string类型,表示payload
|
||||
-- metas:table类型,数据内容如下
|
||||
-- {
|
||||
-- qos: number类型,取值范围0,1,2
|
||||
-- retain:number类型,取值范围0,1
|
||||
-- dup:number类型,取值范围0,1
|
||||
-- message_id: number类型
|
||||
-- }
|
||||
elseif event == "recv" then
|
||||
-- 对接收到的publish数据处理
|
||||
mqtts_ca_receiver.proc(data, payload, metas)
|
||||
|
||||
-- 发送成功publish数据
|
||||
-- data:number类型,表示message id
|
||||
elseif event == "sent" then
|
||||
-- 发送消息通知 mqtts ca sender task
|
||||
sys.sendMsg(mqtts_ca_sender.TASK_NAME, "MQTT_EVENT", "PUBLISH_OK", data)
|
||||
|
||||
-- 服务器断开mqtt连接
|
||||
elseif event == "disconnect" then
|
||||
-- 发送消息通知 mqtts ca main task
|
||||
sys.sendMsg(TASK_NAME, "MQTT_EVENT", "DISCONNECTED", false)
|
||||
|
||||
-- 收到服务器的心跳应答
|
||||
elseif event == "pong" then
|
||||
-- 接收到数据,通知网络环境检测看门狗功能模块进行喂狗
|
||||
sys.publish("FEED_NETWORK_WATCHDOG")
|
||||
|
||||
-- 严重异常,本地会主动断开连接
|
||||
-- data:string类型,表示具体的异常,有以下几种:
|
||||
-- "connect":tcp连接失败
|
||||
-- "tx":数据发送失败
|
||||
-- "conack":mqtt connect后,服务器应答CONNACK鉴权失败,失败码为payload(number类型)
|
||||
-- "other":其他异常
|
||||
elseif event == "error" then
|
||||
if data == "connect" or data == "conack" then
|
||||
-- 发送消息通知 mqtts ca main task,连接失败
|
||||
sys.sendMsg(TASK_NAME, "MQTT_EVENT", "CONNECT", false)
|
||||
elseif data == "other" or data == "tx" then
|
||||
-- 发送消息通知 mqtts ca main task,出现异常
|
||||
sys.sendMsg(TASK_NAME, "MQTT_EVENT", "ERROR")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- mqtts ca main task 的任务处理函数
|
||||
local function mqtts_ca_client_main_task_func()
|
||||
|
||||
local mqtt_client
|
||||
local result, msg
|
||||
|
||||
-- 用来验证server证书是否合法的ca证书文件为openluat_root_ca.crt,此证书永久有效
|
||||
-- 将这个ca证书文件的内容读取出来,赋值给server_ca_cert
|
||||
-- 注意:此处的ca证书文件仅用来验证airtest.openluat.com的server证书
|
||||
-- 在有效期之前,airtest.openluat.com可能会更换server证书
|
||||
-- 如果server证书更换后,此处验证使用的openluat_root_ca.crt也可能需要更换
|
||||
-- 如果你使用的是自己的server,要替换为自己server证书对应的ca证书文件
|
||||
local server_ca_cert = io.readFile("/luadb/openluat_root_ca.crt")
|
||||
|
||||
while true do
|
||||
-- 如果当前时间点设置的默认网卡还没有连接成功,一直在这里循环等待
|
||||
while not socket.adapter(socket.dft()) do
|
||||
log.warn("mqtts_ca_client_main_task_func", "wait IP_READY", socket.dft())
|
||||
-- 在此处阻塞等待默认网卡连接成功的消息"IP_READY"
|
||||
-- 或者等待1秒超时退出阻塞等待状态;
|
||||
-- 注意:此处的1000毫秒超时不要修改的更长;
|
||||
-- 因为当使用exnetif.set_priority_order配置多个网卡连接外网的优先级时,会隐式的修改默认使用的网卡
|
||||
-- 当exnetif.set_priority_order的调用时序和此处的socket.adapter(socket.dft())判断时序有可能不匹配
|
||||
-- 此处的1秒,能够保证,即使时序不匹配,也能1秒钟退出阻塞状态,再去判断socket.adapter(socket.dft())
|
||||
sys.waitUntil("IP_READY", 1000)
|
||||
end
|
||||
|
||||
-- 检测到了IP_READY消息
|
||||
log.info("mqtts_ca_client_main_task_func", "recv IP_READY", socket.dft())
|
||||
|
||||
-- 清空此task绑定的消息队列中的未处理的消息
|
||||
sys.cleanMsg(TASK_NAME)
|
||||
|
||||
-- 创建mqtt client对象,ssl连接,单向证书校验
|
||||
-- client仅单向校验server的证书,server不校验client的证书和密钥文件
|
||||
-- 如果做证书校验,需要特别注意以下几点:
|
||||
-- 1、证书校验前,设备端必须同步为正确的时间,因为校验过程中会检查ca证书以及server证书中的有效期是否合法;本demo中的sntp_app.lua会同步时间;
|
||||
-- 2、任何证书都有有效期,无论是ca证书还是server证书,必须在有效期截止之前,及时更换证书,延长有效期,否则证书校验会失败;
|
||||
-- 3、如果要更换ca证书,需要在设备端远程升级,必须保证ca证书失效之前升级成功,否则校验失败,就无法连接server;
|
||||
-- 综上所述,证书校验虽然安全,可以验证身份,但是后续维护成本比较高;除非有需要,否则可以不配置证书校验功能;
|
||||
mqtt_client = mqtt.create(nil, SERVER_ADDR, SERVER_PORT, {server_cert = server_ca_cert})
|
||||
-- 如果创建mqtt client对象失败
|
||||
if not mqtt_client then
|
||||
log.error("mqtts_ca_client_main_task_func", "mqtt.create error")
|
||||
goto EXCEPTION_PROC
|
||||
end
|
||||
|
||||
-- 配置mqtt client对象的client id,username,password和clean session标志
|
||||
result = mqtt_client:auth(TASK_NAME..mobile.imei(), USERNAME, PASSWORD, true)
|
||||
-- 如果配置失败
|
||||
if not result then
|
||||
log.error("mqtts_ca_client_main_task_func", "mqtt_client:auth error")
|
||||
goto EXCEPTION_PROC
|
||||
end
|
||||
|
||||
-- 注册mqtt client对象的事件回调函数
|
||||
mqtt_client:on(mqtts_ca_client_event_cbfunc)
|
||||
|
||||
-- 设置mqtt keepalive时间为120秒
|
||||
-- 如果没有设置,内核固件中默认为180秒
|
||||
-- 有需要的话,可以打开注释
|
||||
-- mqtt_client:keepalive(120)
|
||||
|
||||
-- 设置遗嘱消息,有需要的话,可以打开注释
|
||||
-- mqtt_client:will(TOPIC_PREFIX .. "/status", "offline")
|
||||
|
||||
-- 配置开启debug信息,有需要的话,可以打开注释
|
||||
-- mqtt_client:debug(true)
|
||||
-- socket.sslLog(3)
|
||||
|
||||
-- 连接server
|
||||
result = mqtt_client:connect()
|
||||
-- 如果连接server失败
|
||||
if not result then
|
||||
log.error("mqtts_ca_client_main_task_func", "mqtt_client:connect error")
|
||||
goto EXCEPTION_PROC
|
||||
end
|
||||
|
||||
-- 连接、断开连接、订阅、取消订阅、异常等各种事件的处理调度逻辑
|
||||
while true do
|
||||
-- 等待"MQTT_EVENT"消息
|
||||
msg = sys.waitMsg(TASK_NAME, "MQTT_EVENT")
|
||||
log.info("mqtts_ca_client_main_task_func waitMsg", msg[2], msg[3], msg[4])
|
||||
|
||||
-- connect连接结果
|
||||
-- msg[3]表示连接结果,true为连接成功,false为连接失败
|
||||
if msg[2] == "CONNECT" then
|
||||
-- mqtt连接成功
|
||||
if msg[3] then
|
||||
log.info("mqtts_ca_client_main_task_func", "connect success")
|
||||
-- 通知数据发送应用模块,MQTT连接成功
|
||||
sys.sendMsg(mqtts_ca_sender.TASK_NAME, "MQTT_EVENT", "CONNECT_OK", mqtt_client)
|
||||
-- mqtt连接失败
|
||||
else
|
||||
log.info("mqtts_ca_client_main_task_func", "connect error")
|
||||
-- 退出循环,发起重连
|
||||
break
|
||||
end
|
||||
|
||||
-- subscribe订阅结果
|
||||
-- msg[3]表示订阅结果,true为订阅成功,false为订阅失败
|
||||
elseif msg[2] == "SUBSCRIBE" then
|
||||
-- 订阅成功
|
||||
if msg[3] then
|
||||
log.info("mqtts_ca_client_main_task_func", "subscribe success", "qos: "..(msg[4] or "nil"))
|
||||
-- 订阅失败
|
||||
else
|
||||
log.error("mqtts_ca_client_main_task_func", "subscribe error", "code", msg[4])
|
||||
-- 主动断开mqtt client连接
|
||||
mqtt_client:disconnect()
|
||||
-- 发送disconnect之后,此处延时1秒,给数据发送预留一点儿时间,发送到服务器;
|
||||
-- 即使1秒的时间不足以发送给服务器也没关系;对服务器来说,mqtt客户端只是没有优雅的断开,不影响什么实质功能;
|
||||
sys.wait(1000)
|
||||
break
|
||||
end
|
||||
|
||||
-- unsubscribe取消订阅成功
|
||||
elseif msg[2] == "UNSUBSCRIBE" then
|
||||
log.info("mqtts_ca_client_main_task_func", "unsubscribe success")
|
||||
|
||||
-- 需要主动关闭mqtt连接
|
||||
-- 用户需要主动关闭mqtt连接时,可以调用sys.sendMsg(TASK_NAME, "MQTT_EVENT", "CLOSE")
|
||||
elseif msg[2] == "CLOSE" then
|
||||
-- 主动断开mqtt client连接
|
||||
mqtt_client:disconnect()
|
||||
-- 发送disconnect之后,此处延时1秒,给数据发送预留一点儿时间,发送到服务器;
|
||||
-- 即使1秒的时间不足以发送给服务器也没关系;对服务器来说,mqtt客户端只是没有优雅的断开,不影响什么实质功能;
|
||||
sys.wait(1000)
|
||||
break
|
||||
|
||||
-- 被动关闭了mqtt连接
|
||||
-- 被网络或者服务器断开了连接
|
||||
elseif msg[2] == "DISCONNECTED" then
|
||||
break
|
||||
|
||||
-- 出现了其他异常
|
||||
elseif msg[2] == "ERROR" then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- 出现异常
|
||||
::EXCEPTION_PROC::
|
||||
|
||||
-- 清空此task绑定的消息队列中的未处理的消息
|
||||
sys.cleanMsg(TASK_NAME)
|
||||
|
||||
-- 通知mqtts ca sender数据发送应用模块的task,MQTT连接已经断开
|
||||
sys.sendMsg(mqtts_ca_sender.TASK_NAME, "MQTT_EVENT", "DISCONNECTED")
|
||||
|
||||
-- 如果存在mqtt client对象
|
||||
if mqtt_client then
|
||||
-- 关闭mqtt client,并且释放mqtt client对象
|
||||
mqtt_client:close()
|
||||
mqtt_client = nil
|
||||
end
|
||||
|
||||
-- 5秒后跳转到循环体开始位置,自动发起重连
|
||||
sys.wait(5000)
|
||||
end
|
||||
end
|
||||
|
||||
--创建并且启动一个task
|
||||
--运行这个task的处理函数mqtts_ca_client_main_task_func
|
||||
sys.taskInitEx(mqtts_ca_client_main_task_func, TASK_NAME)
|
||||
@@ -0,0 +1,64 @@
|
||||
--[[
|
||||
@module mqtts_ca_receiver
|
||||
@summary mqtts ca cient数据接收处理应用功能模块
|
||||
@version 1.0
|
||||
@date 2025.07.29
|
||||
@author 马梦阳
|
||||
@usage
|
||||
本文件为mqtts ca client 数据接收应用功能模块,核心业务逻辑为:
|
||||
处理接收到的publish数据,同时将数据发送给其他应用功能模块做进一步处理;
|
||||
|
||||
本文件的对外接口有2个:
|
||||
1、mqtts_ca_receiver.proc(topic, payload, metas):publish数据处理入口,在mqtts_ca_main.lua中调用;
|
||||
2、sys.publish("RECV_DATA_FROM_SERVER", "recv from mqtt ssl ca server: ", topic, payload):
|
||||
将接收到的publish中的topic和payload数据通过消息"RECV_DATA_FROM_SERVER"发布出去;
|
||||
需要处理数据的应用功能模块订阅处理此消息即可,本demo项目中uart_app.lua中订阅处理了本消息;
|
||||
]]
|
||||
|
||||
local mqtts_ca_receiver = {}
|
||||
|
||||
|
||||
|
||||
--[[
|
||||
处理接收到的publish数据
|
||||
|
||||
@api mqtts_ca_receiver.proc(topic, payload, metas)
|
||||
|
||||
@param1 topic string
|
||||
表示publish主题
|
||||
|
||||
@param2 payload string
|
||||
表示publish数据负载
|
||||
|
||||
@param2 payload string
|
||||
表示publish数据负载
|
||||
|
||||
@param3 metas table
|
||||
表示publish报文的一些参数;格式如下:
|
||||
{
|
||||
qos: number类型,取值范围0,1,2
|
||||
retain:number类型,取值范围0,1
|
||||
dup:number类型,取值范围0,1
|
||||
message_id: number类型
|
||||
}
|
||||
|
||||
@return1 result nil
|
||||
|
||||
@usage
|
||||
|
||||
mqtts_ca_receiver.proc(topic, payload, metas)
|
||||
]]
|
||||
function mqtts_ca_receiver.proc(topic, payload, metas)
|
||||
|
||||
log.info("mqtts_ca_receiver.proc", topic, payload:len(), json.encode(metas))
|
||||
|
||||
-- 接收到数据,通知网络环境检测看门狗功能模块进行喂狗
|
||||
sys.publish("FEED_NETWORK_WATCHDOG")
|
||||
|
||||
-- 将topic和payload通过"RECV_DATA_FROM_SERVER"消息publish出去,给其他应用模块处理
|
||||
sys.publish("RECV_DATA_FROM_SERVER", "recv from mqtt ssl ca server: ", topic, payload)
|
||||
|
||||
-- 也可以直接在此处编写代码,处理topic和payload
|
||||
end
|
||||
|
||||
return mqtts_ca_receiver
|
||||
@@ -0,0 +1,154 @@
|
||||
--[[
|
||||
@module mqtts_ca_sender
|
||||
@summary mqtts ca client数据发送应用功能模块
|
||||
@version 1.0
|
||||
@date 2025.07.29
|
||||
@author 马梦阳
|
||||
@usage
|
||||
本文件为mqtts ca client 数据发送应用功能模块,核心业务逻辑为:
|
||||
1、sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)订阅"SEND_DATA_REQ"消息,将其他应用模块需要发送的数据存储到队列send_queue中;
|
||||
2、mqtts ca sender task接收"CONNECT OK"、"PUBLISH_REQ"、"PUBLISH OK"三种类型的"MQTT_EVENT"消息,遍历队列send_queue,逐条发送数据到server;
|
||||
3、mqtts ca sender task接收"DISCONNECTED"类型的"MQTT_EVENT"消息,丢弃掉队列send_queue中未发送的数据;
|
||||
4、任何一条数据无论发送成功还是失败,只要这条数据有回调函数,都会通过回调函数通知数据发送方;
|
||||
|
||||
本文件的对外接口有1个:
|
||||
1、sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func):订阅"SEND_DATA_REQ"消息;
|
||||
其他应用模块如果需要发送数据,直接sys.publish这个消息即可,将需要发送的topic,payload和qos以及回调函数和回调参数一起publish出去;
|
||||
本demo项目中uart_app.lua和timer_app.lua中publish了这个消息;
|
||||
]]
|
||||
|
||||
local mqtts_ca_sender = {}
|
||||
|
||||
--[[
|
||||
数据发送队列,数据结构为:
|
||||
{
|
||||
[1] = {topic="topic1", payload="payload1", qos=0, cb={func=callback_function1, para=callback_para1}},
|
||||
[2] = {topic="topic2", payload="payload2", qos=1, cb={func=callback_function2, para=callback_para2}},
|
||||
[3] = {topic="topic3", payload="payload3", qos=2, cb={func=callback_function3, para=callback_para3}},
|
||||
}
|
||||
topic的内容为publish的主题,string类型,必须存在;
|
||||
payload的内容为publish的负载数据,string类型,必须存在;
|
||||
qos的内容为publish的质量等级,number类型,取值范围0,1,2,可选,如果用户没有指定,默认为0;
|
||||
cb.func的内容为数据发送结果的用户回调函数,可以不存在;
|
||||
cb.para的内容为数据发送结果的用户回调函数的回调参数,可以不存在;
|
||||
]]
|
||||
local send_queue = {}
|
||||
|
||||
-- mqtts ca client的任务名前缀
|
||||
mqtts_ca_sender.TASK_NAME_PREFIX = "mqtts_ca_"
|
||||
|
||||
-- mqtts_ca_client_sender的任务名
|
||||
mqtts_ca_sender.TASK_NAME = mqtts_ca_sender.TASK_NAME_PREFIX.."sender"
|
||||
|
||||
-- "SEND_DATA_REQ"消息的处理函数
|
||||
local function send_data_req_proc_func(tag, topic, payload, qos, cb)
|
||||
-- 将原始数据增加前缀,然后插入到发送队列send_queue中
|
||||
table.insert(send_queue, {topic=topic, payload="send from "..tag..": "..payload, qos=qos or 0, cb=cb})
|
||||
-- 发送消息通知 mqtts ca sender task,有新数据等待发送
|
||||
sys.sendMsg(mqtts_ca_sender.TASK_NAME, "MQTT_EVENT", "PUBLISH_REQ")
|
||||
end
|
||||
|
||||
-- 按照顺序发送send_queue中的数据
|
||||
-- 如果调用publish接口成功,则返回当前正在发送的数据项
|
||||
-- 如果调用publish接口失败,通知回调函数发送失败后,继续发送下一条数据
|
||||
local function publish_item(mqtt_client)
|
||||
local item
|
||||
-- 如果发送队列中有数据等待发送
|
||||
while #send_queue>0 do
|
||||
-- 取出来第一条数据赋值给item
|
||||
-- 同时从队列send_queue中删除这一条数据
|
||||
item = table.remove(send_queue, 1)
|
||||
|
||||
-- publish数据
|
||||
-- result表示调用publish接口的同步结果,返回值有以下几种:
|
||||
-- 如果失败,返回nil
|
||||
-- 如果成功,number类型,qos为0时直接返回0;qos为1或者2时返回publish报文的message id
|
||||
result = mqtt_client:publish(item.topic, item.payload, item.qos)
|
||||
|
||||
-- publish接口调用成功
|
||||
if result then
|
||||
return item
|
||||
-- publish接口调用失败
|
||||
else
|
||||
-- 如果当前发送的数据有用户回调函数,则执行用户回调函数
|
||||
if item.cb and item.cb.func then
|
||||
item.cb.func(false, item.cb.para)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function publish_item_cbfunc(item, result)
|
||||
if item then
|
||||
-- 如果当前发送的数据有用户回调函数,则执行用户回调函数
|
||||
if item.cb and item.cb.func then
|
||||
item.cb.func(result, item.cb.para)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- mqtts_ca client sender的任务处理函数
|
||||
local function mqtts_ca_client_sender_task_func()
|
||||
|
||||
local mqtt_client
|
||||
local send_item
|
||||
local result, msg
|
||||
|
||||
while true do
|
||||
-- 等待"MQTT_EVENT"消息
|
||||
msg = sys.waitMsg(mqtts_ca_sender.TASK_NAME, "MQTT_EVENT")
|
||||
|
||||
-- mqtt连接成功
|
||||
-- msg[3]表示mqtt client对象
|
||||
if msg[2] == "CONNECT_OK" then
|
||||
mqtt_client = msg[3]
|
||||
-- 发送send_queue中的数据
|
||||
send_item = publish_item(mqtt_client)
|
||||
-- mqtt publish数据请求
|
||||
elseif msg[2] == "PUBLISH_REQ" then
|
||||
-- 如果mqtt client对象存在,并且没有正在等待发送结果的发送数据项
|
||||
if mqtt_client and not send_item then
|
||||
-- 发送send_queue中的数据
|
||||
send_item = publish_item(mqtt_client)
|
||||
end
|
||||
-- mqtt publish数据成功
|
||||
elseif msg[2] == "PUBLISH_OK" then
|
||||
-- publish成功,执行回调函数通知发送方
|
||||
publish_item_cbfunc(send_item, true)
|
||||
-- publish成功,通知网络环境检测看门狗功能模块进行喂狗
|
||||
sys.publish("FEED_NETWORK_WATCHDOG")
|
||||
-- 发送send_queue中的数据
|
||||
send_item = publish_item(mqtt_client)
|
||||
-- mqtt断开连接
|
||||
elseif msg[2] == "DISCONNECTED" then
|
||||
-- 清空mqtt client对象
|
||||
mqtt_client = nil
|
||||
-- 如果存在正在等待发送结果的发送项,执行回调函数通知发送方失败
|
||||
publish_item_cbfunc(send_item, false)
|
||||
-- 如果发送队列中有数据等待发送
|
||||
while #send_queue>0 do
|
||||
-- 取出来第一条数据赋值给send_item
|
||||
-- 同时从队列send_queue中删除这一条数据
|
||||
send_item = table.remove(send_queue,1)
|
||||
-- 执行回调函数通知发送方失败
|
||||
publish_item_cbfunc(send_item, false)
|
||||
end
|
||||
-- 当前没有正在等待发送结果的发送项
|
||||
send_item = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- 订阅"SEND_DATA_REQ"消息;
|
||||
-- 其他应用模块如果需要发送数据,直接sys.publish这个消息即可,将需要发送的数据以及回调函数和回调参数一起publish出去;
|
||||
-- 本demo项目中uart_app.lua和timer_app.lua中publish了这个消息;
|
||||
sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)
|
||||
|
||||
|
||||
--创建并且启动一个task
|
||||
--运行这个task的处理函数mqtts_ca_client_sender_task_func
|
||||
sys.taskInitEx(mqtts_ca_client_sender_task_func, mqtts_ca_sender.TASK_NAME)
|
||||
|
||||
return mqtts_ca_sender
|
||||
@@ -0,0 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,65 @@
|
||||
--[[
|
||||
@module sntp_app
|
||||
@summary sntp时间同步应用功能模块
|
||||
@version 1.0
|
||||
@date 2025.07.01
|
||||
@author 马梦阳
|
||||
@usage
|
||||
本文件为sntp时间同步应用功能模块,核心业务逻辑为:
|
||||
1、连接ntp服务器进行时间同步;
|
||||
2、如果同步成功,1小时之后重新发起同步动作;
|
||||
3、如果同步失败,10秒钟之后重新发起同步动作;
|
||||
|
||||
本文件没有对外接口,直接在其他应用功能模块中require "sntp_app"就可以加载运行;
|
||||
]]
|
||||
|
||||
-- sntp时间同步的任务处理函数
|
||||
local function sntp_task_func()
|
||||
|
||||
while true do
|
||||
-- 如果当前时间点设置的默认网卡还没有连接成功,一直在这里循环等待
|
||||
while not socket.adapter(socket.dft()) do
|
||||
log.warn("sntp_task_func", "wait IP_READY", socket.dft())
|
||||
-- 在此处阻塞等待默认网卡连接成功的消息"IP_READY"
|
||||
-- 或者等待1秒超时退出阻塞等待状态;
|
||||
-- 注意:此处的1000毫秒超时不要修改的更长;
|
||||
-- 因为当使用exnetif.set_priority_order配置多个网卡连接外网的优先级时,会隐式的修改默认使用的网卡
|
||||
-- 当exnetif.set_priority_order的调用时序和此处的socket.adapter(socket.dft())判断时序有可能不匹配
|
||||
-- 此处的1秒,能够保证,即使时序不匹配,也能1秒钟退出阻塞状态,再去判断socket.adapter(socket.dft())
|
||||
sys.waitUntil("IP_READY", 1000)
|
||||
end
|
||||
|
||||
-- 检测到了IP_READY消息
|
||||
log.warn("sntp_task_func", "recv IP_READY")
|
||||
|
||||
-- 发起ntp时间同步动作
|
||||
socket.sntp()
|
||||
|
||||
-- 等待ntp时间同步结果,30秒超时失败,通常只需要几百毫秒就能成功
|
||||
local ret = sys.waitUntil("NTP_UPDATE", 30000)
|
||||
|
||||
--同步成功
|
||||
if ret then
|
||||
-- 以下是获取/打印时间的演示,注意时区问题
|
||||
log.info("sntp_task_func", "时间同步成功", "本地时间", os.date())
|
||||
log.info("sntp_task_func", "时间同步成功", "UTC时间", os.date("!%c"))
|
||||
log.info("sntp_task_func", "时间同步成功", "RTC时钟(UTC时间)", json.encode(rtc.get()))
|
||||
log.info("sntp_task_func", "时间同步成功", "本地时间戳", os.time())
|
||||
local t = os.date("*t")
|
||||
log.info("sntp_task_func", "时间同步成功", "本地时间os.date() json格式", json.encode(t))
|
||||
log.info("sntp_task_func", "时间同步成功", "本地时间os.date(os.time())", os.time(t))
|
||||
|
||||
-- 正常使用, 一小时一次, 已经足够了, 甚至1天一次也可以
|
||||
sys.wait(3600000)
|
||||
--同步失败
|
||||
else
|
||||
log.info("sntp_task_func", "时间同步失败")
|
||||
-- 10秒后重新发起同步动作
|
||||
sys.wait(10000)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--创建并且启动一个task
|
||||
--运行这个task的主函数sntp_task_func
|
||||
sys.taskInit(sntp_task_func)
|
||||
Reference in New Issue
Block a user