Просмотр исходного кода

update:Air780EPM的AirCloud挂测工程

王城钧 4 месяцев назад
Родитель
Сommit
e0eb5b3a56

+ 0 - 0
module/Air780EPM/project/.keep


+ 130 - 0
module/Air780EPM/project/AirCloud挂测工程/AirSHT30_1000.lua

@@ -0,0 +1,130 @@
+--[[
+@module  fota
+@summary 使用合宙iot平台远程升级功能模块
+@version 1.0
+@date    2025.09.25
+@author  王城钧
+@usage
+SHT30获取温湿度
+]]
+
+
+--本文件中的主机是指I2C主机,具体指Air780EHV
+--本文件中的从机是指I2C从机,具体指AirSHT30_1000配件板上的sht30温湿度传感器芯片
+
+local AirSHT30_1000 = 
+{
+    -- i2c_id:主机的i2c id;
+}   
+
+-- 从机地址为0x44
+local slave_addr = 0x44
+
+--电平设为3.3v
+pm.ioVol(pm.IOVOL_ALL_GPIO, 3300)
+--设置gpio2输出,给camera_sda、camera_scl引脚提供上拉
+gpio.setup(2, 1)
+
+-- 计算数据表data中所有数据元素的crc8校验值
+local function crc8(data)
+    local crc = 0xFF
+    for i = 1, #data do
+        crc = bit.bxor(crc, data[i])
+        for j = 1, 8 do
+            crc = crc * 2
+            if crc >= 0x100 then
+                crc = bit.band(bit.bxor(crc, 0x31), 0xff)
+            end
+        end
+    end
+    return crc
+end
+
+
+--打开AirSHT30_1000;
+
+--i2c_id:number类型;
+--        主机使用的I2C ID,用来控制AirSHT30_1000;
+--        取值范围:仅支持0和1;
+--        如果没有传入此参数,则默认为0;
+
+--返回值:成功返回true,失败返回false
+function AirSHT30_1000.open(i2c_id)
+    --如果i2c_id为nil,则赋值为默认值0
+    if i2c_id==nil then i2c_id=0 end
+
+    --检查参数的合法性
+    if not (i2c_id == 0 or i2c_id == 1) then
+        log.error("AirSHT30_1000.open", "invalid i2c_id", i2c_id)
+        return false
+    end
+
+    AirSHT30_1000.i2c_id = i2c_id
+    
+    --初始化I2C
+    if i2c.setup(i2c_id, i2c.FAST) ~= 1 then
+        log.error("AirSHT30_1000.open", "i2c.setup error", i2c_id)
+        return false
+    end
+
+    return true
+end
+
+--读取温湿度数据;
+
+--返回值:失败返回false;
+--       成功返回两个值,第一个为摄氏温度值(number类型,例如23.6表示23.6摄氏度),第二个为百分比湿度值(number类型,例如67表示67%的湿度)
+function AirSHT30_1000.read()
+
+    -- 发送启动测量命令(高精度)
+    i2c.send(AirSHT30_1000.i2c_id, slave_addr, {0x24, 0x00})
+        
+    -- 等待测量完成(SHT30高精度测量需~15ms)
+    sys.wait(20)
+    
+    -- 读取6字节数据(温度高/低 + CRC,湿度高/低 + CRC)
+    local data = i2c.recv(AirSHT30_1000.i2c_id, slave_addr, 6)
+
+    -- 如果没有读取到6字节数据
+    if type(data)~="string" or data:len()~=6 then
+        log.error("AirSHT30_1000.read", "i2c.recv error")
+        return false
+    end
+
+    -- log.info("AirSHT30_1000.read", data:toHex())
+
+    --如果校验值正确
+    if crc8({data:byte(1), data:byte(2)}) == data:byte(3) and crc8({data:byte(4), data:byte(5)}) == data:byte(6) then 
+        -- 提取原始温度值
+        local temp_raw = (data:byte(1) << 8) | data:byte(2)
+        -- 提取原始湿度值
+        local hum_raw = (data:byte(4) << 8) | data:byte(5)
+        
+        -- 转换为实际值(根据SHT30数据手册公式)
+        local temprature = (-45 + 175 * temp_raw / 65535.0)
+        local humidity = (100 * hum_raw / 65535.0)
+        
+        -- 打印输出结果(保留2位小数)
+        -- log.info("AirSHT30_1000.read", "temprature", string.format("%.2f ℃", temprature))
+        -- log.info("AirSHT30_1000.read", "temprature", string.format("%.2f %%RH", humidity))
+
+        return temprature, humidity
+    else
+        log.error("AirSHT30_1000.read", "crc error", i2c_id)
+        return false
+    end
+end
+
+
+--关闭AirSHT30_1000
+
+--返回值:成功返回true,失败返回false
+function AirSHT30_1000.close()
+    --close接口没有返回值,理论上不会关闭失败
+    i2c.close(AirSHT30_1000.i2c_id)
+
+    return true
+end
+
+
+return AirSHT30_1000

+ 286 - 0
module/Air780EPM/project/AirCloud挂测工程/excloud_test.lua

@@ -0,0 +1,286 @@
+--[[
+@module  excloud_test
+@summary excloud测试文件
+@version 1.0
+@date    2025.09.25
+@author  王城钧
+@usage
+本demo演示的功能为:
+演示excloud扩展库的使用。
+]]
+-- 导入excloud库
+local excloud = require("excloud")
+--加载AirSHT30_1000驱动文件
+local air_sht30 = require "AirSHT30_1000"
+--加载lbsLoc2库
+local lbsLoc2 = require("lbsLoc2")
+
+-- 注册回调函数
+excloud.on(function(event, data)
+    log.info("用户回调函数", event, json.encode(data))
+
+    if event == "connect_result" then
+        if data.success then
+            log.info("连接成功")
+        else
+            log.info("连接失败: " .. (data.error or "未知错误"))
+        end
+    elseif event == "auth_result" then
+        if data.success then
+            log.info("认证成功")
+        else
+            log.info("认证失败: " .. data.message)
+        end
+    elseif event == "message" then
+        log.info("收到消息, 流水号: " .. data.header.sequence_num)
+
+        -- 处理服务器下发的消息
+        for _, tlv in ipairs(data.tlvs) do
+            if tlv.field == excloud.FIELD_MEANINGS.CONTROL_COMMAND then
+                log.info("收到控制命令: " .. tostring(tlv.value))
+
+                -- 处理控制命令并发送响应
+                local response_ok, err_msg = excloud.send({
+                    {
+                        field_meaning = excloud.FIELD_MEANINGS.CONTROL_RESPONSE,
+                        data_type = excloud.DATA_TYPES.ASCII,
+                        value = "命令执行成功"
+                    }
+                }, false)
+
+                if not response_ok then
+                    log.info("发送控制响应失败: " .. err_msg)
+                end
+            end
+        end
+    elseif event == "disconnect" then
+        log.warn("与服务器断开连接")
+    elseif event == "reconnect_failed" then
+        log.info("重连失败,已尝试 " .. data.count .. " 次")
+    elseif event == "send_result" then
+        if data.success then
+            log.info("发送成功,流水号: " .. data.sequence_num)
+        else
+            log.info("发送失败: " .. data.error_msg)
+        end
+    end
+end)
+local getCellInfo1
+local band1
+local lat, lng
+-- 定期轮训式
+sys.taskInit(function()
+    sys.wait(3000)
+    while 1 do
+        mobile.reqCellInfo(15)
+        sys.waitUntil("CELL_INFO_UPDATE", 30000)
+        getCellInfo1 = mobile.getCellInfo()
+        band1 = getCellInfo1[1].band
+        log.info("驻留频段", band1, json.encode(getCellInfo1))
+        lat, lng, t = lbsLoc2.request(5000, nil, nil, true) --需要经纬度和当前时间
+        --(时间格式{"year":2024,"min":56,"month":11,"day":12,"sec":44,"hour":14})
+        log.info("lbsLoc2", lat, lng, (json.encode(t or {})))  --打印经纬度和时间
+        sys.wait(60000)
+    end
+end)
+local temprature, humidity
+sys.taskInit(function()
+    -- 如果当前时间点设置的默认网卡还没有连接成功,一直在这里循环等待
+    while not socket.adapter(socket.dft()) do
+        log.warn("tcp_client_main_task_func", "wait IP_READY", socket.dft())
+        -- 在此处阻塞等待默认网卡连接成功的消息"IP_READY"
+        -- 或者等待1秒超时退出阻塞等待状态;
+        -- 注意:此处的1000毫秒超时不要修改的更长;
+        -- 因为当使用exnetif.set_priority_order配置多个网卡连接外网的优先级时,会隐式的修改默认使用的网卡
+        -- 当exnetif.set_priority_order的调用时序和此处的socket.adapter(socket.dft())判断时序有可能不匹配
+        -- 此处的1秒,能够保证,即使时序不匹配,也能1秒钟退出阻塞状态,再去判断socket.adapter(socket.dft())
+        sys.waitUntil("IP_READY", 1000)
+    end
+    sys.wait(1000)
+    -- 配置excloud参数
+    local ok, err_msg = excloud.setup({
+        -- device_id = "862419074073247",   -- 设备ID (IMEI前14位)
+        device_type = 1,                               -- 设备类型: 4G
+        host = "124.71.128.165",                       -- 服务器地址
+        port = 9108,                                   -- 服务器端口
+        auth_key = "SIsRml1ImTsP6XR4lvRAQVuksbZZuUpO", -- 鉴权密钥
+        transport = "tcp",                             -- 使用TCP传输
+        auto_reconnect = true,                         -- 自动重连
+        reconnect_interval = 10,                       -- 重连间隔(秒)
+        max_reconnect = 5,                             -- 最大重连次数
+        timeout = 30,                                  -- 超时时间(秒)
+    })
+    -- 配置MQTT连接参数
+    -- local ok, err_msg = excloud.setup({
+    --     device_type = 1,             -- 设备类型: 4G设备
+    --     transport = "mqtt",          -- 使用MQTT传输协议
+    --     host = "airtest.openluat.com",   -- MQTT服务器地址
+    --     port = 1883,                 -- MQTT服务器端口
+    --     auth_key = "VmhtOb81EgZau6YyuuZJzwF6oUNGCbXi", -- 鉴权密钥(请替换为实际密钥)
+    --     username = "root",    -- MQTT用户名(可选)
+    --     password = "Luat123456", -- MQTT密码(可选)
+    --     keepalive = 300,             -- 心跳间隔(秒)
+    --     -- auto_reconnect = true,       -- 自动重连
+    --     reconnect_interval = 10,     -- 重连间隔(秒)
+    --     max_reconnect = 5,           -- 最大重连次数
+    --     timeout = 30,                -- 超时时间(秒)
+    --     qos = 1,                     -- MQTT QoS等级(0/1/2)
+    --     -- retain = false,              -- MQTT retain标志
+    --     -- clean_session = true,        -- MQTT clean session
+    --     ssl = false                  -- 不使用SSL
+    -- })
+
+    if not ok then
+        log.info("初始化失败: " .. err_msg)
+        return
+    end
+    log.info("excloud初始化成功")
+    -- 开启excloud服务
+    local ok, err_msg = excloud.open()
+    if not ok then
+        log.info("开启excloud服务失败: " .. err_msg)
+        return
+    end
+
+    log.info("excloud服务已开启")
+    -- 在主循环中定期上报数据
+    while true do
+        -- 每60秒上报一次数据
+        sys.wait(60000)       
+        -- 检查连接状态
+        -- local status = excloud.status()
+        -- if not status.is_connected or not status.is_authenticated then
+        --     log.info("设备未连接或未认证,跳过数据上报")
+        -- else
+
+        -- 获取信号强度
+        local rssi = mobile.csq()
+        log.info("信号强度", rssi)
+
+        -- 获取sim卡的iccid
+        local iccid = mobile.iccid()
+        log.info("iccid", iccid)
+
+        -- 获取驻留频段
+        local buff = zbuff.create(40)
+
+        -- 获取驻留小区
+        local cell_info = mobile.scell()
+        local cell_id = cell_info.cid
+        log.info("小区ID", cell_id)
+
+        -- 获取环境温度
+        --打开sht30硬件
+        air_sht30.open(1)
+
+        --读取温湿度数据
+        temprature, humidity = air_sht30.read()
+        log.info("温度", temprature, humidity)
+
+        -- 获取cpu温度
+        adc.open(adc.CH_CPU)              --打开adc.CH_CPU通道
+        local data5 = adc.get(adc.CH_CPU) --获取adc.CH_CPU计算值,将获取到的值赋给data5
+        -- adc.close(adc.CH_CPU)--关闭adc.CH_CPU通道
+        log.info("CPU温度", data5)
+
+        if not lat then
+            lat = 0
+        end
+        
+        if not lng then
+            lng = 0
+        end
+
+        if not rssi then
+            rssi = 0
+        end
+
+        if not cell_id then
+            cell_id = 0
+        end
+
+        if not temprature then
+            temprature = 0
+        end
+
+        if not humidity then
+            humidity = 0
+        end
+
+        if not band1 then
+            band1 = 0
+        end
+
+        if not iccid then
+            iccid = ""
+        end
+
+        if not data5 then
+            data5 = 0
+        end
+
+        local ok, err_msg = excloud.send({
+            {
+                --信号强度
+                field_meaning = excloud.FIELD_MEANINGS.SIGNAL_STRENGTH_4G,
+                data_type = excloud.DATA_TYPES.INTEGER,
+                value = rssi -- 信号强度
+            },
+            {
+                --iccid
+                field_meaning = excloud.FIELD_MEANINGS.SIM_ICCID,
+                data_type = excloud.DATA_TYPES.ASCII,
+                value = iccid -- sim卡iccid
+            },
+            {
+                --驻留频段
+                field_meaning = excloud.FIELD_MEANINGS.SERVING_CELL,
+                data_type = excloud.DATA_TYPES.INTEGER,
+                value = band1 -- 驻留频段
+            },
+            {
+                --驻留小区
+                field_meaning = excloud.FIELD_MEANINGS.CELL_INFO,
+                data_type = excloud.DATA_TYPES.INTEGER,
+                value = cell_id -- 驻留小区
+            },
+            {
+                --环境温度
+                field_meaning = excloud.FIELD_MEANINGS.TEMPERATURE,
+                data_type = excloud.DATA_TYPES.FLOAT,
+                value = temprature -- 环境温度
+            },
+            {
+                --环境湿度
+                field_meaning = excloud.FIELD_MEANINGS.HUMIDITY,
+                data_type = excloud.DATA_TYPES.FLOAT,
+                value = humidity -- 环境湿度
+            },
+            {
+                --cpu温度
+                field_meaning = excloud.FIELD_MEANINGS.ENV_TEMPERATURE,
+                data_type = excloud.DATA_TYPES.INTEGER,
+                value = data5 -- cpu温度
+            },
+            {
+                --经度
+                field_meaning = excloud.FIELD_MEANINGS.GNSS_LONGITUDE,
+                data_type = excloud.DATA_TYPES.ASCII,
+                value = lng
+            },
+            {
+                --纬度
+                field_meaning = excloud.FIELD_MEANINGS.GNSS_LATITUDE,
+                data_type = excloud.DATA_TYPES.ASCII,
+                value = lat
+            }
+        }, false) -- 不需要服务器回复
+
+        if not ok then
+            log.info("发送数据失败: " .. err_msg)
+        else
+            log.info("数据发送成功")
+        end
+        -- end
+    end
+end)

+ 29 - 0
module/Air780EPM/project/AirCloud挂测工程/fota.lua

@@ -0,0 +1,29 @@
+--[[
+@module  fota
+@summary 使用合宙iot平台远程升级功能模块
+@version 1.0
+@date    2025.09.25
+@author  王城钧
+@usage
+实现远程升级功能
+]]
+
+-- 使用合宙iot平台时需要这个参数
+PRODUCT_KEY = "SIsRml1ImTsP6XR4lvRAQVuksbZZuUpO" -- 到 iot.openluat.com 创建项目,获取正确的项目id
+--加在libfota2扩展库
+libfota2 = require "libfota2"
+
+local function fota_cb(ret)
+    log.info("fota", ret)
+    -- fota结束,无论成功还是失败,都释放fota_running标志
+    if ret == 0 then
+        log.info("升级包下载成功,重启模块")
+        rtos.reboot()
+    end
+end
+
+local opts = {}
+
+libfota2.request(fota_cb, opts)
+
+sys.timerLoopStart(libfota2.request, 4 * 3600000, fota_cb, opts)

+ 69 - 0
module/Air780EPM/project/AirCloud挂测工程/main.lua

@@ -0,0 +1,69 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 1.0
+@date    2025.09.25
+@author  王城钧
+@usage
+本demo演示的功能为:
+演示excloud扩展库的使用。
+]]
+
+--[[
+必须定义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 = "excloud_test"
+VERSION = "001.000.000"
+
+--添加硬狗防止程序卡死
+if wdt then
+    wdt.init(9000)--初始化watchdog设置为9s
+    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
+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)
+
+
+
+-- 加载fota升级功能模块
+require "fota"
+
+-- 加载网络驱动设备功能模块
+require "netdrv_device"
+
+-- 加载excloud测试模块
+require "excloud_test"
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 33 - 0
module/Air780EPM/project/AirCloud挂测工程/netdrv/netdrv_4g.lua

@@ -0,0 +1,33 @@
+--[[
+@module  netdrv_4g
+@summary “4G网卡”驱动模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
+本文件为4G网卡驱动模块,核心业务逻辑为:
+1、监听"IP_READY"和"IP_LOSE",在日志中进行打印;
+
+本文件没有对外接口,直接在其他功能模块中require "netdrv_4g"就可以加载运行;
+]]
+
+local function ip_ready_func()
+    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)

+ 85 - 0
module/Air780EPM/project/AirCloud挂测工程/netdrv/netdrv_eth_spi.lua

@@ -0,0 +1,85 @@
+--[[
+@module  netdrv_eth_spi
+@summary “通过SPI外挂CH390H芯片的以太网卡”驱动模块 
+@version 1.0
+@date    2025.07.24
+@author  朱天华
+@usage
+本文件为“通过SPI外挂CH390H芯片的以太网卡”驱动模块 ,核心业务逻辑为:
+1、打开CH390H芯片供电开关;
+2、初始化spi1,初始化以太网卡,并且在以太网卡上开启DHCP(动态主机配置协议);
+3、以太网卡的连接状态发生变化时,在日志中进行打印;
+
+直接使用Air8000开发板硬件测试即可;
+
+本文件没有对外接口,直接在其他功能模块中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测试使用的是Air8000开发板
+--GPIO140为CH390H以太网芯片的供电使能控制引脚
+gpio.setup(140, 1, gpio.PULLUP)
+
+--这个task的核心业务逻辑是:初始化SPI,初始化以太网卡,并在以太网卡上开启动态主机配置协议
+local function netdrv_eth_spi_task_func()
+    -- 初始化SPI1
+    local result = spi.setup(
+        1,--spi_id
+        nil,
+        0,--CPHA
+        0,--CPOL
+        8,--数据宽度
+        25600000--,--频率
+        -- spi.MSB,--高低位顺序    可选,默认高位在前
+        -- spi.master,--主模式     可选,默认主
+        -- spi.full--全双工       可选,默认全双工
+    )
+    log.info("netdrv_eth_spi", "spi open result", result)
+    --返回值为0,表示打开成功
+    if result ~= 0 then
+        log.error("netdrv_eth_spi", "spi open error",result)
+        return
+    end
+
+    --初始化以太网卡
+
+    --以太网联网成功(成功连接路由器,并且获取到了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 1, 片选 GPIO12
+    netdrv.setup(socket.LWIP_ETH, netdrv.CH390, {spi=1, cs=12})
+
+    --在以太上开启动态主机配置协议
+    netdrv.dhcp(socket.LWIP_ETH, true)
+end
+
+--创建并且启动一个task
+--task的处理函数为netdrv_eth_spi_task_func
+sys.taskInit(netdrv_eth_spi_task_func)

+ 95 - 0
module/Air780EPM/project/AirCloud挂测工程/netdrv/netdrv_multiple.lua

@@ -0,0 +1,95 @@
+--[[
+@module  netdrv_multiple
+@summary 多网卡(4G网卡、WIFI STA网卡、通过SPI外挂CH390H芯片的以太网卡)驱动模块 
+@version 1.0
+@date    2025.07.24
+@author  朱天华
+@usage
+本文件为多网卡驱动模块 ,核心业务逻辑为:
+1、调用exnetif.set_priority_order配置多网卡的控制参数以及优先级;
+
+直接使用Air8000开发板硬件测试即可;
+
+本文件没有对外接口,直接在其他功能模块中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芯片”的以太网卡,使用Air8000开发板验证
+            {
+                ETHERNET = {
+                    -- 供电使能GPIO
+                    pwrpin = 140,
+                    -- 设置的多个“已经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=1, cs=12}
+                }
+            },
+
+            -- WIFI STA网卡
+            {
+                WIFI = {
+                    -- 要连接的WIFI路由器名称
+                    ssid = "茶室-降功耗,找合宙!",
+                    -- 要连接的WIFI路由器密码
+                    password = "Air123456", 
+
+                    -- 连通性检测ip(选填参数);
+                    -- 如果没有传入ip地址,exnetif中会默认使用httpdns能否成功获取baidu.com的ip作为是否连通的判断条件;
+                    -- 如果传入,一定要传入可靠的并且可以ping通的ip地址;
+                    -- ping_ip = "填入可靠的并且可以ping通的ip地址",
+                }
+            },
+
+            -- 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)

+ 50 - 0
module/Air780EPM/project/AirCloud挂测工程/netdrv/netdrv_wifi.lua

@@ -0,0 +1,50 @@
+--[[
+@module  netdrv_wifi
+@summary “WIFI STA网卡”驱动模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
+本文件为WIFI STA网卡驱动模块,核心业务逻辑为:
+1、初始化WIFI网络;
+2、连接WIFI路由器;
+3、和WIFI路由器之间的连接状态发生变化时,在日志中进行打印;
+
+本文件没有对外接口,直接在其他功能模块中require "netdrv_wifi"就可以加载运行;
+]]
+
+local function ip_ready_func()
+    log.info("netdrv_wifi.ip_ready_func", "IP_READY", json.encode(wlan.getInfo()))
+end
+
+local function ip_lose_func()
+    log.warn("netdrv_wifi.ip_lose_func", "IP_LOSE")
+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("iPhone", "xiaoshuai", 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网络是否连接成功

+ 33 - 0
module/Air780EPM/project/AirCloud挂测工程/netdrv_device.lua

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