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

Merge branch 'master' of https://gitee.com/openLuat/LuatOS

马亚丹 4 месяцев назад
Родитель
Сommit
6da0177a90
27 измененных файлов с 2406 добавлено и 36 удалено
  1. 7 0
      components/airlink/include/luat_airlink_fota.h
  2. 55 0
      components/airlink/src/luat_airlink_fota.c
  3. 12 0
      components/lcd/luat_lcd.c
  4. 282 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/fs_io/flash_fs_io.lua
  5. 74 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/fs_io/http_download_flash.lua
  6. 78 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/fs_io/main.lua
  7. 143 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/fs_io/readme.md
  8. 1 1
      module/Air780EPM/demo/accessory_board/AirSHT30_1000/main.lua
  9. 8 8
      module/Air780EPM/demo/accessory_board/AirSHT30_1000/readme.md
  10. 0 6
      module/Air780EPM/demo/accessory_board/AirSHT30_1000/sht30_app.lua
  11. 1 1
      module/Air780EPM/demo/accessory_board/AirVOC_1000/main.lua
  12. 8 8
      module/Air780EPM/demo/accessory_board/AirVOC_1000/readme.md
  13. 0 8
      module/Air780EPM/demo/accessory_board/AirVOC_1000/voc_app.lua
  14. 282 0
      module/Air780EPM/demo/fs_io/flash_fs_io.lua
  15. 74 0
      module/Air780EPM/demo/fs_io/http_download_flash.lua
  16. 78 0
      module/Air780EPM/demo/fs_io/main.lua
  17. 141 0
      module/Air780EPM/demo/fs_io/readme.md
  18. 0 3
      module/Air8000/demo/accessory_board/AirETH_1000/http/http_app.lua
  19. 1 1
      module/Air8000/demo/accessory_board/AirETH_1000/network_routing/4g_out_ethernet_in_wifi_in/readme.md
  20. 282 0
      module/Air8000/demo/fs_io/flash_fs_io.lua
  21. 74 0
      module/Air8000/demo/fs_io/http_download_flash.lua
  22. 78 0
      module/Air8000/demo/fs_io/main.lua
  23. 139 0
      module/Air8000/demo/fs_io/readme.md
  24. 282 0
      module/Air8101/demo/fs_io/flash_fs_io.lua
  25. 85 0
      module/Air8101/demo/fs_io/http_download_flash.lua
  26. 78 0
      module/Air8101/demo/fs_io/main.lua
  27. 143 0
      module/Air8101/demo/fs_io/readme.md

+ 7 - 0
components/airlink/include/luat_airlink_fota.h

@@ -3,6 +3,13 @@
 #include "luat_base.h"
 #include "luat_fs.h"
 
+#define AIRLINK_FOTA_SUCCESS            (0)
+#define AIRLINK_FOTA_NO_MEM             (1)
+#define AIRLINK_FOTA_OPEN_FILE_FAIL     (2)
+
+
+
+
 typedef struct luat_airlink_fota {
     uint32_t state;
     size_t total_size;

+ 55 - 0
components/airlink/src/luat_airlink_fota.c

@@ -8,6 +8,46 @@
 #define LUAT_LOG_TAG "airlink.fota"
 #include "luat_log.h"
 
+#ifdef __LUATOS__
+#include "luat_msgbus.h"
+/*
+@sys_pub airlink
+AIRLINK升级结束消息 2025/10/24启用
+AIRLINK_SFOTA_DONE
+@bool result, 升级成功为true,否则为false
+@string reason, 失败原因,当前取值有"no_memory" 内存不足, "file_error" 文件打开异常, "upgrading" 升级中
+@usage
+-- 订阅式
+sys.subscribe("AIRLINK_SFOTA_DONE", function(result, reason)
+    log.info("airlink fota", result, reason)
+end)
+*/
+static int airlink_sfota_lua_cb(lua_State *L, void *ptr)
+{
+    rtos_msg_t *msg = (rtos_msg_t *)lua_topointer(L, -1);
+    lua_getglobal(L, "sys_pub");
+    lua_pushstring(L, "AIRLINK_SFOTA_DONE");
+    lua_pushboolean(L, msg->arg1 == AIRLINK_FOTA_SUCCESS ? 1 : 0);
+    switch (msg->arg1)
+    {
+    case AIRLINK_FOTA_SUCCESS:
+        lua_call(L, 2, 0);
+        break;
+    case AIRLINK_FOTA_NO_MEM:
+        lua_pushstring(L, "no_memory");
+        lua_call(L, 3, 0);
+        break;
+    case AIRLINK_FOTA_OPEN_FILE_FAIL:
+        lua_pushstring(L, "file_error");
+        lua_call(L, 3, 0);
+        break;
+    default:
+        break;
+    }
+    return 0;
+}
+#endif
+
 luat_airlink_fota_t *g_airlink_fota;
 
 int luat_airlink_fota_init(luat_airlink_fota_t *ctx)
@@ -69,6 +109,7 @@ void airlink_sfota_exec(void)
 {
     size_t wait_timeout = 0;
     int ret = 0;
+    uint8_t fota_ret = AIRLINK_FOTA_SUCCESS;
     g_airlink_fota->total_size = luat_fs_fsize(g_airlink_fota->path);
     LLOGI("开始执行sFOTA file size %ld", g_airlink_fota->total_size);
     uint32_t tmpv = 0;
@@ -77,6 +118,7 @@ void airlink_sfota_exec(void)
     if (fd == NULL)
     {
         LLOGE("打开sFOTA文件失败 %s", g_airlink_fota->path);
+        fota_ret = AIRLINK_FOTA_OPEN_FILE_FAIL;
         goto clean;
     }
     if (s_airlink_fota_txbuff == NULL)
@@ -87,6 +129,7 @@ void airlink_sfota_exec(void)
         if (s_airlink_fota_txbuff == NULL || s_airlink_fota_rxbuff == NULL || s_airlink_fota_cmdbuff == NULL)
         {
             LLOGE("申请sFOTA内存失败");
+            fota_ret = AIRLINK_FOTA_NO_MEM;
             goto clean;
         }
         memset(s_airlink_fota_txbuff, 0, AIRLINK_SFOTA_BUFF_SIZE);
@@ -187,6 +230,8 @@ void airlink_sfota_exec(void)
     luat_rtos_task_sleep(wait_timeout);
     LLOGI("FOTA执行完毕");
 
+
+
 clean:
     g_airlink_fota->state = 0;
     if (fd)
@@ -208,5 +253,15 @@ clean:
         luat_heap_opt_free(AIRLINK_MEM_TYPE, s_airlink_fota_cmdbuff);
         s_airlink_fota_cmdbuff = NULL;
     }
+
+#ifdef __LUATOS__
+    rtos_msg_t msg = {
+        .handler = airlink_sfota_lua_cb,
+        .arg1 = fota_ret,
+        .arg2 = 0,
+        .ptr = NULL
+    };
+    luat_msgbus_put(&msg, 0);
+#endif
     return;
 }

+ 12 - 0
components/lcd/luat_lcd.c

@@ -566,6 +566,18 @@ int luat_lcd_draw_line(luat_lcd_conf_t* conf,int16_t x1, int16_t y1, int16_t x2,
     int incx, incy, row, col;
     if (x1 == x2 || y1 == y2) // 直线
     {
+        if (x1 > x2)
+        {
+            int16_t tmp = x1;
+            x1 = x2;
+            x2 = tmp;
+        }
+        if (y1 > y2)
+        {
+            int16_t tmp = y1;
+            y1 = y2;
+            y2 = tmp;
+        }
         size_t dots = (x2 - x1 + 1) * (y2 - y1 + 1);//点数量
         luat_color_t* line_buf = (luat_color_t*) luat_heap_malloc(dots * sizeof(luat_color_t));
         if (conf->endianness_swap)

+ 282 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/fs_io/flash_fs_io.lua

@@ -0,0 +1,282 @@
+--[[
+@module  flash_fs_io
+@summary 内置Flash文件系统操作测试模块
+@version 1.0.0
+@date    2025.09.23
+@author  王棚嶙
+@usage
+本文件为内置Flash文件系统的操作测试流程:
+1. 获取文件系统信息( io.fsstat)
+2. 创建目录
+3. 创建并写入文件
+4. 检查文件是否存在
+5. 获取文件大小(io.fileSize)
+6. 读取文件内容
+7. 启动计数文件操作
+8. 文件追加测试
+9. 按行读取测试
+10. 文件重命名
+11. 列举目录内容
+12. 删除文件
+13. 删除目录
+本文件没有对外接口,直接在main.lua中require "flash_fs_io"就可以加载运行
+]]
+
+function flash_fs_io_task()
+    -- 使用内置Flash文件系统,根目录为"/",
+    local base_path = "/"
+    -- 创建一个目录
+    local demo_dir = "flash_demo"
+    -- 文件名
+    local dir_path = base_path .. demo_dir
+
+    -- ########## 开始进行内置Flash文件系统操作 ##########
+    log.info("文件系统操作", "===== 开始文件系统操作 =====")
+
+    -- 1. 获取文件系统信息 (使用 io.fsstat接口)
+    local success, total_blocks, used_blocks, block_size, fs_type  =  io.fsstat(base_path)
+    if success then
+        log.info(" io.fsstat成功:", 
+            "总空间=" .. total_blocks .. "块", 
+            "已用=" .. used_blocks .. "块", 
+            "块大小=" .. block_size.."字节",
+            "类型=" .. fs_type)
+    else
+        log.error(" io.fsstat", "获取文件系统信息失败")
+        return
+    end
+
+    -- 2. 创建目录
+    -- 如果目录不存在,则创建目录
+    if not io.dexist(dir_path) then
+        -- 创建目录
+        if io.mkdir(dir_path) then
+            log.info("io.mkdir", "目录创建成功", "路径:" .. dir_path)
+        else
+            log.error("io.mkdir", "目录创建失败", "路径:" .. dir_path)
+            return
+        end
+    else
+        log.warn("io.mkdir", "目录已存在,跳过创建", "路径:" .. dir_path)
+    end
+
+    -- 3. 创建并写入文件
+    local file_path = dir_path .. "/boottime"
+    local file = io.open(file_path, "wb")
+    if file then
+        file:write("这是内置Flash文件系统API文档示例的测试内容")
+        file:close()
+        log.info("文件创建", "文件写入成功", "路径:" .. file_path)
+    else
+        log.error("文件创建", "文件创建失败", "路径:" .. file_path)
+        return
+    end
+
+    -- 4. 检查文件是否存在
+    if io.exists(file_path) then
+        log.info("io.exists", "文件存在", "路径:" .. file_path)
+    else
+        log.error("io.exists", "文件不存在", "路径:" .. file_path)
+        return
+    end
+
+    -- 5. 获取文件大小 (使用io.fileSize接口)
+    local 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)
+        return
+    end
+
+    -- 6. 读取文件内容
+    file = io.open(file_path, "rb")
+    if file then
+        local content = file:read("*a")
+        log.info("文件读取", "路径:" .. file_path, "内容:" .. content)
+        file:close()
+    else
+        log.error("文件操作", "无法打开文件读取内容", "路径:" .. file_path)
+        return
+    end
+
+    -- 7. 启动计数文件操作
+    local count = 0
+    file = io.open(file_path, "rb")
+    if file then
+        local 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)
+        return
+    end
+
+    -- 8. 文件追加测试
+    local 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)
+        return
+    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)
+        return
+    end
+
+    -- 验证追加结果
+    file = io.open(append_file, "r")
+    if file then
+        local data = file:read("*a")
+        log.info("文件验证", "路径:" .. append_file, "内容:" .. data, "结果:",
+            data == "ABCdef" and "成功" or "失败")
+        file:close()
+    else
+        log.error("文件验证", "无法打开文件进行验证", "路径:" .. append_file)
+        return
+    end
+
+    -- 9. 按行读取测试
+    local 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)
+        return
+    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)
+        return
+    end
+
+    -- 10. 文件重命名
+    local old_path = append_file
+    local new_path = dir_path .. "/renamed_file.txt"
+    local 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)
+        return
+    end
+
+    -- 11. 列举目录内容
+    log.info("目录操作", "===== 开始目录列举 =====")
+
+    local ret, data = io.lsdir(dir_path, 50, 0)
+    if ret then
+        log.info("fs", "lsdir", json.encode(data))
+    else
+        log.info("fs", "lsdir", "fail", ret, data)
+        return
+    end
+
+    -- 12. 删除文件测试
+    if os.remove(new_path) then
+        log.info("os.remove", "文件删除成功", "路径:" .. new_path)
+        if not io.exists(new_path) then
+            log.info("验证结果", "renamed_file.txt文件删除验证成功")
+        else
+            log.error("验证结果", "renamed_file.txt文件删除验证失败")
+        end
+    else
+        log.error("os.remove", "renamed_file.txt文件删除失败", "路径:" .. new_path)
+        return
+    end
+
+    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("os.remove", "testline文件删除失败", "路径:" .. line_file)
+        return
+    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("os.remove", "boottime文件删除失败", "路径:" .. file_path)
+        return
+    end
+
+    -- 13. 删除目录
+    if io.rmdir(dir_path) then
+        log.info("io.rmdir", "目录删除成功", "路径:" .. dir_path)
+        if not io.dexist(dir_path) then
+            log.info("验证结果", "目录删除验证成功")
+        else
+            log.error("验证结果", "目录删除验证失败")
+        end
+    else
+        log.error("io.rmdir", "目录删除失败", "路径:" .. dir_path)
+        return
+    end
+
+    -- 再次获取文件系统信息,查看空间变化
+    local final_success, final_total_blocks, final_used_blocks, final_block_size, final_fs_type =  io.fsstat(base_path)
+    if final_success then
+        log.info(" io.fsstat", "操作后文件系统信息:", 
+                 "总空间=" .. final_total_blocks .. "块", 
+                 "已用=" .. final_used_blocks .. "块", 
+                 "块大小=" .. final_block_size.."字节",
+                 "类型=" .. final_fs_type)
+    end
+
+    log.info("文件系统操作", "===== 文件系统操作完成 =====")
+end
+
+sys.taskInit(flash_fs_io_task)

+ 74 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/fs_io/http_download_flash.lua

@@ -0,0 +1,74 @@
+--[[
+@module http_download_flash
+@summary HTTP下载文件到内置Flash模块
+@version 1.0.0
+@date    2025.09.23
+@author  王棚嶙
+@usage
+本文件演示的功能为通过HTTP下载文件到内置Flash中:
+1. 网络就绪检测
+2. 创建HTTP下载任务并等待完成
+3. 记录下载结果
+4. 获取并记录文件大小(使用io.fileSize)
+本文件没有对外接口,直接在main.lua中require "http_download_flash"即可
+]]
+
+local function http_download_flash_task()
+    -- 阶段1: 网络就绪检测
+    while not socket.adapter(socket.dft()) do
+        log.warn("HTTP下载", "等待网络连接", socket.dft())
+        sys.waitUntil("IP_READY", 1000)
+    end
+
+    log.info("HTTP下载", "网络已就绪", socket.dft())
+
+    -- 阶段2: 执行下载任务
+    log.info("HTTP下载", "开始下载任务")
+
+    -- 创建下载目录
+    local download_dir = "/downloads"
+    if not io.dexist(download_dir) then
+        io.mkdir(download_dir)
+    end
+
+    -- 核心下载操作开始
+    local code, headers, body_size = http.request("GET",
+                                    "https://gitee.com/openLuat/LuatOS/raw/master/module/Air780EHM_Air780EHV_Air780EGH/demo/audio/sample-6s.mp3",
+                                    nil, nil, {dst = download_dir .. "/sample-6s.mp3"}).wait()
+
+    -- 阶段3: 记录下载结果
+    log.info("HTTP下载", "下载完成", 
+        code == 200 and "success" or "error", 
+        code, 
+        json.encode(headers or {}), 
+        body_size) 
+        
+    if code == 200 then
+        -- 获取实际文件大小 (使用io.fileSize接口)
+        local actual_size = io.fileSize(download_dir .. "/sample-6s.mp3")
+        if not actual_size then
+            -- 备用方案
+            actual_size = io.fileSize(download_dir .. "/sample-6s.mp3")
+        end
+        
+        log.info("HTTP下载", "文件大小验证", "预期:", body_size, "实际:", actual_size)
+        
+        if actual_size ~= body_size then
+            log.error("HTTP下载", "文件大小不一致", "预期:", body_size, "实际:", actual_size)
+        end
+        
+        -- 展示下载后的文件系统状态
+        local success, total_blocks, used_blocks, block_size, fs_type =  io.fsstat("/")
+        if success then
+            log.info("HTTP下载", "下载后文件系统信息:", 
+                     "总空间=" .. total_blocks .. "块", 
+                     "已用=" .. used_blocks .. "块", 
+                     "块大小=" .. block_size.."字节",
+                     "类型=" .. fs_type)
+        end
+    end
+
+end
+
+-- 创建下载任务
+sys.taskInit(http_download_flash_task)

+ 78 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/fs_io/main.lua

@@ -0,0 +1,78 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 001.000.000
+@date    2025.09.23
+@author  王棚嶙
+@usage
+本 Demo 演示了在Air780EHM/780EGH/780EHV内置Flash文件系统中的完整操作流程:
+1. 基础操作:看门狗守护机制
+2. 文件系统操作:
+   - 文件系统信息查询( io.fsstat)
+   - 文件大小获取(io.fileSize)
+   - 文件创建/读写/追加
+   - 目录创建/删除
+   - 文件重命名/删除
+   - 文件存在性检查
+3. 下载功能:
+   - 网络检测与HTTP文件下载到内置Flash
+]]
+
+--[[
+必须定义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 = "flash_fs_io_demo"
+VERSION = "001.000.000"
+
+-- 在日志中打印项目名和项目版本号
+log.info("main", PROJECT, VERSION)
+
+-- 添加硬狗防止程序卡死
+if wdt then
+    -- 初始化watchdog设置为9s
+    wdt.init(9000)
+    -- 3s喂一次狗 
+    sys.timerLoopStart(wdt.feed, 3000) 
+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下载功能是异步操作。放到一个项目中,如果加载的时间点是随机的,就会出现哪个任务先抢到CPU时间片,哪个就先执行,不符合正常的业务逻辑,用户在参考编程的时候也要注意。]]
+
+-- 加载内置Flash文件系统操作演示模块
+require "flash_fs_io"
+-- 加载HTTP下载存入内置Flash功能演示模块
+-- require "http_download_flash"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+-- sys.run()之后后面不要加任何语句!!!!!
+sys.run()

+ 143 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/fs_io/readme.md

@@ -0,0 +1,143 @@
+## **功能模块介绍**
+
+本 Demo 演示了在Air780EHM/780EGH/780EHV内置Flash文件系统中的完整操作流程,覆盖了从文件系统读写到高级文件操作的完整功能链。项目分为两个核心模块:
+
+1、main.lua:主程序入口 <br> 
+2、flash_fs_io.lua:内置Flash文件系统的操作测试流程模块,实现文件系统管理、文件操作和目录管理功能。<br> 
+3、http_download_flash.lua:HTTP下载模块,演示HTTP下载文件到内置Flash中的功能
+
+## **演示功能概述**
+
+### 1、主程序入口模块(main.lua)
+
+- 初始化项目信息和版本号
+- 初始化看门狗,并定时喂狗
+- 启动一个循环定时器,每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况方便分析内存使用是否有异常
+- 加载flash_fs_io模块(通过require "flash_fs_io")
+- 加载http_download_flash模块(通过require "http_download_flash")
+- 最后运行sys.run()。
+
+### 2、内置Flash文件系统演示模块(flash_fs_io.lua)
+
+#### 文件操作
+- 获取文件系统信息( io.fsstat)
+- 创建目录:io.mkdir("/flash_demo")
+- 创建/写入文件: io.open("/flash_demo/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)
+
+### 3、HTTP下载功能 (http_download_flash.lua)
+
+
+#### 网络就绪检测
+
+- 1秒循环等待IP就绪
+- 网络故障处理机制
+
+#### 安全下载
+
+- HTTP下载
+
+#### 结果处理
+
+- 下载状态码解析
+- 自动文件大小验证
+- 获取文件系统信息(fs.fsstat)
+
+## **演示硬件环境**
+
+1、Air780EHM核心板一块(Air780EHM/780EGH/780EHV三种模块的核心板接线方式相同,这里以Air780EHM为例)
+
+2、TYPE-C USB数据线一根
+
+3、SIM卡一张
+
+4、Air780EHM/780EGH/780EHV核心板和数据线的硬件接线方式为
+
+- Air780EHM核心板通过TYPE-C USB口供电;(核心板USB旁边的开关拨到on一端)
+
+- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+## **演示软件环境**
+
+1、Luatools下载调试工具: https://docs.openluat.com/air780epm/common/Luatools/
+
+2、内核固件版本:
+Air780EHM:https://docs.openluat.com/air780epm/luatos/firmware/version/
+Air780EGH:https://docs.openluat.com/air780egh/luatos/firmware/version/
+Air780EHV:https://docs.openluat.com/air780ehv/luatos/firmware/version/
+
+## **演示核心步骤**
+
+1、搭建好硬件环境
+
+2、通过Luatools将demo与固件烧录到开发板中
+
+3、烧录好后,板子开机将会在Luatools上看到如下打印
+
+```lua
+
+(1)文件操作演示
+[2025-10-22 15:23:25.096][000000000.595] I/user.文件系统操作 ===== 开始文件系统操作 =====
+[2025-10-22 15:23:25.104][000000000.601] I/user. io.fsstat成功: 总空间=192块 已用=20块 块大小=4096字节 类型=lfs
+[2025-10-22 15:23:25.118][000000000.644] I/user.io.mkdir 目录创建成功 路径:/flash_demo
+[2025-10-22 15:23:25.127][000000000.648] I/user.文件创建 文件写入成功 路径:/flash_demo/boottime
+[2025-10-22 15:23:25.145][000000000.651] I/user.io.exists 文件存在 路径:/flash_demo/boottime
+[2025-10-22 15:23:25.157][000000000.654] I/user.io.fileSize 文件大小:59字节 路径:/flash_demo/boottime
+[2025-10-22 15:23:25.165][000000000.657] I/user.文件读取 路径:/flash_demo/boottime 内容:这是内置Flash文件系统API文档示例的测试内容
+[2025-10-22 15:23:25.181][000000000.660] I/user.启动计数 文件内容: 这是内置Flash文件系统API文档示例的测试内容 十六进制: E8BF99E698AFE58685E7BDAE466C617368E69687E4BBB6E7B3BBE7BB9F415049E69687E6A1A3E7A4BAE4BE8BE79A84E6B58BE8AF95E58685E5AEB9 118
+[2025-10-22 15:23:25.189][000000000.660] I/user.启动计数 当前值: 0
+[2025-10-22 15:23:25.211][000000000.660] I/user.启动计数 更新值: 1
+[2025-10-22 15:23:25.217][000000000.663] I/user.文件写入 路径:/flash_demo/boottime 内容: 1
+[2025-10-22 15:23:25.224][000000000.669] I/user.文件创建 路径:/flash_demo/test_a 初始内容:ABC
+[2025-10-22 15:23:25.245][000000000.672] I/user.文件追加 路径:/flash_demo/test_a 追加内容:def
+[2025-10-22 15:23:25.251][000000000.675] I/user.文件验证 路径:/flash_demo/test_a 内容:ABCdef 结果: 成功
+[2025-10-22 15:23:25.267][000000000.678] I/user.文件创建 路径:/flash_demo/testline 写入3行文本
+[2025-10-22 15:23:25.277][000000000.681] I/user.按行读取 路径:/flash_demo/testline 第1行: abc
+[2025-10-22 15:23:25.285][000000000.682] I/user.按行读取 路径:/flash_demo/testline 第2行: 123
+[2025-10-22 15:23:25.295][000000000.682] I/user.按行读取 路径:/flash_demo/testline 第3行: wendal
+[2025-10-22 15:23:25.301][000000000.689] I/user.os.rename 文件重命名成功 原路径:/flash_demo/test_a 新路径:/flash_demo/renamed_file.txt
+[2025-10-22 15:23:25.312][000000000.694] D/vfs fopen /flash_demo/test_a r not found
+[2025-10-22 15:23:25.321][000000000.694] I/user.验证结果 重命名验证成功 新文件存在 原文件不存在
+[2025-10-22 15:23:25.340][000000000.695] I/user.目录操作 ===== 开始目录列举 =====
+[2025-10-22 15:23:25.348][000000000.706] I/user.fs lsdir [{"name":"boottime","size":1,"type":0},{"name":"renamed_file.txt","size":6,"type":0},{"name":"testline","size":15,"type":0}]
+[2025-10-22 15:23:25.359][000000000.710] I/user.os.remove 文件删除成功 路径:/flash_demo/renamed_file.txt
+[2025-10-22 15:23:25.366][000000000.713] D/vfs fopen /flash_demo/renamed_file.txt r not found
+[2025-10-22 15:23:25.373][000000000.713] I/user.验证结果 renamed_file.txt文件删除验证成功
+[2025-10-22 15:23:25.378][000000000.716] I/user.os.remove testline文件删除成功 路径:/flash_demo/testline
+[2025-10-22 15:23:25.390][000000000.719] D/vfs fopen /flash_demo/testline r not found
+[2025-10-22 15:23:25.395][000000000.719] I/user.验证结果 testline文件删除验证成功
+[2025-10-22 15:23:25.415][000000000.723] I/user.os.remove 文件删除成功 路径:/flash_demo/boottime
+[2025-10-22 15:23:25.423][000000000.726] D/vfs fopen /flash_demo/boottime r not found
+[2025-10-22 15:23:25.444][000000000.726] I/user.验证结果 boottime文件删除验证成功
+[2025-10-22 15:23:25.453][000000000.732] I/user.io.rmdir 目录删除成功 路径:/flash_demo
+[2025-10-22 15:23:25.464][000000000.734] I/user.验证结果 目录删除验证成功
+[2025-10-22 15:23:25.477][000000000.740] I/user. io.fsstat 操作后文件系统信息: 总空间=192块 已用=20块 块大小=4096字节 类型=lfs
+[2025-10-22 15:23:25.484][000000000.740] I/user.文件系统操作 ===== 文件系统操作完成 =====
+
+
+
+
+(2)网络连接与HTTP下载
+[2025-10-22 15:34:04.507][000000007.471] I/user.HTTP下载 网络已就绪 1 3
+[2025-10-22 15:34:04.550][000000007.471] I/user.HTTP下载 开始下载任务
+[2025-10-22 15:34:04.579][000000007.478] dns_run 676:gitee.com state 0 id 1 ipv6 0 use dns server2, try 0
+[2025-10-22 15:34:04.604][000000007.508] D/mobile TIME_SYNC 0
+[2025-10-22 15:34:04.734][000000007.517] dns_run 693:dns all done ,now stop
+[2025-10-22 15:34:06.390][000000009.741] I/user.HTTP下载 下载完成 success 200 
+[2025-10-22 15:34:06.422][000000009.741] {"Age":"0","Cache-Control":"public, max-age=60","Via":"1.1 varnish","Transfer-Encoding":"chunked","Date":"Wed, 22 Oct 2025 07:34:04 GMT","Access-Control-Allow-Credentials":"true","Vary":"Accept-Encoding","X-Served-By":"cache-ffe9","X-Gitee-Server":"http-pilot 1.9.21","Connection":"keep-alive","Server":"ADAS\/1.0.214","Access-Control-Allow-Headers":"Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With,X-CustomHeader,Content-Range,Range,Set-Language","Content-Security-Policy":"default-src 'none'; style-src 'unsafe-inline'; sandbox","X-Request-Id":"fa536af1-51bd-400f-8d4b-7322355a9db2","Accept-Ranges":"bytes","Etag":"W\/\"2aaa2788d394a924e258d6f26ad78b8c948950f5\"","Content-Type":"text\/plain; charset=utf-8","Access-Control-Allow-Methods":"GET, POST, PUT, PATCH, DELETE, OPTIONS","X-Frame-Options":"DENY","X-Cache":"MISS","Set-Cookie":"BEC=1f1759df3ccd099821dcf0da6feb0357;Path=\/;Max-Age=126000"}
+[2025-10-22 15:34:06.477][000000009.742]  103070
+[2025-10-22 15:34:06.492][000000009.745] I/user.HTTP下载 文件大小验证 预期: 103070 实际: 103070
+[2025-10-22 15:34:06.525][000000009.751] I/user.HTTP下载 下载后文件系统信息: 总空间=192块 已用=46块 块大小=4096字节 类型=lfs
+
+
+```

+ 1 - 1
module/Air780EPM/demo/accessory_board/AirSHT30_1000/main.lua

@@ -7,7 +7,7 @@
 @usage
 AirSHT30_1000是合宙设计生产的一款I2C接口的SHT30温湿度传感器配件板;
 本demo演示的核心功能为:
-Air780EPM开发板+AirSHT30_1000配件板,每隔1秒读取1次温湿度数据;
+Air780EPM核心板+AirSHT30_1000配件板,每隔1秒读取1次温湿度数据;
 更多说明参考本目录下的readme.md文件
 ]]
 

+ 8 - 8
module/Air780EPM/demo/accessory_board/AirSHT30_1000/readme.md

@@ -12,12 +12,12 @@ AirSHT30_1000是合宙设计生产的一款I2C接口的SHT30温湿度传感器
 
 本demo演示的核心功能为:
 
-Air780EPM开发板+AirSHT30_1000配件板,每隔1秒读取1次温湿度数据;
+Air780EPM核心板+AirSHT30_1000配件板,每隔1秒读取1次温湿度数据;
 
 
-## 开发板+配件板资料
+## 核心板+配件板资料
 
-[Air780EPM开发板](https://docs.openluat.com/air780epm/product/shouce/)
+[Air780EPM核心板](https://docs.openluat.com/air780epm/product/shouce/)
 
 [AirSHT30_1000配件板相关资料](https://docs.openluat.com/accessory/AirSHT30_1000/)
 
@@ -26,18 +26,18 @@ Air780EPM开发板+AirSHT30_1000配件板,每隔1秒读取1次温湿度数据
 
 ![](https://docs.openluat.com/accessory/AirSHT30_1000/image/connect_780epm.png)
 
-1、Air780EPM开发
+1、Air780EPM核心
 
 2、AirSHT30_1000配件板
 
 3、母对母的杜邦线4根
 
-| Air780EPM开发板 | AirSHT30_1000配件板|
+| Air780EPM核心板 | AirSHT30_1000配件板|
 | ------------ | ------------------ |
-|     3V3(VDD_EXT)     |         3V3        |
+|     3V3     |         3V3        |
 |     GND   |         GND        |
-|  I2C1_SDA(CAMERA_SDA)  |         SDA        |
-| I2C1_SCL(CAMERA_SCL) |         SCL        |
+|  66/I2C1SDA  |         SDA        |
+| 67/I2C1SCL |         SCL        |
 
 
 ## 演示软件环境

+ 0 - 6
module/Air780EPM/demo/accessory_board/AirSHT30_1000/sht30_app.lua

@@ -15,12 +15,6 @@
 --加载AirSHT30_1000驱动文件
 local air_sht30 = require "AirSHT30_1000"
 
---如果使用的是Air780EPM核心板,则需要注释掉下面这行代码;
---如果使用的是Air780EPM开发板,则需要打开下面这行代码;
---这行代码的作用是:
---因为Air780EPM开发板上I2C内部没上拉,需要外部加上拉
---所以设置gpio2输出高电平,给camera_sda、camera_scl引脚提供上拉
-gpio.setup(2, 1)
 
 --每隔1秒读取一次温湿度数据
 local function read_sht30_task_func()

+ 1 - 1
module/Air780EPM/demo/accessory_board/AirVOC_1000/main.lua

@@ -8,7 +8,7 @@
 AirVOC_1000是合宙设计生产的一款I2C接口的VOC(挥发性有机化合物)气体传感器配件板;
 主要用于检测甲醛、一氧化碳、可燃气体、酒精、氨气、硫化物、苯系蒸汽、烟雾、其它有害气体的监测;
 本demo演示的核心功能为:
-Air780EPM开发板+AirVOC_1000配件板,每隔1秒读取1次TVOC空气质量数据;
+Air780EPM核心板+AirVOC_1000配件板,每隔1秒读取1次TVOC空气质量数据;
 更多说明参考本目录下的readme.md文件
 ]]
 

+ 8 - 8
module/Air780EPM/demo/accessory_board/AirVOC_1000/readme.md

@@ -14,12 +14,12 @@ AirVOC_1000是合宙设计生产的一款I2C接口的VOC(挥发性有机化合
 
 本demo演示的核心功能为:
 
-Air780EPM开发板+AirVOC_1000配件板,每隔1秒读取1次TVOC空气质量数据;
+Air780EPM核心板+AirVOC_1000配件板,每隔1秒读取1次TVOC空气质量数据;
 
 
 ## 核心板+配件板资料
 
-[Air780EPM开发板](https://docs.openluat.com/air780epm/product/shouce/)
+[Air780EPM核心板](https://docs.openluat.com/air780epm/product/shouce/)
 
 [AirVOC_1000配件板相关资料](https://docs.openluat.com/accessory/AirVOC_1000/)
 
@@ -28,20 +28,20 @@ Air780EPM开发板+AirVOC_1000配件板,每隔1秒读取1次TVOC空气质量
 
 ![](https://docs.openluat.com/accessory/AirVOC_1000/image/connect_Air780EPM.png)
 
-1、Air780EPM开发
+1、Air780EPM核心
 
 2、AirVOC_1000配件板
 
 3、母对母的杜邦线4根
 
-4、Air780EPM开发板和AirVOC_1000配件板的硬件接线方式为
+4、Air780EPM核心板和AirVOC_1000配件板的硬件接线方式为
 
-| Air780EPM开发板 | AirVOC_1000配件板  |
+| Air780EPM核心板 | AirVOC_1000配件板  |
 | ------------ | ------------------ |
-|     3V3(VDD_EXT)     |         3V3        |
+|     3V3     |         3V3        |
 |     GND   |         GND        |
-| I2C1_SDA(CAMERA_SDA) |         SDA        |
-| I2C1_SCL(CAMERA_SCL) |         SCL        |
+| 66/I2C1SDA |         SDA        |
+| 67/I2C1SCL |         SCL        |
 
 
 ## 演示软件环境

+ 0 - 8
module/Air780EPM/demo/accessory_board/AirVOC_1000/voc_app.lua

@@ -16,14 +16,6 @@
 local air_voc = require "AirVOC_1000"
 
 
---如果使用的是Air780EPM核心板,则需要注释掉下面这行代码;
---如果使用的是Air780EPM开发板,则需要打开下面这行代码;
---这行代码的作用是:
---因为Air780EPM开发板上I2C内部没上拉,需要外部加上拉
---所以设置gpio2输出高电平,给camera_sda、camera_scl引脚提供上拉
-gpio.setup(2, 1)
-
-
 --每隔1秒读取一次TVOC数据
 local function read_voc_task_func()
     --打开voc硬件

+ 282 - 0
module/Air780EPM/demo/fs_io/flash_fs_io.lua

@@ -0,0 +1,282 @@
+--[[
+@module  flash_fs_io
+@summary 内置Flash文件系统操作测试模块
+@version 1.0.0
+@date    2025.09.23
+@author  王棚嶙
+@usage
+本文件为内置Flash文件系统的操作测试流程:
+1. 获取文件系统信息( io.fsstat)
+2. 创建目录
+3. 创建并写入文件
+4. 检查文件是否存在
+5. 获取文件大小(io.fileSize)
+6. 读取文件内容
+7. 启动计数文件操作
+8. 文件追加测试
+9. 按行读取测试
+10. 文件重命名
+11. 列举目录内容
+12. 删除文件
+13. 删除目录
+本文件没有对外接口,直接在main.lua中require "flash_fs_io"就可以加载运行
+]]
+
+function flash_fs_io_task()
+    -- 使用内置Flash文件系统,根目录为"/",
+    local base_path = "/"
+    -- 创建一个目录
+    local demo_dir = "flash_demo"
+    -- 文件名
+    local dir_path = base_path .. demo_dir
+
+    -- ########## 开始进行内置Flash文件系统操作 ##########
+    log.info("文件系统操作", "===== 开始文件系统操作 =====")
+
+    -- 1. 获取文件系统信息 (使用 io.fsstat接口)
+    local success, total_blocks, used_blocks, block_size, fs_type  =  io.fsstat(base_path)
+    if success then
+        log.info(" io.fsstat成功:", 
+            "总空间=" .. total_blocks .. "块", 
+            "已用=" .. used_blocks .. "块", 
+            "块大小=" .. block_size.."字节",
+            "类型=" .. fs_type)
+    else
+        log.error(" io.fsstat", "获取文件系统信息失败")
+        return
+    end
+
+    -- 2. 创建目录
+    -- 如果目录不存在,则创建目录
+    if not io.dexist(dir_path) then
+        -- 创建目录
+        if io.mkdir(dir_path) then
+            log.info("io.mkdir", "目录创建成功", "路径:" .. dir_path)
+        else
+            log.error("io.mkdir", "目录创建失败", "路径:" .. dir_path)
+            return
+        end
+    else
+        log.warn("io.mkdir", "目录已存在,跳过创建", "路径:" .. dir_path)
+    end
+
+    -- 3. 创建并写入文件
+    local file_path = dir_path .. "/boottime"
+    local file = io.open(file_path, "wb")
+    if file then
+        file:write("这是内置Flash文件系统API文档示例的测试内容")
+        file:close()
+        log.info("文件创建", "文件写入成功", "路径:" .. file_path)
+    else
+        log.error("文件创建", "文件创建失败", "路径:" .. file_path)
+        return
+    end
+
+    -- 4. 检查文件是否存在
+    if io.exists(file_path) then
+        log.info("io.exists", "文件存在", "路径:" .. file_path)
+    else
+        log.error("io.exists", "文件不存在", "路径:" .. file_path)
+        return
+    end
+
+    -- 5. 获取文件大小 (使用io.fileSize接口)
+    local 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)
+        return
+    end
+
+    -- 6. 读取文件内容
+    file = io.open(file_path, "rb")
+    if file then
+        local content = file:read("*a")
+        log.info("文件读取", "路径:" .. file_path, "内容:" .. content)
+        file:close()
+    else
+        log.error("文件操作", "无法打开文件读取内容", "路径:" .. file_path)
+        return
+    end
+
+    -- 7. 启动计数文件操作
+    local count = 0
+    file = io.open(file_path, "rb")
+    if file then
+        local 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)
+        return
+    end
+
+    -- 8. 文件追加测试
+    local 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)
+        return
+    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)
+        return
+    end
+
+    -- 验证追加结果
+    file = io.open(append_file, "r")
+    if file then
+        local data = file:read("*a")
+        log.info("文件验证", "路径:" .. append_file, "内容:" .. data, "结果:",
+            data == "ABCdef" and "成功" or "失败")
+        file:close()
+    else
+        log.error("文件验证", "无法打开文件进行验证", "路径:" .. append_file)
+        return
+    end
+
+    -- 9. 按行读取测试
+    local 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)
+        return
+    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)
+        return
+    end
+
+    -- 10. 文件重命名
+    local old_path = append_file
+    local new_path = dir_path .. "/renamed_file.txt"
+    local 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)
+        return
+    end
+
+    -- 11. 列举目录内容
+    log.info("目录操作", "===== 开始目录列举 =====")
+
+    local ret, data = io.lsdir(dir_path, 50, 0)
+    if ret then
+        log.info("fs", "lsdir", json.encode(data))
+    else
+        log.info("fs", "lsdir", "fail", ret, data)
+        return
+    end
+
+    -- 12. 删除文件测试
+    if os.remove(new_path) then
+        log.info("os.remove", "文件删除成功", "路径:" .. new_path)
+        if not io.exists(new_path) then
+            log.info("验证结果", "renamed_file.txt文件删除验证成功")
+        else
+            log.error("验证结果", "renamed_file.txt文件删除验证失败")
+        end
+    else
+        log.error("os.remove", "renamed_file.txt文件删除失败", "路径:" .. new_path)
+        return
+    end
+
+    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("os.remove", "testline文件删除失败", "路径:" .. line_file)
+        return
+    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("os.remove", "boottime文件删除失败", "路径:" .. file_path)
+        return
+    end
+
+    -- 13. 删除目录
+    if io.rmdir(dir_path) then
+        log.info("io.rmdir", "目录删除成功", "路径:" .. dir_path)
+        if not io.dexist(dir_path) then
+            log.info("验证结果", "目录删除验证成功")
+        else
+            log.error("验证结果", "目录删除验证失败")
+        end
+    else
+        log.error("io.rmdir", "目录删除失败", "路径:" .. dir_path)
+        return
+    end
+
+    -- 再次获取文件系统信息,查看空间变化
+    local final_success, final_total_blocks, final_used_blocks, final_block_size, final_fs_type =  io.fsstat(base_path)
+    if final_success then
+        log.info(" io.fsstat", "操作后文件系统信息:", 
+                 "总空间=" .. final_total_blocks .. "块", 
+                 "已用=" .. final_used_blocks .. "块", 
+                 "块大小=" .. final_block_size.."字节",
+                 "类型=" .. final_fs_type)
+    end
+
+    log.info("文件系统操作", "===== 文件系统操作完成 =====")
+end
+
+sys.taskInit(flash_fs_io_task)

+ 74 - 0
module/Air780EPM/demo/fs_io/http_download_flash.lua

@@ -0,0 +1,74 @@
+--[[
+@module http_download_flash
+@summary HTTP下载文件到内置Flash模块
+@version 1.0.0
+@date    2025.09.23
+@author  王棚嶙
+@usage
+本文件演示的功能为通过HTTP下载文件到内置Flash中:
+1. 网络就绪检测
+2. 创建HTTP下载任务并等待完成
+3. 记录下载结果
+4. 获取并记录文件大小(使用io.fileSize)
+本文件没有对外接口,直接在main.lua中require "http_download_flash"即可
+]]
+
+local function http_download_flash_task()
+    -- 阶段1: 网络就绪检测
+    while not socket.adapter(socket.dft()) do
+        log.warn("HTTP下载", "等待网络连接", socket.dft())
+        sys.waitUntil("IP_READY", 1000)
+    end
+
+    log.info("HTTP下载", "网络已就绪", socket.dft())
+
+    -- 阶段2: 执行下载任务
+    log.info("HTTP下载", "开始下载任务")
+
+    -- 创建下载目录
+    local download_dir = "/downloads"
+    if not io.dexist(download_dir) then
+        io.mkdir(download_dir)
+    end
+
+    -- 核心下载操作开始
+    local code, headers, body_size = http.request("GET",
+                                    "https://gitee.com/openLuat/LuatOS/raw/master/module/Air780EHM_Air780EHV_Air780EGH/demo/audio/sample-6s.mp3",
+                                    nil, nil, {dst = download_dir .. "/sample-6s.mp3"}).wait()
+
+    -- 阶段3: 记录下载结果
+    log.info("HTTP下载", "下载完成", 
+        code == 200 and "success" or "error", 
+        code, 
+        json.encode(headers or {}), 
+        body_size) 
+        
+    if code == 200 then
+        -- 获取实际文件大小 (使用io.fileSize接口)
+        local actual_size = io.fileSize(download_dir .. "/sample-6s.mp3")
+        if not actual_size then
+            -- 备用方案
+            actual_size = io.fileSize(download_dir .. "/sample-6s.mp3")
+        end
+        
+        log.info("HTTP下载", "文件大小验证", "预期:", body_size, "实际:", actual_size)
+        
+        if actual_size ~= body_size then
+            log.error("HTTP下载", "文件大小不一致", "预期:", body_size, "实际:", actual_size)
+        end
+        
+        -- 展示下载后的文件系统状态
+        local success, total_blocks, used_blocks, block_size, fs_type =  io.fsstat("/")
+        if success then
+            log.info("HTTP下载", "下载后文件系统信息:", 
+                     "总空间=" .. total_blocks .. "块", 
+                     "已用=" .. used_blocks .. "块", 
+                     "块大小=" .. block_size.."字节",
+                     "类型=" .. fs_type)
+        end
+    end
+
+end
+
+-- 创建下载任务
+sys.taskInit(http_download_flash_task)

+ 78 - 0
module/Air780EPM/demo/fs_io/main.lua

@@ -0,0 +1,78 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 001.000.000
+@date    2025.09.23
+@author  王棚嶙
+@usage
+本 Demo 演示了在Air780EPM内置Flash文件系统中的完整操作流程:
+1. 基础操作:看门狗守护机制
+2. 文件系统操作:
+   - 文件系统信息查询( io.fsstat)
+   - 文件大小获取(io.fileSize)
+   - 文件创建/读写/追加
+   - 目录创建/删除
+   - 文件重命名/删除
+   - 文件存在性检查
+3. 下载功能:
+   - 网络检测与HTTP文件下载到内置Flash
+]]
+
+--[[
+必须定义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 = "flash_fs_io_demo"
+VERSION = "001.000.000"
+
+-- 在日志中打印项目名和项目版本号
+log.info("main", PROJECT, VERSION)
+
+-- 添加硬狗防止程序卡死
+if wdt then
+    -- 初始化watchdog设置为9s
+    wdt.init(9000)
+    -- 3s喂一次狗 
+    sys.timerLoopStart(wdt.feed, 3000) 
+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下载功能是异步操作。放到一个项目中,如果加载的时间点是随机的,就会出现哪个任务先抢到CPU时间片,哪个就先执行,不符合正常的业务逻辑,用户在参考编程的时候也要注意。]]
+
+-- 加载内置Flash文件系统操作演示模块
+require "flash_fs_io"
+-- 加载HTTP下载存入内置Flash功能演示模块
+-- require "http_download_flash"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+-- sys.run()之后后面不要加任何语句!!!!!
+sys.run()

+ 141 - 0
module/Air780EPM/demo/fs_io/readme.md

@@ -0,0 +1,141 @@
+## **功能模块介绍**
+
+本 Demo 演示了在Air780EPM内置Flash文件系统中的完整操作流程,覆盖了从文件系统读写到高级文件操作的完整功能链。项目分为两个核心模块:
+
+1、main.lua:主程序入口 <br> 
+2、flash_fs_io.lua:内置Flash文件系统的操作测试流程模块,实现文件系统管理、文件操作和目录管理功能。<br> 
+3、http_download_flash.lua:HTTP下载模块,演示HTTP下载文件到内置Flash中的功能
+
+## **演示功能概述**
+
+### 1、主程序入口模块(main.lua)
+
+- 初始化项目信息和版本号
+- 初始化看门狗,并定时喂狗
+- 启动一个循环定时器,每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况方便分析内存使用是否有异常
+- 加载flash_fs_io模块(通过require "flash_fs_io")
+- 加载http_download_flash模块(通过require "http_download_flash")
+- 最后运行sys.run()。
+
+### 2、内置Flash文件系统演示模块(flash_fs_io.lua)
+
+#### 文件操作
+- 获取文件系统信息( io.fsstat)
+- 创建目录:io.mkdir("/flash_demo")
+- 创建/写入文件: io.open("/flash_demo/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)
+
+### 3、HTTP下载功能 (http_download_flash.lua)
+
+
+#### 网络就绪检测
+
+- 1秒循环等待IP就绪
+- 网络故障处理机制
+
+#### 安全下载
+
+- HTTP下载
+
+#### 结果处理
+
+- 下载状态码解析
+- 自动文件大小验证
+- 获取文件系统信息( io.fsstat)
+
+## **演示硬件环境**
+
+1、Air780EPM核心板一块
+
+2、TYPE-C USB数据线一根
+
+3、SIM卡一张
+
+4、Air780EPM核心板和数据线的硬件接线方式为
+
+- Air780EPM核心板通过TYPE-C USB口供电;(核心板USB旁边的开关拨到on一端)
+
+- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+## **演示软件环境**
+
+1、Luatools下载调试工具:https://docs.openluat.com/air780epm/common/Luatools/
+
+2、内核固件版本:https://docs.openluat.com/air780epm/luatos/firmware/version/
+
+## **演示核心步骤**
+
+1、搭建好硬件环境
+
+2、通过Luatools将demo与固件烧录到开发板中
+
+3、烧录好后,板子开机将会在Luatools上看到如下打印
+
+```lua
+
+(1)文件操作演示
+[2025-10-22 16:04:28.032][000000000.214] I/user.文件系统操作 ===== 开始文件系统操作 =====
+[2025-10-22 16:04:28.064][000000000.218] I/user. io.fsstat成功: 总空间=42块 已用=4块 块大小=4096字节 类型=lfs
+[2025-10-22 16:04:28.085][000000000.319] I/user.io.mkdir 目录创建成功 路径:/flash_demo
+[2025-10-22 16:04:28.105][000000000.328] I/user.文件创建 文件写入成功 路径:/flash_demo/boottime
+[2025-10-22 16:04:28.114][000000000.331] I/user.io.exists 文件存在 路径:/flash_demo/boottime
+[2025-10-22 16:04:28.130][000000000.334] I/user.io.fileSize 文件大小:59字节 路径:/flash_demo/boottime
+[2025-10-22 16:04:28.142][000000000.337] I/user.文件读取 路径:/flash_demo/boottime 内容:这是内置Flash文件系统API文档示例的测试内容
+[2025-10-22 16:04:28.167][000000000.340] I/user.启动计数 文件内容: 这是内置Flash文件系统API文档示例的测试内容 十六进制: E8BF99E698AFE58685E7BDAE466C617368E69687E4BBB6E7B3BBE7BB9F415049E69687E6A1A3E7A4BAE4BE8BE79A84E6B58BE8AF95E58685E5AEB9 118
+[2025-10-22 16:04:28.192][000000000.340] I/user.启动计数 当前值: 0
+[2025-10-22 16:04:28.217][000000000.340] I/user.启动计数 更新值: 1
+[2025-10-22 16:04:28.221][000000000.345] I/user.文件写入 路径:/flash_demo/boottime 内容: 1
+[2025-10-22 16:04:28.234][000000000.357] I/user.文件创建 路径:/flash_demo/test_a 初始内容:ABC
+[2025-10-22 16:04:28.242][000000000.360] I/user.文件追加 路径:/flash_demo/test_a 追加内容:def
+[2025-10-22 16:04:28.248][000000000.363] I/user.文件验证 路径:/flash_demo/test_a 内容:ABCdef 结果: 成功
+[2025-10-22 16:04:28.253][000000000.367] I/user.文件创建 路径:/flash_demo/testline 写入3行文本
+[2025-10-22 16:04:28.271][000000000.370] I/user.按行读取 路径:/flash_demo/testline 第1行: abc
+[2025-10-22 16:04:28.279][000000000.370] I/user.按行读取 路径:/flash_demo/testline 第2行: 123
+[2025-10-22 16:04:28.302][000000000.371] I/user.按行读取 路径:/flash_demo/testline 第3行: wendal
+[2025-10-22 16:04:28.331][000000000.377] I/user.os.rename 文件重命名成功 原路径:/flash_demo/test_a 新路径:/flash_demo/renamed_file.txt
+[2025-10-22 16:04:28.347][000000000.383] D/vfs fopen /flash_demo/test_a r not found
+[2025-10-22 16:04:28.361][000000000.383] I/user.验证结果 重命名验证成功 新文件存在 原文件不存在
+[2025-10-22 16:04:28.382][000000000.383] I/user.目录操作 ===== 开始目录列举 =====
+[2025-10-22 16:04:28.393][000000000.396] I/user.fs lsdir [{"name":"boottime","size":1,"type":0},{"name":"renamed_file.txt","size":6,"type":0},{"name":"testline","size":15,"type":0}]
+[2025-10-22 16:04:28.417][000000000.400] I/user.os.remove 文件删除成功 路径:/flash_demo/renamed_file.txt
+[2025-10-22 16:04:28.435][000000000.403] D/vfs fopen /flash_demo/renamed_file.txt r not found
+[2025-10-22 16:04:28.449][000000000.403] I/user.验证结果 renamed_file.txt文件删除验证成功
+[2025-10-22 16:04:28.469][000000000.407] I/user.os.remove testline文件删除成功 路径:/flash_demo/testline
+[2025-10-22 16:04:28.479][000000000.410] D/vfs fopen /flash_demo/testline r not found
+[2025-10-22 16:04:28.489][000000000.411] I/user.验证结果 testline文件删除验证成功
+[2025-10-22 16:04:28.507][000000000.417] I/user.os.remove 文件删除成功 路径:/flash_demo/boottime
+[2025-10-22 16:04:28.514][000000000.420] D/vfs fopen /flash_demo/boottime r not found
+[2025-10-22 16:04:28.519][000000000.421] I/user.验证结果 boottime文件删除验证成功
+[2025-10-22 16:04:28.532][000000000.427] I/user.io.rmdir 目录删除成功 路径:/flash_demo
+[2025-10-22 16:04:28.538][000000000.430] I/user.验证结果 目录删除验证成功
+[2025-10-22 16:04:28.547][000000000.435] I/user. io.fsstat 操作后文件系统信息: 总空间=42块 已用=4块 块大小=4096字节 类型=lfs
+[2025-10-22 16:04:28.554][000000000.435] I/user.文件系统操作 ===== 文件系统操作完成 =====
+
+
+
+
+
+(2)网络连接与HTTP下载
+[2025-10-22 15:34:04.507][000000007.471] I/user.HTTP下载 网络已就绪 1 3
+[2025-10-22 15:34:04.550][000000007.471] I/user.HTTP下载 开始下载任务
+[2025-10-22 15:34:04.579][000000007.478] dns_run 676:gitee.com state 0 id 1 ipv6 0 use dns server2, try 0
+[2025-10-22 15:34:04.604][000000007.508] D/mobile TIME_SYNC 0
+[2025-10-22 15:34:04.734][000000007.517] dns_run 693:dns all done ,now stop
+[2025-10-22 15:34:06.390][000000009.741] I/user.HTTP下载 下载完成 success 200 
+[2025-10-22 15:34:06.422][000000009.741] {"Age":"0","Cache-Control":"public, max-age=60","Via":"1.1 varnish","Transfer-Encoding":"chunked","Date":"Wed, 22 Oct 2025 07:34:04 GMT","Access-Control-Allow-Credentials":"true","Vary":"Accept-Encoding","X-Served-By":"cache-ffe9","X-Gitee-Server":"http-pilot 1.9.21","Connection":"keep-alive","Server":"ADAS\/1.0.214","Access-Control-Allow-Headers":"Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With,X-CustomHeader,Content-Range,Range,Set-Language","Content-Security-Policy":"default-src 'none'; style-src 'unsafe-inline'; sandbox","X-Request-Id":"fa536af1-51bd-400f-8d4b-7322355a9db2","Accept-Ranges":"bytes","Etag":"W\/\"2aaa2788d394a924e258d6f26ad78b8c948950f5\"","Content-Type":"text\/plain; charset=utf-8","Access-Control-Allow-Methods":"GET, POST, PUT, PATCH, DELETE, OPTIONS","X-Frame-Options":"DENY","X-Cache":"MISS","Set-Cookie":"BEC=1f1759df3ccd099821dcf0da6feb0357;Path=\/;Max-Age=126000"}
+[2025-10-22 15:34:06.477][000000009.742]  103070
+[2025-10-22 15:34:06.492][000000009.745] I/user.HTTP下载 文件大小验证 预期: 103070 实际: 103070
+[2025-10-22 15:34:06.525][000000009.751] I/user.HTTP下载 下载后文件系统信息: 总空间=42块 已用=32块 块大小=4096字节 类型=lfs
+
+
+```

+ 0 - 3
module/Air8000/demo/accessory_board/AirETH_1000/http/http_app.lua

@@ -9,9 +9,6 @@
 本文件没有对外接口,直接在main.lua中require "http_app"就可以加载运行;
 ]]
 
-
-
-
 -- 普通的http get请求功能演示
 -- 请求的body数据保存到内存变量中,在内存够用的情况下,最大支持32KB的数据存储到内存中
 -- timeout可以设置超时时间

+ 1 - 1
module/Air8000/demo/accessory_board/AirETH_1000/network_routing/4g_out_ethernet_in_wifi_in/readme.md

@@ -16,7 +16,7 @@
 
 [](https://docs.openLuat.com/cdn/image/AirETH_1000.jpg)
 
-![lan](E:\文档池\新建文件夹\luatos-doc-pool\docs\root\docs\air8000\luatos\app\image\lan.jpg)
+![](https://docs.openLuat.com/cdn/image/AirETH_1000.jpg)
 
 2、TYPE-C USB数据线一根 + 杜邦线若干;
 

+ 282 - 0
module/Air8000/demo/fs_io/flash_fs_io.lua

@@ -0,0 +1,282 @@
+--[[
+@module  flash_fs_io
+@summary 内置Flash文件系统操作测试模块
+@version 1.0.0
+@date    2025.09.23
+@author  王棚嶙
+@usage
+本文件为内置Flash文件系统的操作测试流程:
+1. 获取文件系统信息( io.fsstat)
+2. 创建目录
+3. 创建并写入文件
+4. 检查文件是否存在
+5. 获取文件大小(io.fileSize)
+6. 读取文件内容
+7. 启动计数文件操作
+8. 文件追加测试
+9. 按行读取测试
+10. 文件重命名
+11. 列举目录内容
+12. 删除文件
+13. 删除目录
+本文件没有对外接口,直接在main.lua中require "flash_fs_io"就可以加载运行
+]]
+
+function flash_fs_io_task()
+    -- 使用内置Flash文件系统,根目录为"/",
+    local base_path = "/"
+    -- 创建一个目录
+    local demo_dir = "flash_demo"
+    -- 文件名
+    local dir_path = base_path .. demo_dir
+
+    -- ########## 开始进行内置Flash文件系统操作 ##########
+    log.info("文件系统操作", "===== 开始文件系统操作 =====")
+
+    -- 1. 获取文件系统信息 (使用 io.fsstat接口)
+    local success, total_blocks, used_blocks, block_size, fs_type  =  io.fsstat(base_path)
+    if success then
+        log.info(" io.fsstat成功:", 
+            "总空间=" .. total_blocks .. "块", 
+            "已用=" .. used_blocks .. "块", 
+            "块大小=" .. block_size.."字节",
+            "类型=" .. fs_type)
+    else
+        log.error(" io.fsstat", "获取文件系统信息失败")
+        return
+    end
+
+    -- 2. 创建目录
+    -- 如果目录不存在,则创建目录
+    if not io.dexist(dir_path) then
+        -- 创建目录
+        if io.mkdir(dir_path) then
+            log.info("io.mkdir", "目录创建成功", "路径:" .. dir_path)
+        else
+            log.error("io.mkdir", "目录创建失败", "路径:" .. dir_path)
+            return
+        end
+    else
+        log.warn("io.mkdir", "目录已存在,跳过创建", "路径:" .. dir_path)
+    end
+
+    -- 3. 创建并写入文件
+    local file_path = dir_path .. "/boottime"
+    local file = io.open(file_path, "wb")
+    if file then
+        file:write("这是内置Flash文件系统API文档示例的测试内容")
+        file:close()
+        log.info("文件创建", "文件写入成功", "路径:" .. file_path)
+    else
+        log.error("文件创建", "文件创建失败", "路径:" .. file_path)
+        return
+    end
+
+    -- 4. 检查文件是否存在
+    if io.exists(file_path) then
+        log.info("io.exists", "文件存在", "路径:" .. file_path)
+    else
+        log.error("io.exists", "文件不存在", "路径:" .. file_path)
+        return
+    end
+
+    -- 5. 获取文件大小 (使用io.fileSize接口)
+    local 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)
+        return
+    end
+
+    -- 6. 读取文件内容
+    file = io.open(file_path, "rb")
+    if file then
+        local content = file:read("*a")
+        log.info("文件读取", "路径:" .. file_path, "内容:" .. content)
+        file:close()
+    else
+        log.error("文件操作", "无法打开文件读取内容", "路径:" .. file_path)
+        return
+    end
+
+    -- 7. 启动计数文件操作
+    local count = 0
+    file = io.open(file_path, "rb")
+    if file then
+        local 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)
+        return
+    end
+
+    -- 8. 文件追加测试
+    local 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)
+        return
+    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)
+        return
+    end
+
+    -- 验证追加结果
+    file = io.open(append_file, "r")
+    if file then
+        local data = file:read("*a")
+        log.info("文件验证", "路径:" .. append_file, "内容:" .. data, "结果:",
+            data == "ABCdef" and "成功" or "失败")
+        file:close()
+    else
+        log.error("文件验证", "无法打开文件进行验证", "路径:" .. append_file)
+        return
+    end
+
+    -- 9. 按行读取测试
+    local 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)
+        return
+    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)
+        return
+    end
+
+    -- 10. 文件重命名
+    local old_path = append_file
+    local new_path = dir_path .. "/renamed_file.txt"
+    local 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)
+        return
+    end
+
+    -- 11. 列举目录内容
+    log.info("目录操作", "===== 开始目录列举 =====")
+
+    local ret, data = io.lsdir(dir_path, 50, 0)
+    if ret then
+        log.info("fs", "lsdir", json.encode(data))
+    else
+        log.info("fs", "lsdir", "fail", ret, data)
+        return
+    end
+
+    -- 12. 删除文件测试
+    if os.remove(new_path) then
+        log.info("os.remove", "文件删除成功", "路径:" .. new_path)
+        if not io.exists(new_path) then
+            log.info("验证结果", "renamed_file.txt文件删除验证成功")
+        else
+            log.error("验证结果", "renamed_file.txt文件删除验证失败")
+        end
+    else
+        log.error("os.remove", "renamed_file.txt文件删除失败", "路径:" .. new_path)
+        return
+    end
+
+    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("os.remove", "testline文件删除失败", "路径:" .. line_file)
+        return
+    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("os.remove", "boottime文件删除失败", "路径:" .. file_path)
+        return
+    end
+
+    -- 13. 删除目录
+    if io.rmdir(dir_path) then
+        log.info("io.rmdir", "目录删除成功", "路径:" .. dir_path)
+        if not io.dexist(dir_path) then
+            log.info("验证结果", "目录删除验证成功")
+        else
+            log.error("验证结果", "目录删除验证失败")
+        end
+    else
+        log.error("io.rmdir", "目录删除失败", "路径:" .. dir_path)
+        return
+    end
+
+    -- 再次获取文件系统信息,查看空间变化
+    local final_success, final_total_blocks, final_used_blocks, final_block_size, final_fs_type =  io.fsstat(base_path)
+    if final_success then
+        log.info(" io.fsstat", "操作后文件系统信息:", 
+                 "总空间=" .. final_total_blocks .. "块", 
+                 "已用=" .. final_used_blocks .. "块", 
+                 "块大小=" .. final_block_size.."字节",
+                 "类型=" .. final_fs_type)
+    end
+
+    log.info("文件系统操作", "===== 文件系统操作完成 =====")
+end
+
+sys.taskInit(flash_fs_io_task)

+ 74 - 0
module/Air8000/demo/fs_io/http_download_flash.lua

@@ -0,0 +1,74 @@
+--[[
+@module http_download_flash
+@summary HTTP下载文件到内置Flash模块
+@version 1.0.0
+@date    2025.09.23
+@author  王棚嶙
+@usage
+本文件演示的功能为通过HTTP下载文件到内置Flash中:
+1. 网络就绪检测
+2. 创建HTTP下载任务并等待完成
+3. 记录下载结果
+4. 获取并记录文件大小(使用io.fileSize)
+本文件没有对外接口,直接在main.lua中require "http_download_flash"即可
+]]
+
+local function http_download_flash_task()
+    -- 阶段1: 网络就绪检测
+    while not socket.adapter(socket.dft()) do
+        log.warn("HTTP下载", "等待网络连接", socket.dft())
+        sys.waitUntil("IP_READY", 1000)
+    end
+
+    log.info("HTTP下载", "网络已就绪", socket.dft())
+
+    -- 阶段2: 执行下载任务
+    log.info("HTTP下载", "开始下载任务")
+
+    -- 创建下载目录
+    local download_dir = "/downloads"
+    if not io.dexist(download_dir) then
+        io.mkdir(download_dir)
+    end
+
+    -- 核心下载操作开始
+    local code, headers, body_size = http.request("GET",
+                                    "https://gitee.com/openLuat/LuatOS/raw/master/module/Air780EHM_Air780EHV_Air780EGH/demo/audio/sample-6s.mp3",
+                                    nil, nil, {dst = download_dir .. "/sample-6s.mp3"}).wait()
+
+    -- 阶段3: 记录下载结果
+    log.info("HTTP下载", "下载完成", 
+        code == 200 and "success" or "error", 
+        code, 
+        json.encode(headers or {}), 
+        body_size) 
+        
+    if code == 200 then
+        -- 获取实际文件大小 (使用io.fileSize接口)
+        local actual_size = io.fileSize(download_dir .. "/sample-6s.mp3")
+        if not actual_size then
+            -- 备用方案
+            actual_size = io.fileSize(download_dir .. "/sample-6s.mp3")
+        end
+        
+        log.info("HTTP下载", "文件大小验证", "预期:", body_size, "实际:", actual_size)
+        
+        if actual_size ~= body_size then
+            log.error("HTTP下载", "文件大小不一致", "预期:", body_size, "实际:", actual_size)
+        end
+        
+        -- 展示下载后的文件系统状态
+        local success, total_blocks, used_blocks, block_size, fs_type =  io.fsstat("/")
+        if success then
+            log.info("HTTP下载", "下载后文件系统信息:", 
+                     "总空间=" .. total_blocks .. "块", 
+                     "已用=" .. used_blocks .. "块", 
+                     "块大小=" .. block_size.."字节",
+                     "类型=" .. fs_type)
+        end
+    end
+
+end
+
+-- 创建下载任务
+sys.taskInit(http_download_flash_task)

+ 78 - 0
module/Air8000/demo/fs_io/main.lua

@@ -0,0 +1,78 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 001.000.000
+@date    2025.09.23
+@author  王棚嶙
+@usage
+本 Demo 演示了在Air8000内置Flash文件系统中的完整操作流程:
+1. 基础操作:看门狗守护机制
+2. 文件系统操作:
+   - 文件系统信息查询( io.fsstat)
+   - 文件大小获取(io.fileSize)
+   - 文件创建/读写/追加
+   - 目录创建/删除
+   - 文件重命名/删除
+   - 文件存在性检查
+3. 下载功能:
+   - 网络检测与HTTP文件下载到内置Flash
+]]
+
+--[[
+必须定义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 = "flash_fs_io_demo"
+VERSION = "001.000.000"
+
+-- 在日志中打印项目名和项目版本号
+log.info("main", PROJECT, VERSION)
+
+-- 添加硬狗防止程序卡死
+if wdt then
+    -- 初始化watchdog设置为9s
+    wdt.init(9000)
+    -- 3s喂一次狗 
+    sys.timerLoopStart(wdt.feed, 3000) 
+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下载功能是异步操作。放到一个项目中,如果加载的时间点是随机的,就会出现哪个任务先抢到CPU时间片,哪个就先执行,不符合正常的业务逻辑,用户在参考编程的时候也要注意。]]
+
+-- 加载内置Flash文件系统操作演示模块
+require "flash_fs_io"
+-- 加载HTTP下载存入内置Flash功能演示模块
+-- require "http_download_flash"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+-- sys.run()之后后面不要加任何语句!!!!!
+sys.run()

+ 139 - 0
module/Air8000/demo/fs_io/readme.md

@@ -0,0 +1,139 @@
+## **功能模块介绍**
+
+本 Demo 演示了在Air8000内置Flash文件系统中的完整操作流程,覆盖了从文件系统读写到高级文件操作的完整功能链。项目分为两个核心模块:
+
+1、main.lua:主程序入口 <br> 
+2、flash_fs_io.lua:内置Flash文件系统的操作测试流程模块,实现文件系统管理、文件操作和目录管理功能。<br> 
+3、http_download_flash.lua:HTTP下载模块,演示HTTP下载文件到内置Flash中的功能
+
+## **演示功能概述**
+
+### 1、主程序入口模块(main.lua)
+
+- 初始化项目信息和版本号
+- 初始化看门狗,并定时喂狗
+- 启动一个循环定时器,每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况方便分析内存使用是否有异常
+- 加载flash_fs_io模块(通过require "flash_fs_io")
+- 加载http_download_flash模块(通过require "http_download_flash")
+- 最后运行sys.run()。
+
+### 2、内置Flash文件系统演示模块(flash_fs_io.lua)
+
+#### 文件操作
+- 获取文件系统信息( io.fsstat)
+- 创建目录:io.mkdir("/flash_demo")
+- 创建/写入文件: io.open("/flash_demo/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)
+
+### 3、HTTP下载功能 (http_download_flash.lua)
+
+
+#### 网络就绪检测
+
+- 1秒循环等待IP就绪
+- 网络故障处理机制
+
+#### 安全下载
+
+- HTTP下载
+
+#### 结果处理
+
+- 下载状态码解析
+- 自动文件大小验证
+- 获取文件系统信息( io.fsstat)
+
+## **演示硬件环境**
+
+1、Air8000整机开发板一块
+
+2、sim卡一张
+
+3、TYPE-C USB数据线一根
+
+4、Air8000整机开发板和数据线的硬件接线方式为
+
+- Air8000整机开发板通过TYPE-C USB口供电;(USB旁边的开关拨到USB供电)
+- TYPE-C USB数据线直接插到Air8000整机开发板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+## **演示软件环境**
+
+1、Luatools下载调试工具:https://docs.openluat.com/air780epm/common/Luatools/
+
+2、内核固件版本:https://docs.openluat.com/air8000/luatos/firmware/
+
+## **演示核心步骤**
+
+1、搭建好硬件环境
+
+2、通过Luatools将demo与固件烧录到开发板中
+
+3、烧录好后,板子开机将会在Luatools上看到如下打印
+
+```lua
+
+(1)文件操作演示
+[2025-10-22 15:23:25.096][000000000.595] I/user.文件系统操作 ===== 开始文件系统操作 =====
+[2025-10-22 15:23:25.104][000000000.601] I/user. io.fsstat成功: 总空间=192块 已用=20块 块大小=4096字节 类型=lfs
+[2025-10-22 15:23:25.118][000000000.644] I/user.io.mkdir 目录创建成功 路径:/flash_demo
+[2025-10-22 15:23:25.127][000000000.648] I/user.文件创建 文件写入成功 路径:/flash_demo/boottime
+[2025-10-22 15:23:25.145][000000000.651] I/user.io.exists 文件存在 路径:/flash_demo/boottime
+[2025-10-22 15:23:25.157][000000000.654] I/user.io.fileSize 文件大小:59字节 路径:/flash_demo/boottime
+[2025-10-22 15:23:25.165][000000000.657] I/user.文件读取 路径:/flash_demo/boottime 内容:这是内置Flash文件系统API文档示例的测试内容
+[2025-10-22 15:23:25.181][000000000.660] I/user.启动计数 文件内容: 这是内置Flash文件系统API文档示例的测试内容 十六进制: E8BF99E698AFE58685E7BDAE466C617368E69687E4BBB6E7B3BBE7BB9F415049E69687E6A1A3E7A4BAE4BE8BE79A84E6B58BE8AF95E58685E5AEB9 118
+[2025-10-22 15:23:25.189][000000000.660] I/user.启动计数 当前值: 0
+[2025-10-22 15:23:25.211][000000000.660] I/user.启动计数 更新值: 1
+[2025-10-22 15:23:25.217][000000000.663] I/user.文件写入 路径:/flash_demo/boottime 内容: 1
+[2025-10-22 15:23:25.224][000000000.669] I/user.文件创建 路径:/flash_demo/test_a 初始内容:ABC
+[2025-10-22 15:23:25.245][000000000.672] I/user.文件追加 路径:/flash_demo/test_a 追加内容:def
+[2025-10-22 15:23:25.251][000000000.675] I/user.文件验证 路径:/flash_demo/test_a 内容:ABCdef 结果: 成功
+[2025-10-22 15:23:25.267][000000000.678] I/user.文件创建 路径:/flash_demo/testline 写入3行文本
+[2025-10-22 15:23:25.277][000000000.681] I/user.按行读取 路径:/flash_demo/testline 第1行: abc
+[2025-10-22 15:23:25.285][000000000.682] I/user.按行读取 路径:/flash_demo/testline 第2行: 123
+[2025-10-22 15:23:25.295][000000000.682] I/user.按行读取 路径:/flash_demo/testline 第3行: wendal
+[2025-10-22 15:23:25.301][000000000.689] I/user.os.rename 文件重命名成功 原路径:/flash_demo/test_a 新路径:/flash_demo/renamed_file.txt
+[2025-10-22 15:23:25.312][000000000.694] D/vfs fopen /flash_demo/test_a r not found
+[2025-10-22 15:23:25.321][000000000.694] I/user.验证结果 重命名验证成功 新文件存在 原文件不存在
+[2025-10-22 15:23:25.340][000000000.695] I/user.目录操作 ===== 开始目录列举 =====
+[2025-10-22 15:23:25.348][000000000.706] I/user.fs lsdir [{"name":"boottime","size":1,"type":0},{"name":"renamed_file.txt","size":6,"type":0},{"name":"testline","size":15,"type":0}]
+[2025-10-22 15:23:25.359][000000000.710] I/user.os.remove 文件删除成功 路径:/flash_demo/renamed_file.txt
+[2025-10-22 15:23:25.366][000000000.713] D/vfs fopen /flash_demo/renamed_file.txt r not found
+[2025-10-22 15:23:25.373][000000000.713] I/user.验证结果 renamed_file.txt文件删除验证成功
+[2025-10-22 15:23:25.378][000000000.716] I/user.os.remove testline文件删除成功 路径:/flash_demo/testline
+[2025-10-22 15:23:25.390][000000000.719] D/vfs fopen /flash_demo/testline r not found
+[2025-10-22 15:23:25.395][000000000.719] I/user.验证结果 testline文件删除验证成功
+[2025-10-22 15:23:25.415][000000000.723] I/user.os.remove 文件删除成功 路径:/flash_demo/boottime
+[2025-10-22 15:23:25.423][000000000.726] D/vfs fopen /flash_demo/boottime r not found
+[2025-10-22 15:23:25.444][000000000.726] I/user.验证结果 boottime文件删除验证成功
+[2025-10-22 15:23:25.453][000000000.732] I/user.io.rmdir 目录删除成功 路径:/flash_demo
+[2025-10-22 15:23:25.464][000000000.734] I/user.验证结果 目录删除验证成功
+[2025-10-22 15:23:25.477][000000000.740] I/user. io.fsstat 操作后文件系统信息: 总空间=192块 已用=20块 块大小=4096字节 类型=lfs
+[2025-10-22 15:23:25.484][000000000.740] I/user.文件系统操作 ===== 文件系统操作完成 =====
+
+
+
+
+(2)网络连接与HTTP下载
+[2025-10-22 15:34:04.507][000000007.471] I/user.HTTP下载 网络已就绪 1 3
+[2025-10-22 15:34:04.550][000000007.471] I/user.HTTP下载 开始下载任务
+[2025-10-22 15:34:04.579][000000007.478] dns_run 676:gitee.com state 0 id 1 ipv6 0 use dns server2, try 0
+[2025-10-22 15:34:04.604][000000007.508] D/mobile TIME_SYNC 0
+[2025-10-22 15:34:04.734][000000007.517] dns_run 693:dns all done ,now stop
+[2025-10-22 15:34:06.390][000000009.741] I/user.HTTP下载 下载完成 success 200 
+[2025-10-22 15:34:06.422][000000009.741] {"Age":"0","Cache-Control":"public, max-age=60","Via":"1.1 varnish","Transfer-Encoding":"chunked","Date":"Wed, 22 Oct 2025 07:34:04 GMT","Access-Control-Allow-Credentials":"true","Vary":"Accept-Encoding","X-Served-By":"cache-ffe9","X-Gitee-Server":"http-pilot 1.9.21","Connection":"keep-alive","Server":"ADAS\/1.0.214","Access-Control-Allow-Headers":"Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With,X-CustomHeader,Content-Range,Range,Set-Language","Content-Security-Policy":"default-src 'none'; style-src 'unsafe-inline'; sandbox","X-Request-Id":"fa536af1-51bd-400f-8d4b-7322355a9db2","Accept-Ranges":"bytes","Etag":"W\/\"2aaa2788d394a924e258d6f26ad78b8c948950f5\"","Content-Type":"text\/plain; charset=utf-8","Access-Control-Allow-Methods":"GET, POST, PUT, PATCH, DELETE, OPTIONS","X-Frame-Options":"DENY","X-Cache":"MISS","Set-Cookie":"BEC=1f1759df3ccd099821dcf0da6feb0357;Path=\/;Max-Age=126000"}
+[2025-10-22 15:34:06.477][000000009.742]  103070
+[2025-10-22 15:34:06.492][000000009.745] I/user.HTTP下载 文件大小验证 预期: 103070 实际: 103070
+[2025-10-22 15:34:06.525][000000009.751] I/user.HTTP下载 下载后文件系统信息: 总空间=192块 已用=46块 块大小=4096字节 类型=lfs
+
+
+```

+ 282 - 0
module/Air8101/demo/fs_io/flash_fs_io.lua

@@ -0,0 +1,282 @@
+--[[
+@module  flash_fs_io
+@summary 内置Flash文件系统操作测试模块
+@version 1.0.0
+@date    2025.09.23
+@author  王棚嶙
+@usage
+本文件为内置Flash文件系统的操作测试流程:
+1. 获取文件系统信息( io.fsstat)
+2. 创建目录
+3. 创建并写入文件
+4. 检查文件是否存在
+5. 获取文件大小(io.fileSize)
+6. 读取文件内容
+7. 启动计数文件操作
+8. 文件追加测试
+9. 按行读取测试
+10. 文件重命名
+11. 列举目录内容
+12. 删除文件
+13. 删除目录
+本文件没有对外接口,直接在main.lua中require "flash_fs_io"就可以加载运行
+]]
+
+function flash_fs_io_task()
+    -- 使用内置Flash文件系统,根目录为"/",
+    local base_path = "/"
+    -- 创建一个目录
+    local demo_dir = "flash_demo"
+    -- 文件名
+    local dir_path = base_path .. demo_dir
+
+    -- ########## 开始进行内置Flash文件系统操作 ##########
+    log.info("文件系统操作", "===== 开始文件系统操作 =====")
+
+    -- 1. 获取文件系统信息 (使用 io.fsstat接口)
+    local success, total_blocks, used_blocks, block_size, fs_type  =  io.fsstat(base_path)
+    if success then
+        log.info(" io.fsstat成功:", 
+            "总空间=" .. total_blocks .. "块", 
+            "已用=" .. used_blocks .. "块", 
+            "块大小=" .. block_size.."字节",
+            "类型=" .. fs_type)
+    else
+        log.error(" io.fsstat", "获取文件系统信息失败")
+        return
+    end
+
+    -- 2. 创建目录
+    -- 如果目录不存在,则创建目录
+    if not io.dexist(dir_path) then
+        -- 创建目录
+        if io.mkdir(dir_path) then
+            log.info("io.mkdir", "目录创建成功", "路径:" .. dir_path)
+        else
+            log.error("io.mkdir", "目录创建失败", "路径:" .. dir_path)
+            return
+        end
+    else
+        log.warn("io.mkdir", "目录已存在,跳过创建", "路径:" .. dir_path)
+    end
+
+    -- 3. 创建并写入文件
+    local file_path = dir_path .. "/boottime"
+    local file = io.open(file_path, "wb")
+    if file then
+        file:write("这是内置Flash文件系统API文档示例的测试内容")
+        file:close()
+        log.info("文件创建", "文件写入成功", "路径:" .. file_path)
+    else
+        log.error("文件创建", "文件创建失败", "路径:" .. file_path)
+        return
+    end
+
+    -- 4. 检查文件是否存在
+    if io.exists(file_path) then
+        log.info("io.exists", "文件存在", "路径:" .. file_path)
+    else
+        log.error("io.exists", "文件不存在", "路径:" .. file_path)
+        return
+    end
+
+    -- 5. 获取文件大小 (使用io.fileSize接口)
+    local 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)
+        return
+    end
+
+    -- 6. 读取文件内容
+    file = io.open(file_path, "rb")
+    if file then
+        local content = file:read("*a")
+        log.info("文件读取", "路径:" .. file_path, "内容:" .. content)
+        file:close()
+    else
+        log.error("文件操作", "无法打开文件读取内容", "路径:" .. file_path)
+        return
+    end
+
+    -- 7. 启动计数文件操作
+    local count = 0
+    file = io.open(file_path, "rb")
+    if file then
+        local 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)
+        return
+    end
+
+    -- 8. 文件追加测试
+    local 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)
+        return
+    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)
+        return
+    end
+
+    -- 验证追加结果
+    file = io.open(append_file, "r")
+    if file then
+        local data = file:read("*a")
+        log.info("文件验证", "路径:" .. append_file, "内容:" .. data, "结果:",
+            data == "ABCdef" and "成功" or "失败")
+        file:close()
+    else
+        log.error("文件验证", "无法打开文件进行验证", "路径:" .. append_file)
+        return
+    end
+
+    -- 9. 按行读取测试
+    local 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)
+        return
+    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)
+        return
+    end
+
+    -- 10. 文件重命名
+    local old_path = append_file
+    local new_path = dir_path .. "/renamed_file.txt"
+    local 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)
+        return
+    end
+
+    -- 11. 列举目录内容
+    log.info("目录操作", "===== 开始目录列举 =====")
+
+    local ret, data = io.lsdir(dir_path, 50, 0)
+    if ret then
+        log.info("fs", "lsdir", json.encode(data))
+    else
+        log.info("fs", "lsdir", "fail", ret, data)
+        return
+    end
+
+    -- 12. 删除文件测试
+    if os.remove(new_path) then
+        log.info("os.remove", "文件删除成功", "路径:" .. new_path)
+        if not io.exists(new_path) then
+            log.info("验证结果", "renamed_file.txt文件删除验证成功")
+        else
+            log.error("验证结果", "renamed_file.txt文件删除验证失败")
+        end
+    else
+        log.error("os.remove", "renamed_file.txt文件删除失败", "路径:" .. new_path)
+        return
+    end
+
+    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("os.remove", "testline文件删除失败", "路径:" .. line_file)
+        return
+    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("os.remove", "boottime文件删除失败", "路径:" .. file_path)
+        return
+    end
+
+    -- 13. 删除目录
+    if io.rmdir(dir_path) then
+        log.info("io.rmdir", "目录删除成功", "路径:" .. dir_path)
+        if not io.dexist(dir_path) then
+            log.info("验证结果", "目录删除验证成功")
+        else
+            log.error("验证结果", "目录删除验证失败")
+        end
+    else
+        log.error("io.rmdir", "目录删除失败", "路径:" .. dir_path)
+        return
+    end
+
+    -- 再次获取文件系统信息,查看空间变化
+    local final_success, final_total_blocks, final_used_blocks, final_block_size, final_fs_type =  io.fsstat(base_path)
+    if final_success then
+        log.info(" io.fsstat", "操作后文件系统信息:", 
+                 "总空间=" .. final_total_blocks .. "块", 
+                 "已用=" .. final_used_blocks .. "块", 
+                 "块大小=" .. final_block_size.."字节",
+                 "类型=" .. final_fs_type)
+    end
+
+    log.info("文件系统操作", "===== 文件系统操作完成 =====")
+end
+
+sys.taskInit(flash_fs_io_task)

+ 85 - 0
module/Air8101/demo/fs_io/http_download_flash.lua

@@ -0,0 +1,85 @@
+--[[
+@module http_download_flash
+@summary HTTP下载文件到内置Flash模块
+@version 1.0.0
+@date    2025.09.23
+@author  王棚嶙
+@usage
+本文件演示的功能为通过HTTP下载文件到内置Flash中:
+1. 网络就绪检测
+2. 创建HTTP下载任务并等待完成
+3. 记录下载结果
+4. 获取并记录文件大小(使用io.fileSize)
+本文件没有对外接口,直接在main.lua中require "http_download_flash"即可
+]]
+
+local function http_download_flash_task()
+
+    
+    -- 要连接的WIFI路由器名称
+    local ssid ="A上海合宙通讯"
+    -- 要连接的WIFI路由器密码
+    local password = "HZ88888888"
+    log.info("wifi", ssid, password)
+    wlan.init()
+    -- 连接WIFI
+    wlan.connect(ssid, password, 1)
+
+    -- 阶段1: 网络就绪检测
+    while not socket.adapter(socket.dft()) do
+        log.warn("HTTP下载", "等待网络连接", socket.dft())
+        sys.waitUntil("IP_READY", 1000)
+    end
+
+    log.info("HTTP下载", "网络已就绪", socket.dft())
+
+    -- 阶段2: 执行下载任务
+    log.info("HTTP下载", "开始下载任务")
+
+    -- 创建下载目录
+    local download_dir = "/downloads"
+    if not io.dexist(download_dir) then
+        io.mkdir(download_dir)
+    end
+
+    -- 核心下载操作开始
+    local code, headers, body_size = http.request("GET",
+                                    "https://www.air32.cn/demo/sample-6s.mp3",
+                                    nil, nil, {dst = download_dir .. "/sample-6s.mp3"}).wait()
+
+    -- 阶段3: 记录下载结果
+    log.info("HTTP下载", "下载完成", 
+        code == 200 and "success" or "error", 
+        code, 
+        json.encode(headers or {}), 
+        body_size) 
+        
+    if code == 200 then
+        -- 获取实际文件大小 (使用io.fileSize接口)
+        local actual_size = io.fileSize(download_dir .. "/sample-6s.mp3")
+        if not actual_size then
+            -- 备用方案
+            actual_size = io.fileSize(download_dir .. "/sample-6s.mp3")
+        end
+        
+        log.info("HTTP下载", "文件大小验证", "预期:", body_size, "实际:", actual_size)
+        
+        if actual_size ~= body_size then
+            log.error("HTTP下载", "文件大小不一致", "预期:", body_size, "实际:", actual_size)
+        end
+        
+        -- 展示下载后的文件系统状态
+        local success, total_blocks, used_blocks, block_size, fs_type =  io.fsstat("/")
+        if success then
+            log.info("HTTP下载", "下载后文件系统信息:", 
+                     "总空间=" .. total_blocks .. "块", 
+                     "已用=" .. used_blocks .. "块", 
+                     "块大小=" .. block_size.."字节",
+                     "类型=" .. fs_type)
+        end
+    end
+
+end
+
+-- 创建下载任务
+sys.taskInit(http_download_flash_task)

+ 78 - 0
module/Air8101/demo/fs_io/main.lua

@@ -0,0 +1,78 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 001.000.000
+@date    2025.09.23
+@author  王棚嶙
+@usage
+本 Demo 演示了在Air8101内置Flash文件系统中的完整操作流程:
+1. 基础操作:看门狗守护机制
+2. 文件系统操作:
+   - 文件系统信息查询( io.fsstat)
+   - 文件大小获取(io.fileSize)
+   - 文件创建/读写/追加
+   - 目录创建/删除
+   - 文件重命名/删除
+   - 文件存在性检查
+3. 下载功能:
+   - 网络检测与HTTP文件下载到内置Flash
+]]
+
+--[[
+必须定义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 = "flash_fs_io_demo"
+VERSION = "001.000.000"
+
+-- 在日志中打印项目名和项目版本号
+log.info("main", PROJECT, VERSION)
+
+-- 添加硬狗防止程序卡死
+if wdt then
+    -- 初始化watchdog设置为9s
+    wdt.init(9000)
+    -- 3s喂一次狗 
+    sys.timerLoopStart(wdt.feed, 3000) 
+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下载功能是异步操作。放到一个项目中,如果加载的时间点是随机的,就会出现哪个任务先抢到CPU时间片,哪个就先执行,不符合正常的业务逻辑,用户在参考编程的时候也要注意。]]
+
+-- 加载内置Flash文件系统操作演示模块
+require "flash_fs_io"
+-- 加载HTTP下载存入内置Flash功能演示模块
+-- require "http_download_flash"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+-- sys.run()之后后面不要加任何语句!!!!!
+sys.run()

+ 143 - 0
module/Air8101/demo/fs_io/readme.md

@@ -0,0 +1,143 @@
+## **功能模块介绍**
+
+本 Demo 演示了在Air8101内置Flash文件系统中的完整操作流程,覆盖了从文件系统读写到高级文件操作的完整功能链。项目分为两个核心模块:
+
+1、main.lua:主程序入口 <br> 
+2、flash_fs_io.lua:内置Flash文件系统的操作测试流程模块,实现文件系统管理、文件操作和目录管理功能。<br> 
+3、http_download_flash.lua:HTTP下载模块,演示HTTP下载文件到内置Flash中的功能
+
+## **演示功能概述**
+
+### 1、主程序入口模块(main.lua)
+
+- 初始化项目信息和版本号
+- 初始化看门狗,并定时喂狗
+- 启动一个循环定时器,每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况方便分析内存使用是否有异常
+- 加载flash_fs_io模块(通过require "flash_fs_io")
+- 加载http_download_flash模块(通过require "http_download_flash")
+- 最后运行sys.run()。
+
+### 2、内置Flash文件系统演示模块(flash_fs_io.lua)
+
+#### 文件操作
+- 获取文件系统信息( io.fsstat)
+- 创建目录:io.mkdir("/flash_demo")
+- 创建/写入文件: io.open("/flash_demo/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)
+
+### 3、HTTP下载功能 (http_download_flash.lua)
+
+
+#### 网络就绪检测
+
+- 连接WiFi
+- 1秒循环等待IP就绪
+- 网络故障处理机制
+
+#### 安全下载
+
+- HTTP下载
+
+#### 结果处理
+
+- 下载状态码解析
+- 自动文件大小验证
+- 获取文件系统信息( io.fsstat)
+
+## **演示硬件环境**
+
+1、Air8101核心板一块
+
+2、TYPE-C USB数据线一根
+
+3、Air8101核心板和数据线的硬件接线方式为
+
+- Air8101核心板通过TYPE-C USB口供电;(正面的开关拨到3.3v,背面的开关拨到off)
+
+- TYPE-C USB数据线直接插到Air8101核心板的TYPE-C USB座子,另外一端连接电脑USB口
+
+## **演示软件环境**
+
+1、Luatools下载调试工具:https://docs.openluat.com/air780epm/common/Luatools/
+
+2、内核固件版本:https://docs.openluat.com/air8101/luatos/firmware/
+
+## **演示核心步骤**
+
+1、搭建好硬件环境
+
+2、通过Luatools将demo与固件烧录到开发板中
+
+3、烧录好后,板子开机将会在Luatools上看到如下打印
+
+```lua
+
+(1)文件操作演示
+[2025-10-22 16:32:47.705] luat:U(172):I/user.文件系统操作 ===== 开始文件系统操作 =====
+[2025-10-22 16:32:47.705] luat:U(174):I/user. io.fsstat成功: 总空间=64块 已用=2块 块大小=4096字节 类型=lfs
+[2025-10-22 16:32:47.767] luat:U(215):I/user.io.mkdir 目录创建成功 路径:/flash_demo
+[2025-10-22 16:32:47.767] luat:U(224):I/user.文件创建 文件写入成功 路径:/flash_demo/boottime
+[2025-10-22 16:32:47.767] luat:U(226):I/user.io.exists 文件存在 路径:/flash_demo/boottime
+[2025-10-22 16:32:47.767] luat:U(228):I/user.io.fileSize 文件大小:59字节 路径:/flash_demo/boottime
+[2025-10-22 16:32:47.767] luat:U(230):I/user.文件读取 路径:/flash_demo/boottime 内容:这是内置Flash文件系统API文档示例的测试内容
+[2025-10-22 16:32:47.767] luat:U(232):I/user.启动计数 文件内容: 这是内置Flash文件系统API文档示例的测试内容 十六进制: E8BF99E698AFE58685E7BDAE466C617368E69687E4BBB6E7B3BBE7BB9F415049E69687E6A1A3E7A4BAE4BE8BE79A84E6B58BE8AF95E58685E5AEB9 118
+[2025-10-22 16:32:47.767] luat:U(233):I/user.启动计数 当前值: 0
+[2025-10-22 16:32:47.767] luat:U(233):I/user.启动计数 更新值: 1
+[2025-10-22 16:32:47.767] luat:U(238):I/user.文件写入 路径:/flash_demo/boottime 内容: 1
+[2025-10-22 16:32:47.831] luat:U(249):I/user.文件创建 路径:/flash_demo/test_a 初始内容:ABC
+[2025-10-22 16:32:47.831] luat:U(255):I/user.文件追加 路径:/flash_demo/test_a 追加内容:def
+[2025-10-22 16:32:47.831] luat:U(257):I/user.文件验证 路径:/flash_demo/test_a 内容:ABCdef 结果: 成功
+[2025-10-22 16:32:47.831] luat:U(266):I/user.文件创建 路径:/flash_demo/testline 写入3行文本
+[2025-10-22 16:32:47.831] luat:U(268):I/user.按行读取 路径:/flash_demo/testline 第1行: abc
+[2025-10-22 16:32:47.831] luat:U(269):I/user.按行读取 路径:/flash_demo/testline 第2行: 123
+[2025-10-22 16:32:47.831] luat:U(269):I/user.按行读取 路径:/flash_demo/testline 第3行: wendal
+[2025-10-22 16:32:47.831] luat:U(277):I/user.os.rename 文件重命名成功 原路径:/flash_demo/test_a 新路径:/flash_demo/renamed_file.txt
+[2025-10-22 16:32:47.831] luat:D(281):vfs:fopen /flash_demo/test_a r not found
+[2025-10-22 16:32:47.831] luat:U(281):I/user.验证结果 重命名验证成功 新文件存在 原文件不存在
+[2025-10-22 16:32:47.831] luat:U(282):I/user.目录操作 ===== 开始目录列举 =====
+[2025-10-22 16:32:47.831] luat:U(291):I/user.fs lsdir [{"name":"boottime","size":1,"type":0},{"name":"renamed_file.txt","size":6,"type":0},{"name":"testline","size":15,"type":0}]
+[2025-10-22 16:32:47.831] luat:U(297):I/user.os.remove 文件删除成功 路径:/flash_demo/renamed_file.txt
+[2025-10-22 16:32:47.831] luat:D(299):vfs:fopen /flash_demo/renamed_file.txt r not found
+[2025-10-22 16:32:47.831] luat:U(299):I/user.验证结果 renamed_file.txt文件删除验证成功
+[2025-10-22 16:32:47.831] luat:U(305):I/user.os.remove testline文件删除成功 路径:/flash_demo/testline
+[2025-10-22 16:32:47.831] luat:D(307):vfs:fopen /flash_demo/testline r not found
+[2025-10-22 16:32:47.831] luat:U(307):I/user.验证结果 testline文件删除验证成功
+[2025-10-22 16:32:47.831] luat:U(313):I/user.os.remove 文件删除成功 路径:/flash_demo/boottime
+[2025-10-22 16:32:47.831] luat:D(315):vfs:fopen /flash_demo/boottime r not found
+[2025-10-22 16:32:47.831] luat:U(316):I/user.验证结果 boottime文件删除验证成功
+[2025-10-22 16:32:47.988] luat:U(326):I/user.io.rmdir 目录删除成功 路径:/flash_demo
+[2025-10-22 16:32:47.988] luat:U(327):I/user.验证结果 目录删除验证成功
+[2025-10-22 16:32:47.988] luat:U(329):I/user. io.fsstat 操作后文件系统信息: 总空间=64块 已用=2块 块大小=4096字节 类型=lfs
+[2025-10-22 16:32:47.988] luat:U(330):I/user.文件系统操作 ===== 文件系统操作完成 =====
+
+
+
+
+
+(2)网络连接与HTTP下载
+[2025-10-24 10:51:10.116] luat:U(2895):I/user.HTTP下载 网络已就绪 2 2
+[2025-10-24 10:51:10.116] luat:U(2896):I/user.HTTP下载 开始下载任务
+[2025-10-24 10:51:10.202] luat:D(2967):DNS:www.air32.cn state 0 id 1 ipv6 0 use dns server0, try 0
+[2025-10-24 10:51:10.202] luat:D(2967):net:adatper 2 dns server 192.168.1.1
+[2025-10-24 10:51:10.202] luat:D(2968):net:dns udp sendto 192.168.1.1:53 from 192.168.1.116
+[2025-10-24 10:51:10.202] luat:D(2971):wlan:sta ip 192.168.1.116
+[2025-10-24 10:51:10.202] luat:D(2971):wlan:设置STA网卡可用
+[2025-10-24 10:51:10.222] luat:I(2989):DNS:dns all done ,now stop
+[2025-10-24 10:51:10.222] luat:D(2990):net:adapter 2 connect 49.232.89.122:443 TCP
+[2025-10-24 10:51:14.599] luat:U(7368):I/user.HTTP下载 下载完成 success 200 {"Last-Modified":"Thu, 23 Oct 2025 03:14:41 GMT","Accept-Ranges":"bytes","ETag":"\"68f99da1-1929e\"","Date":"Fri, 24 Oct 2025 02:51:09 GMT","Connection":"keep-alive","Server":"openresty\/1.27.1.2","Content-Length":"103070","Content-Type":"audio\/mpeg"} 103070
+[2025-10-24 10:51:14.599] luat:U(7384):I/user.HTTP下载 文件大小验证 预期: 103070 实际: 103070
+[2025-10-24 10:51:14.630] luat:U(7409):I/user.HTTP下载 下载后文件系统信息: 总空间=64块 已用=30块 块大小=4096字节 类型=lfs
+
+
+
+```