init: Air780EPM 官方 LuatOS 项目代码基线

- 来源: 合宙 LuatOS 官方仓库 air780epm 模块完整代码
- 路径: luatos/air780epm/module/Air780EPM/demo 含官方 demo(含 mqtt/mqtts/socket/uart 等)
- 后续: 基于 demo 开发 UART<->MQTT 数据上报功能
This commit is contained in:
wangfq
2026-08-31 08:53:49 +08:00
commit 173dd6874f
813 changed files with 125484 additions and 0 deletions
@@ -0,0 +1,143 @@
--[[
@module tcp_client_main
@summary tcp client socket主应用功能模块
@version 1.0
@date 2025.09.03
@author 孟伟
@usage
本文件为tcp client socket主应用功能模块,核心业务逻辑为:
1、创建一个tcp client socket,连接server
2、处理连接异常,出现异常后执行重连动作;
3、调用tcp_client_receiver和tcp_client_sender中的外部接口,进行数据收发处理;
本文件没有对外接口,直接在main.lua中require "tcp_client_main"就可以加载运行;
]]
local libnet = require "libnet"
-- 加载tcp client socket数据接收功能模块
local tcp_client_receiver = require "tcp_client_receiver"
-- 加载tcp client socket数据发送功能模块
local tcp_client_sender = require "tcp_client_sender"
-- 电脑访问:https://iot.luatos.com/#/page6/netlab
-- 本工具使用方法可以参考:https://docs.openluat.com/common/TCPUDP_Test/
-- 点击 打开TCP 按钮,会创建一个TCP server
-- 将server的地址和端口赋值给下面这两个变量
local SERVER_ADDR = "112.125.89.8"
local SERVER_PORT = 47123
-- tcp_client_main的任务名
local TASK_NAME = tcp_client_sender.TASK_NAME
-- 处理未识别的消息
local function tcp_client_main_cbfunc(msg)
log.info("tcp_client_main_cbfunc", msg[1], msg[2], msg[3], msg[4])
end
-- tcp client socket的任务处理函数
local function tcp_client_main_task_func()
local socket_client
local result, para1, para2
while true do
-- 如果当前时间点设置的默认网卡还没有连接成功,一直在这里循环等待
while not socket.adapter(socket.dft()) do
log.warn("tcp_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("tcp_client_main_task_func", "recv IP_READY", socket.dft())
-- 创建socket client对象
socket_client = socket.create(nil, TASK_NAME)
-- 如果创建socket client对象失败
if not socket_client then
log.error("tcp_client_main_task_func", "socket.create error")
goto EXCEPTION_PROC
end
-- 配置socket client对象为tcp client
result = socket.config(socket_client, nil, nil, nil, 300, 10, 3)
-- 如果配置失败
if not result then
log.error("tcp_client_main_task_func", "socket.config error")
goto EXCEPTION_PROC
end
-- 连接server
result = libnet.connect(TASK_NAME, 15000, socket_client, SERVER_ADDR, SERVER_PORT)
-- 如果连接server失败
if not result then
log.error("tcp_client_main_task_func", "libnet.connect error")
goto EXCEPTION_PROC
end
log.info("tcp_client_main_task_func", "libnet.connect success")
-- 数据收发以及网络连接异常事件总处理逻辑
while true do
-- 数据接收处理(接收处理必须写在libnet.wait之前,因为老版本的内核固件要求必须这样,新版本的内核固件没这个要求,为了不出问题,写在libnet.wait之前就行了)
-- 如果处理失败,则退出循环
if not tcp_client_receiver.proc(socket_client) then
log.error("tcp_client_main_task_func", "tcp_client_receiver.proc error")
break
end
-- 数据发送处理
-- 如果处理失败,则退出循环
if not tcp_client_sender.proc(TASK_NAME, socket_client) then
log.error("tcp_client_main_task_func", "tcp_client_sender.proc error")
break
end
-- 阻塞等待socket.EVENT事件或者15秒钟超时
-- 以下三种业务逻辑会发布事件:
-- 1、socket client和server之间的连接出现异常(例如server主动断开,网络环境出现异常等),此时在内核固件中会发布事件socket.EVENT
-- 2、socket client接收到server发送过来的数据,此时在内核固件中会发布事件socket.EVENT
-- 3、socket client需要发送数据到server, 在tcp_client_sender.lua中会发布事件socket.EVENT
result, para1, para2 = libnet.wait(TASK_NAME, 15000, socket_client)
log.info("tcp_client_main_task_func", "libnet.wait", result, para1, para2)
-- 如果连接异常,则退出循环
if not result then
log.warn("tcp_client_main_task_func", "connection exception")
break
end
end
-- 出现异常
::EXCEPTION_PROC::
-- 数据发送应用模块对来不及发送的数据做清空和通知失败处理
tcp_client_sender.exception_proc()
-- 如果存在socket client对象
if socket_client then
-- 关闭socket client连接
libnet.close(TASK_NAME, 5000, socket_client)
-- 释放socket client对象
socket.release(socket_client)
socket_client = nil
end
-- 5秒后跳转到循环体开始位置,自动发起重连
sys.wait(5000)
end
end
--创建并且启动一个task
--运行这个task的主函数tcp_client_main_task_func
sys.taskInitEx(tcp_client_main_task_func, TASK_NAME, tcp_client_main_cbfunc)
@@ -0,0 +1,88 @@
--[[
@module tcp_client_receiver
@summary tcp client socket数据接收应用功能模块
@version 1.0
@date 2025.07.01
@author 朱天华
@usage
本文件为tcp client socket数据接收应用功能模块,核心业务逻辑为:
从内核读取接收到的数据,然后将数据发送给其他应用功能模块做进一步处理;
本文件的对外接口有2个:
1、tcp_client_receiver.proc(socket_client):数据接收应用逻辑处理入口,在tcp_client_main.lua中调用;
2、sys.publish("RECV_DATA_FROM_SERVER", "recv from tcp server: ", data)
将接收到的数据通过消息"RECV_DATA_FROM_SERVER"发布出去;
需要处理数据的应用功能模块订阅处理此消息即可,本demo项目中uart_app.lua中订阅处理了本消息;
]]
local tcp_client_receiver = {}
-- socket数据接收缓冲区
local recv_buff = nil
--[[
检查socket client是否收到数据,如果收到数据,读取并且处理完所有数据
@api tcp_client_receiver.proc(socket_client)
@param1 socket_client userdata
表示由socket.create接口创建的socket client对象;
必须传入,不允许为空或者nil
@return1 result bool
表示处理结果,成功为true,失败为false
@usage
--
tcp_client_receiver.proc(socket_client)
]]
function tcp_client_receiver.proc(socket_client)
-- 如果socket数据接收缓冲区还没有申请过空间,则先申请内存空间
if recv_buff==nil then
recv_buff = zbuff.create(1024)
-- 当recv_buff不再使用时,不需要主动调用recv_buff:free()去释放
-- 因为Lua的垃圾处理器会自动释放recv_buff所申请的内存空间
-- 如果等不及垃圾处理器自动处理,在确定以后不会再使用recv_buff时,则可以主动调用recv_buff:free()释放内存空间
end
-- 循环从内核的缓冲区读取接收到的数据
-- 如果读取失败,返回false,退出
-- 如果读取成功,处理数据,并且继续循环读取
-- 如果读取成功,并且读出来的数据为空,表示已经没有数据可读,返回true,退出
while true do
-- 从内核的缓冲区中读取数据到recv_buff中
-- 如果recv_buff的存储空间不足,会自动扩容
local result = socket.rx(socket_client, recv_buff)
-- 读取数据失败
-- 有两种情况:
-- 1、recv_buff扩容失败
-- 2、socket client和server之间的连接断开
if not result then
log.error("tcp_client_receiver.proc", "socket.rx error")
return false
end
-- 如果读取到了数据, used()就必然大于0, 进行处理
if recv_buff:used() > 0 then
log.info("tcp_client_receiver.proc", "recv data len", recv_buff:used())
-- 读取socket数据接收缓冲区中的数据,赋值给data
local data = recv_buff:query()
-- 将数据data通过"RECV_DATA_FROM_SERVER"消息publish出去,给其他应用模块处理
sys.publish("RECV_DATA_FROM_SERVER", "recv from tcp server: ", data)
-- 清空socket数据接收缓冲区中的数据
recv_buff:del()
-- 读取成功,但是读出来的数据为空,表示已经没有数据可读,可以退出循环了
else
break
end
end
return true
end
return tcp_client_receiver
@@ -0,0 +1,136 @@
--[[
@module tcp_client_sender
@summary tcp client socket数据发送应用功能模块
@version 1.0
@date 2025.07.01
@author 朱天华
@usage
本文件为tcp client socket数据发送应用功能模块,核心业务逻辑为:
1、sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)订阅"SEND_DATA_REQ"消息,将其他应用模块需要发送的数据存储到队列send_queue中;
2、tcp_client_main主任务调用tcp_client_sender.proc接口,遍历队列send_queue,逐条发送数据到server
3、tcp client socket和server之间的连接如果出现异常,tcp_client_main主任务调用tcp_client_sender.exception_proc接口,丢弃掉队列send_queue中未发送的数据;
4、任何一条数据无论发送成功还是失败,只要这条数据有回调函数,都会通过回调函数通知数据发送方;
本文件的对外接口有3个:
1、sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func):订阅"SEND_DATA_REQ"消息;
其他应用模块如果需要发送数据,直接sys.publish这个消息即可,将需要发送的数据以及回调函数和回调参数一起publish出去;
本demo项目中uart_app.lua和timer_app.lua中publish了这个消息;
2、tcp_client_sender.proc:数据发送应用逻辑处理入口,在tcp_client_main.lua中调用;
3、tcp_client_sender.exception_proc:数据发送应用逻辑异常处理入口,在tcp_client_main.lua中调用;
]]
local tcp_client_sender = {}
local libnet = require "libnet"
--[[
数据发送队列,数据结构为:
{
[1] = {data="data1", cb={func=callback_function1, para=callback_para1}},
[2] = {data="data2", cb={func=callback_function2, para=callback_para2}},
}
data的内容为真正要发送的数据,必须存在;
func的内容为数据发送结果的用户回调函数,可以不存在
para的内容为数据发送结果的用户回调函数的回调参数,可以不存在;
]]
local send_queue = {}
-- tcp_client_main的任务名
tcp_client_sender.TASK_NAME = "tcp_client_main"
-- "SEND_DATA_REQ"消息的处理函数
local function send_data_req_proc_func(data, cb)
-- 将原始数据增加前缀,然后插入到发送队列send_queue中
table.insert(send_queue, {data=data, cb=cb})
-- 通知tcp_client_main主任务有数据需要发送
-- tcp_client_main主任务如果处在libnet.wait调用的阻塞等待状态,就会退出阻塞状态
sys.sendMsg(tcp_client_sender.TASK_NAME, socket.EVENT, 0)
end
--[[
检查socket client是否需要发送数据,如果需要发送数据,读取并且发送完发送队列中的所有数据
@api tcp_client_sender.proc(task_name, socket_client)
@param1 task_name string
表示socket.create接口创建socket client对象时所处的task的name
必须传入,不允许为空或者nil
@param2 socket_client userdata
表示由socket.create接口创建的socket client对象;
必须传入,不允许为空或者nil
@return1 result bool
表示处理结果,成功为true,失败为false
@usage
tcp_client_sender.proc("tcp_client_main", socket_client)
]]
function tcp_client_sender.proc(task_name, socket_client)
local send_item
local result, buff_full
-- 遍历数据发送队列send_queue
while #send_queue>0 do
-- 取出来第一条数据赋值给send_item
-- 同时从队列send_queue中删除这一条数据
send_item = table.remove(send_queue,1)
-- 发送这条数据,超时时间15秒钟
result, buff_full = libnet.tx(task_name, 15000, socket_client, send_item.data)
-- 发送失败
if not result then
log.error("tcp_client_sender.proc", "libnet.tx error")
-- 如果当前发送的数据有用户回调函数,则执行用户回调函数
if send_item.cb and send_item.cb.func then
send_item.cb.func(false, send_item.cb.para)
end
return false
end
-- 如果内核固件中缓冲区满了,则将send_item再次插入到send_queue的队首位置,等待下次尝试发送
if buff_full then
log.error("tcp_client_sender.proc", "buffer is full, wait for the next time")
table.insert(send_queue, 1, send_item)
return true
end
log.info("tcp_client_sender.proc", "send success")
-- 发送成功,如果当前发送的数据有用户回调函数,则执行用户回调函数
if send_item.cb and send_item.cb.func then
send_item.cb.func(true, send_item.cb.para)
end
end
return true
end
--[[
socket client连接出现异常时,清空等待发送的数据,并且执行发送方的回调函数
@api tcp_client_sender.exception_proc()
@usage
tcp_client_sender.exception_proc()
]]
function tcp_client_sender.exception_proc()
-- 遍历数据发送队列send_queue
while #send_queue>0 do
local send_item = table.remove(send_queue,1)
-- 发送失败,如果当前发送的数据有用户回调函数,则执行用户回调函数
if send_item.cb and send_item.cb.func then
send_item.cb.func(false, send_item.cb.para)
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)
return tcp_client_sender