Browse Source

Merge branch 'master' of https://gitee.com/openLuat/LuatOS

alienwalker 7 months ago
parent
commit
d2fcb3d926
57 changed files with 2657 additions and 1269 deletions
  1. 160 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/gnss/da221.lua
  2. 65 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/gnss/da221gnss.lua
  3. 4 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/gnss/gnss.lua
  4. 2 1
      module/Air780EHM_Air780EHV_Air780EGH/demo/gnss/main.lua
  5. BIN
      module/Air780EHM_Air780EHV_Air780EGH/demo/modbus/test_core/LuatOS-SoC_V2007_Air780EPM_TEMP_20250610_150400.soc
  6. 176 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/README.md
  7. 109 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/main.lua
  8. 33 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/netdrv/netdrv_4g.lua
  9. 94 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/netdrv/netdrv_eth_spi.lua
  10. 98 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/netdrv/netdrv_multiple.lua
  11. 30 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/netdrv_device.lua
  12. 67 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/network_watchdog.lua
  13. 140 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp/tcp_client_main.lua
  14. 90 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp/tcp_client_receiver.lua
  15. 138 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp/tcp_client_sender.lua
  16. 141 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp_ssl/tcp_ssl_main.lua
  17. 96 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp_ssl/tcp_ssl_receiver.lua
  18. 138 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp_ssl/tcp_ssl_sender.lua
  19. 26 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp_ssl_ca/baidu_parent_ca.crt
  20. 66 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp_ssl_ca/sntp_app.lua
  21. 164 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp_ssl_ca/tcp_ssl_ca_main.lua
  22. 96 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp_ssl_ca/tcp_ssl_ca_receiver.lua
  23. 138 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp_ssl_ca/tcp_ssl_ca_sender.lua
  24. 41 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/timer_app.lua
  25. 79 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/uart_app.lua
  26. 142 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/udp/udp_client_main.lua
  27. 90 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/udp/udp_client_receiver.lua
  28. 135 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/udp/udp_client_sender.lua
  29. 1 1
      module/Air780EPM/demo/socket/client/long_connection/main.lua
  30. 6 1
      module/Air780EPM/demo/socket/client/long_connection/netdrv/netdrv_4g.lua
  31. 1 1
      module/Air780EPM/demo/socket/client/long_connection/netdrv/netdrv_eth_spi.lua
  32. 1 1
      module/Air780EPM/demo/socket/client/long_connection/netdrv/netdrv_multiple.lua
  33. 1 1
      module/Air780EPM/demo/socket/client/long_connection/netdrv_device.lua
  34. 1 1
      module/Air780EPM/demo/socket/client/long_connection/network_watchdog.lua
  35. 1 1
      module/Air780EPM/demo/socket/client/long_connection/tcp_ssl/tcp_ssl_main.lua
  36. 1 1
      module/Air780EPM/demo/socket/client/long_connection/tcp_ssl/tcp_ssl_receiver.lua
  37. 1 1
      module/Air780EPM/demo/socket/client/long_connection/tcp_ssl/tcp_ssl_sender.lua
  38. 1 1
      module/Air780EPM/demo/socket/client/long_connection/tcp_ssl_ca/sntp_app.lua
  39. 1 1
      module/Air780EPM/demo/socket/client/long_connection/tcp_ssl_ca/tcp_ssl_ca_main.lua
  40. 1 1
      module/Air780EPM/demo/socket/client/long_connection/tcp_ssl_ca/tcp_ssl_ca_receiver.lua
  41. 1 1
      module/Air780EPM/demo/socket/client/long_connection/tcp_ssl_ca/tcp_ssl_ca_sender.lua
  42. 1 1
      module/Air780EPM/demo/socket/client/long_connection/timer_app.lua
  43. 1 1
      module/Air780EPM/demo/socket/client/long_connection/uart_app.lua
  44. 1 1
      module/Air780EPM/demo/socket/client/long_connection/udp/udp_client_receiver.lua
  45. 1 1
      module/Air780EPM/demo/socket/client/long_connection/udp/udp_client_sender.lua
  46. 13 2
      module/Air8000/demo/libnetif/http_test.lua
  47. 0 540
      module/Air8000/demo/libnetif/libnetif.lua
  48. 2 2
      module/Air8000/demo/libnetif/mqtt_test.lua
  49. 11 1
      module/Air8000/demo/libnetif/net_app.lua
  50. 33 0
      module/Air8000/demo/libnetif/readme.md
  51. BIN
      module/Air8000/project/School_To_Home/core/LuatOS-SoC_V2005_Air8000.soc
  52. 2 2
      module/Air8101/demo/libnetif/http_test.lua
  53. 0 699
      module/Air8101/demo/libnetif/libnetif.lua
  54. 2 2
      module/Air8101/demo/libnetif/mqtt_test.lua
  55. 12 1
      module/Air8101/demo/libnetif/netif_app.lua
  56. BIN
      module/PC/win32/LuatOS-SoC_V2002_PC.zip
  57. 2 2
      script/libs/libnetif.lua

+ 160 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/gnss/da221.lua

@@ -0,0 +1,160 @@
+gnss=require("gnss")
+tcp=require("tcp")
+local da221={}
+
+local i2cId = 0
+local intPin = gpio.WAKEUP2
+local interruptMode = true      -- 中断模式
+-- 是否打印日志
+local logSwitch = true
+local moduleName = "da221"
+
+local da221Addr = 0x27
+local soft_reset = {0x00, 0x24}         -- 软件复位地址
+local chipid_addr = 0x01                -- 芯片ID地址
+local rangeaddr = {0x0f, 0x00}          -- 设置加速度量程,默认2g
+-- local rangeaddr = {0x0f, 0x01}          -- 设置加速度量程,默认4g
+-- local rangeaddr = {0x0f, 0x10}          -- 设置加速度量程,默认8g
+local int_set1_reg = {0x16, 0x87}       --设置x,y,z发生变化时,产生中断
+local int_set2_reg = {0x17, 0x10}       --使能新数据中断,数据变化时,产生中断,本程序不设置
+local int_map1_reg = {0x19, 0x04}       --运动的时候,产生中断
+local int_map2_reg = {0x1a, 0x01}
+
+local active_dur_addr = {0x27, 0x01}    -- 设置激活时间,默认0x01
+local active_ths_addr = {0x28, 0x33}    -- 设置激活阈值,灵敏度最高
+-- local active_ths_addr = {0x28, 0x80}    -- 设置激活阈值,灵敏度适中
+-- local active_ths_addr = {0x28, 0xFE}    -- 设置激活阈值,灵敏度最低
+local odr_addr = {0x10, 0x08}           -- 设置采样率 100Hz
+local mode_addr = {0x11, 0x00}          -- 设置正常模式
+local int_latch_addr = {0x21, 0x02}     -- 设置中断锁存
+
+local x_lsb_reg = 0x02 -- X轴LSB寄存器地址
+local x_msb_reg = 0x03 -- X轴MSB寄存器地址
+local y_lsb_reg = 0x04 -- Y轴LSB寄存器地址
+local y_msb_reg = 0x05 -- Y轴MSB寄存器地址
+local z_lsb_reg = 0x06 -- Z轴LSB寄存器地址
+local z_msb_reg = 0x07 -- Z轴MSB寄存器地址
+
+local active_state = 0x0b -- 激活状态寄存器地址
+local active_state_data
+
+
+local function logF(...)
+    if logSwitch then
+        log.info(moduleName, ...)
+    end
+end
+
+function da221.read_xyz()
+    -- da221是LSB在前,MSB在后,每个寄存器都是1字节数据,每次读取都是6个寄存器数据一起获取
+    -- 因此直接从X轴LSB寄存器(0x02)开始连续读取6字节数据(X/Y/Z各2字节),避免出现数据撕裂问题
+    i2c.send(i2cId, da221Addr, x_lsb_reg, 1)
+    local recv_data = i2c.recv(i2cId, da221Addr, 6)
+
+    -- LSB数据格式为: D[3] D[2] D[1] D[0] unused unused unused unused
+    -- MSB数据格式为: D[11] D[10] D[9] D[8] D[7] D[6] D[5] D[4]
+    -- 数据位为12位,需要将MSB数据左移4位,LSB数据右移4位,最后进行或运算
+    -- 解析X轴数据 (LSB在前,MSB在后)
+    local x_data = (string.byte(recv_data, 2) << 4) | (string.byte(recv_data, 1) >> 4)
+
+    -- 解析Y轴数据 (LSB在前,MSB在后)
+    local y_data = (string.byte(recv_data, 4) << 4) | (string.byte(recv_data, 3) >> 4)
+
+    -- 解析Z轴数据 (LSB在前,MSB在后)
+    local z_data = (string.byte(recv_data, 6) << 4) | (string.byte(recv_data, 5) >> 4)
+
+    -- 转换为12位有符号整数
+    -- 判断X轴数据是否大于2047,若大于则表示数据为负数
+    -- 因为12位有符号整数的范围是 -2048 到 2047,原始数据为无符号形式,大于2047的部分需要转换为负数
+    -- 通过减去4096 (2^12) 将无符号数转换为对应的有符号负数
+    if x_data > 2047 then x_data = x_data - 4096 end
+    -- 判断Y轴数据是否大于2047,若大于则进行同样的有符号转换
+    if y_data > 2047 then y_data = y_data - 4096 end
+    -- 判断Z轴数据是否大于2047,若大于则进行同样的有符号转换
+    if z_data > 2047 then z_data = z_data - 4096 end
+
+    -- 转换为加速度值(单位:g)
+    local x_accel = x_data / 1024
+    local y_accel = y_data / 1024
+    local z_accel = z_data / 1024
+
+    -- 输出加速度值(单位:g)
+    return x_accel, y_accel, z_accel
+end
+
+
+local function da221_init()
+    gpio.setup(24, 1, gpio.PULLUP)  -- gsensor 开关
+    gpio.setup(164, 1, gpio.PULLUP) -- air8000 和解码芯片公用
+    gpio.setup(147, 1, gpio.PULLUP) -- camera的供电使能脚
+    log.info("da221 init...")
+    --关闭i2c
+    i2c.close(i2cId)
+    --重新打开i2c,i2c速度设置为低速
+    i2c.setup(i2cId, i2c.SLOW)
+
+    sys.wait(50)
+    i2c.send(i2cId, da221Addr, soft_reset, 1)
+    sys.wait(50)
+    i2c.send(i2cId, da221Addr, chipid_addr, 1)
+    local chipid = i2c.recv(i2cId, da221Addr, 1)
+    log.info("i2c", "chipid",chipid:toHex())
+    if string.byte(chipid) == 0x13 then
+        log.info("da221 init success")
+        sys.publish("DA221_INIT_SUCCESS")
+    else
+        log.info("da221 init fail")
+    end
+
+    -- 设置寄存器
+    i2c.send(i2cId, da221Addr, rangeaddr, 1)    --设置加速度量程,默认2g
+    sys.wait(5)
+    i2c.send(i2cId, da221Addr, int_set1_reg, 1) --设置x,y,z发生变化时,产生中断
+    sys.wait(5)
+    i2c.send(i2cId, da221Addr, int_map1_reg, 1)--运动的时候,产生中断
+    sys.wait(5)
+    i2c.send(i2cId, da221Addr, active_dur_addr, 1)-- 设置激活时间,默认0x00
+    sys.wait(5)
+    i2c.send(i2cId, da221Addr, active_ths_addr, 1)-- 设置激活阈值
+    sys.wait(5)
+    i2c.send(i2cId, da221Addr, mode_addr, 1)-- 设置模式
+    sys.wait(5)
+    i2c.send(i2cId, da221Addr, odr_addr, 1)-- 设置采样率
+    sys.wait(5)
+    i2c.send(i2cId, da221Addr, int_latch_addr, 1)-- 设置中断锁存 中断一旦触发将保持,直到手动清除
+    sys.wait(5)
+end
+
+--[[
+    打开da221
+@api da221.open(mode)
+@number da221模式设置,1、静态/微动检测,使用场景:微振动检测、手势识别;2、常规运动监测,使用场景:运动监测、车载设备;3、高动态冲击检测,使用场景:碰撞检测、工业冲击
+@return nil
+@usage
+    da221.open(1)
+]]
+function da221.open(mode)
+    if mode==1 or tonumber(mode)==1 then
+        --轻微检测
+        log.info("轻微检测")
+        rangeaddr = {0x0f, 0x00}          -- 设置加速度量程,默认2g
+        active_ths_addr = {0x28, 0x33}    -- 设置激活阈值,灵敏度最高
+        odr_addr = {0x10, 0x04}           -- 设置采样率 100Hz
+        active_dur_addr = {0x27, 0x01}    -- 设置激活时间,默认0x01
+    elseif mode==2 or tonumber(mode)==2 then
+        --常规检测
+        rangeaddr = {0x0f, 0x01}          -- 设置加速度量程,默认4g
+        active_ths_addr = {0x28, 0x26}    -- 设置激活阈值,灵敏度适中
+        odr_addr = {0x10, 0x08}           -- 设置采样率 100Hz
+        active_dur_addr = {0x27, 0x14}    -- 设置激活时间,默认0x01
+    elseif mode==3 or tonumber(mode)==3 then
+        --高动态检测
+        rangeaddr = {0x0f, 0x10}          -- 设置加速度量程,默认8g
+        active_ths_addr = {0x28, 0x80}    -- 设置激活阈值,灵敏度最低
+        odr_addr = {0x10, 0x0F}           -- 设置采样率 100Hz
+        active_dur_addr = {0x27, 0x04}    -- 设置激活时间,默认0x01
+    end
+    sys.taskInit(da221_init)
+end
+
+return da221

+ 65 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/gnss/da221gnss.lua

@@ -0,0 +1,65 @@
+--[[
+@module  da221gnss
+@summary 利用加速度传感器da221实现中断触发gnss定位
+@version 1.0
+@date    2025.08.01
+@author  李源龙
+@usage
+使用Air8000利用内置的da221加速度传感器实现中断触发gnss定位
+]]
+gnss=require("gnss")
+tcp=require("tcp")
+da221=require("da221")
+
+local intPin=gpio.WAKEUP2
+local tid
+local function mode1_cb(tag)
+    log.info("TAGmode1_cb+++++++++",tag)
+    local  rmc=gnss.getRmc(0)
+    log.info("nmea", "rmc", json.encode(gnss.getRmc(0)))
+    tcp.latlngfnc(rmc.lat,rmc.lng)
+end
+local function timer1()
+    log.info("timer1")
+    gnss.close(gnss.DEFAULT,{tag="MODE1"})
+    sys.timerStop(tid)
+end
+
+local function ind()
+    log.info("int", gpio.get(intPin))
+    if gpio.get(intPin) == 1 then
+        sys.timerStart(timer1,10000)
+        local x,y,z =  da221.read_xyz()      --读取x,y,z轴的数据
+        log.info("x", x..'g', "y", y..'g', "z", z..'g')
+        if gnss.openres()~=true then
+            log.info("nmea", "openres", "false")
+            gnss.open(gnss.DEFAULT,{tag="MODE1",cb=mode1_cb}) 
+            tid=sys.timerLoopStart(mode1_cb, 5000)
+        else
+            log.info("nmea", "openres", "true")
+        end
+    end
+end
+
+local function gnss_fnc()
+    log.info("gnss_fnc111")
+    local gnssotps={
+        gnssmode=1, --1为卫星全定位,2为单北斗
+        agps_enable=true,    --是否使用AGPS,开启AGPS后定位速度更快,会访问服务器下载星历,星历时效性为北斗1小时,GPS4小时,默认下载星历的时间为1小时,即一小时内只会下载一次
+        debug=true,    --是否输出调试信息
+        -- uart=2,    --使用的串口,780EGH和8000默认串口2
+        -- uartbaud=115200,    --串口波特率,780EGH和8000默认115200
+        -- bind=1, --绑定uart端口进行GNSS数据读取,是否设置串口转发,指定串口号
+        -- rtc=false    --定位成功后自动设置RTC true开启,flase关闭
+    }
+    gnss.setup(gnssotps)
+    --1、静态/微动检测,使用场景:微振动检测、手势识别;
+    --2、常规运动监测,使用场景:运动监测、车载设备;
+    --3、高动态冲击检测,使用场景:碰撞检测、工业冲击
+    da221.open(1)
+    gpio.debounce(intPin, 100)
+    gpio.setup(intPin, ind)
+
+end
+
+sys.taskInit(gnss_fnc)

+ 4 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/gnss/gnss.lua

@@ -303,6 +303,10 @@ function gnss.agps()
     end
 end
 
+function gnss.openres()
+    return openFlag
+end
+
 --打开gnss,内部函数使用,不推荐给脚本层使用
 local function _open()
     if openFlag then return end

+ 2 - 1
module/Air780EHM_Air780EHV_Air780EGH/demo/gnss/main.lua

@@ -55,9 +55,10 @@ end
 gnss=require("gnss")    
 
 
-normal=require("normal")
+-- normal=require("normal")
 -- lowpower=require("lowpower")
 -- psm=require("psm")
+da221gnss=require("da221gnss")
 
 -- 用户代码已结束---------------------------------------------
 -- 结尾总是这一句

BIN
module/Air780EHM_Air780EHV_Air780EGH/demo/modbus/test_core/LuatOS-SoC_V2007_Air780EPM_TEMP_20250610_150400.soc


+ 176 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/README.md

@@ -0,0 +1,176 @@
+## 功能模块介绍
+
+1、main.lua:主程序入口;
+
+2、netdrv_device.lua:网卡驱动设备,可以配置使用netdrv文件夹内的四种网卡(单4g网卡,单wifi网卡,单spi以太网卡,多网卡)中的任何一种网卡;
+
+3、tcp文件夹:tcp client连接以及数据收发处理逻辑;
+
+4、udp文件夹:udp client连接以及数据收发处理逻辑;
+
+5、tcp_ssl文件夹:tcp ssl client连接以及数据收发处理逻辑;
+
+6、tcp_ssl_ca文件夹:tcp ssl client单向认证连接以及数据收发处理逻辑;
+
+7、network_watchdog.lua:网络环境检测看门狗;
+
+8、timer_app.lua:通知四个client定时发送数据到服务器;
+
+9、uart_app.lua:在四个client和uart外设之间透传数据;
+
+
+
+## 系统消息介绍
+
+1、"IP_READY":某种网卡已经获取到ip信息,仅仅获取到了ip信息,能否和外网连通还不确认;
+
+2、"IP_LOSE":某种网卡已经掉网;
+
+
+
+## 用户消息介绍
+
+1、"RECV_DATA_FROM_SERVER":socket client收到服务器下发的数据后,通过此消息发布出去,给其他应用模块处理;
+
+2、"SEND_DATA_REQ":其他应用模块发布此消息,通知socket client发送数据给服务器;
+
+3、"FEED_NETWORK_WATCHDOG":网络环境检测看门狗的喂狗消息,在需要喂狗的地方发布此消息;
+
+
+
+## 演示功能概述
+
+1、创建四路socket连接,详情如下
+
+- 创建一个tcp client,连接tcp server;
+
+- 创建一个udp client,连接udp server;
+
+- 创建一个tcp ssl client,连接tcp ssl server,不做证书校验;
+
+- 创建一个tcp ssl client,连接tcp ssl server,client仅单向校验server的证书,server不校验client的证书和密钥文件;
+
+2、每一路socket连接出现异常后,自动重连;
+
+3、每一路socket连接,client按照以下几种逻辑发送数据给server
+
+- 串口应用功能模块uart_app.lua,通过uart1接收到串口数据,将串口数据增加send from uart: 前缀后发送给server;
+
+- 定时器应用功能模块timer_app.lua,定时产生数据,将数据增加send from timer:前缀后发送给server;
+
+4、每一路socket连接,client收到server数据后,将数据增加recv from tcp/udp/tcp ssl/tcp ssl ca(四选一)server: 前缀后,通过uart1发送出去;
+
+5、启动一个网络业务逻辑看门狗task,用来监控网络环境,如果连续长时间工作不正常,重启整个软件系统;
+
+6、netdrv_device:配置连接外网使用的网卡,目前支持以下四种选择(四选一)
+
+   (1) netdrv_4g:4G网卡
+
+   (2) netdrv_wifi:WIFI STA网卡
+
+   (3) netdrv_eth_spi:通过SPI外挂CH390H芯片的以太网卡
+
+   (4) netdrv_multiple:支持以上三种网卡,可以配置三种网卡的优先级
+
+
+
+
+## 演示硬件环境
+
+![](https://docs.openluat.com/air780ehv/luatos/common/hwenv/image/Air780EHV.png)
+
+1、Air780EXX核心板一块+可上网的sim卡一张+4g天线一根+网线一根:
+
+- sim卡插入开发板的sim卡槽
+
+- 天线装到开发板上
+
+- 网线一端插入开发板网口,另外一端连接可以上外网的路由器网口
+
+2、TYPE-C USB数据线一根 + USB转串口数据线一根,Air780EXX核心板和数据线的硬件接线方式为:
+
+- Air780EXX核心板通过TYPE-C USB口供电;(外部供电/USB供电 拨动开关 拨到 USB供电一端)
+
+- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+- USB转串口数据线,一般来说,白线连接开发板的UART1_TX,绿线连接开发板的UART1_RX,黑线连接核心板的GND,另外一端连接电脑USB口;
+
+3、5、可选AirPHY_1000配件板一块,Air780EXX核心板和AirPHY_1000配件板的硬件接线方式为:
+
+| Air780EXX核心板  |  AirETH_1000配件板 |
+| --------------- | ----------------- |
+| 3V3             | 3.3v              |
+| gnd             | gnd               |
+| 86/SPI0CLK      | SCK               |
+| 83/SPI0CS       | CSS               |
+| 84/SPI0MISO     | SDO               |
+| 85/SPI0MOSI     | SDI               |
+| 107/GPIO21      | INT               |
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+2、[780EXX V2010版本固件)](https://docs.openluat.com/air780ehv/luatos/firmware/version/)(理论上,2025年7月26日之后发布的固件都可以)
+
+3、PC端的串口工具,例如SSCOM、LLCOM等都可以;
+
+4、PC端浏览器访问[合宙TCP/UDP web测试工具](https://netlab.luatos.com/);
+
+
+## 演示核心步骤
+
+1、搭建好硬件环境
+
+2、PC端浏览器访问[合宙TCP/UDP web测试工具](https://netlab.luatos.com/),点击 打开TCP 按钮,会创建一个TCP server,将server的地址和端口赋值给tcp_client_main.lua中的SERVER_ADDR和SERVER_PORT两个变量
+
+3、PC端浏览器访问[合宙TCP/UDP web测试工具](https://netlab.luatos.com/),点击 打开UDP 按钮,会创建一个UDP server,将server的地址和端口赋值给udp_client_main.lua中的SERVER_ADDR和SERVER_PORT两个变量
+
+4、PC端浏览器访问[合宙TCP/UDP web测试工具](https://netlab.luatos.com/),点击 打开TCP SSL 按钮,会创建一个TCP SSL server,将server的地址和端口赋值给tcp_ssl_main.lua中的SERVER_ADDR和SERVER_PORT两个变量
+
+5、demo脚本代码netdrv_device.lua中,按照自己的网卡需求启用对应的Lua文件
+
+- 如果需要单4G网卡,打开require "netdrv_4g",其余注释掉
+
+- 如果需要单WIFI STA网卡,打开require "netdrv_wifi",其余注释掉;同时netdrv_wifi.lua中的wlan.connect("茶室-降功耗,找合宙!", "Air123456", 1),前两个参数,修改为自己测试时wifi热点的名称和密码;注意:仅支持2.4G的wifi,不支持5G的wifi
+
+- 如果需要以太网卡,打开require "netdrv_eth_spi",其余注释掉
+
+- 如果需要多网卡,打开require "netdrv_multiple",其余注释掉;同时netdrv_multiple.lua中的ssid = "茶室-降功耗,找合宙!", password = "Air123456", 修改为自己测试时wifi热点的名称和密码;注意:仅支持2.4G的wifi,不支持5G的wifi
+
+6、Luatools烧录内核固件和修改后的demo脚本代码
+
+7、烧录成功后,自动开机运行
+
+8、[合宙TCP/UDP web测试工具](https://netlab.luatos.com/)上创建的TCP server、UDP server、TCP SSL server,一共三个server,可以看到有设备连接上来,每隔5秒钟,会接收到一段类似于 send from timer: 1 的数据,最后面的数字每次加1,类似于以下效果:
+
+``` lua
+[2025-06-24 16:47:39.085]send from timer: 1
+73656E642066726F6D2074696D65723A2031
+
+[2025-06-24 16:47:43.247]send from timer: 2
+73656E642066726F6D2074696D65723A2032
+
+[2025-06-24 16:47:48.241]send from timer: 3
+73656E642066726F6D2074696D65723A2033
+```
+
+
+9、打开PC端的串口工具,选择对应的端口,配置波特率115200,数据位8,停止位1,无奇偶校验位;
+
+10、PC端的串口工具输入一段数据,点击发送,在[合宙TCP/UDP web测试工具](https://netlab.luatos.com/)上的四个server页面都可以接收到数据,类似于以下效果:
+
+``` lua
+[2025-06-24 17:19:58.402]send from uart: kerjkjwr
+73656E642066726F6D20756172743A206B65726A6B6A7772
+```
+
+11、在[合宙TCP/UDP web测试工具](https://netlab.luatos.com/)的发送编辑框内,输入一段数据,点击发送,在PC端的串口工具上可以接收到这段数据,并且也能看到是哪一个server发送的,类似于以下效果:
+
+``` lua
+recv from tcp server: 123456798012345678901234567830
+recv from udp server: 123456798012345678901234567830
+recv from tcp_ssl server: 123456789901234
+```
+
+12、注意:第四路连接,连接的是baidu的https网站,连接成功后,Air780EXX每隔一段时间发数据给服务器,因为发送的不是http合法格式的数据,所以每隔一段时间服务器都会主动断开连接,断开连接后,Air780EXX会自动重连,如此循环,属于正常现象。

+ 109 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/main.lua

@@ -0,0 +1,109 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 1.0
+@date    2025.07.31
+@author  孟伟
+@usage
+本demo演示的核心功能为:
+1、创建四路socket连接,详情如下
+- 创建一个tcp client,连接tcp server;
+- 创建一个udp client,连接udp server;
+- 创建一个tcp ssl client,连接tcp ssl server,不做证书校验;
+- 创建一个tcp ssl client,连接tcp ssl server,client仅单向校验server的证书,server不校验client的证书和密钥文件;
+2、每一路socket连接出现异常后,自动重连;
+3、每一路socket连接,client按照以下几种逻辑发送数据给server
+- 串口应用功能模块uart_app.lua,通过uart1接收到串口数据,将串口数据增加send from uart: 前缀后发送给server;
+- 定时器应用功能模块timer_app.lua,定时产生数据,将数据增加send from timer:前缀后发送给server;
+4、每一路socket连接,client收到server数据后,将数据增加recv from tcp/udp/tcp ssl/tcp ssl ca(四选一)server: 前缀后,通过uart1发送出去;
+5、启动一个网络业务逻辑看门狗task,用来监控网络环境,如果连续长时间工作不正常,重启整个软件系统;
+6、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这三位数字必须存在,但是没有任何用处,可以一直写为000
+        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
+]]
+PROJECT = "SOCKET_LONG_CONNECTION"
+VERSION = "001.000.000"
+
+
+-- 在日志中打印项目名和项目版本号
+log.info("main", PROJECT, VERSION)
+
+
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
+end
+
+
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
+
+
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
+
+
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
+
+-- 加载网络环境检测看门狗功能模块
+require "network_watchdog"
+
+-- 加载网络驱动设备功能模块
+require "netdrv_device"
+
+-- 加载串口应用功能模块
+require "uart_app"
+-- 加载定时器应用功能模块
+require "timer_app"
+
+-- 加载tcp client socket主应用功能模块
+require "tcp_client_main"
+
+-- 加载udp client socket主应用功能模块
+require "udp_client_main"
+
+-- 打开内核固件中ssl的调试日志(需要分析问题时再打开)
+-- socket.sslLog(3)
+
+-- 加载tcp ssl client socket主应用功能模块
+require "tcp_ssl_main"
+
+-- 加载tcp ssl ca client socket主应用功能模块
+require "tcp_ssl_ca_main"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 33 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/netdrv/netdrv_4g.lua

@@ -0,0 +1,33 @@
+--[[
+@module  netdrv_4g
+@summary “4G网卡”驱动模块
+@version 1.0
+@date    2025.07.31
+@author  孟伟
+@usage
+本文件为4G网卡驱动模块,核心业务逻辑为:
+1、监听"IP_READY"和"IP_LOSE",在日志中进行打印;
+
+本文件没有对外接口,直接在其他功能模块中require "netdrv_4g"就可以加载运行;
+]]
+
+local function ip_ready_func()
+    log.info("netdrv_4g.ip_ready_func", "IP_READY", socket.localIP(socket.LWIP_GP))
+end
+
+local function ip_lose_func()
+    log.warn("netdrv_4g.ip_lose_func", "IP_LOSE")
+end
+
+
+
+--此处订阅"IP_READY"和"IP_LOSE"两种消息
+--在消息的处理函数中,仅仅打印了一些信息,便于实时观察4G网络的连接状态
+--也可以根据自己的项目需求,在消息处理函数中增加自己的业务逻辑控制,例如可以在连网状态发生改变时更新网络图标
+sys.subscribe("IP_READY", ip_ready_func)
+sys.subscribe("IP_LOSE", ip_lose_func)
+
+-- 设置默认网卡为socket.LWIP_GP
+-- 在Air8000上,内核固件运行起来之后,默认网卡就是socket.LWIP_GP
+-- 在单4G网卡使用场景下,下面这一行代码加不加都没有影响,为了和其他网卡驱动模块的代码风格保持一致,所以加上了
+socket.dft(socket.LWIP_GP)

+ 94 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/netdrv/netdrv_eth_spi.lua

@@ -0,0 +1,94 @@
+--[[
+@module  netdrv_eth_spi
+@summary “通过SPI外挂CH390H芯片的以太网卡”驱动模块
+@version 1.0
+@date    2025.07.31
+@author  孟伟
+@usage
+本文件为“通过SPI外挂CH390H芯片的以太网卡”驱动模块 ,核心业务逻辑为:
+1、打开AirETH_1000配件板供电开关;
+2、初始化spi0,初始化以太网卡,并且在以太网卡上开启DHCP(动态主机配置协议);
+3、以太网卡的连接状态发生变化时,在日志中进行打印;
+
+Air780EXX核心板和AirETH_1000配件板的硬件接线方式为:
+Air780EXX核心板通过TYPE-C USB口供电(核心板背面的功耗测试开关拨到OFF一端);
+| Air780EXX核心板  |  AirETH_1000配件板 |
+| --------------- | ----------------- |
+| 3V3             | 3.3v              |
+| gnd             | gnd               |
+| 86/SPI0CLK      | SCK               |
+| 83/SPI0CS       | CSS               |
+| 84/SPI0MISO     | SDO               |
+| 85/SPI0MOSI     | SDI               |
+| 107/GPIO21      | INT               |
+
+
+本文件没有对外接口,直接在其他功能模块中require "netdrv_eth_spi"就可以加载运行;
+]]
+
+local function ip_ready_func()
+    log.info("netdrv_eth_spi.ip_ready_func", "IP_READY", socket.localIP(socket.LWIP_ETH))
+
+end
+
+local function ip_lose_func()
+    log.warn("netdrv_eth_spi.ip_lose_func", "IP_LOSE")
+end
+
+
+
+--此处订阅"IP_READY"和"IP_LOSE"两种消息
+--在消息的处理函数中,仅仅打印了一些信息,便于实时观察“通过SPI外挂CH390H芯片的以太网卡”的连接状态
+--也可以根据自己的项目需求,在消息处理函数中增加自己的业务逻辑控制,例如可以在连网状态发生改变时更新网络图标
+sys.subscribe("IP_READY", ip_ready_func)
+sys.subscribe("IP_LOSE", ip_lose_func)
+
+
+-- 设置默认网卡为socket.LWIP_ETH
+socket.dft(socket.LWIP_ETH)
+
+--本demo测试使用的是核心板的VDD 3V3引脚对AirETH_1000配件板进行供电
+--3V3管脚是核心板LDO,3.3V输出,供测试用的,仅在使用DCDC供电时有输出,默认打开,无需控制
+
+
+--这个task的核心业务逻辑是:初始化SPI,初始化以太网卡,并在以太网卡上开启动态主机配置协议
+local function netdrv_eth_spi_task_func()
+    sys.wait(500)
+    -- 初始化SPI1
+    local result = spi.setup(
+        0,--spi_id
+        nil,
+        0,--CPHA
+        0,--CPOL
+        8,--数据宽度
+        25600000--,--频率
+        -- spi.MSB,--高低位顺序    可选,默认高位在前
+        -- spi.master,--主模式     可选,默认主
+        -- spi.full--全双工       可选,默认全双工
+    )
+    log.info("main", "open",result)
+    --返回值为0,表示打开成功
+    if result ~= 0 then
+        log.info("main", "spi open error",result)
+        return
+    end
+
+    --以太网联网成功(成功连接路由器,并且获取到了IP地址)后,内核固件会产生一个"IP_READY"消息
+    --各个功能模块可以订阅"IP_READY"消息实时处理以太网联网成功的事件
+    --也可以在任何时刻调用socket.adapter(socket.LWIP_USER1)来获取以太网是否连接成功
+
+    --以太网断网后,内核固件会产生一个"IP_LOSE"消息
+    --各个功能模块可以订阅"IP_LOSE"消息实时处理以太网断网的事件
+    --也可以在任何时刻调用socket.adapter(socket.LWIP_USER1)来获取以太网是否连接成功
+    -- 初始化指定netdrv设备,
+    -- socket.LWIP_ETH 网络适配器编号
+    -- netdrv.CH390外挂CH390
+    -- SPI ID 0, 片选 GPIO8
+    netdrv.setup(socket.LWIP_ETH, netdrv.CH390, {spi=0,cs=8})
+    netdrv.dhcp(socket.LWIP_ETH, true)
+
+end
+
+--创建并且启动一个task
+--task的处理函数为netdrv_eth_spi_task_func
+sys.taskInit(netdrv_eth_spi_task_func)

+ 98 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/netdrv/netdrv_multiple.lua

@@ -0,0 +1,98 @@
+--[[
+@module  netdrv_multiple
+@summary 多网卡(4G网卡、WIFI STA网卡、通过SPI外挂CH390H芯片的以太网卡)驱动模块
+@version 1.0
+@date    2025.07.31
+@author  孟伟
+@usage
+本文件为多网卡驱动模块 ,核心业务逻辑为:
+1、调用libnetif.set_priority_order配置多网卡的控制参数以及优先级;
+
+通过SPI外挂CH390H芯片的以太网卡:
+Air780EXX核心板和AirETH_1000配件板的硬件接线方式为:
+Air780EXX核心板通过TYPE-C USB口供电(核心板背面的功耗测试开关拨到OFF一端);
+| Air780EXX核心板  |  AirETH_1000配件板 |
+| --------------- | ----------------- |
+| 3V3             | 3.3v              |
+| gnd             | gnd               |
+| 86/SPI0CLK      | SCK               |
+| 83/SPI0CS       | CSS               |
+| 84/SPI0MISO     | SDO               |
+| 85/SPI0MOSI     | SDI               |
+| 107/GPIO21      | INT               |
+
+本文件没有对外接口,直接在其他功能模块中require "netdrv_multiple"就可以加载运行;
+]]
+
+
+local libnetif = require "libnetif"
+
+-- 网卡状态变化通知回调函数
+-- 当libnetif中检测到网卡切换或者所有网卡都断网时,会触发调用此回调函数
+-- 当网卡切换切换时:
+--     net_type:string类型,表示当前使用的网卡字符串
+--     adapter:number类型,表示当前使用的网卡id
+-- 当所有网卡断网时:
+--     net_type:为nil
+--     adapter:number类型,为-1
+local function netdrv_multiple_notify_cbfunc(net_type, adapter)
+    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()
+    --设置网卡优先级
+    libnetif.set_priority_order(
+        {
+            -- “通过SPI外挂CH390H芯片”的以太网卡,使用Air780EXX核心板验证
+            {
+                ETHERNET = {
+                    --本demo测试使用的是核心板的VDD 3V3引脚对AirETH_1000配件板进行供电
+                    --3V3管脚是核心板LDO,3.3V输出,供测试用的,仅在使用DCDC供电时有输出,默认打开,无需控制
+                    --如自己板子上设计了供电引脚,则打开下面pwrpin参数
+                    -- 供电使能GPIO
+                    -- pwrpin = 20,
+                    -- 设置的多个“已经IP READY,但是还没有ping通”网卡,循环执行ping动作的间隔(单位毫秒,可选)
+                    -- 如果没有传入此参数,libnetif会使用默认值10秒
+                    ping_time = 3000,
+
+                    -- 连通性检测ip(选填参数);
+                    -- 如果没有传入ip地址,libnetif中会默认使用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
+libnetif.notify_status(netdrv_multiple_notify_cbfunc)
+
+-- 如果存在udp网络应用,并且udp网络应用中,根据应用层的心跳能够判断出来udp数据通信出现了异常;
+-- 可以在判断出现异常的位置,调用一次libnetif.check_network_status()接口,强制对当前正式使用的网卡进行一次连通性检测;
+-- 如果存在tcp网络应用,不需要用户调用libnetif.check_network_status()接口去控制,libnetif会在tcp网络应用通信异常时自动对当前使用的网卡进行连通性检测。
+
+
+-- 启动一个task,task的处理函数为netdrv_multiple_task_func
+-- 在处理函数中调用libnetif.set_priority_order设置网卡优先级
+-- 因为libnetif.set_priority_order要求必须在task中被调用,所以此处启动一个task
+sys.taskInit(netdrv_multiple_task_func)

+ 30 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/netdrv_device.lua

@@ -0,0 +1,30 @@
+--[[
+@module  netdrv_device
+@summary 网络驱动设备功能模块
+@version 1.0
+@date    2025.07.31
+@author  孟伟
+@usage
+本文件为网络驱动设备功能模块,核心业务逻辑为:根据项目需求,选择并且配置合适的网卡(网络适配器)
+1、netdrv_4g:socket.LWIP_GP,4G网卡;
+2、netdrv_ethernet_spi:socket.LWIP_USER1,通过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"

+ 67 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/network_watchdog.lua

@@ -0,0 +1,67 @@
+--[[
+@module  network_watchdog
+@summary 网络环境检测看门狗功能模块
+@version 1.0
+@date    2025.07.31
+@author  孟伟
+@usage
+本文件为网络环境检测看门狗功能模块,监控网络环境是否工作正常(设备和服务器双向通信正常,或者至少单向通信正常),核心业务逻辑为:
+1、启动一个网络环境检测看门狗task,等待其他socket网络应用功能模块来喂狗,如果喂狗超时,则控制软件重启;
+2、如何确定“喂狗超时时间”,一般来说,有以下几个原则;
+   (1) 先确定一个最小基准值T1,2分钟或者5分钟或者10分钟,这个取值取决于具体项目需求,但是不能太短,因为开机后,在网络环境不太好的地方,网络初始化可能需要比较长的时间,一般推荐这个值不能小于2分钟;
+   (2) 再确定一个和产品业务逻辑有关的一个值T2,这个值和产品的应用业务逻辑息息相关,假设你的产品业务中:
+       <1> 服务器会定时下发数据给设备,例如设备连接上业务服务器之后,每隔3分钟,设备都会给服务器发送一次心跳,然后服务器都会立即回复一个心跳应答包;
+           这种情况下,可以取3分钟的大于等于1的倍数(例如1倍,1.5倍,2倍等等)+一段时间(例如10秒钟,如果前面是1倍,则此处必须加一段时间,给网络数据传输过程留够充足的时间);
+       <2> 如果服务器不会定时下发数据给设备,但是socket使用的是tcp传输,并且设备会定时发送数据给服务器,例如设备连接上业务服务器之后,每隔2分钟,设备都会给服务器发送一次心跳;
+           这种情况下,可以取2分钟的大于等于1的倍数(例如1倍,1.5倍,2倍等等)+一段时间(例如10秒钟,如果前面是1倍,则此处必须加一段时间,给网络数据传输过程留够充足的时间);
+       <3> 如果服务器既不会定时或者至少一段时间下发应用数据给设备,设备也不会定时或者至少一段时间上传应用数据到服务器;
+           这种情况下,一般来说也不是长连接应用,一般来说也不需要网络业务逻辑看门狗,遇到这种情况再具体问题具体分析;
+    (3) 取T1和T2的最大值,就是“喂狗超时时间”
+3、其他socket网络业务功能模块的喂狗时机,和上面2.2的描述相对应,一般来说,可以在以下几种时间点执行喂狗动作:
+   (1) 设备收到服务器下发的数据时
+   (2) tcp连接下,设备成功发送数据到服务器时
+   (3) tcp连接成功时(不到迫不得已,这种情况下不要喂狗,如果喂狗,可能会影响以上两点的判断;
+                     因为长连接的收发数据失败会导致一直重连,重连成功喂狗就会掩盖收发数据异常,除非收发数据完全无规律,才可能在tcp连接成功时喂狗)
+4、最重要的一点是:以上所说的原则,仅仅是建议,要根据自己的实际项目业务逻辑以及自己的需求最终确定看门狗方案
+
+5、具体到本demo
+   (1) 产品业务逻辑为:
+       <1> 创建了一个tcp连接,设备每隔5秒钟发送一次数据到服务器,服务器何时下发数据给设备不确定;
+       <2> 创建了一个udp连接,设备每隔5秒钟发送一次数据到服务器,服务器何时下发数据给设备不确定;
+       <3> 创建了一个tcp ssl连接,设备每隔5秒钟发送一次数据到服务器,服务器何时下发数据给设备不确定;
+       <4> 创建了一个tcp ssl 单向校验证书连接,设备每隔5秒钟发送一次数据到服务器,服务器何时下发数据给设备不确定;
+   (2) 确定喂狗超时时间:
+       <1> 本demo支持单以太网、单4G网络连接外网,网络环境准备就绪预留2分钟的时间已经足够,所以最小基准值T1取值2分钟;
+       <2> 本demo中存在4路socket连接,但是这4路socket连接都没有定时或者至少一段时间,服务器下发数据给设备,所以无法基于服务器下发数据的业务逻辑来确定T2的值;
+       <3> 本demo中存在4路socket连接,每1路socket连接,设备都是5秒发送一次数据给服务器,因为5秒的时间太短,在网络环境波动的时候,数据发送延时会比较大;
+           在这个demo中,我能接受的延时发送时长是1分钟,能接受连续3次延时发送时长的失败,所以,T2取值3分钟;
+       <4> 取T1 2分钟和T2 3分钟的最大值,最终的喂狗超时时间就是3分钟;
+   (3) 确定喂狗时机:
+       <1> 4路连接中,任何1路收到服务器的下发数据时;
+       <2> tcp、tcp ssl、tcp ssl单向校验证书3路连接中,任何1路成功发送数据给服务器时;
+6、本demo设计的网络环境检测看门狗功能模块,可以检测以下两种种的任意一种网络环境异常:
+   (1) 网络环境连续超过3分钟没有准备就绪
+   (2) tcp、tcp ssl、tcp ssl单向校验证书3路连接中,连续3分钟没有成功发送数据到服务器;并且4路连接中,连续3分钟没有收到服务器下发的数据;
+
+
+本文件没有对外接口,直接在main.lua中require "network_watchdog"就可以加载运行;
+外部功能模块喂狗时,直接调用sys.publish("FEED_NETWORK_WATCHDOG")
+]]
+
+-- 网络环境检测看门狗task处理函数
+local function network_watchdog_task_func()
+    while true do
+        --如果等待180秒没有等到"FEED_NETWORK_WATCHDOG"消息,则看门狗超时
+        if not sys.waitUntil("FEED_NETWORK_WATCHDOG", 180000) then
+            log.error("network_watchdog_task_func timeout")
+            -- 等待3秒钟,然后软件重启
+            sys.wait(3000)
+            rtos.reboot()
+        end
+    end
+end
+
+--创建并且启动一个task
+--运行这个task的处理函数network_watchdog_task_func
+sys.taskInit(network_watchdog_task_func)
+

+ 140 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp/tcp_client_main.lua

@@ -0,0 +1,140 @@
+--[[
+@module  tcp_client_main
+@summary tcp client socket主应用功能模块
+@version 1.0
+@date    2025.07.31
+@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://netlab.luatos.com/
+-- 点击 打开TCP 按钮,会创建一个TCP server
+-- 将server的地址和端口赋值给下面这两个变量
+local SERVER_ADDR = "112.125.89.8"
+local SERVER_PORT = 45584
+
+-- 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("sntp_task_func", "wait IP_READY", socket.dft())
+            -- 在此处阻塞等待默认网卡连接成功的消息"IP_READY"
+            -- 或者等待1秒超时退出阻塞等待状态;
+            -- 注意:此处的1000毫秒超时不要修改的更长;
+            -- 因为当使用libnetif.set_priority_order配置多个网卡连接外网的优先级时,会隐式的修改默认使用的网卡
+            -- 当libnetif.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)
+        -- 如果配置失败
+        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
+sysplus.taskInitEx(tcp_client_main_task_func, TASK_NAME, tcp_client_main_cbfunc)

+ 90 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp/tcp_client_receiver.lua

@@ -0,0 +1,90 @@
+--[[
+@module  tcp_client_receiver
+@summary tcp client socket数据接收应用功能模块
+@version 1.0
+@date    2025.07.31
+@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)
+
+            -- 接收到数据,通知网络环境检测看门狗功能模块进行喂狗
+            sys.publish("FEED_NETWORK_WATCHDOG")
+
+            -- 清空socket数据接收缓冲区中的数据
+            recv_buff:del()
+            -- 读取成功,但是读出来的数据为空,表示已经没有数据可读,可以退出循环了
+        else
+            break
+        end
+    end
+
+    return true
+end
+
+return tcp_client_receiver

+ 138 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp/tcp_client_sender.lua

@@ -0,0 +1,138 @@
+--[[
+@module  tcp_client_sender
+@summary tcp client socket数据发送应用功能模块
+@version 1.0
+@date    2025.07.31
+@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(tag, data, cb)
+    -- 将原始数据增加前缀,然后插入到发送队列send_queue中
+    table.insert(send_queue, {data="send from "..tag..": "..data, cb=cb})
+    -- 通知tcp_client_main主任务有数据需要发送
+    -- tcp_client_main主任务如果处在libnet.wait调用的阻塞等待状态,就会退出阻塞状态
+    sysplus.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
+
+        -- 发送成功,通知网络环境检测看门狗功能模块进行喂狗
+        sys.publish("FEED_NETWORK_WATCHDOG")
+    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

+ 141 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp_ssl/tcp_ssl_main.lua

@@ -0,0 +1,141 @@
+--[[
+@module  tcp_ssl_main
+@summary tcp_ssl client socket主应用功能模块
+@version 1.0
+@date    2025.07.31
+@author  孟伟
+@usage
+本文件为tcp_ssl client socket主应用功能模块,核心业务逻辑为:
+1、创建一个tcp_ssl client socket,连接server;
+2、处理连接异常,出现异常后执行重连动作;
+3、调用tcp_ssl_receiver和tcp_ssl_sender中的外部接口,进行数据收发处理;
+
+本文件没有对外接口,直接在main.lua中require "tcp_ssl_main"就可以加载运行;
+]]
+
+local libnet = require "libnet"
+
+-- 加载tcp_ssl client socket数据接收功能模块
+local tcp_ssl_receiver = require "tcp_ssl_receiver"
+-- 加载tcp_ssl client socket数据发送功能模块
+local tcp_ssl_sender = require "tcp_ssl_sender"
+
+-- 电脑访问:https://netlab.luatos.com/
+-- 点击 打开TCP SSL 按钮,会创建一个TCP SSL server
+-- 将server的地址和端口赋值给下面这两个变量
+local SERVER_ADDR = "112.125.89.8"
+local SERVER_PORT = 43333
+
+-- tcp_ssl_main的任务名
+local TASK_NAME = tcp_ssl_sender.TASK_NAME
+
+
+-- 处理未识别的消息
+local function tcp_ssl_main_cbfunc(msg)
+    log.info("tcp_ssl_main_cbfunc", msg[1], msg[2], msg[3], msg[4])
+end
+
+-- tcp_ssl client socket的任务处理函数
+local function tcp_ssl_main_task_func()
+    local socket_client
+    local result, para1, para2
+
+    while true do
+        -- 如果当前时间点设置的默认网卡还没有连接成功,一直在这里循环等待
+        while not socket.adapter(socket.dft()) do
+            log.warn("sntp_task_func", "wait IP_READY", socket.dft())
+            -- 在此处阻塞等待默认网卡连接成功的消息"IP_READY"
+            -- 或者等待1秒超时退出阻塞等待状态;
+            -- 注意:此处的1000毫秒超时不要修改的更长;
+            -- 因为当使用libnetif.set_priority_order配置多个网卡连接外网的优先级时,会隐式的修改默认使用的网卡
+            -- 当libnetif.set_priority_order的调用时序和此处的socket.adapter(socket.dft())判断时序有可能不匹配
+            -- 此处的1秒,能够保证,即使时序不匹配,也能1秒钟退出阻塞状态,再去判断socket.adapter(socket.dft())
+            sys.waitUntil("IP_READY", 1000)
+        end
+
+        -- 检测到了IP_READY消息
+        log.info("tcp_ssl_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_ssl_main_task_func", "socket.create error")
+            goto EXCEPTION_PROC
+        end
+
+        -- 配置socket client对象为tcp_ssl client
+        -- 不做证书校验
+        result = socket.config(socket_client, nil, nil, true)
+        -- 如果配置失败
+        if not result then
+            log.error("tcp_ssl_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_ssl_main_task_func", "libnet.connect error")
+            goto EXCEPTION_PROC
+        end
+
+        log.info("tcp_ssl_main_task_func", "libnet.connect success")
+
+        -- 数据收发以及网络连接异常事件总处理逻辑
+        while true do
+            -- 数据接收处理(接收处理必须写在libnet.wait之前,因为老版本的内核固件要求必须这样,新版本的内核固件没这个要求,为了不出问题,写在libnet.wait之前就行了)
+            -- 如果处理失败,则退出循环
+            if not tcp_ssl_receiver.proc(socket_client) then
+                log.error("tcp_ssl_main_task_func", "tcp_ssl_receiver.proc error")
+                break
+            end
+
+            -- 数据发送处理
+            -- 如果处理失败,则退出循环
+            if not tcp_ssl_sender.proc(TASK_NAME, socket_client) then
+                log.error("tcp_ssl_main_task_func", "tcp_ssl_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_ssl_sender.lua中会发布事件socket.EVENT
+            result, para1, para2 = libnet.wait(TASK_NAME, 15000, socket_client)
+            log.info("tcp_ssl_main_task_func", "libnet.wait", result, para1, para2)
+
+            -- 如果连接异常,则退出循环
+            if not result then
+                log.warn("tcp_ssl_main_task_func", "connection exception")
+                break
+            end
+        end
+
+
+        -- 出现异常
+        ::EXCEPTION_PROC::
+
+        -- 数据发送应用模块对来不及发送的数据做清空和通知失败处理
+        tcp_ssl_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_ssl_main_task_func
+sysplus.taskInitEx(tcp_ssl_main_task_func, TASK_NAME, tcp_ssl_main_cbfunc)

+ 96 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp_ssl/tcp_ssl_receiver.lua

@@ -0,0 +1,96 @@
+--[[
+@module  tcp_ssl_receiver
+@summary tcp_ssl client socket数据接收应用功能模块
+@version 1.0
+@date    2025.07.31
+@author  孟伟
+@usage
+本文件为tcp_ssl client socket数据接收应用功能模块,核心业务逻辑为:
+从内核读取接收到的数据,然后将数据发送给其他应用功能模块做进一步处理;
+
+本文件的对外接口有2个:
+1、tcp_ssl_receiver.proc(socket_client):数据接收应用逻辑处理入口,在tcp_ssl_main.lua中调用;
+2、sys.publish("RECV_DATA_FROM_SERVER", "recv from tcp_ssl server: ", data):
+   将接收到的数据通过消息"RECV_DATA_FROM_SERVER"发布出去;
+   需要处理数据的应用功能模块订阅处理此消息即可,本demo项目中uart_app.lua中订阅处理了本消息;
+]]
+
+local tcp_ssl_receiver = {}
+
+-- socket数据接收缓冲区
+local recv_buff = nil
+
+--[[
+检查socket client是否收到数据,如果收到数据,读取并且处理完所有数据
+
+@api tcp_ssl_receiver.proc(socket_client)
+
+@param1 socket_client userdata
+表示由socket.create接口创建的socket client对象;
+必须传入,不允许为空或者nil;
+
+@return1 result bool
+表示处理结果,成功为true,失败为false
+
+@usage
+--
+tcp_ssl_receiver.proc(socket_client)
+]]
+function tcp_ssl_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的存储空间不足,会自动扩容
+        -- 如果使用netlab.luatos.com创建的tcl ssl server来配合测试,注意server存在一个问题,后续可能会解决,也可能没解决,问题如下:
+        -- netlab创建的tcp ssl server,第一次下发数据给client时,会截取输入的完整数据的一半数据做为一包发送,剩余一半做为第二包发送;
+        -- 以后server再次发送其他数据,一包的最大长度一直是第一次下发使用的长度;
+        -- 假设第一次在编辑框输入了12字节的数据,则会拆分成2包数据进行发送,每包6字节;
+        -- 假设第一次在编辑框输入了120字节的数据,则会拆分成20包数据进行发送,每包仍然6字节;
+        -- 如果出现了这个问题,不用担心,和client无关,最终你使用自己的server时只要保证自己的server没问题就行;
+        local result = socket.rx(socket_client, recv_buff)
+
+        -- 读取数据失败
+        -- 有两种情况:
+        -- 1、recv_buff扩容失败
+        -- 2、socket client和server之间的连接断开
+        if not result then
+            log.error("tcp_ssl_receiver.proc", "socket.rx error")
+            return false
+        end
+
+        -- 如果读取到了数据, used()就必然大于0, 进行处理
+        if recv_buff:used() > 0 then
+            log.info("tcp_ssl_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_ssl server: ", data)
+
+            -- 接收到数据,通知网络环境检测看门狗功能模块进行喂狗
+            sys.publish("FEED_NETWORK_WATCHDOG")
+
+            -- 清空socket数据接收缓冲区中的数据
+            recv_buff:del()
+        -- 读取成功,但是读出来的数据为空,表示已经没有数据可读,可以退出循环了
+        else
+            break
+        end
+    end
+
+    return true
+end
+
+return tcp_ssl_receiver

+ 138 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp_ssl/tcp_ssl_sender.lua

@@ -0,0 +1,138 @@
+--[[
+@module  tcp_ssl_sender
+@summary tcp_ssl client socket数据发送应用功能模块
+@version 1.0
+@date    2025.07.31
+@author  孟伟
+@usage
+本文件为tcp_ssl client socket数据发送应用功能模块,核心业务逻辑为:
+1、sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)订阅"SEND_DATA_REQ"消息,将其他应用模块需要发送的数据存储到队列send_queue中;
+2、tcp_ssl_main主任务调用tcp_ssl_sender.proc接口,遍历队列send_queue,逐条发送数据到server;
+3、tcp_ssl client socket和server之间的连接如果出现异常,tcp_ssl_main主任务调用tcp_ssl_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_ssl_sender.proc:数据发送应用逻辑处理入口,在tcp_ssl_main.lua中调用;
+3、tcp_ssl_sender.exception_proc:数据发送应用逻辑异常处理入口,在tcp_ssl_main.lua中调用;
+]]
+
+local tcp_ssl_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_ssl_main的任务名
+tcp_ssl_sender.TASK_NAME = "tcp_ssl_main"
+
+-- "SEND_DATA_REQ"消息的处理函数
+local function send_data_req_proc_func(tag, data, cb)
+    -- 将原始数据增加前缀,然后插入到发送队列send_queue中
+    table.insert(send_queue, {data="send from "..tag..": "..data, cb=cb})
+    -- 通知tcp_ssl_main主任务有数据需要发送
+    -- tcp_ssl_main主任务如果处在libnet.wait调用的阻塞等待状态,就会退出阻塞状态
+    sysplus.sendMsg(tcp_ssl_sender.TASK_NAME, socket.EVENT, 0)
+end
+
+--[[
+检查socket client是否需要发送数据,如果需要发送数据,读取并且发送完发送队列中的所有数据
+
+@api tcp_ssl_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_ssl_sender.proc("tcp_client_main", socket_client)
+]]
+function tcp_ssl_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_ssl_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_ssl_sender.proc", "buffer is full, wait for the next time")
+            table.insert(send_queue, 1, send_item)
+            return true
+        end
+
+        log.info("tcp_ssl_sender.proc", "send success")
+        -- 发送成功,如果当前发送的数据有用户回调函数,则执行用户回调函数
+        if send_item.cb and send_item.cb.func then
+            send_item.cb.func(true, send_item.cb.para)
+        end
+
+        -- 发送成功,通知网络环境检测看门狗功能模块进行喂狗
+        sys.publish("FEED_NETWORK_WATCHDOG")
+    end
+
+    return true
+end
+
+--[[
+socket client连接出现异常时,清空等待发送的数据,并且执行发送方的回调函数
+
+@api tcp_ssl_sender.exception_proc()
+
+@usage
+tcp_ssl_sender.exception_proc()
+]]
+function tcp_ssl_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_ssl_sender

+ 26 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp_ssl_ca/baidu_parent_ca.crt

@@ -0,0 +1,26 @@
+-----BEGIN CERTIFICATE-----
+MIIETjCCAzagAwIBAgINAe5fIh38YjvUMzqFVzANBgkqhkiG9w0BAQsFADBMMSAw
+HgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFs
+U2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0xODExMjEwMDAwMDBaFw0yODEx
+MjEwMDAwMDBaMFAxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52
+LXNhMSYwJAYDVQQDEx1HbG9iYWxTaWduIFJTQSBPViBTU0wgQ0EgMjAxODCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKdaydUMGCEAI9WXD+uu3Vxoa2uP
+UGATeoHLl+6OimGUSyZ59gSnKvuk2la77qCk8HuKf1UfR5NhDW5xUTolJAgvjOH3
+idaSz6+zpz8w7bXfIa7+9UQX/dhj2S/TgVprX9NHsKzyqzskeU8fxy7quRU6fBhM
+abO1IFkJXinDY+YuRluqlJBJDrnw9UqhCS98NE3QvADFBlV5Bs6i0BDxSEPouVq1
+lVW9MdIbPYa+oewNEtssmSStR8JvA+Z6cLVwzM0nLKWMjsIYPJLJLnNvBhBWk0Cq
+o8VS++XFBdZpaFwGue5RieGKDkFNm5KQConpFmvv73W+eka440eKHRwup08CAwEA
+AaOCASkwggElMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB0G
+A1UdDgQWBBT473/yzXhnqN5vjySNiPGHAwKz6zAfBgNVHSMEGDAWgBSP8Et/qC5F
+JK5NUPpjmove4t0bvDA+BggrBgEFBQcBAQQyMDAwLgYIKwYBBQUHMAGGImh0dHA6
+Ly9vY3NwMi5nbG9iYWxzaWduLmNvbS9yb290cjMwNgYDVR0fBC8wLTAroCmgJ4Yl
+aHR0cDovL2NybC5nbG9iYWxzaWduLmNvbS9yb290LXIzLmNybDBHBgNVHSAEQDA+
+MDwGBFUdIAAwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xvYmFsc2lnbi5j
+b20vcmVwb3NpdG9yeS8wDQYJKoZIhvcNAQELBQADggEBAJmQyC1fQorUC2bbmANz
+EdSIhlIoU4r7rd/9c446ZwTbw1MUcBQJfMPg+NccmBqixD7b6QDjynCy8SIwIVbb
+0615XoFYC20UgDX1b10d65pHBf9ZjQCxQNqQmJYaumxtf4z1s4DfjGRzNpZ5eWl0
+6r/4ngGPoJVpjemEuunl1Ig423g7mNA2eymw0lIYkN5SQwCuaifIFJ6GlazhgDEw
+fpolu4usBCOmmQDo8dIm7A9+O4orkjgTHY+GzYZSR+Y0fFukAj6KYXwidlNalFMz
+hriSqHKvoflShx8xpfywgVcvzfTO3PYkz6fiNJBonf6q8amaEsybwMbDqKWwIX7e
+SPY=
+-----END CERTIFICATE-----

+ 66 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp_ssl_ca/sntp_app.lua

@@ -0,0 +1,66 @@
+--[[
+@module  sntp_app
+@summary sntp时间同步应用功能模块
+@version 1.0
+@date    2025.07.31
+@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毫秒超时不要修改的更长;
+            -- 因为当使用libnetif.set_priority_order配置多个网卡连接外网的优先级时,会隐式的修改默认使用的网卡
+            -- 当libnetif.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)
+

+ 164 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp_ssl_ca/tcp_ssl_ca_main.lua

@@ -0,0 +1,164 @@
+--[[
+@module  tcp_ssl_ca_main
+@summary tcp_ssl_ca client socket主应用功能模块
+@version 1.0
+@date    2025.07.31
+@author  孟伟
+@usage
+本文件为tcp_ssl_ca client socket主应用功能模块,核心业务逻辑为:
+1、创建一个tcp_ssl_ca client socket,连接server;
+2、处理连接异常,出现异常后执行重连动作;
+3、调用tcp_ssl_ca_receiver和tcp_ssl_ca_sender中的外部接口,进行数据收发处理;
+
+本文件没有对外接口,直接在main.lua中require "tcp_ssl_ca_main"就可以加载运行;
+]]
+
+local libnet = require "libnet"
+
+-- 加载sntp时间同步应用功能模块(ca证书校验的ssl socket需要时间同步功能)
+require "sntp_app"
+
+-- 加载tcp_ssl_ca client socket数据接收功能模块
+local tcp_ssl_ca_receiver = require "tcp_ssl_ca_receiver"
+-- 加载tcp_ssl_ca client socket数据发送功能模块
+local tcp_ssl_ca_sender = require "tcp_ssl_ca_sender"
+
+-- https://www.baidu.com网站服务器,地址为"www.baidu.com",端口为443
+local SERVER_ADDR = "www.baidu.com"
+local SERVER_PORT = 443
+
+-- tcp_ssl_ca_main的任务名
+local TASK_NAME = tcp_ssl_ca_sender.TASK_NAME
+
+
+-- 处理未识别的消息
+local function tcp_ssl_ca_main_cbfunc(msg)
+	log.info("tcp_ssl_ca_main_cbfunc", msg[1], msg[2], msg[3], msg[4])
+end
+
+-- tcp_ssl_ca client socket的任务处理函数
+local function tcp_ssl_ca_main_task_func()
+
+    local socket_client
+    local result, para1, para2
+
+    -- 用来验证server证书是否合法的ca证书文件为baidu_parent_ca.crt
+    -- 此ca证书的有效期截止到2028年11月21日
+    -- 将这个ca证书文件的内容读取出来,赋值给server_ca_cert
+    -- 注意:此处的ca证书文件仅用来验证baidu网站的server证书
+    -- baidu网站的server证书有效期截止到2026年8月10日
+    -- 在有效期之前,baidu会更换server证书,如果server证书更换后,此处验证使用的baidu_parent_ca.crt也可能需要更换
+    -- 使用电脑上的网页浏览器访问https://www.baidu.com,可以实时看到baidu的server证书以及baidu_parent_ca.crt
+    -- 如果你使用的是自己的server,要替换为自己server证书对应的ca证书文件
+    local server_ca_cert = io.readFile("/luadb/baidu_parent_ca.crt")
+
+    while true do
+        -- 如果当前时间点设置的网卡还没有连接成功,一直在这里循环等待
+        while not socket.adapter(socket.dft()) do
+            log.warn("tcp_ssl_ca_main_task_func", "wait IP_READY", socket.dft())
+            -- 在此处阻塞等待网卡连接成功的消息"IP_READY"
+            -- 或者等待1秒超时退出阻塞等待状态;
+            -- 注意:此处的1000毫秒超时不要修改的更长;
+            -- 因为当使用libnetif.set_priority_order配置多个网卡连接外网的优先级时,会隐式的修改当前使用的网卡
+            -- 当libnetif.set_priority_order的调用时序和此处的socket.adapter(socket.dft())判断时序有可能不匹配
+            -- 此处的1秒,能够保证,即使时序不匹配,也能1秒钟退出阻塞状态,再去判断socket.adapter(socket.dft())
+            sys.waitUntil("IP_READY", 1000)
+        end
+
+        -- 检测到了IP_READY消息
+        log.info("tcp_ssl_ca_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_ssl_ca_main_task_func", "socket.create error")
+            goto EXCEPTION_PROC
+        end
+
+        -- 配置socket client对象为tcp_ssl_ca client
+        -- client仅单向校验server的证书,server不校验client的证书和密钥文件
+        -- 如果做证书校验,需要特别注意以下几点:
+        -- 1、证书校验前,设备端必须同步为正确的时间,因为校验过程中会检查ca证书以及server证书中的有效期是否合法;本demo中的sntp_app.lua会同步时间;
+        -- 2、任何证书都有有效期,无论是ca证书还是server证书,必须在有效期截止之前,及时更换证书,延长有效期,否则证书校验会失败;
+        -- 3、如果要更换ca证书,需要在设备端远程升级,必须保证ca证书失效之前升级成功,否则校验失败,就无法连接server;
+        -- 综上所述,证书校验虽然安全,可以验证身份,但是后续维护成本比较高;除非有需要,否则可以不配置证书校验功能;
+        -- 另外,如果使用https://netlab.luatos.com/创建的TCP SSL Server,使用的server证书有可能过了有效期;
+        -- 如果过了有效期,使用本文件无法连接成功tcp ssl ca server,遇到这种问题,可以在main.lua中打开socket.sslLog(3),观察Luatools的日志,如果出现类似于下面的日志
+        -- expires on        : 2020-12-27 15:46:55
+        -- 表示证书有效期截止到2020-12-27 15:46:55,明显就是证书已经过了有效期
+        -- 遇到这种情况,可以反馈给合宙的技术人员;或者不再使用netlab server测试,使用你自己的tcp ssl server来测试,只要保证你的server证书合法就行;
+        result = socket.config(socket_client, nil, nil, true, nil, nil, nil, server_ca_cert)
+        -- 如果配置失败
+        if not result then
+            log.error("tcp_ssl_ca_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_ssl_ca_main_task_func", "libnet.connect error")
+            goto EXCEPTION_PROC
+        end
+
+        log.info("tcp_ssl_ca_main_task_func", "libnet.connect success")
+
+        -- 数据收发以及网络连接异常事件总处理逻辑
+        while true do
+            -- 数据接收处理(接收处理必须写在libnet.wait之前,因为老版本的内核固件要求必须这样,新版本的内核固件没这个要求,为了不出问题,写在libnet.wait之前就行了)
+            -- 如果处理失败,则退出循环
+            if not tcp_ssl_ca_receiver.proc(socket_client) then
+                log.error("tcp_ssl_ca_main_task_func", "tcp_ssl_ca_receiver.proc error")
+                break
+            end
+
+            -- 数据发送处理
+            -- 如果处理失败,则退出循环
+            if not tcp_ssl_ca_sender.proc(TASK_NAME, socket_client) then
+                log.error("tcp_ssl_ca_main_task_func", "tcp_ssl_ca_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_ssl_ca_sender.lua中会发布事件socket.EVENT
+			result, para1, para2 = libnet.wait(TASK_NAME, 15000, socket_client)
+            log.info("tcp_ssl_ca_main_task_func", "libnet.wait", result, para1, para2)
+
+			-- 如果连接异常,则退出循环
+			if not result then
+				log.warn("tcp_ssl_ca_main_task_func", "connection exception")
+				break
+            end
+        end
+
+
+        -- 出现异常
+        ::EXCEPTION_PROC::
+
+        -- 数据发送应用模块对来不及发送的数据做清空和通知失败处理
+        tcp_ssl_ca_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_ssl_ca_main_task_func
+sysplus.taskInitEx(tcp_ssl_ca_main_task_func, TASK_NAME, tcp_ssl_ca_main_cbfunc)
+

+ 96 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp_ssl_ca/tcp_ssl_ca_receiver.lua

@@ -0,0 +1,96 @@
+--[[
+@module  tcp_ssl_ca_receiver
+@summary tcp_ssl_ca client socket数据接收应用功能模块
+@version 1.0
+@date    2025.07.31
+@author  孟伟
+@usage
+本文件为tcp_ssl_ca client socket数据接收应用功能模块,核心业务逻辑为:
+从内核读取接收到的数据,然后将数据发送给其他应用功能模块做进一步处理;
+
+本文件的对外接口有2个:
+1、tcp_ssl_ca_receiver.proc(socket_client):数据接收应用逻辑处理入口,在tcp_ssl_ca_main.lua中调用;
+2、sys.publish("RECV_DATA_FROM_SERVER", "recv from tcp_ssl_ca server: ", data):
+   将接收到的数据通过消息"RECV_DATA_FROM_SERVER"发布出去;
+   需要处理数据的应用功能模块订阅处理此消息即可,本demo项目中uart_app.lua中订阅处理了本消息;
+]]
+
+local tcp_ssl_ca_receiver = {}
+
+-- socket数据接收缓冲区
+local recv_buff = nil
+
+--[[
+检查socket client是否收到数据,如果收到数据,读取并且处理完所有数据
+
+@api tcp_ssl_ca_receiver.proc(socket_client)
+
+@param1 socket_client userdata
+表示由socket.create接口创建的socket client对象;
+必须传入,不允许为空或者nil;
+
+@return1 result bool
+表示处理结果,成功为true,失败为false
+
+@usage
+--
+tcp_ssl_ca_receiver.proc(socket_client)
+]]
+function tcp_ssl_ca_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的存储空间不足,会自动扩容
+        -- 如果使用netlab.luatos.com创建的tcl ssl server来配合测试,注意server存在一个问题,后续可能会解决,也可能没解决,问题如下:
+        -- netlab创建的tcp ssl server,第一次下发数据给client时,会截取输入的完整数据的一半数据做为一包发送,剩余一半做为第二包发送;
+        -- 以后server再次发送其他数据,一包的最大长度一直是第一次下发使用的长度;
+        -- 假设第一次在编辑框输入了12字节的数据,则会拆分成2包数据进行发送,每包6字节;
+        -- 假设第一次在编辑框输入了120字节的数据,则会拆分成20包数据进行发送,每包仍然6字节;
+        -- 如果出现了这个问题,不用担心,和client无关,最终你使用自己的server时只要保证自己的server没问题就行;
+        local result = socket.rx(socket_client, recv_buff)
+
+        -- 读取数据失败
+        -- 有两种情况:
+        -- 1、recv_buff扩容失败
+        -- 2、socket client和server之间的连接断开
+        if not result then
+            log.error("tcp_ssl_ca_receiver.proc", "socket.rx error")
+            return false
+        end
+
+        -- 如果读取到了数据, used()就必然大于0, 进行处理
+        if recv_buff:used() > 0 then
+            log.info("tcp_ssl_ca_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_ssl_ca server: ", data)
+
+            -- 接收到数据,通知网络环境检测看门狗功能模块进行喂狗
+            sys.publish("FEED_NETWORK_WATCHDOG")
+
+            -- 清空socket数据接收缓冲区中的数据
+            recv_buff:del()
+            -- 读取成功,但是读出来的数据为空,表示已经没有数据可读,可以退出循环了
+        else
+            break
+        end
+    end
+
+    return true
+end
+
+return tcp_ssl_ca_receiver

+ 138 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/tcp_ssl_ca/tcp_ssl_ca_sender.lua

@@ -0,0 +1,138 @@
+--[[
+@module  tcp_ssl_ca_sender
+@summary tcp_ssl_ca client socket数据发送应用功能模块
+@version 1.0
+@date    2025.07.31
+@author  孟伟
+@usage
+本文件为tcp_ssl_ca client socket数据发送应用功能模块,核心业务逻辑为:
+1、sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)订阅"SEND_DATA_REQ"消息,将其他应用模块需要发送的数据存储到队列send_queue中;
+2、tcp_ssl_ca_main主任务调用tcp_ssl_ca_sender.proc接口,遍历队列send_queue,逐条发送数据到server;
+3、tcp_ssl_ca client socket和server之间的连接如果出现异常,tcp_ssl_ca_main主任务调用tcp_ssl_ca_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_ssl_ca_sender.proc:数据发送应用逻辑处理入口,在tcp_ssl_ca_main.lua中调用;
+3、tcp_ssl_ca_sender.exception_proc:数据发送应用逻辑异常处理入口,在tcp_ssl_ca_main.lua中调用;
+]]
+
+local tcp_ssl_ca_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_ssl_ca_main的任务名
+tcp_ssl_ca_sender.TASK_NAME = "tcp_ssl_ca_main"
+
+-- "SEND_DATA_REQ"消息的处理函数
+local function send_data_req_proc_func(tag, data, cb)
+    -- 将原始数据增加前缀,然后插入到发送队列send_queue中
+    table.insert(send_queue, {data="send from "..tag..": "..data, cb=cb})
+    -- 通知tcp_ssl_ca_main主任务有数据需要发送
+    -- tcp_ssl_ca_main主任务如果处在libnet.wait调用的阻塞等待状态,就会退出阻塞状态
+    sysplus.sendMsg(tcp_ssl_ca_sender.TASK_NAME, socket.EVENT, 0)
+end
+
+--[[
+检查socket client是否需要发送数据,如果需要发送数据,读取并且发送完发送队列中的所有数据
+
+@api tcp_ssl_ca_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_ssl_ca_sender.proc("tcp_client_main", socket_client)
+]]
+function tcp_ssl_ca_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_ssl_ca_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_ssl_ca_sender.proc", "buffer is full, wait for the next time")
+            table.insert(send_queue, 1, send_item)
+            return true
+        end
+
+        log.info("tcp_ssl_ca_sender.proc", "send success")
+        -- 发送成功,如果当前发送的数据有用户回调函数,则执行用户回调函数
+        if send_item.cb and send_item.cb.func then
+            send_item.cb.func(true, send_item.cb.para)
+        end
+
+        -- 发送成功,通知网络环境检测看门狗功能模块进行喂狗
+        sys.publish("FEED_NETWORK_WATCHDOG")
+    end
+
+    return true
+end
+
+--[[
+socket client连接出现异常时,清空等待发送的数据,并且执行发送方的回调函数
+
+@api tcp_ssl_ca_sender.exception_proc()
+
+@usage
+tcp_ssl_ca_sender.exception_proc()
+]]
+function tcp_ssl_ca_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_ssl_ca_sender

+ 41 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/timer_app.lua

@@ -0,0 +1,41 @@
+--[[
+@module  timer_app
+@summary 定时器应用功能模块
+@version 1.0
+@date    2025.07.31
+@author  孟伟
+@usage
+本文件为定时器应用功能模块,核心业务逻辑为:
+创建一个5秒的循环定时器,每次产生一段数据,通知四个socket client进行处理;
+
+本文件的对外接口有一个:
+1、sys.publish("SEND_DATA_REQ", "timer", data, {func=send_data_cbfunc, para="timer"..data}),通过publish通知socket client数据发送功能模块发送data数据;
+   数据发送结果通过执行回调函数send_data_cbfunc通知本功能模块;
+]]
+
+local data = 1
+
+-- 数据发送结果回调函数
+-- result:发送结果,true为发送成功,false为发送失败
+-- para:回调参数,sys.publish("SEND_DATA_REQ", "timer", data, {func=send_data_cbfunc, para="timer"..data})中携带的para
+local function send_data_cbfunc(result, para)
+    log.info("send_data_cbfunc", result, para)
+    -- 无论上一次发送成功还是失败,启动一个5秒的定时器,5秒后发送下次数据
+    sys.timerStart(send_data_req_timer_cbfunc, 5000)
+end
+
+-- 定时器回调函数
+function send_data_req_timer_cbfunc()
+    -- 发布消息"SEND_DATA_REQ"
+    -- 携带的第一个参数"timer"表示是定时器应用模块发布的消息
+    -- 携带的第二个参数data为要发送的原始数据
+    -- 携带的第三个参数cb为发送结果回调(可以为空,如果为空,表示不关心socket client发送数据成功还是失败),其中:
+    --       cb.func为回调函数(可以为空,如果为空,表示不关心socket client发送数据成功还是失败)
+    --       cb.para为回调函数的第二个参数(可以为空),回调函数的第一个参数为发送结果(true表示成功,false表示失败)
+    sys.publish("SEND_DATA_REQ", "timer", data, {func=send_data_cbfunc, para="timer"..data})
+    data = data+1
+end
+
+-- 启动一个5秒的单次定时器
+-- 时间到达后,执行一次send_data_req_timer_cbfunc函数
+sys.timerStart(send_data_req_timer_cbfunc, 5000)

+ 79 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/uart_app.lua

@@ -0,0 +1,79 @@
+--[[
+@module  uart_app
+@summary 串口应用功能模块
+@version 1.0
+@date    2025.07.31
+@author  孟伟
+@usage
+本文件为串口应用功能模块,核心业务逻辑为:
+1、打开uart1,波特率115200,数据位8,停止位1,无奇偶校验位;
+2、uart1和pc端的串口工具相连;
+3、从uart1接收到pc端串口工具发送的数据后,通知四个socket client进行处理;
+4、收到四个socket client从socket server接收到的数据后,将数据通过uart1发送到pc端串口工具;
+
+本文件的对外接口有两个:
+1、sys.publish("SEND_DATA_REQ", "uart", read_buf),通过publish通知socket client数据发送功能模块发送read_buf数据,不关心数据发送成功还是失败;
+2、sys.subscribe("RECV_DATA_FROM_SERVER", recv_data_from_server_proc),订阅RECV_DATA_FROM_SERVER消息,处理消息携带的数据;
+]]
+
+
+-- 使用UART1
+local UART_ID = 1
+-- 串口接收数据缓冲区
+local read_buf = ""
+
+-- 将前缀prefix和数据data拼接
+-- 然后末尾增加回车换行两个字符,通过uart发送出去,方便在PC端换行显示查看
+local function recv_data_from_server_proc(prefix, data)
+    uart.write(UART_ID, prefix..data.."\r\n")
+end
+
+
+local function concat_timeout_func()
+    -- 如果存在尚未处理的串口缓冲区数据;
+    -- 将数据通过publish通知其他应用功能模块处理;
+    -- 然后清空本文件的串口缓冲区数据
+    if read_buf:len() > 0 then
+        sys.publish("SEND_DATA_REQ", "uart", read_buf)
+        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)
+
+-- 订阅"RECV_DATA_FROM_SERVER"消息的处理函数recv_data_from_server_proc
+-- 收到"RECV_DATA_FROM_SERVER"消息后,会执行函数recv_data_from_server_proc
+sys.subscribe("RECV_DATA_FROM_SERVER", recv_data_from_server_proc)

+ 142 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/udp/udp_client_main.lua

@@ -0,0 +1,142 @@
+--[[
+@module  udp_client_receiver
+@summary udp client socket主应用功能模块
+@version 1.0
+@date    2025.07.31
+@author  孟伟
+@usage
+本文件为udp client socket主应用功能模块,核心业务逻辑为:
+1、创建一个udp client socket,连接server;
+2、处理连接异常,出现异常后执行重连动作;
+3、调用udp_client_receiver和udp_client_sender中的外部接口,进行数据收发处理;
+
+本文件没有对外接口,直接在main.lua中require "udp_client_main"就可以加载运行;
+]]
+
+local libnet = require "libnet"
+
+-- 加载udp client socket数据接收功能模块
+local udp_client_receiver = require "udp_client_receiver"
+-- 加载udp client socket数据发送功能模块
+local udp_client_sender = require "udp_client_sender"
+
+-- 电脑访问:https://netlab.luatos.com/
+-- 点击 打开UDP 按钮,会创建一个UDP server
+-- 将server的地址和端口赋值给下面这两个变量
+local SERVER_ADDR = "112.125.89.8"
+local SERVER_PORT = 45692
+
+-- udp_client_main的任务名
+local TASK_NAME = udp_client_sender.TASK_NAME
+
+
+-- 处理未识别的消息
+local function udp_client_main_cbfunc(msg)
+	log.info("udp_client_main_cbfunc", msg[1], msg[2], msg[3], msg[4])
+end
+
+-- udp client socket的任务处理函数
+local function udp_client_main_task_func()
+
+    local socket_client
+    local result, para1, para2
+
+    while true do
+        -- 如果当前时间点设置的默认网卡还没有连接成功,一直在这里循环等待
+        while not socket.adapter(socket.dft()) do
+            log.warn("sntp_task_func", "wait IP_READY", socket.dft())
+            -- 在此处阻塞等待默认网卡连接成功的消息"IP_READY"
+            -- 或者等待1秒超时退出阻塞等待状态;
+            -- 注意:此处的1000毫秒超时不要修改的更长;
+            -- 因为当使用libnetif.set_priority_order配置多个网卡连接外网的优先级时,会隐式的修改默认使用的网卡
+            -- 当libnetif.set_priority_order的调用时序和此处的socket.adapter(socket.dft())判断时序有可能不匹配
+            -- 此处的1秒,能够保证,即使时序不匹配,也能1秒钟退出阻塞状态,再去判断socket.adapter(socket.dft())
+            sys.waitUntil("IP_READY", 1000)
+        end
+
+        -- 检测到了IP_READY消息
+        log.info("udp_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("udp_client_main_task_func", "socket.create error")
+            goto EXCEPTION_PROC
+        end
+
+        -- 配置socket client对象为udp client
+        result = socket.config(socket_client, nil, true)
+        -- 如果配置失败
+        if not result then
+            log.error("udp_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("udp_client_main_task_func", "libnet.connect error")
+            goto EXCEPTION_PROC
+        end
+
+        log.info("udp_client_main_task_func", "libnet.connect success")
+
+        -- 数据收发以及网络连接异常事件总处理逻辑
+        while true do
+            -- 数据接收处理(接收处理必须写在libnet.wait之前,因为老版本的内核固件要求必须这样,新版本的内核固件没这个要求,为了不出问题,写在libnet.wait之前就行了)
+            -- 如果处理失败,则退出循环
+            if not udp_client_receiver.proc(socket_client) then
+                log.error("udp_client_main_task_func", "udp_client_receiver.proc error")
+                break
+            end
+
+            -- 数据发送处理
+            -- 如果处理失败,则退出循环
+            if not udp_client_sender.proc(TASK_NAME, socket_client) then
+                log.error("udp_client_main_task_func", "udp_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, 在udp_client_sender.lua中会发布事件socket.EVENT
+			result, para1, para2 = libnet.wait(TASK_NAME, 15000, socket_client)
+            log.info("udp_client_main_task_func", "libnet.wait", result, para1, para2)
+
+			-- 如果连接异常,则退出循环
+			if not result then
+				log.warn("udp_client_main_task_func", "connection exception")
+				break
+            end
+        end
+
+
+        -- 出现异常
+        ::EXCEPTION_PROC::
+
+        -- 数据发送应用模块对来不及发送的数据做清空和通知失败处理
+        udp_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的主函数udp_client_main_task_func
+sysplus.taskInitEx(udp_client_main_task_func, TASK_NAME, udp_client_main_cbfunc)
+

+ 90 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/udp/udp_client_receiver.lua

@@ -0,0 +1,90 @@
+--[[
+@module  udp_client_receiver
+@summary udp client socket数据接收应用功能模块
+@version 1.0
+@date    2025.07.31
+@author  孟伟
+@usage
+本文件为udp client socket数据接收应用功能模块,核心业务逻辑为:
+从内核读取接收到的数据,然后将数据发送给其他应用功能模块做进一步处理;
+
+本文件的对外接口有2个:
+1、udp_client_receiver.proc(socket_client):数据接收应用逻辑处理入口,在udp_client_main.lua中调用;
+2、sys.publish("RECV_DATA_FROM_SERVER", "recv from udp server: ", data):
+   将接收到的数据通过消息"RECV_DATA_FROM_SERVER"发布出去;
+   需要处理数据的应用功能模块订阅处理此消息即可,本demo项目中uart_app.lua中订阅处理了本消息;
+]]
+
+local udp_client_receiver = {}
+
+-- socket数据接收缓冲区
+local recv_buff = nil
+
+--[[
+检查socket client是否收到数据,如果收到数据,读取并且处理完所有数据
+
+@api udp_client_receiver.proc(socket_client)
+
+@param1 socket_client userdata
+表示由socket.create接口创建的socket client对象;
+必须传入,不允许为空或者nil;
+
+@return1 result bool
+表示处理结果,成功为true,失败为false
+
+@usage
+--
+udp_client_receiver.proc(socket_client)
+]]
+function udp_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("udp_client_receiver.proc", "socket.rx error")
+            return false
+        end
+
+        -- 如果读取到了数据, used()就必然大于0, 进行处理
+        if recv_buff:used() > 0 then
+            log.info("udp_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 udp server: ", data)
+
+            -- 接收到数据,通知网络环境检测看门狗功能模块进行喂狗
+            sys.publish("FEED_NETWORK_WATCHDOG")
+
+            -- 清空socket数据接收缓冲区中的数据
+            recv_buff:del()
+            -- 读取成功,但是读出来的数据为空,表示已经没有数据可读,可以退出循环了
+        else
+            break
+        end
+    end
+
+    return true
+end
+
+return udp_client_receiver

+ 135 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/socket/client/long_connection/udp/udp_client_sender.lua

@@ -0,0 +1,135 @@
+--[[
+@module  udp_client_sender
+@summary udp client socket数据发送应用功能模块
+@version 1.0
+@date    2025.07.31
+@author  孟伟
+@usage
+本文件为udp client socket数据发送应用功能模块,核心业务逻辑为:
+1、sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)订阅"SEND_DATA_REQ"消息,将其他应用模块需要发送的数据存储到队列send_queue中;
+2、udp_client_main主任务调用udp_client_sender.proc接口,遍历队列send_queue,逐条发送数据到server;
+3、udp client socket和server之间的连接如果出现异常,udp_client_main主任务调用udp_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、udp_client_sender.proc:数据发送应用逻辑处理入口,在udp_client_main.lua中调用;
+3、udp_client_sender.exception_proc:数据发送应用逻辑异常处理入口,在udp_client_main.lua中调用;
+]]
+
+local udp_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 = {}
+
+-- udp_client_main的任务名
+udp_client_sender.TASK_NAME = "udp_client_main"
+
+-- "SEND_DATA_REQ"消息的处理函数
+local function send_data_req_proc_func(tag, data, cb)
+    -- 将原始数据增加前缀,然后插入到发送队列send_queue中
+    table.insert(send_queue, {data="send from "..tag..": "..data, cb=cb})
+    -- 通知udp_client_main主任务有数据需要发送
+    -- udp_client_main主任务如果处在libnet.wait调用的阻塞等待状态,就会退出阻塞状态
+    sysplus.sendMsg(udp_client_sender.TASK_NAME, socket.EVENT, 0)
+end
+
+--[[
+检查socket client是否需要发送数据,如果需要发送数据,读取并且发送完发送队列中的所有数据
+
+@api udp_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
+udp_client_sender.proc("tcp_client_main", socket_client)
+]]
+function udp_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("udp_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("udp_client_sender.proc", "buffer is full, wait for the next time")
+            table.insert(send_queue, 1, send_item)
+            return true
+        end
+
+        log.info("udp_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 udp_client_sender.exception_proc()
+
+@usage
+udp_client_sender.exception_proc()
+]]
+function udp_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 udp_client_sender

+ 1 - 1
module/Air780EPM/demo/socket/client/long_connection/main.lua

@@ -3,7 +3,7 @@
 @summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
 @version 1.0
 @date    2025.07.31
-@author  mw
+@author  孟伟
 @usage
 本demo演示的核心功能为:
 1、创建四路socket连接,详情如下

+ 6 - 1
module/Air780EPM/demo/socket/client/long_connection/netdrv/netdrv_4g.lua

@@ -3,7 +3,7 @@
 @summary “4G网卡”驱动模块
 @version 1.0
 @date    2025.07.31
-@author  mw
+@author  孟伟
 @usage
 本文件为4G网卡驱动模块,核心业务逻辑为:
 1、监听"IP_READY"和"IP_LOSE",在日志中进行打印;
@@ -26,3 +26,8 @@ end
 --也可以根据自己的项目需求,在消息处理函数中增加自己的业务逻辑控制,例如可以在连网状态发生改变时更新网络图标
 sys.subscribe("IP_READY", ip_ready_func)
 sys.subscribe("IP_LOSE", ip_lose_func)
+
+-- 设置默认网卡为socket.LWIP_GP
+-- 在Air780EPM上,内核固件运行起来之后,默认网卡就是socket.LWIP_GP
+-- 在单4G网卡使用场景下,下面这一行代码加不加都没有影响,为了和其他网卡驱动模块的代码风格保持一致,所以加上了
+socket.dft(socket.LWIP_GP)

+ 1 - 1
module/Air780EPM/demo/socket/client/long_connection/netdrv/netdrv_eth_spi.lua

@@ -3,7 +3,7 @@
 @summary “通过SPI外挂CH390H芯片的以太网卡”驱动模块
 @version 1.0
 @date    2025.07.31
-@author  mw
+@author  孟伟
 @usage
 本文件为“通过SPI外挂CH390H芯片的以太网卡”驱动模块 ,核心业务逻辑为:
 1、打开AirETH_1000配件板供电开关;

+ 1 - 1
module/Air780EPM/demo/socket/client/long_connection/netdrv/netdrv_multiple.lua

@@ -3,7 +3,7 @@
 @summary 多网卡(4G网卡、WIFI STA网卡、通过SPI外挂CH390H芯片的以太网卡)驱动模块
 @version 1.0
 @date    2025.07.31
-@author  mw
+@author  孟伟
 @usage
 本文件为多网卡驱动模块 ,核心业务逻辑为:
 1、调用libnetif.set_priority_order配置多网卡的控制参数以及优先级;

+ 1 - 1
module/Air780EPM/demo/socket/client/long_connection/netdrv_device.lua

@@ -3,7 +3,7 @@
 @summary 网络驱动设备功能模块
 @version 1.0
 @date    2025.07.31
-@author  mw
+@author  孟伟
 @usage
 本文件为网络驱动设备功能模块,核心业务逻辑为:根据项目需求,选择并且配置合适的网卡(网络适配器)
 1、netdrv_4g:socket.LWIP_GP,4G网卡;

+ 1 - 1
module/Air780EPM/demo/socket/client/long_connection/network_watchdog.lua

@@ -3,7 +3,7 @@
 @summary 网络环境检测看门狗功能模块
 @version 1.0
 @date    2025.07.31
-@author  mw
+@author  孟伟
 @usage
 本文件为网络环境检测看门狗功能模块,监控网络环境是否工作正常(设备和服务器双向通信正常,或者至少单向通信正常),核心业务逻辑为:
 1、启动一个网络环境检测看门狗task,等待其他socket网络应用功能模块来喂狗,如果喂狗超时,则控制软件重启;

+ 1 - 1
module/Air780EPM/demo/socket/client/long_connection/tcp_ssl/tcp_ssl_main.lua

@@ -3,7 +3,7 @@
 @summary tcp_ssl client socket主应用功能模块
 @version 1.0
 @date    2025.07.31
-@author  mw
+@author  孟伟
 @usage
 本文件为tcp_ssl client socket主应用功能模块,核心业务逻辑为:
 1、创建一个tcp_ssl client socket,连接server;

+ 1 - 1
module/Air780EPM/demo/socket/client/long_connection/tcp_ssl/tcp_ssl_receiver.lua

@@ -3,7 +3,7 @@
 @summary tcp_ssl client socket数据接收应用功能模块
 @version 1.0
 @date    2025.07.31
-@author  mw
+@author  孟伟
 @usage
 本文件为tcp_ssl client socket数据接收应用功能模块,核心业务逻辑为:
 从内核读取接收到的数据,然后将数据发送给其他应用功能模块做进一步处理;

+ 1 - 1
module/Air780EPM/demo/socket/client/long_connection/tcp_ssl/tcp_ssl_sender.lua

@@ -3,7 +3,7 @@
 @summary tcp_ssl client socket数据发送应用功能模块
 @version 1.0
 @date    2025.07.31
-@author  mw
+@author  孟伟
 @usage
 本文件为tcp_ssl client socket数据发送应用功能模块,核心业务逻辑为:
 1、sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)订阅"SEND_DATA_REQ"消息,将其他应用模块需要发送的数据存储到队列send_queue中;

+ 1 - 1
module/Air780EPM/demo/socket/client/long_connection/tcp_ssl_ca/sntp_app.lua

@@ -3,7 +3,7 @@
 @summary sntp时间同步应用功能模块
 @version 1.0
 @date    2025.07.31
-@author  mw
+@author  孟伟
 @usage
 本文件为sntp时间同步应用功能模块,核心业务逻辑为:
 1、连接ntp服务器进行时间同步;

+ 1 - 1
module/Air780EPM/demo/socket/client/long_connection/tcp_ssl_ca/tcp_ssl_ca_main.lua

@@ -3,7 +3,7 @@
 @summary tcp_ssl_ca client socket主应用功能模块
 @version 1.0
 @date    2025.07.31
-@author  mw
+@author  孟伟
 @usage
 本文件为tcp_ssl_ca client socket主应用功能模块,核心业务逻辑为:
 1、创建一个tcp_ssl_ca client socket,连接server;

+ 1 - 1
module/Air780EPM/demo/socket/client/long_connection/tcp_ssl_ca/tcp_ssl_ca_receiver.lua

@@ -3,7 +3,7 @@
 @summary tcp_ssl_ca client socket数据接收应用功能模块
 @version 1.0
 @date    2025.07.31
-@author  mw
+@author  孟伟
 @usage
 本文件为tcp_ssl_ca client socket数据接收应用功能模块,核心业务逻辑为:
 从内核读取接收到的数据,然后将数据发送给其他应用功能模块做进一步处理;

+ 1 - 1
module/Air780EPM/demo/socket/client/long_connection/tcp_ssl_ca/tcp_ssl_ca_sender.lua

@@ -3,7 +3,7 @@
 @summary tcp_ssl_ca client socket数据发送应用功能模块
 @version 1.0
 @date    2025.07.31
-@author  mw
+@author  孟伟
 @usage
 本文件为tcp_ssl_ca client socket数据发送应用功能模块,核心业务逻辑为:
 1、sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)订阅"SEND_DATA_REQ"消息,将其他应用模块需要发送的数据存储到队列send_queue中;

+ 1 - 1
module/Air780EPM/demo/socket/client/long_connection/timer_app.lua

@@ -3,7 +3,7 @@
 @summary 定时器应用功能模块
 @version 1.0
 @date    2025.07.31
-@author  mw
+@author  孟伟
 @usage
 本文件为定时器应用功能模块,核心业务逻辑为:
 创建一个5秒的循环定时器,每次产生一段数据,通知四个socket client进行处理;

+ 1 - 1
module/Air780EPM/demo/socket/client/long_connection/uart_app.lua

@@ -3,7 +3,7 @@
 @summary 串口应用功能模块
 @version 1.0
 @date    2025.07.31
-@author  mw
+@author  孟伟
 @usage
 本文件为串口应用功能模块,核心业务逻辑为:
 1、打开uart1,波特率115200,数据位8,停止位1,无奇偶校验位;

+ 1 - 1
module/Air780EPM/demo/socket/client/long_connection/udp/udp_client_receiver.lua

@@ -3,7 +3,7 @@
 @summary udp client socket数据接收应用功能模块
 @version 1.0
 @date    2025.07.31
-@author  mw
+@author  孟伟
 @usage
 本文件为udp client socket数据接收应用功能模块,核心业务逻辑为:
 从内核读取接收到的数据,然后将数据发送给其他应用功能模块做进一步处理;

+ 1 - 1
module/Air780EPM/demo/socket/client/long_connection/udp/udp_client_sender.lua

@@ -3,7 +3,7 @@
 @summary udp client socket数据发送应用功能模块
 @version 1.0
 @date    2025.07.31
-@author  mw
+@author  孟伟
 @usage
 本文件为udp client socket数据发送应用功能模块,核心业务逻辑为:
 1、sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)订阅"SEND_DATA_REQ"消息,将其他应用模块需要发送的数据存储到队列send_queue中;

+ 13 - 2
module/Air8000/demo/libnetif/http_test.lua

@@ -9,11 +9,22 @@
 1、循环进行http请求,测试不同网络切换时能否正常使用网络
 本文件没有对外接口,直接在main.lua中require "http_test"就可以加载运行;
 ]]
+-- 如果打算 MQTT+HTTP 同时测试,建议注释掉下段代码,否则会重复打印(MQTT 中也会打印)。
+libnetif.notify_status(function(net_type, adapter)
+    log.info("可以使用优先级更高的网络:", net_type, adapter)
+end)
+
 sys.taskInit(function()
+    -- sys.waitUntil("IP_READY")
     while 1 do
         sys.wait(6000)
-        log.info("http",
-            http.request("GET", "http://httpbin.air32.cn/bytes/4096", nil, nil).wait())
+        local code, headers, body = http.request("GET", "http://httpbin.air32.cn/bytes/4096", nil, nil,{ timeout = 3000}).wait()
+        if body then
+            log.info("http", "状态码: ", code, "body长度: ", #body)
+        else
+            log.info("http", "状态码: ", code)
+        end
     end
 end)
 
+

+ 0 - 540
module/Air8000/demo/libnetif/libnetif.lua

@@ -1,540 +0,0 @@
---[[
-@module libnetif
-@summary libnetif 控制网络优先级(以太网->WIFI->4G)根据优先级选择上网的网卡。简化开启多网融合的操作,4G作为数据出口给WIFI,以太网设备上网,以太网作为数据出口给WIFI,Air8000上网,WIFI作为数据出口给Air8000,以太网上网。
-@version 1.0
-@date    2025.06.26
-@author  wjq
-本文件的对外接口有3个:
-1、libnetif.setPriorityOrder(networkConfigs):设置网络优先级顺序并初始化对应网络,在net_app.lua中调用;
-2、libnetif.notifyStatus(cb_fnc):设置网络状态变化回调函数;
-3、libnetif.setproxy(adapter, main_adapter, ssid, password, power_en):配置网络代理实现多网融合,在net_app.lua中调用;
-]]
-local libnetif = {}
-
-dnsproxy = require("dnsproxy")
-dhcpsrv = require("dhcpsrv")
-httpdns = require("httpdns")
--- 设置pingip
-local wifi_ping_ip
-local eth_ping_ip
-
-
-local ping_time = 10000
--- 连接状态
-local connection_states = {
-    DISCONNECTED = 0,
-    CONNECTING = 1,
-    CONNECTED = 2,
-    OPENED = 3
-}
-
--- 状态回调函数
-local states_cbfnc = function(net_type) end
--- 当前优先级
-local current_priority = { socket.LWIP_ETH, socket.LWIP_STA, socket.LWIP_GP }
--- 连接状态
-local available = {
-    [socket.LWIP_STA] = connection_states.DISCONNECTED,
-    [socket.LWIP_ETH] = connection_states.DISCONNECTED,
-    [socket.LWIP_GP] = connection_states.DISCONNECTED,
-    [socket.LWIP_USER1] = connection_states.DISCONNECTED
-}
--- 当前使用的网卡
-local current_active = socket.LWIP_USER0
-
--- 网络类型转字符串
-local function type_to_string(net_type)
-    local type_map = {
-        [socket.LWIP_STA] = "WiFi",
-        [socket.LWIP_ETH] = "Ethernet",
-        [socket.LWIP_GP] = "4G",
-        [socket.LWIP_USER1] = "8101SPIETH"
-    }
-    return type_map[net_type] or "Unknown"
-end
-
--- 状态更改后重新设置默认网卡
-local function apply_priority()
-    -- 查找优先级最高的可用网络
-    for _, net_type in ipairs(current_priority) do
-        -- log.info("网卡顺序",type_to_string(net_type),available[net_type])
-        if available[net_type] == connection_states.CONNECTED then
-            -- 设置优先级高的网卡
-            log.info("设置网卡", type_to_string(net_type))
-            if current_active ~= net_type then
-                if current_active ~= socket.LWIP_USER0 then
-                    states_cbfnc(type_to_string(net_type)) -- 默认网卡改变的回调函数
-                end
-                socket.dft(net_type)
-                current_active = net_type
-            end
-            break
-        end
-    end
-end
-
---打开以太网Wan功能
-local function setup_eth(config)
-    eth_ping_ip = config.ping_ip
-    if type(config.ping_time) == "number" then
-        ping_time = config.ping_time
-    end
-    log.info("初始化以太网")
-    -- 打开CH390供电
-    gpio.setup(config.pwrpin, 1, gpio.PULLUP)
-    sys.wait(100)
-    if config.tp == nil then
-        log.info("8101以太网")
-        netdrv.setup(socket.LWIP_ETH)
-    else
-        log.info("config.opts.spi",config.opts.spi,",config.type",config.tp)
-        -- 配置SPI和初始化网络驱动
-        local result = spi.setup(config.opts.spi, -- spi id
-        nil, 0, -- CPHA
-        0, -- CPOL
-        8, -- 数据宽度
-        51200000 -- ,--波特率
-        )
-        log.info("main", "open spi", result)
-        if result ~= 0 then -- 返回值为0,表示打开成功
-            log.info("main", "spi open error", result)
-            gpio.close(config.pwrpin)
-            return false
-        end
-        -- 初始化指定netdrv设备,
-        -- socket.LWIP_ETH 网络适配器编号
-        -- netdrv.CH390外挂CH390
-        -- SPI ID 1, 片选 GPIO12
-        netdrv.setup(socket.LWIP_ETH, config.tp, config.opts)
-    end
-    netdrv.dhcp(socket.LWIP_ETH, true)
-    available[socket.LWIP_ETH] = connection_states.OPENED
-    log.info("以太网初始化完成")
-    return true
-end
-
---打开8101spi以太网Wan功能
-local function setup_eth_user1(config)
-    eth_ping_ip = config.ping_ip
-    if type(config.ping_time) == "number" then
-        ping_time = config.ping_time
-    end
-    log.info("初始化以太网")
-    -- 打开CH390供电
-    gpio.setup(config.pwrpin, 1, gpio.PULLUP)
-    sys.wait(100)
-    log.info("config.opts.spi", config.opts.spi, ",config.type", config.tp)
-    -- 配置SPI和初始化网络驱动
-    local result = spi.setup(config.opts.spi,     -- spi id
-        nil, 0,                                   -- CPHA
-        0,                                        -- CPOL
-        8,                                        -- 数据宽度
-        51200000                                  -- ,--波特率
-    )
-    log.info("main", "open spi", result)
-    if result ~= 0 then     -- 返回值为0,表示打开成功
-        log.info("main", "spi open error", result)
-        gpio.close(config.pwrpin)
-        return false
-    end
-    -- 初始化指定netdrv设备,
-    -- socket.LWIP_ETH 网络适配器编号
-    -- netdrv.CH390外挂CH390
-    -- SPI ID 1, 片选 GPIO12
-    netdrv.setup(socket.LWIP_USER1, config.tp, config.opts)
-    netdrv.dhcp(socket.LWIP_USER1, true)
-    available[socket.LWIP_USER1] = connection_states.OPENED
-    log.info("以太网初始化完成")
-    return true
-end
-
---连接wifi(STA模式)
-local function set_wifi_info(config)
-    wifi_ping_ip = config.ping_ip
-    if type(config.ping_time) == "number" then
-        ping_time = config.ping_time
-    end
-    log.info("WiFi名称:", config.ssid)
-    log.info("密码     :", config.password)
-    log.info("ping_ip  :", config.ping_ip)
-    wlan.init()
-
-    -- 尝试连接Wi-Fi,并处理可能出现的错误
-    local success = wlan.connect(config.ssid, config.password)
-    if not success then
-        log.error("WiFi连接失败")
-        return false
-    end
-    available[socket.LWIP_STA] = connection_states.OPENED
-    log.info("WiFi STA初始化完成")
-    return true
-end
-
---[[
-设置网络优先级,相应网卡获取到ip且网络正常视为网卡可用,丢失ip视为网卡不可用.
-例:插入网线且能够dns域名解析获取到baidu.com的ip,网卡状态切换为可用。拔掉网线网卡状态切换为不可用
-@api libnetif.set_priority_order(new_priority)
-@table 网络优先级列表
-@return boolean 成功返回true,失败返回false
-@usage
-libnetif.set_priority_order({
-    { -- 最高优先级网络
-        WIFI = { -- WiFi配置
-            ssid = "your_ssid",       -- WiFi名称(string)
-            password = "your_pwd",    -- WiFi密码(string)
-            ping_ip = "112.125.89.8", -- 连通性检测IP(选填参数),默认使用httpdns获取baidu.com的ip作为判断条件,
-                                      -- 注:如果填写ip,则ping通作为判断网络是否可用的条件,
-                                      -- 所以需要根据网络环境填写内网或者外网ip,
-                                      -- 填写外网ip的话要保证外网ip始终可用,
-                                      -- 填写局域网ip的话要确保相应ip固定且能够被ping通
-            ping_time = 10000         -- 填写ping_ip且未ping通时的检测间隔(ms, 可选,默认为10秒)
-                                      -- 定时ping将会影响模块功耗,使用低功耗模式的话可以适当延迟间隔时间
-        }
-    },
-    { -- 次优先级网络
-        ETHERNET = { -- 以太网配置
-            pwrpin = 140,             -- 供电使能引脚(number)
-            ping_ip = "112.125.89.8", -- 连通性检测IP(选填参数),默认使用httpdns获取baidu.com的ip作为判断条件,
-                                      -- 注:如果填写ip,则ping通作为判断网络是否可用的条件,
-                                      -- 所以需要根据网络环境填写内网或者外网ip,
-                                      -- 填写外网ip的话要保证外网ip始终可用,
-                                      -- 填写局域网ip的话要确保相应ip固定且能够被ping通
-            ping_time = 10000         -- 填写ping_ip且未ping通时的检测间隔(ms, 可选,默认为10秒)
-                                      -- 定时ping将会影响模块功耗,使用低功耗模式的话可以适当延迟间隔时间
-            tp = netdrv.CH390         -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
-            opts = { spi = 1, cs = 12 }   -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
-        }
-    },
-    { -- 最低优先级网络
-        LWIP_GP = true  -- 启用4G网络
-    }
-})
-]]
-function libnetif.set_priority_order(networkConfigs)
-    --判断表中数据个数
-    if #networkConfigs <2 then
-        log.error("请至少添加两个网络")
-        return false
-    end
-    local new_priority = {}
-    for _, config in ipairs(networkConfigs) do
-        if type(config.WIFI) == "table" then
-            --开启wifi
-            local res = set_wifi_info(config.WIFI)
-            if res == false then
-                log.error("wifi连接失败")
-                return false
-            end
-            table.insert(new_priority, socket.LWIP_STA)
-        end
-        if type(config.ETHUSER1) == "table" then
-            --开启以太网
-            local res = setup_eth_user1(config.ETHUSER1)
-            if res == false then
-                log.error("以太网打开失败")
-                return false
-            end
-            table.insert(new_priority, socket.LWIP_USER1)
-        end
-        if type(config.ETHERNET) == "table" then
-            --开启以太网
-            local res = setup_eth(config.ETHERNET)
-            if res == false then
-                log.error("以太网打开失败")
-                return false
-            end
-            table.insert(new_priority, socket.LWIP_ETH)
-        end
-        if config.LWIP_GP then
-            --开启4G
-            table.insert(new_priority, socket.LWIP_GP)
-        end
-    end
-
-    -- 设置新优先级
-    current_priority = new_priority
-    apply_priority()
-
-    return true
-end
-
---[[
-设置网络状态变化回调函数
-@api libnetif.notify_status(cb_fnc)
-@function 回调函数
-@usage
-    libnetif.notify_status(function(net_type)
-    log.info("可以使用优先级更高的网络:", net_type)
-    end)
-]]
-function libnetif.notify_status(cb_fnc)
-    log.info("notify_status", type(cb_fnc))
-    if type(cb_fnc) ~= "function" then
-        log.error("notify_status设置错误,请传入一个函数")
-        return
-    end
-    states_cbfnc = cb_fnc
-end
-
---[[
-设置多网融合模式,例如4G作为数据出口给WIFI或以太网设备上网
-@api libnetif.setproxy(adapter, main_adapter, ssid, password, power_en)
-@adapter 需要使用网络的网卡,例如socket.LWIP_ETH
-@adapter 提供网络的网卡,例如socket.LWIP_GP
-@string wifi名称,没有用到wifi可不填
-@string wifi密码,没有用到wifi可不填
-@int 以太网模块的供电使能引脚(gpio编号),没有用到以太网模块可不填
-@table 以太网模块参数(选填参数),仅spi方式外挂以太网时需要填写。
-@usage
-    --典型应用:
-    -- 4G作为出口供WiFi和以太网设备上网
-    libnetif.setproxy(socket.LWIP_AP, socket.LWIP_GP, "test", "HZ88888888", nil)
-    libnetif.setproxy(socket.LWIP_ETH, socket.LWIP_GP, nil, nil, 140, {tp = netdrv.CH390, opts = { spi = 1, cs = 12}})
-    -- 以太网作为出口供WiFi设备上网
-    libnetif.setproxy(socket.LWIP_AP, socket.LWIP_ETH, "Hotspot", "password123", 13)
-    -- 4G作为出口供以太网设备上网
-    libnetif.setproxy(socket.LWIP_ETH, socket.LWIP_GP, nil, nil, 140, {tp = netdrv.CH390, opts = { spi = 1, cs = 12}})
-]]
-function libnetif.setproxy(adapter, main_adapter, ssid, password, power_en, eth_config)
-    if adapter == socket.LWIP_ETH then
-        log.info("ch390", "打开LDO供电", power_en)
-        gpio.setup(power_en, 1, gpio.PULLUP)
-        -- 打开LAN功能
-        -- sys.wait(3000)
-        -- 配置 SPI 参数,Air8000 使用 SPI 接口与以太网模块进行通信。
-        local result = spi.setup(1, -- spi id
-            nil, 0,                 -- CPHA
-            0,                      -- CPOL
-            8,                      -- 数据宽度
-            51200000                -- ,--波特率
-        )
-        log.info("main", "open spi", result)
-        if result ~= 0 then -- 返回值为 0,表示打开成功
-            log.error("main", "spi open error", result)
-            gpio.close(power_en)
-            return false
-        end
-        -- 初始化以太网,Air8000 指定使用 CH390 芯片。
-        log.info("netdrv", "初始化以太网")
-        netdrv.setup(socket.LWIP_ETH, eth_config.tp, eth_config.opts)
-        log.info("netdrv", "等待以太网就绪")
-        sys.wait(1000)
-        -- 设置以太网的 IP 地址、子网掩码、网关地址
-        netdrv.ipv4(socket.LWIP_ETH, "192.168.5.1", "255.255.255.0", "0.0.0.0")
-        -- 获取以太网网络状态,连接后返回 true,否则返回 false,如果不存在就返回 nil。
-        local count = 1
-        while netdrv.ready(socket.LWIP_ETH) ~= true do
-            if count > 600 then
-                log.error("以太网连接超时,请检查配置")
-                gpio.close(power_en)
-                return false
-            end
-            count = count + 1
-            -- log.info("netdrv", "等待以太网就绪") -- 若以太网设备没有连上,可打开此处注释排查。
-            sys.wait(100)
-        end
-        log.info("netdrv", "以太网就绪")
-        -- 创建 DHCP 服务器,为连接到以太网的设备分配 IP 地址。
-        log.info("netdrv", "创建dhcp服务器, 供以太网使用")
-        dhcpsrv.create({ adapter = socket.LWIP_ETH, gw = { 192, 168, 5, 1 } })
-        -- 创建 DNS 代理服务,使得以太网接口上的设备可以通过 4G 网络访问互联网。
-        log.info("netdrv", "创建dns代理服务, 供以太网使用")
-    elseif adapter == socket.LWIP_AP then
-        wlan.setMode(wlan.APSTA)
-        -- 打开AP功能,设置混合模式
-        log.info("执行AP创建操作", airlink.ready(), "正常吗?")
-        -- 设置 WiFi 热点的名称和密码
-        if type(ssid)  then
-            
-        end
-        wlan.createAP(ssid, password)
-        -- 设置 AP 的 IP 地址、子网掩码、网关地址
-        netdrv.ipv4(socket.LWIP_AP, "192.168.4.1", "255.255.255.0", "0.0.0.0")
-        -- 获取 WiFi AP 网络状态,连接后返回 true,否则返回 false,如果不存在就返回 nil。
-        log.info("netdrv", "等待AP就绪")
-        local count = 1
-        while netdrv.ready(socket.LWIP_AP) ~= true do
-            -- log.info("netdrv", "等待AP就绪")
-            if count > 600 then
-                log.error("AP创建超时,请检查配置")
-                return false
-            end
-            sys.wait(100)
-            count = count + 1
-        end
-        -- 创建 DHCP 服务器,为连接到 WiFi AP 的设备分配 IP 地址。
-        log.info("netdrv", "创建dhcp服务器, 供AP使用")
-        dhcpsrv.create({ adapter = socket.LWIP_AP })
-
-
-        log.info("netdrv", "创建dns代理服务, 供AP使用")
-    end
-
-
-    if main_adapter == socket.LWIP_ETH and available[socket.LWIP_ETH] == connection_states.DISCONNECTED then
-        -- 打开WAN功能
-        log.info("ch390", "打开LDO供电", power_en)
-        -- 打开CH390供电
-        gpio.setup(power_en, 1, gpio.PULLUP)
-        sys.wait(100)
-        if eth_config.tp == nil then
-            log.info("8101以太网")
-            netdrv.setup(socket.LWIP_ETH)
-        else
-            log.info("config.opts.spi", eth_config.opts.spi, ",config.type", eth_config.tp)
-            -- 配置SPI和初始化网络驱动
-            local result = spi.setup(eth_config.opts.spi, -- spi id
-                nil, 0,                           -- CPHA
-                0,                                -- CPOL
-                8,                                -- 数据宽度
-                51200000                          -- ,--波特率
-            )
-            log.info("main", "open spi", result)
-            if result ~= 0 then -- 返回值为0,表示打开成功
-                log.info("main", "spi open error", result)
-                gpio.close(eth_config.pwrpin)
-                return false
-            end
-            -- 初始化指定netdrv设备,
-            local success = netdrv.setup(socket.LWIP_ETH, eth_config.tp, eth_config.opts)
-            if not success then
-                log.error("以太网初始化失败")
-                return false
-            end
-        end
-        netdrv.dhcp(socket.LWIP_ETH, true)
-        local count = 1
-        while 1 do
-            local ip = netdrv.ipv4(socket.LWIP_ETH)
-            if ip and ip ~= "0.0.0.0" then break end
-            if count > 600 then
-                log.error("以太网连接超时,请检查配置")
-                gpio.close(power_en)
-                return false
-            end
-            sys.wait(100)
-            count = count + 1
-        end
-    elseif main_adapter == socket.LWIP_STA and available[socket.LWIP_STA] == connection_states.DISCONNECTED then
-        -- 打开STA功能,设置混合模式
-        wlan.init()
-        wlan.setMode(wlan.APSTA)
-        -- 尝试连接Wi-Fi,并处理可能出现的错误
-        wlan.connect(ssid, password)
-        -- 等待获取IP地址
-        local count = 1
-        while 1 do
-            local ip = netdrv.ipv4(socket.LWIP_STA)
-            if ip and ip ~= "0.0.0.0" then
-                log.info("WiFi STA已连接,IP:", ip)
-                return false
-            end
-            if count > 600 then
-                log.error("WiFi STA连接超时,请检查配置")
-                return false
-            end
-            sys.wait(100)
-            count = count + 1
-        end
-
-        log.info("WiFi STA初始化完成")
-    end
-    dnsproxy.setup(adapter, main_adapter)
-    netdrv.napt(main_adapter)
-    return true
-end
---httpdns域名解析测试
-local function http_dnstest(adaptertest)
-    local ip = httpdns.ali("baidu.com", { adapter = adaptertest, timeout = 3000 })
-    if ip ~= nil then
-        available[adaptertest] = connection_states.CONNECTED
-    end
-    log.info("httpdns", "baidu.com", ip)
-end
--- ping操作
-local function ping_request(adaptertest)
-    log.info("dns_request",type_to_string(adaptertest))
-    if adaptertest == socket.LWIP_ETH or adaptertest == socket.LWIP_USER1 then
-        if eth_ping_ip == nil then
-           http_dnstest(adaptertest)
-        else
-            icmp.setup(adaptertest)
-            icmp.ping(adaptertest, eth_ping_ip)
-        end
-    end
-    if adaptertest == socket.LWIP_STA then
-        if wifi_ping_ip == nil then
-            http_dnstest(adaptertest)
-        else
-            icmp.setup(adaptertest)
-            icmp.ping(adaptertest, wifi_ping_ip)
-        end
-    end
-    if adaptertest == socket.LWIP_GP then
-        if eth_ping_ip ~= nil then
-            icmp.setup(adaptertest)
-            icmp.ping(adaptertest, eth_ping_ip)
-        elseif wifi_ping_ip ~= nil then
-            icmp.setup(adaptertest)
-            icmp.ping(adaptertest, wifi_ping_ip)
-        else
-            http_dnstest(adaptertest)
-        end
-    end
-    apply_priority()
-end
--- 网卡上线回调函数
-local function ip_ready_handle(ip, adapter)
-    log.info("ip_ready_handle", ip, type_to_string(adapter))
-    -- 需要ping操作,ping通后认为网络可用
-    available[adapter] = connection_states.CONNECTING
-    -- ping_request(adapter)
-end
--- 网卡下线回调函数
-local function ip_lose_handle(adapter)
-    log.info("ip_lose_handle", type_to_string(adapter))
-    available[adapter] = connection_states.DISCONNECTED
-    if current_active == adapter then
-        log.info(type_to_string(adapter) .. " 失效,切换到其他网络")
-        apply_priority()
-    end
-end
-
---CONNECTING的网卡需要定时ping
-sys.taskInit(function()
-    while true do
-        for _, net_type in ipairs(current_priority) do
-            -- log.info("网卡顺序",type_to_string(net_type),available[net_type])
-            if available[net_type] == connection_states.CONNECTING then
-                ping_request(net_type)
-                log.info(type_to_string(net_type) .. "网卡未ping通,需要定时ping")
-                sys.wait(ping_time)
-            end
-        end
-        sys.wait(1000)
-    end
-end)
-
-
---[[
-对正常状态的网卡进行ping测试
-@api libnetif.ping_test()
-]]
-function libnetif.ping_test()
-    for _, net_type in ipairs(current_priority) do
-        if available[net_type] == connection_states.CONNECTED then
-            available[net_type] = connection_states.CONNECTING
-        end
-    end
-end
-
-
-sys.subscribe("PING_RESULT", function(id, time, dst)
-    log.info("ping", type_to_string(id), time, dst);
-    available[id] = connection_states.CONNECTED
-    apply_priority()
-end)
--- 订阅网络状态变化的消息
-sys.subscribe("IP_READY", ip_ready_handle)
-sys.subscribe("IP_LOSE", ip_lose_handle)
-return libnetif

+ 2 - 2
module/Air8000/demo/libnetif/mqtt_test.lua

@@ -28,8 +28,8 @@ local mqttc = nil
 
 mqtt_state = false
 
-libnetif.notify_status(function(net_type)
-    log.info("可以使用优先级更高的网络:", net_type)
+libnetif.notify_status(function(net_type, adapter)
+    log.info("可以使用优先级更高的网络:", net_type, adapter)
     sys.publish("mqtt_pub", "close")  --关闭现在的mqtt链接
 end)
 

+ 11 - 1
module/Air8000/demo/libnetif/net_app.lua

@@ -38,5 +38,15 @@ sys.taskInit(function()
     })
     sys.wait(5000)
     --设置多网融合功能
-    -- libnetif.setproxy(socket.LWIP_ETH, socket.LWIP_STA, "test", "HZ88888888", 140 ,{tp = netdrv.CH390, opts = { spi = 1, cs = 12}})
+    -- if  libnetif.setproxy(socket.LWIP_AP, socket.LWIP_ETH, {
+    --     ssid = "test2",                -- WiFi名称(string),网卡包含wifi时填写
+    --     password = "HZ88888888",        -- WiFi密码(string),网卡包含wifi时填写
+    --     ethpower_en = 140,               -- 以太网模块的pwrpin引脚(gpio编号)
+    --     tp = netdrv.CH390,               -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
+    --     opts = { spi = 1, cs = 12},      -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
+    --     -- adapter_addr = "192.168.2.1",    -- adapter网卡的ip地址(选填),需要自定义ip和网关ip时填写
+    --     -- adapter_gw= { 192, 168, 2, 1 },   -- adapter网卡的网关地址(选填),需要自定义ip和网关ip时填写
+    -- }) then
+    --     log.info("libnetif", "setproxy success")
+    -- end
 end)

+ 33 - 0
module/Air8000/demo/libnetif/readme.md

@@ -0,0 +1,33 @@
+
+## 演示功能概述
+1、设置网络优先级功能,根据优先级自动切换可用网络
+
+2、验证网络是否正常切换,http功能是否可用
+
+3、设置多网融合模式,例如以太网作为数据出口给WIFI设备上网
+
+## 演示硬件环境
+
+1、Air8000开发板
+2、网线1根(一端接开发板软件rj45接口,一端接路由器)
+![](https://docs.openluat.com/air8000/luatos/app/multinetwork/multinetwork2/image/OBqobf8qXoeHnwxi7K6ckkDRnlb.png)
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+2、烧录demo代码 [烧录教程](https://docs.openluat.com/air8000/luatos/common/download/)
+
+## 演示核心步骤
+
+1、搭建好硬件环境
+
+2、成功连接wifi,http请求功能正常
+
+3、测试网络切换功能:
+- 插入网线
+- 关闭wifi
+- 打开wifi并拔掉网线
+- 网络可以正常切换,http请求均正常
+
+4、测试多网融合功能时,连接好网线,其他设备连接模块的wifi热点。测试网络是否正常

BIN
module/Air8000/project/School_To_Home/core/LuatOS-SoC_V2005_Air8000.soc


+ 2 - 2
module/Air8101/demo/libnetif/http_test.lua

@@ -11,8 +11,8 @@
 ]]
 
 -- 如果打算 MQTT+HTTP 同时测试,建议注释掉下段代码,否则会重复打印(MQTT 中也会打印)。
-libnetif.notify_status(function(net_type)
-    log.info("可以使用优先级更高的网络:", net_type)
+libnetif.notify_status(function(net_type, adapter)
+    log.info("可以使用优先级更高的网络:", net_type, adapter)
 end)
 
 sys.taskInit(function()

+ 0 - 699
module/Air8101/demo/libnetif/libnetif.lua

@@ -1,699 +0,0 @@
---[[
-@module libnetif
-@summary libnetif 控制网络优先级(以太网->WIFI->4G)根据优先级选择上网的网卡。简化开启多网融合的操作,4G作为数据出口给WIFI,以太网设备上网,以太网作为数据出口给WIFI,Air8000上网,WIFI作为数据出口给Air8000,以太网上网。
-@version 1.0
-@date    2025.06.26
-@author  wjq
-@usage
-本文件的对外接口有4个:
-1、libnetif.set_priority_order(networkConfigs):设置网络优先级顺序并初始化对应网络
-2、libnetif.notify_status(cb_fnc):设置网络状态变化回调函数
-3、libnetif.setproxy(adapter, main_adapter,other_configs):配置网络代理实现多网融合
-4、libnetif.check_network_status(interval),检测间隔时间ms(选填),不填时只检测一次,填写后将根据间隔时间循环检测,会提高模块功耗
-]]
-local libnetif = {}
-
-dnsproxy = require("dnsproxy")
-dhcpsrv = require("dhcpsrv")
-httpdns = require("httpdns")
--- 设置pingip
-local wifi_ping_ip
-local eth_ping_ip
-
-
-local ping_time = 10000
--- 连接状态
-local connection_states = {
-    DISCONNECTED = 0,
-    CONNECTING = 1,
-    CONNECTED = 2,
-    OPENED = 3
-}
-
--- 状态回调函数
-local states_cbfnc = function(net_type) end
--- 当前优先级
-local current_priority = { socket.LWIP_ETH, socket.LWIP_STA, socket.LWIP_GP }
--- 连接状态
-local available = {
-    [socket.LWIP_STA] = connection_states.DISCONNECTED,
-    [socket.LWIP_ETH] = connection_states.DISCONNECTED,
-    [socket.LWIP_GP] = connection_states.DISCONNECTED,
-    [socket.LWIP_USER1] = connection_states.DISCONNECTED
-}
--- 当前使用的网卡
-local current_active = socket.LWIP_USER0
-
--- 网络类型转字符串
-local function type_to_string(net_type)
-    local type_map = {
-        [socket.LWIP_STA] = "WiFi",
-        [socket.LWIP_ETH] = "Ethernet",
-        [socket.LWIP_GP] = "4G",
-        [socket.LWIP_USER1] = "8101SPIETH"
-    }
-    return type_map[net_type] or "Unknown"
-end
-
--- 状态更改后重新设置默认网卡
-local function apply_priority()
-    local usable = false
-    -- 查找优先级最高的可用网络
-    for _, net_type in ipairs(current_priority) do
-        -- log.info("网卡顺序",type_to_string(net_type),available[net_type])
-        if available[net_type] == connection_states.CONNECTED then
-            usable = true
-            -- 设置优先级高的网卡
-            if current_active ~= net_type then
-                log.info("设置网卡", type_to_string(net_type))
-                if current_active ~= socket.LWIP_USER0 then
-                    states_cbfnc(type_to_string(net_type), net_type) -- 默认网卡改变的回调函数
-                end
-                socket.dft(net_type)
-                current_active = net_type
-            end
-            break
-        end
-    end
-    if usable == false then
-        states_cbfnc(nil, -1)
-    end
-end
-
---打开以太网Wan功能
-local function setup_eth(config)
-    eth_ping_ip = config.ping_ip
-    if type(config.ping_time) == "number" then
-        ping_time = config.ping_time
-    end
-    log.info("初始化以太网")
-    available[socket.LWIP_ETH] = connection_states.OPENED
-    -- 打开CH390供电
-    gpio.setup(config.pwrpin, 1, gpio.PULLUP)
-    sys.wait(100)
-    if config.tp == nil then
-        log.info("8101以太网")
-        netdrv.setup(socket.LWIP_ETH)
-    else
-        log.info("config.opts.spi",config.opts.spi,",config.type",config.tp)
-        -- 配置SPI和初始化网络驱动
-        local result = spi.setup(config.opts.spi, -- spi id
-        nil, 0, -- CPHA
-        0, -- CPOL
-        8, -- 数据宽度
-        51200000 -- ,--波特率
-        )
-        log.info("main", "open spi", result)
-        if result ~= 0 then -- 返回值为0,表示打开成功
-            log.info("main", "spi open error", result)
-            gpio.close(config.pwrpin)
-            return false
-        end
-        -- 初始化指定netdrv设备,
-        -- socket.LWIP_ETH 网络适配器编号
-        -- netdrv.CH390外挂CH390
-        -- SPI ID 1, 片选 GPIO12
-        netdrv.setup(socket.LWIP_ETH, config.tp, config.opts)
-    end
-    netdrv.dhcp(socket.LWIP_ETH, true)
-    log.info("以太网初始化完成")
-    return true
-end
-
---打开8101spi以太网Wan功能
-local function setup_eth_user1(config)
-    eth_ping_ip = config.ping_ip
-    if type(config.ping_time) == "number" then
-        ping_time = config.ping_time
-    end
-    log.info("初始化以太网")
-    available[socket.LWIP_USER1] = connection_states.OPENED
-    -- 打开CH390供电
-    gpio.setup(config.pwrpin, 1, gpio.PULLUP)
-    sys.wait(100)
-    log.info("config.opts.spi", config.opts.spi, ",config.type", config.tp)
-    -- 配置SPI和初始化网络驱动
-    local result = spi.setup(config.opts.spi,     -- spi id
-        nil, 0,                                   -- CPHA
-        0,                                        -- CPOL
-        8,                                        -- 数据宽度
-        51200000                                  -- ,--波特率
-    )
-    log.info("main", "open spi", result)
-    if result ~= 0 then     -- 返回值为0,表示打开成功
-        log.info("main", "spi open error", result)
-        gpio.close(config.pwrpin)
-        return false
-    end
-    -- 初始化指定netdrv设备,
-    -- socket.LWIP_ETH 网络适配器编号
-    -- netdrv.CH390外挂CH390
-    -- SPI ID 1, 片选 GPIO12
-    netdrv.setup(socket.LWIP_USER1, config.tp, config.opts)
-    netdrv.dhcp(socket.LWIP_USER1, true)
-    log.info("以太网初始化完成")
-    return true
-end
-
---连接wifi(STA模式)
-local function set_wifi_info(config)
-    wifi_ping_ip = config.ping_ip
-    if type(config.ping_time) == "number" then
-        ping_time = config.ping_time
-    end
-    log.info("WiFi名称:", config.ssid)
-    log.info("密码     :", config.password)
-    log.info("ping_ip  :", config.ping_ip)
-    wlan.init()
-    available[socket.LWIP_STA] = connection_states.OPENED
-    -- 尝试连接Wi-Fi,并处理可能出现的错误
-    local success = wlan.connect(config.ssid, config.password)
-    if not success then
-        log.error("WiFi连接失败")
-        return false
-    end
-    log.info("WiFi STA初始化完成")
-    return true
-end
-
---[[
-设置网络优先级,相应网卡获取到ip且网络正常视为网卡可用,丢失ip视为网卡不可用.
-例:插入网线且能够dns域名解析获取到baidu.com的ip,网卡状态切换为可用。拔掉网线网卡状态切换为不可用
-@api libnetif.set_priority_order(new_priority)
-@table 网络优先级列表
-@return boolean 成功返回true,失败返回false
-@usage
-libnetif.set_priority_order({
-    { -- 最高优先级网络
-        WIFI = { -- WiFi配置
-            ssid = "your_ssid",       -- WiFi名称(string)
-            password = "your_pwd",    -- WiFi密码(string)
-            ping_ip = "112.125.89.8", -- 连通性检测IP(选填参数),默认使用httpdns获取baidu.com的ip作为判断条件,
-                                      -- 注:如果填写ip,则ping通作为判断网络是否可用的条件,
-                                      -- 所以需要根据网络环境填写内网或者外网ip,
-                                      -- 填写外网ip的话要保证外网ip始终可用,
-                                      -- 填写局域网ip的话要确保相应ip固定且能够被ping通
-            ping_time = 10000         -- 填写ping_ip且未ping通时的检测间隔(ms, 可选,默认为10秒)
-                                      -- 定时ping将会影响模块功耗,使用低功耗模式的话可以适当延迟间隔时间
-        }
-    },
-    { -- 次优先级网络
-        ETHERNET = { -- 以太网配置
-            pwrpin = 140,             -- 供电使能引脚(number)
-            ping_ip = "112.125.89.8", -- 连通性检测IP(选填参数),默认使用httpdns获取baidu.com的ip作为判断条件,
-                                      -- 注:如果填写ip,则ping通作为判断网络是否可用的条件,
-                                      -- 所以需要根据网络环境填写内网或者外网ip,
-                                      -- 填写外网ip的话要保证外网ip始终可用,
-                                      -- 填写局域网ip的话要确保相应ip固定且能够被ping通
-            ping_time = 10000         -- 填写ping_ip且未ping通时的检测间隔(ms, 可选,默认为10秒)
-                                      -- 定时ping将会影响模块功耗,使用低功耗模式的话可以适当延迟间隔时间
-            tp = netdrv.CH390         -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
-            opts = { spi = 1, cs = 12 }   -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
-        }
-    },
-    { -- 最低优先级网络
-        LWIP_GP = true  -- 启用4G网络
-    }
-})
-]]
-function libnetif.set_priority_order(networkConfigs)
-    --判断表中数据个数
-    if #networkConfigs <2 then
-        log.error("请至少添加两个网络")
-        return false
-    end
-    local new_priority = {}
-    for _, config in ipairs(networkConfigs) do
-        if type(config.WIFI) == "table" then
-            --开启wifi
-            local res = set_wifi_info(config.WIFI)
-            if res == false then
-                log.error("wifi连接失败")
-                return false
-            end
-            table.insert(new_priority, socket.LWIP_STA)
-        end
-        if type(config.ETHUSER1) == "table" then
-            --开启以太网
-            local res = setup_eth_user1(config.ETHUSER1)
-            if res == false then
-                log.error("以太网打开失败")
-                return false
-            end
-            table.insert(new_priority, socket.LWIP_USER1)
-        end
-        if type(config.ETHERNET) == "table" then
-            --开启以太网
-            local res = setup_eth(config.ETHERNET)
-            if res == false then
-                log.error("以太网打开失败")
-                return false
-            end
-            table.insert(new_priority, socket.LWIP_ETH)
-        end
-        if config.LWIP_GP then
-            --开启4G
-            table.insert(new_priority, socket.LWIP_GP)
-            available[socket.LWIP_GP] = connection_states.OPENED
-        end
-    end
-
-    -- 设置新优先级
-    current_priority = new_priority
-    apply_priority()
-
-    return true
-end
-
---[[
-设置网络状态变化回调函数
-触发条件是 网卡切换或者所有网卡都断网
-返回值为:
-1. 当有可用网络的时候,返回当前使用网卡、网卡id;
-2. 当没有可用网络的时候,返回 nil、-1 。
-@api libnetif.notify_status(cb_fnc)
-@function 回调函数
-@usage
-    libnetif.notify_status(function(net_type,adapter)
-    log.info("可以使用优先级更高的网络:", net_type,adapter)
-    end)
-]]
-function libnetif.notify_status(cb_fnc)
-    log.info("notify_status", type(cb_fnc))
-    if type(cb_fnc) ~= "function" then
-        log.error("notify_status设置错误,请传入一个函数")
-        return
-    end
-    states_cbfnc = cb_fnc
-end
-
---[[
-设置多网融合模式,例如4G作为数据出口给WIFI或以太网设备上网
-@api libnetif.setproxy(adapter, main_adapter,other_configs)
-@adapter 需要使用网络的网卡,例如socket.LWIP_ETH
-@adapter 提供网络的网卡,例如socket.LWIP_GP
-@table 其他设置参数(选填参数),
-{
-        ssid = "Hotspot",                -- WiFi名称(string),网卡包含wifi时填写
-        password = "password123",        -- WiFi密码(string),网卡包含wifi时填写
-        tp = netdrv.CH390,               -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
-        opts = { spi = 1, cs = 12},      -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
-        ethpower_en = 140,               -- 以太网模块的pwrpin引脚(gpio编号)
-        adapter_addr = "192.168.5.1",    -- adapter网卡的ip地址(选填),需要自定义ip和网关ip时填写
-        adapter_gw= { 192, 168, 5, 1 },   -- adapter网卡的网关地址(选填),需要自定义ip和网关ip时填写
-        ap_opts={                        -- AP模式下配置项(选填参数)
-        hidden = false,                  -- 是否隐藏SSID, 默认false,不隐藏
-        max_conn = 4 },                  -- 最大客户端数量, 默认4
-        channel=6                        -- AP建立的通道, 默认6
-}
-@usage
-    --典型应用:
-    -- 4G作为出口供WiFi和以太网设备上网
-    libnetif.setproxy(socket.LWIP_AP, socket.LWIP_GP, {
-        ssid = "Hotspot",                -- WiFi名称(string),网卡包含wifi时填写
-        password = "password123",        -- WiFi密码(string),网卡包含wifi时填写
-        adapter_addr = "192.168.5.1",    -- adapter网卡的ip地址(选填),需要自定义ip和网关ip时填写
-        adapter_gw= { 192, 168, 5, 1 },   -- adapter网卡的网关地址(选填),需要自定义ip和网关ip时填写
-        ap_opts={                        -- AP模式下配置项(选填参数)
-        hidden = false,                  -- 是否隐藏SSID, 默认false,不隐藏
-        max_conn = 4 },                  -- 最大客户端数量, 默认4
-        channel=6                        -- AP建立的通道, 默认6
-    })
-    libnetif.setproxy(socket.LWIP_ETH, socket.LWIP_GP, {
-        tp = netdrv.CH390,               -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
-        opts = { spi = 1, cs = 12},      -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
-        ethpower_en = 140,               -- 以太网模块的pwrpin引脚(gpio编号)
-        adapter_addr = "192.168.5.1",    -- adapter网卡的ip地址(选填),需要自定义ip和网关ip时填写
-        adapter_gw= { 192, 168, 5, 1 },   -- adapter网卡的网关地址(选填),需要自定义ip和网关ip时填写
-    })
-    -- 以太网作为出口供WiFi设备上网
-    libnetif.setproxy(socket.LWIP_AP, socket.LWIP_ETH, {
-        ssid = "Hotspot",                -- WiFi名称(string),网卡包含wifi时填写
-        password = "password123",        -- WiFi密码(string),网卡包含wifi时填写
-        tp = netdrv.CH390,               -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
-        opts = { spi = 1, cs = 12},      -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
-        ethpower_en = 140,               -- 以太网模块的pwrpin引脚(gpio编号)
-    })
-    -- 4G作为出口供以太网设备上网
-    libnetif.setproxy(socket.LWIP_ETH, socket.LWIP_GP, {
-        tp = netdrv.CH390,               -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
-        opts = { spi = 1, cs = 12},      -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
-        ethpower_en = 140,               -- 以太网模块的pwrpin引脚(gpio编号)
-    })
-]]
-function libnetif.setproxy(adapter, main_adapter, other_configs)
-    if adapter == socket.LWIP_ETH then
-        log.info("ch390", "打开LDO供电", other_configs.ethpower_en)
-        gpio.setup(other_configs.ethpower_en, 1, gpio.PULLUP)
-        -- 打开LAN功能
-        -- 配置 SPI 参数,Air8000 使用 SPI 接口与以太网模块进行通信。
-        if other_configs.tp then
-            log.info("netdrv spi挂载以太网", "初始化LAN功能")
-            local result = spi.setup(
-                other_configs.opts.spi, -- spi id
-                nil, 0,                 -- CPHA
-                0,                      -- CPOL
-                8,                      -- 数据宽度
-                51200000                -- ,--波特率
-            )
-            log.info("main", "open spi", result)
-            if result ~= 0 then -- 返回值为 0,表示打开成功
-                log.error("main", "spi open error", result)
-                gpio.close(other_configs.ethpower_en)
-                return false
-            end
-        end
-        -- 初始化以太网,Air8000 指定使用 CH390 芯片。
-        log.info("netdrv", "初始化以太网", other_configs.tp, other_configs.opts)
-        netdrv.setup(socket.LWIP_ETH, other_configs.tp, other_configs.opts)
-        log.info("netdrv", "等待以太网就绪")
-        sys.wait(1000)
-        -- 设置以太网的 IP 地址、子网掩码、网关地址
-        log.info("netdrv", "自定义以太网IP地址", other_configs.adapter_addr, "网关地址", other_configs.adapter_gw)
-        netdrv.ipv4(socket.LWIP_ETH, other_configs.adapter_addr or "192.168.5.1", "255.255.255.0", "0.0.0.0")
-        -- 获取以太网网络状态,连接后返回 true,否则返回 false,如果不存在就返回 nil。
-        local count = 1
-        while netdrv.ready(socket.LWIP_ETH) ~= true do
-            if count > 600 then
-                log.error("以太网连接超时,请检查配置")
-                gpio.close(other_configs.ethpower_en)
-                return false
-            end
-            count = count + 1
-            -- log.info("netdrv", "等待以太网就绪") -- 若以太网设备没有连上,可打开此处注释排查。
-            sys.wait(100)
-        end
-        log.info("netdrv", "以太网就绪")
-        -- 创建 DHCP 服务器,为连接到以太网的设备分配 IP 地址。
-        log.info("netdrv", "创建dhcp服务器, 供以太网使用")
-        if other_configs.adapter_gw then
-            dhcpsrv.create({ adapter = socket.LWIP_ETH, gw = other_configs.adapter_gw })
-        else
-            dhcpsrv.create({ adapter = socket.LWIP_ETH, gw = { 192, 168, 5, 1 } })
-        end
-        -- 创建 DNS 代理服务,使得以太网接口上的设备可以通过 4G 网络访问互联网。
-        log.info("netdrv", "创建dns代理服务, 供以太网使用")
-    elseif adapter == socket.LWIP_AP then
-        wlan.setMode(wlan.APSTA)
-        -- 打开AP功能,设置混合模式
-        log.info("执行AP创建操作", airlink.ready(), "正常吗?")
-        wlan.createAP(other_configs.ssid, other_configs.password, other_configs.adapter_addr or "192.168.4.1",
-            "255.255.255.0",
-            other_configs.channel, other_configs.ap_opts)
-        -- 设置 AP 的 IP 地址、子网掩码、网关地址
-        netdrv.ipv4(socket.LWIP_AP, other_configs.adapter_addr or "192.168.4.1", "255.255.255.0", "0.0.0.0")
-        -- 获取 WiFi AP 网络状态,连接后返回 true,否则返回 false,如果不存在就返回 nil。
-        log.info("netdrv", "等待AP就绪")
-        local count = 1
-        while netdrv.ready(socket.LWIP_AP) ~= true do
-            -- log.info("netdrv", "等待AP就绪")
-            if count > 600 then
-                log.error("AP创建超时,请检查配置")
-                return false
-            end
-            sys.wait(100)
-            count = count + 1
-        end
-        -- 创建 DHCP 服务器,为连接到 WiFi AP 的设备分配 IP 地址。
-        log.info("netdrv", "创建dhcp服务器, 供AP使用")
-        if other_configs.adapter_gw then
-            dhcpsrv.create({ adapter = socket.LWIP_AP, gw = other_configs.adapter_gw })
-        else
-            dhcpsrv.create({ adapter = socket.LWIP_AP })
-        end
-    elseif adapter == socket.LWIP_USER1 then
-        log.info("ch390", "打开LDO供电", other_configs.ethpower_en)
-        gpio.setup(other_configs.ethpower_en, 1, gpio.PULLUP)
-        -- 打开LAN功能
-        -- 配置 SPI 参数,Air8101 使用 SPI 接口与以太网模块进行通信。
-        log.info("netdrv spi挂载以太网", "初始化LAN功能")
-        local result = spi.setup(
-            other_configs.opts.spi,     -- spi id
-            nil, 0,                     -- CPHA
-            0,                          -- CPOL
-            8,                          -- 数据宽度
-            51200000                    -- ,--波特率
-        )
-        log.info("main", "open spi", result)
-        if result ~= 0 then     -- 返回值为 0,表示打开成功
-            log.error("main", "spi open error", result)
-            gpio.close(other_configs.ethpower_en)
-            return false
-        end
-        -- 初始化以太网,Air8000 指定使用 CH390 芯片。
-        log.info("netdrv", "初始化以太网", other_configs.tp, other_configs.opts)
-        netdrv.setup(socket.LWIP_USER1, other_configs.tp, other_configs.opts)
-        log.info("netdrv", "等待以太网就绪")
-        sys.wait(1000)
-        -- 设置以太网的 IP 地址、子网掩码、网关地址
-        log.info("netdrv", "自定义以太网IP地址", other_configs.adapter_addr, "网关地址", other_configs.adapter_gw)
-        netdrv.ipv4(socket.LWIP_USER1, other_configs.adapter_addr or "192.168.5.1", "255.255.255.0", "0.0.0.0")
-        -- 获取以太网网络状态,连接后返回 true,否则返回 false,如果不存在就返回 nil。
-        local count = 1
-        while netdrv.ready(socket.LWIP_USER1) ~= true do
-            if count > 600 then
-                log.error("以太网连接超时,请检查配置")
-                gpio.close(other_configs.ethpower_en)
-                return false
-            end
-            count = count + 1
-            -- log.info("netdrv", "等待以太网就绪") -- 若以太网设备没有连上,可打开此处注释排查。
-            sys.wait(100)
-        end
-        log.info("netdrv", "以太网就绪")
-        -- 创建 DHCP 服务器,为连接到以太网的设备分配 IP 地址。
-        log.info("netdrv", "创建dhcp服务器, 供以太网使用")
-        if other_configs.adapter_gw then
-            dhcpsrv.create({ adapter = socket.LWIP_USER1, gw = other_configs.adapter_gw })
-        else
-            dhcpsrv.create({ adapter = socket.LWIP_USER1, gw = { 192, 168, 5, 1 } })
-        end
-        -- 创建 DNS 代理服务,使得以太网接口上的设备可以通过 4G 网络访问互联网。
-        log.info("netdrv", "创建dns代理服务, 供以太网使用")
-    end
-
-
-    if main_adapter == socket.LWIP_ETH and available[socket.LWIP_ETH] == connection_states.DISCONNECTED then
-        -- 打开WAN功能
-        log.info("ch390", "打开LDO供电", other_configs.ethpower_en)
-        available[socket.LWIP_ETH] = connection_states.OPENED
-        -- 打开CH390供电
-        gpio.setup(other_configs.ethpower_en, 1, gpio.PULLUP)
-        sys.wait(100)
-        if other_configs.tp == nil then
-            log.info("8101以太网")
-            netdrv.setup(socket.LWIP_ETH)
-        else
-            log.info("config.opts.spi", other_configs.opts.spi, ",config.type", other_configs.tp)
-            -- 配置SPI和初始化网络驱动
-            local result = spi.setup(other_configs.opts.spi, -- spi id
-                nil, 0,                                      -- CPHA
-                0,                                           -- CPOL
-                8,                                           -- 数据宽度
-                51200000                                     -- ,--波特率
-            )
-            log.info("main", "open spi", result)
-            if result ~= 0 then -- 返回值为0,表示打开成功
-                log.info("main", "spi open error", result)
-                gpio.close(other_configs.ethpower_en)
-                return false
-            end
-            -- 初始化指定netdrv设备,
-            local success = netdrv.setup(socket.LWIP_ETH, other_configs.tp, other_configs.opts)
-            if not success then
-                log.error("以太网初始化失败")
-                return false
-            end
-        end
-        netdrv.dhcp(socket.LWIP_ETH, true)
-        local count = 1
-        while 1 do
-            local ip = netdrv.ipv4(socket.LWIP_ETH)
-            if ip and ip ~= "0.0.0.0" then break end
-            if count > 600 then
-                log.error("以太网连接超时,请检查配置")
-                gpio.close(other_configs.ethpower_en)
-                return false
-            end
-            sys.wait(100)
-            count = count + 1
-        end
-    elseif main_adapter == socket.LWIP_USER1 and available[socket.LWIP_USER1] == connection_states.DISCONNECTED then
-        log.info("初始化以太网")
-        -- 打开CH390供电
-        gpio.setup(other_configs.ethpower_en, 1, gpio.PULLUP)
-        sys.wait(100)
-        log.info("config.opts.spi", other_configs.opts.spi, ",config.type", other_configs.tp)
-        available[socket.LWIP_USER1] = connection_states.OPENED
-        -- 配置SPI和初始化网络驱动
-        local result = spi.setup(other_configs.opts.spi, -- spi id
-            nil, 0,                               -- CPHA
-            0,                                    -- CPOL
-            8,                                    -- 数据宽度
-            51200000                              -- ,--波特率
-        )
-        log.info("main", "open spi", result)
-        if result ~= 0 then -- 返回值为0,表示打开成功
-            log.info("main", "spi open error", result)
-            gpio.close(other_configs.ethpower_en)
-            return false
-        end
-        -- 初始化指定netdrv设备,
-        -- socket.LWIP_ETH 网络适配器编号
-        -- netdrv.CH390外挂CH390
-        -- SPI ID 1, 片选 GPIO12
-        netdrv.setup(socket.LWIP_USER1, other_configs.tp, other_configs.opts)
-        netdrv.dhcp(socket.LWIP_USER1, true)
-        log.info("以太网初始化完成")
-        local count = 1
-        while 1 do
-            local ip = netdrv.ipv4(socket.LWIP_USER1)
-            if ip and ip ~= "0.0.0.0" then break end
-            if count > 600 then
-                log.error("以太网连接超时,请检查配置")
-                gpio.close(other_configs.ethpower_en)
-                return false
-            end
-            sys.wait(100)
-            count = count + 1
-        end
-    elseif main_adapter == socket.LWIP_STA and available[socket.LWIP_STA] == connection_states.DISCONNECTED then
-        -- 打开STA功能,设置混合模式
-        wlan.init()
-        wlan.setMode(wlan.APSTA)
-        available[socket.LWIP_STA] = connection_states.OPENED
-        -- 尝试连接Wi-Fi,并处理可能出现的错误
-        wlan.connect(other_configs.ssid, other_configs.password)
-        -- 等待获取IP地址
-        local count = 1
-        while 1 do
-            local ip = netdrv.ipv4(socket.LWIP_STA)
-            if ip and ip ~= "0.0.0.0" then
-                log.info("WiFi STA已连接,IP:", ip)
-                break
-            end
-            if count > 600 then
-                log.error("WiFi STA连接超时,请检查配置")
-                return false
-            end
-            sys.wait(100)
-            count = count + 1
-        end
-        log.info("WiFi STA初始化完成")
-    end
-    dnsproxy.setup(adapter, main_adapter)
-    netdrv.napt(main_adapter)
-    return true
-end
---httpdns域名解析测试
-local function http_dnstest(adaptertest)
-    local ip = httpdns.ali("baidu.com", { adapter = adaptertest, timeout = 3000 })
-    if ip ~= nil then
-        available[adaptertest] = connection_states.CONNECTED
-    end
-    log.info("httpdns", "baidu.com", ip)
-end
--- ping操作
-local function ping_request(adaptertest)
-    log.info("dns_request",type_to_string(adaptertest))
-    if adaptertest == socket.LWIP_ETH or adaptertest == socket.LWIP_USER1 then
-        if eth_ping_ip == nil then
-           http_dnstest(adaptertest)
-        else
-            icmp.setup(adaptertest)
-            icmp.ping(adaptertest, eth_ping_ip)
-        end
-    end
-    if adaptertest == socket.LWIP_STA then
-        if wifi_ping_ip == nil then
-            http_dnstest(adaptertest)
-        else
-            icmp.setup(adaptertest)
-            icmp.ping(adaptertest, wifi_ping_ip)
-        end
-    end
-    if adaptertest == socket.LWIP_GP then
-        if eth_ping_ip ~= nil then
-            icmp.setup(adaptertest)
-            icmp.ping(adaptertest, eth_ping_ip)
-        elseif wifi_ping_ip ~= nil then
-            icmp.setup(adaptertest)
-            icmp.ping(adaptertest, wifi_ping_ip)
-        else
-            http_dnstest(adaptertest)
-        end
-    end
-    apply_priority()
-end
--- 网卡上线回调函数
-local function ip_ready_handle(ip, adapter)
-    log.info("ip_ready_handle", ip, type_to_string(adapter),"state",available[adapter])
-    -- 需要ping操作,ping通后认为网络可用
-    if available[adapter] == connection_states.OPENED then
-        available[adapter] = connection_states.CONNECTING
-    end
-    -- ping_request(adapter)
-end
--- 网卡下线回调函数
-local function ip_lose_handle(adapter)
-    log.info("ip_lose_handle", type_to_string(adapter))
-    available[adapter] = connection_states.OPENED
-    if current_active == adapter then
-        log.info(type_to_string(adapter) .. " 失效,切换到其他网络")
-        apply_priority()
-    end
-end
-
---CONNECTING的网卡需要定时ping
-sys.taskInit(function()
-    while true do
-        for _, net_type in ipairs(current_priority) do
-            -- log.info("网卡顺序",type_to_string(net_type),available[net_type])
-            if available[net_type] == connection_states.CONNECTING then
-                ping_request(net_type)
-                log.info(type_to_string(net_type) .. "网卡未ping通,需要定时ping")
-                sys.wait(ping_time)
-            end
-        end
-        sys.wait(1000)
-    end
-end)
-
-local interval_time = nil
-
---[[
-对正常状态的网卡进行ping测试
-@api libnetif.check_network_status(interval),
-@int 检测间隔时间ms(选填),不填时只检测一次,填写后将根据间隔时间循环检测,会提高模块功耗
-]]
-function libnetif.check_network_status(interval)
-    if interval ~= nil then
-        interval_time = interval
-    end
-    for _, net_type in ipairs(current_priority) do
-        if available[net_type] == connection_states.CONNECTED then
-            available[net_type] = connection_states.CONNECTING
-        end
-    end
-end
-
---循环ping检测任务,默认不启用
-sys.taskInit(function()
-    while true do
-        if interval_time ~= nil then
-            sys.wait(interval_time)
-            libnetif.check_network_status()
-        end
-        sys.wait(1000)
-    end
-end)
-
-sys.subscribe("PING_RESULT", function(id, time, dst)
-    log.info("ping", type_to_string(id), time, dst);
-    available[id] = connection_states.CONNECTED
-    apply_priority()
-end)
--- 订阅网络状态变化的消息
-sys.subscribe("IP_READY", ip_ready_handle)
-sys.subscribe("IP_LOSE", ip_lose_handle)
-return libnetif

+ 2 - 2
module/Air8101/demo/libnetif/mqtt_test.lua

@@ -28,8 +28,8 @@ local mqttc = nil
 
 mqtt_state = false
 
-libnetif.notify_status(function(net_type)
-    log.info("可以使用优先级更高的网络:", net_type)
+libnetif.notify_status(function(net_type, adapter)
+    log.info("可以使用优先级更高的网络:", net_type, adapter)
     sys.publish("mqtt_pub", "close")  --关闭现在的mqtt链接
 end)
 

+ 12 - 1
module/Air8101/demo/libnetif/netif_app.lua

@@ -44,5 +44,16 @@ sys.taskInit(function()
         }
     })
     --设置多网融合功能
-    -- libnetif.setproxy(socket.LWIP_AP, socket.LWIP_ETH, "test", "HZ88888888", 13)
+    if  libnetif.setproxy(socket.LWIP_AP, socket.LWIP_ETH, {
+        ssid = "test2",                -- WiFi名称(string),网卡包含wifi时填写
+        password = "HZ88888888",        -- WiFi密码(string),网卡包含wifi时填写
+        ethpower_en = 13,               -- 以太网模块的pwrpin引脚(gpio编号)
+        -- tp = netdrv.CH390,               -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
+        -- opts = { spi = 1, cs = 12},      -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
+        -- adapter_addr = "192.168.2.1",    -- adapter网卡的ip地址(选填),需要自定义ip和网关ip时填写
+        -- adapter_gw= { 192, 168, 2, 1 },   -- adapter网卡的网关地址(选填),需要自定义ip和网关ip时填写
+    }) then
+        log.info("libnetif", "setproxy success")
+    end
+    
 end)

BIN
module/PC/win32/LuatOS-SoC_V2002_PC.zip


+ 2 - 2
script/libs/libnetif.lua

@@ -217,7 +217,7 @@ end
 --[[
 设置网络优先级,相应网卡获取到ip且网络正常视为网卡可用,丢失ip视为网卡不可用.(需要在task中调用)
 @api libnetif.set_priority_order(new_priority)
-@table 网络优先级列表
+@table 网络优先级列表,优先级从高到低对应table中的第一个参数到最后一个参数
 @return boolean 成功返回true,失败返回false
 @usage
 libnetif.set_priority_order({
@@ -316,7 +316,7 @@ function libnetif.set_priority_order(networkConfigs)
 end
 
 --[[
-设置网络状态变化回调函数。触发条件:网卡切换或者所有网卡都断网。返回值为:1. 当有可用网络的时候,返回当前使用网卡、网卡id;2. 当没有可用网络的时候,返回 nil、-1 。
+设置网络状态变化回调函数。触发条件:网卡切换或者所有网卡都断网。回调函数的输入参数: 1. 当有可用网络的时候,返回当前使用网卡、网卡id;2. 当没有可用网络的时候,返回 nil、-1 。
 @api libnetif.notify_status(cb_fnc)
 @function 回调函数
 @usage