跳转至

07 AirMICROSD_1010

作者:王棚嶙 | 最后修改:2026-04-29

一、AirMICROSD-1010 概述

AirMICROSD_1010 是合宙推出的一款通过杜邦线连接的SD卡配件板,支持spi接口,适用于Air780系列、Air8000系列和Air8101核心板。

二、演示功能概述

本demo演示了在嵌入式环境中对TF卡(SD卡)的完整操作流程,覆盖了从文件系统挂载到高级文件操作的完整功能链。项目分为四个核心模块:

1、main.lua:主程序入口

2、tfcard_app.lua:TF卡基础应用模块,实现文件系统管理、文件操作和目录管理功能

3、http_download_file.lua:HTTP下载模块,实现网络检测与文件下载到TF卡的功能

4、http_upload_file.lua:HTTP上传模块,实现网络检测与文件上传到服务器的功能

三、准备硬件环境

AirMICROSD_1010 :

Air780EGH核心板管脚图,如下图所示:

AirMICROSD_1010配件板+Air780EGH核心板,硬件连接示意图,如下图所示:

1、Air780EGH核心板一块(Air780EHM/780EGH/780EHV三种模块的核心板接线方式相同)

2、TYPE-C USB数据线一根

3、AirMICROSD_1010模块一个和SD卡一张

4、SIM卡一张

5、Air780EHM/780EGH/780EHV核心板和数据线的硬件接线方式为

  • Air780EGH核心板通过TYPE-C USB口供电;(核心板USB旁边的开关拨到on一端)
  • TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;

6、Air780EGH核心板和AirMICROSD_1010模块接线方式

Air780EGH AirMICROSD_1010
GND(任意) GND
VDD_EXT 3V3
GPIO8/SPI0_CS spi_cs
SPI0_CLK spi_clk,时钟
SPI0_MOSI spi_mosi,主机输出,从机输入
SPI0_MISO spi_miso,主机输入,从机输出

四、准备软件环境

1、Luatools下载调试工具:下载调试工具

2、内核固件固件版本(底层 core 固件文件):

本demo开发测试时使用的固件为Air780EGH V2032 版本固件,本demo对固件版本没有什么特殊要求,所以你如果要测试本demo时,可以直接使用最新版的内核固件;如果发现最新版本的内核固件测试有问题,可以使用我们开发本demo时使用的内核固件版本来对比测试;

3、LuatOS 需要的脚本和资源文:脚本资源下载

4、lib 脚本文件:使用 Luatools 烧录时,勾选 添加默认 lib 选项,使用默认 lib 脚本文件

准备好软件环境之后,接下来查看如何使用工具烧录,将本篇文章中演示使用的项目文件烧录到 Air780EGH核心板中

五、演示功能概述

5.1 主程序入口模块(main.lua)

  • 初始化项目信息和版本号
  • 初始化看门狗,并定时喂狗
  • 启动一个循环定时器,每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况方便分析内存使用是否有异常
  • 加载tfcard_app模块(通过require "tfcard_app")
  • 加载http_download_file模块(通过require "http_download_file")
  • 加载http_upload_file模块(通过require "http_upload_file")
  • 最后运行sys.run()。
--添加硬狗防止程序卡死
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)

--[[在加载以下三个功能时,建议分别打开进行测试,因为文件操作,http下载功能和http大文件上传功能是异步操作。
放到一个项目中,如果加载的时间点是随机的,就会出现tfcard_app在spi.setup和fatfs挂载文件系统之后,
还没有释放资源,然后http_download_file或http_upload_file又去重复spi.setup和fatfs挂载文件系统了,
不符合正常的业务逻辑,用户在参考编程的时候也要注意。]]

--加载tf卡测试应用模块
require "tfcard_app"
--加载HTTP下载存入TF卡功能演示模块
-- require "http_download_file"
--加载HTTP上传文件到服务器的功能演示模块
-- require "http_upload_file"

-- 用户代码已结束---------------------------------------------
-- 结尾总是这一句
sys.run()

5.2 TF卡核心演示模块(tfcard_app.lua)

5.2.1 文件系统管理

  • SPI初始化与挂载:
  • 配置SPI接口参数(频率400kHz)
  • 挂载FAT32文件系统到/sd路径
  • 自动格式化检测与处理
  • 空间信息获取:
  • 实时查询TF卡可用空间
  • 输出详细存储信息(总空间/剩余空间)
-- ##########  SPI初始化 ##########
-- 在Air780EHM/EHV/EGH核心板上TF卡的的pin_cs为gpio8,spi_id为0.请根据实际硬件修改
spi_id, pin_cs = 0, 8
spi.setup(spi_id, nil, 0, 0, 8, 2000000)
--初始化后拉高pin_cs,准备开始挂载TF卡
gpio.setup(pin_cs, 1)

-- ########## 开始进行tf卡挂载 ##########
-- 挂载失败默认格式化,
-- 如无需格式化应改为fatfs.mount(fatfs.SPI, "/sd", spi_id, pin_cs, 24 * 1000 * 1000, nil, 1, false),
-- 一般是在测试硬件是否有问题的时候把格式化取消掉
mount_ok, mount_err = fatfs.mount(fatfs.SPI, "/sd", spi_id, pin_cs, 24 * 1000 * 1000)
if mount_ok then
    log.info("fatfs.mount", "挂载成功", mount_err)
else
    log.error("fatfs.mount", "挂载失败", mount_err)
    goto resource_cleanup
end

-- ########## 获取SD卡的可用空间信息并打印。 ########## 
data, err = fatfs.getfree("/sd")
if data then
    --打印SD卡的可用空间信息
    log.info("fatfs", "getfree", json.encode(data))
else
    --打印错误信息
    log.info("fatfs", "getfree", "err", err)
    goto resource_cleanup
end

-- 列出所有挂载点,如不需要,可注释掉。
data = io.lsmount()
log.info("fs", "lsmount", json.encode(data))

-- ########## 功能: 启用fatfs调试模式 ##########
-- fatfs.debug(1) -- 若挂载失败,可以尝试打开调试信息,查找原因.(设置调试模式)

5.2.2 文件操作

  • 创建目录:io.mkdir("/sd/io_test")
  • 创建/写入文件: io.open("/sd/io_test/boottime", "wb")
  • 检查文件存在: io.exists(file_path)
  • 获取文件大小:io.fileSize(file_path)
  • 读取文件内容: io.open(file_path, "rb"):read("*a")
  • 启动计数文件: 记录设备启动次数
  • 文件追加: io.open(append_file, "a+")
  • 按行读取: file:read("*l")
  • 文件关闭: file:close()
  • 文件重命名: os.rename(old_path, new_path)
  • 列举目录: io.lsdir(dir_path)
  • 删除文件: os.remove(file_path)
  • 删除目录: io.rmdir(dir_path)
-- 执行tfcard文件操作演示
log.info("文件操作", "===== 开始文件操作 =====")

dir_path = "/sd/io_test"

-- 1. 创建目录
if io.mkdir(dir_path) then
    log.info("io.mkdir", "目录创建成功", "路径:" .. dir_path)
else
    -- 检查是否目录已存在
    if io.exists(dir_path) then
        log.warn("io.mkdir", "目录已存在,跳过创建", "路径:" .. dir_path)
    else
        log.error("io.mkdir", "目录创建失败且目录不存在", "路径:" .. dir_path)
        goto resource_cleanup
    end
end

-- 2. 创建并写入文件
file_path = dir_path .. "/boottime"
file = io.open(file_path, "wb")
if file then
    file:write("这是io库API文档示例的测试内容")
    file:close()
    --在LuatOS文件操作中,执行file:close()是必须且关键的操作,它用于关闭文件句柄,释放资源,并确保数据被正确写入磁盘。
    -- 如果不执行file:close(),可能会导致数据丢失、文件损坏或其他不可预测的问题。
    log.info("文件创建", "文件写入成功", "路径:" .. file_path)
else
    log.error("文件创建", "文件创建失败", "路径:" .. file_path)
    goto resource_cleanup
end

-- 3. 检查文件是否存在
if io.exists(file_path) then
    log.info("io.exists", "文件存在", "路径:" .. file_path)
else
    log.error("io.exists", "文件不存在", "路径:" .. file_path)
    goto resource_cleanup
end

-- 4. 获取文件大小
file_size = io.fileSize(file_path)
if file_size then
    log.info("io.fileSize", "文件大小:" .. file_size .. "字节", "路径:" .. file_path)
else
    log.error("io.fileSize", "获取文件大小失败", "路径:" .. file_path)
    goto resource_cleanup
end

-- 5. 读取文件内容
file = io.open(file_path, "rb")
if file then
    content = file:read("*a")
    log.info("文件读取", "路径:" .. file_path, "内容:" .. content)
    file:close()
else
    log.error("文件操作", "无法打开文件读取内容", "路径:" .. file_path)
    goto resource_cleanup
end

-- 6. 启动计数文件操作
count = 0
--以只读模式打开文件
file = io.open(file_path, "rb")
if file then
    data = file:read("*a")
    log.info("启动计数", "文件内容:", data, "十六进制:", data:toHex())
    count = tonumber(data) or 0
    file:close()
else
    log.warn("启动计数", "文件不存在或无法打开")

end

log.info("启动计数", "当前值:", count)
count=count + 1
log.info("启动计数", "更新值:", count)

file = io.open(file_path, "wb")
if file then
    file:write(tostring(count))
    file:close()
    log.info("文件写入", "路径:" .. file_path, "内容:", count)
else
    log.error("文件写入", "无法打开文件", "路径:" .. file_path)
    goto resource_cleanup
end

-- 7. 文件追加测试
append_file = dir_path .. "/test_a"
-- 清理旧文件
os.remove(append_file)

-- 创建并写入初始内容
file = io.open(append_file, "wb")
if file then
    file:write("ABC")
    file:close()
    log.info("文件创建", "路径:" .. append_file, "初始内容:ABC")
else
    log.error("文件创建", "无法创建文件", "路径:" .. append_file)
    goto resource_cleanup
end

-- 追加内容
file = io.open(append_file, "a+")
if file then
    file:write("def")
    file:close()
    log.info("文件追加", "路径:" .. append_file, "追加内容:def")
else
    log.error("文件追加", "无法打开文件进行追加", "路径:" .. append_file)
    goto resource_cleanup

end

-- 验证追加结果
file = io.open(append_file, "r")
if file then
    data = file:read("*a")
    log.info("文件验证", "路径:" .. append_file, "内容:" .. data, "结果:",
        data == "ABCdef" and "成功" or "失败")
    file:close()
else
    log.error("文件验证", "无法打开文件进行验证", "路径:" .. append_file)
    goto resource_cleanup
end

-- 8. 按行读取测试
line_file = dir_path .. "/testline"
file = io.open(line_file, "w")
if file then
    file:write("abc\n")
    file:write("123\n")
    file:write("wendal\n")
    file:close()
    log.info("文件创建", "路径:" .. line_file, "写入3行文本")
else
    log.error("文件创建", "无法创建文件", "路径:" .. line_file)
    goto resource_cleanup
end

-- 按行读取文件
file = io.open(line_file, "r")
if file then
    log.info("按行读取", "路径:" .. line_file, "第1行:", file:read("*l"))
    log.info("按行读取", "路径:" .. line_file, "第2行:", file:read("*l"))
    log.info("按行读取", "路径:" .. line_file, "第3行:", file:read("*l"))
    file:close()
else
    log.error("按行读取", "无法打开文件", "路径:" .. line_file)
    goto resource_cleanup
end

-- 9. 文件重命名
old_path = append_file
new_path = dir_path .. "/renamed_file.txt"
success, err = os.rename(old_path, new_path)
if success then
    log.info("os.rename", "文件重命名成功", "原路径:" .. old_path, "新路径:" .. new_path)

    -- 验证重命名结果
    if io.exists(new_path) and not io.exists(old_path) then
        log.info("验证结果", "重命名验证成功", "新文件存在", "原文件不存在")
    else
        log.error("验证结果", "重命名验证失败")
    end
else
    log.error("os.rename", "重命名失败", "错误:" .. tostring(err), "原路径:" .. old_path)
    goto resource_cleanup
end

-- 10. 列举目录内容
log.info("目录操作", "===== 开始目录列举 =====")

ret, data = io.lsdir(dir_path, 50, 0) -- 50表示最多返回50个文件,0表示从目录开头开始
if ret then
    log.info("fs", "lsdir", json.encode(data))
else
    log.info("fs", "lsdir", "fail", ret, data)
    goto resource_cleanup
end

-- 11. 删除文件测试
-- 测试删除renamed_file.txt文件
if os.remove(new_path) then
    log.info("os.remove", "文件删除成功", "路径:" .. new_path)

    -- 验证renamed_file.txt删除结果
    if not io.exists(new_path) then
        log.info("验证结果", "renamed_file.txt文件删除验证成功")
    else
        log.error("验证结果", "renamed_file.txt文件删除验证失败")
    end
else
    log.error("io.remove", "renamed_file.txt文件删除失败", "路径:" .. new_path)
    goto resource_cleanup
end

-- 测试删除testline文件
if os.remove(line_file) then
    log.info("os.remove", "testline文件删除成功", "路径:" .. line_file)

    -- 验证删除结果
    if not io.exists(line_file) then
        log.info("验证结果", "testline文件删除验证成功")
    else
        log.error("验证结果", "testline文件删除验证失败")
    end
else
    log.error("io.remove", "testline文件删除失败", "路径:" .. line_file)
    goto resource_cleanup
end

if os.remove(file_path) then
    log.info("os.remove", "文件删除成功", "路径:" .. file_path)

    -- 验证删除结果
    if not io.exists(file_path) then
        log.info("验证结果", "boottime文件删除验证成功")
    else
        log.error("验证结果", "boottime文件删除验证失败")
    end
else
    log.error("io.remove", "boottime文件删除失败", "路径:" .. file_path)
    goto resource_cleanup
end

-- 12. 删除目录(不能删除非空目录,所以在删除目录前要确保目录内没有文件或子目录)
if io.rmdir(dir_path) then
    log.info("io.rmdir", "目录删除成功", "路径:" .. dir_path)

    -- 验证删除结果
    if not io.exists(dir_path) then
        log.info("验证结果", "目录删除验证成功")
    else
        log.error("验证结果", "目录删除验证失败")
    end
else
    log.error("io.rmdir", "目录删除失败", "路径:" .. dir_path)
    goto resource_cleanup
end

log.info("文件操作", "===== 文件操作完成 =====")

5.2.3 结果处理

  • 资源清理(卸载/SPI关闭)
log.info("结束", "开始执行关闭操作...")  
-- 如已挂载需先卸载文件系统,未挂载直接关闭SPI
if mount_ok then
    if fatfs.unmount("/sd") then
        log.info("文件系统", "卸载成功")
    else
        log.error("文件系统", "卸载失败")
    end
end

-- 2. 关闭SPI接口
spi.close(spi_id)
log.info("SPI接口", "已关闭")

5.3 HTTP下载功能 (http_download_file.lua)

5.3.1 文件系统管理

  • SPI初始化与挂载

5.3.2 网络就绪检测

  • 1秒循环等待IP就绪
  • 网络故障处理机制

5.3.3 安全下载

  • HTTP下载

5.3.4 结果处理

  • 下载状态码解析
  • 自动文件大小验证
  • 资源清理(卸载/spi关闭)
local function http_download_file_task()

    -- 阶段1: 网络就绪检测

    while not socket.adapter(socket.dft()) do
        log.warn("HTTP下载", "等待网络连接", socket.dft())
        -- 等待IP_READY消息,超时设为1秒
        sys.waitUntil("IP_READY", 1000)
    end

    -- 检测到了IP_READY消息
    log.info("HTTP下载", "网络已就绪", socket.dft())

    -- 在Air780EHM/EHV/EGH核心板上TF卡的的pin_cs为gpio8,spi_id为0.请根据实际硬件修改
    spi_id, pin_cs = 0, 8
    spi.setup(spi_id, nil, 0, 0, 8, 2000000)
    -- 初始化后拉高pin_cs,准备开始挂载TF卡
    gpio.setup(pin_cs, 1)

    -- 挂载文件系统
    local mount_ok = fatfs.mount(fatfs.SPI, "/sd", spi_id, pin_cs, 24 * 1000 * 1000)
    if not mount_ok then
        log.error("HTTP下载", "文件系统挂载失败")
        fatfs.unmount("/sd")
        spi.close(spi_id)
        return
    end

    -- 阶段2: 执行下载任务
    log.info("HTTP下载", "开始下载任务")

    -- 核心下载操作开始 (支持http和https)
    --local code, headers, body = http.request("GET", "...", nil, nil, {dst = "/sd/3_23MB.bin"}).wait()
    -- 其中 "..."为url地址, 支持 http和https, 支持域名, 支持自定义端口。
    local code, headers, body_size = http.request("GET",
                                   "https://cdn.openluat-erp.openluat.com/erp_site_file/product_file/AirM2M_780EHT_V2017_LTE_AT.dfota.bin",
                                    nil, nil, {dst = "/sd/3_23MB.bin"}).wait()
    -- 阶段3: 记录下载结果
    log.info("HTTP下载", "下载完成", 
        code==200 and "success" or "error", 
        code, 
        -- headers是下载的文件头信息
        json.encode(headers or {}), 
        -- body_size是下载的文件大小(字节数)
        body_size) 

    if code == 200 then
        -- 获取实际文件大小
        local actual_size = io.fileSize("/sd/3_23MB.bin")
        log.info("HTTP下载", "文件大小验证", "预期:", body_size, "实际:", actual_size)

        if actual_size~= body_size then
            log.error("HTTP下载", "文件大小不一致", "预期:", body_size, "实际:", actual_size)
        end
    end

    -- 阶段4: 资源清理
    fatfs.unmount("/sd")
    spi.close(spi_id)
    log.info("HTTP下载", "资源清理完成")
end

-- 创建下载任务
sys.taskInit(http_download_file_task)

5.4 HTTP上传功能 (http_upload_file.lua)

5.4.1 文件系统管理

  • SPI初始化与挂载

5.4.2 网络就绪检测

  • 1秒循环等待IP就绪
  • 网络故障处理机制

5.4.3 安全上传

  • HTTP上传

5.4.4 结果处理

  • 下载状态码解析
  • 自动文件大小验证
  • 资源清理(卸载/spi关闭)
-- 加载httpplus扩展库,不可省略
local httpplus = require "httpplus"

local function http_upload_task()
    -- 阶段1: 网络就绪检测
    while not socket.adapter(socket.dft()) do
        log.warn("HTTP上传", "等待网络连接", socket.dft())
        -- 待IP_READY消息,超时设为1秒
        sys.waitUntil("IP_READY", 1000)
    end

    -- 检测到了IP_READY消息
    log.info("HTTP上传", "网络已就绪", socket.dft())

    -- 在Air780EHM/EHV/EGH核心板上TF卡的的pin_cs为gpio8,spi_id为0.请根据实际硬件修改
    spi_id, pin_cs = 0, 8
    spi.setup(spi_id, nil, 0, 0, 8, 2000000)
    -- 初始化后拉高pin_cs,准备开始挂载TF卡
    gpio.setup(pin_cs, 1)


    -- 挂载文件系统
    local mount_ok = fatfs.mount(fatfs.SPI, "/sd", spi_id, pin_cs, 24 * 1000 * 1000)
    if not mount_ok then
        log.error("HTTP上传", "文件系统挂载失败")
        fatfs.unmount("/sd")
        spi.close(spi_id)
        return
    end

    -- 阶段3: 检查要上传的文件是否存在
    -- 替换为实际的文件路径
    local upload_file_path ="/sd/3_23MB.bin" 
    if not io.exists(upload_file_path) then
        log.error("HTTP上传", "要上传的文件不存在", upload_file_path)
        fatfs.unmount("/sd")
        spi.close(spi_id)
        return
    end

    -- 获取文件大小
    local file_size = io.fileSize(upload_file_path)
    log.info("HTTP上传", "准备上传文件", upload_file_path, "大小:", file_size, "字节")

    -- 阶段4: 执行文件上传
    log.info("HTTP上传", "开始上传任务")

    -- 使用httpplus库上传文件,参考httpplus_app_post_file的实现
    -- hhtplus.request接口支持单文件上传、多文件上传、单文本上传、多文本上传、单/多文本+单/多文件上传
    -- http://airtest.openluat.com:2900/uploadFileToStatic 仅支持单文件上传,并且上传的文件name必须使用"uploadFile"
    -- 所以此处仅演示了单文件上传功能,并且"uploadFile"不能改成其他名字,否则会出现上传失败的应答
    local code, response = httpplus.request({
        url = "http://airtest.openluat.com:2900/uploadFileToStatic",
        files = {
            -- 服务器要求文件名必须为"uploadFile"
            ["uploadFile"] = upload_file_path, 
        },
    })

    -- 阶段5: 记录上传结果
    log.info("HTTP上传", "上传完成", 
        code == 200 and "success" or "error", 
        code)

    if code == 200 then
        log.info("HTTP上传", "服务器响应头", json.encode(response.headers or {}))
        local body = response.body and response.body:query()
        log.info("HTTP上传", "服务器响应体长度", body and body:len() or 0)

        -- 可以进一步解析服务器响应
        if body then
            log.info("HTTP上传", "服务器响应内容", body:len() > 512 and "内容过长,不显示" or body)
        end
    else
        log.error("HTTP上传", "上传失败", code)
    end

    -- 阶段6: 资源清理
    fatfs.unmount("/sd")
    spi.close(spi_id)
    log.info("HTTP上传", "资源清理完成")
end

-- 创建上传任务
sys.taskInit(http_upload_task)

六、运行结果展示

结合三、四部分的软硬件环境准备说明,将代码烧录进Air780EGH核心板中,可观察到以下日志:

1TF卡初始化与挂载
[2025-11-14 11:49:12.769][000000000.278] SPI_HWInit 552:spi0 speed 2000000,1994805,154
[2025-11-14 11:49:12.788][000000000.279] D/fatfs init sdcard at spi=0 cs=8
[2025-11-14 11:49:12.811][000000000.407] D/SPI_TF 卡容量 125173760KB
[2025-11-14 11:49:12.822][000000000.408] D/SPI_TF sdcard init OK OCR:0xc0ff8000!
[2025-11-14 11:49:12.835][000000000.412] I/user.fatfs.mount 挂载成功 0
[2025-11-14 11:49:12.844][000000000.665] I/user.fatfs getfree {"free_sectors":250335488,"total_kb":125168512,"free_kb":125167744,"total_sectors":250337024}
[2025-11-14 11:49:12.873][000000000.666] I/user.fs lsmount [{"fs":"ec7xx","path":""},{"fs":"inline","path":"\/lua\/"},{"fs":"ram","path":"\/ram\/"},{"fs":"luadb","path":"\/luadb\/"},{"fs":"fatfs","path":"\/sd"}]

2)文件操作演示
[2025-11-14 11:49:12.880][000000000.666] I/user.文件操作 ===== 开始文件操作 =====
[2025-11-14 11:49:13.111][000000001.254] I/user.io.mkdir 目录创建成功 路径:/sd/io_test
[2025-11-14 11:49:13.116][000000001.267] I/user.文件创建 文件写入成功 路径:/sd/io_test/boottime
[2025-11-14 11:49:13.123][000000001.270] I/user.io.exists 文件存在 路径:/sd/io_test/boottime
[2025-11-14 11:49:13.127][000000001.273] I/user.io.fileSize 文件大小:41字节 路径:/sd/io_test/boottime
[2025-11-14 11:49:13.131][000000001.276] I/user.文件读取 路径:/sd/io_test/boottime 内容:这是io库API文档示例的测试内容
[2025-11-14 11:49:13.148][000000001.280] I/user.启动计数 文件内容: 这是io库API文档示例的测试内容 十六进制: E8BF99E698AF696FE5BA93415049E69687E6A1A3E7A4BAE4BE8BE79A84E6B58BE8AF95E58685E5AEB9 82
[2025-11-14 11:49:13.157][000000001.280] I/user.启动计数 当前值: 0
[2025-11-14 11:49:13.165][000000001.281] I/user.启动计数 更新值: 1
[2025-11-14 11:49:13.178][000000001.292] I/user.文件写入 路径:/sd/io_test/boottime 内容: 1
[2025-11-14 11:49:13.188][000000001.308] I/user.文件创建 路径:/sd/io_test/test_a 初始内容:ABC
[2025-11-14 11:49:13.194][000000001.314] I/user.文件追加 路径:/sd/io_test/test_a 追加内容:def
[2025-11-14 11:49:13.203][000000001.318] I/user.文件验证 路径:/sd/io_test/test_a 内容:ABCdef 结果: 成功
[2025-11-14 11:49:13.208][000000001.334] I/user.文件创建 路径:/sd/io_test/testline 写入3行文本
[2025-11-14 11:49:13.215][000000001.338] I/user.按行读取 路径:/sd/io_test/testline 1: abc
[2025-11-14 11:49:13.219][000000001.338] I/user.按行读取 路径:/sd/io_test/testline 2: 123
[2025-11-14 11:49:13.223][000000001.339] I/user.按行读取 路径:/sd/io_test/testline 3: wendal
[2025-11-14 11:49:13.232][000000001.344] I/user.os.rename 文件重命名成功 原路径:/sd/io_test/test_a 新路径:/sd/io_test/renamed_file.txt
[2025-11-14 11:49:13.236][000000001.348] D/fatfs f_open /io_test/test_a 4
[2025-11-14 11:49:13.242][000000001.349] D/vfs fopen /sd/io_test/test_a r not found
[2025-11-14 11:49:13.247][000000001.349] I/user.验证结果 重命名验证成功 新文件存在 原文件不存在
[2025-11-14 11:49:13.252][000000001.349] I/user.目录操作 ===== 开始目录列举 =====
[2025-11-14 11:49:13.259][000000001.357] I/user.fs lsdir [{"name":"boottime","size":1,"type":0},{"name":"testline","size":15,"type":0},{"name":"renamed_file.txt","size":6,"type":0}]
[2025-11-14 11:49:13.264][000000001.364] I/user.os.remove 文件删除成功 路径:/sd/io_test/renamed_file.txt
[2025-11-14 11:49:13.276][000000001.366] D/fatfs f_open /io_test/renamed_file.txt 4
[2025-11-14 11:49:13.281][000000001.366] D/vfs fopen /sd/io_test/renamed_file.txt r not found
[2025-11-14 11:49:13.287][000000001.367] I/user.验证结果 renamed_file.txt文件删除验证成功
[2025-11-14 11:49:13.293][000000001.373] I/user.os.remove testline文件删除成功 路径:/sd/io_test/testline
[2025-11-14 11:49:13.297][000000001.375] D/fatfs f_open /io_test/testline 4
[2025-11-14 11:49:13.305][000000001.376] D/vfs fopen /sd/io_test/testline r not found
[2025-11-14 11:49:13.310][000000001.376] I/user.验证结果 testline文件删除验证成功
[2025-11-14 11:49:13.315][000000001.383] I/user.os.remove 文件删除成功 路径:/sd/io_test/boottime
[2025-11-14 11:49:13.323][000000001.385] D/fatfs f_open /io_test/boottime 4
[2025-11-14 11:49:13.328][000000001.386] D/vfs fopen /sd/io_test/boottime r not found
[2025-11-14 11:49:13.332][000000001.386] I/user.验证结果 boottime文件删除验证成功
[2025-11-14 11:49:13.339][000000001.393] I/user.io.rmdir 目录删除成功 路径:/sd/io_test
[2025-11-14 11:49:13.343][000000001.395] D/fatfs f_open /io_test 4
[2025-11-14 11:49:13.348][000000001.395] D/vfs fopen /sd/io_test r not found
[2025-11-14 11:49:13.352][000000001.395] I/user.验证结果 目录删除验证成功
[2025-11-14 11:49:13.359][000000001.395] I/user.文件操作 ===== 文件操作完成 =====
[2025-11-14 11:49:13.369][000000001.396] I/user.结束 开始执行关闭操作...
[2025-11-14 11:49:13.373][000000001.396] I/user.文件系统 卸载成功
[2025-11-14 11:49:13.379][000000001.396] I/user.SPI接口 已关闭

3)网络连接与HTTP下载
[2025-11-14 11:52:24.522][000000002.310] I/user.HTTP下载 开始下载任务
[2025-11-14 11:52:24.539][000000002.314] dns_run 676:cdn.openluat-erp.openluat.com state 0 id 1 ipv6 0 use dns server2, try 0
[2025-11-14 11:52:24.550][000000002.316] D/mobile TIME_SYNC 0
[2025-11-14 11:52:24.567][000000002.390] dns_run 693:dns all done ,now stop
[2025-11-14 11:53:02.635][000000040.462] I/user.HTTP下载 下载完成 success 200 
[2025-11-14 11:53:02.650][000000040.462] {"x-oss-hash-crc64ecma":"7570337686322137116","x-oss-server-time":"104","x-oss-object-type":"Normal","Content-Length":"3389723","Via":"cache19.l2cn3022[331,331,200-0,M], cache35.l2cn3022[333,0], kunlun19.cn5230[416,415,200-0,M], kunlun19.cn5230[421,0]","x-oss-cdn-auth":"success","Date":"Fri, 14 Nov 2025 03:52:24 GMT","x-oss-request-id":"6916A7782B654B37362C05B4","Content-MD5":"Ap894+Aw36xpOHjjgfW0Cw==","Last-Modified":"Wed, 03 Sep 2025 07:26:20 GMT","Connection":"keep-alive","Server":"Tengine","ETag":"\"029F3DE3E030DFAC693878E381F5B40B\"","Timing-Allow-Origin":"*","X-Swift-CacheTime":"3600","Accept-Ranges":"bytes","x-oss-storage-class":"Standard","Content-Type":"application\/octet-stream","X-Swift-SaveTime":"Fri, 14 Nov 2025 03:52:24 GMT","X-Cache":"MISS TCP_MISS dirn:-2:-2","Ali-Swift-Global-Savetime":"1763092344","EagleId":"6f30473017630923443325751e"}
[2025-11-14 11:53:02.667][000000040.462]  3389723
[2025-11-14 11:53:02.679][000000040.464] I/user.HTTP下载 文件大小验证 预期: 3389723 实际: 3389723
[2025-11-14 11:53:02.693][000000040.465] I/user.HTTP下载 资源清理完成

4)网络连接与HTTP上传
[2025-11-14 12:10:07.428][000000000.266] I/user.main tfcard 001.000.000
[2025-11-14 12:10:07.433][000000000.288] W/user.HTTP上传 等待网络连接 1 1
[2025-11-14 12:10:07.732][000000001.289] W/user.HTTP上传 等待网络连接 1 1
[2025-11-14 12:10:08.580][000000002.059] D/mobile cid1, state0
[2025-11-14 12:10:08.586][000000002.059] D/mobile bearer act 0, result 0
[2025-11-14 12:10:08.597][000000002.060] D/mobile NETIF_LINK_ON -> IP_READY
[2025-11-14 12:10:08.606][000000002.061] I/user.HTTP上传 网络已就绪 1 1
[2025-11-14 12:10:08.611][000000002.062] SPI_HWInit 552:spi0 speed 2000000,1994805,154
[2025-11-14 12:10:08.615][000000002.062] D/fatfs init sdcard at spi=0 cs=8
[2025-11-14 12:10:08.666][000000002.224] D/SPI_TF 卡容量 125173760KB
[2025-11-14 12:10:08.671][000000002.224] D/SPI_TF sdcard init OK OCR:0xc0ff8000!
[2025-11-14 12:10:08.676][000000002.230] I/user.HTTP上传 准备上传文件 /sd/3_23MB.bin 大小: 3389723 字节
[2025-11-14 12:10:08.687][000000002.230] I/user.HTTP上传 开始上传任务
[2025-11-14 12:10:08.691][000000002.234] D/socket connect to airtest.openluat.com,2900
[2025-11-14 12:10:08.699][000000002.235] dns_run 676:airtest.openluat.com state 0 id 1 ipv6 0 use dns server2, try 0
[2025-11-14 12:10:08.704][000000002.237] D/mobile TIME_SYNC 0
[2025-11-14 12:10:08.736][000000002.291] dns_run 693:dns all done ,now stop
[2025-11-14 12:10:19.571][000000013.118] I/user.httpplus 等待服务器完成响应
[2025-11-14 12:10:19.974][000000013.519] I/user.httpplus 等待服务器完成响应
[2025-11-14 12:10:19.980][000000013.529] I/user.httpplus 服务器已完成响应,开始解析响应
[2025-11-14 12:10:20.005][000000013.555] I/user.HTTP上传 上传完成 success 200
[2025-11-14 12:10:20.010][000000013.555] I/user.HTTP上传 服务器响应头 {"Content-Type":"text\/plain;charset=UTF-8","Connection":"close","Content-Length":"20","Vary":"Access-Control-Request-Headers","Date":"Fri, 14 Nov 2025 04"}
[2025-11-14 12:10:20.017][000000013.556] I/user.HTTP上传 服务器响应体长度 20
[2025-11-14 12:10:20.026][000000013.557] I/user.HTTP上传 服务器响应内容 uploadFileToStaticOK
[2025-11-14 12:10:20.031][000000013.557] I/user.HTTP上传 资源清理完成

七、总结

通过本章内容的学习,你可以学习到使用Air780EGH核心板+AirMICROCSD_1010配件板,,实现文件操作功能,HTTP文件下载以及HTTP文件上传的应用。