Explorar o código

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

alienwalker hai 8 meses
pai
achega
4c6bdc8da0
Modificáronse 52 ficheiros con 1616 adicións e 556 borrados
  1. 19 0
      components/airlink/src/exec/luat_airlink_bt_exec_task.c
  2. 10 0
      components/airlink/src/exec/luat_airlink_cmd_exec_bluetooth.c
  3. 2 0
      components/airlink/src/luat_airlink_cmds.c
  4. 7 1
      components/bluetooth/include/luat_drv_ble.h
  5. 3 2
      components/bluetooth/src/luat_lib_ble.c
  6. 6 0
      components/fskv/luat_fskv.c
  7. 3 0
      components/wlan/luat_lib_wlan.c
  8. 4 4
      luat/demo/airlink/air8000_wifi/main.lua
  9. 7 22
      luat/demo/ftp/main.lua
  10. 0 2
      luat/modules/luat_lib_gpio.c
  11. 123 54
      luat/vfs/luat_fs_mem.c
  12. 51 0
      module/Air780EHM/demo/fastlz/fastlz_test.lua
  13. 32 36
      module/Air780EHM/demo/fastlz/main.lua
  14. 90 0
      module/Air780EHM/demo/little_flash/little_flash.lua
  15. 32 53
      module/Air780EHM/demo/little_flash/main.lua
  16. 13 12
      module/Air780EHM/demo/little_flash/readme.md
  17. 65 0
      module/Air780EHM/demo/sfud/main.lua
  18. 78 0
      module/Air780EHM/demo/sfud/readme.md
  19. 105 0
      module/Air780EHM/demo/sfud/sfud_test.lua
  20. 52 58
      module/Air780EHM/demo/ymodem/main.lua
  21. 68 0
      module/Air780EHM/demo/ymodem/readme.md
  22. 103 0
      module/Air780EHM/demo/ymodem/ymodem_receive.lua
  23. 57 0
      module/Air8000/demo/ble/ibeacon/ble_ibeacon.lua
  24. 56 50
      module/Air8000/demo/ble/ibeacon/main.lua
  25. 34 0
      module/Air8000/demo/ble/ibeacon/readme.md
  26. 93 0
      module/Air8000/demo/ble/master/ble_master.lua
  27. 54 70
      module/Air8000/demo/ble/master/main.lua
  28. 34 0
      module/Air8000/demo/ble/master/readme.md
  29. 124 0
      module/Air8000/demo/ble/peripheral/ble_peripheral.lua
  30. 51 112
      module/Air8000/demo/ble/peripheral/main.lua
  31. 3 5
      module/Air8000/demo/ble/peripheral/readme.md
  32. 70 0
      module/Air8000/demo/ble/scan/ble_scan.lua
  33. 48 67
      module/Air8000/demo/ble/scan/main.lua
  34. 2 4
      module/Air8000/demo/ble/scan/readme.md
  35. 6 0
      module/Air8101/demo/socket/client/long_connection/main.lua
  36. 6 0
      module/Air8101/demo/socket/client/long_connection/sntp_app.lua
  37. 6 0
      module/Air8101/demo/socket/client/long_connection/tcp_client_main.lua
  38. 6 0
      module/Air8101/demo/socket/client/long_connection/tcp_client_receiver.lua
  39. 6 0
      module/Air8101/demo/socket/client/long_connection/tcp_client_sender.lua
  40. 6 0
      module/Air8101/demo/socket/client/long_connection/tcp_ssl_ca_main.lua
  41. 6 0
      module/Air8101/demo/socket/client/long_connection/tcp_ssl_ca_receiver.lua
  42. 6 0
      module/Air8101/demo/socket/client/long_connection/tcp_ssl_ca_sender.lua
  43. 6 0
      module/Air8101/demo/socket/client/long_connection/tcp_ssl_main.lua
  44. 6 0
      module/Air8101/demo/socket/client/long_connection/tcp_ssl_receiver.lua
  45. 6 0
      module/Air8101/demo/socket/client/long_connection/tcp_ssl_sender.lua
  46. 11 0
      module/Air8101/demo/socket/client/long_connection/test_app.lua
  47. 7 1
      module/Air8101/demo/socket/client/long_connection/timer_app.lua
  48. 7 1
      module/Air8101/demo/socket/client/long_connection/uart_app.lua
  49. 6 0
      module/Air8101/demo/socket/client/long_connection/udp_client_main.lua
  50. 6 0
      module/Air8101/demo/socket/client/long_connection/udp_client_receiver.lua
  51. 6 0
      module/Air8101/demo/socket/client/long_connection/udp_client_sender.lua
  52. 8 2
      module/Air8101/demo/socket/client/long_connection/wifi_app.lua

+ 19 - 0
components/airlink/src/exec/luat_airlink_bt_exec_task.c

@@ -389,6 +389,25 @@ static void drv_bt_task(void *param) {
                 LLOGD("unknow bt cmd id %d", msg->cmd_id);
                 break;
             }
+            uint64_t seq = luat_airlink_get_next_cmd_id();
+            airlink_queue_item_t item = {
+                .len =  sizeof(luat_airlink_cmd_t) + sizeof(luat_drv_ble_msg_t) + sizeof(luat_drv_ble_result_t)
+            };
+            luat_airlink_cmd_t* cmd = luat_airlink_cmd_new(0x511, item.len - sizeof(luat_airlink_cmd_t));
+            if (cmd != NULL) {
+                luat_drv_ble_msg_t *resp_msg = (luat_drv_ble_msg_t *)cmd->data;
+                resp_msg->id = seq;
+                resp_msg->cmd_id = LUAT_DRV_BT_CMD_BLE_EXEC_RESULT;
+                void* ptr = cmd->data + sizeof(luat_drv_ble_msg_t);
+                luat_drv_ble_result_t resp_result = {.last_id = msg->id, .result = ret};
+                memcpy(ptr, &resp_result, sizeof(luat_drv_ble_result_t));
+                item.cmd = cmd;
+                luat_airlink_queue_send(LUAT_AIRLINK_QUEUE_CMD, &item);
+            }
+            else
+            {
+                LLOGW("out of memory when alloc ble event");
+            }
             luat_heap_free(msg);
         }
         luat_rtos_task_sleep(5); // TODO 删掉

+ 10 - 0
components/airlink/src/exec/luat_airlink_cmd_exec_bluetooth.c

@@ -133,6 +133,16 @@ int luat_airlink_cmd_exec_bt_resp_cb(luat_airlink_cmd_t *cmd, void *userdata) {
     return 0;
 }
 
+int luat_airlink_cmd_exec_bt_resp_result_cb(luat_airlink_cmd_t *cmd, void *userdata) {
+    if (cmd->len < 10) {
+        return -100;
+    }
+    luat_drv_ble_msg_t* msg = (luat_drv_ble_msg_t *)(cmd->data);
+    luat_drv_ble_result_t *resp_result = (luat_drv_ble_result_t *)(msg->data);
+    LLOGD("执行结果 result: %d", resp_result->result);
+    return 0;
+}
+
 #endif
 
 #if defined(LUAT_USE_AIRLINK_EXEC_BLUETOOTH) || defined(LUAT_USE_AIRLINK_EXEC_BLUETOOTH_RESP)

+ 2 - 0
components/airlink/src/luat_airlink_cmds.c

@@ -72,6 +72,7 @@ CMD_DEFINE(uart_sent_cb);
 CMD_DEFINE(bt_request);
 
 CMD_DEFINE(bt_resp_cb);
+CMD_DEFINE(bt_resp_result_cb);
 
 // PM指令, 0x600开始
 CMD_DEFINE(pm_request);
@@ -145,6 +146,7 @@ __USER_FUNC_IN_RAM__ const luat_airlink_cmd_reg_t airlink_cmds[] = {
 #endif
 #ifdef LUAT_USE_AIRLINK_EXEC_BLUETOOTH_RESP
     CMD_REG(0x510, bt_resp_cb),
+    CMD_REG(0x511, bt_resp_result_cb),
 #endif
 
 #ifdef LUAT_USE_AIRLINK_EXEC_PM

+ 7 - 1
components/bluetooth/include/luat_drv_ble.h

@@ -11,6 +11,12 @@ typedef struct luat_drv_ble_msg
     uint8_t data[0];
 }luat_drv_ble_msg_t;
 
+typedef struct luat_drv_ble_result
+{
+    uint64_t last_id;
+    int32_t result;
+}luat_drv_ble_result_t;
+
 // 定义蓝牙cmd id
 
 enum {
@@ -42,7 +48,7 @@ enum {
 
 
     LUAT_DRV_BT_CMD_BLE_EVENT_CB = 128, // 事件回调
-
+    LUAT_DRV_BT_CMD_BLE_EXEC_RESULT,
     LUAT_DRV_BT_CMD_MAX
 };
 

+ 3 - 2
components/bluetooth/src/luat_lib_ble.c

@@ -202,7 +202,7 @@ int l_ble_callback(lua_State *L, void *ptr)
 exit:
     if (param)
     {
-        if (LUAT_BLE_EVENT_WRITE == evt && param->write_req.value)
+        if (LUAT_BLE_EVENT_WRITE == evt && param->write_req.value_len)
         {
             // LLOGD("free write_req.value %p", param->write_req.value);
             luat_heap_free(param->write_req.value);
@@ -214,7 +214,7 @@ exit:
             luat_heap_free(param->adv_req.data);
             param->adv_req.data = NULL;
         }
-        else if (LUAT_BLE_EVENT_READ_VALUE == evt && param->read_req.value){
+        else if (LUAT_BLE_EVENT_READ_VALUE == evt && param->read_req.value_len){
             // LLOGD("free read_req.value %p", param->read_req.value);
             luat_heap_free(param->read_req.value);
             param->read_req.value = NULL;
@@ -231,6 +231,7 @@ void luat_ble_cb(luat_ble_t *args, luat_ble_event_t ble_event, luat_ble_param_t
     if (ble_param){
         // LLOGD("ble param: %p", ble_param);
         luat_ble_param = luat_heap_malloc(sizeof(luat_ble_param_t));
+        memset(luat_ble_param,0,sizeof(luat_ble_param_t));
         memcpy(luat_ble_param, ble_param, sizeof(luat_ble_param_t));
         if (ble_event == LUAT_BLE_EVENT_WRITE && ble_param->write_req.value_len){
             luat_ble_param->write_req.value = luat_heap_malloc(ble_param->write_req.value_len);

+ 6 - 0
components/fskv/luat_fskv.c

@@ -7,9 +7,15 @@
 #define LUAT_LOG_TAG "fskv"
 #include "luat_log.h"
 
+#ifdef TYPE_EC718HM
 #define LFS_BLOCK_DEVICE_READ_SIZE (256)
 #define LFS_BLOCK_DEVICE_PROG_SIZE (256)
 #define LFS_BLOCK_DEVICE_CACHE_SIZE (256)
+#else
+#define LFS_BLOCK_DEVICE_READ_SIZE (4)
+#define LFS_BLOCK_DEVICE_PROG_SIZE (4)
+#define LFS_BLOCK_DEVICE_CACHE_SIZE (4096)
+#endif
 #define LFS_BLOCK_DEVICE_ERASE_SIZE (4096) // one sector 4KB
 //#define LFS_BLOCK_DEVICE_TOTOAL_SIZE (64 * 1024)
 #define LFS_BLOCK_DEVICE_LOOK_AHEAD (16)

+ 3 - 0
components/wlan/luat_lib_wlan.c

@@ -23,6 +23,9 @@
 #include "luat/drv_wlan.h"
 #include "luat_hmeta.h"
 #endif
+#ifdef LUAT_USE_AIRLINK
+#include "luat_airlink.h"
+#endif
 #include "luat_network_adapter.h"
 #ifdef LUAT_USE_NETDRV
 #include "luat_netdrv.h"

+ 4 - 4
luat/demo/airlink/air8000_wifi/main.lua

@@ -22,10 +22,10 @@ gpio.setup(0, function()
     end)
 end, gpio.PULLDOWN)
 
-if airlink.irqmode then
-    log.info("尝试开启IRQ模式", "GPIO20 <--> GPIO140")
-    -- airlink.irqmode(true, 20, 140)
-end
+-- if airlink.irqmode then
+--     log.info("尝试开启IRQ模式", "GPIO20 <--> GPIO140")
+--     airlink.irqmode(true, 20, 140)
+-- end
 
 function test_ap()
     log.info("执行AP创建操作")

+ 7 - 22
luat/demo/ftp/main.lua

@@ -41,34 +41,19 @@ sys.taskInit(function()
         local ssid = "luatos1234"
         local password = "12341234"
         log.info("wifi", ssid, password)
-        -- TODO 改成esptouch配网
-        -- LED = gpio.setup(12, 0, gpio.PULLUP)
         wlan.init()
         wlan.setMode(wlan.STATION)
         wlan.connect(ssid, password, 1)
-        local result, data = sys.waitUntil("IP_READY", 30000)
-        log.info("wlan", "IP_READY", result, data)
-        device_id = wlan.getMac()
-    elseif rtos.bsp() == "AIR105" then
-        -- w5500 以太网, 当前仅Air105支持
-        -- w5500.init(spi.SPI_2, 24000000, pin.PB03, pin.PC00, pin.PC03)
-        w5500.init(spi.HSPI_0, 24000000, pin.PC14, pin.PC01, pin.PC00)
-        log.info("auto mac", w5500.getMac():toHex())
-        w5500.config() -- 默认是DHCP模式
-        w5500.bind(socket.ETH0)
-        -- LED = gpio.setup(62, 0, gpio.PULLUP)
-        sys.wait(1000)
-        -- TODO 获取mac地址作为device_id
     elseif mobile then
         -- Air780E/Air600E系列
         -- mobile.simid(2)
         -- LED = gpio.setup(27, 0, gpio.PULLUP)
-        device_id = mobile.imei()
-        sys.waitUntil("IP_READY") -- 死等到联网成功
-        log.info("联网成功")
-        -- 联网成功后发布联网成功的消息
-        sys.publish("net_ready")
+        -- device_id = mobile.imei()
     end
+    sys.waitUntil("IP_READY") -- 死等到联网成功
+    log.info("联网成功")
+    -- 联网成功后发布联网成功的消息
+    sys.publish("net_ready")
 end)
 
 --[[操作ftp的函数,因为用了sys.wait,请在task内调用
@@ -97,8 +82,8 @@ local function ftp_test()
 
         log.info("获取当前工作目录下的文件名列表", ftp.command("LIST").wait())
 
-        log.info("在本地创建一个文件", "文件名及其目录为" .. local_name,
-            io.writeFile(local_name, "23noianfdiasfhnpqw39fhawe;fuibnnpw3fheaios;fna;osfhisao;fadsfl"))
+        log.info("在本地创建一个文件", "文件名及其目录为" .. local_name)
+        io.writeFile(local_name, "23noianfdiasfhnpqw39fhawe;fuibnnpw3fheaios;fna;osfhisao;fadsfl")
         --[[如果下载失败,部分服务器可能会直接断开本次连接,如果遇到下述打印
             net_lwip_tcp_err_cb 662:adapter 1 socket 20 not closing, but error -14
             是正常的,此处打印服务器主动断开客户端时会出现

+ 0 - 2
luat/modules/luat_lib_gpio.c

@@ -765,10 +765,8 @@ static const rotable_Reg_t reg_gpio[] =
     { "WAKEUP6",         ROREG_INT(HAL_WAKEUP_CHARGE)},
     { "CHG_DET",         ROREG_INT(HAL_WAKEUP_CHARGE)},
 #endif
-#if (defined LUAT_MODEL_AIR780EHV)
     //@const AUDIOPA_EN number 音频PA使能脚, 仅Air780EHV特有的引脚
     { "AUDIOPA_EN",      ROREG_INT(HAL_GPIO_22)},
-#endif
 	//@const PWR_KEY number 开机键,支持双向触发中断,不支持输出
 	{ "PWR_KEY",         ROREG_INT(HAL_WAKEUP_PWRKEY)},
 #endif

+ 123 - 54
luat/vfs/luat_fs_mem.c

@@ -5,20 +5,27 @@
 #define LUAT_LOG_TAG "fs"
 #include "luat_log.h"
 
+#define BLOCK_SIZE 4096
+
+typedef struct ram_file_block
+{
+    uint8_t data[BLOCK_SIZE];
+    struct ram_file_block* next;
+} ram_file_block_t;
+
 typedef struct ram_file
 {
-    size_t size;     // 当前文件大小, 也是指针对应内存块的大小
-    // size_t ptr_size; // 数值指针的大小
+    size_t size;     // 当前文件大小
     char name[32];   // 文件名称
-    char ptr[4];
-}ram_file_t;
+    ram_file_block_t* head; // 链表头指针
+} ram_file_t;
 
 typedef struct luat_ram_fd
 {
     int fid;
-    uint32_t  offset;
+    uint32_t offset;
     uint8_t readonly;
-}luat_raw_fd_t;
+} luat_raw_fd_t;
 
 #define RAM_FILE_MAX (64)
 static ram_file_t* files[RAM_FILE_MAX];
@@ -33,7 +40,7 @@ FILE* luat_vfs_ram_fopen(void* userdata, const char *filename, const char *mode)
     if (!strcmp("r", mode) || !strcmp("rb", mode)) {
         for (size_t i = 0; i < RAM_FILE_MAX; i++)
         {
-            if (files[i]== NULL)
+            if (files[i] == NULL)
                 continue;
             if (!strcmp(files[i]->name, filename)) {
                 luat_raw_fd_t* fd = luat_heap_malloc(sizeof(luat_raw_fd_t));
@@ -54,7 +61,7 @@ FILE* luat_vfs_ram_fopen(void* userdata, const char *filename, const char *mode)
         // 先看看是否存在, 如果存在就重用老的
         for (size_t i = 0; i < RAM_FILE_MAX; i++)
         {
-            if (files[i]== NULL)
+            if (files[i] == NULL)
                 continue;
             if (!strcmp(files[i]->name, filename)) {
                 luat_raw_fd_t* fd = luat_heap_malloc(sizeof(luat_raw_fd_t));
@@ -67,16 +74,14 @@ FILE* luat_vfs_ram_fopen(void* userdata, const char *filename, const char *mode)
                 fd->offset = 0;
                 if (!strcmp("w+", mode) || !strcmp("wb+", mode) || !strcmp("w+b", mode)) {
                     // 截断模式
-                    char* tmp = luat_heap_realloc(files[i], sizeof(ram_file_t));
-                    if (tmp) {
-                        files[i] = (ram_file_t*)tmp;
-                    }
-                    else {
-                        LLOGE("realloc ram_file_t failed");
-                        luat_heap_free(fd);
-                        return NULL;
-                    }
                     files[i]->size = 0;
+                    ram_file_block_t* block = files[i]->head;
+                    while (block) {
+                        ram_file_block_t* next = block->next;
+                        luat_heap_free(block);
+                        block = next;
+                    }
+                    files[i]->head = NULL;
                 }
                 return (FILE*)fd;
             }
@@ -143,11 +148,19 @@ int luat_vfs_ram_getc(void* userdata, FILE* stream) {
     if (files[fd->fid] == NULL) {
         return -1;
     }
-    if (fd->offset < files[fd->fid]->size) {
-        uint8_t c = (uint8_t)files[fd->fid]->ptr[fd->offset];
-        fd->offset ++;
-        //LLOGD("getc %02X", c);
-        return c;
+    ram_file_block_t* block = files[fd->fid]->head;
+    size_t offset = fd->offset;
+    while (block) {
+        if (offset < BLOCK_SIZE) {
+            uint8_t c = block->data[offset];
+            if (c == '\0') {
+                return -1;
+            }
+            fd->offset++;
+            return c;
+        }
+        offset -= BLOCK_SIZE;
+        block = block->next;
     }
     return -1;
 }
@@ -155,18 +168,17 @@ int luat_vfs_ram_getc(void* userdata, FILE* stream) {
 int luat_vfs_ram_fseek(void* userdata, FILE* stream, long int offset, int origin) {
     (void)userdata;
     luat_raw_fd_t* fd = (luat_raw_fd_t*)stream;
+    // LLOGE("luat_vfs_ram_fseek seek %p %p %d %d", userdata, stream, offset, origin);
     if (origin == SEEK_CUR) {
         fd->offset += offset;
-        return 0;
     }
     else if (origin == SEEK_SET) {
         fd->offset = offset;
-        return 0;
     }
     else {
         fd->offset = files[fd->fid]->size - offset;
-        return 0;
     }
+    return 0;
 }
 
 int luat_vfs_ram_ftell(void* userdata, FILE* stream) {
@@ -183,58 +195,103 @@ int luat_vfs_ram_fclose(void* userdata, FILE* stream) {
     luat_heap_free(fd);
     return 0;
 }
+
 int luat_vfs_ram_feof(void* userdata, FILE* stream) {
     (void)userdata;
     luat_raw_fd_t* fd = (luat_raw_fd_t*)stream;
     //LLOGD("feof %p %p %d %d", userdata, stream, fd->size, fd->offset);
     return fd->offset >= files[fd->fid]->size ? 1 : 0;
 }
+
 int luat_vfs_ram_ferror(void* userdata, FILE *stream) {
     (void)userdata;
     (void)stream;
     return 0;
 }
+
 size_t luat_vfs_ram_fread(void* userdata, void *ptr, size_t size, size_t nmemb, FILE *stream) {
     (void)userdata;
     luat_raw_fd_t* fd = (luat_raw_fd_t*)stream;
-    //LLOGD("fread %p %p %d %d", userdata, stream, fd->size, fd->offset);
-    //LLOGD("fread2 %p %p %d %d", userdata, stream, size * nmemb, fd->offset);
-    size_t read_size = size*nmemb;
+    size_t read_size = size * nmemb;
     if (fd->offset >= files[fd->fid]->size) {
         return 0;
     }
     if (fd->offset + read_size >= files[fd->fid]->size) {
         read_size = files[fd->fid]->size - fd->offset;
     }
-    memcpy(ptr, files[fd->fid]->ptr + fd->offset, read_size);
-    fd->offset += read_size;
-    return read_size;
+    ram_file_block_t* block = files[fd->fid]->head;
+    size_t offset = fd->offset;
+    uint8_t* dst = (uint8_t*)ptr;
+    size_t bytes_read = 0; // 用于记录实际读取的字节数
+    while (block && read_size > 0) {
+        size_t copy_size = BLOCK_SIZE - (offset % BLOCK_SIZE);
+        if (copy_size > read_size) {
+            copy_size = read_size;
+        }
+        memcpy(dst, block->data + (offset % BLOCK_SIZE), copy_size);
+        dst += copy_size;
+        read_size -= copy_size;
+        offset += copy_size;
+        bytes_read += copy_size; // 累加读取的字节数
+        if (offset % BLOCK_SIZE == 0) {
+            block = block->next;
+        }
+    }
+    fd->offset += bytes_read; // 更新文件偏移量
+    return bytes_read; // 返回实际读取的字节数
 }
+
 size_t luat_vfs_ram_fwrite(void* userdata, const void *ptr, size_t size, size_t nmemb, FILE *stream) {
     (void)userdata;
     luat_raw_fd_t* fd = (luat_raw_fd_t*)stream;
-    size_t write_size = size*nmemb;
-    if (write_size > 128 * 1024) {
-        LLOGW("ramfs large write !! %ld", write_size);
-    }
+    size_t write_size = size * nmemb;
     if (fd->readonly) {
         LLOGW("readonly fd %d!! path %s", fd->fid, files[fd->fid]->name);
         return 0;
     }
-    
-    if (fd->offset + write_size > files[fd->fid]->size) {
-        char* ptr = luat_heap_realloc(files[fd->fid], fd->offset + write_size + sizeof(ram_file_t));
-        if (ptr == NULL) {
-            LLOGW("/ram out of sys memory!! %ld", write_size);
-            return 0;
+    ram_file_block_t* block = files[fd->fid]->head;
+    size_t offset = fd->offset;
+    const uint8_t* src = (const uint8_t*)ptr;
+    while (write_size > 0) {
+        if (block == NULL || offset % BLOCK_SIZE == 0) {
+            if (block == NULL) {
+                block = luat_heap_malloc(sizeof(ram_file_block_t));
+                if (block == NULL) {
+                    LLOGW("out of memory when malloc ram_file_block_t");
+                    return 0;
+                }
+                memset(block, 0, sizeof(ram_file_block_t));
+                block->next = NULL;
+                if (files[fd->fid]->head == NULL) {
+                    files[fd->fid]->head = block;
+                } else {
+                    ram_file_block_t* last = files[fd->fid]->head;
+                    while (last->next) {
+                        last = last->next;
+                    }
+                    last->next = block;
+                }
+            }
+        }
+        size_t copy_size = BLOCK_SIZE - (offset % BLOCK_SIZE);
+        if (copy_size > write_size) {
+            copy_size = write_size;
+        }
+        memcpy(block->data + (offset % BLOCK_SIZE), src, copy_size);
+        src += copy_size;
+        write_size -= copy_size;
+        offset += copy_size;
+        if (offset % BLOCK_SIZE == 0) {
+            block = block->next;
         }
-        files[fd->fid] = (ram_file_t*)ptr;
-        files[fd->fid]->size = fd->offset + write_size;
     }
-    memcpy(files[fd->fid]->ptr + fd->offset, ptr, write_size);
-    fd->offset += write_size;
-    return write_size;
+    fd->offset += (size_t)(src - (const uint8_t*)ptr);
+    files[fd->fid]->size = offset > files[fd->fid]->size ? offset : files[fd->fid]->size;
+    // 打印一下写入的数据
+    // LLOGD("write data %s", (char*)ptr);
+    return (size_t)(src - (const uint8_t*)ptr);
 }
+
 int luat_vfs_ram_remove(void* userdata, const char *filename) {
     (void)userdata;
     for (size_t i = 0; i < RAM_FILE_MAX; i++)
@@ -242,12 +299,20 @@ int luat_vfs_ram_remove(void* userdata, const char *filename) {
         if (files[i] == NULL)
             continue;
         if (!strcmp(filename, files[i]->name)) {
+            ram_file_block_t* block = files[i]->head;
+            while (block) {
+                ram_file_block_t* next = block->next;
+                luat_heap_free(block);
+                block = next;
+            }
             luat_heap_free(files[i]);
             files[i] = NULL;
+            return 0;
         }
     }
     return 0;
 }
+
 int luat_vfs_ram_rename(void* userdata, const char *old_filename, const char *new_filename) {
     (void)userdata;
     if (old_filename == NULL || new_filename == NULL)
@@ -265,6 +330,7 @@ int luat_vfs_ram_rename(void* userdata, const char *old_filename, const char *ne
     }
     return 0;
 }
+
 int luat_vfs_ram_fexist(void* userdata, const char *filename) {
     (void)userdata;
     for (size_t i = 0; i < RAM_FILE_MAX; i++)
@@ -295,7 +361,7 @@ void* luat_vfs_ram_mmap(void* userdata, FILE *stream) {
     (void)userdata;
     luat_raw_fd_t *fd = (luat_raw_fd_t*)(stream);
     //LLOGD("fsize %p %p %d %d", userdata, fd);
-    return files[fd->fid]->ptr;
+    return files[fd->fid]->head->data;
 }
 
 int luat_vfs_ram_mkfs(void* userdata, luat_fs_conf_t *conf) {
@@ -365,8 +431,8 @@ int luat_vfs_ram_info(void* userdata, const char* path, luat_fs_info_t *conf) {
     
     conf->type = 0;
     conf->total_block = 64;
-    conf->block_used = (ftotal + 1023) / 1024;
-    conf->block_size = 1024;
+    conf->block_used = (ftotal + BLOCK_SIZE) / BLOCK_SIZE;
+    conf->block_size = BLOCK_SIZE;
     return 0;
 }
 
@@ -376,13 +442,16 @@ int luat_vfs_ram_truncate(void* fsdata, char const* path, size_t nsize) {
         if (files[i] == NULL)
             continue;
         if (!strcmp(files[i]->name, path)) {
-            if (files[i]->size > nsize) {
-                files[i]->size = nsize;
-                char* ptr = luat_heap_realloc(files[i], nsize + sizeof(ram_file_t));
-                if (ptr) {
-                    files[i] = (ram_file_t*)ptr;
+            ram_file_block_t* block = files[i]->head;
+            size_t offset = 0;
+            while (block) {
+                if (offset + BLOCK_SIZE > nsize) {
+                    memset(block->data + (nsize - offset), 0, BLOCK_SIZE - (nsize - offset));
                 }
+                offset += BLOCK_SIZE;
+                block = block->next;
             }
+            files[i]->size = nsize;
             return 0;
         }
     }

+ 51 - 0
module/Air780EHM/demo/fastlz/fastlz_test.lua

@@ -0,0 +1,51 @@
+--[[
+@module  fastlz_test
+@summary fastlz压缩与解压缩测试功能模块
+@version 1.0
+@date    2025.07.01
+@author  孟伟
+@usage
+使用Air780EHM演示压缩与解压缩的流程。
+]]
+
+function test_fastlz_func()
+    sys.wait(1000)
+    -- 原始数据
+    local originStr = io.readFile("/luadb/test.txt") or "q309pura;dsnf;asdouyf89q03fonaewofhaeop;fhiqp02398ryhai;ofinap983fyua0weo;ifhj3p908fhaes;iofaw789prhfaeiwop;fhaesp98fadsjklfhasklfsjask;flhadsfk"
+    local maxOut = #originStr
+    log.info("原始数据长度", #originStr)
+
+    -- 以压缩等级1 进行压缩
+    local L1 = fastlz.compress(originStr,1)
+    log.info("压缩等级1:压缩后的数据长度", #L1)
+
+    -- 解压
+    local dstr1 = fastlz.uncompress(L1,maxOut)
+    log.info("压缩等级1:解压后的的数据长度", #dstr1)
+    -- 判断解压后的数据是否与原始数据相同
+    if originStr == dstr1 then
+        log.info("压缩等级1:解压后的数据与原始数据相同")
+    else
+        log.info("压缩等级1:解压后的数据与原始数据不同")
+    end
+
+    sys.wait(1000)
+
+    -- 以压缩等级2 进行压缩
+    local L2 = fastlz.compress(originStr, 2)
+    log.info("压缩等级2:压缩后的数据长度", #L2)
+
+    -- 解压
+    local dstr2 = fastlz.uncompress(L2,maxOut)
+    log.info("压缩等级2:解压后的数据长度", #dstr2)
+
+    -- 判断解压后的数据是否与原始数据相同
+    if originStr == dstr2 then
+        log.info("压缩等级2:解压后的数据与原始数据相同")
+    else
+        log.info("压缩等级2:解压后的数据与原始数据不同")
+    end
+end
+--创建并且启动一个task
+--运行这个task的主函数test_fastlz_func
+sys.taskInit(test_fastlz_func)

+ 32 - 36
module/Air780EHM/demo/fastlz/main.lua

@@ -1,4 +1,14 @@
 --[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 1.0
+@date    2025.07.01
+@author  孟伟
+@usage
+本demo演示的功能为:
+实现使用Air780EHM来对文本/段落序列、原始像素数据序列或具有大量重复的任何其他数据块的快速压缩与解压缩。
+]]
+--[[
 必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
 PROJECT:项目名,ascii string类型
         可以随便定义,只要不使用,就行
@@ -8,12 +18,11 @@ VERSION:项目版本号,ascii string类型
             因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
         如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
 
-本demo演示的功能为:
-实现使用Air780EHM来对文本/段落序列、原始像素数据序列或具有大量重复的任何其他数据块的快速压缩与解压缩。
+
 ]]
 -- LuaTools需要PROJECT和VERSION这两个信息
 PROJECT = "FastLZ"
-VERSION = "1.0.0"
+VERSION = "001.000.000"
 
 --添加硬狗防止程序卡死
 if wdt then
@@ -21,46 +30,33 @@ if wdt then
     sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
 end
 
-function test_fastlz()
-    sys.wait(1000)
-    -- 原始数据
-    local originStr = io.readFile("/luadb/test.txt") or "q309pura;dsnf;asdouyf89q03fonaewofhaeop;fhiqp02398ryhai;ofinap983fyua0weo;ifhj3p908fhaes;iofaw789prhfaeiwop;fhaesp98fadsjklfhasklfsjask;flhadsfk"
-    local maxOut = #originStr
-    log.info("原始数据长度", #originStr)
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
 
-    -- 以压缩等级1 进行压缩
-    local L1 = fastlz.compress(originStr,1)
-    log.info("压缩等级1:压缩后的数据长度", #L1)
 
-    -- 解压
-    local dstr1 = fastlz.uncompress(L1,maxOut)
-    log.info("压缩等级1:解压后的的数据长度", #dstr1)
-    -- 判断解压后的数据是否与原始数据相同
-    if originStr == dstr1 then
-        log.info("压缩等级1:解压后的数据与原始数据相同")
-    else
-        log.info("压缩等级1:解压后的数据与原始数据不同")
-    end
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
 
-    sys.wait(1000)
 
-    -- 以压缩等级2 进行压缩
-    local L2 = fastlz.compress(originStr, 2)
-    log.info("压缩等级2:压缩后的数据长度", #L2)
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
 
-    -- 解压
-    local dstr2 = fastlz.uncompress(L2,maxOut)
-    log.info("压缩等级2:解压后的数据长度", #dstr2)
 
-    -- 判断解压后的数据是否与原始数据相同
-    if originStr == dstr2 then
-        log.info("压缩等级2:解压后的数据与原始数据相同")
-    else
-        log.info("压缩等级2:解压后的数据与原始数据不同")
-    end
-end
+--加载fastlz测试应用模块
+require "fastlz_test"
 
-sys.taskInit(test_fastlz)
 
 
 -- 用户代码已结束---------------------------------------------

+ 90 - 0
module/Air780EHM/demo/little_flash/little_flash.lua

@@ -0,0 +1,90 @@
+
+--[[
+@module  little_flash
+@summary little_flash测试功能模块
+@version 1.0
+@date    2025.07.01
+@author  孟伟
+@usage
+本demo是 Air780EHM + spi_flash. 以 Air780EHM核心板为例, 接线如下:
+
+Air780EHM            SPI_FLASH
+GND(任意)            GND
+VDD_EXT              VCC
+GPIO8/SPI0_CS        CS,片选
+SPI0_SLK             CLK,时钟
+SPI0_MOSI            DI,主机输出,从机输入
+SPI0_MISO            DO,主机输入,从机输出
+]]
+--使用spi0,cs接在gpio8上
+local spi_id,pin_cs = 0,8
+function little_flash_func()
+    sys.wait(1000)
+    log.info("lf", "SPI", spi_id, "CS PIN", pin_cs)
+    --以对象的方式初始化spi_flash
+    spi_flash = spi.deviceSetup(spi_id,pin_cs,0,0,8,20*1000*1000,spi.MSB,1,0)
+    log.info("lf", "spi_flash", spi_flash)
+    little_flash_device = lf.init(spi_flash)   --初始化 little_flash
+    if little_flash_device then
+        log.info("lf.init ok",little_flash_device)
+    else
+        log.info("lf.init Error")
+        return
+    end
+
+    if lf.mount then
+        --挂载 little_flash lfs文件系统
+        local ret = lf.mount(little_flash_device,"/little_flash")
+        log.info("lf.mount", ret)
+        if ret then
+            log.info("little_flash", "挂载成功")
+            --获取lfs文件系统信息
+            log.info("fsstat", fs.fsstat("/little_flash"))
+
+            -- 挂载成功后,可以像操作文件一样操作
+            --以写模式打开文件,并返回文件句柄,io接口含义可参考lua5.3手册https://wiki.luatos.com/_static/lua53doc/contents.html
+            local f = io.open("/little_flash/test", "w")
+            local write_str = os.date()
+            log.info("/little_flash/test文件写入数据",write_str)
+            --写入当前时间到文件中
+            f:write(write_str)
+            --关闭文件
+            f:close()
+            --读取文件内容并打印
+            local read_str = io.readFile("/little_flash/test")
+            log.info("little_flash", read_str)
+            if write_str == read_str then
+                log.info("写入测试成功,写入字符串与读出字符串一样")
+            else
+                log.info("写入测试失败,写入字符串与读出字符串不一样")
+            end
+
+            -- 文件追加
+            --文件追加测试写入之前先删除一下
+            os.remove("/little_flash/test2")
+            --将"LuatOS"字符串写入到test2文件中
+            io.writeFile("/little_flash/test2", "LuatOS-")
+            --以追加的方式打开test2文件
+            local f = io.open("/little_flash/test2", "a+")
+            local time_str = os.date()
+            f:write(time_str)
+            write_str = "LuatOS-"..time_str
+            log.info("/little_flash/test2文件写入数据",write_str)
+
+            --关闭文件
+            f:close()
+            read_str = io.readFile("/little_flash/test2")
+            log.info("little_flash", read_str)
+            if write_str == read_str then
+                log.info("追加测试成功,写入字符串与读出字符串一样")
+            else
+                log.info("追加测试失败,写入字符串与读出字符串不一样")
+            end
+        else
+            log.info("little_flash", "挂载失败")
+        end
+    end
+end
+--创建并且启动一个task
+--运行这个task的主函数little_flash_func
+sys.taskInit(little_flash_func)

+ 32 - 53
module/Air780EHM/demo/little_flash/main.lua

@@ -1,3 +1,13 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 1.0
+@date    2025.07.01
+@author  孟伟
+@usage
+本demo演示的功能为:
+实现使用Air780EHM核心板将spi_flash挂测成lfs文件系统,并演示lfs文件系统中的文件的读写、删除、追加等操作。
+]]
 
 --[[
 必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
@@ -8,13 +18,10 @@ VERSION:项目版本号,ascii string类型
             X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
             因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
         如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
-
-本demo演示的功能为:
-实现使用Air780EHM核心板将spi_flash挂测成lfs文件系统,并演示lfs文件系统中的文件的读写、删除、追加等操作。
 ]]
 PROJECT = "little_flash_demo"
-VERSION = "1.0.0"
-
+VERSION = "001.000.000"
+-- 在日志中打印项目名和项目版本号
 log.info("main", PROJECT, VERSION)
 
 
@@ -23,61 +30,33 @@ if wdt then
     wdt.init(9000)--初始化watchdog设置为9s
     sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
 end
---[[
-本demo是 Air780EHM + spi_flash. 以 Air780EHM核心板为例, 接线如下:
-
-Air780EHM            SPI_FLASH
-GND(任意)            GND
-VDD_EXT              VCC
-GPIO8/SPI0_CS        CS,片选
-SPI0_SLK             CLK,时钟
-SPI0_MOSI            DI,主机输出,从机输入
-SPI0_MISO            DO,主机输入,从机输出
-]]
-local spi_id,pin_cs = 0,8
-sys.taskInit(function()
-    -- log.info("等5秒")
-    sys.wait(1000)
 
-    log.info("lf", "SPI", spi_id, "CS PIN", pin_cs)
-    spi_flash = spi.deviceSetup(spi_id,pin_cs,0,0,8,20*1000*1000,spi.MSB,1,0) --以对象的方式初始化spi_flash
-    log.info("lf", "spi_flash", spi_flash)
-    little_flash_device = lf.init(spi_flash)   --初始化 little_flash
-    if little_flash_device then
-        log.info("lf.init ok",little_flash_device)
-    else
-        log.info("lf.init Error")
-        return
-    end
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
 
-    if lf.mount then
-        local ret = lf.mount(little_flash_device,"/little_flash")  --挂载 little_flash lfs文件系统
-        log.info("lf.mount", ret)
-        if ret then
-            log.info("little_flash", "挂载成功")
-            log.info("fsstat", fs.fsstat("/little_flash"))   --获取lfs文件系统信息
 
-            -- 挂载成功后,可以像操作文件一样操作
-            local f = io.open("/little_flash/test", "w")
-            f:write(os.date())
-            f:close()
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
 
-            log.info("little_flash", io.readFile("/little_flash/test"))
 
-            -- 文件追加
-            os.remove("/little_flash/test2")
-            io.writeFile("/little_flash/test2", "LuatOS")
-            local f = io.open("/little_flash/test2", "a+")
-            f:write(" - " .. os.date())
-            f:close()
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
 
-            log.info("little_flash", io.readFile("/little_flash/test2"))
-        else
-            log.info("little_flash", "挂载失败")
-        end
-    end
-end)
 
+--加载little_flash应用模块
+require "little_flash"
 
 -- 用户代码已结束---------------------------------------------
 -- 结尾总是这一句

+ 13 - 12
module/Air780EHM/demo/little_flash/readme.md

@@ -1,7 +1,7 @@
 
 ## 演示功能概述
 
-使用Air780EHM核心板对SPI FLASH来挂载成lfs文件系统,并通过文件系统相关接口去操作lfs文件系统中的文件,演示文件的读写、删除、追加等操作
+使用Air780EHM核心板将SPI FLASH挂载成lfs文件系统,并通过文件系统相关接口去操作lfs文件系统中的文件,演示文件的读写、删除、追加等操作
 
 ## 演示硬件环境
 
@@ -16,18 +16,19 @@
 - Air780EHM核心板通过TYPE-C USB口供电;(核心板USB旁边的开关拨到on一端)
 
 - TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
 5、Air780EHM核心板和spi flash模块接线方式
-``` lua
---[[
-Air780EHM            SPI_FLASH
-GND(任意)            GND
-VDD_EXT              VCC
-GPIO8/SPI0_CS        CS,片选
-SPI0_SLK             CLK,时钟
-SPI0_MOSI            DI,主机输出,从机输入
-SPI0_MISO            DO,主机输入,从机输出
-]]
-```
+
+|   Air780EHM     |       SPI_FLASH       |
+| --------------- | --------------------- |
+|  GND(任意)      |          GND          |
+|  VDD_EXT        |          VCC          |
+|  GPIO8/SPI0_CS  |        CS,片选        |
+|  SPI0_SLK       |        CLK,时钟       |
+|  SPI0_MOSI      |  DI,主机输出,从机输入  |
+|  SPI0_MISO      |  DO,主机输入,从机输出  |
+
+
 
 ## 演示软件环境
 

+ 65 - 0
module/Air780EHM/demo/sfud/main.lua

@@ -0,0 +1,65 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 1.0
+@date    2025.07.01
+@author  孟伟
+@usage
+本demo演示的功能为:
+使用Air780EHM核心板通过SFUD库实现对SPI Flash的高效操作,并可以挂载sfud lfs文件系统,通过文件系统相关接口去操作sfud lfs文件系统中的文件,并演示文件的读写、删除、追加等操作。
+]]
+
+--[[
+必须定义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 = "sfuddemo"
+VERSION = "001.000.000"
+
+-- 在日志中打印项目名和项目版本号
+log.info("main", PROJECT, VERSION)
+
+--添加硬狗防止程序卡死
+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)
+
+
+--加载SFUD测试应用模块
+require "sfud_test"
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 78 - 0
module/Air780EHM/demo/sfud/readme.md

@@ -0,0 +1,78 @@
+
+## 演示功能概述
+
+使用Air780EHM核心板通过SFUD库实现对SPI Flash的高效操作,并可以挂载sfud lfs文件系统,通过文件系统相关接口去操作sfud lfs文件系统中的文件,并演示文件的读写、删除、追加等操作。
+
+## 演示硬件环境
+
+1、Air780EHM核心板一块
+
+2、TYPE-C USB数据线一根
+
+3、spi flash模块一个
+
+4、Air780EHM核心板和数据线的硬件接线方式为
+
+- Air780EHM核心板通过TYPE-C USB口供电;(核心板USB旁边的开关拨到on一端)
+
+- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+5、Air780EHM核心板和spi flash模块接线方式
+
+|   Air780EHM     |       SPI_FLASH       |
+| --------------- | --------------------- |
+|  GND(任意)      |          GND          |
+|  VDD_EXT        |          VCC          |
+|  GPIO8/SPI0_CS  |        CS,片选        |
+|  SPI0_SLK       |        CLK,时钟       |
+|  SPI0_MOSI      |  DI,主机输出,从机输入  |
+|  SPI0_MISO      |  DO,主机输入,从机输出  |
+
+
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+2、[Air780EHM V2008版本固件](https://gitee.com/openLuat/LuatOS/tree/master/module/Air780EHM/core)(理论上最新版本固件也可以,如果使用最新版本的固件不可以,可以烧录V2007固件对比验证)
+
+## 演示核心步骤
+
+1、搭建好硬件环境
+
+2、通过Luatools将demo与固件烧录到核心板中
+
+3、烧录好后,板子开机将会在Luatools上看到如下打印:
+
+(1) 直接操作sfud接口去读写Flash,结果如下:
+
+```lua
+[2025-07-01 21:25:27.134][000000001.369] I/user.sfud	spi_flash	SPI*: 0C7F6EF0
+[2025-07-01 21:25:27.139][000000001.370] I/sfud Found a Winbond flash chip. Size is 4194304 bytes.
+[2025-07-01 21:25:27.142][000000001.389] I/sfud LuatOS-sfud flash device initialized successfully.
+[2025-07-01 21:25:27.149][000000001.390] I/user.sfud.init ok
+[2025-07-01 21:25:27.153][000000001.390] I/user.sfud.getDeviceNum	1
+[2025-07-01 21:25:27.157][000000001.390] I/user.sfud.getInfo	4194304	4096
+[2025-07-01 21:25:27.162][000000001.421] I/user.sfud.eraseWrite	0
+[2025-07-01 21:25:27.167][000000001.422] I/user.sfud.read	luat
+```
+
+(2) 将Flash设备成功挂载为sfud lfs文件系统后,通过标准化文件管理接口对文件系统进行了全流程验证,结果如下:
+
+```lua
+[2025-07-01 21:03:53.218][000000001.280] I/user.sfud.init ok
+[2025-07-01 21:03:53.239][000000001.281] I/user.sfud.getDeviceNum	1
+[2025-07-01 21:03:53.262][000000001.281] I/user.sfud.getInfo	4194304	4096
+[2025-07-01 21:03:53.280][000000001.289] D/sfud lfs_mount 0
+[2025-07-01 21:03:53.302][000000001.289] D/sfud vfs mount /sfud ret 0
+[2025-07-01 21:03:53.317][000000001.290] I/user.sfud.mount	true
+[2025-07-01 21:03:53.331][000000001.290] I/user.sfud	挂载成功
+[2025-07-01 21:03:53.340][000000001.298] I/user.fsstat	true	1024	2	4096	lfs
+[2025-07-01 21:03:53.352][000000001.303] I/user./sfud/test文件写入数据	Sun   0 08:00:01
+[2025-07-01 21:03:53.365][000000001.313] I/user.sfud_lfs read	Sun   0 08:00:01
+[2025-07-01 21:03:53.374][000000001.314] I/user.写入测试成功,写入字符串与读出字符串一样
+[2025-07-01 21:03:53.383][000000001.347] I/user./sfud/test2	LuatOS-Sun   0 08:00:01
+[2025-07-01 21:03:53.395][000000001.361] I/user.sfud read	LuatOS-Sun   0 08:00:01
+[2025-07-01 21:03:53.416][000000001.362] I/user.追加测试成功,写入字符串与读出字符串一样
+
+```

+ 105 - 0
module/Air780EHM/demo/sfud/sfud_test.lua

@@ -0,0 +1,105 @@
+--[[
+@module  sfud_test
+@summary sfud_test测试功能模块
+@version 1.0
+@date    2025.07.01
+@author  孟伟
+@usage
+本demo演示的功能为:使用Air780EHM核心板通过SFUD库实现对SPI Flash的高效操作,并可以挂载sfud lfs文件系统,通过文件系统相关接口去操作sfud lfs文件系统中的文件,并演示文件的读写、删除、追加等操作。
+以 Air780EHM核心板为例, 接线如下:
+Air780EHM            SPI_FLASH
+GND(任意)            GND
+VDD_EXT              VCC
+GPIO8/SPI0_CS        CS,片选
+SPI0_SLK             CLK,时钟
+SPI0_MOSI            DI,主机输出,从机输入
+SPI0_MISO            DO,主机输入,从机输出
+]]
+--使用SPI0,CS接在gpio8上
+local spi_id,pin_cs = 0,8
+
+function sfud_test_func()
+    sys.wait(1000)
+    log.info("sfud", "SPI", spi_id, "CS PIN", pin_cs)
+    --以对象的方式初始化spi_flash
+    spi_flash = spi.deviceSetup(spi_id,pin_cs,0,0,8,20*1000*1000,spi.MSB,1,0)
+    log.info("sfud", "spi_flash", spi_flash)
+    --初始化sfud,并判断初始化结果
+    local ret = sfud.init(spi_flash)
+    if ret then
+        log.info("sfud.init ok")
+    else
+        log.info("sfud.init Error")
+        return
+    end
+    --获取flash设备信息表中的设备总数
+    log.info("sfud.getDeviceNum",sfud.getDeviceNum())
+    --获取flash设备信息表
+    local sfud_device = sfud.getDeviceTable()
+    --判断是否支持sfud.getInfo接口,支持的话获取 Flash 容量和page大小
+    if sfud.getInfo then
+        log.info("sfud.getInfo", sfud.getInfo(sfud_device))
+    end
+    --定义两个变量,用来控制是通过sfud接口来操作flash还是挂载为sfud lfs文件系统。也可以同时使用两种方式,不过要注意同时使用时flash地址和挂载时的偏移量需要设计好
+    local test_sfud_raw = true
+    local test_sfud_mount = false
+    --通过sfud接口对flash进行写入和读取操作
+    if test_sfud_raw then
+        log.info("sfud.eraseWrite",sfud.eraseWrite(sfud_device,1024,"luatos-sfud1234567890123456789012345678901234567890"))
+        log.info("sfud.read",sfud.read(sfud_device,1024,4))
+    end
+    --挂载为sfud lfs文件系统,通过文件系统相关接口去操作sfud lfs文件系统中的文件,并演示文件的读写、删除、追加等操作。
+    if test_sfud_mount then
+        --挂载sfud lfs文件系统
+        local ret = sfud.mount(sfud_device,"/sfud")
+        log.info("sfud.mount", ret)
+        if ret then
+            log.info("sfud", "挂载成功")
+            log.info("fsstat", fs.fsstat("/sfud"))
+
+            -- 挂载成功后,可以像操作文件一样操作
+            local f = io.open("/sfud/test", "w")
+            local write_str = os.date()
+            log.info("/sfud/test文件写入数据",write_str)
+            --写入当前时间到文件中
+            f:write(write_str)
+            --关闭文件
+            f:close()
+            --读取文件内容并打印
+            local read_str = io.readFile("/sfud/test")
+            log.info("sfud_lfs read", read_str)
+            if read_str == write_str then
+                log.info("写入测试成功,写入字符串与读出字符串一样")
+            else
+                log.info("写入测试失败,写入字符串与读出字符串不一样")
+            end
+            -- 文件追加
+            --文件追加测试写入之前先删除一下
+            os.remove("/sfud/test2")
+            --将"LuatOS"字符串写入到test2文件中
+            io.writeFile("/sfud/test2", "LuatOS-")
+            --以追加的方式打开test2文件
+            local f = io.open("/sfud/test2", "a+")
+            local time_str = os.date()
+            f:write(time_str)
+            write_str = "LuatOS-"..time_str
+            log.info("/sfud/test2",write_str)
+            --关闭文件
+            f:close()
+            read_str = io.readFile("/sfud/test2")
+            log.info("sfud read", read_str)
+            if write_str == read_str then
+                log.info("追加测试成功,写入字符串与读出字符串一样")
+            else
+                log.info("追加测试失败,写入字符串与读出字符串不一样")
+            end
+        else
+            log.info("sfud", "挂载失败")
+        end
+
+
+    end
+end
+--创建并且启动一个task
+--运行这个task的主函数sfud_test_func
+sys.taskInit(sfud_test_func)

+ 52 - 58
module/Air780EHM/demo/ymodem/main.lua

@@ -1,70 +1,64 @@
--- LuaTools需要PROJECT和VERSION这两个信息
-PROJECT = "uart_ymodem"
-VERSION = "1.0.0"
-log.style(1)
-log.info("main", PROJECT, VERSION)
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 1.0
+@date    2025.07.01
+@author  杨鹏
+@usage
+本demo演示的核心功能为:
+使用Air780EHM核心板的UART1连接PC端的串口调试仿真工具SecureCRT,通过Ymodem协议接收文件。
+更多说明参考本目录下的readme.md文件
+]]
 
--- 引入必要的库文件(lua编写), 内部库不需要require
-sys = require("sys")
 
-if wdt then
-    --添加硬狗防止程序卡死,在支持的设备上启用这个功能
-    wdt.init(9000)--初始化watchdog设置为9s
-    sys.timerLoopStart(wdt.feed, 3000)--3s喂一次狗
-end
-
-local uartid = 1 -- 根据实际设备选取不同的uartid
+--[[
+必须定义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 = "ymodem_receive"
+VERSION = "001.000.000"
 
---初始化
-local result = uart.setup(
-    uartid,--串口id
-    115200,--波特率
-    8,--数据位
-    1--停止位
-)
-local ymodem_running = false --  定义一个局部变量,用于表示Ymodem协议是否正在运行
+log.info("main", "project name is ", PROJECT, "version is ", VERSION)
 
-local rxbuff = zbuff.create(1024 + 32) --  创建一个缓冲区,大小为1024 + 32
-local ymodem_handler = ymodem.create("/","save.bin") --  创建一个ymodem处理程序,保存路径为"/",文件名为"save.bin"
-local function ymodem_to() --  定义一个ymodem_to函数,用于发送C字符,并重置ymodem处理程序
-    if not ymodem_running then --  如果ymodem协议没有在运行,则发送请求
-        uart.write(uartid, "C")
-        ymodem.reset(ymodem_handler) --  重置ymodem处理程序
-    end
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
 end
 
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
 
-sys.timerLoopStart(ymodem_to,500) --  每隔500ms调用ymodem_to函数
 
-local function ymodem_rx(id,len) --  定义一个ymodem_rx函数,用于接收数据
-    uart.rx(id,rxbuff) --  从uart接收数据到缓冲区
-    log.info(rxbuff:used()) --  打印缓冲区已使用的大小
-    local result,ack,flag,file_done,all_done = ymodem.receive(ymodem_handler,rxbuff) --  调用ymodem.receive函数,接收数据
-    ymodem_running = result
-    log.info(ymodem_running,ack,flag,file_done,all_done)
-    rxbuff:del()
-    if result then
-        rxbuff:copy(0, ack,flag)
-        uart.tx(id, rxbuff)
-    end
-    if all_done then --  所有数据都接收完毕
-        local exists=io.exists("/save.bin") -- 判断/save.bin文件是否存在
-        if exists then
-            log.info("io", "save.bin file exists:", exists) --  打印日志,判断/save.bin文件是否存在
-            log.info("io", "save.bin file size:", io.fileSize("/save.bin")) --  打印日志,显示/save.bin文件大小
-        else
-            log.info("io", "save.bin file not exists") --  打印日志,/save.bin文件不存在
-        end
-            
-        ymodem_running = false  --再次开始接收
-    end
-    rxbuff:del()
-end
-uart.on(uartid, "receive", ymodem_rx) --  监听串口接收事件
+-- 使用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)
 
-uart.on(uartid, "sent", function(id) --  监听串口发送事件
-    log.info("uart", "sent", id) --  打印发送事件
-end)
+-- 加载 ymodem_receive 功能模块
+require "ymodem_receive"
 
 -- 用户代码已结束---------------------------------------------
 -- 结尾总是这一句

+ 68 - 0
module/Air780EHM/demo/ymodem/readme.md

@@ -0,0 +1,68 @@
+
+## 演示功能概述
+本demo演示的核心功能为:
+使用Air780EHM核心板的UART1连接PC端的串口调试仿真工具SecureCRT,通过Ymodem协议接收文件。
+
+## 演示硬件环境
+
+1、Air780EHM核心板一块
+
+2、TYPE-C USB数据线一根
+
+3、USB转串口线数据线一根
+
+4、Air780EHM核心板和数据线的硬件接线方式为
+
+- Air780EHM核心板通过TYPE-C USB口供电;(核心板背面的功耗测试开关拨到OFF一端)
+
+- 如果测试发现软件频繁重启,重启原因值为:poweron reason 0,可能是供电不足,此时再通过直流稳压电源对核心板的vbat管脚进行4V供电,或者VIN管脚进行5V供电;
+
+- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+- USB转串口数据线,一般来说,白线连接核心板的18/U1TX,绿线连接核心板的17/U1RX,黑线连接核心板的gnd,另外一端连接电脑USB口;
+
+| Air780EHM核心板 | USB转串口数据线 |
+| -------------- | -------------- |
+| U1TX           | 白线           |
+| U1RX           | 绿线           |
+| GND            | GND            |
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+2、[Air780EHM最新版本固件](https://docs.openluat.com/air780epm/luatos/firmware/version/)
+
+3、PC端的串口仿真工具SecureCRT:[下载链接](https://www.vandyke.com/download/index.html)
+
+
+## 演示核心步骤
+
+1、搭建好演示硬件环境
+
+2、不需要修改demo脚本代码
+
+3、Luatools烧录内核固件和demo脚本代码
+
+4、烧录成功后,自动开机运行
+
+5、打开SecureCRT,连接上Air780EHM UATR1端口,等待窗口接收到Air780EHM发送的字符"C" 表示准备接收数据,选择.bin文件发送,等待传输完成,传输完成后,Luatools的运行日志输出:
+``` lua
+[2025-06-25 11:28:30.664][000000185.351] I/main.lua:41	1029
+[2025-06-25 11:28:30.665][000000185.353] D/ymodem save.bin,1024,1
+[2025-06-25 11:28:30.667][000000185.353] I/main.lua:44	true	6	67	false	false
+[2025-06-25 11:28:30.668][000000185.354] I/main.lua:66	uart	sent	1
+[2025-06-25 11:28:30.755][000000185.456] I/main.lua:41	1029
+[2025-06-25 11:28:30.769][000000185.465] I/main.lua:44	true	6	nil	true	false
+[2025-06-25 11:28:30.771][000000185.466] I/main.lua:66	uart	sent	1
+[2025-06-25 11:28:30.800][000000185.491] I/main.lua:41	1
+[2025-06-25 11:28:30.802][000000185.492] I/main.lua:44	true	21	nil	false	false
+[2025-06-25 11:28:30.803][000000185.492] I/main.lua:66	uart	sent	1
+[2025-06-25 11:28:30.832][000000185.522] I/main.lua:41	1
+[2025-06-25 11:28:30.835][000000185.522] I/main.lua:44	true	6	67	false	false
+[2025-06-25 11:28:30.836][000000185.523] I/main.lua:66	uart	sent	1
+[2025-06-25 11:28:30.861][000000185.563] I/main.lua:41	133
+[2025-06-25 11:28:30.866][000000185.563] I/main.lua:44	true	6	nil	false	true
+[2025-06-25 11:28:30.867][000000185.566] I/main.lua:53	io	save.bin file exists:	true
+[2025-06-25 11:28:30.868][000000185.568] I/main.lua:54	io	save.bin file size:	1024
+```

+ 103 - 0
module/Air780EHM/demo/ymodem/ymodem_receive.lua

@@ -0,0 +1,103 @@
+--[[
+@module  main
+@summary ymodem 接收文件应用功能模块 
+@version 1.0
+@date    2025.07.01
+@author  杨鹏
+@usage
+本文件为Air780EHM核心板演示ymodem功能的代码示例,核心业务逻辑为:
+1. 初始化串口通信
+2. 创建Ymodem处理对象
+    local ymodem_handler = ymodem.create("/","save.bin")
+3. 启动请求发送任务
+    uart.write(uartid, "C")
+4. 监听串口数据接收
+    uart.on(uartid, "receive", ymodem_rx)
+5. 处理Ymodem数据包
+    local function ymodem_rx(id,len)
+]]
+
+-- 根据实际设备选取不同的uartid
+local uartid = 1 
+
+--初始化
+local result = uart.setup(
+    uartid,--串口id
+    115200,--波特率
+    8,--数据位
+    1--停止位
+)
+local taskName = "ymodem_to"
+
+-- 处理未识别的消息
+local function ymodem_to_cb(msg)
+	log.info("ymodem_to_cb", msg[1], msg[2], msg[3], msg[4])
+end
+
+--  定义一个局部变量,用于表示Ymodem协议是否正在运行
+local ymodem_running = false 
+
+--  创建一个缓冲区,大小为1024 + 32
+local rxbuff = zbuff.create(1024 + 32) 
+
+--  创建一个ymodem处理程序,保存路径为"/",文件名为"save.bin"
+local ymodem_handler = ymodem.create("/","save.bin")
+
+--  定义一个ymodem_to函数,用于发送C字符,并重置ymodem处理程序
+local function ymodem_to() 
+    while true do
+        --  如果ymodem协议没有在运行,则发送请求;并重置ymodem处理程序
+        if not ymodem_running then 
+            uart.write(uartid, "C")
+            ymodem.reset(ymodem_handler) 
+        end
+        sys.wait(500)
+    end
+end
+
+--  定义一个ymodem_rx函数,用于接收数据
+local function ymodem_rx(id,len) 
+    --  从uart接收数据到缓冲区
+    uart.rx(id,rxbuff) 
+    --  打印缓冲区已使用的大小
+    log.info(rxbuff:used()) 
+    --  调用ymodem.receive函数,接收数据
+    local result,ack,flag,file_done,all_done = ymodem.receive(ymodem_handler,rxbuff) 
+    ymodem_running = result
+    log.info("result:",ymodem_running,ack,flag,file_done,all_done)
+    rxbuff:del()
+    if result then
+        rxbuff:copy(0, ack,flag)
+        uart.tx(id, rxbuff)
+    end
+
+    --  所有数据都接收完毕
+    if all_done then 
+        -- 判断/save.bin文件是否存在
+        local exists=io.exists("/save.bin") 
+
+        -- 判断/save.bin文件是否存在,存在则打印日志,显示/save.bin文件大小;不存在则打印日志,显示/save.bin文件不存在
+        if exists then
+            log.info("io", "save.bin file exists:", exists) 
+            log.info("io", "save.bin file size:", io.fileSize("/save.bin")) 
+        else
+            log.info("io", "save.bin file not exists") 
+        end
+        
+        --ymodem_running置为false,再次开始接收
+        ymodem_running = false 
+    end
+    rxbuff:del()
+end
+
+--  监听串口接收事件
+uart.on(uartid, "receive", ymodem_rx) 
+
+--  监听串口发送事件
+uart.on(uartid, "sent", function(id) 
+    log.info("uart", "sent", id) 
+end)
+
+--创建并且启动一个task
+--运行这个task的主函数ymodem_to
+sysplus.taskInitEx(ymodem_to, taskName,ymodem_to_cb)

+ 57 - 0
module/Air8000/demo/ble/ibeacon/ble_ibeacon.lua

@@ -0,0 +1,57 @@
+--[[
+@module  ble_ibeacon
+@summary Air8000演示ibeacon功能模块
+@version 1.0
+@date    2025.07.01
+@author  wangshihao
+@usage
+本文件为Air8000核心板演示ibeacon功能的代码示例,核心业务逻辑为:
+1. 初始化蓝牙底层框架
+2. 创建BLE对象实例
+3. 配置ibeacon广播数据包
+    - 包含厂商特定数据格式,ibeacon类型标识符
+    - 设置UUID、Major、Minor等关键参数
+4. 启动BLE广播功能
+]]
+
+function ble_callback()
+    -- 无事可做
+end
+
+function ble_ibeacon()
+    local ret = 0
+    sys.wait(500)
+    log.info("开始初始化蓝牙核心")
+    bluetooth_device = bluetooth.init()
+    sys.wait(100)
+    log.info("初始化BLE功能")
+    ble_device = bluetooth_device:ble(ble_callback)
+    sys.wait(100)
+
+    sys.wait(100)
+    log.info("开始设置广播内容")
+
+    local adv_data = string.char(0x4C, 0x00, -- Manufacturer ID(2字节)
+                                0x02, 0x15, -- ibeacon数据类型(2字节)
+                                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, -- UUID(16字节)
+                                0x00, 0x01, -- Major(2字节)
+                                0x00, 0x02, -- Minor(2字节)
+                                0xC0) -- Signal Power(1字节)
+
+    ble_device:adv_create({
+        addr_mode = ble.PUBLIC, -- 广播地址模式, 可选值: ble.PUBLIC, ble.RANDOM, ble.RPA, ble.NRPA
+        channel_map = ble.CHNLS_ALL, -- 广播的通道, 可选值: ble.CHNLS_37, ble.CHNLS_38, ble.CHNLS_39, ble.CHNLS_ALL
+        intv_min = 120, -- 广播间隔最小值, 单位为0.625ms, 最小值为20, 最大值为10240
+        intv_max = 120, -- 广播间隔最大值, 单位为0.625ms, 最小值为20, 最大值为10240
+        adv_data = { -- 支持表格形式, 也支持字符串形式(255字节以内)
+            {ble.FLAGS, string.char(0x06)}, 
+            {ble.MANUFACTURER_SPECIFIC_DATA, adv_data} 
+        }
+    })
+
+    sys.wait(100)
+    log.info("开始广播")
+    ble_device:adv_start()
+end
+
+sys.taskInit(ble_ibeacon)

+ 56 - 50
module/Air8000/demo/ble/ibeacon/main.lua

@@ -1,62 +1,68 @@
--- LuaTools需要PROJECT和VERSION这两个信息
-PROJECT = "ble"
-VERSION = "1.0.0"
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 1.0
+@date    2025.07.01
+@author  wangshihao
+@usage
+本demo演示的核心功能为:
+Air8000核心板演示ibeacon功能;
+iBeacon是一种基于蓝牙低功耗(BLE)技术的室内定位和近场通信技术,
+它允许设备在一定范围内周期性广播ibeacon信息,从而实现位置感知和交互功能。
+本demo中,Air8000核心板作为ibeacon设备,定期广播ibeacon信号,
+其他支持ibeacon的设备可以接收这些信号并进行相应的处理。
 
--- 引入必要的库文件(lua编写), 内部库不需要require
-sys = require("sys")
+更多说明参考本目录下的readme.md文件
+]]
+
+--[[
+必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
+PROJECT:项目名,ascii string类型
+        可以随便定义,只要不使用,就行
+VERSION:项目版本号,ascii string类型
+        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
+            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
+            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
+        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
+]]
+PROJECT = "ble_ibeacon"
+VERSION = "001.000.000"
 
 log.info("main", "project name is ", PROJECT, "version is ", VERSION)
 
--- 通过boot按键方便刷Air8000S
-function PWR8000S(val) gpio.set(23, val) end
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
+end
 
-gpio.debounce(0, 1000)
-gpio.setup(0, function()
-    sys.taskInit(function()
-        log.info("复位Air8000S")
-        PWR8000S(0)
-        sys.wait(20)
-        PWR8000S(1)
-    end)
-end, gpio.PULLDOWN)
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
 
-function ble_callback()
-    -- 无事可做
-end
 
-sys.taskInit(function()
-    local ret = 0
-    sys.wait(500)
-    log.info("开始初始化蓝牙核心")
-    bluetooth_device = bluetooth.init()
-    sys.wait(100)
-    log.info("初始化BLE功能")
-    ble_device = bluetooth_device:ble(ble_callback)
-    sys.wait(100)
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
 
-    sys.wait(100)
-    log.info("开始设置广播内容")
-    local adv_data = string.char(0x4C, 0x00,
-                                0x02, 0x15, 0x01, 0x02, 0x03, 0x04, 0x05,
-                                        0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
-                                        0x00, 0x01,
-                                        0x00, 0x02,
-                                        0x00)
-    ble_device:adv_create({
-        addr_mode = ble.PUBLIC,
-        channel_map = ble.CHNLS_ALL,
-        intv_min = 120,
-        intv_max = 120,
-        adv_data = {
-            {ble.FLAGS, string.char(0x06)},
-            {ble.MANUFACTURER_SPECIFIC_DATA, adv_data}
-        }
-    })
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
 
-    sys.wait(100)
-    log.info("开始广播")
-    ble_device:adv_start()
-end)
+-- 加载 ibeacon 蓝牙功能模块
+require "ble_ibeacon"
 
 -- 用户代码已结束---------------------------------------------
 -- 结尾总是这一句

+ 34 - 0
module/Air8000/demo/ble/ibeacon/readme.md

@@ -0,0 +1,34 @@
+
+## 演示功能概述
+
+将使用Air8000核心板演示ibeacon功能。
+
+## 演示硬件环境
+
+1、Air8000核心板一块
+
+2、TYPE-C USB数据线一根
+
+3、Air8000核心板和数据线的硬件接线方式为
+
+- Air8000核心板通过TYPE-C USB口供电;(核心板USB旁边的开关拨到 "充电" 一端)
+
+- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+[如何使用 LuaTools 烧录软件 - luatos@air8000 - 合宙模组资料中心](https://docs.openluat.com/air8000/luatos/common/download/)
+
+2、[Air8000 V2008版本固件(2025年6月27日及之后发布)](https://gitee.com/openLuat/LuatOS/tree/master/module/Air8000/core)(测试通过的固件是LuatOS-SoC_V2008_Air8000_LVGL_0627.soc)
+
+## 演示核心步骤
+
+1、搭建好演示硬件环境
+
+2、通过Luatools将demo与固件烧录到核心板中
+
+3、烧录成功后,自动开机运行
+
+4、接下来通过蓝牙APP 扫描并查看ibeacon信息

+ 93 - 0
module/Air8000/demo/ble/master/ble_master.lua

@@ -0,0 +1,93 @@
+--[[
+@module  ble_ibeacon
+@summary Air8000演示ibeacon功能模块
+@version 1.0
+@date    2025.07.01
+@author  wangshihao
+@usage
+本文件为Air8000核心板演示master功能的代码示例,核心业务逻辑为:
+1. 初始化蓝牙底层框架
+    bluetooth_device = bluetooth.init()
+2. 创建BLE对象实例
+    local ble_device = bluetooth_device:ble(ble_event_cb)
+3. 创建BLE扫描
+    ble_device:scan_create({})
+4. 开始BLE扫描
+    ble_device:scan_start()
+5. 在回调函数中处理事件
+    a. 扫描报告事件(ble.EVENT_SCAN_REPORT)
+        - 每收到一个扫描report,计数器'scan_count'加1。
+        - 如果扫描次数超过100次,停止扫描,15秒后重新开始(防止过度扫描)。
+        - 检查扫描到的设备广播数据中是否包含"LuatOS"字符串,并且地址类型为0(公开地址)。如果满足条件,停止扫描并尝试连接该设备。
+    b. 连接事件(ble.EVENT_CONN)
+        - 连接成功,打印日志。
+    c. 断开连接事件(ble.EVENT_DISCONN)
+        - 打印日志,重新开始扫描。
+    d. 读写数据事件(ble.EVENT_READ_VALUE, ble.EVENT_WRITE)
+        - 打印日志。
+    e. 通知事件(ble.EVENT_NOTIFY)
+        - 打印日志。
+    f. GATT事件(ble.EVENT_GATT_ITEM, ble.EVENT_GATT_DONE)
+        - 开启指定服务(UUID:FA00)和特征值(UUID:EA01)的通知功能。
+        - 向特征值(UUID:EA02)写入数据(十六进制数据"1234")。
+        - 读取特征值(UUID:EA03)的数据。
+]]
+
+local scan_count = 0
+
+local function ble_callback(ble_device, ble_event, ble_param)
+    if ble_event == ble.EVENT_CONN then
+        log.info("ble", "connect 成功")
+    elseif ble_event == ble.EVENT_DISCONN then
+        log.info("ble", "disconnect", ble_param.reason)
+        sys.timerStart(function() ble_device:scan_start() end, 1000)
+    elseif ble_event == ble.EVENT_WRITE then
+        log.info("ble", "write", ble_param.handle,ble_param.uuid_service:toHex(),ble_param.uuid_characteristic:toHex())
+        log.info("ble", "data", ble_param.data:toHex())
+    elseif ble_event == ble.EVENT_READ_VALUE then
+        log.info("ble", "read", ble_param.handle,ble_param.uuid_service:toHex(),ble_param.uuid_characteristic:toHex(),ble_param.data:toHex())
+    elseif ble_event == ble.EVENT_SCAN_REPORT then
+        print("ble scan report",ble_param.addr_type,ble_param.rssi,ble_param.adv_addr:toHex(),ble_param.data:toHex())
+        scan_count = scan_count + 1
+        if scan_count > 100 then
+            log.info("ble", "扫描次数超过100次, 停止扫描, 15秒后重新开始")
+            scan_count = 0
+            ble_device:scan_stop()
+            sys.timerStart(function() ble_device:scan_start() end, 15000)
+        end
+        -- 注意, 这里是连接到另外一个设备, 设备名称带LuatOS字样
+        if ble_param.addr_type == 0 and ble_param.data:find("LuatOS") then
+            log.info("ble", "停止扫描, 连接设备", ble_param.adv_addr:toHex(), ble_param.addr_type)
+            ble_device:scan_stop()
+            ble_device:connect(ble_param.adv_addr,ble_param.addr_type)
+        end
+    elseif ble_event == ble.EVENT_GATT_ITEM then
+        -- 读取GATT完成, 打印出来
+        log.info("ble", "gatt item", ble_param)
+    elseif ble_event == ble.EVENT_GATT_DONE then
+        log.info("ble", "gatt done", ble_param.service_num)
+        local wt = {uuid_service = string.fromHex("FA00"), uuid_characteristic = string.fromHex("EA01")}
+        ble_device:notify_enable(wt, true) -- 开启通知
+
+        -- 主动写入数据, 但不带通知, 带通知是 write_notify
+        local wt = {uuid_service = string.fromHex("FA00"), uuid_characteristic = string.fromHex("EA02")}
+        ble_device:write_value(wt,string.fromHex("1234"))
+
+        local wt = {uuid_service = string.fromHex("FA00"), uuid_characteristic = string.fromHex("EA03")}
+        ble_device:read_value(wt)
+    end
+end
+
+function ble_master()
+    log.info("开始初始化蓝牙核心")
+    bluetooth_device = bluetooth.init()
+    log.info("初始化BLE功能")
+    ble_device = bluetooth_device:ble(ble_callback)
+
+    -- master
+    ble_device:scan_create({})
+    ble_device:scan_start()
+    -- ble_device:scan_stop()
+end
+
+sys.taskInit(ble_master)

+ 54 - 70
module/Air8000/demo/ble/master/main.lua

@@ -1,83 +1,67 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 1.0
+@date    2025.07.01
+@author  wangshihao
+@usage
+本demo演示的核心功能为:
+演示了Air8000核心板作为BLE Master的核心功能。
+主要功能是扫描周围的BLE设备,当发现设备名称中包含"LuatOS"的设备时,自动连接该设备,然后进行GATT操作(如开启通知、写入数据、读取数据)等。
 
--- LuaTools需要PROJECT和VERSION这两个信息
-PROJECT = "ble"
-VERSION = "1.0.0"
+更多说明参考本目录下的readme.md文件
+]]
 
-log.info("main", "project name is ", PROJECT, "version is ", VERSION)
-
--- 通过boot按键方便刷Air8000S
-function PWR8000S(val) gpio.set(23, val) end
-
-gpio.debounce(0, 1000)
-gpio.setup(0, function()
-    sys.taskInit(function()
-        log.info("复位Air8000S")
-        PWR8000S(0)
-        sys.wait(20)
-        PWR8000S(1)
-    end)
-end, gpio.PULLDOWN)
-
-local scan_count = 0
+--[[
+必须定义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 = "ble_master"
+VERSION = "001.000.000"
 
-local function ble_callback(ble_device, ble_event, ble_param)
-    if ble_event == ble.EVENT_CONN then
-        log.info("ble", "connect 成功")
-    elseif ble_event == ble.EVENT_DISCONN then
-        log.info("ble", "disconnect", ble_param.reason)
-        sys.timerStart(function() ble_device:scan_start() end, 1000)
-    elseif ble_event == ble.EVENT_WRITE then
-        log.info("ble", "write", ble_param.handle,ble_param.uuid_service:toHex(),ble_param.uuid_characteristic:toHex())
-        log.info("ble", "data", ble_param.data:toHex())
-    elseif ble_event == ble.EVENT_READ_VALUE then
-        log.info("ble", "read", ble_param.handle,ble_param.uuid_service:toHex(),ble_param.uuid_characteristic:toHex(),ble_param.data:toHex())
-    elseif ble_event == ble.EVENT_SCAN_REPORT then
-        print("ble scan report",ble_param.addr_type,ble_param.rssi,ble_param.adv_addr:toHex(),ble_param.data:toHex())
-        scan_count = scan_count + 1
-        if scan_count > 100 then
-            log.info("ble", "扫描次数超过100次, 停止扫描, 15秒后重新开始")
-            scan_count = 0
-            ble_device:scan_stop()
-            sys.timerStart(function() ble_device:scan_start() end, 15000)
-        end
-        -- 注意, 这里是连接到另外一个设备, 设备名称带LuatOS字样
-        if ble_param.addr_type == 0 and ble_param.data:find("LuatOS") then
-            log.info("ble", "停止扫描, 连接设备", ble_param.adv_addr:toHex(), ble_param.addr_type)
-            ble_device:scan_stop()
-            ble_device:connect(ble_param.adv_addr,ble_param.addr_type)
-        end
-    elseif ble_event == ble.EVENT_GATT_ITEM then
-        -- 读取GATT完成, 打印出来
-        log.info("ble", "gatt item", ble_param)
-    elseif ble_event == ble.EVENT_GATT_DONE then
-        log.info("ble", "gatt done", ble_param.service_num)
-        local wt = {uuid_service = string.fromHex("FA00"), uuid_characteristic = string.fromHex("EA01")}
-        ble_device:notify_enable(wt, true) -- 开启通知
-
-        -- 主动写入数据, 但不带通知, 带通知是 write_notify
-        local wt = {uuid_service = string.fromHex("FA00"), uuid_characteristic = string.fromHex("EA02")}
-        ble_device:write_value(wt,string.fromHex("1234"))
+log.info("main", "project name is ", PROJECT, "version is ", VERSION)
 
-        local wt = {uuid_service = string.fromHex("FA00"), uuid_characteristic = string.fromHex("EA03")}
-        ble_device:read_value(wt)
-    end
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
 end
 
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
+
 
-sys.taskInit(function()
-    log.info("开始初始化蓝牙核心")
-    bluetooth_device = bluetooth.init()
-    log.info("初始化BLE功能")
-    ble_device = bluetooth_device:ble(ble_callback)
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
 
-    -- master
-    ble_device:scan_create({})
-    ble_device:scan_start()
-    -- ble_device:scan_stop()
-end)
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
 
+-- 加载 master 蓝牙功能模块
+require "ble_master"
 
 -- 用户代码已结束---------------------------------------------
 -- 结尾总是这一句
 sys.run()
--- sys.run()之后后面不要加任何语句!!!!!
+-- sys.run()之后后面不要加任何语句!!!!!

+ 34 - 0
module/Air8000/demo/ble/master/readme.md

@@ -0,0 +1,34 @@
+
+## 演示功能概述
+
+将使用Air8000核心板演示master功能。
+
+## 演示硬件环境
+
+1、Air8000核心板一块
+
+2、TYPE-C USB数据线一根
+
+3、Air8000核心板和数据线的硬件接线方式为
+
+- Air8000核心板通过TYPE-C USB口供电;(核心板USB旁边的开关拨到 "充电" 一端)
+
+- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+[如何使用 LuaTools 烧录软件 - luatos@air8000 - 合宙模组资料中心](https://docs.openluat.com/air8000/luatos/common/download/)
+
+2、[Air8000 V2008版本固件(2025.06.27及之后发布)](https://gitee.com/openLuat/LuatOS/tree/master/module/Air8000/core)(测试通过的固件是LuatOS-SoC_V2008_Air8000_LVGL_0627.soc)
+
+## 演示核心步骤
+
+1、搭建好演示硬件环境
+
+2、通过Luatools将demo与固件烧录到核心板中
+
+3、烧录成功后,自动开机运行
+
+4、接下来通过蓝牙APP 演示Air8000的BLE master功能

+ 124 - 0
module/Air8000/demo/ble/peripheral/ble_peripheral.lua

@@ -0,0 +1,124 @@
+--[[
+@module  ble_ibeacon
+@summary Air8000演示ibeacon功能模块
+@version 1.0
+@date    2025.07.01
+@author  wangshihao
+@usage
+本文件为Air8000核心板演示peripheral功能的代码示例,核心业务逻辑为:
+从机模式(peripheral)的基本流程(概要描述)
+1. 初始化蓝牙框架
+2. 创建BLE对象
+    local ble_device = bluetooth_device:ble(ble_event_cb)
+3. 创建GATT描述
+    local att_db = {xxx}
+4. 创建广播信息
+    ble_device:adv_create(adv_data)
+5. 开始广播
+    ble_device:adv_start()
+6. 等待连接
+7. 在回调函数中处理连接事件, 如接收数据, 发送数据等
+]]
+
+local att_db = { -- Service
+    string.fromHex("FA00"), -- Service UUID
+    -- Characteristic
+    { -- Characteristic 1
+        string.fromHex("EA01"), -- Characteristic UUID Value
+        ble.NOTIFY | ble.READ | ble.WRITE -- Properties
+    }, { -- Characteristic 2
+        string.fromHex("EA02"), ble.WRITE
+    }, { -- Characteristic 3
+        string.fromHex("EA03"), ble.READ
+    }, { -- Characteristic 4
+        string.fromHex("EA04"), ble.IND | ble.READ
+    }
+}
+
+ble_stat = false
+
+local function ble_callback(dev, evt, param)
+    if evt == ble.EVENT_CONN then
+        log.info("ble", "connect 成功", param, param and param.addr and param.addr:toHex() or "unknow")
+        ble_stat = true
+    elseif evt == ble.EVENT_DISCONN then
+        log.info("ble", "disconnect")
+        ble_stat = false
+        -- 1秒后重新开始广播
+        sys.timerStart(function() dev:adv_start() end, 1000)
+    elseif evt == ble.EVENT_WRITE then
+        -- 收到写请求
+        log.info("ble", "接收到写请求", param.uuid_service:toHex(), param.uuid_characteristic:toHex(), param.data:toHex())
+    end
+end
+
+local bt_scan = false -- 是否扫描蓝牙
+
+function ble_peripheral()
+    local ret = 0
+    sys.wait(500)
+    log.info("开始初始化蓝牙核心")
+    bluetooth_device = bluetooth.init()
+    sys.wait(100)
+    log.info("初始化BLE功能")
+    ble_device = bluetooth_device:ble(ble_callback)
+    if ble_device == nil then
+        log.error("当前固件不支持完整的BLE")
+        return
+    end
+    sys.wait(100)
+
+    log.info('开始创建GATT')
+    ret = ble_device:gatt_create(att_db)
+    log.info("创建的GATT", ret)
+
+    sys.wait(100)
+    log.info("开始设置广播内容")
+    ble_device:adv_create({
+        addr_mode = ble.PUBLIC,
+        channel_map = ble.CHNLS_ALL,
+        intv_min = 120,
+        intv_max = 120,
+        adv_data = {
+            {ble.FLAGS, string.char(0x06)},
+            {ble.COMPLETE_LOCAL_NAME, "LuatOS123"},
+            {ble.SERVICE_DATA, string.fromHex("FE01")},
+            {ble.MANUFACTURER_SPECIFIC_DATA, string.fromHex("05F0")}
+        }
+    })
+
+    sys.wait(100)
+    log.info("开始广播")
+    ble_device:adv_start()
+
+        
+    -- 放入预设值, 注意是有READ属性的特性才能读取
+    -- 手机APP设置MTU到256
+    local wt = {
+        uuid_service = string.fromHex("FA00"),
+        uuid_characteristic = string.fromHex("EA01"), 
+    }
+    ble_device:write_value(wt, "12345678901234567890")
+
+    while 1 do
+        sys.wait(3000)
+        if ble_stat then
+            local wt = {
+                uuid_service = string.fromHex("FA00"),
+                uuid_characteristic = string.fromHex("EA01"), 
+            }
+            local result = ble_device:write_notify(wt, "123456" .. os.date())
+            log.info("ble", "发送数据", result)
+        else
+            -- log.info("等待连接成功之后发送数据")
+        end
+        
+        local wt = {
+            uuid_service = string.fromHex("FA00"),
+            uuid_characteristic = string.fromHex("EA03"), 
+        }
+        ble_device:write_value(wt, "8888 123454")
+    end
+end
+
+sys.taskInit(ble_peripheral)

+ 51 - 112
module/Air8000/demo/ble/peripheral/main.lua

@@ -1,125 +1,64 @@
--- LuaTools需要PROJECT和VERSION这两个信息
-PROJECT = "ble"
-VERSION = "1.0.0"
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 1.0
+@date    2025.07.01
+@author  wangshihao
+@usage
+本demo演示的核心功能为:
+Air8000的BLE从机模式,通过示例演示了如何发送通知,以及如何通过手机对从机设备进行读写操作。
 
--- 引入必要的库文件(lua编写), 内部库不需要require
-sys = require("sys")
+更多说明参考本目录下的readme.md文件
+]]
 
-log.info("main", "project name is ", PROJECT, "version is ", VERSION)
-
--- 通过boot按键方便刷Air8000S
-function PWR8000S(val) gpio.set(23, val) end
-
-gpio.debounce(0, 1000)
-gpio.setup(0, function()
-    sys.taskInit(function()
-        log.info("复位Air8000S")
-        PWR8000S(0)
-        sys.wait(20)
-        PWR8000S(1)
-    end)
-end, gpio.PULLDOWN)
-
-local att_db = { -- Service
-    string.fromHex("FA00"), -- Service UUID
-    -- Characteristic
-    { -- Characteristic 1
-        string.fromHex("EA01"), -- Characteristic UUID Value
-        ble.NOTIFY | ble.READ | ble.WRITE -- Properties
-    }, { -- Characteristic 2
-        string.fromHex("EA02"), ble.WRITE
-    }, { -- Characteristic 3
-        string.fromHex("EA03"), ble.READ
-    }, { -- Characteristic 4
-        string.fromHex("EA04"), ble.IND | ble.READ
-    }
-}
+--[[
+必须定义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 = "ble_peripheral"
+VERSION = "001.000.000"
 
-ble_stat = false
+log.info("main", "project name is ", PROJECT, "version is ", VERSION)
 
-local function ble_callback(dev, evt, param)
-    if evt == ble.EVENT_CONN then
-        log.info("ble", "connect 成功", param, param and param.addr and param.addr:toHex() or "unknow")
-        ble_stat = true
-    elseif evt == ble.EVENT_DISCONN then
-        log.info("ble", "disconnect")
-        ble_stat = false
-        -- 1秒后重新开始广播
-        sys.timerStart(function() dev:adv_start() end, 1000)
-    elseif evt == ble.EVENT_WRITE then
-        -- 收到写请求
-        log.info("ble", "接收到写请求", param.uuid_service:toHex(), param.uuid_characteristic:toHex(), param.data:toHex())
-    end
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
 end
 
-local bt_scan = false -- 是否扫描蓝牙
-
-sys.taskInit(function()
-    local ret = 0
-    sys.wait(500)
-    log.info("开始初始化蓝牙核心")
-    bluetooth_device = bluetooth.init()
-    sys.wait(100)
-    log.info("初始化BLE功能")
-    ble_device = bluetooth_device:ble(ble_callback)
-    if ble_device == nil then
-        log.error("当前固件不支持完整的BLE")
-        return
-    end
-    sys.wait(100)
-
-    log.info('开始创建GATT')
-    ret = ble_device:gatt_create(att_db)
-    log.info("创建的GATT", ret)
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
 
-    sys.wait(100)
-    log.info("开始设置广播内容")
-    ble_device:adv_create({
-        addr_mode = ble.PUBLIC,
-        channel_map = ble.CHNLS_ALL,
-        intv_min = 120,
-        intv_max = 120,
-        adv_data = {
-            {ble.FLAGS, string.char(0x06)},
-            {ble.COMPLETE_LOCAL_NAME, "LuatOS123"},
-            {ble.SERVICE_DATA, string.fromHex("FE01")},
-            {ble.MANUFACTURER_SPECIFIC_DATA, string.fromHex("05F0")}
-        }
-    })
 
-    sys.wait(100)
-    log.info("开始广播")
-    ble_device:adv_start()
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
 
-        
-    -- 放入预设值, 注意是有READ属性的特性才能读取
-    -- 手机APP设置MTU到256
-    local wt = {
-        uuid_service = string.fromHex("FA00"),
-        uuid_characteristic = string.fromHex("EA01"), 
-    }
-    ble_device:write_value(wt, "12345678901234567890")
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
 
-    while 1 do
-        sys.wait(3000)
-        if ble_stat then
-            local wt = {
-                uuid_service = string.fromHex("FA00"),
-                uuid_characteristic = string.fromHex("EA01"), 
-            }
-            local result = ble_device:write_notify(wt, "123456" .. os.date())
-            log.info("ble", "发送数据", result)
-        else
-            -- log.info("等待连接成功之后发送数据")
-        end
-        
-        local wt = {
-            uuid_service = string.fromHex("FA00"),
-            uuid_characteristic = string.fromHex("EA03"), 
-        }
-        ble_device:write_value(wt, "8888 123454")
-    end
-end)
+-- 加载 peripheral 蓝牙功能模块
+require "ble_peripheral"
 
 -- 用户代码已结束---------------------------------------------
 -- 结尾总是这一句

+ 3 - 5
module/Air8000/demo/ble/peripheral/readme.md

@@ -21,16 +21,14 @@
 
 [如何使用 LuaTools 烧录软件 - luatos@air8000 - 合宙模组资料中心](https://docs.openluat.com/air8000/luatos/common/download/)
 
-2、[Air8000 固件](https://gitee.com/openLuat/LuatOS/tree/master/module/Air8000/core)
-
-3、[Air8000 BLE从机代码](https://gitee.com/openLuat/LuatOS/tree/master/module/Air8000/demo/ble/peripheral)
+2、[Air8000 V2008版本固件(2025.06.27及之后发布)](https://gitee.com/openLuat/LuatOS/tree/master/module/Air8000/core)(测试通过的固件是LuatOS-SoC_V2008_Air8000_LVGL_0627.soc)
 
 ## 演示核心步骤
 
-1、核心板通过usb数据线连接到电脑上
+1、搭建好演示硬件环境
 
 2、通过Luatools将demo与固件烧录到核心板中
 
 3、烧录成功后,自动开机运行
 
-4、接下来通过蓝牙APP 连接作为蓝牙从机设备的Air8000进行操作,详见文档:https://docs.openluat.com/air8000/luatos/app/BLE/peripheral/
+4、接下来通过蓝牙APP 连接作为蓝牙从机设备的Air8000进行操作

+ 70 - 0
module/Air8000/demo/ble/scan/ble_scan.lua

@@ -0,0 +1,70 @@
+--[[
+@module  ble_ibeacon
+@summary Air8000演示ibeacon功能模块
+@version 1.0
+@date    2025.07.01
+@author  wangshihao
+@usage
+本文件为Air8000核心板演示scan功能的代码示例,核心业务逻辑为:
+观察者模式(scan)的基本流程(概要描述)
+1. 初始化蓝牙框架
+2. 创建BLE对象
+local ble_device = bluetooth_device:ble(ble_event_cb)
+3.设置扫描模式
+ble_device:scan_create() -- 使用默认参数, addr_mode=0, scan_interval=100, scan_window=100
+4. 开始扫描
+ble_device:scan_start()
+5. 在回调函数中处理扫描事件, 如接收设备信息等
+6. 按需停止扫描
+ble_device:scan_stop()
+]]
+
+local function ble_callback(ble_device, ble_event, ble_param)
+    if ble_event == ble.EVENT_SCAN_INIT then
+        log.info("ble", "scan init")
+    elseif ble_event == ble.EVENT_SCAN_REPORT then
+        log.info("ble", "scan report", ble_param.rssi, ble_param.adv_addr:toHex(), ble_param.data:toHex())
+        -- 解析广播数据, 日志很多, 按需使用
+        -- local adv_data = ble_device:adv_decode(ble_param.data)
+        -- if adv_data then
+        --     for k, v in pairs(adv_data) do
+        --         log.info("ble", "adv data", v.len, v.tp, v.data:toHex())
+        --     end
+        -- end
+        -- if ble_param.data:byte(1) == 0x1A then
+        --     log.info("ble", "ibeacon数据", ble_param.rssi, ble_param.adv_addr:toHex(), ble_param.data:toHex())
+        -- end
+    elseif ble_event == ble.EVENT_SCAN_STOP then
+        log.info("ble", "scan stop")
+    end
+end
+
+local bt_scan = false   -- 是否扫描蓝牙
+
+function ble_scan()
+    sys.wait(500)
+    log.info("开始初始化蓝牙核心")
+    bluetooth_device = bluetooth.init()
+    sys.wait(100)
+    log.info("初始化BLE功能")
+    ble_device = bluetooth_device:ble(ble_callback)
+    if ble_device == nil then
+        log.error("当前固件不支持完整的BLE")
+        return
+    end
+    sys.wait(100)
+    -- 扫描模式
+    sys.wait(1000)
+    ble_device:scan_create() -- 使用默认参数, addr_mode=0, scan_interval=100, scan_window=100
+    -- ble_device:scan_create(0, 10, 10) -- 使用自定义参数
+    sys.wait(100)
+    log.info("开始扫描")
+    ble_device:scan_start()
+
+    -- sys.wait(15000)
+    -- log.info("停止扫描")
+    -- ble_device:scan_stop()
+
+end
+
+sys.taskInit(ble_scan)

+ 48 - 67
module/Air8000/demo/ble/scan/main.lua

@@ -1,83 +1,64 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 1.0
+@date    2025.07.01
+@author  wangshihao
+@usage
+本demo演示的核心功能为:
+Air8000的BLE的观察者模式(SCAN),通过示例演示了如何开启蓝牙设备的观察者模式(SCAN),扫描附近的蓝牙设备信息并打印出来。
 
--- LuaTools需要PROJECT和VERSION这两个信息
-PROJECT = "ble"
-VERSION = "1.0.0"
+更多说明参考本目录下的readme.md文件
+]]
 
--- 引入必要的库文件(lua编写), 内部库不需要require
-sys = require("sys")
+--[[
+必须定义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 = "ble_scan"
+VERSION = "001.000.000"
 
 log.info("main", "project name is ", PROJECT, "version is ", VERSION)
 
--- 通过boot按键方便刷Air8000S
-function PWR8000S(val)
-    gpio.set(23, val)
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
 end
 
-gpio.debounce(0, 1000)
-gpio.setup(0, function()
-    sys.taskInit(function()
-        log.info("复位Air8000S")
-        PWR8000S(0)
-        sys.wait(20)
-        PWR8000S(1)
-    end)
-end, gpio.PULLDOWN)
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
 
-local function ble_callback(ble_device, ble_event, ble_param)
-    if ble_event == ble.EVENT_SCAN_INIT then
-        log.info("ble", "scan init")
-    elseif ble_event == ble.EVENT_SCAN_REPORT then
-        log.info("ble", "scan report", ble_param.rssi, ble_param.adv_addr:toHex(), ble_param.data:toHex())
-        -- 解析广播数据, 日志很多, 按需使用
-        -- local adv_data = ble_device:adv_decode(ble_param.data)
-        -- if adv_data then
-        --     for k, v in pairs(adv_data) do
-        --         log.info("ble", "adv data", v.len, v.tp, v.data:toHex())
-        --     end
-        -- end
-        -- if ble_param.data:byte(1) == 0x1A then
-        --     log.info("ble", "ibeacon数据", ble_param.rssi, ble_param.adv_addr:toHex(), ble_param.data:toHex())
-        -- end
-    elseif ble_event == ble.EVENT_SCAN_STOP then
-        log.info("ble", "scan stop")
-    end
-end
-
-local bt_scan = false   -- 是否扫描蓝牙
-
-sys.taskInit(function()
-    sys.wait(500)
-    log.info("开始初始化蓝牙核心")
-    bluetooth_device = bluetooth.init()
-    sys.wait(100)
-    log.info("初始化BLE功能")
-    ble_device = bluetooth_device:ble(ble_callback)
-    if ble_device == nil then
-        log.error("当前固件不支持完整的BLE")
-        return
-    end
-    sys.wait(100)
-    -- 扫描模式
-    sys.wait(1000)
-    ble_device:scan_create() -- 使用默认参数, addr_mode=0, scan_interval=100, scan_window=100
-    -- ble_device:scan_create(0, 10, 10) -- 使用自定义参数
-    sys.wait(100)
-    log.info("开始扫描")
-    ble_device:scan_start()
-
-    -- sys.wait(15000)
-    -- log.info("停止扫描")
-    -- ble_device:scan_stop()
 
-end)
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
 
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
 -- sys.timerLoopStart(function()
---     print("hi, LuatOS")
---     print("mem.lua", rtos.meminfo())
---     print("mem.sys", rtos.meminfo("sys"))
---     print("mem.psram", rtos.meminfo("psram"))
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
 -- end, 3000)
 
+-- 加载 scan 蓝牙功能模块
+require "ble_scan"
 
 -- 用户代码已结束---------------------------------------------
 -- 结尾总是这一句

+ 2 - 4
module/Air8000/demo/ble/scan/readme.md

@@ -21,13 +21,11 @@
 
 [如何使用 LuaTools 烧录软件 - luatos@air8000 - 合宙模组资料中心](https://docs.openluat.com/air8000/luatos/common/download/)
 
-2、[Air8000 固件](https://gitee.com/openLuat/LuatOS/tree/master/module/Air8000/core)
-
-3、[Air8000 BLE扫描代码](https://gitee.com/openLuat/LuatOS/blob/master/module/Air8000/demo/ble/scan)
+2、[Air8000 V2008版本固件(2025.06.27及之后发布)](https://gitee.com/openLuat/LuatOS/tree/master/module/Air8000/core)(测试通过的固件是LuatOS-SoC_V2008_Air8000_LVGL_0627.soc)
 
 ## 演示核心步骤
 
-1、核心板通过usb数据线连接到电脑上
+1、搭建好演示硬件环境
 
 2、通过Luatools将demo与固件烧录到核心板中
 

+ 6 - 0
module/Air8101/demo/socket/client/long_connection/main.lua

@@ -1,4 +1,10 @@
 --[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
 本demo演示的核心功能为:
 1、创建四路socket连接,详情如下
 - 创建一个tcp client,连接tcp server;

+ 6 - 0
module/Air8101/demo/socket/client/long_connection/sntp_app.lua

@@ -1,4 +1,10 @@
 --[[
+@module  sntp_app
+@summary sntp时间同步应用功能模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
 本文件为sntp时间同步应用功能模块,核心业务逻辑为:
 1、连接ntp服务器进行时间同步;
 2、如果同步成功,1小时之后重新发起同步动作;

+ 6 - 0
module/Air8101/demo/socket/client/long_connection/tcp_client_main.lua

@@ -1,4 +1,10 @@
 --[[
+@module  tcp_client_main
+@summary tcp client socket主应用功能模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
 本文件为tcp client socket主应用功能模块,核心业务逻辑为:
 1、创建一个tcp client socket,连接server;
 2、处理连接异常,出现异常后执行重连动作;

+ 6 - 0
module/Air8101/demo/socket/client/long_connection/tcp_client_receiver.lua

@@ -1,4 +1,10 @@
 --[[
+@module  tcp_client_receiver
+@summary tcp client socket数据接收应用功能模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
 本文件为tcp client socket数据接收应用功能模块,核心业务逻辑为:
 从内核读取接收到的数据,然后将数据发送给其他应用功能模块做进一步处理;
 

+ 6 - 0
module/Air8101/demo/socket/client/long_connection/tcp_client_sender.lua

@@ -1,4 +1,10 @@
 --[[
+@module  tcp_client_sender
+@summary tcp client socket数据发送应用功能模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
 本文件为tcp client socket数据发送应用功能模块,核心业务逻辑为:
 1、sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)订阅"SEND_DATA_REQ"消息,将其他应用模块需要发送的数据存储到队列send_queue中;
 2、tcp_client_main主任务调用tcp_client_sender.proc接口,遍历队列send_queue,逐条发送数据到server;

+ 6 - 0
module/Air8101/demo/socket/client/long_connection/tcp_ssl_ca_main.lua

@@ -1,4 +1,10 @@
 --[[
+@module  tcp_ssl_ca_main
+@summary tcp_ssl_ca client socket主应用功能模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
 本文件为tcp_ssl_ca client socket主应用功能模块,核心业务逻辑为:
 1、创建一个tcp_ssl_ca client socket,连接server;
 2、处理连接异常,出现异常后执行重连动作;

+ 6 - 0
module/Air8101/demo/socket/client/long_connection/tcp_ssl_ca_receiver.lua

@@ -1,4 +1,10 @@
 --[[
+@module  tcp_ssl_ca_receiver
+@summary tcp_ssl_ca client socket数据接收应用功能模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
 本文件为tcp_ssl_ca client socket数据接收应用功能模块,核心业务逻辑为:
 从内核读取接收到的数据,然后将数据发送给其他应用功能模块做进一步处理;
 

+ 6 - 0
module/Air8101/demo/socket/client/long_connection/tcp_ssl_ca_sender.lua

@@ -1,4 +1,10 @@
 --[[
+@module  tcp_ssl_ca_sender
+@summary tcp_ssl_ca client socket数据发送应用功能模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
 本文件为tcp_ssl_ca client socket数据发送应用功能模块,核心业务逻辑为:
 1、sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)订阅"SEND_DATA_REQ"消息,将其他应用模块需要发送的数据存储到队列send_queue中;
 2、tcp_ssl_ca_main主任务调用tcp_ssl_ca_sender.proc接口,遍历队列send_queue,逐条发送数据到server;

+ 6 - 0
module/Air8101/demo/socket/client/long_connection/tcp_ssl_main.lua

@@ -1,4 +1,10 @@
 --[[
+@module  tcp_ssl_main
+@summary tcp_ssl client socket主应用功能模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
 本文件为tcp_ssl client socket主应用功能模块,核心业务逻辑为:
 1、创建一个tcp_ssl client socket,连接server;
 2、处理连接异常,出现异常后执行重连动作;

+ 6 - 0
module/Air8101/demo/socket/client/long_connection/tcp_ssl_receiver.lua

@@ -1,4 +1,10 @@
 --[[
+@module  tcp_ssl_receiver
+@summary tcp_ssl client socket数据接收应用功能模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
 本文件为tcp_ssl client socket数据接收应用功能模块,核心业务逻辑为:
 从内核读取接收到的数据,然后将数据发送给其他应用功能模块做进一步处理;
 

+ 6 - 0
module/Air8101/demo/socket/client/long_connection/tcp_ssl_sender.lua

@@ -1,4 +1,10 @@
 --[[
+@module  tcp_ssl_sender
+@summary tcp_ssl client socket数据发送应用功能模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
 本文件为tcp_ssl client socket数据发送应用功能模块,核心业务逻辑为:
 1、sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)订阅"SEND_DATA_REQ"消息,将其他应用模块需要发送的数据存储到队列send_queue中;
 2、tcp_ssl_main主任务调用tcp_ssl_sender.proc接口,遍历队列send_queue,逐条发送数据到server;

+ 11 - 0
module/Air8101/demo/socket/client/long_connection/test_app.lua

@@ -3,6 +3,17 @@
 仅调试需要,项目量产时不需要;
 ]]
 
+--[[
+@module  test_app
+@summary 测试应用功能模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
+本文件为测试应用功能模块,用来测试其他功能模块的外部接口;
+仅调试需要,项目量产时不需要;
+]]
+
 
 -- 模拟四个socket client从server收到了数据,然后publish消息"RECV_DATA_FROM_SERVER"
 sys.taskInit(function()

+ 7 - 1
module/Air8101/demo/socket/client/long_connection/timer_app.lua

@@ -1,9 +1,15 @@
 --[[
+@module  timer_app
+@summary 定时器应用功能模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
 本文件为定时器应用功能模块,核心业务逻辑为:
 创建一个5秒的循环定时器,每次产生一段数据,通知四个socket client进行处理;
 
 本文件的对外接口有一个:
-1、sys.publish("SEND_DATA_REQ", "timer", data),通过publish通知其他应用功能模块处理data数据;
+1、sys.publish("SEND_DATA_REQ", "timer", data),通过publish通知其他应用功能模块处理data数据
 ]]
 
 local data = 1

+ 7 - 1
module/Air8101/demo/socket/client/long_connection/uart_app.lua

@@ -1,4 +1,10 @@
 --[[
+@module  uart_app
+@summary 串口应用功能模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
 本文件为串口应用功能模块,核心业务逻辑为:
 1、打开uart1,波特率115200,数据位8,停止位1,无奇偶校验位;
 2、uart1和pc端的串口工具相连;
@@ -7,7 +13,7 @@
 
 本文件的对外接口有两个:
 1、sys.publish("SEND_DATA_REQ", "uart", read_buf),通过publish通知其他应用功能模块处理read_buf数据;
-2、sys.subscribe("RECV_DATA_FROM_SERVER", recv_data_from_server_proc),订阅RECV_DATA_FROM_SERVER消息,处理消息携带的数据;
+2、sys.subscribe("RECV_DATA_FROM_SERVER", recv_data_from_server_proc),订阅RECV_DATA_FROM_SERVER消息,处理消息携带的数据;
 ]]
 
 

+ 6 - 0
module/Air8101/demo/socket/client/long_connection/udp_client_main.lua

@@ -1,4 +1,10 @@
 --[[
+@module  udp_client_receiver
+@summary udp client socket主应用功能模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
 本文件为udp client socket主应用功能模块,核心业务逻辑为:
 1、创建一个udp client socket,连接server;
 2、处理连接异常,出现异常后执行重连动作;

+ 6 - 0
module/Air8101/demo/socket/client/long_connection/udp_client_receiver.lua

@@ -1,4 +1,10 @@
 --[[
+@module  udp_client_receiver
+@summary udp client socket数据接收应用功能模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
 本文件为udp client socket数据接收应用功能模块,核心业务逻辑为:
 从内核读取接收到的数据,然后将数据发送给其他应用功能模块做进一步处理;
 

+ 6 - 0
module/Air8101/demo/socket/client/long_connection/udp_client_sender.lua

@@ -1,4 +1,10 @@
 --[[
+@module  udp_client_sender
+@summary udp client socket数据发送应用功能模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
 本文件为udp client socket数据发送应用功能模块,核心业务逻辑为:
 1、sys.subscribe("SEND_DATA_REQ", send_data_req_proc_func)订阅"SEND_DATA_REQ"消息,将其他应用模块需要发送的数据存储到队列send_queue中;
 2、udp_client_main主任务调用udp_client_sender.proc接口,遍历队列send_queue,逐条发送数据到server;

+ 8 - 2
module/Air8101/demo/socket/client/long_connection/wifi_app.lua

@@ -1,14 +1,20 @@
 --[[
+@module  wifi_app
+@summary WIFI网络连接管理应用功能模块 
+@version 1.0
+@date    2025.07.01
+@author  朱天华
+@usage
 本文件为WIFI网络连接管理应用功能模块,核心业务逻辑为:
 1、初始化WIFI网络;
 2、连接WIFI路由器;
 3、和WIFI路由器之间的连接状态发生变化时,在日志中进行打印;
 
-本文件没有对外接口;
+本文件没有对外接口,直接在main.lua中require "wifi_app"就可以加载运行
 ]]
 
 local function ip_ready_func()
-    log.info("wlan_connect.ip_ready_func", "IP_READY")
+    log.info("wlan_connect.ip_ready_func", "IP_READY", json.encode(wlan.getInfo()))
 end
 
 local function ip_lose_func()