init: Air780EPM 官方 LuatOS 项目代码基线

- 来源: 合宙 LuatOS 官方仓库 air780epm 模块完整代码
- 路径: luatos/air780epm/module/Air780EPM/demo 含官方 demo(含 mqtt/mqtts/socket/uart 等)
- 后续: 基于 demo 开发 UART<->MQTT 数据上报功能
This commit is contained in:
wangfq
2026-08-31 08:53:49 +08:00
commit 173dd6874f
813 changed files with 125484 additions and 0 deletions
@@ -0,0 +1,259 @@
--[[
@module excloud_test
@summary excloud测试文件
@version 1.0
@date 2025.09.22
@author 孟伟
@usage
本demo演示的功能为:
本demo演示了excloud扩展库的完整使用流程,包括:
1. 设备连接与认证
2. 数据上报与接收
3. 运维日志管理
4. 文件上传功能
5. 心跳保活机制
]]
-- 导入excloud库
local excloud = require("excloud")
-- 注册回调函数
-- 注册回调函数
function on_excloud_event(event, data)
log.info("用户回调函数", event, json.encode(data))
if event == "connect_result" then
if data.success then
log.info("连接成功")
sys.publish("aircloud_connected")
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
log.info("TLV字段", "含义:", tlv.field, "类型:", tlv.type, "值:", tlv.value)
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.UNICODE,
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
elseif event == "mtn_log_upload_start" then
log.info("运维日志上传开始", "文件数量:", data.file_count)
elseif event == "mtn_log_upload_progress" then
log.info("运维日志上传进度",
"当前文件:", data.current_file,
"总数:", data.total_files,
"文件名:", data.file_name,
"状态:", data.status)
elseif event == "mtn_log_upload_complete" then
log.info("运维日志上传完成",
"成功:", data.success_count,
"失败:", data.failed_count,
"总计:", data.total_files)
end
end
-- 注册回调
excloud.on(on_excloud_event)
-- 主任务函数
function excloud_task_func()
-- 如果当前时间点设置的默认网卡还没有连接成功,一直在这里循环等待
while not socket.adapter(socket.dft()) do
log.warn("excloud_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
-- -- 配置excloud参数
local ok, err_msg = excloud.setup({
use_getip = true, -- 使用getip服务
device_type = 1, -- 4G设备
auth_key = "VmhtOb81EgZau6YyuuZJzwF6oUNGCbXi",
transport = "tcp", -- 使用TCP传输
auto_reconnect = true, -- 自动重连
reconnect_interval = 10, -- 重连间隔(秒)
max_reconnect = 5, -- 最大重连次数
mtn_log_enabled = true, -- 启用运维日志
mtn_log_blocks = 1, -- 日志文件块数
mtn_log_write_way = excloud.MTN_LOG_CACHE_WRITE -- 缓存写入方式
})
--不使用getip服务,注意把use_getip设置为false
-- local ok, err_msg = excloud.setup({
-- use_getip = false, -- 不使用getip服务
-- device_type = 1, -- 设备类型: 4G
-- host = "112.125.89.8", -- 服务器地址
-- port = 32585, -- 服务器端口
-- auth_key = "VmhtOb81EgZau6YyuuZJzwF6oUNGCbXi", -- 鉴权密钥
-- transport = "tcp", -- 使用TCP传输
-- auto_reconnect = true, -- 自动重连
-- reconnect_interval = 10, -- 重连间隔(秒)
-- max_reconnect = 5, -- 最大重连次数
-- mtn_log_enabled = true -- 启用运维日志
-- })
-- 配置excloud参数,虚拟设备链接
-- local ok, err_msg = excloud.setup({
-- use_getip = true, --使用getip服务
-- device_type = 9,
-- auth_key = "VmhtOb81EgZau6YyuuZJzwF6oUNGCbXi",
-- virtual_phone_number = "15893470522", -- 11位手机号
-- virtual_serial_num = 1, -- 序列号(0-999
-- transport = "tcp", -- 由于mqtt链接需要使用imei,虚拟设备没有,所以只能使用TCP传输
-- mtn_log_enabled = true
-- })
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服务已开启")
-- 启动自动心跳,默认5分钟一次的心跳
excloud.start_heartbeat()
log.info("自动心跳已启动")
-- 启动3分钟一次的心跳,可配置自定义内容
-- excloud.start_heartbeat(180, {
-- { field_meaning = excloud.FIELD_MEANINGS.TIMESTAMP,
-- data_type = excloud.DATA_TYPES.INTEGER,
-- value = os.time() }
-- })
-- 停止自动心跳
--excloud.stop_heartbeat()
-- 记录启动日志
--excloud.mtn_log("system", "设备启动完成", "version", "1.0.0")
-- 获取并打印二维码信息
local qrinfo = excloud.get_qrinfo()
if qrinfo and qrinfo.url then
log.info("二维码URL:", qrinfo.url)
else
log.info("未获取到二维码信息")
end
-- 主循环:定期上报数据
while true do
-- 每30秒上报一次数据
sys.wait(30000)
-- 检查连接状态
local status = excloud.status()
if not status.is_connected then
log.warn("设备未连接,跳过数据上报")
else
-- 上报基础状态数据
local ok, err_msg = excloud.send({
{
field_meaning = excloud.FIELD_MEANINGS.SIGNAL_STRENGTH_4G,
data_type = excloud.DATA_TYPES.INTEGER,
value = 22 -- 信号强度
},
{
field_meaning = excloud.FIELD_MEANINGS.SIM_ICCID,
data_type = excloud.DATA_TYPES.ASCII,
value = "89860118801012345678" -- SIM卡ICCID
},
{
field_meaning = excloud.FIELD_MEANINGS.TIMESTAMP,
data_type = excloud.DATA_TYPES.INTEGER,
value = os.time()
}
}, false)
if ok then
log.info("基础数据上报成功")
else
log.error("基础数据上报失败:", err_msg)
end
end
end
end
-- 启动主任务
sys.taskInit(excloud_task_func)
--上传图片示例
function upload_image_fun()
-- 等待连接建立
sys.waitUntil("aircloud_connected", 10000)
-- 上传图片
log.info("开始上传图片")
if not excloud.status().is_connected then
log.info("设备未连接,跳过图片上传")
return
end
if io.exists("/luadb/test.jpg") then
local ok, err = excloud.upload_image("/luadb/test.jpg", "test.jpg")
if ok then
log.info("图片上传成功")
else
log.error("图片上传失败:", err)
end
else
log.warn("测试图片文件不存在")
end
end
sys.taskInit(upload_image_fun)
-- 运维日志测试示例
function mtnlog_test_task()
local test_count = 0
while true do
test_count = test_count + 1
excloud.mtn_log("info", "mtn_test", test_count)
-- 每30秒记录一次
sys.wait(1000)
end
end
sys.taskInit(mtnlog_test_task)
@@ -0,0 +1,61 @@
--[[
@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这三位数字必须存在,但是没有任何用处,可以一直写为999
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
PROJECT = "excloud_test"
VERSION = "001.999.000"
-- 如果内核固件支持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()之后后面不要加任何语句!!!!!
@@ -0,0 +1,43 @@
--[[
@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(ip, adapter)
if adapter == socket.LWIP_GP then
-- 在位置1和2设置自定义的DNS服务器ip地址:
-- "223.5.5.5",这个DNS服务器IP地址是阿里云提供的DNS服务器IP地址;
-- "114.114.114.114",这个DNS服务器IP地址是国内通用的DNS服务器IP地址;
-- 可以加上以下两行代码,在自动获取的DNS服务器工作不稳定的情况下,这两个新增的DNS服务器会使DNS服务更加稳定可靠;
-- 如果使用专网卡,不要使用这两行代码;
-- 如果使用国外的网络,不要使用这两行代码;
socket.setDNS(adapter, 1, "223.5.5.5")
socket.setDNS(adapter, 2, "114.114.114.114")
log.info("netdrv_4g.ip_ready_func", "IP_READY", socket.localIP(socket.LWIP_GP))
end
end
local function ip_lose_func(adapter)
if adapter == socket.LWIP_GP then
log.warn("netdrv_4g.ip_lose_func", "IP_LOSE")
end
end
-- 此处订阅"IP_READY"和"IP_LOSE"两种消息
-- 在消息的处理函数中,仅仅打印了一些信息,便于实时观察4G网络的连接状态
-- 也可以根据自己的项目需求,在消息处理函数中增加自己的业务逻辑控制,例如可以在连网状态发生改变时更新网络图标
sys.subscribe("IP_READY", ip_ready_func)
sys.subscribe("IP_LOSE", ip_lose_func)
-- 在Air780EPM上,内核固件运行起来之后,默认网卡就是socket.LWIP_GP
@@ -0,0 +1,77 @@
--[[
@module netdrv_eth_spi
@summary “通过SPI外挂CH390H芯片的以太网卡”驱动模块
@version 1.0
@date 2025.07.24
@author 马梦阳
@usage
本文件为“通过SPI外挂CH390H芯片的以太网卡”驱动模块,核心业务逻辑为:
1、打开CH390H芯片供电开关;
2、初始化spi0,初始化以太网卡,并且在以太网卡上开启DHCP(动态主机配置协议);
3、以太网卡的连接状态发生变化时,在日志中进行打印;
直接使用Air780EPM V1.3版本开发板硬件测试即可;
本文件没有对外接口,直接在其他功能模块中require "netdrv_eth_spi"就可以加载运行;
]]
local exnetif = require "exnetif"
local function ip_ready_func(ip, adapter)
if adapter == socket.LWIP_ETH then
-- 在位置1和2设置自定义的DNS服务器ip地址:
-- "223.5.5.5",这个DNS服务器IP地址是阿里云提供的DNS服务器IP地址;
-- "114.114.114.114",这个DNS服务器IP地址是国内通用的DNS服务器IP地址;
-- 可以加上以下两行代码,在自动获取的DNS服务器工作不稳定的情况下,这两个新增的DNS服务器会使DNS服务更加稳定可靠;
-- 如果使用专网卡,不要使用这两行代码;
-- 如果使用国外的网络,不要使用这两行代码;
socket.setDNS(adapter, 1, "223.5.5.5")
socket.setDNS(adapter, 2, "114.114.114.114")
log.info("netdrv_eth_spi.ip_ready_func", "IP_READY", socket.localIP(socket.LWIP_ETH))
end
end
local function ip_lose_func(adapter)
if adapter == socket.LWIP_ETH then
log.warn("netdrv_eth_spi.ip_lose_func", "IP_LOSE")
end
end
-- 以太网联网成功(成功连接路由器,并且获取到了IP地址)后,内核固件会产生一个"IP_READY"消息
-- 各个功能模块可以订阅"IP_READY"消息实时处理以太网联网成功的事件
-- 也可以在任何时刻调用socket.adapter(socket.LWIP_ETH)来获取以太网是否连接成功
-- 以太网断网后,内核固件会产生一个"IP_LOSE"消息
-- 各个功能模块可以订阅"IP_LOSE"消息实时处理以太网断网的事件
-- 也可以在任何时刻调用socket.adapter(socket.LWIP_ETH)来获取以太网是否连接成功
--此处订阅"IP_READY"和"IP_LOSE"两种消息
--在消息的处理函数中,仅仅打印了一些信息,便于实时观察“通过SPI外挂CH390H芯片的以太网卡”的连接状态
--也可以根据自己的项目需求,在消息处理函数中增加自己的业务逻辑控制,例如可以在连网状态发生改变时更新网络图标
sys.subscribe("IP_READY", ip_ready_func)
sys.subscribe("IP_LOSE", ip_lose_func)
local function netdrv_eth_spi_task_func()
-- 配置SPI外接以太网芯片CH390H的单网卡,exnetif.set_priority_order使用的网卡编号为socket.LWIP_ETH
-- 本demo使用Air780EPM V1.3版本开发板测试,开发板上的硬件配置为:
-- GPIO20为CH390H以太网芯片的供电使能控制引脚
-- 使用spi0,片选引脚使用GPIO8,中断引脚使用GPIO22
-- 如果使用的硬件和以上描述的环境不同,根据自己的硬件配置修改以下参数
exnetif.set_priority_order({
{
ETHERNET = {
pwrpin = 20,
tp = netdrv.CH390,
opts = {spi = 0, cs = 8, irq = 22}
}
}
})
end
-- 启动一个tasktask的处理函数为netdrv_eth_spi_task_func
-- 在处理函数中调用exnetif.set_priority_order设置网卡优先级
-- 因为exnetif.set_priority_order要求必须在task中被调用,所以此处启动一个task
sys.taskInit(netdrv_eth_spi_task_func)
@@ -0,0 +1,88 @@
--[[
@module netdrv_multiple
@summary 多网卡(4G网卡、通过SPI外挂CH390H芯片的以太网卡)驱动模块
@version 1.0
@date 2025.07.24
@author 马梦阳
@usage
本文件为多网卡驱动模块,核心业务逻辑为:
1、调用exnetif.set_priority_order配置多网卡的控制参数以及优先级;
直接使用Air780EPM V1.3版本开发板硬件测试即可;
本文件没有对外接口,直接在其他功能模块中require "netdrv_multiple"就可以加载运行;
]]
local exnetif = require "exnetif"
-- 网卡状态变化通知回调函数
-- 当exnetif中检测到网卡切换或者所有网卡都断网时,会触发调用此回调函数
-- 当网卡切换切换时:
-- net_typestring类型,表示当前使用的网卡字符串
-- adapternumber类型,表示当前使用的网卡id
-- 当所有网卡断网时:
-- net_type:为nil
-- adapternumber类型,为-1
local function netdrv_multiple_notify_cbfunc(net_type,adapter)
-- 在位置1和2设置自定义的DNS服务器ip地址:
-- "223.5.5.5",这个DNS服务器IP地址是阿里云提供的DNS服务器IP地址;
-- "114.114.114.114",这个DNS服务器IP地址是国内通用的DNS服务器IP地址;
-- 可以加上以下两行代码,在自动获取的DNS服务器工作不稳定的情况下,这两个新增的DNS服务器会使DNS服务更加稳定可靠;
-- 如果使用专网卡,不要使用这两行代码;
-- 如果使用国外的网络,不要使用这两行代码;
socket.setDNS(adapter, 1, "223.5.5.5")
socket.setDNS(adapter, 2, "114.114.114.114")
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芯片”的以太网卡,使用Air780EPM V1.3版本开发板验证
{
ETHERNET = {
-- 供电使能GPIO
pwrpin = 20,
-- 设置的多个“已经IP READY,但是还没有ping通”网卡,循环执行ping动作的间隔(单位毫秒,可选)
-- 如果没有传入此参数,exnetif会使用默认值10秒
ping_time = 3000,
-- 连通性检测ip(选填参数)
-- 如果没有传入ip地址,exnetif中会默认使用httpdns能否成功获取baidu.com的ip作为是否连通的判断条件;
-- 如果传入,一定要传入可靠的并且可以ping通的ip地址;
-- ping_ip = "填入可靠的并且可以ping通的ip地址",
-- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
tp = netdrv.CH390,
opts = {spi=0, cs=8, irq=22}
}
},
-- 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网络应用通信异常时自动对当前使用的网卡进行连通性检测。
-- 启动一个tasktask的处理函数为netdrv_multiple_task_func
-- 在处理函数中调用exnetif.set_priority_order设置网卡优先级
-- 因为exnetif.set_priority_order要求必须在task中被调用,所以此处启动一个task
sys.taskInit(netdrv_multiple_task_func)
@@ -0,0 +1,57 @@
--[[
@module netdrv_pc
@summary “pc模拟器网卡”驱动模块
@version 1.0
@date 2025.07.01
@author 孟伟
@usage
本文件为pc模拟器网卡驱动模块,核心业务逻辑为:
1、监听"IP_READY"和"IP_LOSE",在日志中进行打印;
本文件没有对外接口,直接在其他功能模块中require "netdrv_pc"就可以加载运行;
]]
local function ip_ready_func(ip, adapter)
if adapter == socket.ETH0 then
-- 在位置1和2设置自定义的DNS服务器ip地址:
-- "223.5.5.5",这个DNS服务器IP地址是阿里云提供的DNS服务器IP地址;
-- "114.114.114.114",这个DNS服务器IP地址是国内通用的DNS服务器IP地址;
-- 可以加上以下两行代码,在自动获取的DNS服务器工作不稳定的情况下,这两个新增的DNS服务器会使DNS服务更加稳定可靠;
-- 如果使用专网卡,不要使用这两行代码;
-- 如果使用国外的网络,不要使用这两行代码;
socket.setDNS(adapter, 1, "223.5.5.5")
socket.setDNS(adapter, 2, "114.114.114.114")
log.info("netdrv_pc.ip_ready_func", "IP_READY", socket.localIP(socket.ETH0))
end
end
local function ip_lose_func(adapter)
if adapter == socket.ETH0 then
log.warn("netdrv_pc.ip_lose_func", "IP_LOSE")
end
end
--此处订阅"IP_READY"和"IP_LOSE"两种消息
--在消息的处理函数中,仅仅打印了一些信息,便于实时观察pc模拟器网络的连接状态
--也可以根据自己的项目需求,在消息处理函数中增加自己的业务逻辑控制,例如可以在连网状态发生改变时更新网络图标
sys.subscribe("IP_READY", ip_ready_func)
sys.subscribe("IP_LOSE", ip_lose_func)
-- 设置默认网卡为socket.ETH0
-- pc模拟器上的默认网卡仍然需要使用接口(socket.ETH0)来设置,因为exnetif扩展库当前还不支持模拟器
socket.dft(socket.ETH0)
-- 下面这段代码是在PC模拟器上构造一个唯一的ID;不同PC上运行模拟器,这个ID要不一样
-- 因为mqtt client使用的是这个ID做为client id,如果不同PC上的id一样,模拟器在不同PC上同时运行时,mqtt client就会频繁出现被踢下线的问题
-- 目前模拟器上还没有合适的接口获取唯一ID,所以此处先简单的构造一个ID,需要手动保证ID唯一,在此处我简单使用了zhutianhua1做为ID
_G.mobile = {}
function mobile.imei()
return "zhutianhua1"
-- log.info("mcu.unique_id()", mcu.unique_id())
-- return mcu.unique_id().."zhutianhua1"
end
@@ -0,0 +1,34 @@
--[[
@module netdrv_device
@summary 网络驱动设备功能模块
@version 1.0
@date 2025.07.24
@author 朱天华
@usage
本文件为网络驱动设备功能模块,核心业务逻辑为:根据项目需求,选择并且配置合适的网卡(网络适配器)
1、netdrv_4gsocket.LWIP_GP4G网卡;
2、netdrv_eth_spisocket.LWIP_USER1,通过SPI外挂CH390H芯片的以太网卡;
3、netdrv_multiple:可以配置多种网卡的优先级,按照优先级配置,使用其中一种网卡连接外网;
4、netdrv_pcpc模拟器上的网卡
根据自己的项目需求,只需要require以上四种中的一种即可;
本文件没有对外接口,直接在main.lua中require "netdrv_device"就可以加载运行;
]]
-- 根据自己的项目需求,只需要require以下四种中的一种即可;
-- 加载“4G网卡”驱动模块
require "netdrv_4g"
-- 加载“通过SPI外挂CH390H芯片的以太网卡”驱动模块
-- require "netdrv_eth_spi"
-- 加载“可以配置优先级的多种网卡”驱动模块
-- require "netdrv_multiple"
-- 加载“pc模拟器网卡”驱动模块
-- require "netdrv_pc"
@@ -0,0 +1,147 @@
## 功能模块介绍
1、main.lua:主程序入口;
2、netdrv_device.lua:网卡驱动设备,可以配置使用netdrv文件夹内的四种网卡(单4g网卡,单spi以太网卡,单pc模拟器网卡,多网卡)中的任何一种网卡;
3、excloud.lua aircloud的实现库
4、excloud_test.luaaircloud的应用模块,实现了aircloud的应用场景。
## 演示功能概述
使用Air780EPM V1.3开发板测试aircloud功能
AirCloud 概述:AirCloud 是 LuatOS 物联网设备云服务通信协议,提供设备连接、数据上报、远程控制和文件上传等核心功能。excloud 扩展库是 AirCloud 协议的实现,通过该库设备可以快速接入云服务平台,实现远程监控和管理。
本demo演示了excloud扩展库的完整使用流程,包括:
1. 设备连接与认证
2. 数据上报与接收
3. 运维日志管理
4. 文件上传功能
5. 心跳保活机制
## 演示硬件环境
![img](https://docs.openluat.com/air780epm/luatos/app/driver/eth/image/RFSvb75NRoEWqYxfCRVcVrOKnsf.jpg)
1、Air780EPM V1.3版本开发板一块+可上网的sim卡一张+4g天线一根:
- sim卡插入开发板的sim卡槽
- 天线装到开发板上
2、TYPE-C USB数据线一根 Air780EPM V1.3版本开发板和数据线的硬件接线方式为:
- Air780EPM V1.3版本开发板通过TYPE-C USB口供电;(外部供电/USB供电 拨动开关 拨到 USB供电一端)
- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
## 演示软件环境
1、Luatools下载调试工具
2、[Air780EPM V2018版本固件](https://docs.openluat.com/air780epm/luatos/firmware/version/)
## 演示核心步骤
1、搭建好硬件环境
2、demo脚本代码netdrv_device.lua中,按照自己的网卡需求启用对应的Lua文件
- 如果需要单4G网卡,打开require "netdrv_4g",其余注释掉
- 如果需要以太网卡,打开require "netdrv_eth_spi",其余注释掉
- 如果需要多网卡,打开require "netdrv_multiple",其余注释掉
3、修改excloud_test.lua文件中excloud.setup接口的相关参数,根据自己需求配置连接协议、是否启用运维日志、项目key、设备类型,是否启用getip等内容。
4、烧录好后,板子开机同时在luatools上查看日志:
```lua
[2025-10-16 17:59:41.066][000000003.897] I/user.[excloud]excloud.setup ID: 862419074072389
[2025-10-16 17:59:41.072][000000003.897] I/user.excloud初始化成功
[2025-10-16 17:59:41.074][000000003.897] I/user.[excloud]...
[2025-10-16 17:59:41.077][000000003.898] I/user.[excloud]excloud.getip : 3 key: VmhtOb81EgZau6YyuuZJzwF6oUNGCbXi-862419074072389
[2025-10-16 17:59:41.080][000000003.904] D/socket connect to gps.openluat.com,443
[2025-10-16 17:59:41.083][000000003.904] dns_run 676:gps.openluat.com state 0 id 1 ipv6 0 use dns server2, try 0
[2025-10-16 17:59:41.144][000000003.969] dns_run 693:dns all done ,now stop
[2025-10-16 17:59:41.706][000000004.539] I/user.httpplus
[2025-10-16 17:59:41.862][000000004.693] I/user.httpplus
[2025-10-16 17:59:41.893][000000004.712] I/user.httpplus ,
[2025-10-16 17:59:41.924][000000004.745] I/user.[excloud]excloud.getip响应 HTTP Code: 200 Body: {"msg":"ok","conninfo":{"ipv4":"124.71.128.165","port":9108},"imginfo":{"url":"https://gps.openluat.com/iot/aircloud/upload/image","data_key":"f","data_param":{"key":"WMi7G6Kcx8H7UoknB2Knt8btqbDvTAEvAWeQgg","tip":""}},"audinfo":{"url":"https://gps.openluat.com/iot/aircloud/upload/audio","data_key":"f","data_param":{"key":"WMi7G6Kcx8H7UoknB2Knt8btqbDvTAEvAWeQgg","tip":""}}} Body: nil Cannot serialise userdata: type not supported
[2025-10-16 17:59:41.933][000000004.746] I/user.[excloud]excloud.getip响应 JSON: ok
[2025-10-16 17:59:41.938][000000004.747] I/user.[excloud]excloud.getip 124.71.128.165 9108
[2025-10-16 17:59:41.943][000000004.748] I/user.[excloud]excloud.getip : true : {"audinfo":{"data_param":{"key":"WMi7G6Kcx8H7UoknB2Knt8btqbDvTAEvAWeQgg","tip":""},"data_key":"f","url":"https:\/\/gps.openluat.com\/iot\/aircloud\/upload\/audio"},"imginfo":{"data_param":{"key":"WMi7G6Kcx8H7UoknB2Knt8btqbDvTAEvAWeQgg","tip":""},"data_key":"f","url":"https:\/\/gps.openluat.com\/iot\/aircloud\/upload\/image"},"msg":"ok","conninfo":{"ipv4":"124.71.128.165","port":9108}}
[2025-10-16 17:59:41.947][000000004.748] I/user.[excloud] true {"audinfo":{"data_param":{"key":"WMi7G6Kcx8H7UoknB2Knt8btqbDvTAEvAWeQgg","tip":""},"data_key":"f","url":"https:\/\/gps.openluat.com\/iot\/aircloud\/upload\/audio"},"imginfo":{"data_param":{"key":"WMi7G6Kcx8H7UoknB2Knt8btqbDvTAEvAWeQgg","tip":""},"data_key":"f","url":"https:\/\/gps.openluat.com\/iot\/aircloud\/upload\/image"},"msg":"ok","conninfo":{"ipv4":"124.71.128.165","port":9108}} url https://gps.openluat.com/iot/aircloud/upload/image
[2025-10-16 17:59:41.952][000000004.749] I/user.[excloud]TCP连接
[2025-10-16 17:59:41.959][000000004.750] D/socket connect to 124.71.128.165,9108
[2025-10-16 17:59:41.962][000000004.750] network_socket_connect 1605:network 0 local port auto select 50642
[2025-10-16 17:59:41.965][000000004.751] I/user.[excloud]TCP连接结果 true false
[2025-10-16 17:59:41.969][000000004.752] I/user.[excloud]excloud service started
[2025-10-16 17:59:41.976][000000004.752] I/user.excloud服务已开启
[2025-10-16 17:59:41.979][000000004.753] I/user.[excloud]excloud 300
[2025-10-16 17:59:41.983][000000004.792] network_default_socket_callback 1120:before process socket 1,event:0xf2000009(),state:3(),wait:2()
[2025-10-16 17:59:41.985][000000004.792] network_default_socket_callback 1124:after process socket 1,state:5(线),wait:0()
[2025-10-16 17:59:41.997][000000004.793] I/user.[excloud]socket cb userdata: 0C199080 33554449 0
[2025-10-16 17:59:42.002][000000004.794] I/user.[excloud]socket TCP连接成功
[2025-10-16 17:59:42.006][000000004.794] I/user. connect_result {"success":true}
[2025-10-16 17:59:42.008][000000004.794] I/user.
[2025-10-16 17:59:42.011][000000004.797] I/user.[excloud]333 16 3 VmhtOb81EgZau6YyuuZJzwF6oUNGCbXi-862419074072389-20250228145308A686442A0057563473
[2025-10-16 17:59:42.014][000000004.798] I/user.[excloud]tlv发送数据长度4 85
[2025-10-16 17:59:42.020][000000004.799] I/user.[excloud] $ @r8
[2025-10-16 17:59:42.023][000000004.801] I/user.[excloud] 16 85 101 0186241907407238000100550000001130100051566D68744F62383145675A617536597975755A4A7A7746366F554E47436258692D3836323431393037343037323338392D3230323530323238313435333038413638363434324130303537353633343733 202
[2025-10-16 17:59:42.027][000000004.802] I/user. send_result {"sequence_num":0,"success":true,"error_msg":"Send successful"}
[2025-10-16 17:59:42.031][000000004.802] I/user.: 0
[2025-10-16 17:59:42.035][000000004.803] I/user.[excloud] 101
[2025-10-16 17:59:42.041][000000004.848] network_default_socket_callback 1120:before process socket 1,event:0xf2000004(),state:5(线),wait:3()
[2025-10-16 17:59:42.045][000000004.848] network_default_socket_callback 1124:after process socket 1,state:5(线),wait:0()
[2025-10-16 17:59:42.048][000000004.849] I/user.[excloud]socket cb userdata: 0C199080 33554450 0
[2025-10-16 17:59:42.057][000000004.849] I/user.[excloud]socket
[2025-10-16 17:59:47.455][000000010.283] I/user.
[2025-10-16 17:59:47.461][000000010.284] I/user.[excloud] : 1 : test.jpg : 199658
[2025-10-16 17:59:47.465][000000010.286] I/user.[excloud]333 23 4 
[2025-10-16 17:59:47.471][000000010.287] I/user.[excloud]tlv发送数据长度4 32
[2025-10-16 17:59:47.483][000000010.289] I/user.[excloud] $ @r8
[2025-10-16 17:59:47.486][000000010.290] I/user.[excloud] 16 32 48 018624190740723800020020000000014017001C031000040000000133110008746573742E6A70670312000400030BEA 96
[2025-10-16 17:59:47.509][000000010.291] I/user. send_result {"sequence_num":1,"success":true,"error_msg":"Send successful"}
[2025-10-16 17:59:47.514][000000010.291] I/user.: 1
[2025-10-16 17:59:47.518][000000010.292] I/user.[excloud] 48
[2025-10-16 17:59:47.523][000000010.295] D/socket connect to gps.openluat.com,443
[2025-10-16 17:59:47.529][000000010.296] dns_run 676:gps.openluat.com state 0 id 2 ipv6 0 use dns server2, try 0
[2025-10-16 17:59:47.545][000000010.326] dns_run 693:dns all done ,now stop
[2025-10-16 17:59:47.549][000000010.352] network_default_socket_callback 1120:before process socket 1,event:0xf2000004(),state:5(线),wait:3()
[2025-10-16 17:59:47.554][000000010.352] network_default_socket_callback 1124:after process socket 1,state:5(线),wait:0()
[2025-10-16 17:59:47.557][000000010.353] I/user.[excloud]socket cb userdata: 0C199080 33554450 0
[2025-10-16 17:59:47.561][000000010.353] I/user.[excloud]socket
[2025-10-16 17:59:49.789][000000012.616] I/user.httpplus
[2025-10-16 17:59:49.913][000000012.744] I/user.httpplus
[2025-10-16 17:59:49.961][000000012.792] I/user.httpplus ,
[2025-10-16 17:59:50.006][000000012.825] I/user.[excloud]excloud.getip文件上传响应 HTTP Code: 200 Body: {"info":"iot./iot/aircloud/upload/image->iam-server./iam/tenant/getbyoid/6268048492107342913","code":0,"trace":"code:iot./iot/aircloud/upload/image->iam-server./iam/tenant/getbyoid/6268048492107342913, trcace:clear 1 temp suc infos.","log":"^^^","value":{"uri":"/vsna/luatos/336677/aircloud_image/5411605038321602040/2025-10/test.jpg","size":"194.00KB","thumb":"/vsna/luatos/336677/aircloud_image/5411605038321602040/2025-10/testt.jpg"}}
[2025-10-16 17:59:50.024][000000012.825] Body:
[2025-10-16 17:59:50.035][000000012.825] nil Cannot serialise userdata: type not supported
[2025-10-16 17:59:50.050][000000012.826] E/user. : nil : nil
[2025-10-16 17:59:50.064][000000012.829] I/user.[excloud]333 24 4 
[2025-10-16 17:59:50.077][000000012.830] I/user.[excloud]tlv发送数据长度4 32
[2025-10-16 17:59:50.090][000000012.831] I/user.[excloud] $ @r8
[2025-10-16 17:59:50.103][000000012.832] I/user.[excloud] 16 32 48 018624190740723800030020000000014018001C031000040000000133110008746573742E6A70670313000400000000 96
[2025-10-16 17:59:50.114][000000012.834] I/user. send_result {"sequence_num":2,"success":true,"error_msg":"Send successful"}
[2025-10-16 17:59:50.127][000000012.835] I/user.: 2
[2025-10-16 17:59:50.140][000000012.835] I/user.[excloud] 48
[2025-10-16 17:59:50.153][000000012.835] E/user.: : nil
[2025-10-16 17:59:50.169][000000012.876] network_default_socket_callback 1120:before process socket 1,event:0xf2000004(),state:5(线),wait:3()
[2025-10-16 17:59:50.182][000000012.876] network_default_socket_callback 1124:after process socket 1,state:5(线),wait:0()
[2025-10-16 17:59:50.191][000000012.877] I/user.[excloud]socket cb userdata: 0C199080 33554450 0
[2025-10-16 17:59:50.205][000000012.877] I/user.[excloud]socket
[2025-10-16 18:00:11.918][000000034.753] I/user.[excloud]333 782 0 22
[2025-10-16 18:00:11.941][000000034.755] I/user.[excloud]333 783 3 8 
[2025-10-16 18:00:11.955][000000034.755] I/user.[excloud]tlv发送数据长度4 13
[2025-10-16 18:00:11.969][000000034.757] I/user.[excloud] $ @r8
[2025-10-16 18:00:11.984][000000034.758] I/user.[excloud] 16 13 29 01862419074072380004000D00000001030E000400000016330F000138 58
[2025-10-16 18:00:11.994][000000034.762] I/user. send_result {"sequence_num":3,"success":true,"error_msg":"Send successful"}
[2025-10-16 18:00:12.010][000000034.762] I/user.: 3
[2025-10-16 18:00:12.022][000000034.762] I/user.[excloud] 29
[2025-10-16 18:00:12.035][000000034.763] I/user.
[2025-10-16 18:00:12.049][000000034.873] network_default_socket_callback 1120:before process socket 1,event:0xf2000004(),state:5(线),wait:3()
[2025-10-16 18:00:12.061][000000034.874] network_default_socket_callback 1124:after process socket 1,state:5(线),wait:0()
[2025-10-16 18:00:12.075][000000034.874] I/user.[excloud]socket cb userdata: 0C199080 33554450 0
[2025-10-16 18:00:12.086][000000034.875] I/user.[excloud]socket
```
Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB