Quellcode durchsuchen

add: Air8000 新增httpsrv demo

王文中 vor 4 Monaten
Ursprung
Commit
79f7f5a878

+ 34 - 0
module/Air8000/demo/httpsrv/check_wifi.lua

@@ -0,0 +1,34 @@
+--[[
+@module  check_wifi
+@summary 远程升级wifi固件模块
+@version 1.1
+@date    2025.10.28
+@author  拓毅恒
+@usage
+检查WiFi版本并自动升级
+功能:检查当前Air8000模组的WiFi固件是否为最新版本,若不是则自动启动升级(需确保模组已经正常联网)。
+完毕后最好取消注意:升级调用,防止后期版本升级过高导致程序使用不稳定。
+
+本文件没有对外接口,直接在main.lua中require "check_wifi"就可以加载运行。
+]]
+
+local exfotawifi = require("exfotawifi")
+
+local function fota_wifi_task()
+    local result = exfotawifi.request()
+    if result then
+        log.info("exfotawifi", "升级任务执行成功")
+    else
+        log.info("exfotawifi", "升级任务执行失败")
+    end
+
+    -- 注意:固件版本需≥V2017版本才有`AIRLINK_SFOTA_DONE`事件
+    -- 如下操作,在下载完毕后会重启模组,将版本更新到最新
+    sys.waitUntil("AIRLINK_SFOTA_DONE")
+    log.info("fotawifi","WIFI下载完毕,开始重启")
+    sys.wait(100)
+    pm.reboot()
+end
+
+-- 在设备启动时检查SIM卡状态
+sys.taskInit(fota_wifi_task)

+ 108 - 0
module/Air8000/demo/httpsrv/httpsrv_start.lua

@@ -0,0 +1,108 @@
+--[[
+@module  httpsrv_start
+@summary Air8000 HTTP服务器应用模块
+@version 1.0
+@date    2025.10.24
+@author  拓毅恒
+@usage
+本文件为Air8000开发板演示 HTTP 服务器功能的应用模块,核心业务逻辑为:
+1. 初始化HTTP服务器
+2. 配置HTTP服务器参数
+3. 启动HTTP服务器服务
+4. 处理HTTP请求和响应
+]]
+
+-- 初始化LED灯, 这里演示控制Air8000开发板绿灯,其他开发板请查看硬件原理图自行修改(如果使用整机开发板可以用GPIO146)
+local LEDA = gpio.setup(146, 0, gpio.PULLUP)
+
+local function handle_http_request(fd, method, uri, headers, body)
+    log.info("httpsrv", method, uri, body or "")
+    
+    if uri == "/led/1" then
+        LEDA(1)
+        log.info("LED Control", "Turned ON")
+        return 200, {}, "ok"
+    elseif uri == "/led/0" then
+        LEDA(0)
+        log.info("LED Control", "Turned OFF")
+        return 200, {}, "ok"
+    elseif uri == "/send/text" then
+        log.info("Text Request", "Method:", method, "Body Length:", body and #body or 0)
+        if method == "POST" and body and #body > 0 then
+            -- 直接打印接收到的文本内容
+            log.info("Received Text:", body)
+            return 200, {}, "ok"
+        end
+        return 400, {}, "Invalid request"
+    elseif uri == "/scan/go" then
+        wlan.scan()
+        return 200, {}, "ok"
+    elseif uri == "/scan/list" then
+        return 200, {["Content-Type"]="application/json"}, (json.encode({data=scan_result, ok=true}))
+    elseif uri == "/connect" then
+        if method == "POST" and body and #body > 2 then
+            local jdata = json.decode(body)
+            if jdata and jdata.ssid then
+                sys.timerStart(wlan.connect, 500, jdata.ssid, jdata.passwd)
+                return 200, {}, "ok"
+            end
+        end
+        return 400, {}, "ok"
+    elseif uri == "/connok" then
+        log.info("connok", json.encode({ip=socket.localIP(2)}))
+        return 200, {["Content-Type"]="application/json"}, json.encode({ip=socket.localIP(2)})
+    end
+    return 404, {}, "Not Found" .. uri
+end
+
+
+-- 检测当前使用的网卡适配器
+local function get_current_adapter()
+    -- 检查各个网卡是否就绪
+    if netdrv and netdrv.ready(socket.LWIP_AP) then
+        return socket.LWIP_AP, "AP"
+    elseif netdrv and netdrv.ready(socket.LWIP_STA) then
+        return socket.LWIP_STA, "STA"
+    elseif netdrv and netdrv.ready(socket.LWIP_ETH) then
+        return socket.LWIP_ETH, "ETH"
+    end
+end
+
+local function start_http_server()
+    -- 等待网络就绪事件
+    sys.waitUntil("CREATE_OK")
+    
+    -- 获取当前使用的网卡适配器
+    local adapter, adapter_name = get_current_adapter()
+    local local_ip = socket.localIP(adapter)
+    
+    -- 启动HTTP服务器
+    httpsrv.start(80, handle_http_request, adapter)
+    log.info("HTTP", "文件服务器已启动,使用" .. adapter_name .. "模式")
+    
+    if adapter == socket.LWIP_AP then
+        log.info("HTTP", "请连接WiFi: luatos8888 密码: 12345678")
+    end
+    
+    log.info("HTTP", "访问地址: http://" .. (local_ip or "192.168.4.1"))
+end
+
+local function scan_done_handle()
+    local result = wlan.scanResult()
+    scan_result = {}
+    for k, v in pairs(result) do
+        log.info("scan", (v["ssid"] and #v["ssid"] > 0) and v["ssid"] or "[隐藏SSID]", v["rssi"], (v["bssid"]:toHex()))
+        if v["ssid"] and #v["ssid"] > 0 then
+            table.insert(scan_result, {
+                ssid = v["ssid"],
+                rssi = v["rssi"]
+            })
+        end
+    end
+    log.info("scan", "aplist count:", #scan_result)
+end
+
+-- 订阅WiFi扫描完成事件
+sys.subscribe("WLAN_SCAN_DONE", scan_done_handle)
+
+sys.taskInit(start_http_server)

+ 248 - 0
module/Air8000/demo/httpsrv/index.html

@@ -0,0 +1,248 @@
+<!DOCTYPE html>
+<html lang="zh">
+<header>
+    <meta charset="utf-8" />
+    <title>Air8000 控制中心</title>
+    <style>
+        body {
+            font-family: Arial, sans-serif;
+            background-color: #f5f5f5;
+            margin: 0;
+            padding: 20px;
+            color: #333;
+        }
+        h2, h4 {
+            color: #2c3e50;
+        }
+        .container {
+            max-width: 600px;
+            margin: auto;
+            background: white;
+            padding: 20px;
+            border-radius: 8px;
+            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+        }
+        button {
+            background-color: #3498db;
+            color: white;
+            border: none;
+            padding: 10px 15px;
+            border-radius: 5px;
+            cursor: pointer;
+            font-size: 16px;
+            margin: 5px 0;
+            transition: background-color 0.3s;
+        }
+        button:hover {
+            background-color: #2980b9;
+        }
+        input[type="text"] {
+            width: 100%;
+            padding: 10px;
+            margin: 5px 0 15px;
+            border: 1px solid #ccc;
+            border-radius: 5px;
+            box-sizing: border-box;
+        }
+        .control-panel {
+            margin-bottom: 20px;
+            padding: 15px;
+            border: 1px solid #eee;
+            border-radius: 8px;
+            background-color: #fafafa;
+        }
+        .status {
+            font-weight: bold;
+            margin-top: 10px;
+            color: #27ae60;
+        }
+        .led-buttons {
+            display: flex;
+            gap: 10px;
+        }
+        .led-buttons button {
+            flex: 1;
+        }
+        #sendBtn {
+            background-color: #27ae60;
+        }
+        #sendBtn:hover {
+            background-color: #229954;
+        }
+        #scanBtn {
+            background-color: #9b59b6;
+        }
+        #scanBtn:hover {
+            background-color: #8e44ad;
+        }
+        #wifiResults {
+            margin-top: 15px;
+            max-height: 300px;
+            overflow-y: auto;
+            border: 1px solid #ddd;
+            border-radius: 5px;
+            padding: 10px;
+            background-color: #f9f9f9;
+        }
+        #wifiResults ul {
+            list-style: none;
+            padding: 0;
+            margin: 0;
+        }
+        #wifiResults li {
+            padding: 8px;
+            margin: 2px 0;
+            background-color: white;
+            border-radius: 4px;
+            border: 1px solid #eee;
+        }
+        .ssid {
+            font-weight: bold;
+            margin-right: 10px;
+        }
+        .rssi-strong {
+            color: #27ae60;
+        }
+        .rssi-medium {
+            color: #f39c12;
+        }
+        .rssi-weak {
+            color: #e74c3c;
+        }
+    </style>
+    <script type="text/javascript">
+        function led(key) {
+            fetch("/led/" + key)
+                .then(function(resp) {
+                    if (resp.status == 200) {
+                        document.getElementById("status").textContent = "LED已" + (key == 1 ? "开启" : "关闭");
+                    }
+                })
+        }
+        function sendText() {
+            var text = document.getElementById("inputText").value;
+            if (text.trim() === "") {
+                alert("请输入文本内容");
+                return;
+            }
+            
+            // 添加调试日志
+            console.log("发送文本:", text);
+            
+            fetch("/send/text", {
+                method: "POST",
+                headers: {
+                    "Content-Type": "text/plain"
+                },
+                body: text
+            }).then(function(resp) {
+                console.log("响应状态:", resp.status);
+                if (resp.status == 200) {
+                    document.getElementById("status").textContent = "文本已发送至设备日志";
+                    document.getElementById("inputText").value = "";
+                } else {
+                    document.getElementById("status").textContent = "发送失败: " + resp.status;
+                }
+            }).catch(function(error) {
+                console.error("发送错误:", error);
+                document.getElementById("status").textContent = "发送出错: " + error.message;
+            });
+        }
+        
+        // 按下回车键时发送文本
+        function handleKeyPress(event) {
+            if (event.key === "Enter") {
+                sendText();
+            }
+        }
+        
+        // WiFi扫描功能
+        function scanWifi() {
+            document.getElementById("status").textContent = "正在扫描WiFi...";
+            document.getElementById("wifiResults").innerHTML = "";
+            
+            fetch("/scan/go")
+                .then(function(resp) {
+                    if (resp.status == 200) {
+                        document.getElementById("status").textContent = "扫描已开始,正在获取结果...";
+                        // 等待1秒后获取扫描结果
+                        setTimeout(getWifiResults, 1000);
+                    } else {
+                        document.getElementById("status").textContent = "扫描失败: " + resp.status;
+                    }
+                })
+                .catch(function(error) {
+                    console.error("扫描错误:", error);
+                    document.getElementById("status").textContent = "扫描出错: " + error.message;
+                });
+        }
+        
+        // 获取WiFi扫描结果
+        function getWifiResults() {
+            fetch("/scan/list")
+                .then(function(resp) {
+                    if (resp.status == 200) {
+                        return resp.json();
+                    } else {
+                        throw new Error("获取结果失败: " + resp.status);
+                    }
+                })
+                .then(function(data) {
+                    const resultsDiv = document.getElementById("wifiResults");
+                    if (data && data.data && data.data.length > 0) {
+                        let html = "<ul>";
+                        data.data.forEach(function(ap) {
+                            let rssiClass = "rssi-weak";
+                            if (ap.rssi > -70) rssiClass = "rssi-strong";
+                            else if (ap.rssi > -85) rssiClass = "rssi-medium";
+                            
+                            html += `<li><span class="ssid">${ap.ssid}</span> <span class="${rssiClass}">信号: ${ap.rssi} dBm</span></li>`;
+                        });
+                        html += "</ul>";
+                        resultsDiv.innerHTML = html;
+                        document.getElementById("status").textContent = "扫描完成,共发现 " + data.data.length + " 个WiFi网络";
+                    } else {
+                        resultsDiv.innerHTML = "未发现WiFi网络";
+                        document.getElementById("status").textContent = "扫描完成,未发现WiFi网络";
+                    }
+                })
+                .catch(function(error) {
+                    console.error("获取结果错误:", error);
+                    document.getElementById("status").textContent = "获取结果出错: " + error.message;
+                });
+        }
+    </script>
+</header>
+<body>
+    <div class="container">
+        <h2>Air8000 控制中心</h2>
+        
+        <!-- 文本发送功能 -->
+        <div class="control-panel">
+            <h4>文本发送</h4>
+            <label for="inputText">输入文本:</label>
+            <input type="text" id="inputText" placeholder="请输入要发送到设备日志的文本" onkeypress="handleKeyPress(event)">
+            <button id="sendBtn" onclick="sendText()">发送文本</button>
+        </div>
+        
+        <!-- LED控制功能 -->
+        <div class="control-panel">
+            <h4>LED控制</h4>
+            <div class="led-buttons">
+                <button onclick="led(1)">开启LED</button>
+                <button onclick="led(0)">关闭LED</button>
+            </div>
+        </div>
+        
+        <!-- WiFi扫描功能 -->
+        <div class="control-panel">
+            <h4>WiFi扫描</h4>
+            <button id="scanBtn" onclick="scanWifi()">扫描WiFi网络</button>
+            <div id="wifiResults">请点击上方按钮开始扫描</div>
+        </div>
+        
+        <!-- 状态显示 -->
+        <div class="status" id="status">就绪</div>
+    </div>
+</body>
+</html>

+ 77 - 0
module/Air8000/demo/httpsrv/main.lua

@@ -0,0 +1,77 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 1.0
+@date    2025.10.24
+@author  拓毅恒
+@usage
+本demo演示的核心功能为:
+HTTP服务器应用功能,通过加载httpsrv_start模块来启动和配置HTTP服务器,处理HTTP请求和响应。
+
+netdrv_device:配置连接外网使用的网卡,目前支持以下三种选择(三选一)
+   (1) netdrv_4g:4G网卡
+   (2) netdrv_wifi:WIFI STA网卡
+   (3) netdrv_eth_spi:通过SPI外挂CH390H芯片的以太网卡
+
+更多说明参考本目录下的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 = "httpsrv_testdemo"
+VERSION = "001.000.000"
+
+log.info("main", "project name is ", PROJECT, "version is ", 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)
+
+-- 如果无法使用此功能,可以开启此功能升级WiFi固件版本后再次尝试
+-- 升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定
+-- require "check_wifi" 
+
+-- 加载网络驱动设备功能模块
+require "netdrv_device"
+
+-- 加载 httpsrv_start 功能模块
+require "httpsrv_start"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 50 - 0
module/Air8000/demo/httpsrv/netdrv/netdrv_ap.lua

@@ -0,0 +1,50 @@
+--[[
+@module  netdrv_ap
+@summary "WIFI AP网卡"驱动模块
+@version 1.0
+@date    2025.11.4
+@author  拓毅恒
+@usage
+本文件为WIFI AP网卡驱动模块,核心业务逻辑为:
+1、初始化网络;
+2、创建WIFI AP热点;
+3、配置IP地址和DHCP服务器;
+4、发布AP创建完成事件;
+
+本文件没有对外接口,直接在其他功能模块中require "netdrv_ap"就可以加载运行;
+]]
+
+dnsproxy = require("dnsproxy")
+dhcpsrv = require("dhcpsrv")
+
+-- AP热点创建完成回调函数
+local function ap_ready_func()
+    log.info("netdrv_ap", "AP热点创建成功,IP地址为: 192.168.4.1")
+    -- 发布AP创建完成事件
+    sys.publish("CREATE_OK")
+end
+
+-- 创建并启动AP热点初始化任务
+local function netdrv_ap_init_task()
+    -- 初始化WIFI
+    wlan.init()
+    log.info("netdrv_ap", "执行AP创建操作", "luatos8888")
+    sys.wait(100)
+    -- 创建AP热点,名称为luatos8888,密码为12345678
+    wlan.createAP("luatos8888", "12345678")
+    -- AP启动成功后,设置IP地址和DHCP服务器
+    netdrv.ipv4(socket.LWIP_AP, "192.168.4.1", "255.255.255.0", "0.0.0.0")
+    -- 等待网络准备就绪
+    while netdrv.ready(socket.LWIP_AP) ~= true do
+        sys.wait(100)
+    end
+    -- 设置DNS代理
+    dnsproxy.setup(socket.LWIP_AP, socket.LWIP_GP)
+    -- 创建DHCP服务器
+    dhcpsrv.create({adapter=socket.LWIP_AP})
+    -- 调用AP就绪回调
+    ap_ready_func()
+end
+
+-- 启动AP初始化任务
+sys.taskInit(netdrv_ap_init_task)

+ 77 - 0
module/Air8000/demo/httpsrv/netdrv/netdrv_eth_spi.lua

@@ -0,0 +1,77 @@
+--[[
+@module  netdrv_eth_spi
+@summary "以太网SPI网卡"驱动模块
+@version 1.0
+@date    2025.11.4
+@author  拓毅恒
+@usage
+本文件为以太网SPI网卡驱动模块,核心业务逻辑为:
+1、初始化以太网SPI接口;
+2、配置以太网适配器;
+3、设置IP地址;
+4、当网络连接成功后,会发布CREATE_OK事件通知HTTP服务器启动;
+
+本文件没有对外接口,直接在其他功能模块中require "netdrv_eth_spi"就可以加载运行;
+]]
+
+gpio.setup(140, 1, gpio.PULLUP)     --打开ch390供电
+
+-- 以太网IP状态变化处理
+local function eth_ip_ready_func(ip, adapter)
+    if adapter == socket.LWIP_ETH then
+        log.info("netdrv_eth_spi", "IP_READY", ip)
+        -- 发布CREATE_OK事件,通知HTTP服务器启动
+        sys.publish("CREATE_OK")
+    end
+end
+
+-- 订阅以太网相关事件
+sys.subscribe("IP_READY", eth_ip_ready_func)
+
+-- 创建并启动以太网初始化任务
+local function netdrv_eth_init_task()
+    -- 设置默认网卡为socket.LWIP_ETH
+    socket.dft(socket.LWIP_ETH)
+    
+    -- 初始化SPI接口连接CH390
+    local result = spi.setup(
+        1,--串口id
+        nil,
+        0,--CPHA
+        0,--CPOL
+        8,--数据宽度
+        25600000--频率
+    )
+    if result ~= 0 then--返回值为0,表示打开成功
+        log.info("netdrv_eth_spi", "SPI初始化失败", result)
+        return
+    end
+    log.info("netdrv_eth_spi", "SPI初始化成功")
+
+    -- 设置CH390驱动和网络参数
+    netdrv.setup(socket.LWIP_ETH, netdrv.CH390, {spi=1,cs=12})
+    sys.wait(3000)
+    
+    -- 配置从路由器获取IP地址(DHCP客户端模式)
+    log.info("netdrv_ethernet_spi", "开始从路由器获取IP地址...")
+    netdrv.dhcp(socket.LWIP_ETH, true)  -- 启用DHCP客户端
+    sys.wait(3000)
+    
+    -- 获取并显示分配的IP地址
+    local ipv4, mark, gw = netdrv.ipv4(socket.LWIP_ETH)
+    -- 手动设置IP地址
+    -- local ipv4,mark, gw = netdrv.ipv4(socket.LWIP_ETH, "192.168.4.1", "255.255.255.0", "192.168.4.1")
+    server_ip = ipv4
+    log.info("netdrv_ethernet_spi", "IP配置完成:", ipv4, mark, gw)
+    -- 等待以太网连接
+    while netdrv.link(socket.LWIP_ETH) ~= true do
+        sys.wait(100)
+    end
+    while netdrv.link(socket.LWIP_GP) ~= true do
+        sys.wait(100)
+    end
+    log.info("netdrv_ethernet_spi", "以太网连接状态:", netdrv.link(socket.LWIP_ETH))
+end
+
+-- 启动以太网初始化任务
+sys.taskInit(netdrv_eth_init_task)

+ 55 - 0
module/Air8000/demo/httpsrv/netdrv/netdrv_wifi.lua

@@ -0,0 +1,55 @@
+--[[
+@module  netdrv_wifi
+@summary “WIFI STA网卡”驱动模块
+@version 1.0
+@date    2025.11.4
+@author  拓毅恒
+@usage
+本文件为WIFI STA网卡驱动模块,核心业务逻辑为:
+1、初始化WIFI网络;
+2、连接WIFI路由器;
+3、和WIFI路由器之间的连接状态发生变化时,在日志中进行打印;
+
+本文件没有对外接口,直接在其他功能模块中require "netdrv_wifi"就可以加载运行;
+]]
+
+local function ip_ready_func(ip, adapter)
+    if adapter == socket.LWIP_STA then
+        log.info("netdrv_wifi.ip_ready_func", "IP_READY", json.encode(wlan.getInfo()))
+        -- STA模式联网成功后发布CREATE_OK事件,通知HTTP服务器启动
+        sys.publish("CREATE_OK")
+    end
+end
+
+local function ip_lose_func(adapter)
+    if adapter == socket.LWIP_STA then
+        log.warn("netdrv_wifi.ip_lose_func", "IP_LOSE")
+    end
+end
+
+
+-- 此处订阅"IP_READY"和"IP_LOSE"两种消息
+-- 在消息的处理函数中,仅仅打印了一些信息,便于实时观察WIFI的连接状态
+-- 也可以根据自己的项目需求,在消息处理函数中增加自己的业务逻辑控制,例如可以在连网状态发生改变时更新网络图标
+sys.subscribe("IP_READY", ip_ready_func)
+sys.subscribe("IP_LOSE", ip_lose_func)
+
+
+-- 设置默认网卡为socket.LWIP_STA
+socket.dft(socket.LWIP_STA)
+
+
+wlan.init()
+-- 连接WIFI热点,连接结果会通过"IP_READY"或者"IP_LOSE"消息通知
+-- Air8000仅支持2.4G的WIFI,不支持5G的WIFI
+-- 此处前两个参数表示WIFI热点名称以及密码,更换为自己测试时的真实参数即可
+-- 第三个参数1表示WIFI连接异常时,内核固件会自动重连
+wlan.connect("茶室-降功耗,找合宙!", "Air123456", 1)
+
+-- WIFI联网成功(做为STATION成功连接AP,并且获取到了IP地址)后,内核固件会产生一个"IP_READY"消息
+-- 各个功能模块可以订阅"IP_READY"消息实时处理WIFI联网成功的事件
+-- 也可以在任何时刻调用socket.adapter(socket.LWIP_STA)来获取WIFI网络是否连接成功
+
+-- WIFI断网后,内核固件会产生一个"IP_LOSE"消息
+-- 各个功能模块可以订阅"IP_LOSE"消息实时处理WIFI断网的事件
+-- 也可以在任何时刻调用socket.adapter(socket.LWIP_STA)来获取WIFI网络是否连接成功

+ 29 - 0
module/Air8000/demo/httpsrv/netdrv_device.lua

@@ -0,0 +1,29 @@
+--[[
+@module  netdrv_device
+@summary 网络驱动设备功能模块
+@version 1.0
+@date    2025.11.4
+@author  拓毅恒
+@usage
+本文件为网络驱动设备功能模块,核心业务逻辑为:根据项目需求,选择并且配置合适的网卡(网络适配器)
+1、netdrv_ap:socket.LWIP_AP,WIFI AP网卡;
+2、netdrv_wifi:socket.LWIP_STA,WIFI STA网卡;
+3、netdrv_eth_spi:socket.LWIP_ETH,通过SPI外挂CH390H芯片的以太网卡;
+
+无论选择哪种网卡模式,在成功联网后都会发布"CREATE_OK"事件,用于通知httpsrv_start.lua启动HTTP服务器。
+
+使用说明:取消注释下面对应网卡的require语句即可使用该网卡模式。
+本文件没有对外接口,直接在main.lua中require "netdrv_device"就可以加载运行;
+]]
+
+-- 配置选择的网卡模式,取消注释对应行以启用
+
+-- 加载"WIFI AP网卡"驱动模块(默认启用)
+require "netdrv_ap"
+
+-- 加载"WIFI STA网卡"驱动模块
+-- require "netdrv_wifi"
+
+-- 加载"通过SPI外挂CH390H芯片的以太网卡"驱动模块
+-- 注意:使用此模式需要确保硬件连接正确且芯片驱动已加载
+-- require "netdrv_eth_spi"

+ 198 - 0
module/Air8000/demo/httpsrv/readme.md

@@ -0,0 +1,198 @@
+## 功能模块介绍
+
+1、main.lua:主程序入口;
+
+2、httpsrv_start.lua:HTTP服务器实现模块,包含服务器初始化、路由处理、LED控制、文本发送和WiFi扫描功能;
+
+3、check_wifi.lua:WiFi固件升级模块,负责检查当前Air8000模组的WiFi固件是否为最新版本,若不是则自动启动升级(需确保模组已经正常联网)。
+
+4、index.html:Web控制界面,提供LED控制按钮、文本发送输入框和WiFi扫描功能;
+
+5、netdrv_device.lua:网络驱动设备功能模块,用于选择和配置合适的网卡模式;
+
+6、netdrv/netdrv_ap.lua:WiFi AP模式网卡驱动,创建WiFi热点;
+
+7、netdrv/netdrv_wifi.lua:WiFi STA模式网卡驱动,连接外部WiFi路由器;
+
+8、netdrv/netdrv_eth_spi.lua:以太网SPI网卡驱动,通过SPI接口连接CH390H芯片实现有线网络连接;
+
+## 演示功能概述
+
+1、HTTP服务器:创建Web服务器,提供Web控制界面
+
+- 支持三种网卡模式:WiFi AP模式、WiFi STA模式和以太网SPI模式
+- HTTP服务器监听80端口,具体IP地址取决于使用的网卡模式:
+  - WiFi AP模式:自动创建名为"luatos8888"的WiFi热点,密码为"12345678",IP地址为192.168.4.1
+  - WiFi STA模式:连接外部WiFi路由器,IP地址由路由器DHCP分配
+  - 以太网SPI模式:通过网线连接网络,IP地址由路由器DHCP分配
+- 支持访问Web控制界面
+
+2、LED控制功能:通过Web界面控制设备上的LED灯
+
+- 提供点亮LED(/led/1)接口
+- 提供熄灭LED(/led/0)接口
+- Web界面上有对应的控制按钮
+
+3、文本发送功能:通过Web界面发送文本数据
+
+- 提供文本发送(/send/text)接口
+- 支持在Web界面的输入框中输入文本并发送
+- 发送的文本会在设备日志中显示
+
+4、WiFi扫描功能:搜索周围可用的WiFi热点
+
+- 提供开始扫描(/scan/go)接口
+- 提供获取扫描结果(/scan/list)接口
+- Web界面上有扫描按钮,点击后显示周围WiFi热点列表
+- 显示WiFi的SSID和信号强度信息
+
+## 演示硬件环境
+
+1、Air8000开发板一块+可上网的sim卡一张+wifi天线一根:
+
+- 天线装到开发板上
+
+2、TYPE-C USB数据线一根 + USB转串口数据线一根,Air8000开发板和数据线的硬件接线方式为:
+
+- Air8000开发板通过TYPE-C USB口供电;(外部供电/USB供电 拨动开关 拨到 USB供电一端)
+- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+## 选择网卡模式
+
+本项目支持三种网卡模式,默认使用WiFi AP模式。要切换网卡模式,请按照以下步骤操作:
+
+1、打开 `netdrv_device.lua` 文件
+
+2、取消注释你想要使用的网卡模式对应的require语句,并确保其他模式的require语句保持注释状态
+
+示例:
+
+- 使用WiFi AP模式(默认):
+
+```lua
+-- 加载"WIFI AP网卡"驱动模块(默认启用)
+require "netdrv_ap"
+
+-- 加载"WIFI STA网卡"驱动模块
+-- require "netdrv_wifi"
+
+-- 加载"通过SPI外挂CH390H芯片的以太网卡"驱动模块
+-- require "netdrv_eth_spi"
+```
+
+- 使用WiFi STA模式:
+
+```lua
+-- 加载"WIFI AP网卡"驱动模块(默认启用)
+-- require "netdrv_ap"
+
+-- 加载"WIFI STA网卡"驱动模块
+require "netdrv_wifi"
+
+-- 加载"通过SPI外挂CH390H芯片的以太网卡"驱动模块
+-- require "netdrv_eth_spi"
+```
+
+- 使用 eth 模式:
+
+```lua
+-- 加载"WIFI AP网卡"驱动模块(默认启用)
+-- require "netdrv_ap"
+
+-- 加载"WIFI STA网卡"驱动模块
+-- require "netdrv_wifi"
+
+-- 加载"通过SPI外挂CH390H芯片的以太网卡"驱动模块
+require "netdrv_eth_spi"
+```
+
+3、如果使用WiFi STA模式,请修改 `netdrv/netdrv_wifi.lua` 文件中的WiFi配置:
+
+```lua
+-- 连接WIFI热点,连接结果会通过"IP_READY"或者"IP_LOSE"消息通知
+-- 此处前两个参数表示WIFI热点名称以及密码,更换为自己测试时的真实参数
+wlan.connect("你的WiFi名称", "你的WiFi密码", 1)
+```
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+2、Air8000固件[Air8000 版本固件](https://docs.openluat.com/air8000/luatos/firmware/)
+
+## 演示核心步骤
+
+### WiFi AP模式(默认)
+
+1、搭建好硬件环境
+
+2、使用Luatools烧录内核固件和demo脚本代码
+
+3、烧录成功后,设备自动开机运行,创建名为"luatos8888"的WiFi热点(密码:12345678)
+
+4、使用电脑或手机连接到"luatos8888" WiFi热点
+
+5、在浏览器中输入地址:http://192.168.4.1,访问Web控制界面
+
+### WiFi STA模式
+
+1、按照选择网卡模式的说明,配置为WiFi STA模式并设置正确的WiFi名称和密码
+
+2、使用Luatools烧录内核固件和demo脚本代码
+
+3、烧录成功后,设备自动开机运行并尝试连接配置的WiFi路由器
+
+4、通过Luatools日志查看设备获取的IP地址(例如:192.168.1.100)
+
+5、确保你的电脑或手机连接到同一WiFi网络
+
+6、在浏览器中输入设备的IP地址(如http://192.168.1.100),访问Web控制界面
+
+### 以太网SPI模式
+
+1、按照选择网卡模式的说明,配置为以太网SPI模式
+
+2、确保CH390H以太网模块正确连接到Air8000开发板
+
+3、使用网线将以太网模块连接到路由器或网络交换机
+
+4、使用Luatools烧录内核固件和demo脚本代码
+
+5、烧录成功后,设备自动开机运行并尝试通过以太网连接到网络
+
+6、通过Luatools日志查看设备获取的IP地址(例如:192.168.1.101)
+
+7、确保你的电脑连接到同一路由器或网络
+
+8、在浏览器中输入设备的IP地址(如http://192.168.1.101),访问Web控制界面
+
+## Web控制界面功能
+
+在浏览器访问Web控制界面后,你可以使用以下功能:
+
+- 控制LED灯的开关
+- 发送文本消息(会显示在设备日志中)
+- 点击WiFi扫描按钮,查看周围可用的WiFi热点列表
+
+```lua
+[2025-10-23 23:44:18.901][000000000.433] I/user.main project name is  httpsrv_testdemo version is  001.000.000
+[2025-10-23 23:44:18.902][000000000.484] I/user.执行AP创建操作 luatos8888
+[2025-10-23 23:44:19.116][000000000.907] D/airlink wifi ap已开启 0.0.0.0 c10f378
+[2025-10-23 23:44:19.119][000000000.907] D/netdrv 网卡(3)设置为UP
+[2025-10-23 23:44:19.799][000000001.584] D/net network ready 3, setup dns server
+[2025-10-23 23:44:19.803][000000001.585] I/user.dnsproxy 3 1
+[2025-10-23 23:44:19.805][000000001.586] I/user.dnsproxy 开始监听
+[2025-10-23 23:44:19.808][000000001.586] D/socket connect to 255.255.255.255,0
+[2025-10-23 23:44:19.810][000000001.586] D/net adapter 3 connect 255.255.255.255:0 UDP
+[2025-10-23 23:44:19.812][000000001.587] D/socket connect to 119.29.29.29,53
+[2025-10-23 23:44:19.813][000000001.588] I/user.dhcpsrv 自动获取网卡IP作为网关 192.168.4.1
+[2025-10-23 23:44:19.816][000000001.589] D/socket connect to 255.255.255.255,0
+[2025-10-23 23:44:19.817][000000001.589] D/net adapter 3 connect 255.255.255.255:0 UDP
+[2025-10-23 23:44:19.819][000000001.590] I/user.WIFI AP热点创建成功
+[2025-10-23 23:44:19.820][000000001.591] I/httpsrv http listen at 192.168.4.1:80
+[2025-10-23 23:44:19.822][000000001.592] I/user.HTTP 文件服务器已启动
+[2025-10-23 23:44:19.823][000000001.592] I/user.HTTP 请连接WiFi: luatos8888 密码: 12345678
+[2025-10-23 23:44:19.826][000000001.592] I/user.HTTP 然后访问: http://192.168.4.1/
+```
+
+7、通过Luatools工具,可以查看设备的运行日志,包括收到的文本消息和WiFi扫描结果