Browse Source

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

alienwalker 7 months ago
parent
commit
e1f5e02c4f

+ 3 - 2
lua/src/liolib.c

@@ -64,8 +64,9 @@ end
 #include "lauxlib.h"
 #include "lualib.h"
 
-
-
+#define l_fseek luat_fs_fseek
+#define l_ftell luat_fs_ftell
+#define l_seeknum int32_t
 
 /*
 ** Change this macro to accept other modes for 'fopen' besides

+ 5 - 0
luat/modules/luat_lib_sensor.c

@@ -15,6 +15,11 @@
 #include "luat_gpio.h"
 #include "luat_zbuff.h"
 #ifdef LUAT_USE_SENSOR
+
+#ifdef LUAT_USE_DRV_GPIO
+#include "luat/drv_gpio.h"
+#endif
+
 #define LUAT_LOG_TAG "sensor"
 #include "luat_log.h"
 

+ 9 - 2
luat/vfs/luat_fs_luadb.c

@@ -114,6 +114,10 @@ size_t luat_luadb_read(luadb_fs_t *fs, int fd, void *dst, size_t size) {
         return 0;
     luadb_fd_t *fdt = &fs->fds[fd];
     int re = size;
+    if (fdt->fd_pos >= fdt->file->size) {
+        //LLOGD("luadb read name %s offset %d size %d ret 0", fdt->file->name, fdt->fd_pos, size);
+        return 0; // 已经读完了
+    }
     if (fdt->fd_pos + size > fdt->file->size) {
         re = fdt->file->size - fdt->fd_pos;
     }
@@ -126,14 +130,14 @@ size_t luat_luadb_read(luadb_fs_t *fs, int fd, void *dst, size_t size) {
         fdt->fd_pos += re;
     }
     //LLOGD("luadb read name %s offset %d size %d ret %d", fdt->file->name, fdt->fd_pos, size, re);
-    return re;
+    return re > 0 ? re : 0;
 }
 
 long luat_luadb_lseek(luadb_fs_t *fs, int fd, long /*off_t*/ offset, int mode) {
     if (fd < 0 || fd >= LUAT_LUADB_MAX_OPENFILE || fs->fds[fd].file == NULL)
         return -1;
     if (mode == SEEK_END) {
-        fs->fds[fd].fd_pos = fs->fds[fd].file->size;
+        fs->fds[fd].fd_pos = fs->fds[fd].file->size - offset;
     }
     else if (mode == SEEK_CUR) {
         fs->fds[fd].fd_pos += offset;
@@ -141,6 +145,9 @@ long luat_luadb_lseek(luadb_fs_t *fs, int fd, long /*off_t*/ offset, int mode) {
     else {
         fs->fds[fd].fd_pos = offset;
     }
+    if (fs->fds[fd].fd_pos > fs->fds[fd].file->size) {
+        fs->fds[fd].fd_pos = fs->fds[fd].file->size;
+    }
     return fs->fds[fd].fd_pos;
 }
 

+ 6 - 1
luat/vfs/luat_fs_mem.c

@@ -5,7 +5,7 @@
 #define LUAT_LOG_TAG "fs"
 #include "luat_log.h"
 
-#if 0
+#if 1
 
 #define BLOCK_SIZE 4096
 
@@ -32,6 +32,7 @@ typedef struct luat_ram_fd
 #define RAM_FILE_MAX (64)
 static ram_file_t* files[RAM_FILE_MAX];
 
+size_t luat_vfs_ram_fread(void* userdata, void *ptr, size_t size, size_t nmemb, FILE *stream);
 
 FILE* luat_vfs_ram_fopen(void* userdata, const char *filename, const char *mode) {
     (void)userdata;
@@ -161,6 +162,10 @@ int luat_vfs_ram_fseek(void* userdata, FILE* stream, long int offset, int origin
     else {
         fd->offset = files[fd->fid]->size - offset;
     }
+    if (fd->offset > files[fd->fid]->size) {
+        // 如果偏移量超过了文件大小,设置为文件大小
+        fd->offset = files[fd->fid]->size;
+    }
     return 0;
 }
 

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

@@ -0,0 +1,65 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑
+@version 1.0
+@date    2025.07.02
+@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 = "fatfs"
+VERSION = "1.0.0"
+
+-- 在日志中打印项目名和项目版本号
+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 "tf_test"
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 76 - 0
module/Air780EHM/demo/tf/readme.md

@@ -0,0 +1,76 @@
+
+## 演示功能概述
+
+使用Air780EHM核心板通过fatfs库实现对SPI SD的高效操作,并可以挂载fatfs文件系统,通过文件系统相关接口去操作fatfs文件系统中的文件,并演示文件的读写、删除、追加以及HTTP服务器下载到SD卡等操作。
+
+## 演示硬件环境
+
+1、Air780EHM核心板一块
+
+2、TYPE-C USB数据线一根
+
+3、spi SD卡模块一个和SD卡一张
+
+4、Air780EHM核心板和数据线的硬件接线方式为
+
+- Air780EHM核心板通过TYPE-C USB口供电;(核心板USB旁边的开关拨到on一端)
+
+- TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+5、Air780EHM核心板和spi SD卡模块接线方式
+
+|   Air780EHM     |       SPI_SD卡模块    |
+| --------------- | --------------------- |
+|  GND(任意)      |          GND          |
+|  VDD_EXT        |          VCC          |
+|  GPIO8/SPI0_CS  |        CS,片选        |
+|  SPI0_SLK       |        CLK,时钟       |
+|  SPI0_MOSI      |  MOSI,主机输出,从机输入|
+|  SPI0_MISO      |  MISO,主机输入,从机输出|
+
+
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+2、[Air780EHM V2008版本固件](https://gitee.com/openLuat/LuatOS/tree/master/module/Air780EHM/core)
+
+## 演示核心步骤
+
+1、搭建好硬件环境
+
+2、通过Luatools将demo与固件烧录到核心板中
+
+3、烧录好后,板子开机将会在Luatools上看到如下打印:
+
+(1) 用SD卡接口fatfs挂载SD卡成文件系统,然后用文件系统接口操作:
+
+```lua
+[2025-07-03 12:58:38.413][000000001.547] D/SPI_TF 卡容量 62367744KB
+[2025-07-03 12:58:38.414][000000001.547] D/SPI_TF sdcard init OK OCR:0xc0ff8000!
+[2025-07-03 12:58:38.515][000000001.654] I/user.fatfs	getfree	{"free_sectors":124665856,"total_kb":62334976,"free_kb":62332928,"total_sectors":124669952}
+[2025-07-03 12:58:38.517][000000001.657] I/user.fs	data	6	36	2
+[2025-07-03 12:58:38.519][000000001.657] I/user.fs	boot count	6
+[2025-07-03 12:58:38.524][000000001.665] I/user.fs	write c to file	7	7
+[2025-07-03 12:58:38.548][000000001.680] I/user.fsstat	true	128	4	4096	lfs
+[2025-07-03 12:58:38.550][000000001.680] I/user.fsstat	true	124669952	4096	512	fatfs
+[2025-07-03 12:58:38.609][000000001.757] I/user.data	ABCdef	true
+[2025-07-03 12:58:38.700][000000001.826] D/mobile cid1, state0
+[2025-07-03 12:58:38.702][000000001.827] D/mobile bearer act 0, result 0
+[2025-07-03 12:58:38.703][000000001.827] D/mobile NETIF_LINK_ON -> IP_READY
+[2025-07-03 12:58:38.733][000000001.872] I/user.sdio	line1	abc
+[2025-07-03 12:58:38.734][000000001.873] I/user.sdio	line2	123
+[2025-07-03 12:58:38.736][000000001.873] I/user.sdio	line3	wendal
+
+```
+
+(2) 使用HTTP去下载文件到SD卡,然后读取下文件的大小:
+
+```lua
+[2025-07-03 12:58:38.738][000000001.874] dns_run 674:airtest.openluat.com state 0 id 1 ipv6 0 use dns server2, try 0
+[2025-07-03 12:58:38.764][000000001.907] D/mobile TIME_SYNC 0
+[2025-07-03 12:58:38.794][000000001.917] dns_run 691:dns all done ,now stop
+[2025-07-03 12:58:44.719][000000007.856] I/user.下载完成	200	table: 0C7F62D8	411922
+[2025-07-03 12:58:44.728][000000007.859] I/user.io.fileSize	411922
+```

+ 128 - 0
module/Air780EHM/demo/tf/tf_test.lua

@@ -0,0 +1,128 @@
+--[[]
+运行环境:Air780EHM核心板+SD卡扩展板
+最后修改时间:2025-7-2
+使用了如下IO口:
+SD卡的使用IO口:
+[83, "SPI0CS", " PIN83脚, 用于SD卡片选脚"],
+[84, "SPI0MISO," PIN84脚, 用于SD卡数据脚"],
+[85, "SPI0MOSI", " PIN85脚, 用于SD卡数据脚"],
+[86, "SPI0CLK", " PIN86脚, 用于SD卡时钟脚"],
+[24, "VDD_EXT", " PIN24脚, 用于给SD卡供电脚"],
+GND
+执行逻辑为:
+1. 初始化SD卡
+2. 挂载文件系统
+3. 在文件系统中创建一个文件
+4. 向文件中写入数据
+5. 读取文件中的数据
+6. 删除文件
+7. 卸载文件系统
+8. 关闭SD卡
+
+扩展了HTTP从服务器下载文件到SD卡的功能,可以下载指定URL的文件到SD卡中,并保存到SD卡里面
+]]
+-- sys库是标配
+_G.sys = require("sys")
+
+
+local function main_task()
+    sys.wait(1000)
+    -- fatfs.debug(1) -- 若挂载失败,可以尝试打开调试信息,查找原因
+
+    -- 此为spi方式挂载SD卡
+    local spi_id, pin_cs,tp = 0,8 
+    spi.setup(spi_id, nil, 0, 0, pin_cs, 400 * 1000)
+    gpio.setup(pin_cs, 1)
+    fatfs.mount(fatfs.SPI, "/sd", spi_id, pin_cs, 24 * 1000 * 1000)
+
+    --获取SD卡的可用空间信息
+    local data, err = fatfs.getfree("/sd")
+    if data then
+        log.info("fatfs", "getfree", json.encode(data))
+    else
+        log.info("fatfs", "err", err)
+    end
+
+    -- #################################################
+    -- 文件操作测试
+    -- #################################################
+    --只读模式,打开文件
+    local f = io.open("/sd/boottime", "rb")
+    local c = 0
+    if f then
+        --读取文件内容"a": 从当前位置开始读取整个文件。 如果已在文件末尾,返回空串。
+        local data = f:read("*a")   
+        log.info("fs", "data", data, data:toHex())
+        c = tonumber(data)
+        f:close()
+    end
+    log.info("fs", "boot count", c)
+    if c == nil then
+        c = 0
+    end
+    c = c + 1
+    --写入模式,打开文件
+    f = io.open("/sd/boottime", "wb")
+    if f ~= nil then
+        log.info("fs", "write c to file", c, tostring(c))
+        f:write(tostring(c))
+        f:close()
+    else
+        log.warn("sdio", "mount not good?!")
+    end
+    --获取文件系统信息
+    if fs then
+        log.info("fsstat", fs.fsstat("/"))
+        log.info("fsstat", fs.fsstat("/sd"))
+    end
+
+    -- 测试一下追加
+    os.remove("/sd/test_a")
+    sys.wait(50)
+    --打开文件,写入模式,写入内容
+    f = io.open("/sd/test_a", "w")
+    if f then
+        f:write("ABC")
+        f:close()
+    end
+    --打开文件,追加模式,写入内容
+    f = io.open("/sd/test_a", "a+")
+    if f then
+        f:write("def")
+        f:close()
+    end
+    --打开文件,只读模式
+    f = io.open("/sd/test_a", "r")
+    --对比下数据是不是和写入的一样
+    if  f then
+        local data = f:read("*a")
+        log.info("data", data, data == "ABCdef")
+        f:close()
+    end
+
+    -- 测试一下按行读取
+    f = io.open("/sd/testline", "w")
+    if f then
+        f:write("abc\n")
+        f:write("123\n")
+        f:write("wendal\n")
+        f:close()
+    end
+    sys.wait(100)
+    f = io.open("/sd/testline", "r")
+    if f then
+        log.info("sdio", "line1", f:read("*l"))
+        log.info("sdio", "line2", f:read("*l"))
+        log.info("sdio", "line3", f:read("*l"))
+        f:close()
+    end
+
+    -- 测试一下http下载到sd卡里面
+    local code, headers, body =
+        http.request("GET", "http://airtest.openluat.com:2900/download/1.mp3", nil, nil, {dst = "/sd/1.mp3"}).wait()
+    --存到sd卡里面
+    log.info("下载完成", code, headers, body)
+    log.info("io.fileSize",io.fileSize("/sd/1.mp3"))
+end
+
+sys.taskInit(main_task)

+ 564 - 0
script/libs/espblufi.lua

@@ -0,0 +1,564 @@
+--[[
+@module espblufi
+@summary espblufi esp blufi 蓝牙配网(注意:初版不支持加密功能,需要后续版本支持!!!!!!!!)
+@version 1.0
+@date    2025.07.01
+@author  Dozingfiretruck
+@usage
+-- 此为Blufi 配网库
+-- BluFi 配网指南:https://www.espressif.com/sites/default/files/documentation/esp32_bluetooth_networking_user_guide_cn.pdf
+
+-- 安卓测试APP下载地址:https://github.com/EspressifApp/EspBlufiForAndroid/releases
+-- 安卓APP源码下载地址:https://github.com/EspressifApp/EspBlufiForAndroid
+
+-- IOS测试APP下载地址:https://apps.apple.com/cn/app/espblufi/id1450614082
+-- IOSAPP源码下载地址:https://github.com/EspressifApp/EspBlufiForiOS
+
+-- 小程序测试:微信搜索小程序:ESP Config
+-- 小程序源码下载地址:https://github.com/EspressifApps/ESP-Config-WeChat
+
+-- 注意:初版不支持加密功能,需要后续版本支持!!!!!!!!
+
+-- 用法实例
+local espblufi = require("espblufi")
+
+local function espblufi_callback(event,data)
+    if event == espblufi.EVENT_STA_INFO then
+        for i, v in pairs(data) do
+            print(i,v)
+        end
+    elseif event == espblufi.EVENT_SOFTAP_INFO then
+        for i, v in pairs(data) do
+            print(i,v)
+        end
+    elseif event == espblufi.EVENT_CUSTOM_DATA then
+        espblufi.send_custom_data(data)
+    end
+end
+
+sys.taskInit(function()
+    espblufi.init(espblufi_callback)
+    espblufi.start()
+    while 1 do
+        sys.wait(1000)
+    end
+end)
+
+]]
+local espblufi = {}
+
+local sys = require "sys"
+
+local BLUFI_TOPIC = "espblufi"
+
+local BTC_BLUFI_GREAT_VER   =   0x01  --Version + Subversion
+local BTC_BLUFI_SUB_VER     =   0x03  --Version + Subversion
+local BTC_BLUFI_VERSION     =   ((BTC_BLUFI_GREAT_VER<<8)|BTC_BLUFI_SUB_VER)  --Version + Subversion
+
+-- packet type
+local BLUFI_TYPE_MASK       =   0x03
+local BLUFI_TYPE_SHIFT      =   0
+local BLUFI_SUBTYPE_MASK    =   0xFC
+local BLUFI_SUBTYPE_SHIFT   =   2
+
+local function BLUFI_GET_TYPE(type) return ((type) & BLUFI_TYPE_MASK) end
+local function BLUFI_GET_SUBTYPE(type) return (((type) & BLUFI_SUBTYPE_MASK) >>BLUFI_SUBTYPE_SHIFT) end
+local function BLUFI_BUILD_TYPE(type,subtype) return (((type) & BLUFI_TYPE_MASK) | ((subtype)<<BLUFI_SUBTYPE_SHIFT)) end
+
+local BLUFI_TYPE_CTRL                                 = 0x0
+local BLUFI_TYPE_CTRL_SUBTYPE_ACK                     = 0x00
+local BLUFI_TYPE_CTRL_SUBTYPE_SET_SEC_MODE            = 0x01
+local BLUFI_TYPE_CTRL_SUBTYPE_SET_WIFI_OPMODE         = 0x02
+local BLUFI_TYPE_CTRL_SUBTYPE_CONN_TO_AP              = 0x03
+local BLUFI_TYPE_CTRL_SUBTYPE_DISCONN_FROM_AP         = 0x04
+local BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_STATUS         = 0x05
+local BLUFI_TYPE_CTRL_SUBTYPE_DEAUTHENTICATE_STA      = 0x06
+local BLUFI_TYPE_CTRL_SUBTYPE_GET_VERSION             = 0x07
+local BLUFI_TYPE_CTRL_SUBTYPE_DISCONNECT_BLE          = 0x08
+local BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_LIST           = 0x09
+
+local BLUFI_TYPE_DATA                                 = 0x1
+local BLUFI_TYPE_DATA_SUBTYPE_NEG                     = 0x00
+local BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID               = 0x01
+local BLUFI_TYPE_DATA_SUBTYPE_STA_SSID                = 0x02
+local BLUFI_TYPE_DATA_SUBTYPE_STA_PASSWD              = 0x03
+local BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_SSID             = 0x04
+local BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_PASSWD           = 0x05
+local BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM     = 0x06
+local BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE        = 0x07
+local BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_CHANNEL          = 0x08
+local BLUFI_TYPE_DATA_SUBTYPE_USERNAME                = 0x09
+local BLUFI_TYPE_DATA_SUBTYPE_CA                      = 0x0a
+local BLUFI_TYPE_DATA_SUBTYPE_CLIENT_CERT             = 0x0b
+local BLUFI_TYPE_DATA_SUBTYPE_SERVER_CERT             = 0x0c
+local BLUFI_TYPE_DATA_SUBTYPE_CLIENT_PRIV_KEY         = 0x0d
+local BLUFI_TYPE_DATA_SUBTYPE_SERVER_PRIV_KEY         = 0x0e
+local BLUFI_TYPE_DATA_SUBTYPE_WIFI_REP                = 0x0f
+local BLUFI_TYPE_DATA_SUBTYPE_REPLY_VERSION           = 0x10
+local BLUFI_TYPE_DATA_SUBTYPE_WIFI_LIST               = 0x11
+local BLUFI_TYPE_DATA_SUBTYPE_ERROR_INFO              = 0x12
+local BLUFI_TYPE_DATA_SUBTYPE_CUSTOM_DATA             = 0x13
+local BLUFI_TYPE_DATA_SUBTYPE_STA_MAX_CONN_RETRY      = 0x14
+local BLUFI_TYPE_DATA_SUBTYPE_STA_CONN_END_REASON     = 0x15
+local BLUFI_TYPE_DATA_SUBTYPE_STA_CONN_RSSI           = 0x16
+
+local function BLUFI_TYPE_IS_CTRL(type) return (BLUFI_GET_TYPE((type)) == BLUFI_TYPE_CTRL) end
+local function BLUFI_TYPE_IS_DATA(type) return (BLUFI_GET_TYPE((type)) == BLUFI_TYPE_DATA) end
+
+-- packet frame control
+local BLUFI_FC_ENC_MASK         = 0x01
+local BLUFI_FC_CHECK_MASK       = 0x02
+local BLUFI_FC_DIR_MASK         = 0x04
+local BLUFI_FC_REQ_ACK_MASK     = 0x08
+local BLUFI_FC_FRAG_MASK        = 0x10
+
+local BLUFI_FC_ENC              = 0x01
+local BLUFI_FC_CHECK            = 0x02
+local BLUFI_FC_DIR_P2E          = 0x00
+local BLUFI_FC_DIR_E2P          = 0x04
+local BLUFI_FC_REQ_ACK          = 0x08
+local BLUFI_FC_FRAG             = 0x10
+
+local BLUFI_SEQUENCE_ERROR      = 0x00
+local BLUFI_CHECKSUM_ERROR      = 0x01
+local BLUFI_DECRYPT_ERROR       = 0x02
+local BLUFI_ENCRYPT_ERROR       = 0x03
+local BLUFI_INIT_SECURITY_ERROR = 0x04
+local BLUFI_DH_MALLOC_ERROR     = 0x05
+local BLUFI_DH_PARAM_ERROR      = 0x06
+local BLUFI_READ_PARAM_ERROR    = 0x07
+local BLUFI_MAKE_PUBLIC_ERROR   = 0x08
+local BLUFI_DATA_FORMAT_ERROR   = 0x09
+local BLUFI_CALC_MD5_ERROR      = 0x0a
+local BLUFI_WIFI_SCAN_FAIL      = 0x0b
+local BLUFI_MSG_STATE_ERROR     = 0x0c
+
+local BLUFI_OPMODE_NULL         = 0x00
+local BLUFI_OPMODE_STA          = 0x01
+local BLUFI_OPMODE_SOFTAP       = 0x02
+local BLUFI_OPMODE_SOFTAPSTA    = 0x03
+
+local BLUFI_STA_CONN_SUCCESS    = 0x00
+local BLUFI_STA_CONN_FAIL       = 0x01
+local BLUFI_STA_CONNECTING      = 0x02
+local BLUFI_STA_NO_IP           = 0x03
+
+local BLUFI_BLE_STATE_DISCONN   = 0x00
+local BLUFI_BLE_STATE_CONNED    = 0x01
+
+local BLUFI_WLAN_STATE_DISCONN  = 0x00
+local BLUFI_WLAN_STATE_CONNING  = 0x01
+local BLUFI_WLAN_STATE_CONNED   = 0x02
+
+local BLUFI_SEQUENCE_ERROR      = 0x00
+local BLUFI_CHECKSUM_ERROR      = 0x01
+local BLUFI_DECRYPT_ERROR       = 0x02
+local BLUFI_ENCRYPT_ERROR       = 0x03
+local BLUFI_INIT_SECURITY_ERROR = 0x04
+local BLUFI_DH_MALLOC_ERROR     = 0x05
+local BLUFI_DH_PARAM_ERROR      = 0x06
+local BLUFI_READ_PARAM_ERROR    = 0x07
+local BLUFI_MAKE_PUBLIC_ERROR   = 0x08
+local BLUFI_DATA_FORMAT_ERROR   = 0x09
+local BLUFI_CALC_MD5_ERROR      = 0x0a
+local BLUFI_WIFI_SCAN_FAIL      = 0x0b
+local BLUFI_MSG_STATE_ERROR     = 0x0c
+
+local function BLUFI_FC_IS_ENC(fc) return      ((fc) & BLUFI_FC_ENC_MASK) ~= 0 end
+local function BLUFI_FC_IS_CHECK(fc) return     ((fc) & BLUFI_FC_CHECK_MASK) ~= 0 end
+local function BLUFI_FC_IS_REQ_ACK(fc) return   ((fc) & BLUFI_FC_REQ_ACK_MASK) ~= 0 end
+local function BLUFI_FC_IS_FRAG(fc) return      ((fc) & BLUFI_FC_FRAG_MASK) ~= 0 end
+
+local BLUFI_PROTOCOL_DATA   = "BLUFI_PROTOCOL_DATA"
+local BLUFI_TASK_EXIT       = "BLUFI_TASK_EXIT"
+
+local espblufi_uuid_service  = "0xFFFF"
+local espblufi_uuid2device   = "0xFF01"
+local espblufi_uuid2mobile   = "0xFF02"
+
+local espblufi_att_db = {
+    string.fromHex(espblufi_uuid_service),
+    {string.fromHex(espblufi_uuid2device),ble.WRITE,},
+    {string.fromHex(espblufi_uuid2mobile),ble.NOTIFY|ble.READ}
+}
+
+local blufi_env = {ble_device=nil,callback=nil,isfrag=nil,
+                    ble_state = BLUFI_BLE_STATE_DISCONN,
+                    wlan_state = BLUFI_WLAN_STATE_DISCONN,
+                    opmode = BLUFI_OPMODE_NULL,
+                    recv_seq=0,send_seq=0,sec_mode=0,
+                    softap_conn_num = 0, 
+                    softap_auth_mode=0,
+                    softap_max_conn_num=0,
+                    softap_channel = nil,
+                    sta_max_conn_retry = nil,
+                    sta_conn_end_reason = nil,
+                    sta_ssid=nil,sta_passwd=nil,
+                    softap_ssid=nil,softap_passwd=nil}
+
+local blufi_hdr = {type=nil,fc=nil,seq=nil,data_len=nil,data=nil}
+
+espblufi.EVENT_STA_INFO     = 0x01
+espblufi.EVENT_SOFTAP_INFO  = 0x02
+espblufi.EVENT_CUSTOM_DATA  = 0x03
+
+sys.subscribe("WLAN_AP_INC", function(state, mac)
+    if state == "CONNECTED" then
+        blufi_env.softap_conn_num = blufi_env.softap_conn_num+1
+    elseif state == "DISCONNECTED" and blufi_env.softap_conn_num>0 then
+        blufi_env.softap_conn_num = blufi_env.softap_conn_num-1
+    end
+end)
+
+local function blufi_crc_checksum(data)
+    return crypto.crc16("IBM",data)
+end
+
+local function blufi_send_encap(blufi_hdr_send)
+    local send_data = string.char(blufi_hdr_send.type,blufi_hdr_send.fc,blufi_hdr_send.seq,#blufi_hdr_send.data) .. blufi_hdr_send.data
+    if BLUFI_TYPE_IS_CTRL(blufi_hdr_send.type) then
+        blufi_hdr_send.fc = blufi_hdr_send.fc|BLUFI_FC_CHECK
+        send_data = send_data .. blufi_crc_checksum(send_data)
+    end
+    blufi_env.send_seq = blufi_env.send_seq + 1
+    blufi_env.ble_device:write_notify({uuid_service=string.fromHex(espblufi_uuid_service),uuid_characteristic=string.fromHex(espblufi_uuid2mobile)},send_data)
+end
+
+local function btc_blufi_send_encap(type,data)
+    if blufi_env.ble_state == BLUFI_BLE_STATE_DISCONN then
+        return
+    end
+    local blufi_hdr_send = {type=type,fc=0,seq=blufi_env.send_seq,data=data}
+    blufi_send_encap(blufi_hdr_send)
+end
+
+local function btc_blufi_wifi_conn_report(sta_conn_state)
+    local data = string.char(blufi_env.opmode,blufi_env.wlan_state,blufi_env.softap_conn_num)
+    local wlan_info = wlan.getInfo()
+    if wlan_info then
+        if wlan_info.bssid then
+            data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID,6) .. string.fromHex(wlan_info.bssid)
+        end
+        print("rssi",type(wlan_info.rssi),wlan_info.rssi)
+        -- if wlan_info.rssi then
+        --     data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_STA_CONN_RSSI,1) .. tonumber(wlan_info.rssi)
+        -- end
+    end
+    if blufi_env.sta_ssid then
+        data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_STA_SSID,#blufi_env.sta_ssid) .. blufi_env.sta_ssid
+    end
+    if blufi_env.sta_passwd then
+        data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_STA_PASSWD,#blufi_env.sta_passwd) .. blufi_env.sta_passwd
+    end
+    if blufi_env.softap_ssid then
+        data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_SSID,#blufi_env.softap_ssid) .. blufi_env.softap_ssid
+    end
+    if blufi_env.softap_passwd then
+        data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_PASSWD,#blufi_env.softap_passwd) .. blufi_env.softap_passwd
+    end
+    if blufi_env.softap_authmode then
+        data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE,1) .. blufi_env.softap_authmode
+    end
+    if blufi_env.softap_max_conn_num then
+        data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM,1) .. blufi_env.softap_max_conn_num
+    end
+    if blufi_env.softap_channel then
+        data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_CHANNEL,1) .. blufi_env.softap_channel
+    end
+    if blufi_env.sta_max_conn_retry then
+        data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_STA_MAX_CONN_RETRY,1) .. blufi_env.sta_max_conn_retry
+    end
+    if blufi_env.sta_conn_end_reason then
+        data = data .. string.char(BLUFI_TYPE_DATA_SUBTYPE_STA_CONN_END_REASON,1) .. blufi_env.sta_conn_end_reason
+    end
+    btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_WIFI_REP), data);
+end
+
+local function btc_blufi_send_wifi_list(results)
+    local data = ""
+    for k,v in pairs(results) do data = data .. string.char(#v["ssid"]+1,v["rssi"]%256) .. v["ssid"] end
+    btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_WIFI_LIST),data)
+end
+
+local function btc_blufi_send_ack(seq)
+    btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_CTRL, BLUFI_TYPE_CTRL_SUBTYPE_ACK),string.char(seq))
+end
+
+local function btc_blufi_send_error_info(state)
+    btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_ERROR_INFO),string.char(state))
+end
+
+local function btc_blufi_send_custom_data(data)
+    btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_CUSTOM_DATA),data)
+end
+
+local SEC_TYPE_DH_PARAM_LEN   = 0x00
+local SEC_TYPE_DH_PARAM_DATA  = 0x01
+local SEC_TYPE_DH_P           = 0x02
+local SEC_TYPE_DH_G           = 0x03
+local SEC_TYPE_DH_PUBLIC      = 0x04
+
+local blufi_sec = {dh_param_len=0}
+
+local function blufi_dh_negotiate_data_handler(data)
+    if #data<3 then
+        btc_blufi_send_error_info(BLUFI_DATA_FORMAT_ERROR)
+    end
+
+    local type = data:byte(1)
+    if type == SEC_TYPE_DH_PARAM_LEN then
+        blufi_sec.dh_param_len = ((data:byte(2)<<8)|data:byte(3));
+        -- print("dh_param_len",blufi_sec.dh_param_len)
+    elseif type == SEC_TYPE_DH_PARAM_DATA then
+        -- print("SEC_TYPE_DH_PARAM_DATA")
+        if #data < (blufi_sec.dh_param_len + 1) then
+            btc_blufi_send_error_info(BLUFI_DH_PARAM_ERROR);
+            return;
+        end
+
+        -- 秘钥待实现,功能需要mbedtls引出c接口
+        btc_blufi_send_error_info(BLUFI_INIT_SECURITY_ERROR);
+
+    elseif type == SEC_TYPE_DH_P then
+        print("SEC_TYPE_DH_P")
+    elseif type == SEC_TYPE_DH_G then
+        print("SEC_TYPE_DH_G")
+    elseif type == SEC_TYPE_DH_PUBLIC then
+        print("SEC_TYPE_DH_PUBLIC")
+    end
+
+end
+
+local function btc_blufi_protocol_handler(parse_data)
+    local target_data_len = 0
+    local parse_data_len = #parse_data
+
+    if parse_data_len<4 then return end
+
+    blufi_hdr.type,blufi_hdr.fc,blufi_hdr.seq,blufi_hdr.data_len = string.unpack('<BBBB',parse_data)
+
+    if BLUFI_FC_IS_CHECK(blufi_hdr.fc) then
+        target_data_len = blufi_hdr.data_len + 4 + 2 -- // Data + (Type + Frame Control + Sequence Number + Data Length) + Checksum
+    else
+        target_data_len = blufi_hdr.data_len + 4 -- Data + (Type + Frame Control + Sequence Number + Data Length)
+    end
+    -- print("target_data_len",target_data_len,"parse_data_len",parse_data_len)
+    if target_data_len ~= parse_data_len then
+        return
+    end
+
+    if blufi_hdr.seq ~= blufi_env.recv_seq then
+        return
+    end
+    blufi_env.recv_seq = blufi_env.recv_seq + 1
+
+    if BLUFI_FC_IS_ENC(blufi_hdr.fc) then
+        -- 解密功能需要mbedtls引出c接口
+    end
+    if BLUFI_FC_IS_CHECK(blufi_hdr.fc) and crypto then
+        -- 需要app配合调试,暂时不强制校验
+        local checksum = blufi_crc_checksum()
+    end
+
+    if BLUFI_FC_IS_REQ_ACK(blufi_hdr.fc) then
+        btc_blufi_send_ack(blufi_hdr.seq)
+    end
+
+    if blufi_hdr.data_len and blufi_hdr.data_len>0 then
+        if blufi_env.isfrag then
+            blufi_hdr.data = blufi_hdr.data .. parse_data:sub(5)
+        else
+            blufi_hdr.data = parse_data:sub(5)
+        end
+    end
+
+    if BLUFI_FC_IS_FRAG(blufi_hdr.fc) then
+        blufi_env.isfrag = true
+        return
+    else
+        blufi_env.isfrag = false
+    end
+
+    -- print(blufi_hdr.type,blufi_hdr.fc,blufi_hdr.seq,blufi_hdr.data_len)
+    -- print(blufi_hdr.data,blufi_hdr.data:toHex())
+
+    local blufi_type = BLUFI_GET_TYPE(blufi_hdr.type)
+    local blufi_subtype = BLUFI_GET_SUBTYPE(blufi_hdr.type)
+    if blufi_type == BLUFI_TYPE_CTRL then
+        if blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_ACK then
+        elseif blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_SET_SEC_MODE then
+            blufi_env.sec_mode = blufi_hdr.data;
+        elseif blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_SET_WIFI_OPMODE then
+            blufi_env.opmode = blufi_hdr.data:byte(1)
+        elseif blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_CONN_TO_AP then
+            blufi_env.callback(espblufi.EVENT_STA_INFO,
+                                {ssid=blufi_env.sta_ssid,password=blufi_env.sta_passwd,})
+            wlan.connect(blufi_env.sta_ssid, blufi_env.sta_passwd)
+            blufi_env.wlan_state = BLUFI_WLAN_STATE_CONNING
+            local results = sys.waitUntil("IP_READY",1000)
+            if results then blufi_env.wlan_state = BLUFI_WLAN_STATE_CONNED 
+            else blufi_env.wlan_state = BLUFI_WLAN_STATE_DISCONN end
+            btc_blufi_wifi_conn_report()
+        elseif blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_DISCONN_FROM_AP then
+            wlan.disconnect()
+        elseif blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_STATUS then
+            btc_blufi_wifi_conn_report()
+        elseif blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_DEAUTHENTICATE_STA then
+            -- print("BLUFI_TYPE_CTRL_SUBTYPE_DEAUTHENTICATE_STA")
+        elseif blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_GET_VERSION then
+            btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_REPLY_VERSION), string.char(BTC_BLUFI_GREAT_VER,BTC_BLUFI_SUB_VER))
+        elseif blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_DISCONNECT_BLE then
+            blufi_env.ble_device:disconnect()
+        elseif blufi_subtype == BLUFI_TYPE_CTRL_SUBTYPE_GET_WIFI_LIST then
+            wlan.scan()
+            sys.waitUntil("WLAN_SCAN_DONE", 15000)
+            local results = wlan.scanResult()
+            if results and #results>0 then
+                btc_blufi_send_wifi_list(results)
+            else
+                btc_blufi_send_error_info(BLUFI_WIFI_SCAN_FAIL)
+            end
+        end
+    elseif blufi_type == BLUFI_TYPE_DATA then
+        if blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_NEG then
+            local data = blufi_dh_negotiate_data_handler(blufi_hdr.data)
+            if data then
+                btc_blufi_send_encap(BLUFI_BUILD_TYPE(BLUFI_TYPE_DATA, BLUFI_TYPE_DATA_SUBTYPE_NEG),data)
+            end
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_STA_BSSID then
+            blufi_env.sta_bssid = blufi_hdr.data
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_STA_SSID then
+            blufi_env.sta_ssid = blufi_hdr.data
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_STA_PASSWD then
+            blufi_env.sta_passwd = blufi_hdr.data
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_SSID then
+            blufi_env.softap_ssid = blufi_hdr.data
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_PASSWD then
+            blufi_env.softap_passwd = blufi_hdr.data
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_MAX_CONN_NUM then
+            blufi_env.softap_max_conn_num = blufi_hdr.data:byte(1)
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_AUTH_MODE then
+            blufi_env.softap_auth_mode = blufi_hdr.data:byte(1)
+            blufi_env.callback(espblufi.EVENT_SOFTAP_INFO,
+                                {ssid=blufi_env.softap_ssid,password=blufi_env.softap_passwd,})
+            wlan.createAP(blufi_env.softap_ssid, blufi_env.softap_passwd,nil,nil,blufi_env.softap_max_channel,{max_conn  = blufi_env.softap_max_conn_num})
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_SOFTAP_CHANNEL then
+            blufi_env.softap_max_channel = blufi_hdr.data:byte(1)
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_USERNAME then
+            -- print("BLUFI_TYPE_DATA_SUBTYPE_USERNAME")
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_CA then
+            -- print("BLUFI_TYPE_DATA_SUBTYPE_CA")
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_CLIENT_CERT then
+            -- print("BLUFI_TYPE_DATA_SUBTYPE_CLIENT_CERT")
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_SERVER_CERT then
+            -- print("BLUFI_TYPE_DATA_SUBTYPE_SERVER_CERT")
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_CLIENT_PRIV_KEY then
+            -- print("BLUFI_TYPE_DATA_SUBTYPE_CLIENT_PRIV_KEY")
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_SERVER_PRIV_KEY then
+            -- print("BLUFI_TYPE_DATA_SUBTYPE_SERVER_PRIV_KEY")
+        elseif blufi_subtype == BLUFI_TYPE_DATA_SUBTYPE_CUSTOM_DATA then
+            blufi_env.callback(espblufi.EVENT_CUSTOM_DATA,blufi_hdr.data)
+        end
+    else
+        return
+
+    end
+end
+
+local function espblufi_task() 
+    while true do
+        local result, event, data = sys.waitUntil(BLUFI_TOPIC)
+        if result then
+            if event == BLUFI_PROTOCOL_DATA then
+                btc_blufi_protocol_handler(data)
+            elseif event == BLUFI_TASK_EXIT then
+                break
+            end
+        end
+    end
+
+end
+
+local function espblufi_ble_callback(ble_device, ble_event, ble_param)
+    if ble_event == ble.EVENT_CONN then
+        blufi_env.ble_state = BLUFI_BLE_STATE_CONNED
+    elseif ble_event == ble.EVENT_DISCONN then
+        blufi_env.ble_state = BLUFI_BLE_STATE_DISCONN
+    elseif ble_event == ble.EVENT_WRITE then
+        sys.publish(BLUFI_TOPIC,BLUFI_PROTOCOL_DATA,ble_param.data)
+    end
+end
+
+--[[
+初始化espblufi
+@api espblufi.init(espblufi_callback,local_name)
+@function 事件回调函数
+@number 蓝牙名,可选,默认为"BLUFI_xxx",xxx为设备型号(因为esp的配网测试app默认过滤蓝牙名称为BLUFI_开头的设备进行显示,可手动修改)
+@usage
+espblufi.init(espblufi_callback)
+]]
+function espblufi.init(espblufi_callback,local_name)
+    if not bluetooth or not ble or not wlan then
+        return
+    end
+    if not espblufi_callback then
+        return
+    else
+        blufi_env.callback = espblufi_callback
+    end
+    if not local_name then
+        local_name = "BLUFI_"..rtos.bsp()
+    end
+    wlan.init()
+    local bluetooth_device = bluetooth.init()
+    local ble_device = bluetooth_device:ble(espblufi_ble_callback)
+    ble_device:gatt_create(espblufi_att_db)
+    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, local_name}},
+    })
+    blufi_env.ble_device = ble_device
+    return
+end
+
+--[[
+开始配网
+@api espblufi.start()
+@usage
+espblufi.start()
+]]
+function espblufi.start()
+    sys.taskInit(espblufi_task)
+    blufi_env.ble_device:adv_start()
+end
+
+--[[
+停止配网
+@api espblufi.stop()
+@usage
+espblufi.stop()
+]]
+function espblufi.stop()
+    blufi_env.ble_device:adv_stop()
+    sys.publish(BLUFI_TOPIC,BLUFI_TASK_EXIT)
+end
+
+function espblufi.deinit()
+    
+end
+
+--[[
+发送自定义数据,一般用于接收到客户端发送的自定义命令后进行回复
+@api espblufi.send_custom_data(data)
+@string 回复的数据包内容
+@usage
+espblufi.send_custom_data(data)
+]]
+function espblufi.send_custom_data(data)
+    btc_blufi_send_custom_data(data)
+end
+
+return espblufi