Răsfoiți Sursa

update:提交aircloud的库和dmeo

mw 4 luni în urmă
părinte
comite
35e81c4cc5

+ 154 - 0
module/Air8000/demo/Aircloud/excloud_test.lua

@@ -0,0 +1,154 @@
+--[[
+@module  excloud_test
+@summary excloud测试文件
+@version 1.0
+@date    2025.09.22
+@author  孟伟
+@usage
+本demo演示的功能为:
+演示excloud扩展库的使用。
+]]
+-- 导入excloud库
+local excloud = require("excloud")
+
+-- 注册回调函数
+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)
+
+
+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 = "112.125.89.8",         -- 服务器地址
+    --     port = 33316,                     -- 服务器端口
+    --     auth_key = "VmhtOb81EgZau6YyuuZJzwF6oUNGCbXi", -- 鉴权密钥
+    --     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
+        -- 每30秒上报一次数据
+        sys.wait(30000)
+        -- 检查连接状态
+        local status = excloud.status()
+        -- if not status.is_connected or not status.is_authenticated then
+        --     log.info("设备未连接或未认证,跳过数据上报")
+        -- else
+        local ok, err_msg = excloud.send({
+            {
+                field_meaning = excloud.FIELD_MEANINGS.LOCATION_METHOD,
+                data_type = excloud.DATA_TYPES.INTEGER,
+                value = 22     -- 随机温度值
+            },
+            {
+                field_meaning = excloud.FIELD_MEANINGS.HUMIDITY,
+                data_type = excloud.DATA_TYPES.FLOAT,
+                value = 33.2543     -- 随机湿度值
+            }
+        }, false)                   -- 不需要服务器回复
+
+        if not ok then
+            log.info("发送数据失败: " .. err_msg)
+        else
+            log.info("数据发送成功")
+        end
+        -- end
+    end
+end)

+ 65 - 0
module/Air8000/demo/Aircloud/main.lua

@@ -0,0 +1,65 @@
+
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 1.0
+@date    2025.09.22
+@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)
+
+-- -- 加载网络驱动设备功能模块
+require "netdrv_device"
+
+-- 加载excloud测试模块
+require"excloud_test"
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 33 - 0
module/Air8000/demo/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/Air8000/demo/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/Air8000/demo/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/Air8000/demo/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/Air8000/demo/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"

+ 1167 - 0
script/libs/excloud.lua

@@ -0,0 +1,1167 @@
+--[[
+@summary excloud扩展库
+@version 1.0
+@date    2025.09.22
+@author  孟伟
+@usage
+-- 应用场景
+该扩展库适用于各种物联网设备(如4G/WiFi/以太网设备)与云端服务器进行数据交互的场景。
+可用于设备状态上报、数据采集、远程控制等物联网应用。
+
+实现的功能:
+1. 支持多种设备类型(4G/WiFi/以太网)的接入认证
+2. 提供TCP和MQTT两种传输协议选择
+3. 实现设备与云端的双向通信(数据上报和命令下发)
+4. 支持数据的TLV格式编解码
+5. 提供自动重连机制,保证连接稳定性
+6. 支持不同数据类型(整数、浮点数、布尔值、字符串、二进制等)的传输
+
+-- 用法实例
+本扩展库对外提供了以下6个接口:
+1. excloud.setup(params) - 设置配置参数
+2. excloud.on(cbfunc) - 注册回调函数
+3. excloud.open() - 开启excloud服务
+4. excloud.send(data, need_reply, is_auth_msg) - 发送数据
+5. excloud.close() - 关闭excloud服务
+6. excloud.status() - 获取当前状态
+
+-- 示例:
+-- 导入excloud库
+local excloud = require("excloud")
+
+-- 注册回调函数
+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)
+
+
+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 = "112.125.89.8",         -- 服务器地址
+    --     port = 33316,                     -- 服务器端口
+    --     auth_key = "VmhtOb81EgZau6YyuuZJzwF6oUNGCbXi", -- 鉴权密钥
+    --     transport = "tcp",               -- 使用TCP传输
+    --     auto_reconnect = true,           -- 自动重连
+    --     reconnect_interval = 10,         -- 重连间隔(秒)
+    --     max_reconnect = 5,               -- 最大重连次数
+    --     timeout = 30,                    -- 超时时间(秒)
+    -- })
+
+    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
+        -- 每30秒上报一次数据
+        sys.wait(30000)
+        local ok, err_msg = excloud.send({
+            {
+                field_meaning = excloud.FIELD_MEANINGS.LOCATION_METHOD,
+                data_type = excloud.DATA_TYPES.INTEGER,
+                value = 22     -- 随机温度值
+            },
+            {
+                field_meaning = excloud.FIELD_MEANINGS.HUMIDITY,
+                data_type = excloud.DATA_TYPES.FLOAT,
+                value = 33.2543     -- 随机湿度值
+            }
+        }, false)                   -- 不需要服务器回复
+
+        if not ok then
+            log.info("发送数据失败: " .. err_msg)
+        else
+            log.info("数据发送成功")
+        end
+    end
+end)
+
+]]
+local excloud = {}
+
+-- 内部状态变量
+local config = {
+    device_type = 1,           -- 默认设备类型: 4G
+    device_id = "",            -- 设备ID
+    protocol_version = 1,      -- 协议版本
+    transport = "tcp",         -- 传输协议: tcp/mqtt
+    host = "cloud.luatos.com", -- 服务器地址
+    port = 8900,               -- 服务器端口
+    auth_key = nil,            -- 用户鉴权密钥
+    keepalive = 300,           -- mqtt心跳
+    auto_reconnect = true,     -- 是否自动重连
+    reconnect_interval = 10,   -- 重连间隔(秒)
+    max_reconnect = 3,         -- 最大重连次数
+    timeout = 30,              -- 连接超时时间(秒)
+    qos = 0,                   -- MQTT QoS等级
+    -- retain = false,            -- MQTT retain标志
+    -- clean_session = true,      -- MQTT clean session标志
+    ssl = false,               -- 是否使用SSL
+    username = nil,            -- MQTT用户名
+    password = nil,            -- MQTT密码
+    udp_auth_key = nil,        -- UDP鉴权密钥
+}
+
+local callback_func = nil         -- 回调函数
+local is_open = false             -- 服务是否开启
+local is_connected = false        -- 是否已连接
+local is_authenticated = false    -- 是否已鉴权
+local sequence_num = 0            -- 流水号
+local connection = nil            -- 连接对象
+local device_id_binary = nil      -- 二进制格式的设备ID
+local reconnect_timer = nil       -- 重连定时器
+local reconnect_count = 0         -- 重连次数
+local pending_messages = {}       -- 待发送消息队列
+local rxbuff = nil                -- 接收缓冲区
+local connect_timeout_timer = nil -- 连接超时定时器
+
+-- 数据类型定义
+local DATA_TYPES = {
+    INTEGER = 0x0, -- 整数
+    FLOAT = 0x1,   -- 浮点数
+    BOOLEAN = 0x2, -- 布尔值
+    ASCII = 0x3,   -- ASCII字符串
+    BINARY = 0x4,  -- 二进制数据
+    UNICODE = 0x5  -- Unicode字符串
+}
+
+-- 字段含义定义
+local FIELD_MEANINGS = {
+    -- 控制信令类型 (16-255)
+    AUTH_REQUEST = 16,     -- 鉴权请求
+    AUTH_RESPONSE = 17,    -- 鉴权回复
+    REPORT_RESPONSE = 18,  -- 上报回应
+    CONTROL_COMMAND = 19,  -- 控制命令
+    CONTROL_RESPONSE = 20, -- 控制回应
+    IRTU_DOWN = 21,        -- iRTU下行命令
+    IRTU_UP = 22,          -- iRTU上行回复
+
+    -- 传感类 (256-511)
+    TEMPERATURE = 256,     -- 温度
+    HUMIDITY = 257,        -- 湿度
+    PARTICULATE = 258,     -- 颗粒数
+    ACIDITY = 259,         -- 酸度
+    ALKALINITY = 260,      -- 碱度
+    ALTITUDE = 261,        -- 海拔
+    WATER_LEVEL = 262,     -- 水位
+    ENV_TEMPERATURE = 263, -- 环境温度
+    POWER_METERING = 264,  -- 电量计量
+
+    -- 资产管理类 (512-767)
+    GNSS_LONGITUDE = 512,     -- GNSS经度
+    GNSS_LATITUDE = 513,      -- GNSS纬度
+    SPEED = 514,              -- 行驶速度
+    GNSS_CN = 515,            -- 最强的4颗GNSS卫星的CN
+    SATELLITES_TOTAL = 516,   -- 搜到的所有卫星数
+    SATELLITES_VISIBLE = 517, -- 可见卫星数
+    HEADING = 518,            -- 航向角
+    LOCATION_METHOD = 519,    -- 基站定位/GNSS定位标识
+    GNSS_INFO = 520,          -- GNSS芯片型号和固件版本号
+    DIRECTION = 521,          -- 方向
+
+    -- 设备参数类 (768-1023)
+    HEIGHT = 768,          -- 高度
+    WIDTH = 769,           -- 宽度
+    ROTATION_SPEED = 770,  -- 转速
+    BATTERY_LEVEL = 771,   -- 电量(mV)
+    SERVING_CELL = 772,    -- 驻留小区
+    CELL_INFO = 773,       -- 驻留小区和邻区
+    COMPONENT_MODEL = 774, -- 元器件型号
+    GPIO_LEVEL = 775,      -- GPIO高低电平
+    BOOT_REASON = 776,     -- 开机原因
+    BOOT_COUNT = 777,      -- 开机次数
+    SLEEP_MODE = 778,      -- 休眠模式
+    WAKE_INTERVAL = 779,   -- 定时唤醒间隔
+    NETWORK_IP_TYPE = 780, -- 设备入网的IP类型
+    NETWORK_TYPE = 781,    -- 当前联网方式
+
+    -- 软件数据类 (1024-1279)
+    LUA_CORE_ERROR = 1024,   -- Lua核心库错误上报
+    LUA_EXT_ERROR = 1025,    -- Lua扩展卡错误上报
+    LUA_APP_ERROR = 1026,    -- Lua业务错误上报
+    FIRMWARE_VERSION = 1027, -- 固件版本号
+    SMS_FORWARD = 1028,      -- SMS转发
+    CALL_FORWARD = 1029,     -- 来电转发
+
+    -- 设备无关数据类 (1280-1535)
+    TIMESTAMP = 1280,  -- 时间
+    RANDOM_DATA = 1281 -- 无意义数据
+}
+
+-- 将数字转换为大端字节序列
+local function to_big_endian(num, bytes)
+    local result = {}
+    for i = bytes, 1, -1 do
+        result[i] = string.char(num % 256)
+        num = math.floor(num / 256)
+    end
+    return table.concat(result)
+end
+
+-- 从大端字节序列转换为数字
+local function from_big_endian(data, start, length)
+    local value = 0
+    for i = start, start + length - 1 do
+        value = value * 256 + data:byte(i)
+    end
+    log.info("from_big_endian", value)
+    return value
+end
+
+-- 将设备ID进行编码
+function packDeviceInfo(deviceType, deviceId)
+    -- 验证设备类型
+    if deviceType ~= 1 and deviceType ~= 2 then
+        log.info("设备类型错误: 4G设备应为1, WIFI设备应为2")
+    end
+
+    -- 设备类型字节
+    local result = { string.char(deviceType) }
+
+    -- 清理设备ID(移除非数字和字母字符,并转换为大写)
+    local cleanId = deviceId:gsub("[^%w]", ""):upper()
+
+    -- 处理不同类型的设备ID
+    if deviceType == 1 then
+        -- 4G设备 - IMEI处理
+        -- 只取前14位数字,忽略第15位
+        cleanId = cleanId:gsub("%D", ""):sub(1, 14)
+
+        -- 确保长度为14位(不足时前面补0)
+        if #cleanId < 14 then
+            cleanId = string.rep("0", 14 - #cleanId) .. cleanId
+        end
+
+        -- 转换为BCD格式的字节
+        for i = 1, 14, 2 do
+            local byte = (tonumber(cleanId:sub(i, i)) * 16) + tonumber(cleanId:sub(i + 1, i + 1))
+            table.insert(result, string.char(byte))
+        end
+    elseif deviceType == 2 then
+        -- WIFI设备 - MAC地址处理
+        -- 移除非十六进制字符
+        cleanId = cleanId:gsub("[^0-9A-Fa-f]", "")
+
+        -- 确保长度为12个十六进制字符(6字节)
+        if #cleanId < 12 then
+            cleanId = string.rep("0", 12 - #cleanId) .. cleanId
+        else
+            cleanId = cleanId:sub(1, 12)
+        end
+
+        -- 转换为字节
+        local bytes = {}
+        for i = 1, 12, 2 do
+            local byteStr = cleanId:sub(i, i + 1)
+            table.insert(bytes, string.char(tonumber(byteStr, 16)))
+        end
+
+        -- 确保有7个字节(不足时前面补0)
+        while #bytes < 7 do
+            table.insert(bytes, 1, string.char(0))
+        end
+
+        -- 添加到结果中
+        for _, byte in ipairs(bytes) do
+            table.insert(result, byte)
+        end
+    else
+        log.info("未知设备类型 ")
+        return deviceId
+    end
+
+    -- 返回8字节的二进制数据
+    return table.concat(result)
+end
+
+-- 编码数据值
+local function encode_value(data_type, value)
+    if data_type == DATA_TYPES.INTEGER then
+        if type(value) == "number" then
+            return to_big_endian(math.floor(value), 4)
+        else
+            log.info("Integer value expected")
+            return nil
+        end
+    elseif data_type == DATA_TYPES.FLOAT then
+        if type(value) == "number" then
+            -- 简化处理:将浮点数转换为整数,乘以1000以保留三位小数
+            -- 然后调用to_big_endian函数将其转换为4字节的大端字节序列
+            return to_big_endian(math.floor(value * 1000), 4)
+        else
+            log.info("Number value expected")
+            return nil
+        end
+    elseif data_type == DATA_TYPES.BOOLEAN then
+        if type(value) == "boolean" then
+            return value and "\1" or "\0"
+        else
+            log.info("Boolean value expected")
+            return nil
+        end
+    elseif data_type == DATA_TYPES.ASCII then
+        if type(value) == "string" then
+            return value
+        else
+            log.info("String value expected")
+            return nil
+        end
+    elseif data_type == DATA_TYPES.BINARY then
+        if type(value) == "string" then
+            return value
+        else
+            log.info("String value expected")
+            return nil
+        end
+    elseif data_type == DATA_TYPES.UNICODE then
+        if type(value) == "string" then
+            -- 简化处理:直接使用字符串(实际应转换为UTF-16)
+            return value
+        else
+            log.info("String value expected")
+            return nil
+        end
+    else
+        log.info("Unsupported data type: " .. data_type)
+        return nil
+    end
+end
+
+-- 解码数据值
+local function decode_value(data_type, value)
+    if data_type == DATA_TYPES.INTEGER then
+        return from_big_endian(value, 1, #value)
+    elseif data_type == DATA_TYPES.FLOAT then
+        -- 简化处理:将整数转换为浮点数(实际应使用IEEE 754格式)
+        return from_big_endian(value, 1, #value) / 1000
+    elseif data_type == DATA_TYPES.BOOLEAN then
+        return value:byte(1) ~= 0
+    elseif data_type == DATA_TYPES.ASCII then
+        return value
+    elseif data_type == DATA_TYPES.BINARY then
+        return value
+    elseif data_type == DATA_TYPES.UNICODE then
+        return value
+    else
+        log.info("Unsupported data type: " .. data_type)
+        return nil
+    end
+end
+
+-- 构建消息头
+-- @param need_reply boolean 是否需要服务器回复
+-- @param has_auth_key boolean 是否携带鉴权key
+-- @param data_length number 数据长度
+local function build_header(need_reply, is_udp_transport, data_length)
+    sequence_num = (sequence_num + 1) % 65536
+
+    -- 消息标识字段
+    local flags = config.protocol_version -- bit0-3: 协议版本号
+    if need_reply then
+        flags = flags + 16                -- bit4: 是否需要回复
+    end
+    if is_udp_transport then
+        flags = flags + 32 -- bit5: 是否是UDP承载
+    end
+    log.info("构建消息头", device_id_binary, to_big_endian(sequence_num, 2), to_big_endian(data_length, 2),
+        to_big_endian(flags, 4))
+    return device_id_binary ..
+        to_big_endian(sequence_num, 2) ..
+        to_big_endian(data_length, 2) ..
+        to_big_endian(flags, 4)
+end
+
+-- 构建TLV字段
+local function build_tlv(field_meaning, data_type, value)
+    local value_encoded = encode_value(data_type, value)
+    if value_encoded == nil then
+        log.info("构建tlv打包数据时长度为0")
+        return nil
+    end
+    local length = #value_encoded
+    -- 字段类型(字段含义 + 数据类型)
+    local head          = (field_meaning & 0x0FFF) | (data_type << 12) -- 2 字节头
+    return to_big_endian(head, 2) ..
+        to_big_endian(length, 2) ..
+        value_encoded
+end
+
+-- 解析消息头
+local function parse_header(header)
+    if #header < 16 then
+        log.info("消息头解析失败", "Header too short")
+        return nil, "Header too short"
+    end
+
+    local device_id = header:sub(1, 8)
+    local seq_num = from_big_endian(header, 9, 2)
+    local msg_length = from_big_endian(header, 11, 2)
+    local flags = from_big_endian(header, 13, 4)
+
+    -- 提取标志位
+    local protocol_version = flags % 16
+    local need_reply = (flags % 32) >= 16
+    local is_udp_transport = (flags % 64) >= 32
+
+    -- 打印解析结果,方便调试
+    log.info("消息头解析结果",
+        string.format(
+            "device_id: %s, sequence_num: %d, msg_length: %d, protocol_version: %d, need_reply: %s, is_udp_transport: %s",
+            string.toHex(device_id), seq_num, msg_length, protocol_version,
+            tostring(need_reply), tostring(is_udp_transport)))
+
+    return {
+        device_id = string.toHex(device_id),
+        sequence_num = seq_num,
+        msg_length = msg_length,
+        protocol_version = protocol_version,
+        need_reply = need_reply,
+        is_udp_transport = is_udp_transport
+    }
+end
+
+
+-- 工具函数:解析TLV
+local function parse_tlv(data, startPos)
+    -- 检查数据是否足够解析TLV的T的长度。
+    if #data < startPos + 3 then
+        return nil, startPos, "TLV data too short"
+    end
+
+    local fieldType = from_big_endian(data, startPos, 2)
+    local length = from_big_endian(data, startPos + 2, 2)
+    -- 提取原始字节值
+    local value = data:sub(startPos + 4, startPos + 4 + length - 1)
+    --解析TLV字段中的T
+    -- bit0-11: 字段含义
+    -- bit12-15: 数据类型
+    local field_meaning = fieldType & 0x0FFF -- 取低12位作为字段含义
+    local data_type = fieldType >> 12        -- 取高4位作为数据类型
+
+    local decoded_value = decode_value(data_type, value)
+    log.info("消息体解析结果", field_meaning, data_type, decoded_value)
+    return {
+        field = field_meaning,
+        type = data_type,
+        value = decoded_value,
+        length = length, --数据长度
+    }, startPos + 4 + length
+end
+
+-- 解析完整消息
+local function parse_message(data)
+    local header, err = parse_header(data:sub(1, 16))
+    if not header then
+        return nil, err
+    end
+    local auth_key = nil
+    local body_start = 17
+
+    -- 如果是UDP传输,解析认证key
+    if header.is_udp_transport then
+        if #data >= body_start + 64 - 1 then
+            auth_key = data:sub(body_start, body_start + 64 - 1)
+            body_start = body_start + 64
+        else
+            return nil, "Incomplete UDP authentication key"
+        end
+    end
+
+    -- 解析TLV字段
+    local tlvs = {}
+    local pos = body_start
+    local end_pos = 16 + (header.msg_length)
+
+    if #data < end_pos then
+        return nil, "Message incomplete"
+    end
+    while pos < end_pos do
+        local tlv, new_pos, err = parse_tlv(data, pos)
+        if not tlv then
+            return nil, "Failed to parse TLV at position " .. pos
+        end
+        table.insert(tlvs, tlv)
+        -- 更新解析位置为解析完当前TLV字段后的新位置,以便继续解析后续的TLV字段
+        pos = new_pos
+    end
+
+    return {
+        header = header,
+        auth_key = auth_key,
+        tlvs = tlvs
+    }
+end
+
+-- 发送鉴权请求
+local function send_auth_request()
+    if not config.auth_key then
+        return false, "No auth key configured"
+    end
+    local auth_data
+    --  auth_data = config.auth_key .. "-" .. config.device_id .. "-" .. "323B131815B0DFC9"
+   --设备实测时打开
+    if config.device_type == 1 then
+        auth_data = config.auth_key .. "-" .. mobile.imei() .. "-" ..mobile.muid()
+    elseif config.device_type == 2 then
+        auth_data = config.auth_key .. "-" .. wlan.getMac(nil, true) .. "-" .. mobile.muid():toHex()
+    else
+        auth_data = config.auth_key .. "-"
+    end
+
+    local message = {
+        {
+            field_meaning = FIELD_MEANINGS.AUTH_REQUEST,
+            data_type = DATA_TYPES.ASCII,
+            value = auth_data
+        }
+    }
+    log.info("send auth request", message,message[1].value,message[1].data_type,message[1].field_meaning)
+    return excloud.send(message, true, true) -- 鉴权消息需要设置 is_auth_msg 为 true
+end
+
+-- 处理认证响应AAA
+local function handle_auth_response(tlvs)
+    for _, tlv in ipairs(tlvs) do
+        if tlv.field_meaning == FIELD_MEANINGS.AUTH_RESPONSE then
+            local success = (tlv.value == "OK" or tlv.value == "SUCCESS")
+            is_authenticated = success
+            if callback_func then
+                callback_func("auth_result", {
+                    success = success,
+                    message = tlv.value
+                })
+            end
+            -- 认证成功,发送待处理消息
+            if success then
+                for _, msg in ipairs(pending_messages) do
+                    excloud.send(msg.data, msg.need_reply)
+                end
+                pending_messages = {}
+            end
+
+            return success
+        end
+    end
+
+    return false
+end
+
+-- 接收消息解析处理
+local function parse_data(data)
+    local message, err = parse_message(data)
+    if not message then
+        log.info("Failed to parse message: " .. err)
+        return
+    end
+
+    --数据返回给回调
+    if callback_func then
+        callback_func("message", message)
+    end
+
+    -- -- 如果需要回复,发送确认AAA
+    -- if message.header.need_reply then
+    --     local response = {
+    --         {
+    --             field_meaning = FIELD_MEANINGS.REPORT_RESPONSE,
+    --             data_type = DATA_TYPES.ASCII,
+    --             value = "ACK"
+    --         }
+    --     }
+    --     excloud.send(response, false)
+    -- end
+end
+-- 重连  AAA
+local function schedule_reconnect()
+    if reconnect_count >= config.max_reconnect then
+        log.info("Max reconnection attempts reached")
+        if callback_func then
+            callback_func("reconnect_failed", { count = reconnect_count })
+        end
+        return
+    end
+
+    reconnect_count = reconnect_count + 1
+    log.info("重连次数 " .. reconnect_count)
+
+    -- 使用定时器安排重连
+    reconnect_timer = sys.timerStart(function()
+        log.info("Attempting to reconnect...")
+        excloud.open()
+    end, config.reconnect_interval * 1000)
+end
+-- TCP socket事件回调函数
+local function tcp_socket_callback(netc, event, param)
+    log.info("111111socket", netc, event, param)
+    -- 取消连接超时定时器
+    if connect_timeout_timer then
+        sys.timerStop(connect_timeout_timer)
+        connect_timeout_timer = nil
+    end
+
+    if param ~= 0 then
+        log.info("socket", "连接断开")
+        is_connected = false
+        is_authenticated = false
+
+        if callback_func then
+            callback_func("disconnect", {})
+        end
+        -- 连接断开,释放资源
+        socket.release(connection)
+        connection = nil
+
+        -- 尝试重连
+        if config.auto_reconnect and is_open then
+            is_open = false
+            schedule_reconnect()
+        end
+        return
+    end
+    if event == socket.LINK then
+        -- -- 网络连接成功
+        -- log.info("socket", "网络连接成功")
+    elseif event == socket.ON_LINE then
+        -- TCP连接成功
+        log.info("socket", "TCP连接成功")
+        is_connected = true
+
+        -- 重置重连计数,如果是重连的话,连接上服务器给重连计数重置为0
+        reconnect_count = 0
+
+
+        -- 发送认证请求
+        send_auth_request()
+        if callback_func then
+            callback_func("connect_result", { success = true })
+        end
+    elseif event == socket.EVENT then
+        -- 有数据到达
+        socket.rx(netc, rxbuff)
+        if rxbuff:used() > 0 then
+            local data = rxbuff:query()
+            log.info("socket", "收到数据", #data, "字节", data:toHex())
+            -- 处理接收到的数据
+            parse_data(data)
+        end
+        -- 清空缓冲区
+        rxbuff:del()
+    elseif event == socket.TX_OK then
+        socket.wait(netc)
+        log.info("socket", "发送完成")
+    elseif event == socket.CLOSED then
+        -- 连接错误或关闭
+        log.info("socket", "主动断开链接")
+    end
+end
+
+-- mqtt client的事件回调函数
+local function mqtt_client_event_cbfunc(connected, event, data, payload, metas)
+    log.info("mqtt_client_event_cbfunc", connected, event, data, payload, json.encode(metas))
+    -- 取消连接超时定时器(如果连接成功或失败)
+    if connect_timeout_timer then
+        sys.timerStop(connect_timeout_timer)
+        connect_timeout_timer = nil
+    end
+
+    -- mqtt连接成功
+    if event == "conack" then
+        is_connected = true
+        log.info("MQTT connected")
+        -- 重置重连计数,如果是重连的话,连接上服务器给重连计数重置为0
+        reconnect_count = 0
+        -- 订阅主题
+        local auth_topic = "/AirCloud/" .. config.device_id .. "/auth"
+        local all_topic = "/AirCloud/" .. config.device_id .. "/all"
+        log.info("mqtt_client_event_cbfunc", "订阅主题", auth_topic, all_topic)
+        connection:subscribe(auth_topic, 0)
+        connection:subscribe(all_topic, 0)
+
+        -- 发送认证请求
+        send_auth_request()
+
+        if callback_func then
+            callback_func("connect_result", { success = true })
+        end
+
+        -- 订阅成功
+    elseif event == "suback" then
+        -- 取消订阅成功
+    elseif event == "unsuback" then
+        -- 接收到服务器下发的publish数据
+        -- data:string类型,表示topic
+        -- payload:string类型,表示payload
+        -- metas:table类型,数据内容如下
+        -- {
+        --     qos: number类型,取值范围0,1,2
+        --     retain:number类型,取值范围0,1
+        --     dup:number类型,取值范围0,1
+        --     message_id: number类型
+        -- }
+    elseif event == "recv" then
+        -- 对接收到的publish数据处理
+        parse_data(payload)
+
+
+        -- 发送成功publish数据
+        -- data:number类型,表示message id
+    elseif event == "sent" then
+        -- 服务器断开mqtt连接
+    elseif event == "disconnect" then
+        is_connected = false
+        is_authenticated = false
+        connection:disconnect()
+        log.info("MQTT disconnected")
+
+        if callback_func then
+            callback_func("disconnect", {})
+        end
+
+        -- 尝试重连
+        if config.auto_reconnect and is_open then
+            is_open = false
+            schedule_reconnect()
+        end
+
+        -- 收到服务器的心跳应答
+    elseif event == "pong" then
+        -- 严重异常,本地会主动断开连接
+        -- data:string类型,表示具体的异常,有以下几种:
+        --       "connect":tcp连接失败
+        --       "tx":数据发送失败
+        --       "conack":mqtt connect后,服务器应答CONNACK鉴权失败,失败码为payload(number类型)
+        --       "other":其他异常
+    elseif event == "error" then
+        is_connected = false
+        is_authenticated = false
+        connection:disconnect()
+        local error_msg = "Unknown MQTT error"
+
+        -- 根据错误类型提供更详细的错误信息
+        if data == "connect" then
+            error_msg = "TCP connection failed"
+        elseif data == "tx" then
+            error_msg = "Data transmission failed"
+        elseif data == "conack" then
+            error_msg = "MQTT authentication failed with code: " .. tostring(payload)
+        end
+
+        log.info("MQTT error: " .. error_msg)
+
+        if callback_func then
+            callback_func("disconnect", { error = error_msg })
+        end
+
+        -- 尝试重连
+        if config.auto_reconnect and is_open then
+            is_open = false
+            schedule_reconnect()
+        end
+    end
+end
+
+
+
+
+
+
+-- 设置配置参数
+function excloud.setup(params)
+    if is_open then
+        return false, "excloud is already open"
+    end
+
+    -- 合并配置参数
+    for k, v in pairs(params) do
+        config[k] = v
+    end
+
+    -- 验证必要参数
+    -- if not config.device_id or config.device_id == "" then
+    --     return false, "Device ID is required"
+    -- end
+
+    if config.device_type == 1 then
+        config.device_id = mobile.imei()
+    elseif config.device_type == 2 then
+        config.device_id = wlan.getMac(nil, true)
+    --以太网设备
+    elseif config.device_type == 4 then
+        config.device_id = netdrv.mac(socket.LWIP_ETH)
+    else
+        return false, "未知设备类型"
+    end
+    -- 打包设备id
+    device_id_binary = packDeviceInfo(config.device_type, config.device_id)
+
+    return true
+end
+
+-- 注册回调函数
+function excloud.on(cbfunc)
+    if type(cbfunc) ~= "function" then
+        return false, "Callback must be a function"
+    end
+
+    callback_func = cbfunc
+    return true
+end
+
+-- 开启excloud服务
+function excloud.open()
+    -- 检查是否已打开
+    if is_open then
+        return false, "excloud is already open"
+    end
+
+    --判断是否初始化
+    if not device_id_binary then
+        return false, "excloud 没有初始化,请先调用setup"
+    end
+
+
+
+    -- 根据传输协议创建连接
+    if config.transport == "tcp" then
+        -- 创建接收缓冲区
+        rxbuff = zbuff.create(2048)
+        -- 创建TCP连接
+        log.info("创建TCP连接")
+        connection = socket.create(nil, tcp_socket_callback)
+        if not connection then
+            return false, "Failed to create socket"
+        end
+        -- 配置socket
+        socket.config(connection)      -- 开启TCP保活,防止长时间无数据交互被运营商断线
+        socket.debug(connection, true) -- 开启调试
+
+        -- 设置连接超时定时器
+        connect_timeout_timer = sys.timerStart(function()
+            if not is_connected then
+                log.error("TCP connection timeout")
+                if connection then
+                    socket.close(connection)
+                    socket.release(connection)
+                    connection = nil
+                end
+
+                if callback_func then
+                    callback_func("connect_result", { success = false, error = "Connection timeout" })
+                end
+
+                -- 尝试重连
+                if config.auto_reconnect and is_open then
+                    is_open = false
+                    schedule_reconnect()
+                end
+            end
+        end, config.timeout * 1000)
+
+        -- 连接到服务器
+        local ok, err = socket.connect(connection, config.host, config.port)
+        log.info("TCP连接结果", ok, err)
+        if not ok then
+            --发生异常,强制close
+            socket.close(connection)
+            --释放资源
+            socket.release(connection)
+            connection = nil
+            -- 首次连接失败尝试重连
+            if config.auto_reconnect and is_open then
+                is_open = false
+                schedule_reconnect()
+            end
+            return false,  err
+        end
+    elseif config.transport == "mqtt" then
+        -- 创建MQTT客户端
+        connection = mqtt.create(nil, config.host, config.port, config.ssl)
+
+        -- 设置认证信息
+        connection:auth(config.device_id, config.username, config.password)
+
+        -- 注册事件回调
+        connection:on(mqtt_client_event_cbfunc)
+        -- 设置保持连接间隔
+        connection:keepalive(config.keepalive)
+
+        -- 设置连接超时定时器
+        connect_timeout_timer = sys.timerStart(function()
+            if not is_connected then
+                log.error("MQTT connection timeout")
+
+                if callback_func then
+                    callback_func("connect_result", { success = false, error = "Connection timeout" })
+                end
+
+                -- 尝试重连
+                if config.auto_reconnect and is_open then
+                    is_open = false
+                    schedule_reconnect()
+                end
+            end
+        end, config.timeout * 1000)
+
+        -- 连接到服务器
+        local ok = connection:connect()
+        if not ok then
+            --连接失败,释放资源
+            connection:disconnect()
+            -- 发起连接失败,尝试重连
+            if config.auto_reconnect and is_open then
+                is_open = false
+                schedule_reconnect()
+            end
+            return false
+        end
+    else
+        return false, "Unsupported transport: " .. config.transport
+    end
+
+    is_open = true
+    log.info("excloud service started")
+
+    return true
+end
+
+-- 发送数据
+-- 发送消息到云端
+-- @param data table 待发送的数据,每个元素是一个包含 field_meaning、data_type 和 value 的表
+-- @param need_reply boolean 是否需要服务器回复,默认为 false
+-- @param is_auth_msg boolean 是否是鉴权消息,默认为 false
+function excloud.send(data, need_reply, is_auth_msg)
+    -- 检查参数是否为table
+    if type(data) ~= "table" then
+        -- 通过回调返回错误
+        if callback_func then
+            callback_func("send_result", {
+                success = false,
+                error_msg = "Invalid data parameter: table expected"
+            })
+        end
+        return false,"Invalid data parameter: table expected"
+    end
+
+    -- 检查服务是否开启
+    if not is_open then
+        if callback_func then
+            callback_func("send_result", {
+                success = false,
+                error_msg = "excloud service not open"
+            })
+        end
+        return false, "excloud service not open"
+    end
+
+    -- 检查是否已连接
+    if not is_connected then
+        if callback_func then
+            callback_func("send_result", {
+                success = false,
+                error_msg = "Not connected to server"
+            })
+        end
+        return false, "Not connected to server"
+    end
+
+    -- 保存当前序列号用于回调
+    local current_sequence = sequence_num
+
+    -- 构建消息体
+    local message_body = ""
+    for _, item in ipairs(data) do
+        log.info("发送数据333", item.field_meaning, item.data_type, item.value, message_body)
+
+        message_body = message_body .. build_tlv(item.field_meaning, item.data_type, item.value)
+    end
+
+    -- 检查消息长度
+    local udp_auth_key = config.udp_auth_key and true or false
+    local total_length = #message_body + (udp_auth_key and 64 or 0)
+
+    log.info("tlv发送数据长度4", total_length)
+
+    -- 构建消息头
+    local is_udp_transport = (config.transport == "udp")
+    local header = build_header(need_reply or false, is_udp_transport, total_length)
+
+    -- -- 添加鉴权key(如果是UDP的话)
+    local auth_key_part = ""
+    if config.transport == "udp" and udp_auth_key then
+        auth_key_part = config.udp_auth_key
+        if #auth_key_part < 64 then
+            auth_key_part = auth_key_part .. string.rep("\0", 64 - #auth_key_part)
+        elseif #auth_key_part > 64 then
+            auth_key_part = auth_key_part:sub(1, 64)
+        end
+    end
+    local full_message
+    -- 发送完整消息
+    if config.transport == "udp" then
+        full_message = header .. auth_key_part .. message_body
+    else
+        full_message = header .. message_body
+    end
+
+    log.info("发送消息长度", #header, #message_body, #full_message, full_message:toHex())
+
+    local success, err_code, err_msg
+    if config.transport == "tcp" then
+        if not connection then
+            err_msg = "TCP connection not available"
+            success = false
+        else
+            success, err_msg = socket.tx(connection, full_message)
+        end
+    elseif config.transport == "mqtt" then
+         -- 根据是否为鉴权消息选择不同的topic
+        local topic
+        if is_auth_msg then
+            topic = "/AirCloud/" .. config.device_id .. "/auth"
+        else
+            topic = "/AirCloud/" .. config.device_id .. "/all"
+        end
+        log.info("发布主题", topic, #full_message, full_message:toHex())
+        success = connection:publish(topic, full_message, config.qos, config.retain)
+    end
+
+    -- 通过回调返回发送结果
+    if callback_func then
+        callback_func("send_result", {
+            success = success,
+            error_msg = success and "Send successful" or err_msg,
+            sequence_num = current_sequence
+        })
+    end
+
+    if success then
+        log.info("数据发送成功", #full_message, "字节")
+        return true
+    else
+        log.error("数据发送失败", err_msg)
+        return false, err_code, err_msg
+    end
+end
+
+-- 关闭excloud服务
+function excloud.close()
+    if not is_open then
+        return false, "excloud not open"
+    end
+
+    -- 取消重连定时器
+    if reconnect_timer then
+        sys.timerStop(reconnect_timer)
+        reconnect_timer = nil
+    end
+
+    -- 关闭连接
+    if connection then
+        if config.transport == "tcp" then
+            socket.close(connection)
+            socket.release(connection)
+        elseif config.transport == "mqtt" then
+            connection:disconnect()
+        end
+        connection = nil
+    end
+
+    -- 重置状态
+    is_open = false
+    is_connected = false
+    is_authenticated = false
+    pending_messages = {}
+    rxbuff = nil
+
+    log.info("excloud service stopped")
+    return true
+end
+
+-- 获取当前状态
+function excloud.status()
+    return {
+        is_open = is_open,
+        is_connected = is_connected,
+        sequence_num = sequence_num,
+        reconnect_count = reconnect_count,
+        pending_messages = #pending_messages,
+    }
+end
+
+
+
+-- 导出常量
+excloud.DATA_TYPES = DATA_TYPES
+excloud.FIELD_MEANINGS = FIELD_MEANINGS
+
+return excloud