Răsfoiți Sursa

update:Air780EXX AirETH_1000 demo

王城钧 4 luni în urmă
părinte
comite
f0f6756e4e

+ 141 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirETH_1000/http/http_app.lua

@@ -0,0 +1,141 @@
+--[[
+@module  http_app
+@summary http应用功能模块
+@version 1.0
+@date    2025.09.17
+@author  王城钧
+@usage
+本文件为http应用功能模块,核心业务逻辑为:基于不同的应用场景,演示http核心库的使用方式;
+本文件没有对外接口,直接在main.lua中require "http_app"就可以加载运行;
+]]
+
+--[[
+此处先详细解释下http.request接口的使用方法
+
+接口定义:
+    http.request(method, url, headers, body, opts, server_ca_cert, client_cert, client_key, client_password)
+
+使用方法:
+    local code, headers, body = http.request(method, url, headers, body, opts, server_ca_cert, client_cert, client_key, client_password).wait()
+    只能在task中使用
+    发送http请求到服务器,等待服务器的http应答,此处会阻塞当前task,等待整个过程成功结束或者出现错误异常结束或者超时结束
+
+参数定义:
+    method,stirng类型,必须包含此参数,表示HTTP请求方法,支持"GET"、"POST"、"HEAD"等所有HTTP请求方法
+    url,string类型,必须包含此参数,表示HTTP请求URL地址,支持HTTP、HTTPS,支持域名、IP地址,支持自定义端口,标准的HTTP URL格式都支持
+    headers,table或者nil类型,可选包含此参数,表示HTTP请求头,例如 {["Content-Type"] = "application/x-www-form-urlencoded", ["self_defined_key"] = "self_defined_value"}
+    body,string或者zbuff或者nil类型,可选包含此参数,表示HTTP请求体,如果请求体是一个文件中的内容,要把文件内容读出来,赋值给body使用
+    opts,table或者nil类型,可选包含此参数,表示HTTP请求的一些额外配置,包含以下内容
+    {
+        timeout    -- -- number或者nil类型,单位毫秒,可选包含此参数,表示从发送请求到读取到服务器响应整个过程的超时时间,如果传入0,表示永久等待;如果没有传入此参数或者传入nil,则使用默认值10分钟
+        dst        -- 下载路径,string类型,当HTTP请求的数据需要保存到文件中时,此处填写完整的文件路径
+        adapter    -- 使用的网卡ID,number类型,例如4G网卡,SPI外挂以太网卡,WIFI网卡等;如果没有传入此参数,内核固件会自动选择当前时间点其他功能模块设置的默认网卡
+                    -- 除非你HTTP请求时,一定要使用某一种网卡,才设置此参数;如果没什么特别要求,不要使用此参数,使用系统中设置的默认网卡即可
+                    -- 这个参数和本demo中的netdrv_device.lua关系比较大,netdrv_device会设置默认网卡,此处http不要设置adapter参数,直接使用netdrv_device设置的默认网卡就行
+        debug      -- 调试开关,bool类型,true表示打开debug调试信息日志,false表示关闭debug调试信息日志,默认为关闭状态
+        ipv6       -- 是否为ipv6,bool类型,true表示使用ipv6,false表示不使用ipv6,默认为false
+        userdata   -- 下载回调函数使用的用户自定义回调参数,做为callback回调函数的第三个参数使用
+        callback   -- 下载回调函数,function类型,当下载数据时,无论是保存到内存中,还是保存到文件系统中,如果设置了callback,内核固件中每收到一包body数据,都会自动执行一次callback回调函数
+                    -- 回调函数的调用形式为callback(content_len, body_len, userdata)
+                    --     content_len:number类型,数据总长度
+                    --     body_len:number类型,已经下载的数据长度
+                    --     userdata:下载回调函数使用的用户自定义回调参数
+    }
+    server_ca_cert,string类型,服务器ca证书数据,可选包含此参数,当客户端需要验证服务器证书时,需要此参数,如果证书数据在一个文件中,要把文件内容读出来,赋值给server_ca_cert
+    client_cert,string类型,客户端证书数据,可选包含此参数,当服务器需要验证客户端证书时,需要此参数,如果证书数据在一个文件中,要把文件内容读出来,赋值给client_cert
+    client_key, string类型,客户端加密后的私钥数据,可选包含此参数,当服务器需要验证客户端证书时,需要此参数,如果加密后的私钥数据在一个文件中,要把文件内容读出来,赋值给client_key
+    client_password,string类型,客户端私钥口令数据,可选包含此参数,当服务器需要验证客户端证书时,需要此参数,如果私钥口令数据在一个文件中,要把文件内容读出来,赋值给client_password
+
+返回值定义:
+
+    http.request().wait()有三个返回值code,headers,body
+    code表示执行结果,number类型,有以下两种含义:
+        1、code大于等于100时,表示服务器返回的HTTP状态码,例如200表示成功,详细说明可以通过搜索引擎搜索“HTTP状态码”自行了解
+        2、code小于0时,表示内核固件中检测到通信异常,有如下几种:
+            -1 HTTP_ERROR_STATE 错误的状态, 一般是底层异常,请报issue
+            -2 HTTP_ERROR_HEADER 错误的响应头部, 通常是服务器问题
+            -3 HTTP_ERROR_BODY 错误的响应体,通常是服务器问题
+            -4 HTTP_ERROR_CONNECT 连接服务器失败, 未联网,地址错误,域名错误
+            -5 HTTP_ERROR_CLOSE 提前断开了连接, 网络或服务器问题
+            -6 HTTP_ERROR_RX 接收数据报错, 网络问题
+            -7 HTTP_ERROR_DOWNLOAD 下载文件过程报错, 网络问题或下载路径问题
+            -8 HTTP_ERROR_TIMEOUT 超时, 包括连接超时,读取数据超时
+            -9 HTTP_ERROR_FOTA fota功能报错,通常是更新包不合法
+    headers有以下两种含义:
+        1、当code的返回值大于等于100时,headers表示服务器返回的应答头,table类型
+        2、当code的返回值小于0时,headers为nil
+    body有以下三种含义
+        1、当code的返回值大于等于100时,如果请求的body数据不需要保存到文件中,而是直接保存到内存中,则body表示请求到的数据内容,string类型
+        2、当code的返回值大于等于100时,如果请求的body数据需要保存到文件中,则body表示保存请求数据后的文件的大小,number类型
+        3、当code的返回值小于0时,body为nil
+]]
+
+
+-- 普通的http get请求功能演示
+-- 请求的body数据保存到内存变量中,在内存够用的情况下,最大支持32KB的数据存储到内存中
+-- timeout可以设置超时时间
+-- callback可以设置回调函数,可用于实时检测body数据的下载进度
+local function http_app_get()
+    -- https get请求https://www.air32.cn/网页内容
+    -- 如果请求成功,请求的数据保存到body中
+    local code, headers, body = http.request("GET", "https://www.air32.cn/").wait()
+    log.info("http_app_get1",
+        code == 200 and "success" or "error",
+        code,
+        json.encode(headers or {}),
+        body and (body:len() > 512 and body:len() or body) or "nil")
+
+    -- https get请求https://www.luatos.com/网页内容,超时时间为10秒
+    -- 请求超时时间为10秒,用户自己写代码时,不要照抄10秒,根据自己业务逻辑的需要设置合适的超时时间
+    -- 回调函数为http_cbfunc,回调函数使用的第三个回调参数为"http_app_get2"
+    -- 如果请求成功,请求的数据保存到body中
+    code, headers, body = http.request("GET", "https://www.luatos.com/", nil, nil,
+        { timeout = 10000, userdata = "http_app_get2", callback = http_cbfunc }).wait()
+    log.info("http_app_get2",
+        code == 200 and "success" or "error",
+        code,
+        json.encode(headers or {}),
+        body and (body:len() > 512 and body:len() or body) or "nil")
+
+    -- http get请求http://httpbin.air32.cn/get网页内容,超时时间为3秒
+    -- 请求超时时间为3秒,用户自己写代码时,不要照抄3秒,根据自己业务逻辑的需要设置合适的超时时间
+    -- 回调函数为http_cbfunc,回调函数使用的第三个回调参数为"http_app_get3"
+    -- 如果请求成功,请求的数据保存到body中
+    code, headers, body = http.request("GET", "http://httpbin.air32.cn/get", nil, nil,
+        { timeout = 3000, userdata = "http_app_get3", callback = http_cbfunc }).wait()
+    log.info("http_app_get3",
+        code == 200 and "success" or "error",
+        code,
+        json.encode(headers or {}),
+        body and (body:len() > 512 and body:len() or body) or "nil")
+end
+
+-- http app task 的任务处理函数
+local function http_app_task_func()
+    while true do
+        -- 如果当前时间点设置的默认网卡还没有连接成功,一直在这里循环等待
+        while not socket.adapter(socket.dft()) do
+            log.warn("http_app_task_func", "wait IP_READY", socket.dft())
+            -- 在此处阻塞等待默认网卡连接成功的消息"IP_READY"
+            -- 或者等待1秒超时退出阻塞等待状态;
+            -- 注意:此处的1000毫秒超时不要修改的更长;
+            -- 因为当使用exnetif.set_priority_order配置多个网卡连接外网的优先级时,会隐式的修改默认使用的网卡
+            -- 当exnetif.set_priority_order的调用时序和此处的socket.adapter(socket.dft())判断时序有可能不匹配
+            -- 此处的1秒,能够保证,即使时序不匹配,也能1秒钟退出阻塞状态,再去判断socket.adapter(socket.dft())
+            sys.waitUntil("IP_READY", 1000)
+        end
+
+        -- 检测到了IP_READY消息
+        log.info("http_app_task_func", "recv IP_READY", socket.dft())
+
+        -- 普通的http get请求功能演示
+        http_app_get()
+
+        -- 50秒之后,循环测试
+        sys.wait(50000)
+    end
+end
+
+--创建并且启动一个task
+--运行这个task的处理函数http_app_task_func
+sys.taskInit(http_app_task_func)

+ 80 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirETH_1000/http/main.lua

@@ -0,0 +1,80 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 1.0
+@date    2025.09.17
+@author  王城钧
+@usage
+本demo演示的核心功能为:
+1、分别使用http核心库和httpplus扩展库,演示以下这种应用场景的使用方式
+   (1) 普通的http get请求功能演示;
+2、netdrv_device:配置连接外网使用的网卡,目前支持以下四种选择(二选一)
+   (1) netdrv_eth_spi:通过SPI外挂CH390H芯片的以太网卡
+   (2) 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 = "HTTP"
+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 "netdrv_device"
+
+-- 加载http应用功能模块
+require "http_app"
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 90 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirETH_1000/http/netdrv/netdrv_eth_spi.lua

@@ -0,0 +1,90 @@
+--[[
+@module  netdrv_eth_spi
+@summary “通过SPI外挂CH390H芯片的以太网卡”驱动模块
+@version 1.0
+@date    2025.07.24
+@author  马梦阳
+@usage
+本文件为“通过SPI外挂CH390H芯片的以太网卡”驱动模块,核心业务逻辑为:
+1、打开CH390H芯片供电开关;
+2、初始化spi0,初始化以太网卡,并且在以太网卡上开启DHCP(动态主机配置协议);
+3、以太网卡的连接状态发生变化时,在日志中进行打印;
+
+直接使用Air780EXX 核心板外挂AirETH_1000硬件测试即可;
+
+本文件没有对外接口,直接在其他功能模块中require "netdrv_eth_spi"就可以加载运行;
+]]
+
+local exnetif = require "exnetif"
+
+local function ip_ready_func(ip, adapter)
+    if adapter == socket.LWIP_ETH then
+        log.info("netdrv_eth_spi.ip_ready_func", "IP_READY", socket.localIP(socket.LWIP_ETH))
+    end
+end
+
+local function ip_lose_func(adapter)
+    if adapter == socket.LWIP_ETH then
+        log.warn("netdrv_eth_spi.ip_lose_func", "IP_LOSE")
+    end
+end
+
+
+-- 此处订阅"IP_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)
+
+
+-- GPIO20为CH390H以太网芯片的供电使能控制引脚
+gpio.setup(20, 1, gpio.PULLUP)
+
+-- 这个task的核心业务逻辑是:初始化SPI,初始化以太网卡,并在以太网卡上开启动态主机配置协议
+local function netdrv_eth_spi_task_func()
+    local result = exnetif.set_priority_order({
+    {
+        ETHUSER1 = {
+                    -- 供电使能GPIO
+                    pwrpin = 20,
+                    -- 设置的多个“已经IP READY,但是还没有ping通”网卡,循环执行ping动作的间隔(单位毫秒,可选)
+                    -- 如果没有传入此参数,exnetif会使用默认值10秒
+                    ping_time = 3000,
+
+                    -- 连通性检测ip(选填参数);
+                    -- 如果没有传入ip地址,exnetif中会默认使用httpdns能否成功获取baidu.com的ip作为是否连通的判断条件;
+                    -- 如果传入,一定要传入可靠的并且可以ping通的ip地址;
+                    -- ping_ip = "填入可靠的并且可以ping通的ip地址",
+
+                    -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
+                    tp = netdrv.CH390,
+                    opts = {spi=0, cs=8}
+        }
+    }
+})
+    --初始化以太网卡
+
+    --以太网联网成功(成功连接路由器,并且获取到了IP地址)后,内核固件会产生一个"IP_READY"消息
+    --各个功能模块可以订阅"IP_READY"消息实时处理以太网联网成功的事件
+    --也可以在任何时刻调用socket.adapter(socket.LWIP_ETH)来获取以太网是否连接成功
+
+    --以太网断网后,内核固件会产生一个"IP_LOSE"消息
+    --各个功能模块可以订阅"IP_LOSE"消息实时处理以太网断网的事件
+    --也可以在任何时刻调用socket.adapter(socket.LWIP_ETH)来获取以太网是否连接成功
+
+    -- 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)

+ 79 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirETH_1000/http/netdrv/netdrv_multiple.lua

@@ -0,0 +1,79 @@
+--[[
+@module  netdrv_multiple
+@summary 多网卡(4G网卡、通过SPI外挂CH390H芯片的以太网卡)驱动模块
+@version 1.0
+@date    2025.07.24
+@author  马梦阳
+@usage
+本文件为多网卡驱动模块,核心业务逻辑为:
+1、调用exnetif.set_priority_order配置多网卡的控制参数以及优先级;
+
+直接使用Air780EXX. 核心板外挂AirETH_1000硬件测试即可;
+
+本文件没有对外接口,直接在其他功能模块中require "netdrv_multiple"就可以加载运行;
+]]
+
+local exnetif = require "exnetif"
+
+-- 网卡状态变化通知回调函数
+-- 当exnetif中检测到网卡切换或者所有网卡都断网时,会触发调用此回调函数
+-- 当网卡切换切换时:
+--     net_type:string类型,表示当前使用的网卡字符串
+--     adapter:number类型,表示当前使用的网卡id
+-- 当所有网卡断网时:
+--     net_type:为nil
+--     adapter:number类型,为-1
+local function netdrv_multiple_notify_cbfunc(net_type,adapter)
+    if type(net_type)=="string" then
+        log.info("netdrv_multiple_notify_cbfunc", "use new adapter", net_type, adapter)
+    elseif type(net_type)=="nil" then
+        log.warn("netdrv_multiple_notify_cbfunc", "no available adapter", net_type, adapter)
+    else
+        log.warn("netdrv_multiple_notify_cbfunc", "unknown status", net_type, adapter)
+    end
+end
+
+local function netdrv_multiple_task_func()
+    --设置网卡优先级
+    exnetif.set_priority_order(
+        {
+            -- “通过SPI外挂CH390H芯片”的以太网卡,使用Air780EXX核心板验证
+            {
+                ETHERNET = {
+                    -- 供电使能GPIO
+                    pwrpin = 20,
+                    -- 设置的多个“已经IP READY,但是还没有ping通”网卡,循环执行ping动作的间隔(单位毫秒,可选)
+                    -- 如果没有传入此参数,exnetif会使用默认值10秒
+                    ping_time = 3000,
+
+                    -- 连通性检测ip(选填参数);
+                    -- 如果没有传入ip地址,exnetif中会默认使用httpdns能否成功获取baidu.com的ip作为是否连通的判断条件;
+                    -- 如果传入,一定要传入可靠的并且可以ping通的ip地址;
+                    -- ping_ip = "填入可靠的并且可以ping通的ip地址",
+
+                    -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
+                    tp = netdrv.CH390,
+                    opts = {spi=0, cs=8}
+                }
+            },
+
+            -- 4G网卡
+            {
+                LWIP_GP = true
+            }
+        }
+    )
+end
+
+-- 设置网卡状态变化通知回调函数netdrv_multiple_notify_cbfunc
+exnetif.notify_status(netdrv_multiple_notify_cbfunc)
+
+-- 如果存在udp网络应用,并且udp网络应用中,根据应用层的心跳能够判断出来udp数据通信出现了异常;
+-- 可以在判断出现异常的位置,调用一次exnetif.check_network_status()接口,强制对当前正式使用的网卡进行一次连通性检测;
+-- 如果存在tcp网络应用,不需要用户调用exnetif.check_network_status()接口去控制,exnetif会在tcp网络应用通信异常时自动对当前使用的网卡进行连通性检测。
+
+
+-- 启动一个task,task的处理函数为netdrv_multiple_task_func
+-- 在处理函数中调用exnetif.set_priority_order设置网卡优先级
+-- 因为exnetif.set_priority_order要求必须在task中被调用,所以此处启动一个task
+sys.taskInit(netdrv_multiple_task_func)

+ 25 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirETH_1000/http/netdrv_device.lua

@@ -0,0 +1,25 @@
+--[[
+@module  netdrv_device
+@summary 网络驱动设备功能模块
+@version 1.0
+@date    2025.07.24
+@author  马梦阳
+@usage
+本文件为网络驱动设备功能模块,核心业务逻辑为:根据项目需求,选择并且配置合适的网卡(网络适配器)
+1、netdrv_ethernet_spi:socket.LWIP_ETH,通过SPI外挂CH390H芯片的以太网卡;
+2、netdrv_multiple:可以配置多种网卡的优先级,按照优先级配置,使用其中一种网卡连接外网;
+
+根据自己的项目需求,只需要require以上其中的一种即可;
+
+
+本文件没有对外接口,直接在main.lua中require "netdrv_device"就可以加载运行;
+]]
+
+
+-- 根据自己的项目需求,只需要require以下其中的一种即可;
+
+-- 加载“通过SPI外挂CH390H芯片的以太网卡”驱动模块
+require "netdrv_eth_spi"
+
+-- 加载“可以配置优先级的多种网卡”驱动模块
+-- require "netdrv_multiple"

+ 75 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirETH_1000/http/readme.md

@@ -0,0 +1,75 @@
+## 功能模块介绍:
+
+1、main.lua:主程序入口;
+
+2、netdrv_device.lua:加载网络驱动设备功能模块;
+
+3、http_lua:加载http应用模块;
+
+## 演示功能概述
+
+1、以太网给模组供网,通过连接http测试连通。
+
+## 演示硬件环境
+
+1、Air780EXX核心板一块+可上网的sim卡一张+网线一根+AirETH_1000板子一个;
+
+[](https://docs.openLuat.com/cdn/image/AirETH_1000.jpg)
+
+![lan](E:\文档池\新建文件夹\luatos-doc-pool\docs\root\docs\air780EXX\luatos\app\image\lan.jpg)
+
+2、TYPE-C USB数据线一根 + 杜邦线若干;
+
+* Air780EXX核心板通过TYPE-C USB口供电;(外部供电/USB供电 拨动开关 拨到 USB供电一端)
+
+* TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+* AirETH_1000板子网口与电脑网口通过网线连接;
+
+3、Air780EXX核心板和AirETH_1000配件板的硬件接线方式为:
+
+| Air780EXX核心板 | AirETH_1000配件板 |
+| ------------ | -------------- |
+| 3.3V         | 3.3v           |
+| gnd          | gnd            |
+| spi0_sclk    | SCK            |
+| spi0_cs      | CSS            |
+| spi0_miso    | SDO            |
+| spi0_mosi    | SDI            |
+| gpio21       | INT            |
+
+演示软件环境
+------
+
+1、Luatools下载调试工具
+
+2、[Air780EHM V2012版本固件]([固件版本 - luatos@air780epm - 合宙模组资料中心](https://docs.openluat.com/air780epm/luatos/firmware/version/))、[Air780EHV V2012版本固件]([固件版本 - luatos@air780ehv - 合宙模组资料中心](https://docs.openluat.com/air780ehv/luatos/firmware/version/))、[Air780EGH V2012版本固件]([固件版本 - luatos@air780egh - 合宙模组资料中心](https://docs.openluat.com/air780egh/luatos/firmware/version/)(理论上,2025年7月26日之后发布的固件都可以)
+
+## 演示核心步骤
+
+1、搭建好硬件环境,按接线图连接硬件。
+
+2、烧录内核固件和本项目的Lua脚本:main.lua:主程序入口(需要在main.lua文件中打开require"netdrv_device"和require"http_app")
+
+3、启动设备,观察日志输出:
+
+出现类似如下打印,就表示成功。
+
+```
+[2025-09-18 11:31:29.931][000000003.367] I/user.netdrv_eth_spi.ip_ready_func IP_READY 192.168.0.52 255.255.255.0 192.168.0.1 nil
+
+[2025-09-18 11:31:29.934][000000003.368] I/user.http_app_task_func recv IP_READY 4 
+
+[2025-09-18 11:31:29.938][000000003.372] dns_run 676:www.air32.cn state 0 id 1 ipv6 0 use dns server0, try
+
+[2025-09-18 11:31:29.942][000000003.372] D/net adatper 4 dns server 192.168.0.1
+
+[2025-09-18 11:31:29.944][000000003.372] D/net dns udp sendto 192.168.0.1:53 from 192.168.0.52
+
+[2025-09-18 11:31:29.948][000000003.384] dns_run 693:dns all done ,now stop
+
+[2025-09-18 11:31:29.950][000000003.385] D/net connect 49.232.89.122:443 TCP
+
+[2025-09-18 11:31:30.641][000000004.123] I/user.http_app_get1 success 200 {"Transfer-Encoding":"chunked","Date":"Thu, 18 Sep 2025 03:31:33 GMT","Connection":"keep-alive","Server":"openresty\/1.27.1.2","Content-Type":"text\/html"} 2416
+
+```

+ 71 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirETH_1000/network_routing/4g_out_ethernet_in/main.lua

@@ -0,0 +1,71 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 1.0
+@date    2025.09.22
+@author  王城钧
+@usage
+本demo演示的核心功能为:
+1.设置多网融合功能,4G提供网络供以太网设备上网
+更多说明参考本目录下的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 = "4g_out_ethernet_in"
+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 "netif_app"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 48 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirETH_1000/network_routing/4g_out_ethernet_in/netif_app.lua

@@ -0,0 +1,48 @@
+--[[
+@module  netif_app
+@summary netif_app 网络管理模块,开启多网融合功能,4G提供网络供以太网设备上网
+@version 1.0
+@date    2025.09.22
+@author  王城钧
+@usage
+本文件为网络管理模块,核心业务逻辑为:
+1、设置多网融合功能,4G提供网络供以太网设备上网
+2、http测试4G网络
+本文件没有对外接口,直接在main.lua中require "netif_app"就可以加载运行;
+]] 
+exnetif = require "exnetif"
+
+function netif_app_task_func()
+    local res
+    -- 等待4G网络连接成功
+    while not socket.adapter() do
+        -- 在此处阻塞等待4G网卡连接成功的消息"IP_READY"
+        -- 或者等待1秒超时退出阻塞等待状态;
+        -- 注意:此处的1000毫秒超时不要修改的更长;
+        sys.waitUntil("IP_READY", 1000)
+    end
+    -- 设置多网融合功能,4G提供网络供以太网设备上网
+    res = exnetif.setproxy(socket.LWIP_ETH, socket.LWIP_GP, {
+        ethpower_en = 20,                   -- 以太网模块的pwrpin引脚(gpio编号)
+        tp = netdrv.CH390,                  -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
+        opts = {spi = 0, cs = 8},           -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
+        adapter_addr = "192.168.2.1",       -- 自定义LWIP_ETH网卡的ip地址(选填),需要自定义ip和网关ip时填写
+        adapter_gw = {192, 168, 2, 1}       -- 自定义LWIP_ETH网卡的网关地址(选填),需要自定义ip和网关ip时填写
+    })
+
+    if res then
+        log.info("exnetif", "setproxy success")
+    else
+        log.info("开启失败,请检查配置项是否正确,日志中是否打印了错误信息")
+    end
+
+    -- 每5秒进行HTTPS连接测试,实时监测4G网络连接状态, 仅供测试需要,量产不需要,用来判断当前网络是否可用,需要的话可以打开注释
+    -- while 1 do
+    --     local code, headers, body = http.request("GET", "https://httpbin.air32.cn/bytes/2048", nil, nil,
+    --         { adapter = socket.LWIP_GP, timeout = 5000, debug = false }).wait()
+    --     log.info("http执行结果", code, headers, body and #body)
+    --     sys.wait(10000)
+    -- end
+end
+
+sys.taskInit(netif_app_task_func)

+ 60 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirETH_1000/network_routing/4g_out_ethernet_in/readme.md

@@ -0,0 +1,60 @@
+## 功能模块介绍
+
+1、main.lua:主程序入口;
+
+2、netif_app: 网络管理模块,开启多网融合功能,4G提供网络供以太网设备上网;
+
+## 演示功能概述
+
+1、开启多网融合模式,4G连接外部网络,以太网lan模式为其他以太网设备提供接入
+
+2、​网络监控​,每5秒进行HTTPS连接测试,实时监测4G网络的连接状态
+
+## 演示硬件环境
+
+![](%20https://docs.openLuat.com/cdn/image/780EXX_adc.jpg)
+
+1、Air780EXX 核心板一块+可上网的sim卡一张+网线一根:
+
+- sim卡插入核心板的sim卡槽
+
+- 网线一端插入核心板外接的AirETH_1000小板上,另外一端连接需接入以太网的设备
+
+2、TYPE-C USB数据线一根 + 网线一根,Air780EXX核心板和数据线的硬件接线方式为:
+
+- Air780EXX 本核心板通过TYPE-C USB口供电;(外部供电/USB供电 拨动开关 拨到 USB供电一端)
+
+- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+- 核心板与AirETH_1000的接线方式如下:
+  
+  | Air780EXX核心板 | AirETH_1000配件板 |
+  | ------------ | -------------- |
+  | vdd          | 3.3v           |
+  | gnd          | gnd            |
+  | spi0_sclk    | SCK            |
+  | spi0_cs      | CSS            |
+  | spi0_miso    | SDO            |
+  | spi0_mosi    | SDI            |
+  | gpio21       | INT            |
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+2、[[Air780EHM V2012版本固件]([固件版本 - luatos@air780epm - 合宙模组资料中心](https://docs.openluat.com/air780epm/luatos/firmware/version/))、[Air780EHV V2012版本固件]([固件版本 - luatos@air780ehv - 合宙模组资料中心](https://docs.openluat.com/air780ehv/luatos/firmware/version/))、[Air780EGH V2012版本固件]([固件版本 - luatos@air780egh - 合宙模组资料中心](https://docs.openluat.com/air780egh/luatos/firmware/version/)(理论上,2025年7月26日之后发布的固件都可以)
+
+## 演示核心步骤
+
+1、搭建好硬件环境,按接线图连接硬件,
+
+2、烧录内核固件和本项目的Lua脚本:main.lua:主程序入口,netif_app.lua:网络管理模块
+
+3、启动设备,观察日志输出:
+
+```lua
+[INFO] exnetif setproxy success
+[INFO] http执行结果 200 ... 
+```
+
+4、其他设备通过以太网接入780EXX,其他设备都能正常上网,则表示验证成功。

+ 11 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirETH_1000/readme.md

@@ -0,0 +1,11 @@
+本文件下共有三个示例demo
+
+1、http:通过外挂AirETH_1000小板使用WAN功能或者多网切换模式连接http测试网络连通性。
+
+2、network_routing:
+
+(1)4g_out_ethernet_in
+
+使用网络路由功能,4G提供网络供以太网设备上网
+
+