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,460 @@
--[[
@module http_app
@summary http应用功能模块
@version 1.0
@date 2025.08.01
@author 马梦阳
@usage
本文件为http应用功能模块,核心业务逻辑为:基于不同的应用场景,演示http核心库的使用方式;
http核心库和httpplus扩展库的区别如下:
| 区别项 | http核心库 | httpplus扩展库 |
| --------------------------------- | ------------------------------------------------------------------------- | ---------------------------- |
| 文件上传 | 文件最大64KB | 只要内存够用,文件大小不限 |
| 文件下载 | 支持,只要文件系统空间够用,文件大小不限 | 不支持 |
| http header的key: value的限制 | 所有header的value总长度不能超过4KB,单个header的value长度不能超过1KB | 只要内存够用,header长度不限 |
| 鉴权URL自动识别 | 不支持 | 支持 |
| 接收到的body数据存储支持zbuff | 不支持 | 支持,可以直接传输给uart等库 |
| 接收到的body数据存储到内存中 | 最大支持32KB | 只要内存够用,大小不限 |
| chunk编码 | 支持 | 不支持 |
本文件没有对外接口,直接在main.lua中require "http_app"就可以加载运行;
]]
-- http下载数据回调函数
-- content_lennumber类型,数据总长度
-- body_lennumber类型,已经下载的数据长度
-- userdata:下载回调函数使用的用户自定义回调参数
-- 每收到一包body数据,就会调用一次http_cbfunc回调函数
local function http_cbfunc(content_len, body_len, userdata)
log.info("http_cbfunc", content_len, body_len, userdata)
end
-- 普通的http get请求功能演示
-- 请求的body数据保存到内存变量中,在内存够用的情况下,最大支持32KB的数据存储到内存中
-- timeout可以设置超时时间
-- callback可以设置回调函数,可用于实时检测body数据的下载进度
local function http_app_get()
-- https get请求https://httpbin.luatos.com/get网页内容
-- 如果请求成功,请求的数据保存到body中
local code, headers, body = http.request("GET", "https://httpbin.luatos.com/get").wait()
log.info("http_app_get1",
code==200 and "success" or "error",
code,
json.encode(headers or {}),
body and (body:len()>512 and body:len() or body) or "nil")
-- https get请求https://www.luatos.com/网页内容,超时时间为10秒
-- 请求超时时间为10秒,用户自己写代码时,不要照抄10秒,根据自己业务逻辑的需要设置合适的超时时间
-- 回调函数为http_cbfunc,回调函数使用的第三个回调参数为"http_app_get2"
-- 如果请求成功,请求的数据保存到body中
code, headers, body = http.request("GET", "https://www.luatos.com/", nil, nil, {timeout=10000, userdata="http_app_get2", callback=http_cbfunc}).wait()
log.info("http_app_get2",
code==200 and "success" or "error",
code,
json.encode(headers or {}),
body and (body:len()>512 and body:len() or body) or "nil")
-- http get请求http://httpbin.luatos.com/get网页内容,超时时间为3秒
-- 请求超时时间为3秒,用户自己写代码时,不要照抄3秒,根据自己业务逻辑的需要设置合适的超时时间
-- 回调函数为http_cbfunc,回调函数使用的第三个回调参数为"http_app_get3"
-- 如果请求成功,请求的数据保存到body中
code, headers, body = http.request("GET", "http://httpbin.luatos.com/get", nil, nil, {timeout=3000, userdata="http_app_get3", callback=http_cbfunc}).wait()
log.info("http_app_get3",
code==200 and "success" or "error",
code,
json.encode(headers or {}),
body and (body:len()>512 and body:len() or body) or "nil")
end
-- http get下载压缩数据的功能演示
local function http_app_get_gzip()
-- https get请求https://devapi.qweather.com/v7/weather/now?location=101010100&key=0e8c72015e2b4a1dbff1688ad54053de网页内容
-- 如果请求成功,请求的数据保存到body中
local code, headers, body = http.request("GET", "https://devapi.qweather.com/v7/weather/now?location=101010100&key=0e8c72015e2b4a1dbff1688ad54053de").wait()
log.info("http_app_get_gzip",
code==200 and "success" or "error",
code,
json.encode(headers or {}),
body and (body:len()>512 and body:len() or body) or "nil")
-- 如果请求成功
if code == 200 then
-- 从body的第11个字节开始解压缩
local uncompress_data = miniz.uncompress(body:sub(11,-1), 0)
if not uncompress_data then
log.error("http_app_get_gzip uncompress error")
return
end
local json_data = json.decode(uncompress_data)
if not json_data then
log.error("http_app_get_gzip json.decode error")
return
end
log.info("http_app_get_gzip json_data", json_data)
log.info("http_app_get_gzip", "和风天气", json_data.code)
if json_data.now then
log.info("http_app_get_gzip", "和风天气", "天气", json_data.now.text)
log.info("http_app_get_gzip", "和风天气", "温度", json_data.now.temp)
end
end
end
-- http get下载数据保存到文件中的功能演示
-- 请求的body数据保存到文件中,在文件系统够用的情况下,文件大小不限
-- timeout可以设置超时时间
-- callback可以设置回调函数,可用于实时检测文件下载进度
local function http_app_get_file()
-- 创建/http_download目录,用来存放通过http下载的文件
-- 重复创建目录会返回失败
-- 在创建目录之前可以使用io.dexist判断下目录是否存在
-- io.dexist接口仅新版本支持(Air780系列需要使用V2014及以上固件)
-- 若有报错提示,请检查是否是因为使用了旧版本的内核固件
local download_dir = "/http_download/"
if not io.dexist(download_dir) then
local result, reason = io.mkdir(download_dir)
if not result then
log.error("http_app_get_file io.mkdir error", reason)
end
end
local file_path = download_dir.."get_file1.html"
-- https get请求https://httpbin.luatos.com/get网页内容
-- 如果请求成功,请求的数据保存到文件file_path中
local code, headers, body_size = http.request("GET", "https://httpbin.luatos.com/get", nil, nil, {dst=file_path}).wait()
log.info("http_app_get_file1",
code==200 and "success" or "error",
code,
json.encode(headers or {}),
body_size)
-- 如果下载成功
if code==200 then
-- 读取文件大小
local size = io.fileSize(file_path)
log.info("http_app_get_file1", "io.fileSize="..size)
if size~=body_size then
log.error("io.fileSize doesn't equal with body_size, error", size, body_size)
end
--文件使用完之后,如果以后不再用到,根据需要可以自行删除
os.remove(file_path)
end
file_path = download_dir.."get_file2.html"
-- https get请求https://www.luatos.com/网页内容
-- 请求超时时间为10秒,用户自己写代码时,不要照抄10秒,根据自己业务逻辑的需要设置合适的超时时间
-- 回调函数为http_cbfunc,回调函数使用的第三个回调参数为"http_app_get_file2"
-- 如果请求成功,请求的数据保存到文件file_path中
code, headers, body_size = http.request("GET", "https://www.luatos.com/", nil, nil, {dst=file_path, timeout=10000, userdata="http_app_get_file2", callback=http_cbfunc}).wait()
log.info("http_app_get_file2",
code==200 and "success" or "error",
code,
json.encode(headers or {}),
body_size)
-- 如果下载成功
if code==200 then
-- 读取文件大小
local size = io.fileSize(file_path)
log.info("http_app_get_file2", "io.fileSize="..size)
if size~=body_size then
log.error("io.fileSize doesn't equal with body_size, error", size, body_size)
end
--文件使用完之后,如果以后不再用到,根据需要可以自行删除
os.remove(file_path)
end
file_path = download_dir.."get_file3.html"
-- http get请求http://httpbin.luatos.com/get网页内容,超时时间为3秒
-- 请求超时时间为3秒,用户自己写代码时,不要照抄3秒,根据自己业务逻辑的需要设置合适的超时时间
-- 回调函数为http_cbfunc,回调函数使用的第三个回调参数为"http_app_get_file3"
-- 如果请求成功,请求的数据保存到文件file_path中
code, headers, body_size = http.request("GET", "http://httpbin.luatos.com/get", nil, nil, {dst=file_path, timeout=3000, userdata="http_app_get_file3", callback=http_cbfunc}).wait()
log.info("http_app_get_file3",
code==200 and "success" or "error",
code,
json.encode(headers or {}),
body_size)
-- 如果下载成功
if code==200 then
-- 读取文件大小
local size = io.fileSize(file_path)
log.info("http_app_get_file3", "io.fileSize="..size)
if size~=body_size then
log.error("io.fileSize doesn't equal with body_size, error", size, body_size)
end
--文件使用完之后,如果以后不再用到,根据需要可以自行删除
os.remove(file_path)
end
end
-- http post提交表单数据功能演示
local function http_app_post_form()
local params = {
username = "LuatOS",
password = "123456"
}
local body = ""
-- 拼接成url编码的键值对的形式
for k, v in pairs(params) do
body = body .. k .. "=" .. tostring(v):urlEncode() .. "&"
end
-- 删除最后一位的&字符,最终为string类型的username=LuatOS&password=123456
body = body:sub(1,-2)
-- http post提交表单数据
-- http://httpbin.luatos.com/post为回环测试服务器,服务器收到post提交的表单数据后,还会下发同样的表单数据给设备
-- ["Content-Type"] = "application/x-www-form-urlencoded" 表示post提交的body数据格式为url编码的键值对形式的表单数据
-- 如果请求成功,服务器应答的数据会保存到resp_body中
local code, headers, resp_body = http.request("POST", "http://httpbin.luatos.com/post", {["Content-Type"] = "application/x-www-form-urlencoded"}, body).wait()
log.info("http_app_post_form",
code==200 and "success" or "error",
code,
json.encode(headers or {}),
resp_body and (resp_body:len()>512 and resp_body:len() or resp_body) or "nil")
end
-- http post提交json数据功能演示
local function http_app_post_json()
local params = {
username = "LuatOS",
password = "123456"
}
local body = json.encode(params)
-- http post提交json数据
-- http://httpbin.luatos.com/post为回环测试服务器,服务器收到post提交的json数据后,还会下发同样的json数据给设备
-- ["Content-Type"] = "application/json" 表示post提交的body数据格式为json格式的数据
-- 如果请求成功,服务器应答的数据会保存到resp_body中
local code, headers, resp_body = http.request("POST", "http://httpbin.luatos.com/post", {["Content-Type"] = "application/json"}, body).wait()
log.info("http_app_post_json",
code==200 and "success" or "error",
code,
json.encode(headers or {}),
resp_body and (resp_body:len()>512 and resp_body:len() or resp_body) or "nil")
end
-- http post提交纯文本数据功能演示
local function http_app_post_text()
-- http post提交纯文本数据
-- http://httpbin.luatos.com/post为回环测试服务器,服务器收到post提交的纯文本数据后,还会下发同样的纯文本数据给设备
-- ["Content-Type"] = "text/plain" 表示post提交的body数据格式为纯文本格式的数据
-- 如果请求成功,服务器应答的数据会保存到resp_body中
local code, headers, resp_body = http.request("POST", "http://httpbin.luatos.com/post", {["Content-Type"] = "text/plain"}, "This is a raw text message from LuatOS device").wait()
log.info("http_app_post_text",
code==200 and "success" or "error",
code,
json.encode(headers or {}),
resp_body and (resp_body:len()>512 and resp_body:len() or resp_body) or "nil")
end
-- http post提交xml数据功能演示
local function http_app_post_xml()
-- [=[ 和 ]=] 之间是一个多行字符串
local body = [=[
<?xml version="1.0" encoding="UTF-8"?>
<user>
<name>LuatOS</name>
<password>123456</password>
</user>
]=]
-- http post提交xml数据
-- http://httpbin.luatos.com/post为回环测试服务器,服务器收到post提交的xml数据后,还会下发同样的xml数据给设备
-- ["Content-Type"] = "text/xml" 表示post提交的body数据格式为xml格式的数据
-- 如果请求成功,服务器应答的数据会保存到resp_body中
local code, headers, resp_body = http.request("POST", "http://httpbin.luatos.com/post", {["Content-Type"] = "text/xml"}, body).wait()
log.info("http_app_post_xml",
code==200 and "success" or "error",
code,
json.encode(headers or {}),
resp_body and (resp_body:len()>512 and resp_body:len() or resp_body) or "nil")
end
-- http post提交原始二进制数据功能演示
local function http_app_post_binary()
local body = io.readFile("/luadb/logo.jpg")
-- http post提交原始二进制数据
-- http://uploadtest.luatos.com/api/upload/jpg为jpg图片上传测试服务器
-- 此处将logo.jpg的原始二进制数据做为body上传到服务器
-- 上传成功后,电脑上浏览器打开https://uploadtest.luatos.com/upload/jpg/,打开对应的测试日期目录,点击具体的测试时间照片,可以查看上传的照片
-- ["Content-Type"] = "application/octet-stream" 表示post提交的body数据格式为原始二进制格式的数据
-- 如果请求成功,服务器应答的数据会保存到resp_body中
-- 因为Air32.com平台已经不开放使用了,所以该DEMO仅作上传照片至服务器的演示作用,使用时请将上传URL修改为您自己的服务器地址
-- 或者通过excloud扩展库上传到合宙IOT平台uploadtest.luatos.com
local code, headers, resp_body = http.request("POST", "http://uploadtest.luatos.com/api/upload/jpg", {["Content-Type"] = "application/octet-stream"}, body).wait()
log.info("http_app_post_binary",
code==200 and "success" or "error",
code,
json.encode(headers or {}),
resp_body and (resp_body:len()>512 and resp_body:len() or resp_body) or "nil")
end
local function post_multipart_form_data(url, params)
local boundary = "----WebKitFormBoundary"..os.time()
local req_headers = {
["Content-Type"] = "multipart/form-data; boundary="..boundary,
}
local body = {}
-- 解析拼接 body
for k,v in pairs(params) do
if k=="texts" then
local bodyText = ""
for kk,vv in pairs(v) do
print(kk,vv)
bodyText = bodyText.."--"..boundary.."\r\nContent-Disposition: form-data; name=\""..kk.."\"\r\n\r\n"..vv.."\r\n"
end
table.insert(body, bodyText)
elseif k=="files" then
local contentType =
{
txt = "text/plain", -- 文本
jpg = "image/jpeg", -- JPG 格式图片
jpeg = "image/jpeg", -- JPEG 格式图片
png = "image/png", -- PNG 格式图片
gif = "image/gif", -- GIF 格式图片
html = "image/html", -- HTML
json = "application/json", -- JSON
}
for kk,vv in pairs(v) do
if type(vv) == "table" then
for i=1, #vv do
print(kk,vv[i])
table.insert(body, "--"..boundary.."\r\nContent-Disposition: form-data; name=\""..kk.."\"; filename=\""..vv[i]:match("[^%/]+%w$").."\"\r\nContent-Type: "..contentType[vv[i]:match("%.(%w+)$")].."\r\n\r\n")
table.insert(body, io.readFile(vv[i]))
table.insert(body, "\r\n")
end
else
print(kk,vv)
table.insert(body, "--"..boundary.."\r\nContent-Disposition: form-data; name=\""..kk.."\"; filename=\""..vv:match("[^%/]+%w$").."\"\r\nContent-Type: "..contentType[vv:match("%.(%w+)$")].."\r\n\r\n")
table.insert(body, io.readFile(vv))
table.insert(body, "\r\n")
end
end
end
end
table.insert(body, "--"..boundary.."--\r\n")
body = table.concat(body)
log.info("headers: ", "\r\n" .. json.encode(req_headers), type(body))
log.info("body: " .. body:len() .. "\r\n" .. body)
local code, headers, resp_body = http.request("POST", url, req_headers, body).wait()
log.info("post_multipart_form_data",
code==200 and "success" or "error",
code,
json.encode(headers or {}),
resp_body and (resp_body:len()>512 and resp_body:len() or resp_body) or "nil")
end
-- http post文件上传功能演示
local function http_app_post_file()
-- 此接口post_multipart_form_data支持单文件上传、多文件上传、单文本上传、多文本上传、单/多文本+单/多文件上传
-- https://airtest.luatos.com/iot/luat_test_file/add 只支持单文件上传或者单文件+单文本上传
-- 要求上传的文件name必须使用"f",上传的文本name必须使用"params"
-- 所以此处仅演示了单文件+单文本上传功能,并且"f"和"params"不能改成其他名字,否则会出现上传失败的应答
-- 测试接口的响应说明:
-- 成功:HTTP 200 OK{"code":0,"value":"上传成功"}
-- 失败:HTTP 状态码非 200 OK 或是 200 OK 但 code 不为 0
-- 如何在网页端查看上传的文件:
-- 在浏览器中打开https://iot.luatos.com/#/p8000/netlab_file_server,即可查看上传的文件;
-- 如果你自己的http服务支持更多类型的文本/文件混合上传,可以打开注释自行验证
post_multipart_form_data(
"https://airtest.luatos.com/iot/luat_test_file/add",
{
texts =
{
["params"] = json.encode({
username = "LuatOS",
password = "123456"
}),
-- ["username"] = "LuatOS",
-- ["password"] = "123456",
},
files =
{
["f"] = "/luadb/logo.jpg",
-- ["logo1.jpg"] = "/luadb/logo.jpg",
}
}
)
end
-- http app task 的任务处理函数
local function http_app_task_func()
while true do
-- 如果当前时间点设置的默认网卡还没有连接成功,一直在这里循环等待
while not socket.adapter(socket.dft()) do
log.warn("http_app_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
-- 检测到了IP_READY消息
log.info("http_app_task_func", "recv IP_READY", socket.dft())
-- 普通的http get请求功能演示
http_app_get()
-- http get下载压缩数据的功能演示
http_app_get_gzip()
-- http get下载数据保存到文件中的功能演示
http_app_get_file()
-- http post提交表单数据功能演示
http_app_post_form()
-- http post提交json数据功能演示
http_app_post_json()
-- http post提交纯文本数据功能演示
http_app_post_text()
-- http post提交xml数据功能演示
http_app_post_xml()
-- http post提交原始二进制数据功能演示
http_app_post_binary()
-- http post文件上传功能演示
http_app_post_file()
-- 60秒之后,循环测试
sys.wait(60000)
end
end
--创建并且启动一个task
--运行这个task的处理函数http_app_task_func
sys.taskInit(http_app_task_func)
@@ -0,0 +1,304 @@
--[[
@module httpplus
@summary httpplus应用功能模块
@version 1.0
@date 2025.08.06
@author 马梦阳
@usage
本文件为httpplus应用功能模块,核心业务逻辑为:基于不同的应用场景,演示httpplus扩展库的使用方式;
http核心库和httpplus扩展库的区别如下:
| 区别项 | http核心库 | httpplus扩展库 |
| --------------------------------- | ------------------------------------------------------------------------- | ---------------------------- |
| 文件上传 | 文件最大64KB | 只要内存够用,文件大小不限 |
| 文件下载 | 支持,只要文件系统空间够用,文件大小不限 | 不支持 |
| http header的key: value的限制 | 所有header的value总长度不能超过4KB,单个header的value长度不能超过1KB | 只要内存够用,header长度不限 |
| 鉴权URL自动识别 | 不支持 | 支持 |
| 接收到的body数据存储支持zbuff | 不支持 | 支持,可以直接传输给uart等库 |
| 接收到的body数据存储到内存中 | 最大支持32KB | 只要内存够用,大小不限 |
| chunk编码 | 支持 | 不支持 |
本文件没有对外接口,直接在main.lua中require "httpplus_app"就可以加载运行;
]]
local httpplus = require "httpplus"
-- 普通的http get请求功能演示
-- 请求的body数据保存到内存变量中,在内存够用的情况下,长度不限
-- timeout可以设置超时时间
local function httpplus_app_get()
local body
-- https get请求https://httpbin.luatos.com/get网页内容
-- 如果请求成功,请求的数据保存到response.body中
local code, response = httpplus.request({url="https://httpbin.luatos.com/get"})
log.info("httpplus_app_get1", code==200 and "success" or "error", code)
if code==200 then
log.info("httpplus_app_get1 headers", json.encode(response.headers or {}))
body = response.body:query()
log.info("httpplus_app_get1 body", body and (body:len()>512 and body:len() or body) or "nil")
end
-- http get请求http://httpbin.luatos.com/get网页内容,超时时间为3秒
-- 请求超时时间为3秒,用户自己写代码时,不要照抄3秒,根据自己业务逻辑的需要设置合适的超时时间
-- 如果请求成功,请求的数据保存到body中
code, response = httpplus.request({url="http://httpbin.luatos.com/get", timeout=3})
log.info("httpplus_app_get2", code==200 and "success" or "error", code)
if code==200 then
log.info("httpplus_app_get2 headers", json.encode(response.headers or {}))
body = response.body:query()
log.info("httpplus_app_get2 body", body and (body:len()>512 and body:len() or body) or "nil")
end
end
-- http get下载压缩数据的功能演示
local function httpplus_app_get_gzip()
local body
-- https get请求https://devapi.qweather.com/v7/weather/now?location=101010100&key=0e8c72015e2b4a1dbff1688ad54053de网页内容,超时时间为3秒
-- 如果请求成功,请求的数据保存到response.body中
local code, response = httpplus.request({url="https://devapi.qweather.com/v7/weather/now?location=101010100&key=0e8c72015e2b4a1dbff1688ad54053de"})
log.info("httpplus_app_get_gzip", code==200 and "success" or "error", code)
if code==200 then
log.info("httpplus_app_get_gzip headers", json.encode(response.headers or {}))
body = response.body:query()
log.info("httpplus_app_get_gzip body", body and (body:len()>512 and body:len() or body) or "nil")
end
-- 如果请求成功
if code == 200 then
-- 从body的第11个字节开始解压缩
local uncompress_data = miniz.uncompress(body:sub(11,-1), 0)
if not uncompress_data then
log.error("httpplus_app_get_gzip uncompress error")
return
end
local json_data = json.decode(uncompress_data)
if not json_data then
log.error("httpplus_app_get_gzip json.decode error")
return
end
log.info("httpplus_app_get_gzip json_data", json_data)
log.info("httpplus_app_get_gzip", "和风天气", json_data.code)
if json_data.now then
log.info("httpplus_app_get_gzip", "和风天气", "天气", json_data.now.text)
log.info("httpplus_app_get_gzip", "和风天气", "温度", json_data.now.temp)
end
end
end
-- http post提交表单数据功能演示
local function httpplus_app_post_form()
-- http post提交表单数据
-- http://httpbin.luatos.com/post为回环测试服务器,服务器收到post提交的表单数据后,还会下发同样的表单数据给设备
-- 如果请求成功,服务器应答的数据会保存到response.body中
local code, response = httpplus.request(
{
url = "http://httpbin.luatos.com/post",
forms = {username="LuatOS", password="123456"}
})
log.info("httpplus_app_post_form", code==200 and "success" or "error", code)
if code==200 then
log.info("httpplus_app_post_form headers", json.encode(response.headers or {}))
local body = response.body:query()
log.info("httpplus_app_post_form body", body and (body:len()>512 and body:len() or body) or "nil")
end
end
-- http post提交json数据功能演示
local function httpplus_app_post_json()
local params = {
username = "LuatOS",
password = "123456"
}
-- http post提交json数据
-- http://httpbin.luatos.com/post为回环测试服务器,服务器收到post提交的json数据后,还会下发同样的json数据给设备
-- ["Content-Type"] = "application/json" 表示post提交的body数据格式为json格式的数据
-- 如果请求成功,服务器应答的数据会保存到response.body中
local code, response = httpplus.request(
{
method = "POST",
url = "http://httpbin.luatos.com/post",
headers = {["Content-Type"] = "application/json"},
body = json.encode(params)
})
log.info("httpplus_app_post_json", code==200 and "success" or "error", code)
if code==200 then
log.info("httpplus_app_post_json headers", json.encode(response.headers or {}))
local body = response.body:query()
log.info("httpplus_app_post_json body", body and (body:len()>512 and body:len() or body) or "nil")
end
end
-- http post提交纯文本数据功能演示
local function httpplus_app_post_text()
-- http post提交纯文本数据
-- http://httpbin.luatos.com/post为回环测试服务器,服务器收到post提交的纯文本数据后,还会下发同样的纯文本数据给设备
-- ["Content-Type"] = "text/plain" 表示post提交的body数据格式为纯文本格式的数据
-- 如果请求成功,服务器应答的数据会保存到response.body中
local code, response = httpplus.request(
{
method = "POST",
url = "http://httpbin.luatos.com/post",
headers = {["Content-Type"] = "text/plain"},
body = "This is a raw text message from LuatOS device"
})
log.info("httpplus_app_post_text", code==200 and "success" or "error", code)
if code==200 then
log.info("httpplus_app_post_text headers", json.encode(response.headers or {}))
local body = response.body:query()
log.info("httpplus_app_post_text body", body and (body:len()>512 and body:len() or body) or "nil")
end
end
-- http post提交xml数据功能演示
local function httpplus_app_post_xml()
-- [=[ 和 ]=] 之间是一个多行字符串
local body = [=[
<?xml version="1.0" encoding="UTF-8"?>
<user>
<name>LuatOS</name>
<password>123456</password>
</user>
]=]
-- http post提交xml数据
-- http://httpbin.luatos.com/post为回环测试服务器,服务器收到post提交的xml数据后,还会下发同样的xml数据给设备
-- ["Content-Type"] = "text/xml" 表示post提交的body数据格式为xml格式的数据
-- 如果请求成功,服务器应答的数据会保存到response.body中
local code, response = httpplus.request(
{
method = "POST",
url = "http://httpbin.luatos.com/post",
headers = {["Content-Type"] = "text/xml"},
body = body
})
log.info("httpplus_app_post_xml", code==200 and "success" or "error", code)
if code==200 then
log.info("httpplus_app_post_xml headers", json.encode(response.headers or {}))
body = response.body:query()
log.info("httpplus_app_post_xml body", body and (body:len()>512 and body:len() or body) or "nil")
end
end
-- http post提交原始二进制数据功能演示
local function httpplus_app_post_binary()
local body = io.readFile("/luadb/logo.jpg")
-- http post提交原始二进制数据
-- http://uploadtest.luatos.com/api/upload/jpg为jpg图片上传测试服务器
-- 此处将logo.jpg的原始二进制数据做为body上传到服务器
-- 上传成功后,电脑上浏览器打开https://uploadtest.luatos.com/upload/jpg/,打开对应的测试日期目录,点击具体的测试时间照片,可以查看上传的照片
-- ["Content-Type"] = "application/octet-stream" 表示post提交的body数据格式为原始二进制格式的数据
-- 如果请求成功,服务器应答的数据会保存到response.body中
-- 因为Air32.com平台已经不开放使用了,所以该DEMO仅作上传照片至服务器的演示作用,使用时请将上传URL修改为您自己的服务器地址
-- 或者通过excloud扩展库上传到合宙IOT平台uploadtest.luatos.com
local code, response = httpplus.request(
{
method = "POST",
url = "http://uploadtest.luatos.com/api/upload/jpg",
headers = {["Content-Type"] = "application/octet-stream"},
body = body
})
log.info("httpplus_app_post_binary", code==200 and "success" or "error", code)
if code==200 then
log.info("httpplus_app_post_binary headers", json.encode(response.headers or {}))
body = response.body:query()
log.info("httpplus_app_post_binary body", body and (body:len()>512 and body:len() or body) or "nil")
end
end
-- http post文件上传功能演示
local function httpplus_app_post_file()
-- hhtplus.request接口支持单文件上传、多文件上传、单文本上传、多文本上传、单/多文本+单/多文件上传
-- https://airtest.luatos.com/iot/luat_test_file/add 只支持单文件上传或者单文件+单文本上传
-- 要求上传的文件name必须使用"f",上传的文本name必须使用"params"
-- 所以此处仅演示了单文件+单文本上传功能,并且"f"和"params"不能改成其他名字,否则会出现上传失败的应答
-- 测试接口的响应说明:
-- 成功:HTTP 200 OK{"code":0,"value":"上传成功"}
-- 失败:HTTP 状态码非 200 OK 或是 200 OK 但 code 不为 0
-- 如何在网页端查看上传的文件:
-- 在浏览器中打开https://iot.luatos.com/#/p8000/netlab_file_server,即可查看上传的文件;
-- 如果你自己的http服务支持更多类型的文本/文件混合上传,可以打开注释自行验证
local code, response = httpplus.request(
{
url = "https://airtest.luatos.com/iot/luat_test_file/add",
files =
{
["f"] = "/luadb/logo.jpg",
-- ["logo1.jpg"] = "/luadb/logo.jpg",
},
forms =
{
["params"] = json.encode({
username = "LuatOS",
password = "123456"
}),
-- ["username"] = "LuatOS",
-- ["password"] = "123456",
},
})
log.info("httpplus_app_post_file", code==200 and "success" or "error", code)
if code==200 then
log.info("httpplus_app_post_file headers", json.encode(response.headers or {}))
local body = response.body:query()
log.info("httpplus_app_post_file body", body and (body:len()>512 and body:len() or body) or "nil")
end
end
-- http app task 的任务处理函数
local function httpplus_app_task_func()
while true do
-- 如果当前时间点设置的默认网卡还没有连接成功,一直在这里循环等待
while not socket.adapter(socket.dft()) do
log.warn("httpplus_app_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
-- 检测到了IP_READY消息
log.info("httpplus_app_task_func", "recv IP_READY", socket.dft())
-- 普通的http get请求功能演示
httpplus_app_get()
-- http get下载压缩数据的功能演示
httpplus_app_get_gzip()
-- http post提交表单数据功能演示
httpplus_app_post_form()
-- -- http post提交json数据功能演示
httpplus_app_post_json()
-- http post提交纯文本数据功能演示
httpplus_app_post_text()
-- http post提交xml数据功能演示
httpplus_app_post_xml()
-- http post提交原始二进制数据功能演示
httpplus_app_post_binary()
-- http post文件上传功能演示
httpplus_app_post_file()
-- 60秒之后,循环测试
sys.wait(60000)
end
end
--创建并且启动一个task
--运行这个task的处理函数httpplus_app_task_func
sys.taskInit(httpplus_app_task_func)
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

@@ -0,0 +1,82 @@
--[[
@module main
@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
@version 1.0
@date 2025.07.28
@author 马梦阳
@usage
本demo演示的核心功能为:
1、分别使用http核心库和httpplus扩展库,演示以下几种应用场景的使用方式
(1) 普通的http get请求功能演示;
(2) http get下载压缩数据的功能演示;
(3) http get下载数据保存到文件中的功能演示;(仅http核心库支持,httpplus扩展库不支持)
(4) http post提交表单数据功能演示;
(5) http post提交json数据功能演示;
(6) http post提交纯文本数据功能演示;
(7) http post提交xml数据功能演示;
(8) http post提交原始二进制数据功能演示;
(9) http post文件上传功能演示;
2、netdrv_device:配置连接外网使用的网卡,目前支持以下三种选择(三选一)
(1) netdrv_4g4G网卡
(2) netdrv_eth_spi:通过SPI外挂CH390H芯片的以太网卡
(3) netdrv_multiple:支持以上两种网卡,可以配置两种网卡的优先级
更多说明参考本目录下的readme.md文件
]]
--[[
必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
PROJECT:项目名,ascii string类型
可以随便定义,只要不使用,就行
VERSION:项目版本号,ascii string类型
如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为999
如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
]]
PROJECT = "HTTP"
VERSION = "001.999.000"
-- 在日志中打印项目名和项目版本号
log.info("main", PROJECT, VERSION)
-- 如果内核固件支持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"
-- 加载http应用功能模块
require "http_app"
-- 加载httpplus应用功能模块
require "httpplus_app"
-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
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,45 @@
--[[
@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)
@@ -0,0 +1,29 @@
--[[
@module netdrv_device
@summary 网络驱动设备功能模块
@version 1.0
@date 2025.07.24
@author 马梦阳
@usage
本文件为网络驱动设备功能模块,核心业务逻辑为:根据项目需求,选择并且配置合适的网卡(网络适配器)
1、netdrv_4gsocket.LWIP_GP4G网卡;
2、netdrv_eth_spisocket.LWIP_ETH,通过SPI外挂CH390H芯片的以太网卡;
3、netdrv_multiple:可以配置多种网卡的优先级,按照优先级配置,使用其中一种网卡连接外网;
根据自己的项目需求,只需要require以上三种中的一种即可;
本文件没有对外接口,直接在main.lua中require "netdrv_device"就可以加载运行;
]]
-- 根据自己的项目需求,只需要require以下三种中的一种即可;
-- 加载“4G网卡”驱动模块
require "netdrv_4g"
-- 加载“通过SPI外挂CH390H芯片的以太网卡”驱动模块
-- require "netdrv_eth_spi"
-- 加载“可以配置优先级的多种网卡”驱动模块
-- require "netdrv_multiple"
@@ -0,0 +1,130 @@
## 功能模块介绍
1、main.lua:主程序入口;
2、netdrv_device.lua:网卡驱动设备,可以配置使用netdrv文件夹内的三种网卡(单4g网卡,单spi以太网卡,多网卡)中的任何一种网卡;
3、http_app.lua:基于不同的应用场景,演示http核心库的使用方式;
4、httpplus_app.lua:基于不同的应用场景,演示httpplus扩展库的使用方式;
## 演示功能概述
1、http_app:使用http核心库,演示以下几种应用场景的使用方式
- 普通的http get请求功能演示;
- http get下载压缩数据的功能演示;
- http get下载数据保存到文件中的功能演示;
- http post提交表单数据功能演示;
- http post提交json数据功能演示;
- http post提交纯文本数据功能演示;
- http post提交xml数据功能演示;
- http post提交原始二进制数据功能演示;
- http post文件上传功能演示;
2、httpplus_app:使用httpplus扩展库,演示以下几种应用场景的使用方式
- 普通的http get请求功能演示;
- http get下载压缩数据的功能演示;
- http post提交表单数据功能演示;
- http post提交json数据功能演示;
- http post提交纯文本数据功能演示;
- http post提交xml数据功能演示;
- http post提交原始二进制数据功能演示;
- http post文件上传功能演示;
3、netdrv_device:配置连接外网使用的网卡,目前支持以下三种选择(三选一)
(1) netdrv_4g4G网卡
(2) netdrv_eth_spi:通过SPI外挂CH390H芯片的以太网卡
(3) netdrv_multiple:支持以上两种网卡,可以配置两种网卡的优先级
## 演示硬件环境
![](https://docs.openluat.com/air780epm/luatos/app/driver/eth/image/RFSvb75NRoEWqYxfCRVcVrOKnsf.jpg)
1、Air780EPM V1.3版本开发板一块+可上网的sim卡一张+4g天线一根+网线一根:
- sim卡插入开发板的sim卡槽
- 天线装到开发板上
- 网线一端插入开发板网口,另外一端连接可以上外网的路由器网口
2、TYPE-C USB数据线一根 + USB转串口数据线一根,Air780EPM V1.3版本开发板和数据线的硬件接线方式为:
- Air780EPM V1.3版本开发板通过TYPE-C USB口供电;(外部供电/USB供电 拨动开关 拨到 USB供电一端)
- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
- USB转串口数据线,一般来说,白线连接开发板的UART1_TX,绿线连接开发板的UART1_RX,黑线连接核心板的GND,另外一端连接电脑USB口;
## 演示软件环境
1、Luatools下载调试工具
2、[Air780EPM V2012版本固件](https://docs.openluat.com/air780epm/luatos/firmware/version/)(理论上,2025年8月10日之后发布的固件都可以)
## 演示核心步骤
1、搭建好硬件环境
2、demo脚本代码netdrv_device.lua中,按照自己的网卡需求启用对应的Lua文件
- 如果需要单4G网卡,打开require "netdrv_4g",其余注释掉
- 如果需要以太网卡,打开require "netdrv_eth_spi",其余注释掉
- 如果需要多网卡,打开require "netdrv_multiple",其余注释掉
3、Luatools烧录内核固件和修改后的demo脚本代码
4、烧录成功后,自动开机运行,在日志中搜索success 200,每隔1分钟测试一轮,如果每轮出现22次success 200,如以下日志所示,就表示成功;详细日志所表示的含义,可以结合代码自行分析
``` lua
[2025-08-06 15:34:56.201][000000007.113] I/user.http_app_get1 success 200 {"Transfer-
[2025-08-06 15:34:56.354][000000007.271] I/user.httpplus_app_get1 success 200
[2025-08-06 15:34:56.622][000000007.537] I/user.httpplus_app_get2 success 200
[2025-08-06 15:34:57.896][000000008.796] I/user.httpplus_app_get_gzip success 200
[2025-08-06 15:34:58.287][000000009.070] I/user.http_app_get2 success 200 {"Vary":"Ac
[2025-08-06 15:34:58.369][000000009.112] I/user.httpplus_app_post_form success 200
[2025-08-06 15:34:58.592][000000009.248] I/user.http_app_get3 success 200 {"Connectio
[2025-08-06 15:34:58.765][000000009.412] I/user.httpplus_app_post_json success 200
[2025-08-06 15:34:59.043][000000009.951] I/user.http_app_get_gzip success 200 {"Conte
[2025-08-06 15:34:59.291][000000010.065] I/user.httpplus_app_post_text success 200
[2025-08-06 15:34:59.820][000000010.736] I/user.httpplus_app_post_xml success 200
[2025-08-06 15:34:59.903][000000010.744] I/user.http_app_get_file1 success 200 {"Tran
[2025-08-06 15:35:00.706][000000011.503] I/user.httpplus_app_post_binary success 200
[2025-08-06 15:35:01.008][000000011.862] I/user.http_app_get_file2 success 200 {"Vary
[2025-08-06 15:35:01.094][000000011.917] I/user.httpplus_app_post_file success 200
[2025-08-06 15:35:01.215][000000012.079] I/user.http_app_get_file3 success 200 {"Conn
[2025-08-06 15:35:01.356][000000012.270] I/user.http_app_post_form success 200 {"Conn
[2025-08-06 15:35:01.569][000000012.479] I/user.http_app_post_json success 200 {"Conn
[2025-08-06 15:35:01.769][000000012.681] I/user.http_app_post_text success 200 {"Conn
[2025-08-06 15:35:01.949][000000012.858] I/user.http_app_post_xml success 200 {"Conne
[2025-08-06 15:35:02.236][000000013.145] I/user.http_app_post_binary success 200 {"Da
[2025-08-06 15:35:02.437][000000013.348] I/user.post_multipart_form_data success 200
```