wjq пре 3 месеци
родитељ
комит
e905b6b9e6

+ 130 - 0
module/Air8101/demo/wlan/wlan/ap_config_net.lua

@@ -0,0 +1,130 @@
+--[[
+@module  ap_config_net
+@summary wifi配网功能模块 
+@version 1.0
+@date    2025.10.20
+@author  魏健强
+@usage 本文为wifi配网功能模块,核心逻辑为
+1、开启wifi_ap热点;
+2、模块开启http服务器;
+3、用户通过手机等设备连接wifi_ap热点,访问http网页进行wifi配网;
+直接使用Air8101核心板硬件测试即可;
+
+本文件没有对外接口,直接在其他功能模块中require "ap_config_net"就可以加载运行;
+]]
+dhcpsrv = require("dhcpsrv")
+ 
+-- 初始化LED灯, 根据实际GPIO修改
+-- local LEDA= gpio.setup(12, 0, gpio.PULLUP)
+
+local scan_result = {}
+
+local function create_ap()
+    log.info("执行AP创建操作", "test2")
+    wlan.createAP("test2", "HZ88888888")
+    netdrv.ipv4(socket.LWIP_AP, "192.168.4.1", "255.255.255.0", "192.168.4.1")
+    dhcpsrv.create({
+        adapter = socket.LWIP_AP
+    })
+end
+
+local function handle_http_request(fd, method, uri, headers, body)
+    -- log.info("httpsrv", method, uri, json.encode(headers), body)
+    log.info("httpsrv", "fd", fd, "method", method, "uri", uri, "headers", json.encode(headers), "body", body)
+    -- /led是控制灯的API
+    if uri == "/led/1" then
+        -- LEDA(1)
+        log.info("led", "on")
+        return 200, {}, "ok"
+    elseif uri == "/led/0" then
+        -- LEDA(0)
+        log.info("led", "off")
+        return 200, {}, "ok"
+        -- 处理消息
+    elseif uri == "/msg" then
+        local messageData = json.decode(body) -- 假设消息是 JSON 格式
+        if messageData and messageData.message then
+            log.info("Received message:", messageData.message)
+            -- 处理接收到的消息,例如保存、转发、响应等等
+            return 200, {}, "Message received: " .. messageData.message
+        end
+
+        -- 扫描AP
+    elseif uri == "/scan/go" then
+        wlan.scan()
+        log.info("scan", "start")
+        return 200, {}, "ok"
+        -- 前端获取AP列表
+    elseif uri == "/scan/list" then
+        return 200, {
+            ["Content-Type"] = "applaction/json"
+        }, (json.encode({
+            data = scan_result,
+            ok = true
+        }))
+        -- 前端填好了ssid和密码, 那就连接吧
+    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"
+        -- 根据ip地址来判断是否已经连接成功
+    elseif uri == "/connok" then
+        return 200, {
+            ["Content-Type"] = "applaction/json"
+        }, json.encode({
+            ip = socket.localIP()
+        })
+    elseif uri == "/send" then
+        if method == "POST" and body and #body > 2 then
+            local jdata = json.decode(body)
+            if jdata and jdata.msg then
+                log.info("Received message:", jdata.msg)
+                return 200, {}, "Message received"
+            end
+        end
+        return 400, {}, "Bad Request"
+
+    end
+    -- 其他情况就是找不到了
+    return 404, {}, "Not Found" .. uri
+end
+
+local function wifi_networking()
+    httpsrv.start(80, handle_http_request, socket.LWIP_AP)
+    log.info("web", "pls open url http://192.168.4.1/")
+end
+
+-- wifi扫描成功后, 会有WLAN_SCAN_DONE消息, 读取即可
+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, v["ssid"])
+        end
+    end
+    log.info("scan", "aplist", json.encode(scan_result))
+end
+
+local function main_task()
+    wlan.init()
+    create_ap()
+    wifi_networking()
+end
+
+local function ip_ready_handle()
+    -- wifi联网成功后, 在这里进行后续应用逻辑的扩展处理
+    log.info("wlan", "已联网")
+    -- sys.taskInit(sockettest)
+end
+
+sys.subscribe("WLAN_SCAN_DONE", scan_done_handle)
+sys.subscribe("IP_READY", ip_ready_handle)
+sys.taskInit(main_task)

+ 173 - 0
module/Air8101/demo/wlan/wlan/index.html

@@ -0,0 +1,173 @@
+
+<!DOCTYPE html>
+<html lang="zh">
+<header>
+    <meta charset="utf-8" />
+    <title>Air8101 Wi-Fi 配网系统</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;
+        }
+        select, input[type="text"] {
+            width: 100%;
+            padding: 10px;
+            margin: 5px 0 15px;
+            border: 1px solid #ccc;
+            border-radius: 5px;
+        }
+        #aplist {
+            height: 40px;
+        }
+        .status {
+            font-weight: bold;
+            margin-top: 10px;
+        }
+    </style>
+    <!-- fetch api-->
+    <!-- <script type="text/javascript" src="/petite-vue.js"></script> -->
+    <script type="text/javascript">
+        function led(key) {
+            fetch("/led/" + key)
+        }
+        function sendMessage() {
+            const message = document.getElementById('messageInput').value;
+            fetch('/msg', {
+                method: 'POST',
+                headers: {
+                    'Content-Type': 'application/json'
+                },
+                body: JSON.stringify({ message: message })
+            })
+        }
+        function wifi_get_aplist() {
+            fetch("/scan/list").then(function (resp) {
+                console.log(resp, resp.status)
+                if (resp.status != 200) {
+                    return
+                }
+                resp.json().then(function (data) {
+                    console.log("data", data)
+                    var tmp = ""
+                    for (let index = 0; index < data.data.length; index++) {
+                        const apname = data.data[index];
+                        if (index == 0) {
+                            tmp += "<option value='" + index + "' selected>" + apname + "</option>\n"
+                        } else {
+                            tmp += "<option value='" + index + "'>" + apname + "</option>\n"
+                        }
+                    }
+                    document.getElementById("aplist").innerHTML = tmp
+                })
+            })
+        }
+        function wifi_scan() {
+            fetch("/scan/go")
+            setTimeout(wifi_get_aplist, 3000)
+        }
+        function wifi_connect() {
+            var ssid = document.getElementById("ssid").value
+            var passwd = document.getElementById("passwd").value
+            console.log(ssid, passwd)
+            fetch("/connect", {
+                method: "POST",
+                body: JSON.stringify({ ssid: ssid, passwd: passwd })
+            }).then(function (resp) {
+                if (resp.status == 200) {
+                    alert("正在尝试连接")
+                } else {
+                    alert("出了点问题")
+                }
+            })
+        }
+        function wifi_ipstat() {
+            fetch("/connok").then(function (resp) {
+                if (resp.status != 200)
+                    return
+                resp.json().then(function (data) {
+                    console.log(data)
+                    if (data && data.ip != "0.0.0.0") {
+                        document.getElementById("ipstat").innerHTML = "已联网"
+                    }
+                })
+            })
+        }
+        function select_changed(event) {
+            var apselect = document.getElementById("aplist")
+            var ssid = document.getElementById("ssid")
+            ssid.value = apselect.options[apselect.selectedIndex].text
+        }
+
+        setTimeout(wifi_get_aplist, 3000)
+    </script>
+</header>
+
+<body>
+    <div class="container">
+        <h1>Air8101工业引擎 Wi-Fi 配网系统</h1>
+        <h2>LED 控制</h2>
+        <div>
+            <button onclick="led(1)">LED亮</button>
+            <button onclick="led(0)">LED灭</button>
+        </div>
+        <h2>发送消息</h2>
+        <div>
+            <input type="text" id="messageInput" placeholder="输入消息" />
+            <button onclick="sendMessage()">发送消息</button>
+        </div>
+
+        <h2>AP WEB 配网</h2>
+        <div v-scope="{aps:[]}"></div>
+            <div>
+                <button onclick="wifi_scan()">扫描 Wi-Fi</button>
+            </div>
+            <div>
+                <h4>Wi-Fi 列表</h4>
+                <select id="aplist" onchange="select_changed()">
+                    <option vaule="">Wi-Fi名称</option>
+                </select>
+                <input type="text" id="ssid" placeholder="输入 Wi-Fi 名称" style="width: 580px;"/>
+                <input type="text" id="passwd" placeholder="输入 Wi-Fi 密码" style="width: 580px;"/>
+                <button onclick="wifi_connect()">连接</button>
+            </div>
+            <div>
+                <h4>联网状态:</h4>
+                <span id="ipstat" class="status">未联网</span>
+                <button onclick="wifi_ipstat()">检查状态</button>
+            </div>
+            <div>
+                <h4>Power by <a href="https://docs.openluat.com">LuatOS</a></h4>
+            </div>
+        </div>
+    </div>
+</body>
+
+</html>

+ 72 - 0
module/Air8101/demo/wlan/wlan/main.lua

@@ -0,0 +1,72 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 1.0
+@date    2025.10.20
+@author  魏健强
+@usage
+本demo演示的核心功能为:
+1.启用wifi STA功能
+2.wifi_ap配网
+3.wifi扫描
+更多说明参考本目录下的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 = "air8101_wifi"
+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 "wifi_sta"
+require "ap_config_net"
+-- require "wifi_scan"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 55 - 0
module/Air8101/demo/wlan/wlan/readme.md

@@ -0,0 +1,55 @@
+## 功能模块介绍
+
+1、main.lua:主程序入口;
+
+2、wifi_sta.lua:wifi_sta功能
+
+3、wifi_scan.lua:wifi扫描
+
+4、ap_config_net.lua:wifi_ap配网
+
+## 演示功能概述
+
+- STA:展示air8101:使用wifi 路由器上网能力
+- ap_config_net:AP方式配网,即通过链接air8101 wifi 的AP(热点),打开网页配置需要连接的wifi名称和密码
+- wifi_scan:扫描周围2.4Gwifi的名称和mac
+
+## 演示硬件环境
+
+### Air8101 核心板
+
+![](https://docs.openluat.com/air8101/luatos/app/multinetwork/4G/image/LzuBbS3NxoVu34x4dj7c3d04nDb.jpg)
+Air8101 核心板一块 + TYPE-C USB 数据线一根
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+2、内核固件:[Air8101 V1006 版本固件](https://docs.openluat.com/air8101/luatos/firmware/)如有更新可以使用最新固件。
+
+## 演示核心步骤
+
+1、搭建好硬件环境,按接线图连接硬件,
+
+2、测试wifi功能时按需修改WiFi配置:
+ap_config_net.lua中的wlan.createAP("test", "HZ88888888"),修改生成wifi热点的名称和密码.
+wifi_sta.lua中的wlan.connect("test", "HZ88888888")修改需要连接的wifi的名称和密码
+
+3、在main.lua中按照自己的网卡需求启用对应的Lua文件
+
+- 如果需要测试wifi_sta功能,打开require "wifi_sta",其余注释掉
+
+- 如果需要测试wifi扫描功能,打开require "wifi_scan",其余注释掉
+
+- 如果需要测试wifi_ap配网功能,打开require "ap_config_net",其余注释掉
+
+4、Luatools烧录内核固件和修改后的demo脚本代码
+
+wifi_sta:模块连接wifi成功,http请求测试成功
+![](https://docs.openluat.com/air8101/luatos/app/image/8101-wlan4.png)
+
+wifi_scan:模块扫描附近2.4Gwifi并打印扫描结果
+![](https://docs.openluat.com/air8101/luatos/app/image/8101-wlan3.png)
+
+ap_config_net:电脑连接模块的ap热点,浏览器打开192.168.4.1通过网页配置模块连接对应wifi
+![](https://docs.openluat.com/air8101/luatos/app/image/8101-wlan1.png)

+ 38 - 0
module/Air8101/demo/wlan/wlan/wifi_scan.lua

@@ -0,0 +1,38 @@
+--[[
+@module  wifi_scan
+@summary wifi_scan模块 
+@version 1.0
+@date    2025.10.20
+@author  魏健强
+@usage 本文为wifi扫描功能模块,核心逻辑为
+1、开启WiFi扫描;
+2、打印扫描结果;
+
+本文件没有对外接口,直接在其他功能模块中require "wifi_scan"就可以加载运行;
+]] 
+local scan_result = {}
+
+wlan.init()
+function test_scan()
+    while true do
+        log.info("10秒后执行wifi扫描")
+        sys.wait(10 * 1000)
+        wlan.scan()
+    end
+end
+
+function scan_done_handle()
+    local result = wlan.scanResult()
+    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, v["ssid"])
+        end
+    end
+    log.info("scan", "aplist", json.encode(scan_result))
+end
+
+
+sys.subscribe("WLAN_SCAN_DONE", scan_done_handle)
+
+sys.taskInit(test_scan)

+ 47 - 0
module/Air8101/demo/wlan/wlan/wifi_sta.lua

@@ -0,0 +1,47 @@
+--[[
+@module  wifi_sta
+@summary wifi_sta模块 
+@version 1.0
+@date    2025.10.20
+@author  魏健强
+@usage 本文为wifi_sta功能模块,核心逻辑为
+1、模块连接wifi;
+2、发送http请求,测试网络;
+本文件没有对外接口,直接在其他功能模块中require "wifi_sta"就可以加载运行;
+]] 
+-- wifi的STA相关事件
+sys.subscribe("WLAN_STA_INC", function(evt, data)
+    -- evt 可能的值有: "CONNECTED", "DISCONNECTED"
+    -- 当evt=CONNECTED, data是连接的AP的ssid, 字符串类型
+    -- 当evt=DISCONNECTED, data断开的原因, 整数类型
+    log.info("收到STA事件", evt, data)
+end)
+
+
+function test_sta()
+    wlan.init()
+    log.info("执行STA连接操作")
+    wlan.connect("test", "HZ88888888")
+    -- 等待wifi_sta网络连接成功
+    while not socket.adapter(socket.LWIP_STA) do
+        -- 在此处阻塞等待wifi连接成功的消息"IP_READY"
+        -- 或者等待1秒超时退出阻塞等待状态;
+        -- 注意:此处的1000毫秒超时不要修改的更长;
+        sys.waitUntil("IP_READY", 1000)
+    end
+    while true do
+        local code, headers, body = http.request("GET", "https://httpbin.air32.cn/bytes/2048", nil, nil, {adapter=socket.LWIP_STA,timeout=5000,debug=false}).wait()
+        log.info("http执行结果", code, headers, body and #body)
+        sys.wait(2000)
+    end
+end
+
+function ip_ready_handle(ip, adapter)
+    log.info("ip_ready_handle",ip, adapter)
+    if adapter == socket.LWIP_STA then
+        log.info("wifi sta 链接成功")
+    end
+end
+
+sys.taskInit(test_sta)
+sys.subscribe("IP_READY", ip_ready_handle)