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,110 @@
|
||||
## 功能模块介绍
|
||||
|
||||
1、main.lua:主程序入口;
|
||||
|
||||
2、netdrv_device.lua:网卡驱动设备,可以配置使用netdrv文件夹内的四种网卡(单4g网卡,单wifi网卡,单spi以太网卡,多网卡)中的任何一种网卡;
|
||||
|
||||
3、errdump_read.lua:手动读取errdump异常日志功能模块;
|
||||
|
||||
4、errdump_tcp文件夹:将手动读取到的日志发到tcp服务器中。
|
||||
|
||||
5、uart_app.lua:uart应用层,用于将手动读取的异常日志通过串口发出去;
|
||||
|
||||
6、auto_dump_air_srv.lua:自动上报异常日志到合宙服务器中;
|
||||
|
||||
7、auto_dump_udp_srv.lua:自动上报异常日志到自建udp服务器中;注:1、必须udp服务器;2、收到模组上报的异常日志后要回复一个大写的OK。
|
||||
|
||||
## 系统消息介绍
|
||||
|
||||
1、"IP_READY":某种网卡已经获取到ip信息,仅仅获取到了ip信息,能否和外网连通还不确认;
|
||||
|
||||
2、"IP_LOSE":某种网卡已经掉网;
|
||||
|
||||
## 用户消息介绍
|
||||
|
||||
1、"ERRDUMP_DATA_SEND_UART":手动读取到的异常日志通过此消息发送到uart应用层;
|
||||
|
||||
2、"SEND_DATA_REQ":手动读取到异常日志后发布此消息,通知socket client发送数据给服务器;
|
||||
|
||||
## 演示功能概述
|
||||
|
||||
1、主要是使用Air8000开发板演示四种errdump异常日志上报功能,使用的时候根据自己需求在main.lua文件中选择要使用的功能,注意不能同时使用自动上报和手动读取功能。
|
||||
|
||||
(1)自动上报异常日志到iot平台
|
||||
|
||||
(2)自动上报异常日志到自建udp服务器
|
||||
|
||||
(3)手动读取异常日志并通过串口传输
|
||||
|
||||
(4)手动读取异常日志并通过tcp传输
|
||||
|
||||
2、netdrv_device:配置连接外网使用的网卡,目前支持以下三种选择(三选一)
|
||||
|
||||
(1) netdrv_4g:4G网卡
|
||||
|
||||
(2) netdrv_eth_spi:通过SPI外挂CH390H芯片的以太网卡
|
||||
|
||||
(3) netdrv_multiple:支持以上三种网卡,可以配置三种网卡的优先级
|
||||
|
||||
## 演示硬件环境
|
||||
|
||||

|
||||
|
||||
1、Air780EPM 1.3版本开发板一块+可上网的sim卡一张+4g天线一根+网线一根:
|
||||
|
||||
- sim卡插入开发板的sim卡槽
|
||||
|
||||
- 天线装到开发板上
|
||||
|
||||
- 网线一端插入开发板网口,另外一端连接可以上外网的路由器网口
|
||||
|
||||
2、TYPE-C USB数据线一根 + USB转串口数据线一根,Air780EPM开发板和数据线的硬件接线方式为:
|
||||
|
||||
- Air780EPM开发板通过TYPE-C USB口供电;(外部供电/USB供电 拨动开关 拨到 USB供电一端)
|
||||
|
||||
- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
|
||||
|
||||
- USB转串口数据线,一般来说,白线连接开发板的UART1_TX,绿线连接开发板的UART1_RX,黑线连接核心板的GND,另外一端连接电脑USB口;
|
||||
|
||||
|
||||
## 演示软件环境
|
||||
|
||||
1、Luatools下载调试工具
|
||||
|
||||
2、[780EPM V2008版本固件)](https://docs.openluat.com/air780epm/luatos/firmware/version/)(理论上,2025年7月26日之后发布的固件都可以)
|
||||
|
||||
3、PC端的串口工具,例如SSCOM、LLCOM等都可以;
|
||||
|
||||
4、PC端浏览器访问[合宙TCP/UDP web测试工具](https://iot.luatos.com/#/page6/netlab);
|
||||
详细使用说明参考:[合宙 TCP/UDP web 测试工具使用说明](https://iot.luatos.com/#/p8000/netlab) 。
|
||||
## 演示开发步骤
|
||||
|
||||
1、搭建好硬件环境
|
||||
|
||||
2、在main.lua文件中选择好要使用的功能,通过Luatools将demo与固件烧录到开发板中
|
||||
|
||||
3、烧录好后,板子开机同时在luatools上查看日志:
|
||||
|
||||
可以看到设备打印的死机日志以及errdump ok!字样
|
||||
|
||||
```lua
|
||||
[2025-07-15 16:17:59.681][000000034.674] D/errDump errdump ok!
|
||||
[2025-07-15 16:18:14.479][000000049.480] dns_run 674:dev_msg1.openluat.com state 0 id 4 ipv6 0 use dns server2, try 0
|
||||
[2025-07-15 16:18:14.634][000000049.625] dns_run 691:dns all done ,now stop
|
||||
[2025-07-15 16:18:14.728][000000049.721] D/errDump errdump ok!
|
||||
[2025-07-15 16:18:25.443][000000060.434] E/user.coroutine.resume auto_dump_air_srv.lua:42: attempt to index a nil value (global 'lllllllllog')
|
||||
stack traceback:
|
||||
auto_dump_air_srv.lua:42: in function <errdump_test.lua:30>
|
||||
[2025-07-15 16:18:25.944][000000060.934] E/main Luat:
|
||||
[2025-07-15 16:18:25.961][000000060.935] E/main auto_dump_air_srv.lua:42: attempt to index a nil value (global 'lllllllllog')
|
||||
stack traceback:
|
||||
auto_dump_air_srv.lua:42: in function <errdump_test.lua:30>
|
||||
|
||||
```
|
||||
|
||||
烧录不同的功能代码,可以在对应的平台看到如下信息:
|
||||
|
||||
```lua
|
||||
errdump_demo_LuatOS-SoC_V2009_Air8000,001.000.000,866597072472820,866597072472820, 测试一下用户的记录功能
|
||||
errdump_demo_LuatOS-SoC_V2009_Air8000,001.000.000,866597072472820,866597072472820, poweron reason:3 auto_dump_air_srv.lua:42: attempt to index a nil value (global 'lllllllllog') stack traceback: auto_dump_air_srv.lua:42: in function <errdump_test.lua:30>
|
||||
```
|
||||
@@ -0,0 +1,46 @@
|
||||
--[[
|
||||
@module auto_dump_air_srv
|
||||
@summary 自动上报异常日志到iot平台功能模块
|
||||
@version 1.0
|
||||
@date 2025.09.3
|
||||
@author 孟伟
|
||||
@usage
|
||||
本功能模块演示的内容为:自动上报异常日志到iot平台功能
|
||||
自动上报异常日志到合宙iot平台,如果是系统异常日志,则会在重启后自动上报,如果是用户写入调试日志,则周期性上报。
|
||||
]]
|
||||
|
||||
-- 使用合宙iot平台时需要这个参数
|
||||
PRODUCT_KEY = "123" -- 到 iot.openluat.com 创建项目,获取正确的项目id
|
||||
|
||||
local function test_user_log()
|
||||
-- 如果当前时间点设置的默认网卡还没有连接成功,一直在这里循环等待
|
||||
while not socket.adapter(socket.dft()) do
|
||||
log.warn("auto_dump_air_srv_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
|
||||
-- 下面演示自动发送异常日志到合宙iot平台,如果是系统异常日志,则会在重启后自动上报,如果是用户写入调试日志,则周期性上报。
|
||||
errDump.config(true, 600)
|
||||
while true do
|
||||
sys.wait(15000)
|
||||
-- 上报用户调试日志
|
||||
errDump.record("测试一下用户的调试日志记录功能")
|
||||
end
|
||||
end
|
||||
|
||||
--故意写错用来触发系统异常日志记录
|
||||
local function test_error_log()
|
||||
sys.wait(60000)
|
||||
--故意写错代码死机
|
||||
lllllllllog.info("此处使用一个不存在的库文件,导致出现异常")
|
||||
end
|
||||
|
||||
-- 启动errdemp测试任务
|
||||
sys.taskInit(test_user_log)
|
||||
--启动错误函数任务
|
||||
sys.taskInit(test_error_log)
|
||||
@@ -0,0 +1,46 @@
|
||||
--[[
|
||||
@module auto_dump_udp_srv
|
||||
@summary 自动上报异常日志到自建udp服务器功能模块
|
||||
@version 1.0
|
||||
@date 2025.09.3
|
||||
@author 孟伟
|
||||
@usage
|
||||
本功能模块演示的内容为:
|
||||
自动上报异常日志到自建udp服务器功能
|
||||
自动上报异常日志到自建udp服务器,如果是系统异常日志,则会在重启后自动上报,如果是用户写入调试日志,则周期性上报。
|
||||
使用此功能时需要注意的是,自己的udp服务器收到上报的dump日志时,需要回复一个大写的"OK"来通知模组服务器收到消息了,不然会重复发送,并且也不会删除已发送的日志
|
||||
]]
|
||||
|
||||
|
||||
local function test_user_log()
|
||||
-- 如果当前时间点设置的默认网卡还没有连接成功,一直在这里循环等待
|
||||
while not socket.adapter(socket.dft()) do
|
||||
log.warn("auto_dump_udp_srv_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
|
||||
-- 下面演示自动发送异常日志到自建udp服务器,如果是系统异常日志,则会在重启后自动上报,如果是用户写入调试日志,则周期性上报。
|
||||
errDump.config(true,600,nil,nil,"112.125.89.8",47539)
|
||||
while true do
|
||||
sys.wait(15000)
|
||||
-- 上报用户调试日志
|
||||
errDump.record("测试一下用户的调试日志记录功能")
|
||||
end
|
||||
end
|
||||
|
||||
--故意写错用来触发系统异常日志记录
|
||||
local function test_error_log()
|
||||
sys.wait(60000)
|
||||
--故意写错代码死机
|
||||
lllllllllog.info("此处使用一个不存在的库文件,导致出现异常")
|
||||
end
|
||||
|
||||
-- 启动errdemp测试任务
|
||||
sys.taskInit(test_user_log)
|
||||
--启动错误函数任务
|
||||
sys.taskInit(test_error_log)
|
||||
@@ -0,0 +1,67 @@
|
||||
--[[
|
||||
@module errdump_read
|
||||
@summary errdump手动读取功能模块
|
||||
@version 1.0
|
||||
@date 2025.09.05
|
||||
@author 孟伟
|
||||
@usage
|
||||
本功能模块演示的内容为:
|
||||
手动读取异常日志,通过消息"ERRDUMP_DATA_SEND_UART"发布出去通知串口接收进行处理,
|
||||
通过消息"SEND_DATA_REQ"发布出去通知tcp接收进行处理。
|
||||
如果是系统异常日志,则会在重启后手动读取上报,用户写入的调试日志则需要手动读取上报。
|
||||
注意:用户写入的调试日志只能手动读取上报,不能自动上报。
|
||||
]]
|
||||
|
||||
--加载uart模块
|
||||
require "uart_app"
|
||||
--加载tcp主应用模块
|
||||
require "tcp_client_main"
|
||||
|
||||
local function test_user_log()
|
||||
-- 下面演示手动获取异常日志信息,手动读取到异常日志可以上报到自己服务器
|
||||
errDump.config(true, 0) --配置为手动读取,如果配置为自动上报将无法手动读取系统异常日志
|
||||
local err_buff = zbuff.create(4096)
|
||||
local new_flag = errDump.dump(err_buff, errDump.TYPE_SYS) -- 开机手动读取一次系统异常日志
|
||||
if err_buff:used() > 0 then
|
||||
-- log.info(err_buff:toStr(0, err_buff:used())) -- 打印出异常日志
|
||||
-- 将数据data通过"ERRDUMP_DATA_SEND_UART"消息publish给串口发送出去
|
||||
sys.publish("ERRDUMP_DATA_SEND_UART", err_buff:toStr(0, err_buff:used()))
|
||||
-- 将读取到的系统异常日志通过"SEND_DATA_REQ"消息publish给tcp发送出去
|
||||
sys.publish("SEND_DATA_REQ", err_buff:toStr(0, err_buff:used()))
|
||||
end
|
||||
--手动读取的话需要手动删除日志,否则下次读取会继续读取上次的日志
|
||||
-- errDump.dumpf返回值:true表示本次读取前并没有写入数据,false反之,在删除日志前,最好再读一下确保没有新的数据写入了
|
||||
new_flag = errDump.dump(err_buff, errDump.TYPE_SYS)
|
||||
if not new_flag then
|
||||
log.info("没有新数据了,删除系统错误日志")
|
||||
errDump.dump(nil, errDump.TYPE_SYS, true)
|
||||
end
|
||||
-- 开机读取完系统异常日志后循环读取用户调试日志
|
||||
while true do
|
||||
local new_flag = errDump.dump(err_buff, errDump.TYPE_USR)
|
||||
if new_flag then
|
||||
log.info("errBuff", err_buff:toStr(0, err_buff:used()))
|
||||
-- 将数据data通过"ERRDUMP_DATA_SEND_UART"消息publish给串口发送出去
|
||||
sys.publish("ERRDUMP_DATA_SEND_UART", err_buff:toStr(0, err_buff:used()))
|
||||
-- 将读取到的用户调试日志通过"SEND_DATA_REQ"消息publish给tcp发送出去
|
||||
sys.publish("SEND_DATA_REQ", err_buff:toStr(0, err_buff:used()))
|
||||
end
|
||||
new_flag = errDump.dump(err_buff, errDump.TYPE_USR)
|
||||
if not new_flag then
|
||||
log.info("没有新数据了,删除用户调试日志")
|
||||
errDump.dump(nil, errDump.TYPE_USR, true)
|
||||
end
|
||||
sys.wait(15000)
|
||||
errDump.record("测试一下用户的调试日志记录功能") --写入用户的调试日志,注意最大只有4KB,超过部分新的覆盖旧的
|
||||
end
|
||||
end
|
||||
|
||||
local function test_error_log() --故意写错用来触发系统异常日志记录
|
||||
sys.wait(60000)
|
||||
--故意写错代码死机
|
||||
lllllllllog.info("此处使用一个不存在的库文件,导致出现异常")
|
||||
end
|
||||
|
||||
|
||||
sys.taskInit(test_user_log) -- 启动errdemp测试任务
|
||||
sys.taskInit(test_error_log) --启动错误函数任务
|
||||
@@ -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
|
||||
@@ -0,0 +1,70 @@
|
||||
--[[
|
||||
@module main
|
||||
@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
|
||||
@version 1.0
|
||||
@date 2025.07.13
|
||||
@author 孟伟
|
||||
@usage
|
||||
本demo演示的功能为:
|
||||
1、主要是演示四种errdump异常日志上报功能,使用的时候根据自己需求在下面选择要使用的功能,注意不能同时使用自动上报和手动读取功能
|
||||
(1)自动上报异常日志到iot平台
|
||||
(2)自动上报异常日志到自建udp服务器
|
||||
(3)手动读取异常日志并通过串口传输
|
||||
(4)手动读取异常日志并通过tcp传输
|
||||
2、netdrv_device:配置连接外网使用的网卡,目前支持以下三种选择(三选一)
|
||||
(1) netdrv_4g:4G网卡
|
||||
(2) netdrv_eth_spi:通过SPI外挂CH390H芯片的以太网卡
|
||||
(3) netdrv_multiple:支持以上三种网卡,可以配置三种网卡的优先级
|
||||
|
||||
更多说明参考本目录下的readme.md文件
|
||||
]]
|
||||
|
||||
|
||||
|
||||
--[[
|
||||
必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
|
||||
PROJECT:项目名,ascii string类型
|
||||
可以随便定义,只要不使用,就行
|
||||
VERSION:项目版本号,ascii string类型
|
||||
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
|
||||
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
|
||||
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为999
|
||||
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
|
||||
]]
|
||||
PROJECT = "errdump_demo"
|
||||
VERSION = "001.999.000"
|
||||
|
||||
-- 在日志中打印项目名和项目版本号
|
||||
log.info("main", PROJECT, VERSION)
|
||||
|
||||
|
||||
|
||||
-- 启动一个循环定时器
|
||||
-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
|
||||
-- 方便分析内存使用是否有异常
|
||||
-- sys.timerLoopStart(function()
|
||||
-- log.info("mem.lua", rtos.meminfo())
|
||||
-- log.info("mem.sys", rtos.meminfo("sys"))
|
||||
-- end, 3000)
|
||||
|
||||
|
||||
-- 加载网络驱动设备功能模块
|
||||
require "netdrv_device"
|
||||
|
||||
--下面三种情况只能打开一种,根据自己需求进行选择,不能同时打开,手动读取的errdump_read.lua中可以选择是通过串口传输还是通过tcp协议传输
|
||||
-- 加载errdump测试模块
|
||||
--自动上报异常日志到IOT平台
|
||||
require "auto_dump_air_srv"
|
||||
|
||||
--自动上报异常日志到自建UDP平台
|
||||
-- require "auto_dump_udp_srv"
|
||||
|
||||
--手动读取异常日志并通过串口和TCP传输
|
||||
--加载手动读取异常日志模块
|
||||
-- require "errdump_read"
|
||||
|
||||
|
||||
-- 用户代码已结束---------------------------------------------
|
||||
-- 结尾总是这一句
|
||||
sys.run()
|
||||
-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行
|
||||
@@ -0,0 +1,43 @@
|
||||
--[[
|
||||
@module netdrv_4g
|
||||
@summary “4G网卡”驱动模块
|
||||
@version 1.0
|
||||
@date 2025.07.01
|
||||
@author 马梦阳
|
||||
@usage
|
||||
本文件为4G网卡驱动模块,核心业务逻辑为:
|
||||
1、监听"IP_READY"和"IP_LOSE",在日志中进行打印;
|
||||
|
||||
本文件没有对外接口,直接在其他功能模块中require "netdrv_4g"就可以加载运行;
|
||||
]]
|
||||
|
||||
local function ip_ready_func(ip, adapter)
|
||||
if adapter == socket.LWIP_GP then
|
||||
-- 在位置1和2设置自定义的DNS服务器ip地址:
|
||||
-- "223.5.5.5",这个DNS服务器IP地址是阿里云提供的DNS服务器IP地址;
|
||||
-- "114.114.114.114",这个DNS服务器IP地址是国内通用的DNS服务器IP地址;
|
||||
-- 可以加上以下两行代码,在自动获取的DNS服务器工作不稳定的情况下,这两个新增的DNS服务器会使DNS服务更加稳定可靠;
|
||||
-- 如果使用专网卡,不要使用这两行代码;
|
||||
-- 如果使用国外的网络,不要使用这两行代码;
|
||||
socket.setDNS(adapter, 1, "223.5.5.5")
|
||||
socket.setDNS(adapter, 2, "114.114.114.114")
|
||||
|
||||
log.info("netdrv_4g.ip_ready_func", "IP_READY", socket.localIP(socket.LWIP_GP))
|
||||
end
|
||||
end
|
||||
|
||||
local function ip_lose_func(adapter)
|
||||
if adapter == socket.LWIP_GP then
|
||||
log.warn("netdrv_4g.ip_lose_func", "IP_LOSE")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- 此处订阅"IP_READY"和"IP_LOSE"两种消息
|
||||
-- 在消息的处理函数中,仅仅打印了一些信息,便于实时观察4G网络的连接状态
|
||||
-- 也可以根据自己的项目需求,在消息处理函数中增加自己的业务逻辑控制,例如可以在连网状态发生改变时更新网络图标
|
||||
sys.subscribe("IP_READY", ip_ready_func)
|
||||
sys.subscribe("IP_LOSE", ip_lose_func)
|
||||
|
||||
-- 在Air780EPM上,内核固件运行起来之后,默认网卡就是socket.LWIP_GP
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
--[[
|
||||
@module netdrv_eth_spi
|
||||
@summary “通过SPI外挂CH390H芯片的以太网卡”驱动模块
|
||||
@version 1.0
|
||||
@date 2025.07.24
|
||||
@author 马梦阳
|
||||
@usage
|
||||
本文件为“通过SPI外挂CH390H芯片的以太网卡”驱动模块,核心业务逻辑为:
|
||||
1、打开CH390H芯片供电开关;
|
||||
2、初始化spi0,初始化以太网卡,并且在以太网卡上开启DHCP(动态主机配置协议);
|
||||
3、以太网卡的连接状态发生变化时,在日志中进行打印;
|
||||
|
||||
直接使用Air780EPM V1.3版本开发板硬件测试即可;
|
||||
|
||||
本文件没有对外接口,直接在其他功能模块中require "netdrv_eth_spi"就可以加载运行;
|
||||
]]
|
||||
|
||||
local exnetif = require "exnetif"
|
||||
|
||||
local function ip_ready_func(ip, adapter)
|
||||
if adapter == socket.LWIP_ETH then
|
||||
-- 在位置1和2设置自定义的DNS服务器ip地址:
|
||||
-- "223.5.5.5",这个DNS服务器IP地址是阿里云提供的DNS服务器IP地址;
|
||||
-- "114.114.114.114",这个DNS服务器IP地址是国内通用的DNS服务器IP地址;
|
||||
-- 可以加上以下两行代码,在自动获取的DNS服务器工作不稳定的情况下,这两个新增的DNS服务器会使DNS服务更加稳定可靠;
|
||||
-- 如果使用专网卡,不要使用这两行代码;
|
||||
-- 如果使用国外的网络,不要使用这两行代码;
|
||||
socket.setDNS(adapter, 1, "223.5.5.5")
|
||||
socket.setDNS(adapter, 2, "114.114.114.114")
|
||||
|
||||
log.info("netdrv_eth_spi.ip_ready_func", "IP_READY", socket.localIP(socket.LWIP_ETH))
|
||||
end
|
||||
end
|
||||
|
||||
local function ip_lose_func(adapter)
|
||||
if adapter == socket.LWIP_ETH then
|
||||
log.warn("netdrv_eth_spi.ip_lose_func", "IP_LOSE")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- 以太网联网成功(成功连接路由器,并且获取到了IP地址)后,内核固件会产生一个"IP_READY"消息
|
||||
-- 各个功能模块可以订阅"IP_READY"消息实时处理以太网联网成功的事件
|
||||
-- 也可以在任何时刻调用socket.adapter(socket.LWIP_ETH)来获取以太网是否连接成功
|
||||
|
||||
-- 以太网断网后,内核固件会产生一个"IP_LOSE"消息
|
||||
-- 各个功能模块可以订阅"IP_LOSE"消息实时处理以太网断网的事件
|
||||
-- 也可以在任何时刻调用socket.adapter(socket.LWIP_ETH)来获取以太网是否连接成功
|
||||
|
||||
--此处订阅"IP_READY"和"IP_LOSE"两种消息
|
||||
--在消息的处理函数中,仅仅打印了一些信息,便于实时观察“通过SPI外挂CH390H芯片的以太网卡”的连接状态
|
||||
--也可以根据自己的项目需求,在消息处理函数中增加自己的业务逻辑控制,例如可以在连网状态发生改变时更新网络图标
|
||||
sys.subscribe("IP_READY", ip_ready_func)
|
||||
sys.subscribe("IP_LOSE", ip_lose_func)
|
||||
|
||||
|
||||
local function netdrv_eth_spi_task_func()
|
||||
-- 配置SPI外接以太网芯片CH390H的单网卡,exnetif.set_priority_order使用的网卡编号为socket.LWIP_ETH
|
||||
-- 本demo使用Air780EPM V1.3版本开发板测试,开发板上的硬件配置为:
|
||||
-- GPIO20为CH390H以太网芯片的供电使能控制引脚
|
||||
-- 使用spi0,片选引脚使用GPIO8
|
||||
-- 如果使用的硬件和以上描述的环境不同,根据自己的硬件配置修改以下参数
|
||||
exnetif.set_priority_order({
|
||||
{
|
||||
ETHERNET = {
|
||||
pwrpin = 20,
|
||||
tp = netdrv.CH390,
|
||||
opts = {spi = 0, cs = 8}
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
-- 启动一个task,task的处理函数为netdrv_eth_spi_task_func
|
||||
-- 在处理函数中调用exnetif.set_priority_order设置网卡优先级
|
||||
-- 因为exnetif.set_priority_order要求必须在task中被调用,所以此处启动一个task
|
||||
sys.taskInit(netdrv_eth_spi_task_func)
|
||||
@@ -0,0 +1,88 @@
|
||||
--[[
|
||||
@module netdrv_multiple
|
||||
@summary 多网卡(4G网卡、通过SPI外挂CH390H芯片的以太网卡)驱动模块
|
||||
@version 1.0
|
||||
@date 2025.07.24
|
||||
@author 马梦阳
|
||||
@usage
|
||||
本文件为多网卡驱动模块,核心业务逻辑为:
|
||||
1、调用exnetif.set_priority_order配置多网卡的控制参数以及优先级;
|
||||
|
||||
直接使用Air780EPM V1.3版本开发板硬件测试即可;
|
||||
|
||||
本文件没有对外接口,直接在其他功能模块中require "netdrv_multiple"就可以加载运行;
|
||||
]]
|
||||
|
||||
local exnetif = require "exnetif"
|
||||
|
||||
-- 网卡状态变化通知回调函数
|
||||
-- 当exnetif中检测到网卡切换或者所有网卡都断网时,会触发调用此回调函数
|
||||
-- 当网卡切换切换时:
|
||||
-- net_type:string类型,表示当前使用的网卡字符串
|
||||
-- adapter:number类型,表示当前使用的网卡id
|
||||
-- 当所有网卡断网时:
|
||||
-- net_type:为nil
|
||||
-- adapter:number类型,为-1
|
||||
local function netdrv_multiple_notify_cbfunc(net_type,adapter)
|
||||
-- 在位置1和2设置自定义的DNS服务器ip地址:
|
||||
-- "223.5.5.5",这个DNS服务器IP地址是阿里云提供的DNS服务器IP地址;
|
||||
-- "114.114.114.114",这个DNS服务器IP地址是国内通用的DNS服务器IP地址;
|
||||
-- 可以加上以下两行代码,在自动获取的DNS服务器工作不稳定的情况下,这两个新增的DNS服务器会使DNS服务更加稳定可靠;
|
||||
-- 如果使用专网卡,不要使用这两行代码;
|
||||
-- 如果使用国外的网络,不要使用这两行代码;
|
||||
socket.setDNS(adapter, 1, "223.5.5.5")
|
||||
socket.setDNS(adapter, 2, "114.114.114.114")
|
||||
|
||||
if type(net_type)=="string" then
|
||||
log.info("netdrv_multiple_notify_cbfunc", "use new adapter", net_type, adapter)
|
||||
elseif type(net_type)=="nil" then
|
||||
log.warn("netdrv_multiple_notify_cbfunc", "no available adapter", net_type, adapter)
|
||||
else
|
||||
log.warn("netdrv_multiple_notify_cbfunc", "unknown status", net_type, adapter)
|
||||
end
|
||||
end
|
||||
|
||||
local function netdrv_multiple_task_func()
|
||||
--设置网卡优先级
|
||||
exnetif.set_priority_order(
|
||||
{
|
||||
-- “通过SPI外挂CH390H芯片”的以太网卡,使用Air780EPM V1.3版本开发板验证
|
||||
{
|
||||
ETHERNET = {
|
||||
-- 供电使能GPIO
|
||||
pwrpin = 20,
|
||||
-- 设置的多个“已经IP READY,但是还没有ping通”网卡,循环执行ping动作的间隔(单位毫秒,可选)
|
||||
-- 如果没有传入此参数,exnetif会使用默认值10秒
|
||||
ping_time = 3000,
|
||||
|
||||
-- 连通性检测ip(选填参数);
|
||||
-- 如果没有传入ip地址,exnetif中会默认使用httpdns能否成功获取baidu.com的ip作为是否连通的判断条件;
|
||||
-- 如果传入,一定要传入可靠的并且可以ping通的ip地址;
|
||||
-- ping_ip = "填入可靠的并且可以ping通的ip地址",
|
||||
|
||||
-- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
|
||||
tp = netdrv.CH390,
|
||||
opts = {spi=0, cs=8}
|
||||
}
|
||||
},
|
||||
|
||||
-- 4G网卡
|
||||
{
|
||||
LWIP_GP = true
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
-- 设置网卡状态变化通知回调函数netdrv_multiple_notify_cbfunc
|
||||
exnetif.notify_status(netdrv_multiple_notify_cbfunc)
|
||||
|
||||
-- 如果存在udp网络应用,并且udp网络应用中,根据应用层的心跳能够判断出来udp数据通信出现了异常;
|
||||
-- 可以在判断出现异常的位置,调用一次exnetif.check_network_status()接口,强制对当前正式使用的网卡进行一次连通性检测;
|
||||
-- 如果存在tcp网络应用,不需要用户调用exnetif.check_network_status()接口去控制,exnetif会在tcp网络应用通信异常时自动对当前使用的网卡进行连通性检测。
|
||||
|
||||
|
||||
-- 启动一个task,task的处理函数为netdrv_multiple_task_func
|
||||
-- 在处理函数中调用exnetif.set_priority_order设置网卡优先级
|
||||
-- 因为exnetif.set_priority_order要求必须在task中被调用,所以此处启动一个task
|
||||
sys.taskInit(netdrv_multiple_task_func)
|
||||
@@ -0,0 +1,45 @@
|
||||
--[[
|
||||
@module netdrv_pc
|
||||
@summary “pc模拟器网卡”驱动模块
|
||||
@version 1.0
|
||||
@date 2025.07.01
|
||||
@author 朱天华
|
||||
@usage
|
||||
本文件为pc模拟器网卡驱动模块,核心业务逻辑为:
|
||||
1、监听"IP_READY"和"IP_LOSE",在日志中进行打印;
|
||||
|
||||
本文件没有对外接口,直接在其他功能模块中require "netdrv_pc"就可以加载运行;
|
||||
]]
|
||||
|
||||
local function ip_ready_func(ip, adapter)
|
||||
if adapter == socket.ETH0 then
|
||||
-- 在位置1和2设置自定义的DNS服务器ip地址:
|
||||
-- "223.5.5.5",这个DNS服务器IP地址是阿里云提供的DNS服务器IP地址;
|
||||
-- "114.114.114.114",这个DNS服务器IP地址是国内通用的DNS服务器IP地址;
|
||||
-- 可以加上以下两行代码,在自动获取的DNS服务器工作不稳定的情况下,这两个新增的DNS服务器会使DNS服务更加稳定可靠;
|
||||
-- 如果使用专网卡,不要使用这两行代码;
|
||||
-- 如果使用国外的网络,不要使用这两行代码;
|
||||
socket.setDNS(adapter, 1, "223.5.5.5")
|
||||
socket.setDNS(adapter, 2, "114.114.114.114")
|
||||
|
||||
log.info("netdrv_pc.ip_ready_func", "IP_READY", socket.localIP(socket.ETH0))
|
||||
end
|
||||
end
|
||||
|
||||
local function ip_lose_func(adapter)
|
||||
if adapter == socket.ETH0 then
|
||||
log.warn("netdrv_pc.ip_lose_func", "IP_LOSE")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--此处订阅"IP_READY"和"IP_LOSE"两种消息
|
||||
--在消息的处理函数中,仅仅打印了一些信息,便于实时观察pc模拟器网络的连接状态
|
||||
--也可以根据自己的项目需求,在消息处理函数中增加自己的业务逻辑控制,例如可以在连网状态发生改变时更新网络图标
|
||||
sys.subscribe("IP_READY", ip_ready_func)
|
||||
sys.subscribe("IP_LOSE", ip_lose_func)
|
||||
|
||||
-- 设置默认网卡为socket.ETH0
|
||||
-- pc模拟器上的默认网卡仍然需要使用接口(socket.ETH0)来设置,因为exnetif扩展库当前还不支持模拟器
|
||||
socket.dft(socket.ETH0)
|
||||
@@ -0,0 +1,29 @@
|
||||
--[[
|
||||
@module netdrv_device
|
||||
@summary 网络驱动设备功能模块
|
||||
@version 1.0
|
||||
@date 2025.09.05
|
||||
@author 孟伟
|
||||
@usage
|
||||
本文件为网络驱动设备功能模块,核心业务逻辑为:根据项目需求,选择并且配置合适的网卡(网络适配器)
|
||||
1、netdrv_4g:socket.LWIP_GP,4G网卡;
|
||||
2、netdrv_eth_spi:socket.LWIP_ETH,通过SPI外挂CH390H芯片的以太网卡;
|
||||
3、netdrv_multiple:可以配置多种网卡的优先级,按照优先级配置,使用其中一种网卡连接外网;
|
||||
|
||||
根据自己的项目需求,只需要require以上三种中的一种即可;
|
||||
|
||||
|
||||
本文件没有对外接口,直接在main.lua中require "netdrv_device"就可以加载运行;
|
||||
]]
|
||||
|
||||
|
||||
-- 根据自己的项目需求,只需要require以下三种中的一种即可;
|
||||
|
||||
-- 加载“4G网卡”驱动模块
|
||||
require "netdrv_4g"
|
||||
|
||||
-- 加载“通过SPI外挂CH390H芯片的以太网卡”驱动模块
|
||||
-- require "netdrv_eth_spi"
|
||||
|
||||
-- 加载“可以配置优先级的多种网卡”驱动模块
|
||||
-- require "netdrv_multiple"
|
||||
@@ -0,0 +1,77 @@
|
||||
--[[
|
||||
@module uart_app
|
||||
@summary 串口应用功能模块
|
||||
@version 1.0
|
||||
@date 2025.09.07
|
||||
@author 孟伟
|
||||
@usage
|
||||
本文件为串口应用功能模块,核心业务逻辑为:
|
||||
1、打开uart1,波特率115200,数据位8,停止位1,无奇偶校验位;
|
||||
2、uart1和pc端的串口工具相连;
|
||||
3、接收"ERRDUMP_DATA_SEND_UART"消息,将手动读取到异常日志通过uart1发送到pc端串口工具;
|
||||
|
||||
本文件的对外接口有两个:
|
||||
1、sys.subscribe("ERRDUMP_DATA_SEND_UART", dump_data_send),订阅"ERRDUMP_DATA_SEND_UART"消息,处理消息携带的数据;
|
||||
]]
|
||||
|
||||
|
||||
-- 使用UART1
|
||||
local UART_ID = 1
|
||||
-- 串口接收数据缓冲区
|
||||
local read_buf = ""
|
||||
|
||||
|
||||
-- 末尾增加回车换行两个字符,通过uart发送出去,方便在PC端换行显示查看
|
||||
local function dump_data_send(data)
|
||||
uart.write(UART_ID, data.."\r\n")
|
||||
end
|
||||
|
||||
|
||||
local function concat_timeout_func()
|
||||
-- 如果存在尚未处理的串口缓冲区数据;
|
||||
-- 将数据通过publish通知其他应用功能模块处理;
|
||||
-- 然后清空本文件的串口缓冲区数据
|
||||
if read_buf:len() > 0 then
|
||||
log.info("uart_read", read_buf:len())
|
||||
read_buf = ""
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- UART1的数据接收中断处理函数,UART1接收到数据时,会执行此函数
|
||||
local function read()
|
||||
local s
|
||||
while true do
|
||||
-- 非阻塞读取UART1接收到的数据,最长读取1024字节
|
||||
s = uart.read(UART_ID, 1024)
|
||||
|
||||
-- 如果从串口没有读到数据
|
||||
if not s or s:len() == 0 then
|
||||
-- 启动50毫秒的定时器,如果50毫秒内没收到新的数据,则处理当前收到的所有数据
|
||||
-- 这样处理是为了防止将一大包数据拆分成多个小包来处理
|
||||
-- 例如pc端串口工具下发1100字节的数据,可能会产生将近20次的中断进入到read函数,才能读取完整
|
||||
-- 此处的50毫秒可以根据自己项目的需求做适当修改,在满足整包拼接完整的前提下,时间越短,处理越及时
|
||||
sys.timerStart(concat_timeout_func, 50)
|
||||
-- 跳出循环,退出本函数
|
||||
break
|
||||
end
|
||||
|
||||
log.info("uart_app.read len", s:len())
|
||||
-- log.info("uart_app.read", s)
|
||||
|
||||
-- 将本次从串口读到的数据拼接到串口缓冲区read_buf中
|
||||
read_buf = read_buf..s
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- 初始化UART1,波特率115200,数据位8,停止位1
|
||||
uart.setup(UART_ID, 115200, 8, 1)
|
||||
|
||||
-- 注册UART1的数据接收中断处理函数,UART1接收到数据时,会执行read函数
|
||||
uart.on(UART_ID, "receive", read)
|
||||
|
||||
-- 订阅"ERRDUMP_DATA_SEND_UART"消息的处理函数dump_data_send
|
||||
-- 收到"ERRDUMP_DATA_SEND_UART"消息后,会执行函数dump_data_send
|
||||
sys.subscribe("ERRDUMP_DATA_SEND_UART", dump_data_send)
|
||||
Reference in New Issue
Block a user