Przeglądaj źródła

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

alienwalker 3 lat temu
rodzic
commit
c0defd75dd

+ 203 - 0
components/fskv/luat_fskv.c

@@ -0,0 +1,203 @@
+#include "luat_base.h"
+#include "luat_fskv.h"
+#include "luat_malloc.h"
+#include "luat_msgbus.h"
+#include "luat_sfd.h"
+
+#define LUAT_LOG_TAG "fskv"
+#include "luat_log.h"
+
+#include "lfs.h"
+
+luat_fskv_t* fskv;
+
+int sfd_onchip_init (void* userdata);
+int sfd_onchip_status (void* userdata);
+int sfd_onchip_read (void* userdata, char* buff, size_t offset, size_t len);
+int sfd_onchip_write (void* userdata, const char* buff, size_t offset, size_t len);
+int sfd_onchip_erase (void* userdata, size_t offset, size_t len);
+int sfd_onchip_ioctl (void* userdata, size_t cmd, void* buff);
+
+// Read a block
+static int block_device_read(const struct lfs_config *cfg, lfs_block_t block,
+        lfs_off_t off, void *buffer, lfs_size_t size) {
+    int ret = sfd_onchip_read(NULL, buffer, block * LFS_BLOCK_DEVICE_ERASE_SIZE + off, size);
+    // LLOGD("sfd read %d %d %d %d", block, off, size, ret);
+    if (ret == size) {
+        // LLOGD("block_device_read return LFS_ERR_OK");
+        return LFS_ERR_OK;
+    }
+    // LLOGD("block_device_read return LFS_ERR_IO");
+    return LFS_ERR_IO;
+}
+
+// Program a block
+//
+// The block must have previously been erased.
+static int block_device_prog(const struct lfs_config *cfg, lfs_block_t block,
+        lfs_off_t off, const void *buffer, lfs_size_t size) {
+    int ret = sfd_onchip_write(NULL, buffer, block * LFS_BLOCK_DEVICE_ERASE_SIZE + off, size);
+    // LLOGD("sfd write %d %d %d %d", block, off, size, ret);
+    if (ret == size) {
+        // LLOGD("block_device_prog return LFS_ERR_OK");
+        return LFS_ERR_OK;
+    }
+    // LLOGD("block_device_prog return LFS_ERR_IO");
+    return LFS_ERR_IO;
+}
+
+// Erase a block
+//
+// A block must be erased before being programmed. The
+// state of an erased block is undefined.
+static int block_device_erase(const struct lfs_config *cfg, lfs_block_t block) {
+    int ret = sfd_onchip_erase(NULL, block * LFS_BLOCK_DEVICE_ERASE_SIZE, LFS_BLOCK_DEVICE_ERASE_SIZE);
+    // LLOGD("sfd erase %d %d", block, ret);
+    (void)ret;
+    return 0;
+}
+
+// Sync the block device
+static int block_device_sync(const struct lfs_config *cfg) {
+    return 0;
+}
+
+int luat_fskv_init(void) {
+    int ret = 0;
+    if (fskv == NULL) {
+        fskv = luat_heap_malloc(sizeof(luat_fskv_t));
+        if (fskv == NULL) {
+            LLOGE("out of memory when malloc fskv");
+            return -1;
+        }
+        memset(fskv, 0, sizeof(luat_fskv_t));
+        fskv->conf.read = block_device_read;
+        fskv->conf.prog = block_device_prog;
+        fskv->conf.erase = block_device_erase;
+        fskv->conf.sync = block_device_sync;
+        fskv->conf.attr_max = 0;
+        fskv->conf.file_max = 4096;
+        fskv->conf.block_count = (LFS_BLOCK_DEVICE_TOTOAL_SIZE) / LFS_BLOCK_DEVICE_ERASE_SIZE;
+        fskv->conf.block_size = LFS_BLOCK_DEVICE_ERASE_SIZE;
+        fskv->conf.block_cycles = 200;
+        fskv->conf.name_max = 63;
+        fskv->conf.read_size = LFS_BLOCK_DEVICE_CACHE_SIZE;
+        fskv->conf.cache_size = LFS_BLOCK_DEVICE_CACHE_SIZE;
+        fskv->conf.prog_size = LFS_BLOCK_DEVICE_PROG_SIZE;
+        fskv->conf.cache_size = LFS_BLOCK_DEVICE_CACHE_SIZE;
+        fskv->conf.lookahead_size = LFS_BLOCK_DEVICE_LOOK_AHEAD;
+        fskv->conf.lookahead_buffer = fskv->lookahead_buffer;
+        fskv->conf.prog_buffer = fskv->prog_buffer;
+        fskv->conf.read_buffer = fskv->read_buffer;
+
+        // LLOGD("fskv->conf.block_count %d", fskv->conf.block_count);
+        // LLOGD("fskv->conf.block_size %d", fskv->conf.block_size);
+        // LLOGD("fskv->conf.read_size %d", fskv->conf.read_size);
+        // LLOGD("fskv->conf.prog_size %d", fskv->conf.prog_size);
+        // LLOGD("fskv->conf.cache_size %d", fskv->conf.cache_size);
+
+        sfd_onchip_init(NULL);
+
+        // block_device_read(NULL, 0, 0, fskv->prog_buffer, 256);
+        // LLOGD("Flash starts %02X %02X %02X %02X", fskv->prog_buffer[0], fskv->prog_buffer[1], 
+        //                                        fskv->prog_buffer[2], fskv->prog_buffer[3]);
+        // if (fskv->prog_buffer[0] == 0x00 && fskv->prog_buffer[1] == 0x00 && 
+        //     fskv->prog_buffer[2] == 245 && fskv->prog_buffer[3] == 256) {
+
+        //     }
+        // sfd_onchip_erase(NULL, 0, LFS_BLOCK_DEVICE_TOTOAL_SIZE);
+
+        ret = lfs_mount(&fskv->lfs, &fskv->conf);
+        if (ret != LFS_ERR_OK) {
+            LLOGI("fskv mount ret %d, exe auto-format", ret);
+            ret = lfs_format(&fskv->lfs, &fskv->conf);
+            if (ret != LFS_ERR_OK) {
+                luat_heap_free(fskv);
+                fskv = NULL;
+                LLOGE("fskv auto-format ret %d", ret);
+                return ret;
+            }
+            ret = lfs_mount(&fskv->lfs, &fskv->conf);
+            if (ret != LFS_ERR_OK) {
+                luat_heap_free(fskv);
+                fskv = NULL;
+                LLOGE("fskv remount ret %d", ret);
+                return ret;
+            }
+        }
+        LLOGD("init ok");
+    }
+    return 0;
+}
+
+
+int luat_fskv_del(const char* key) {
+    lfs_remove(&fskv->lfs, key);
+    return 0;
+}
+
+int luat_fskv_set(const char* key, void* data, size_t len) {
+    lfs_file_t fd = {0};
+    int ret = 0;
+    ret = lfs_file_open(&fskv->lfs, &fd, key, LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC);
+    if (ret != LFS_ERR_OK) {
+        return -1;
+    }
+    ret = lfs_file_write(&fskv->lfs, &fd, data, len);
+    lfs_file_close(&fskv->lfs, &fd);
+    return ret;
+}
+
+int luat_fskv_get(const char* key, void* data, size_t len) {
+    lfs_file_t fd = {0};
+    int ret = 0;
+    ret = lfs_file_open(&fskv->lfs, &fd, key, LFS_O_RDONLY);
+    if (ret != LFS_ERR_OK) {
+        return 0;
+    }
+    ret = lfs_file_read(&fskv->lfs, &fd, data, len);
+    lfs_file_close(&fskv->lfs, &fd);
+    return ret > 0 ? ret : 0;
+}
+
+int luat_fskv_clear(void) {
+    int ret = 0;
+    ret = lfs_format(&fskv->lfs, &fskv->conf);
+    if (ret != LFS_ERR_OK) {
+        luat_heap_free(fskv);
+        LLOGE("fskv clear ret %d", ret);
+        return ret;
+    }
+    ret = lfs_mount(&fskv->lfs, &fskv->conf);
+    if (ret != LFS_ERR_OK) {
+        luat_heap_free(fskv);
+        LLOGE("fskv reinit ret %d", ret);
+        return ret;
+    }
+    return 0;
+}
+
+int luat_fskv_stat(size_t *using_sz, size_t *total, size_t *kv_count) {
+    *using_sz = lfs_fs_size(&fskv->lfs) * LFS_BLOCK_DEVICE_ERASE_SIZE;
+    *total = LFS_BLOCK_DEVICE_TOTOAL_SIZE;
+    lfs_dir_t dir = {0};
+    int ret = lfs_dir_open(&fskv->lfs, &dir, "");
+    if (ret != LFS_ERR_OK) {
+        LLOGW("lfs_dir_open ret %d", ret);
+        return -1;
+    }
+    size_t count = 0;
+    struct lfs_info info = {0};
+    while (1) {
+        ret = lfs_dir_read(&fskv->lfs, &dir, &info);
+        if (ret > 0) {
+            if (info.type == LFS_TYPE_REG)
+                count ++;
+        }
+        else
+            break;
+    }
+    lfs_dir_close(&fskv->lfs, &dir);
+    *kv_count = count;
+    return 0;
+}

+ 96 - 0
components/fskv/luat_fskv.h

@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2022 OpenLuat & AirM2M
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef LUAT_FSKV_H
+#define LUAT_FSKV_H
+
+#include "lfs.h"
+
+#define LFS_BLOCK_DEVICE_READ_SIZE (256)
+#define LFS_BLOCK_DEVICE_PROG_SIZE (256)
+#define LFS_BLOCK_DEVICE_CACHE_SIZE (256)
+#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)
+
+#define LUAT_FSKV_MAX_SIZE (4096)
+
+
+typedef struct luat_fskv
+{
+    char read_buffer[LFS_BLOCK_DEVICE_READ_SIZE];
+    char prog_buffer[LFS_BLOCK_DEVICE_PROG_SIZE];
+    // char cache_buffer[LFS_BLOCK_DEVICE_CACHE_SIZE];
+    char lookahead_buffer[LFS_BLOCK_DEVICE_LOOK_AHEAD];
+    lfs_t lfs;
+    struct lfs_config conf;
+}luat_fskv_t;
+
+/**
+ * @defgroup luatos_fskv 持久化数据存储接口
+ * @{
+ */
+
+/**
+ * @brief 初始化kv数据存储
+ * 
+ * @return int == 0 正常 != 0失败
+ */
+int luat_fskv_init(void);
+
+/**
+ * @brief 删除指定的key
+ * @param key[IN] 待删除的key值
+ * @return int == 0 正常 != 0失败
+ */
+int luat_fskv_del(const char* key);
+
+/**
+ * @brief 写入指定key的数据
+ * @param key[IN] 待写入的key值,不能为NULL,必须是\0结尾,最大长度64字节
+ * @param data[IN] 待写入的数据, 不需要\0结尾
+ * @param len[IN] 待写入的数据长度, 不含\0,当前支持最大长度255字节
+ * @return int == 0 正常 != 0失败
+ */
+int luat_fskv_set(const char* key, void* data, size_t len);
+
+/**
+ * @brief 读取指定key的数据
+ * @param key[IN] 待读取的key值,不能为NULL,必须是\0结尾
+ * @param data[IN] 待读取的数据, 可写入空间必须大于等于len值
+ * @param len[IN] 待读取的数据长度最大长度, 不含\0
+ * @return int > 0 实际读取的长度, <=0 失败
+ */
+int luat_fskv_get(const char* key, void* data, size_t len);
+
+/**
+ * @brief 清空所有数据
+ * @return int == 0 正常 != 0失败
+ */
+int luat_fskv_clear(void);
+
+int luat_fskv_stat(size_t *using_sz, size_t *max_sz, size_t *kv_count);
+
+/**
+ * @}
+ */
+
+#endif

+ 418 - 0
components/fskv/luat_lib_fskv.c

@@ -0,0 +1,418 @@
+
+/*
+@module  fskv
+@summary kv数据库,掉电不丢数据
+@version 1.0
+@date    2022.12.29
+@demo    fskv
+@tag     LUAT_USE_FSKV
+@usage
+-- 本库的目标是替代fdb库
+-- 1. 兼容fdb的函数
+-- 2. 使用fdb的flash空间,启用时也会替代fdb库
+fskv.init()
+fskv.set("wendal", 1234)
+log.info("fskv", "wendal", fskv.get("wendal"))
+
+--[[ 
+fskv与fdb的实现机制导致的差异
+
+                    fskv          fdb
+1. value长度        4096           255
+2. key长度          63             64
+]]
+*/
+
+#include "luat_base.h"
+#include "luat_msgbus.h"
+#include "luat_malloc.h"
+
+#include "luat_fskv.h"
+
+#ifndef LUAT_LOG_TAG
+#define LUAT_LOG_TAG "fskv"
+#include "luat_log.h"
+#endif
+
+extern luat_fskv_t* fskv;
+
+static char fskv_read_buff[LUAT_FSKV_MAX_SIZE];
+
+/**
+初始化kv数据库
+@api fskv.kvdb_init(name, partition)
+@string 数据库名,当前仅支持env
+@string FAL分区名,当前仅支持onchip_fdb
+@return boolean 成功返回true,否则返回false
+@usage
+-- fdb库基于 flashdb , 再次表示感谢.
+if fskv.kvdb_init("env", "onchip_fdb") then
+    log.info("fdb", "kv数据库初始化成功")
+end
+
+-- 关于清空fdb库
+-- 下载工具是没有提供直接清除fdb数据的途径的, 但有有办法解决
+-- 写一个main.lua, 执行 fskv.kvdb_init 后 执行 fskv.clear() 即可全清fdb数据.
+ */
+static int l_fskvdb_init(lua_State *L) {
+    if (fskv == NULL) {
+        int ret = luat_fskv_init();
+        lua_pushboolean(L, ret == 0 ? 1 : 0);
+    }
+    else {
+        lua_pushboolean(L, 1);
+    }
+    return 1;
+}
+
+/**
+设置一对kv数据
+@api fskv.kv_set(key, value)
+@string key的名称,必填,不能空字符串
+@string 用户数据,必填,不能nil, 支持字符串/数值/table/布尔值, 数据长度最大255字节
+@return boolean 成功返回true,否则返回false
+@return number 第二个为返回为flashdb的fdb_kv_set_blob返回详细状态,0:无错误 1:擦除错误 2:读错误 3:些错误 4:未找到 5:kv名字错误 6:kv名字存在 7:已保存 8:初始化错误
+@usage
+if fskv.kvdb_init("env", "onchip_fdb") then
+    log.info("fdb", fskv.kv_set("wendal", "goodgoodstudy"))
+end
+ */
+static int l_fskv_set(lua_State *L) {
+    if (fskv == 0) {
+        LLOGE("call fskv.init() first!!!");
+        return 0;
+    }
+    size_t len;
+    luaL_Buffer buff;
+    luaL_buffinit(L, &buff);
+    const char* key = luaL_checkstring(L, 1);
+    //luaL_addchar(&buff, 0xA5);
+    int type = lua_type(L, 2);
+    switch (type)
+    {
+    case LUA_TBOOLEAN:
+        luaL_addchar(&buff, LUA_TBOOLEAN);
+        bool val = lua_toboolean(L, 2);
+        luaL_addlstring(&buff, (const char*)&val, sizeof(val));
+        break;
+    case LUA_TNUMBER:
+        if (lua_isinteger(L, 2)) {
+            luaL_addchar(&buff, LUA_TINTEGER); // 自定义类型
+            lua_Integer val = luaL_checkinteger(L, 2);
+            luaL_addlstring(&buff, (const char*)&val, sizeof(val));
+        }
+        else {
+            luaL_addchar(&buff, LUA_TNUMBER);
+            lua_getglobal(L, "pack");
+            if (lua_isnil(L, -1)) {
+                LLOGW("float number need pack lib");
+                lua_pushboolean(L, 0);
+                return 1;
+            }
+            lua_getfield(L, -1, "pack");
+            lua_pushstring(L, ">f");
+            lua_pushvalue(L, 2);
+            lua_call(L, 2, 1);
+            if (lua_isstring(L, -1)) {
+                const char* val = luaL_checklstring(L, -1, &len);
+                luaL_addlstring(&buff, val, len);
+            }
+            else {
+                LLOGW("kdb store number fail!!");
+                lua_pushboolean(L, 0);
+                return 1;
+            }
+        }
+        break;
+    case LUA_TSTRING:
+    {
+        luaL_addchar(&buff, LUA_TSTRING);
+        const char* val = luaL_checklstring(L, 2, &len);
+        luaL_addlstring(&buff, val, len);
+        break;
+    }
+    case LUA_TTABLE:
+    {
+        lua_settop(L, 2);
+        lua_getglobal(L, "json");
+        if (lua_isnil(L, -1)) {
+            LLOGW("miss json lib, not support table value");
+            lua_pushboolean(L, 0);
+            return 1;
+        }
+        lua_getfield(L, -1, "encode");
+        if (lua_isfunction(L, -1)) {
+            lua_pushvalue(L, 2);
+            lua_call(L, 1, 1);
+            if (lua_isstring(L, -1)) {
+                luaL_addchar(&buff, LUA_TTABLE);
+                const char* val = luaL_checklstring(L, -1, &len);
+                luaL_addlstring(&buff, val, len);
+            }
+            else {
+                LLOGW("json.encode(val) report error");
+                lua_pushboolean(L, 0);
+                return 1;
+            }
+        }
+        else {
+            LLOGW("miss json.encode, not support table value");
+            lua_pushboolean(L, 0);
+            return 1;
+        }
+        break;
+    }
+    default:
+    {
+        LLOGW("function/userdata/nil/thread isn't allow");
+        lua_pushboolean(L, 0);
+        return 1;
+    }
+    }
+    if (buff.n > LUAT_FSKV_MAX_SIZE) {
+        LLOGE("value too big %d max %d", buff.n, LUAT_FSKV_MAX_SIZE);
+        lua_pushboolean(L, 0);
+        return 1;
+    }
+    int ret = luat_fskv_set(key, buff.b, buff.n);
+    lua_pushboolean(L, ret == buff.n ? 1 : 0);
+    lua_pushinteger(L, ret);
+    return 2;
+}
+
+/**
+根据key获取对应的数据
+@api fskv.kv_get(key, skey)
+@string key的名称,必填,不能空字符串
+@string 可选的次级key,仅当原始值为table时有效,相当于 fskv.kv_get(key)[skey]
+@return any 存在则返回数据,否则返回nil
+@usage
+if fskv.init() then
+    log.info("fdb", fskv.get("wendal"))
+end
+ */
+static int l_fskv_get(lua_State *L) {
+    if (fskv == NULL) {
+        LLOGE("call fskv.init() first!!!");
+        return 0;
+    }
+    // luaL_Buffer buff;
+    const char* key = luaL_checkstring(L, 1);
+    const char* skey = luaL_optstring(L, 2, "");
+    // luaL_buffinitsize(L, &buff, 8192);
+    char* buff = fskv_read_buff;
+    size_t read_len = luat_fskv_get(key, buff, LUAT_FSKV_MAX_SIZE);
+
+    if (read_len < 2) {
+        return 0;
+    }
+
+    lua_Integer *intVal;
+    // lua_Number *numVal;
+
+    if (read_len) {
+        // LLOGD("KV value T=%02X", buff.b[0]);
+        switch(buff[0]) {
+        case LUA_TBOOLEAN:
+            lua_pushboolean(L, buff[1]);
+            break;
+        case LUA_TNUMBER:
+            lua_getglobal(L, "pack");
+            lua_getfield(L, -1, "unpack");
+            lua_pushlstring(L, (char*)(buff + 1), read_len - 1);
+            lua_pushstring(L, ">f");
+            lua_call(L, 2, 2);
+            // _, val = pack.unpack(data, ">f")
+            break;
+        case LUA_TINTEGER:
+            intVal = (lua_Integer*)(&buff[1]);
+            lua_pushinteger(L, *intVal);
+            break;
+        case LUA_TSTRING:
+            lua_pushlstring(L, (const char*)(buff + 1), read_len - 1);
+            break;
+        case LUA_TTABLE:
+            lua_getglobal(L, "json");
+            lua_getfield(L, -1, "decode");
+            lua_pushlstring(L, (const char*)(buff + 1), read_len - 1);
+            lua_call(L, 1, 1);
+            if (strlen(skey) > 0 && lua_istable(L, -1)) {
+                lua_getfield(L, -1, skey);
+            }
+            break;
+        default :
+            LLOGW("bad value prefix %02X", buff[0]);
+            lua_pushnil(L);
+            break;
+        }
+        return 1;
+    }
+    lua_pushnil(L);
+    return 1;
+}
+
+/**
+根据key删除数据
+@api fskv.kv_del(key)
+@string key的名称,必填,不能空字符串
+@return bool 成功返回true,否则返回false
+@usage
+if fskv.kvdb_init("env", "onchip_fdb") then
+    log.info("fdb", fskv.kv_del("wendal"))
+end
+ */
+static int l_fskv_del(lua_State *L) {
+    if (fskv == NULL) {
+        LLOGE("call fskv.init() first!!!");
+        return 0;
+    }
+    const char* key = luaL_checkstring(L, 1);
+    if (key == NULL) {
+        lua_pushboolean(L, 0);
+        return 1;
+    }
+    int ret = luat_fskv_del(key);
+    lua_pushboolean(L, ret == 0 ? 1 : 0);
+    return 1;
+}
+
+/**
+清空整个kv数据库
+@api fskv.kv_clr()
+@return bool 成功返回true,否则返回false
+@usage
+-- 清空
+fskv.kv_clr()
+ */
+static int l_fskv_clr(lua_State *L) {
+    if (fskv == NULL) {
+        LLOGE("call fskv.init() first!!!");
+        return 0;
+    }
+    int ret = luat_fskv_clear();
+    lua_pushboolean(L, ret == 0 ? 1 : 0);
+    return 1;
+}
+
+
+// /**
+// kv数据库迭代器
+// @api fskv.kv_iter()
+// @return userdata 成功返回迭代器指针,否则返回nil
+// @usage
+// -- 清空
+// local iter = fskv.kv_iter()
+// if iter then
+//     while 1 do
+//         local k = fskv.kv_next(iter)
+//         if not k then
+//             break
+//         end
+//         log.info("fdb", k, "value", fskv.kv_get(k))
+//     end
+// end
+//  */
+// static int l_fskv_iter(lua_State *L) {
+//     if (kvdb_inited == 0) {
+//         LLOGE("call fskv.kvdb_init first!!!");
+//         return 0;
+//     }
+//     fdb_kv_iterator_t iter = lua_newuserdata(L, sizeof(struct fdb_kv_iterator));
+//     if (iter == NULL) {
+//         return 0;
+//     }
+//     iter = fdb_kv_iterator_init(iter);
+//     if (iter != NULL) {
+//         return 1;
+//     }
+//     return 0;
+// }
+
+// /**
+// kv迭代器获取下一个key
+// @api fskv.kv_iter(iter)
+// @userdata fskv.kv_iter()返回的指针
+// @return string 成功返回字符串key值, 否则返回nil
+// @usage
+// -- 清空
+// local iter = fskv.kv_iter()
+// if iter then
+//     while 1 do
+//         local k = fskv.kv_next(iter)
+//         if not k then
+//             break
+//         end
+//         log.info("fdb", k, "value", fskv.kv_get(k))
+//     end
+// end
+//  */
+// static int l_fskv_next(lua_State *L) {
+//     fdb_kv_t cur_kv = NULL;
+//     fdb_kv_iterator_t iter = lua_touserdata(L, 1);
+//     if (iter == NULL) {
+//         return 0;
+//     }
+//     bool ret = fdb_kv_iterate(kvdb, iter);
+//     if (ret) {
+//         cur_kv = &(iter->curr_kv);
+//         lua_pushlstring(L, cur_kv->name, cur_kv->name_len);
+//         // TODO 把值也返回一下?
+//         return 1;
+//     }
+//     return 0;
+// }
+
+/*
+获取kv数据库状态
+@api fskv.stat()
+@return int 已使用的空间,单位字节
+@return int 总可用空间, 单位字节
+@return int 总kv键值对数量, 单位个
+@usage
+local used, total,kv_count = fskv.stat()
+log.info("fdb", "kv", used,total,kv_count)
+*/
+static int l_fskv_stat(lua_State *L) {
+    size_t using_sz = 0;
+    size_t max_sz = 0;
+    size_t kv_count = 0;
+    if (fskv == 0) {
+        LLOGE("call fskv.init() first!!!");
+        return 0;
+    }
+    luat_fskv_stat(&using_sz, &max_sz, &kv_count);
+    lua_pushinteger(L, using_sz);
+    lua_pushinteger(L, max_sz);
+    lua_pushinteger(L, kv_count);
+    return 3;
+}
+
+#include "rotable2.h"
+static const rotable_Reg_t reg_fskv[] =
+{
+    { "init" ,              ROREG_FUNC(l_fskvdb_init)},
+    { "set",                ROREG_FUNC(l_fskv_set)},
+    { "get",                ROREG_FUNC(l_fskv_get)},
+    { "del",                ROREG_FUNC(l_fskv_del)},
+    { "clr",                ROREG_FUNC(l_fskv_clr)},
+    { "stat",               ROREG_FUNC(l_fskv_stat)},
+    // { "kv_iter",            ROREG_FUNC(l_fskv_iter)},
+    // { "kv_next",            ROREG_FUNC(l_fskv_next)},
+    // { "kv_stat",            ROREG_FUNC(l_fskv_stat)},
+
+    // -- 提供与fdb兼容的API
+    { "kvdb_init" ,         ROREG_FUNC(l_fskvdb_init)},
+    { "kv_set",             ROREG_FUNC(l_fskv_set)},
+    { "kv_get",             ROREG_FUNC(l_fskv_get)},
+    { "kv_del",             ROREG_FUNC(l_fskv_del)},
+    { "kv_clr",             ROREG_FUNC(l_fskv_clr)},
+    { "kv_stat",            ROREG_FUNC(l_fskv_stat)},
+    { NULL,                 ROREG_INT(0)}
+};
+
+LUAMOD_API int luaopen_fskv( lua_State *L ) {
+    luat_newlib2(L, reg_fskv);
+    lua_pushvalue(L, -1);
+    lua_setglobal(L, "fdb");
+    return 1;
+}

+ 14 - 5
components/lfs/lfs_util.h

@@ -25,12 +25,21 @@
 #include <string.h>
 
 //--------------------------------
-//#include <inttypes.h>
+// #if (defined(AIR101) || defined(AIR103))
+
+// #else
+// #include <inttypes.h>
+// #endif
+#include "luat_base.h"
 #include "luat_malloc.h"
 #define LFS_NO_ASSERT 1
 #define LFS_NO_DEBUG 1
 #define LFS_NO_WARN 1
 #define LFS_NO_ERROR 1
+#define LFS_NO_ERROR 1
+// #define LFS_YES_TRACE
+#define LUAT_LOG_TAG "lfs"
+#include "luat_log.h"
 //--------------------------------
 
 #ifndef LFS_NO_MALLOC
@@ -59,28 +68,28 @@ extern "C"
 // Logging functions
 #ifdef LFS_YES_TRACE
 #define LFS_TRACE(fmt, ...) \
-    printf("lfs_trace:%d: " fmt "\n", __LINE__, __VA_ARGS__)
+    LLOGD("lfs_trace:%d: " fmt "\n", __LINE__, __VA_ARGS__)
 #else
 #define LFS_TRACE(fmt, ...)
 #endif
 
 #ifndef LFS_NO_DEBUG
 #define LFS_DEBUG(fmt, ...) \
-    printf("lfs_debug:%d: " fmt "\n", __LINE__, __VA_ARGS__)
+    LLOGD("lfs_debug:%d: " fmt "\n", __LINE__, __VA_ARGS__)
 #else
 #define LFS_DEBUG(fmt, ...)
 #endif
 
 #ifndef LFS_NO_WARN
 #define LFS_WARN(fmt, ...) \
-    printf("lfs_warn:%d: " fmt "\n", __LINE__, __VA_ARGS__)
+    LLOGW("lfs_warn:%d: " fmt "\n", __LINE__, __VA_ARGS__)
 #else
 #define LFS_WARN(fmt, ...)
 #endif
 
 #ifndef LFS_NO_ERROR
 #define LFS_ERROR(fmt, ...) \
-    printf("lfs_error:%d: " fmt "\n", __LINE__, __VA_ARGS__)
+    LLOGE("lfs_error:%d: " fmt "\n", __LINE__, __VA_ARGS__)
 #else
 #define LFS_ERROR(fmt, ...)
 #endif

+ 33 - 0
components/mempool/profiler/bind/luat_lib_profiler.c

@@ -0,0 +1,33 @@
+#include "luat_base.h"
+#include "luat_malloc.h"
+#include "luat_malloc.h"
+#include "luat_timer.h"
+
+static int l_profiler_start(lua_State *L) {
+    luat_profiler_start();
+    return 0;
+}
+
+static int l_profiler_stop(lua_State *L) {
+    luat_profiler_stop();
+    return 0;
+}
+
+static int l_profiler_print(lua_State *L) {
+    luat_profiler_print();
+    return 0;
+}
+
+#include "rotable2.h"
+static const rotable_Reg_t reg_profiler[] =
+{
+    { "start" ,        ROREG_FUNC(l_profiler_start)},
+    { "stop" ,         ROREG_FUNC(l_profiler_stop)},
+    { "print",         ROREG_FUNC(l_profiler_print)},
+	{ NULL,            ROREG_INT(0)}
+};
+
+LUAMOD_API int luaopen_profiler( lua_State *L ) {
+    luat_newlib2(L, reg_profiler);
+    return 1;
+}

+ 27 - 0
components/mempool/profiler/include/luat_profiler.h

@@ -0,0 +1,27 @@
+#ifndef LUAT_PROFILER_H
+#define LUAT_PROFILER_H
+
+#include "stdint.h"
+
+typedef struct luat_profiler_ctx
+{
+    int tag;
+    int ticks_start;
+    int ticks_stop;
+    uint32_t counter_malloc;
+    uint32_t counter_free;
+    uint32_t counter_realloc;
+    uint32_t lua_heap_begin_used;
+    uint32_t lua_heap_end_used;
+    uint32_t sys_heap_begin_used;
+    uint32_t sys_heap_end_used;
+}luat_profiler_ctx_t;
+
+void* luat_profiler_alloc(void *ud, void *ptr, size_t osize, size_t nsize);
+
+int luat_profiler_start(void);
+int luat_profiler_stop(void);
+
+void luat_profiler_print(void);
+
+#endif

+ 79 - 0
components/mempool/profiler/src/luat_profiler.c

@@ -0,0 +1,79 @@
+#include "luat_base.h"
+#include "luat_malloc.h"
+#include "luat_profiler.h"
+
+#define LUAT_LOG_TAG "profiler"
+#include "luat_log.h"
+
+static luat_profiler_ctx_t ctx;
+
+void* luat_profiler_alloc(void *ud, void *ptr, size_t osize, size_t nsize) {
+    // TODO 加调试信息的head, 这样才能记录实际malloc和realloc的大小
+    void* dst = NULL;
+    if (ctx.tag == 0) { // 停用状态, 原样返回
+        return luat_heap_alloc(ud, ptr, osize, nsize);
+    }
+    LLOGD("ud %p ptr %p oszie %08X nsize %08X", ud, ptr, osize, nsize);
+    if (ptr == NULL && nsize == 0) {
+        LLOGD("free NULL");
+        return NULL;
+    }
+    // 如果指针不为NULL, 目标大小为0, 那就是free
+    else if (ptr != NULL && nsize == 0) {
+        LLOGD("call free %p", ptr);
+        ctx.counter_free ++;
+        dst = luat_heap_alloc(ud, ptr, osize, nsize);
+        LLOGD("done free %p", ptr);
+        return dst;
+    }
+    // 如果指针为NULL, 目标大小不为0, 那就是malloc
+    else if (ptr == NULL && nsize > 0) {
+        ctx.counter_malloc ++;
+        LLOGD("call malloc %08X type %08X", nsize, osize);
+        dst = luat_heap_alloc(ud, ptr, osize, nsize);
+        LLOGD("call malloc %08X type %08X %p", nsize, osize, dst);
+        return dst;
+    }
+    // 最后剩下realloc
+    else {
+        ctx.counter_realloc ++;
+        LLOGD("call realloc %08X osize %08X", nsize, osize);
+        dst = luat_heap_alloc(ud, ptr, osize, nsize);
+        LLOGD("call realloc %08X osize %08X %p", nsize, osize, dst);
+        return dst;
+    }
+}
+
+int luat_profiler_start(void) {
+    size_t total; size_t used; size_t max_used;
+    LLOGD("start profiler");
+    memset(&ctx, 0, sizeof(luat_profiler_ctx_t));
+    ctx.tag = 1;
+    luat_meminfo_luavm(&total, &ctx.lua_heap_begin_used, &max_used);
+    LLOGD("%s luavm %ld %ld %ld", "profiler start", total, ctx.lua_heap_begin_used, max_used);
+    luat_meminfo_sys(&total, &ctx.sys_heap_begin_used, &max_used);
+    LLOGD("%s sys   %ld %ld %ld", "profiler start", total, ctx.sys_heap_begin_used, max_used);
+    return 0;
+}
+
+int luat_profiler_stop(void) {
+    size_t total; size_t used; size_t max_used;
+    LLOGD("stop profiler");
+    ctx.tag = 0;
+    luat_meminfo_luavm(&total, &ctx.lua_heap_end_used, &max_used);
+    LLOGD("%s luavm %ld %ld %ld", "profiler stop", total, ctx.lua_heap_end_used, max_used);
+    luat_meminfo_sys(&total, &ctx.sys_heap_end_used, &max_used);
+    LLOGD("%s sys   %ld %ld %ld", "profiler stop", total, ctx.sys_heap_end_used, max_used);
+    return 0;
+}
+
+void luat_profiler_print(void) {
+    size_t total; size_t used; size_t max_used;
+    LLOGD("============================================");
+    // 输出调用次数
+    LLOGD("counter malloc %08X free %08X realloc %08X", ctx.counter_malloc, ctx.counter_free, ctx.counter_realloc);
+    // 输出前后内存大小
+    LLOGD("heap used at start: lua %08X sys %08X", ctx.lua_heap_begin_used, ctx.sys_heap_begin_used);
+    LLOGD("heap used at stop : lua %08X sys %08X", ctx.lua_heap_end_used, ctx.sys_heap_end_used);
+    LLOGD("============================================");
+}

+ 247 - 0
components/minmea/luat_gnss.c

@@ -0,0 +1,247 @@
+#include "luat_base.h"
+#include "luat_msgbus.h"
+#include "luat_malloc.h"
+#include "luat_uart.h"
+#include "luat_rtc.h"
+#include "luat_mcu.h"
+
+#define LUAT_LOG_TAG "gnss"
+#include "luat_log.h"
+
+#include "minmea.h"
+
+luat_libgnss_t *libgnss_gnss;
+luat_libgnss_tmp_t *libgnss_gnsstmp;
+char *libgnss_recvbuff;
+
+// static int parse_nmea(const char* line);
+// static int parse_data(const char* data, size_t len);
+
+void luat_libgnss_uart_recv_cb(int uart_id, uint32_t data_len) {
+    (void)data_len;
+    if (libgnss_recvbuff == NULL)
+        return;
+    //LLOGD("uart recv cb");
+    int len = 0;
+    while (1) {
+        len = luat_uart_read(uart_id, libgnss_recvbuff, RECV_BUFF_SIZE - 1);
+        if (len < 1 || len > RECV_BUFF_SIZE)
+            break;
+        //LLOGD("uart recv %d", len);
+        libgnss_recvbuff[len] = 0;
+        if (libgnss_gnss == NULL)
+            continue;
+        if (libgnss_gnss->debug) {
+            //LLOGD("recv %s", libgnss_recvbuff);
+        }
+        luat_libgnss_parse_data(libgnss_recvbuff, len);
+    }
+}
+
+
+static uint32_t msg_counter[MINMEA_SENTENCE_MAX_ID];
+
+int luat_libgnss_init(void) {
+    if (libgnss_gnss == NULL) {
+        libgnss_gnss = luat_heap_malloc(sizeof(luat_libgnss_t));
+        if (libgnss_gnss == NULL) {
+            LLOGW("out of memory for libgnss data parse");
+            return 0;
+        }
+        libgnss_gnsstmp = luat_heap_malloc(sizeof(luat_libgnss_tmp_t));
+        if (libgnss_gnsstmp == NULL) {
+            luat_heap_free(libgnss_gnss);
+            LLOGW("out of memory for libgnss data parse");
+            return 0;
+        }
+        // gnss->lua_ref = luaL_ref(L, LUA_REGISTRYINDEX);
+        memset(libgnss_gnss, 0, sizeof(luat_libgnss_t));
+        memset(libgnss_gnsstmp, 0, sizeof(luat_libgnss_tmp_t));
+    }
+    //lua_pushboolean(L, 1);
+    return 1;
+}
+
+int luat_libgnss_parse_data(const char* data, size_t len) {
+    size_t prev = 0;
+    static char nmea_tmp_buff[86] = {0}; // nmea 最大长度82,含换行符
+    if (data[0] == 0xAA && data[1] == 0xF0) {
+        LLOGD("EPH data resp?");
+    }
+
+    for (size_t offset = 0; offset < len; offset++)
+    {
+        // \r == 0x0D  \n == 0x0A
+        if (data[offset] == 0x0A) {
+            // 最短也需要是 OK\r\n
+            // 应该\r\n的
+            // 太长了
+            if (offset - prev < 3 || data[offset - 1] != 0x0D || offset - prev > 82) {
+                prev = offset + 1;
+                continue;
+            }
+            memcpy(nmea_tmp_buff, data + prev, offset - prev - 1);
+            nmea_tmp_buff[offset - prev - 1] = 0x00;
+            if (libgnss_gnss->debug) {
+                LLOGD(">> %s", nmea_tmp_buff);
+            }
+            luat_libgnss_parse_nmea((const char*)nmea_tmp_buff);
+            prev = offset + 1;
+        }
+    }
+    return 0;
+}
+
+int luat_libgnss_parse_nmea(const char* line) {
+    // $GNRMC,080313.00,A,2324.40756,N,11313.86184,E,0.284,,010720,,,A*68
+    //if (gnss != NULL && gnss->debug)
+    //    LLOGD("GNSS [%s]", line);
+    if (libgnss_gnss == NULL && !luat_libgnss_init()) {
+        return 0;
+    }
+    struct minmea_sentence_gsv *frame_gsv = &libgnss_gnsstmp->frame_gsv;
+    enum minmea_sentence_id id = minmea_sentence_id(line, false);
+    if (id == MINMEA_UNKNOWN || id >= MINMEA_SENTENCE_MAX_ID || id == MINMEA_INVALID)
+        return -1;
+    msg_counter[id] ++;
+    // int ticks = 0;
+    struct tm tblock = {0};
+    int ticks = luat_mcu_ticks();
+    switch (id) {
+        case MINMEA_SENTENCE_RMC: {
+            if (minmea_parse_rmc(&(libgnss_gnsstmp->frame_rmc), line)) {
+                // 清空gsa
+                memset(libgnss_gnss->frame_gsa, 0, sizeof(struct minmea_sentence_gsa) * FRAME_GSA_MAX);
+                if (libgnss_gnsstmp->frame_rmc.valid) {
+                    memcpy(&(libgnss_gnss->frame_rmc), &libgnss_gnsstmp->frame_rmc, sizeof(struct minmea_sentence_rmc));
+                    #ifdef LUAT_USE_MCU
+                    if (libgnss_gnss->rtc_auto && (libgnss_gnss->fix_at_ticks == 0 || ((uint32_t)(ticks - libgnss_gnss->fix_at_ticks)) > 600*1000)) {
+                        LLOGI("Auto-Set RTC by GNSS RMC");
+                        tblock.tm_sec =  libgnss_gnss->frame_rmc.time.seconds;
+                        tblock.tm_min =  libgnss_gnss->frame_rmc.time.minutes;
+                        tblock.tm_hour = libgnss_gnss->frame_rmc.time.hours;
+                        tblock.tm_mday = libgnss_gnss->frame_rmc.date.day;
+                        tblock.tm_mon =  libgnss_gnss->frame_rmc.date.month;
+                        tblock.tm_year = libgnss_gnss->frame_rmc.date.year;
+                        luat_rtc_set(&tblock);
+                        // luat_rtc_set_tamp32(mktime(&tblock));
+                    }
+                    if (libgnss_gnss->fix_at_ticks == 0) {
+                        LLOGI("Fixed %d %d", libgnss_gnss->frame_rmc.latitude.value, libgnss_gnss->frame_rmc.longitude.value);
+                        // 发布系统消息
+                        luat_libgnss_state_onchanged(GNSS_STATE_FIXED);
+                    }
+                    libgnss_gnss->fix_at_ticks = luat_mcu_ticks();
+                    #endif
+                }
+                else {
+                    if (libgnss_gnss->fix_at_ticks && libgnss_gnss->frame_rmc.valid) {
+                        LLOGI("Lose"); // 发布系统消息
+                        luat_libgnss_state_onchanged(GNSS_STATE_LOSE);
+                    }
+                    libgnss_gnss->fix_at_ticks = 0;
+                    libgnss_gnss->frame_rmc.valid = 0;
+                    if (libgnss_gnsstmp->frame_rmc.date.year > 0) {
+                        memcpy(&(libgnss_gnss->frame_rmc.date), &(libgnss_gnsstmp->frame_rmc.date), sizeof(struct minmea_date));
+                    }
+                    if (libgnss_gnsstmp->frame_rmc.time.hours > 0) {
+                        memcpy(&(libgnss_gnss->frame_rmc.time), &(libgnss_gnsstmp->frame_rmc.time), sizeof(struct minmea_time));
+                    }
+                }
+            }
+        } break;
+
+        case MINMEA_SENTENCE_GGA: {
+            //struct minmea_sentence_gga frame_gga;
+            if (minmea_parse_gga(&libgnss_gnsstmp->frame_gga, line)) {
+                memcpy(&(libgnss_gnss->frame_gga), &libgnss_gnsstmp->frame_gga, sizeof(struct minmea_sentence_gga));
+                //LLOGD("$GGA: fix quality: %d", frame_gga.fix_quality);
+            }
+        } break;
+
+        case MINMEA_SENTENCE_GSA: {
+            //LLOGD("GSV %s", line);
+            if (minmea_parse_gsa(&(libgnss_gnsstmp->frame_gsa), line)) {
+                for (size_t i = 0; i < FRAME_GSA_MAX; i++)
+                {
+                    // 如果是mode=0,代表空的
+                    if (libgnss_gnss->frame_gsa[i].mode == 0) {
+                        memcpy(&libgnss_gnss->frame_gsa[i], &(libgnss_gnsstmp->frame_gsa), sizeof(struct minmea_sentence_gsa));
+                        break;
+                    }
+                }
+                
+            }
+        } break;
+
+        case MINMEA_SENTENCE_GLL: {
+            if (minmea_parse_gll(&(libgnss_gnss->frame_gll), line)) {
+                // memcpy(&(gnss->frame_gll), &frame_gll, sizeof(struct minmea_sentence_gsa));
+            }
+        } break;
+
+        // case MINMEA_SENTENCE_GST: {
+        //     if (minmea_parse_gst(&gnsstmp->frame_gst, line)) {
+        //         memcpy(&(gnss->frame_gst), &gnsstmp->frame_gst, sizeof(struct minmea_sentence_gst));
+        //     }
+        // } break;
+
+        case MINMEA_SENTENCE_GSV: {
+            //LLOGD("Got GSV : %s", line);
+            if (minmea_parse_gsv(frame_gsv, line)) {
+                struct minmea_sentence_gsv *gsvs = libgnss_gnss->frame_gsv_gp;
+                if (0 == memcmp("$GPGSV", line, strlen("$GPGSV"))) {
+                    gsvs = libgnss_gnss->frame_gsv_gp;
+                }
+                else if (0 == memcmp("$GBGSV", line, strlen("$GBGSV"))) {
+                    gsvs = libgnss_gnss->frame_gsv_gb;
+                }
+                else if (0 == memcmp("$GLGSV", line, strlen("$GLGSV"))) {
+                    gsvs = libgnss_gnss->frame_gsv_gl;
+                }
+                else if (0 == memcmp("$GAGSV", line, strlen("$GAGSV"))) {
+                    gsvs = libgnss_gnss->frame_gsv_ga;
+                }
+                //LLOGD("$GSV: message %d of %d", frame_gsv.msg_nr, frame_gsv.total_msgs);
+                if (frame_gsv->msg_nr == 1) {
+                    //LLOGD("Clean GSV");
+                    memset(gsvs, 0, sizeof(struct minmea_sentence_gsv) * FRAME_GSV_MAX);
+                }
+                if (frame_gsv->msg_nr >= 1 && frame_gsv->msg_nr <= FRAME_GSV_MAX) {
+                    //LLOGD("memcpy GSV %d", frame_gsv.msg_nr);
+                    memcpy(&gsvs[frame_gsv->msg_nr - 1], frame_gsv, sizeof(struct minmea_sentence_gsv));
+                }
+            }
+            else {
+                //LLOGD("bad GSV %s", line);
+            }
+        } break;
+
+        case MINMEA_SENTENCE_VTG: {
+            //struct minmea_sentence_vtg frame_vtg;
+            if (minmea_parse_vtg(&(libgnss_gnsstmp->frame_vtg), line)) {
+                memcpy(&(libgnss_gnss->frame_vtg), &libgnss_gnsstmp->frame_vtg, sizeof(struct minmea_sentence_vtg));
+                //--------------------------------------
+                // 暂时不发GPS_MSG_REPORT
+                // lua_getglobal(L, "sys_pub");
+                // if (lua_isfunction(L, -1)) {
+                //     lua_pushstring(L, "GPS_MSG_REPORT");
+                //     lua_call(L, 1, 0);
+                // }
+                // else {
+                //     lua_pop(L, 1);
+                // }
+                //--------------------------------------
+            }
+        } break;
+        case MINMEA_SENTENCE_ZDA: {
+            if (minmea_parse_zda(&(libgnss_gnsstmp->frame_zda), line)) {
+                memcpy(&(libgnss_gnss->frame_zda), &libgnss_gnsstmp->frame_zda, sizeof(struct minmea_sentence_zda));
+            }
+        } break;
+        default:
+            //LLOGD("why happen");
+            break;
+    }
+    return 0;
+}

+ 422 - 327
components/minmea/luat_lib_libgnss.c

@@ -6,181 +6,151 @@
 @date    2020.07.03
 @demo libgnss
 @tag LUAT_USE_LIBGNSS
+@usage
+-- 方案1, 经lua层进行数据中转
+uart.setup(2, 115200)
+uart.on(2, "recv", function(id, len)
+    while 1 do
+        local data = uart.read(id, 1024)
+        if data and #data > 1 then
+            libgnss.parse(data)
+        else
+            break
+        end
+    end
+end)
+-- 方案2, 适合2022.12.26之后编译固件,效率更高一些
+uart.setup(2, 115200)
+libgnss.bind(2)
+
+-- 可选调试模式
+-- libgnss.debug(true)
+
+sys.subscribe("GNSS_STATE", function(event, ticks)
+    -- event取值有 
+    -- FIXED 定位成功
+    -- LOSE  定位丢失
+    -- ticks是事件发生的时间,一般可以忽略
+    log.info("gnss", "state", event, ticks)
+end)
 */
 #include "luat_base.h"
 #include "luat_msgbus.h"
 #include "luat_malloc.h"
 #include "luat_uart.h"
+#include "luat_mcu.h"
+#include "luat_rtc.h"
 
-#define LUAT_LOG_TAG "luat.gnss"
+#define LUAT_LOG_TAG "gnss"
 #include "luat_log.h"
 
 #include "minmea.h"
 
-typedef struct luat_libgnss
-{
-    uint8_t debug;
-    uint8_t prev_fixed;
-    // int lua_ref;
-    struct minmea_sentence_rmc frame_rmc;
-    struct minmea_sentence_gga frame_gga;
-    struct minmea_sentence_gll frame_gll;
-    struct minmea_sentence_gst frame_gst;
-    struct minmea_sentence_gsv frame_gsv[3];
-    struct minmea_sentence_vtg frame_vtg;
-    struct minmea_sentence_gsa frame_gsa;
-    struct minmea_sentence_zda frame_zda;
-} luat_libgnss_t;
-
-static luat_libgnss_t *gnss = NULL;
-static luat_libgnss_t *gnsstmp = NULL;
-
-static int luat_libgnss_init(void) {
-    if (gnss == NULL) {
-        gnss = luat_heap_malloc(sizeof(luat_libgnss_t));
-        if (gnss == NULL) {
-            LLOGW("out of memory for libgnss data parse");
-            return 0;
-        }
-        gnsstmp = luat_heap_malloc(sizeof(luat_libgnss_t));
-        if (gnsstmp == NULL) {
-            luat_heap_free(gnss);
-            LLOGW("out of memory for libgnss data parse");
-            return 0;
-        }
-        // gnss->lua_ref = luaL_ref(L, LUA_REGISTRYINDEX);
-        memset(gnss, 0, sizeof(luat_libgnss_t));
-        memset(gnsstmp, 0, sizeof(luat_libgnss_t));
+extern luat_libgnss_t *libgnss_gnss;
+// extern luat_libgnss_t *libgnss_gnsstmp;
+extern char* libgnss_recvbuff;
+
+void luat_uart_set_app_recv(int id, luat_uart_recv_callback_t cb);
+
+static inline void push_gnss_value(lua_State *L, struct minmea_float *f, int mode) {
+    if (f->value == 0) {
+        lua_pushinteger(L, 0);
+        return;
+    }
+    switch (mode)
+    {
+    case 0:
+        lua_pushinteger(L, minmea_tofloat(f));
+        break;
+    case 1:
+        lua_pushinteger(L, f->value);
+        break;
+    case 2:
+        lua_pushnumber(L, minmea_tocoord(f));
+        break;
+    default:
+        break;
     }
-    //lua_pushboolean(L, 1);
-    return 1;
 }
 
-static int parse_nmea(const char* line) {
-    // $GNRMC,080313.00,A,2324.40756,N,11313.86184,E,0.284,,010720,,,A*68
-    //if (gnss != NULL && gnss->debug)
-    //    LLOGD("GNSS [%s]", line);
-    if (gnss == NULL && !luat_libgnss_init()) {
+static int luat_libgnss_state_handler(lua_State *L, void* ptr) {
+    (void)ptr;
+    rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
+    lua_getglobal(L, "sys_pub");
+    if (!lua_isfunction(L, -1)) {
         return 0;
     }
-    struct minmea_sentence_gsv frame_gsv = {0};
+/*
+@sys_pub libgnss
+GNSS状态变化
+GNSS_STATE
+@usage
+sys.subscribe("GNSS_STATE", function(event, ticks)
+    -- event取值有 
+    -- FIXED 定位成功
+    -- LOSE  定位丢失
+    -- ticks是事件发生的时间,一般可以忽略
+    log.info("gnss", "state", event, ticks)
+end)
+*/
+    lua_pushliteral(L, "GNSS_STATE");
+    switch (msg->arg1)
+    {
+    case GNSS_STATE_FIXED:
+        lua_pushliteral(L, "FIXED");
+        break;
+    case GNSS_STATE_LOSE:
+        lua_pushliteral(L, "LOSE");
+        break;
+    case GNSS_STATE_OPEN:
+        lua_pushliteral(L, "OPEN");
+        break;
+    case GNSS_STATE_CLOSE:
+        lua_pushliteral(L, "CLOSE");
+        break;
+    default:
+        return 0;
+    }
+    lua_pushinteger(L, msg->arg2);
+    lua_call(L, 3, 0);
+    return 0;
+}
 
-    switch (minmea_sentence_id(line, false)) {
-        case MINMEA_INVALID : {
-            LLOGD("bad line %s", line);
-            break;
-        }
-        case MINMEA_SENTENCE_RMC: {
-            if (minmea_parse_rmc(&(gnsstmp->frame_rmc), line)) {
-                if (gnsstmp->frame_rmc.valid) {
-                    memcpy(&(gnss->frame_rmc), &gnsstmp->frame_rmc, sizeof(struct minmea_sentence_rmc));
-                }
-                else {
-                    gnss->frame_rmc.valid = 0;
-                    if (gnsstmp->frame_rmc.date.year > 0) {
-                        memcpy(&(gnss->frame_rmc.date), &(gnsstmp->frame_rmc.date), sizeof(struct minmea_date));
-                    }
-                    if (gnsstmp->frame_rmc.time.hours > 0) {
-                        memcpy(&(gnss->frame_rmc.time), &(gnsstmp->frame_rmc.time), sizeof(struct minmea_time));
-                    }
-                }
-                //memcpy(&(gnss->frame_rmc), &frame_rmc, sizeof(struct minmea_sentence_rmc));
-                //LLOGD("RMC %s", line);
-                //LLOGD("RMC isFix(%d) Lat(%ld) Lng(%ld)", gnss->frame_rmc.valid, gnss->frame_rmc.latitude.value, gnss->frame_rmc.longitude.value);
-                // if (prev_gnss_fixed != gnss->frame_rmc.valid) {
-                //     lua_getglobal(L, "sys_pub");
-                //     if (lua_isfunction(L, -1)) {
-                //         lua_pushliteral(L, "GPS_STATE");
-                //         lua_pushstring(L, gnss->frame_rmc.valid ? "LOCATION_SUCCESS" : "LOCATION_FAIL");
-                //         lua_call(L, 2, 0);
-                //     }
-                //     else {
-                //         lua_pop(L, 1);
-                //     }
-                //     prev_gnss_fixed = gnss->frame_rmc.valid;
-                // }
-            }
-        } break;
+int luat_libgnss_state_onchanged(int state) {
+    rtos_msg_t msg = {0};
+    msg.handler = luat_libgnss_state_handler;
+    msg.arg1 = state;
+    #ifdef LUAT_USE_MCU
+    msg.arg2 = luat_mcu_ticks();
+    #endif
+    luat_msgbus_put(&msg, 0);
+    return 0;
+}
 
-        case MINMEA_SENTENCE_GGA: {
-            //struct minmea_sentence_gga frame_gga;
-            if (minmea_parse_gga(&gnsstmp->frame_gga, line)) {
-                memcpy(&(gnss->frame_gga), &gnsstmp->frame_gga, sizeof(struct minmea_sentence_gga));
-                //LLOGD("$GGA: fix quality: %d", frame_gga.fix_quality);
-            }
-        } break;
+static void put_datetime(lua_State*L, struct tm* rtime) {
+    lua_pushliteral(L, "year");
+    lua_pushinteger(L, rtime->tm_year);
+    lua_settable(L, -3);
 
-        case MINMEA_SENTENCE_GSA: {
-            if (minmea_parse_gsa(&(gnss->frame_gsa), line)) {
+    lua_pushliteral(L, "month");
+    lua_pushinteger(L, rtime->tm_mon + 1); // 比较纠结, 要不要兼容老的呢?
+    lua_settable(L, -3);
 
-            }
-        } break;
+    lua_pushliteral(L, "day");
+    lua_pushinteger(L, rtime->tm_mday);
+    lua_settable(L, -3);
 
-        case MINMEA_SENTENCE_GLL: {
-            if (minmea_parse_gll(&(gnss->frame_gll), line)) {
-                // memcpy(&(gnss->frame_gll), &frame_gll, sizeof(struct minmea_sentence_gsa));
-            }
-        } break;
-
-        // case MINMEA_SENTENCE_GST: {
-        //     if (minmea_parse_gst(&gnsstmp->frame_gst, line)) {
-        //         memcpy(&(gnss->frame_gst), &gnsstmp->frame_gst, sizeof(struct minmea_sentence_gst));
-        //     }
-        // } break;
-
-        case MINMEA_SENTENCE_GSV: {
-            //LLOGD("Got GSV : %s", line);
-            if (minmea_parse_gsv(&frame_gsv, line)) {
-                //LLOGD("$GSV: message %d of %d", frame_gsv.msg_nr, frame_gsv.total_msgs);
-                if (frame_gsv.msg_nr == 1) {
-                    //LLOGD("Clean GSV");
-                    memset(&(gnss->frame_gsv), 0, sizeof(struct minmea_sentence_gsv) * 3);
-                }
-                if (frame_gsv.msg_nr >= 1 && frame_gsv.msg_nr <= 3) {
-                    //LLOGD("memcpy GSV %d", frame_gsv.msg_nr);
-                    memcpy(&(gnss->frame_gsv[frame_gsv.msg_nr - 1]), &frame_gsv, sizeof(struct minmea_sentence_gsv));
-                }
-                // LLOGD("$GSV: message %d of %d", frame_gsv.msg_nr, frame_gsv.total_msgs);
-                // LLOGD("$GSV: sattelites in view: %d", frame_gsv.total_sats);
-                // for (int i = 0; i < 4; i++)
-                //     LLOGD("$GSV: sat nr %d, elevation: %d, azimuth: %d, snr: %d dbm",
-                //         frame_gsv.sats[i].nr,
-                //         frame_gsv.sats[i].elevation,
-                //         frame_gsv.sats[i].azimuth,
-                //         frame_gsv.sats[i].snr);
-            }
-            else {
-                //LLOGD("bad GSV %s", line);
-            }
-        } break;
-
-        case MINMEA_SENTENCE_VTG: {
-            //struct minmea_sentence_vtg frame_vtg;
-            if (minmea_parse_vtg(&(gnsstmp->frame_vtg), line)) {
-                memcpy(&(gnss->frame_vtg), &gnsstmp->frame_vtg, sizeof(struct minmea_sentence_vtg));
-                //--------------------------------------
-                // 暂时不发GPS_MSG_REPORT
-                // lua_getglobal(L, "sys_pub");
-                // if (lua_isfunction(L, -1)) {
-                //     lua_pushstring(L, "GPS_MSG_REPORT");
-                //     lua_call(L, 1, 0);
-                // }
-                // else {
-                //     lua_pop(L, 1);
-                // }
-                //--------------------------------------
-            }
-        } break;
-        case MINMEA_SENTENCE_ZDA: {
-            if (minmea_parse_zda(&(gnsstmp->frame_zda), line)) {
-                memcpy(&(gnss->frame_zda), &gnsstmp->frame_zda, sizeof(struct minmea_sentence_zda));
-            }
-        } break;
-        default:
-            //LLOGD("why happen");
-            break;
-    }
-    return 0;
+    lua_pushliteral(L, "hour");
+    lua_pushinteger(L, rtime->tm_hour);
+    lua_settable(L, -3);
+
+    lua_pushliteral(L, "min");
+    lua_pushinteger(L, rtime->tm_min);
+    lua_settable(L, -3);
+
+    lua_pushliteral(L, "sec");
+    lua_pushinteger(L, rtime->tm_sec);
+    lua_settable(L, -3);
 }
 
 /**
@@ -188,35 +158,16 @@ static int parse_nmea(const char* line) {
 @api libgnss.parse(str)
 @string 原始nmea数据
 @usage
--- 解析nmea
+-- 解析nmea数据
 libgnss.parse(indata)
 log.info("nmea", json.encode(libgnss.getRmc()))
  */
 static int l_libgnss_parse(lua_State *L) {
     size_t len = 0;
     const char* str = luaL_checklstring(L, 1, &len);
-    if (len == 0) {
-        return 0;
-    }
-    // TODO 处理粘包,分包的情况
-    char buff[85] = {0}; // nmea 最大长度82,含换行符
-    char *ptr = (char*)str;
-    size_t prev = 0;
-    for (size_t i = 1; i < len; i++)
-    {
-        if (*(ptr + i) == 0x0A) {
-            if (i - prev > 10 && i - prev < 82) {
-                memcpy(buff, ptr + prev, i - prev - 1);
-                if (buff[0] == '$') {
-                    buff[i - prev - 1] = 0; // 确保结束符存在
-                    parse_nmea((const char*)buff);
-                }
-            }
-            i ++;
-            prev = i;
-        }
+    if (len > 0) {
+        luat_libgnss_parse_data(str, len);
     }
-
     return 0;
 }
 
@@ -230,19 +181,19 @@ libgnss.parse(indata)
 log.info("nmea", "isFix", libgnss.isFix())
  */
 static int l_libgnss_is_fix(lua_State *L) {
-    if (gnss == NULL) {
+    if (libgnss_gnss == NULL) {
         lua_pushboolean(L, 0);
     }
     else
-        lua_pushboolean(L, gnss->frame_rmc.valid != 0 ? 1 : 0);
+        lua_pushboolean(L, libgnss_gnss->frame_rmc.valid != 0 ? 1 : 0);
     return 1;
 }
 
 /**
 获取位置信息
 @api libgnss.getIntLocation()
-@return int lat数据, 格式为 ddmmmmmmm
-@return int lng数据, 格式为 ddmmmmmmm
+@return int lat数据, 格式为 ddddddddd
+@return int lng数据, 格式为 ddddddddd
 @return int speed数据
 @usage
 -- 解析nmea
@@ -250,10 +201,10 @@ libgnss.parse(indata)
 log.info("nmea", "loc", libgnss.getIntLocation())
  */
 static int l_libgnss_get_int_location(lua_State *L) {
-    if (gnss != NULL && gnss->frame_rmc.valid) {
-        lua_pushinteger(L, minmea_tofloat(&(gnss->frame_rmc.latitude)));
-        lua_pushinteger(L, minmea_tofloat(&(gnss->frame_rmc.longitude)));
-        lua_pushinteger(L, gnss->frame_rmc.speed.value);
+    if (libgnss_gnss != NULL && libgnss_gnss->frame_rmc.valid) {
+        lua_pushinteger(L, libgnss_gnss->frame_rmc.latitude.value);
+        lua_pushinteger(L, libgnss_gnss->frame_rmc.longitude.value);
+        lua_pushinteger(L, libgnss_gnss->frame_rmc.speed.value);
     } else {
         lua_pushinteger(L, 0);
         lua_pushinteger(L, 0);
@@ -264,7 +215,8 @@ static int l_libgnss_get_int_location(lua_State *L) {
 
 /**
 获取原始RMC位置信息
-@api libgnss.getRmc()
+@api libgnss.getRmc(data_mode)
+@int 坐标类数据的格式, 0-DDMM.MMM格式, 1-DDDDDDD格式, 2-DD.DDDDD格式
 @return table 原始rmc数据
 @usage
 -- 解析nmea
@@ -272,61 +224,91 @@ libgnss.parse(indata)
 log.info("nmea", "rmc", json.encode(libgnss.getRmc()))
  */
 static int l_libgnss_get_rmc(lua_State *L) {
+    int mode = luaL_optinteger(L, 1, 0);
+    lua_settop(L, 0);
     lua_createtable(L, 0, 12);
 
-    if (gnss != NULL) {
+    struct tm rtime = {0};
 
-        lua_pushliteral(L, "valid");
-        lua_pushboolean(L, gnss->frame_rmc.valid);
-        lua_settable(L, -3);
+    if (libgnss_gnss != NULL) {
+        lua_pushboolean(L, libgnss_gnss->frame_rmc.valid);
+        lua_setfield(L, -2, "valid");
 
-        lua_pushliteral(L, "lat");
-        lua_pushnumber(L, minmea_tofloat(&(gnss->frame_rmc.latitude)));
-        lua_settable(L, -3);
+        if (libgnss_gnss->frame_rmc.valid) {
+            push_gnss_value(L, &(libgnss_gnss->frame_rmc.latitude), mode);
+        }
+        else
+            lua_pushinteger(L, 0);
+        lua_setfield(L, -2, "lat");
 
-        lua_pushliteral(L, "lng");
-        lua_pushnumber(L, minmea_tofloat(&(gnss->frame_rmc.longitude)));
-        lua_settable(L, -3);
+        if (libgnss_gnss->frame_rmc.valid) {
+            push_gnss_value(L, &(libgnss_gnss->frame_rmc.longitude), mode);
+        }
+        else
+            lua_pushinteger(L, 0);
+        lua_setfield(L, -2, "lng");
 
-        lua_pushliteral(L, "speed");
-        lua_pushinteger(L, gnss->frame_rmc.speed.value);
-        lua_settable(L, -3);
+        if (libgnss_gnss->frame_rmc.valid) {
+            push_gnss_value(L, &(libgnss_gnss->frame_rmc.speed), mode);
+        }
+        else
+            lua_pushinteger(L, 0);
+        lua_setfield(L, -2, "speed");
 
-        lua_pushliteral(L, "course");
-        lua_pushinteger(L, gnss->frame_rmc.course.value);
-        lua_settable(L, -3);
+        if (libgnss_gnss->frame_rmc.valid) {
+            push_gnss_value(L, &(libgnss_gnss->frame_rmc.course), mode);
+        }
+        else
+            lua_pushinteger(L, 0);
+        lua_setfield(L, -2, "course");
 
+        if (libgnss_gnss->frame_rmc.valid) {
+            push_gnss_value(L, &(libgnss_gnss->frame_rmc.variation), mode);
+        }
+        else
+            lua_pushinteger(L, 0);
+        lua_setfield(L, -2, "variation");
 
-        lua_pushliteral(L, "variation");
-        lua_pushinteger(L, gnss->frame_rmc.variation.value);
-        lua_settable(L, -3);
+        // 时间类
+        minmea_getdatetime(&rtime, &libgnss_gnss->frame_rmc.date, &libgnss_gnss->frame_rmc.time);
+        put_datetime(L, &rtime);
+    }
 
-        lua_pushliteral(L, "year");
-        lua_pushinteger(L, gnss->frame_rmc.date.year + 2000);
-        lua_settable(L, -3);
+    return 1;
+}
 
-        lua_pushliteral(L, "month");
-        lua_pushinteger(L, gnss->frame_rmc.date.month);
-        lua_settable(L, -3);
+static void add_gsv(lua_State*L, struct minmea_sentence_gsv* gsvs, size_t *count) {
 
-        lua_pushliteral(L, "day");
-        lua_pushinteger(L, gnss->frame_rmc.date.day);
-        lua_settable(L, -3);
+    for (size_t i = 0; i < 3; i++)
+    {
+        for (size_t j = 0; j < 4; j++)
+        {
+            //LLOGD("nr %d snr %d", gnss->frame_gsv[i].sats[j].nr, gnss->frame_gsv[i].sats[j].snr);
+            if (gsvs[i].sats[j].nr) {
+                lua_pushinteger(L, *count);
+                lua_createtable(L, 0, 4);
 
-        lua_pushliteral(L, "hour");
-        lua_pushinteger(L, gnss->frame_rmc.time.hours);
-        lua_settable(L, -3);
+                lua_pushliteral(L, "nr");
+                lua_pushinteger(L, gsvs[i].sats[j].nr);
+                lua_settable(L, -3);
 
-        lua_pushliteral(L, "min");
-        lua_pushinteger(L, gnss->frame_rmc.time.minutes);
-        lua_settable(L, -3);
+                lua_pushliteral(L, "snr");
+                lua_pushinteger(L, gsvs[i].sats[j].snr);
+                lua_settable(L, -3);
 
-        lua_pushliteral(L, "sec");
-        lua_pushinteger(L, gnss->frame_rmc.time.seconds);
-        lua_settable(L, -3);
-    }
+                lua_pushliteral(L, "elevation");
+                lua_pushinteger(L, gsvs[i].sats[j].elevation);
+                lua_settable(L, -3);
 
-    return 1;
+                lua_pushliteral(L, "azimuth");
+                lua_pushinteger(L, gsvs[i].sats[j].azimuth);
+                lua_settable(L, -3);
+
+                lua_settable(L, -3);
+                *count = *count + 1;
+            }
+        }
+    }
 }
 
 /**
@@ -340,46 +322,29 @@ log.info("nmea", "gsv", json.encode(libgnss.getGsv()))
  */
 static int l_libgnss_get_gsv(lua_State *L) {
     lua_createtable(L, 0, 2);
+    if (libgnss_gnss == NULL)
+        return 1;
 
-    if (gnss != NULL) {
-        int count = 1;
-        lua_pushliteral(L, "total_sats");
-        lua_pushinteger(L, gnss->frame_gsv[0].total_sats);
-        lua_settable(L, -3);
+    size_t count = 1;
 
-        lua_pushliteral(L, "sats");
-        lua_createtable(L, 12, 0);
-        for (size_t i = 0; i < 3; i++)
-        {
-            for (size_t j = 0; j < 4; j++)
-            {
-                //LLOGD("nr %d snr %d", gnss->frame_gsv[i].sats[j].nr, gnss->frame_gsv[i].sats[j].snr);
-                if (gnss->frame_gsv[i].sats[j].nr) {
-                    lua_pushinteger(L, count++);
-                    lua_createtable(L, 0, 4);
-
-                    lua_pushliteral(L, "nr");
-                    lua_pushinteger(L, gnss->frame_gsv[i].sats[j].nr);
-                    lua_settable(L, -3);
-
-                    lua_pushliteral(L, "snr");
-                    lua_pushinteger(L, gnss->frame_gsv[i].sats[j].snr);
-                    lua_settable(L, -3);
-
-                    lua_pushliteral(L, "elevation");
-                    lua_pushinteger(L, gnss->frame_gsv[i].sats[j].elevation);
-                    lua_settable(L, -3);
-
-                    lua_pushliteral(L, "azimuth");
-                    lua_pushinteger(L, gnss->frame_gsv[i].sats[j].azimuth);
-                    lua_settable(L, -3);
-
-                    lua_settable(L, -3);
-                }
-            }
-        }
-        lua_settable(L, -3);
+    lua_createtable(L, 12, 0);
+    if (libgnss_gnss->frame_gsv_gp->total_sats > 0) {
+        add_gsv(L, libgnss_gnss->frame_gsv_gp, &count);
+    }
+    if (libgnss_gnss->frame_gsv_gb->total_sats > 0) {
+        add_gsv(L, libgnss_gnss->frame_gsv_gb, &count);
     }
+    if (libgnss_gnss->frame_gsv_gl->total_sats > 0) {
+        add_gsv(L, libgnss_gnss->frame_gsv_gl, &count);
+    }
+    if (libgnss_gnss->frame_gsv_ga->total_sats > 0) {
+        add_gsv(L, libgnss_gnss->frame_gsv_ga, &count);
+    }
+    lua_setfield(L, -2, "sats");
+
+    lua_pushliteral(L, "total_sats");
+    lua_pushinteger(L, count - 1);
+    lua_settable(L, -3);
 
     return 1;
 }
@@ -387,7 +352,8 @@ static int l_libgnss_get_gsv(lua_State *L) {
 
 /**
 获取原始GSA信息
-@api libgnss.getGsa()
+@api libgnss.getGsa(data_mode)
+@int 坐标类数据的格式, 0-DDMM.MMM格式, 1-DDDDDDD格式, 2-DD.DDDDD格式
 @return table 原始GSA数据
 @usage
 -- 解析nmea
@@ -395,6 +361,10 @@ libgnss.parse(indata)
 log.info("nmea", "gsa", json.encode(libgnss.getGsa()))
  */
 static int l_libgnss_get_gsa(lua_State *L) {
+    int mode = luaL_optinteger(L, 1, 0);
+    lua_settop(L, 0);
+    if (libgnss_gnss == NULL)
+        return 0;
     lua_createtable(L, 0, 10);
 
     //lua_pushliteral(L, "mode");
@@ -402,29 +372,32 @@ static int l_libgnss_get_gsa(lua_State *L) {
     //lua_settable(L, -3);
 
     lua_pushliteral(L, "fix_type");
-    lua_pushinteger(L, gnss ? gnss->frame_gsa.fix_type : 0);
+    lua_pushinteger(L, libgnss_gnss->frame_gsa[0].fix_type);
     lua_settable(L, -3);
 
     lua_pushliteral(L, "pdop");
-    lua_pushinteger(L, gnss ? gnss->frame_gsa.pdop.value : 0);
+    push_gnss_value(L, &(libgnss_gnss->frame_gsa[0].pdop), mode);
     lua_settable(L, -3);
 
     lua_pushliteral(L, "hdop");
-    lua_pushinteger(L, gnss ? gnss->frame_gsa.hdop.value : 0);
+    push_gnss_value(L, &(libgnss_gnss->frame_gsa[0].hdop), mode);
     lua_settable(L, -3);
 
     lua_pushliteral(L, "vdop");
-    lua_pushinteger(L, gnss ? gnss->frame_gsa.vdop.value : 0);
+    push_gnss_value(L, &(libgnss_gnss->frame_gsa[0].vdop), mode);
     lua_settable(L, -3);
 
     lua_pushliteral(L, "sats");
     lua_createtable(L, 12, 0);
-    if (gnss != NULL) {
-        for (size_t i = 0; i < 12; i++) {
-            if (gnss->frame_gsa.sats[i] == 0) break;
-            lua_pushinteger(L, i + 1);
-            lua_pushinteger(L, gnss->frame_gsa.sats[i]);
-            lua_settable(L, -3);
+    size_t pos = 1;
+    for (size_t i = 0; i < 12; i++) {
+        for (size_t j = 0; j < 3; j++)
+        {
+            if (libgnss_gnss->frame_gsa[j].sats[i] == 0)
+                continue;
+            lua_pushinteger(L, libgnss_gnss->frame_gsa[j].sats[i]);
+            lua_seti(L, -2, pos);
+            pos ++;
         }
     }
 
@@ -436,7 +409,8 @@ static int l_libgnss_get_gsa(lua_State *L) {
 
 /**
 获取原始VTA位置信息
-@api libgnss.getVtg()
+@api libgnss.getVtg(data_mode)
+@int 坐标类数据的格式, 0-DDMM.MMM格式, 1-DDDDDDD格式, 2-DD.DDDDD格式
 @return table 原始VTA数据
 @usage
 -- 解析nmea
@@ -444,26 +418,30 @@ libgnss.parse(indata)
 log.info("nmea", "vtg", json.encode(libgnss.getVtg()))
  */
 static int l_libgnss_get_vtg(lua_State *L) {
+    int mode = luaL_optinteger(L, 1, 0);
+    lua_settop(L, 0);
+    if (libgnss_gnss == NULL)
+        return 0;
     lua_createtable(L, 0, 10);
 
-    //lua_pushliteral(L, "faa_mode");
-    //lua_pushlstring(L, gnss ? &(gnss->frame_vtg.faa_mode) : 'N', 1);
-    //lua_settable(L, -3);
+    // lua_pushliteral(L, "faa_mode");
+    // lua_pushlstring(L, libgnss_gnss->frame_vtg.faa_mode, 1);
+    // lua_settable(L, -3);
 
     lua_pushliteral(L, "true_track_degrees");
-    lua_pushinteger(L, gnss ? gnss->frame_vtg.true_track_degrees.value : 0);
+    push_gnss_value(L, &(libgnss_gnss->frame_vtg.true_track_degrees), mode);
     lua_settable(L, -3);
 
     lua_pushliteral(L, "magnetic_track_degrees");
-    lua_pushinteger(L, gnss ? gnss->frame_vtg.magnetic_track_degrees.value : 0);
+    push_gnss_value(L, &(libgnss_gnss->frame_vtg.magnetic_track_degrees), mode);
     lua_settable(L, -3);
 
     lua_pushliteral(L, "speed_knots");
-    lua_pushinteger(L, gnss ? gnss->frame_vtg.speed_knots.value : 0);
+    push_gnss_value(L, &(libgnss_gnss->frame_vtg.speed_knots), mode);
     lua_settable(L, -3);
 
     lua_pushliteral(L, "speed_kph");
-    lua_pushinteger(L, gnss ? gnss->frame_vtg.speed_kph.value : 0);
+    push_gnss_value(L, &(libgnss_gnss->frame_vtg.speed_kph), mode);
     lua_settable(L, -3);
 
     return 1;
@@ -480,100 +458,148 @@ log.info("nmea", "zda", json.encode(libgnss.getZda()))
  */
 static int l_libgnss_get_zda(lua_State *L) {
     lua_createtable(L, 0, 9);
-
-    if (gnss != NULL) {
+    struct tm rtime = {0};
+    if (libgnss_gnss != NULL) {
 
         lua_pushliteral(L, "hour_offset");
-        lua_pushinteger(L, gnss->frame_zda.hour_offset);
+        lua_pushinteger(L, libgnss_gnss->frame_zda.hour_offset);
         lua_settable(L, -3);
 
         lua_pushliteral(L, "minute_offset");
-        lua_pushinteger(L, gnss->frame_zda.minute_offset);
-        lua_settable(L, -3);
-
-        lua_pushliteral(L, "year");
-        lua_pushinteger(L, gnss->frame_zda.date.year);
-        lua_settable(L, -3);
-
-        lua_pushliteral(L, "month");
-        lua_pushinteger(L, gnss->frame_zda.date.month);
-        lua_settable(L, -3);
-
-        lua_pushliteral(L, "day");
-        lua_pushinteger(L, gnss->frame_zda.date.day);
-        lua_settable(L, -3);
-
-        lua_pushliteral(L, "hour");
-        lua_pushinteger(L, gnss->frame_zda.time.hours);
-        lua_settable(L, -3);
-
-        lua_pushliteral(L, "min");
-        lua_pushinteger(L, gnss->frame_zda.time.minutes);
+        lua_pushinteger(L, libgnss_gnss->frame_zda.minute_offset);
         lua_settable(L, -3);
 
-        lua_pushliteral(L, "sec");
-        lua_pushinteger(L, gnss->frame_zda.time.seconds);
-        lua_settable(L, -3);
-
-        lua_pushliteral(L, "mic");
-        lua_pushinteger(L, gnss->frame_zda.time.microseconds);
-        lua_settable(L, -3);
+        // 时间相关
+        minmea_getdatetime(&rtime, &libgnss_gnss->frame_zda.date, &libgnss_gnss->frame_zda.time);
+        put_datetime(L, &rtime);
     }
 
     return 1;
 }
 
+/**
+设置调试模式
+@api libgnss.debug(mode)
+@bool true开启调试,false关闭调试,默认为false
+@usage
+-- 开启调试
+libgnss.debug(true)
+-- 关闭调试
+libgnss.debug(false)
+ */
 static int l_libgnss_debug(lua_State *L) {
-    if (gnss == NULL && luat_libgnss_init()) {
+    if (libgnss_gnss == NULL && luat_libgnss_init()) {
         return 0;
     }
     if (lua_isboolean(L, 1) && lua_toboolean(L, 1)) {
-        gnss->debug = 1;
+        LLOGD("Debug ON");
+        libgnss_gnss->debug = 1;
     }
     else
     {
-        gnss->debug = 0;
+        LLOGD("Debug OFF");
+        libgnss_gnss->debug = 0;
     }
 
     return 0;
 }
 
-
+/*
+获取GGA数据
+@api libgnss.getGga(data_mode)
+@int 坐标类数据的格式, 0-DDMM.MMM格式, 1-DDDDDDD格式, 2-DD.DDDDD格式
+@return table GGA数据, 若如不存在会返回nil
+local gga = libgnss.getGga()
+if gga then
+    log.info("GGA", json.encode(gga))
+end
+*/
 static int l_libgnss_get_gga(lua_State* L) {
-    if (gnss == NULL)
+    int mode = luaL_optinteger(L, 1, 0);
+    lua_settop(L, 0);
+    if (libgnss_gnss == NULL)
         return 0;
     lua_newtable(L);
 
     lua_pushstring(L, "altitude");
-    lua_pushinteger(L, gnss->frame_gga.altitude.value);
+    push_gnss_value(L, &(libgnss_gnss->frame_gga.altitude), mode);
     lua_settable(L, -3);
 
     lua_pushstring(L, "latitude");
-    lua_pushinteger(L, gnss->frame_gga.latitude.value);
+    push_gnss_value(L, &(libgnss_gnss->frame_gga.latitude), mode);
     lua_settable(L, -3);
 
     lua_pushstring(L, "longitude");
-    lua_pushinteger(L, gnss->frame_gga.longitude.value);
+    push_gnss_value(L, &(libgnss_gnss->frame_gga.longitude), mode);
     lua_settable(L, -3);
 
     lua_pushstring(L, "fix_quality");
-    lua_pushinteger(L, gnss->frame_gga.fix_quality);
+    lua_pushinteger(L, libgnss_gnss->frame_gga.fix_quality);
     lua_settable(L, -3);
 
     lua_pushstring(L, "satellites_tracked");
-    lua_pushinteger(L, gnss->frame_gga.satellites_tracked);
+    lua_pushinteger(L, libgnss_gnss->frame_gga.satellites_tracked);
     lua_settable(L, -3);
 
     lua_pushstring(L, "hdop");
-    lua_pushinteger(L, gnss->frame_gga.hdop.value);
+    push_gnss_value(L, &(libgnss_gnss->frame_gga.hdop), mode);
     lua_settable(L, -3);
 
     lua_pushstring(L, "height");
-    lua_pushinteger(L, gnss->frame_gga.height.value);
+    push_gnss_value(L, &(libgnss_gnss->frame_gga.height), mode);
     lua_settable(L, -3);
 
     lua_pushstring(L, "dgps_age");
-    lua_pushinteger(L, gnss->frame_gga.dgps_age.value);
+    push_gnss_value(L, &(libgnss_gnss->frame_gga.dgps_age), mode);
+    lua_settable(L, -3);
+
+    return 1;
+}
+
+/*
+获取GLL数据
+@api libgnss.getGll(data_mode)
+@int 坐标类数据的格式, 0-DDMM.MMM格式, 1-DDDDDDD格式, 2-DD.DDDDD格式
+@return table GLL数据, 若如不存在会返回nil
+local gll = libgnss.getGll()
+if gll then
+    log.info("GLL", json.encode(gll))
+end
+*/
+static int l_libgnss_get_gll(lua_State* L) {
+    int mode = luaL_optinteger(L, 1, 0);
+    lua_settop(L, 0);
+    if (libgnss_gnss == NULL)
+        return 0;
+    lua_newtable(L);
+
+    lua_pushstring(L, "latitude");
+    push_gnss_value(L, &(libgnss_gnss->frame_gll.latitude), mode);
+    lua_settable(L, -3);
+
+    lua_pushstring(L, "longitude");
+    push_gnss_value(L, &(libgnss_gnss->frame_gll.longitude), mode);
+    lua_settable(L, -3);
+
+    lua_pushstring(L, "mode");
+    lua_pushfstring(L, "%c", libgnss_gnss->frame_gll.mode);
+    lua_settable(L, -3);
+
+    lua_pushstring(L, "status");
+    lua_pushfstring(L, "%c", libgnss_gnss->frame_gll.status);
+    lua_settable(L, -3);
+
+    lua_pushstring(L, "hour");
+    lua_pushinteger(L, libgnss_gnss->frame_gll.time.hours);
+    lua_settable(L, -3);
+    lua_pushstring(L, "us");
+    lua_pushinteger(L, libgnss_gnss->frame_gll.time.microseconds);
+    lua_settable(L, -3);
+    lua_pushstring(L, "min");
+    lua_pushinteger(L, libgnss_gnss->frame_gll.time.minutes);
+    lua_settable(L, -3);
+    lua_pushstring(L, "sec");
+    lua_pushinteger(L, libgnss_gnss->frame_gll.time.seconds);
     lua_settable(L, -3);
 
     return 1;
@@ -585,9 +611,73 @@ static int l_libgnss_get_gga(lua_State* L) {
 @return nil 无返回值
  */
 static int l_libgnss_clear(lua_State*L) {
-    if (gnss == NULL && !luat_libgnss_init())
+    (void)L;
+    if (libgnss_gnss == NULL && !luat_libgnss_init())
+        return 0;
+    memset(libgnss_gnss, 0, sizeof(luat_libgnss_t));
+    return 0;
+}
+
+/*
+绑定uart端口进行GNSS数据读取
+@api libgnss.bind(id)
+@int uart端口号
+@usage
+-- 配置串口信息, 通常为 115200 8N1
+uart.setup(2, 115200)
+-- 绑定uart, 马上开始解析GNSS数据
+libgnss.bind(2)
+-- 无需再调用uart.on然后调用libgnss.parse
+-- 开发期可打开调试日志
+libgnss.debug(true)
+*/
+static int l_libgnss_bind(lua_State* L) {
+    int uart_id = luaL_checkinteger(L, 1);
+    l_libgnss_clear(L);
+    if (libgnss_recvbuff == NULL) {
+        libgnss_recvbuff = luat_heap_malloc(RECV_BUFF_SIZE);
+    }
+    if (luat_uart_exist(uart_id)) {
+        //uart_app_recvs[uart_id] = nmea_uart_recv_cb;
+        luat_uart_set_app_recv(uart_id, luat_libgnss_uart_recv_cb);
+    }
+    return 0;
+}
+
+static int l_libgnss_locStr(lua_State *L) {
+    int mode = luaL_optinteger(L, 1, 0);
+    char buff[64] = {0};
+    float lat_f = minmea_tofloat(&libgnss_gnss->frame_rmc.latitude);
+    float lng_f = minmea_tofloat(&libgnss_gnss->frame_rmc.longitude);
+    switch (mode)
+    {
+    case 0:
+        snprintf_(buff, 63, "%.7g,%c,%.7g,%c,%.7g", 
+                            fabs(lat_f), lat_f > 0 ? 'N' : 'S', 
+                            fabs(lng_f), lng_f > 0 ? 'E' : 'W',
+                            libgnss_gnss->frame_gga.height.value == 0 ? 1.0 : minmea_tofloat(&libgnss_gnss->frame_gga.height));
+        break;
+    case 1:
+        snprintf_(buff, 63, "%d,%d", libgnss_gnss->frame_rmc.latitude.value, libgnss_gnss->frame_rmc.longitude.value);
+        break;
+    default:
+        break;
+    }
+    lua_pushstring(L, buff);
+    return 1;
+}
+
+static int l_libgnss_rtc_auto(lua_State *L) {
+    if (libgnss_gnss == NULL)
         return 0;
-    memset(gnss, 0, sizeof(luat_libgnss_t));
+    if (lua_isboolean(L, 1) && lua_toboolean(L, 1)) {
+        libgnss_gnss->rtc_auto = 1;
+        LLOGD("GNSS->RTC Auto-Set now is ON");
+    }
+    else {
+        libgnss_gnss->rtc_auto = 0;
+        LLOGD("GNSS->RTC Auto-Set now is OFF");
+    }
     return 0;
 }
 
@@ -602,9 +692,14 @@ static const rotable_Reg_t reg_libgnss[] =
     { "getGsa", ROREG_FUNC(l_libgnss_get_gsa)},
     { "getVtg", ROREG_FUNC(l_libgnss_get_vtg)},
     { "getGga", ROREG_FUNC(l_libgnss_get_gga)},
+    { "getGll", ROREG_FUNC(l_libgnss_get_gll)},
     { "getZda", ROREG_FUNC(l_libgnss_get_zda)},
+    { "locStr", ROREG_FUNC(l_libgnss_locStr)},
+    { "rtcAuto",ROREG_FUNC(l_libgnss_rtc_auto)},
+    
     { "debug",  ROREG_FUNC(l_libgnss_debug)},
     { "clear",  ROREG_FUNC(l_libgnss_clear)},
+    { "bind",   ROREG_FUNC(l_libgnss_bind)},
 
 	{ NULL,      ROREG_INT(0)}
 };

+ 67 - 33
components/minmea/minmea.c

@@ -10,9 +10,7 @@
 
 #include <stdlib.h>
 #include <string.h>
-#include <ctype.h>
 #include <stdarg.h>
-#include <time.h>
 
 #define boolstr(s) ((s) ? "true" : "false")
 
@@ -46,10 +44,6 @@ bool minmea_check(const char *sentence, bool strict)
 {
     uint8_t checksum = 0x00;
 
-    // Sequence length is limited.
-    if (strlen(sentence) > MINMEA_MAX_LENGTH + 3)
-        return false;
-
     // A valid sentence starts with "$".
     if (*sentence++ != '$')
         return false;
@@ -79,20 +73,25 @@ bool minmea_check(const char *sentence, bool strict)
     }
 
     // The only stuff allowed at this point is a newline.
-    if (*sentence && strcmp(sentence, "\n") && strcmp(sentence, "\r\n"))
+    while (*sentence == '\r' || *sentence == '\n') {
+        sentence++;
+    }
+    
+    if (*sentence) {
         return false;
+    }
 
     return true;
 }
 
-static inline bool minmea_isfield(char c) {
-    return isprint((unsigned char) c) && c != ',' && c != '*';
-}
-
 bool minmea_scan(const char *sentence, const char *format, ...)
 {
     bool result = false;
     bool optional = false;
+
+    if (sentence == NULL)
+        return false;
+
     va_list ap;
     va_start(ap, format);
 
@@ -358,18 +357,20 @@ enum minmea_sentence_id minmea_sentence_id(const char *sentence, bool strict)
     if (!minmea_scan(sentence, "t", type))
         return MINMEA_INVALID;
 
-    if (!strcmp(type+2, "RMC"))
-        return MINMEA_SENTENCE_RMC;
+    if (!strcmp(type+2, "GBS"))
+        return MINMEA_SENTENCE_GBS;
     if (!strcmp(type+2, "GGA"))
         return MINMEA_SENTENCE_GGA;
-    if (!strcmp(type+2, "GSA"))
-        return MINMEA_SENTENCE_GSA;
     if (!strcmp(type+2, "GLL"))
         return MINMEA_SENTENCE_GLL;
+    if (!strcmp(type+2, "GSA"))
+        return MINMEA_SENTENCE_GSA;
     if (!strcmp(type+2, "GST"))
         return MINMEA_SENTENCE_GST;
     if (!strcmp(type+2, "GSV"))
         return MINMEA_SENTENCE_GSV;
+    if (!strcmp(type+2, "RMC"))
+        return MINMEA_SENTENCE_RMC;
     if (!strcmp(type+2, "VTG"))
         return MINMEA_SENTENCE_VTG;
     if (!strcmp(type+2, "ZDA"))
@@ -378,6 +379,28 @@ enum minmea_sentence_id minmea_sentence_id(const char *sentence, bool strict)
     return MINMEA_UNKNOWN;
 }
 
+bool minmea_parse_gbs(struct minmea_sentence_gbs *frame, const char *sentence)
+{
+    // $GNGBS,170556.00,3.0,2.9,8.3,,,,*5C
+    char type[6];
+    if (!minmea_scan(sentence, "tTfffifff",
+            type,
+            &frame->time,
+            &frame->err_latitude,
+            &frame->err_longitude,
+            &frame->err_altitude,
+            &frame->svid,
+            &frame->prob,
+            &frame->bias,
+            &frame->stddev
+            ))
+        return false;
+    if (strcmp(type+2, "GBS"))
+        return false;
+
+    return true;
+}
+
 bool minmea_parse_rmc(struct minmea_sentence_rmc *frame, const char *sentence)
 {
     // $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62
@@ -561,7 +584,7 @@ bool minmea_parse_vtg(struct minmea_sentence_vtg *frame, const char *sentence)
     char type[6];
     char c_true, c_magnetic, c_knots, c_kph, c_faa_mode;
 
-    if (!minmea_scan(sentence, "tfcfcfcfc;c",
+    if (!minmea_scan(sentence, "t;fcfcfcfcc",
             type,
             &frame->true_track_degrees,
             &c_true,
@@ -575,12 +598,15 @@ bool minmea_parse_vtg(struct minmea_sentence_vtg *frame, const char *sentence)
         return false;
     if (strcmp(type+2, "VTG"))
         return false;
-    // check chars
-    if (c_true != 'T' ||
-        c_magnetic != 'M' ||
-        c_knots != 'N' ||
-        c_kph != 'K')
-        return false;
+    // values are only valid with the accompanying characters
+    if (c_true != 'T')
+        frame->true_track_degrees.scale = 0;
+    if (c_magnetic != 'M')
+        frame->magnetic_track_degrees.scale = 0;
+    if (c_knots != 'N')
+        frame->speed_knots.scale = 0;
+    if (c_kph != 'K')
+        frame->speed_kph.scale = 0;
     frame->faa_mode = (enum minmea_faa_mode)c_faa_mode;
 
     return true;
@@ -612,25 +638,33 @@ bool minmea_parse_zda(struct minmea_sentence_zda *frame, const char *sentence)
   return true;
 }
 
-int minmea_gettime(struct timespec *ts, const struct minmea_date *date, const struct minmea_time *time_)
+int minmea_getdatetime(struct tm *tm, const struct minmea_date *date, const struct minmea_time *time_)
 {
     if (date->year == -1 || time_->hours == -1)
         return -1;
 
-    struct tm tm;
-    memset(&tm, 0, sizeof(tm));
+    memset(tm, 0, sizeof(*tm));
     if (date->year < 80) {
-        tm.tm_year = 2000 + date->year - 1900;  // 2000-2079
+        tm->tm_year = 2000 + date->year; // 2000-2079
     } else if (date->year >= 1900) {
-        tm.tm_year = date->year - 1900; // 4 digit year, use directly
+        tm->tm_year = date->year;        // 4 digit year, use directly
     } else {
-        tm.tm_year = date->year;    // 1980-1999
+        tm->tm_year = date->year + 1900;               // 1980-1999
     }
-    tm.tm_mon = date->month - 1;
-    tm.tm_mday = date->day;
-    tm.tm_hour = time_->hours;
-    tm.tm_min = time_->minutes;
-    tm.tm_sec = time_->seconds;
+    tm->tm_mon = date->month - 1;
+    tm->tm_mday = date->day;
+    tm->tm_hour = time_->hours;
+    tm->tm_min = time_->minutes;
+    tm->tm_sec = time_->seconds;
+
+    return 0;
+}
+
+int minmea_gettime(struct timespec *ts, const struct minmea_date *date, const struct minmea_time *time_)
+{
+    struct tm tm;
+    if (minmea_getdatetime(&tm, date, time_))
+        return -1;
 
     // time_t timestamp = timegm(&tm); /* See README.md if your system lacks timegm(). */
     // if (timestamp != (time_t)-1) {

+ 101 - 13
components/minmea/minmea.h

@@ -13,29 +13,33 @@
 extern "C" {
 #endif
 
-#include <stdio.h>
+#include <ctype.h>
 #include <stdint.h>
 #include <stdbool.h>
-#include <errno.h>
 #include <time.h>
 #include <math.h>
 #ifdef MINMEA_INCLUDE_COMPAT
 #include <minmea_compat.h>
 #endif
 
-#define MINMEA_MAX_LENGTH 80
+#ifndef MINMEA_MAX_SENTENCE_LENGTH
+#define MINMEA_MAX_SENTENCE_LENGTH 80
+#endif
 
 enum minmea_sentence_id {
     MINMEA_INVALID = -1,
     MINMEA_UNKNOWN = 0,
-    MINMEA_SENTENCE_RMC,
+    MINMEA_SENTENCE_GBS,
     MINMEA_SENTENCE_GGA,
-    MINMEA_SENTENCE_GSA,
     MINMEA_SENTENCE_GLL,
+    MINMEA_SENTENCE_GSA,
     MINMEA_SENTENCE_GST,
     MINMEA_SENTENCE_GSV,
+    MINMEA_SENTENCE_RMC,
     MINMEA_SENTENCE_VTG,
     MINMEA_SENTENCE_ZDA,
+
+    MINMEA_SENTENCE_MAX_ID
 };
 
 struct minmea_float {
@@ -56,6 +60,17 @@ struct minmea_time {
     int microseconds;
 };
 
+struct minmea_sentence_gbs {
+    struct minmea_time time;
+    struct minmea_float err_latitude;
+    struct minmea_float err_longitude;
+    struct minmea_float err_altitude;
+    int svid;
+    struct minmea_float prob;
+    struct minmea_float bias;
+    struct minmea_float stddev;
+};
+
 struct minmea_sentence_rmc {
     struct minmea_time time;
     bool valid;
@@ -187,11 +202,14 @@ enum minmea_sentence_id minmea_sentence_id(const char *sentence, bool strict);
  * Scanf-like processor for NMEA sentences. Supports the following formats:
  * c - single character (char *)
  * d - direction, returned as 1/-1, default 0 (int *)
- * f - fractional, returned as value + scale (int *, int *)
+ * f - fractional, returned as value + scale (struct minmea_float *)
  * i - decimal, default zero (int *)
  * s - string (char *)
  * t - talker identifier and type (char *)
- * T - date/time stamp (int *, int *, int *)
+ * D - date (struct minmea_date *)
+ * T - time stamp (struct minmea_time *)
+ * _ - ignore this field
+ * ; - following fields are optional
  * Returns true on success. See library source code for details.
  */
 bool minmea_scan(const char *sentence, const char *format, ...);
@@ -199,6 +217,7 @@ bool minmea_scan(const char *sentence, const char *format, ...);
 /*
  * Parse a specific type of sentence. Return true on success.
  */
+bool minmea_parse_gbs(struct minmea_sentence_gbs *frame, const char *sentence);
 bool minmea_parse_rmc(struct minmea_sentence_rmc *frame, const char *sentence);
 bool minmea_parse_gga(struct minmea_sentence_gga *frame, const char *sentence);
 bool minmea_parse_gsa(struct minmea_sentence_gsa *frame, const char *sentence);
@@ -208,15 +227,20 @@ bool minmea_parse_gsv(struct minmea_sentence_gsv *frame, const char *sentence);
 bool minmea_parse_vtg(struct minmea_sentence_vtg *frame, const char *sentence);
 bool minmea_parse_zda(struct minmea_sentence_zda *frame, const char *sentence);
 
+/**
+ * Convert GPS UTC date/time representation to a UNIX calendar time.
+ */
+int minmea_getdatetime(struct tm *tm, const struct minmea_date *date, const struct minmea_time *time_);
+
 /**
  * Convert GPS UTC date/time representation to a UNIX timestamp.
  */
-//int minmea_gettime(struct timespec *ts, const struct minmea_date *date, const struct minmea_time *time_);
+int minmea_gettime(struct timespec *ts, const struct minmea_date *date, const struct minmea_time *time_);
 
 /**
  * Rescale a fixed-point value to a different scale. Rounds towards zero.
  */
-static inline int_least32_t minmea_rescale(struct minmea_float *f, int_least32_t new_scale)
+static inline int_least32_t minmea_rescale(const struct minmea_float *f, int_least32_t new_scale)
 {
     if (f->scale == 0)
         return 0;
@@ -232,10 +256,10 @@ static inline int_least32_t minmea_rescale(struct minmea_float *f, int_least32_t
  * Convert a fixed-point value to a floating-point value.
  * Returns NaN for "unknown" values.
  */
-static inline float minmea_tofloat(struct minmea_float *f)
+static inline float minmea_tofloat(const struct minmea_float *f)
 {
     if (f->scale == 0)
-        return NAN;
+        return 0;
     return (float) f->value / (float) f->scale;
 }
 
@@ -243,15 +267,79 @@ static inline float minmea_tofloat(struct minmea_float *f)
  * Convert a raw coordinate to a floating point DD.DDD... value.
  * Returns NaN for "unknown" values.
  */
-static inline float minmea_tocoord(struct minmea_float *f)
+static inline float minmea_tocoord(const struct minmea_float *f)
 {
     if (f->scale == 0)
-        return NAN;
+        return 0;
+    if (f->scale  > (INT_LEAST32_MAX / 100))
+        return 0;
+    if (f->scale < (INT_LEAST32_MIN / 100))
+        return 0;
     int_least32_t degrees = f->value / (f->scale * 100);
     int_least32_t minutes = f->value % (f->scale * 100);
     return (float) degrees + (float) minutes / (60 * f->scale);
 }
 
+/**
+ * Check whether a character belongs to the set of characters allowed in a
+ * sentence data field.
+ */
+static inline bool minmea_isfield(char c) {
+    return isprint((unsigned char) c) && c != ',' && c != '*';
+}
+
+
+// 扩展
+
+#define RECV_BUFF_SIZE (2048)
+#define FRAME_GSA_MAX   (3)
+#define FRAME_GSV_MAX   (5)
+
+int luat_libgnss_init(void);
+int luat_libgnss_parse_data(const char* data, size_t len);
+int luat_libgnss_parse_nmea(const char* line);
+void luat_libgnss_uart_recv_cb(int uart_id, uint32_t data_len);
+int luat_libgnss_state_onchanged(int state);
+
+enum GNSS_STATE {
+    GNSS_STATE_INIT = 0,
+    GNSS_STATE_FIXED,
+    GNSS_STATE_LOSE,
+    GNSS_STATE_OPEN,
+    GNSS_STATE_CLOSE
+};
+
+typedef struct luat_libgnss
+{
+    uint8_t debug;
+    uint8_t rtc_auto;
+    uint32_t fix_at_ticks;
+    // int lua_ref;
+    struct minmea_sentence_rmc frame_rmc;
+    struct minmea_sentence_gga frame_gga;
+    struct minmea_sentence_gll frame_gll;
+    struct minmea_sentence_gst frame_gst;
+    struct minmea_sentence_gsv frame_gsv_gp[FRAME_GSV_MAX];
+    struct minmea_sentence_gsv frame_gsv_gb[FRAME_GSV_MAX];
+    struct minmea_sentence_gsv frame_gsv_gl[FRAME_GSV_MAX];
+    struct minmea_sentence_gsv frame_gsv_ga[FRAME_GSV_MAX];
+    struct minmea_sentence_vtg frame_vtg;
+    struct minmea_sentence_gsa frame_gsa[FRAME_GSA_MAX];
+    struct minmea_sentence_zda frame_zda;
+} luat_libgnss_t;
+
+typedef struct luat_libgnss_tmp
+{
+    struct minmea_sentence_rmc frame_rmc;
+    struct minmea_sentence_gga frame_gga;
+    struct minmea_sentence_gll frame_gll;
+    struct minmea_sentence_gst frame_gst;
+    struct minmea_sentence_gsv frame_gsv;
+    struct minmea_sentence_vtg frame_vtg;
+    struct minmea_sentence_gsa frame_gsa;
+    struct minmea_sentence_zda frame_zda;
+} luat_libgnss_tmp_t;
+
 #ifdef __cplusplus
 }
 #endif

+ 44 - 67
components/network/adapter/luat_lib_socket.c

@@ -737,52 +737,6 @@ static int l_socket_set_ssl_log(lua_State *L)
 	return 0;
 }
 
-#include "rotable2.h"
-static const rotable_Reg_t reg_socket_adapter[] =
-{
-	{"create",			ROREG_FUNC(l_socket_create)},
-	{"debug",		ROREG_FUNC(l_socket_set_debug)},
-	{"config",		ROREG_FUNC(l_socket_config)},
-	{"linkup",			ROREG_FUNC(l_socket_linkup)},
-	{"connect",			ROREG_FUNC(l_socket_connect)},
-	{"listen",			ROREG_FUNC(l_socket_listen)},
-	{"accept",			ROREG_FUNC(l_socket_accept)},
-	{"discon",			ROREG_FUNC(l_socket_disconnect)},
-	{"close",			ROREG_FUNC(l_socket_close)},
-	{"tx",			ROREG_FUNC(l_socket_tx)},
-	{"rx",			ROREG_FUNC(l_socket_rx)},
-	{"wait",			ROREG_FUNC(l_socket_wait)},
-	//{"listen",			ROREG_FUNC(l_socket_listen)},
-	//{"accept",			ROREG_FUNC(l_socket_accept)},
-	{"release",			ROREG_FUNC(l_socket_release)},
-	{ "setDNS",           ROREG_FUNC(l_socket_set_dns)},
-	{ "sslLog",			ROREG_FUNC(l_socket_set_ssl_log)},
-	{"localIP",         	ROREG_FUNC(l_socket_local_ip)},
-	//@const ETH0 number 带硬件协议栈的ETH0
-    { "ETH0",           ROREG_INT(NW_ADAPTER_INDEX_ETH0)},
-	//@const LWIP_ETH number 使用LWIP协议栈的以太网卡
-	{ "LWIP_ETH",          	ROREG_INT(NW_ADAPTER_INDEX_LWIP_ETH)},
-	//@const LWIP_STA number 使用LWIP协议栈的WIFI STA
-	{ "LWIP_STA",          	ROREG_INT(NW_ADAPTER_INDEX_LWIP_WIFI_STA)},
-	//@const LWIP_AP number 使用LWIP协议栈的WIFI AP
-	{ "LWIP_AP",     		ROREG_INT(NW_ADAPTER_INDEX_LWIP_WIFI_AP)},
-	//@const LWIP_GP number 使用LWIP协议栈的移动蜂窝模块
-	{ "LWIP_GP",          	ROREG_INT(NW_ADAPTER_INDEX_LWIP_GPRS)},
-	//@const USB number 使用LWIP协议栈的USB网卡
-	{ "USB",     		ROREG_INT(NW_ADAPTER_INDEX_USB)},
-	//@const LINK number LINK事件
-    { "LINK",           ROREG_INT(EV_NW_RESULT_LINK & 0x0fffffff)},
-    //@const ON_LINE number ON_LINE事件
-	{ "ON_LINE",          	ROREG_INT(EV_NW_RESULT_CONNECT & 0x0fffffff)},
-    //@const EVENT number EVENT事件
-	{ "EVENT",          	ROREG_INT(EV_NW_RESULT_EVENT & 0x0fffffff)},
-    //@const TX_OK number TX_OK事件
-	{ "TX_OK",     		ROREG_INT(EV_NW_RESULT_TX & 0x0fffffff)},
-    //@const CLOSED number CLOSED事件
-	{ "CLOSED",     		ROREG_INT(EV_NW_RESULT_CLOSE & 0x0fffffff)},
-	{ NULL,            ROREG_INT(0)}
-};
-
 #else
 static int32_t l_socket_callback(lua_State *L, void* ptr)
 {
@@ -1338,37 +1292,60 @@ static int l_socket_set_ssl_log(lua_State *L)
 	return 0;
 }
 
+#endif
+
+#ifdef LUAT_USE_SNTP
+#include "luat_sntp.h"
+#endif
+
 #include "rotable2.h"
 static const rotable_Reg_t reg_socket_adapter[] =
 {
-	{"create",				ROREG_FUNC(l_socket_create)},
-	{"debug",				ROREG_FUNC(l_socket_set_debug)},
-	{"config",				ROREG_FUNC(l_socket_config)},
-	{"linkup",				ROREG_FUNC(l_socket_linkup)},
-	{"connect",				ROREG_FUNC(l_socket_connect)},
-	{"listen",				ROREG_FUNC(l_socket_listen)},
-	{"accept",				ROREG_FUNC(l_socket_accept)},
-	{"discon",				ROREG_FUNC(l_socket_disconnect)},
-	{"close",				ROREG_FUNC(l_socket_close)},
-	{"tx",					ROREG_FUNC(l_socket_tx)},
-	{"rx",					ROREG_FUNC(l_socket_rx)},
-	{"wait",				ROREG_FUNC(l_socket_wait)},
+	{"create",			ROREG_FUNC(l_socket_create)},
+	{"debug",		ROREG_FUNC(l_socket_set_debug)},
+	{"config",		ROREG_FUNC(l_socket_config)},
+	{"linkup",			ROREG_FUNC(l_socket_linkup)},
+	{"connect",			ROREG_FUNC(l_socket_connect)},
+	{"listen",			ROREG_FUNC(l_socket_listen)},
+	{"accept",			ROREG_FUNC(l_socket_accept)},
+	{"discon",			ROREG_FUNC(l_socket_disconnect)},
+	{"close",			ROREG_FUNC(l_socket_close)},
+	{"tx",			ROREG_FUNC(l_socket_tx)},
+	{"rx",			ROREG_FUNC(l_socket_rx)},
+	{"wait",			ROREG_FUNC(l_socket_wait)},
 	//{"listen",			ROREG_FUNC(l_socket_listen)},
 	//{"accept",			ROREG_FUNC(l_socket_accept)},
-	{"release",				ROREG_FUNC(l_socket_release)},
-	{ "setDNS",           	ROREG_FUNC(l_socket_set_dns)},
-	{ "sslLog",				ROREG_FUNC(l_socket_set_ssl_log)},
+	{"release",			ROREG_FUNC(l_socket_release)},
+	{ "setDNS",           ROREG_FUNC(l_socket_set_dns)},
+	{ "sslLog",			ROREG_FUNC(l_socket_set_ssl_log)},
 	{"localIP",         	ROREG_FUNC(l_socket_local_ip)},
-
-    { "ETH0",           	ROREG_INT(NW_ADAPTER_INDEX_ETH0)},
-    { "LINK",           	ROREG_INT(EV_NW_RESULT_LINK & 0x0fffffff)},
+#ifdef LUAT_USE_SNTP
+	{"sntp",         	ROREG_FUNC(l_sntp_get)},
+#endif
+	//@const ETH0 number 带硬件协议栈的ETH0
+    { "ETH0",           ROREG_INT(NW_ADAPTER_INDEX_ETH0)},
+	//@const LWIP_ETH number 使用LWIP协议栈的以太网卡
+	{ "LWIP_ETH",          	ROREG_INT(NW_ADAPTER_INDEX_LWIP_ETH)},
+	//@const LWIP_STA number 使用LWIP协议栈的WIFI STA
+	{ "LWIP_STA",          	ROREG_INT(NW_ADAPTER_INDEX_LWIP_WIFI_STA)},
+	//@const LWIP_AP number 使用LWIP协议栈的WIFI AP
+	{ "LWIP_AP",     		ROREG_INT(NW_ADAPTER_INDEX_LWIP_WIFI_AP)},
+	//@const LWIP_GP number 使用LWIP协议栈的移动蜂窝模块
+	{ "LWIP_GP",          	ROREG_INT(NW_ADAPTER_INDEX_LWIP_GPRS)},
+	//@const USB number 使用LWIP协议栈的USB网卡
+	{ "USB",     		ROREG_INT(NW_ADAPTER_INDEX_USB)},
+	//@const LINK number LINK事件
+    { "LINK",           ROREG_INT(EV_NW_RESULT_LINK & 0x0fffffff)},
+    //@const ON_LINE number ON_LINE事件
 	{ "ON_LINE",          	ROREG_INT(EV_NW_RESULT_CONNECT & 0x0fffffff)},
+    //@const EVENT number EVENT事件
 	{ "EVENT",          	ROREG_INT(EV_NW_RESULT_EVENT & 0x0fffffff)},
-	{ "TX_OK",     			ROREG_INT(EV_NW_RESULT_TX & 0x0fffffff)},
+    //@const TX_OK number TX_OK事件
+	{ "TX_OK",     		ROREG_INT(EV_NW_RESULT_TX & 0x0fffffff)},
+    //@const CLOSED number CLOSED事件
 	{ "CLOSED",     		ROREG_INT(EV_NW_RESULT_CLOSE & 0x0fffffff)},
-	{ NULL,            		ROREG_INT(0)}
+	{ NULL,            ROREG_INT(0)}
 };
-#endif
 
 LUAMOD_API int luaopen_socket_adapter( lua_State *L ) {
     luat_newlib2(L, reg_socket_adapter);

+ 11 - 0
components/network/adapter/luat_network_adapter.c

@@ -588,6 +588,17 @@ static int network_state_connecting(network_ctrl_t *ctrl, OS_EVENT *event, netwo
 			ctrl->ssl->p_bio = ctrl;
 			ctrl->ssl->f_send = tls_send;
 			ctrl->ssl->f_recv = tls_recv;
+			// add by wendal
+			// cloudflare的https需要设置hostname才能访问
+			if (ctrl->domain_name_len > 0 && ctrl->domain_name_len < 256) {
+				char host[257] = {0};
+				memcpy(host, ctrl->domain_name, ctrl->domain_name_len);
+				mbedtls_ssl_set_hostname(ctrl->ssl, host);
+				//LLOGD("CALL mbedtls_ssl_set_hostname(%s)", host);
+			}
+			else {
+				//LLOGD("skip mbedtls_ssl_set_hostname");
+			}
 
 			ctrl->state = NW_STATE_SHAKEHAND;
 	    	do

+ 2 - 2
components/network/libhttp/luat_http.h

@@ -23,7 +23,7 @@ typedef struct{
 	uint16_t remote_port; 		// 远程端口号
 	const char *url;			// url
 	const char *uri;			// uri
-	const char *method;			// method
+	char method[12];			// method
 
 	// 发送相关
 	uint8_t request_message[HTTP_REQUEST_BUF_LEN_MAX];
@@ -41,7 +41,7 @@ typedef struct{
 	uint32_t headers_len;		//headers缓存长度
 	char* body;
 	uint32_t body_len;			//body缓存长度
-	uint8_t is_chunk;			//是否chunk编码
+	// uint8_t is_chunk;			//是否chunk编码
 	uint8_t re_request_count;
 
 	// 响应相关

+ 31 - 61
components/network/libhttp/luat_http_client.c

@@ -41,6 +41,7 @@ end)
 static void http_send_message(luat_http_ctrl_t *http_ctrl);
 
 static int http_close(luat_http_ctrl_t *http_ctrl){
+	LLOGI("http close %p", http_ctrl);
 	if (http_ctrl->netc){
 		network_force_close_socket(http_ctrl->netc);
 		network_release_ctrl(http_ctrl->netc);
@@ -54,9 +55,6 @@ static int http_close(luat_http_ctrl_t *http_ctrl){
 	if (http_ctrl->uri){
 		luat_heap_free(http_ctrl->uri);
 	}
-	if (http_ctrl->method){
-		luat_heap_free(http_ctrl->method);
-	}
 	if (http_ctrl->req_header){
 		luat_heap_free(http_ctrl->req_header);
 	}
@@ -86,14 +84,14 @@ static int32_t l_http_callback(lua_State *L, void* ptr){
     luat_http_ctrl_t *http_ctrl =(luat_http_ctrl_t *)msg->ptr;
 	uint64_t idp = http_ctrl->idp;
 
+	network_close(http_ctrl->netc, 0);
+
 	// LLOGD("l_http_callback arg1:%d is_download:%d idp:%d",msg->arg1,http_ctrl->is_download,idp);
 	if (msg->arg1){
 		lua_pushinteger(L, msg->arg1); // 把错误码返回去
 		luat_cbcwait(L, idp, 1);
-		http_close(http_ctrl);
-		return 0;
+		goto exit;
 	}
-	network_close(http_ctrl->netc, 0);
 
 	lua_pushinteger(L, http_ctrl->parser.status_code);
 	lua_newtable(L);
@@ -123,7 +121,7 @@ static int32_t l_http_callback(lua_State *L, void* ptr){
 			// 下载操作一切正常, 返回长度
 			lua_pushinteger(L, http_ctrl->body_len);
 			luat_cbcwait(L, idp, 3); // code, headers, body
-			return 0;
+			goto exit;
 		}else if (http_ctrl->fd != NULL) {
 			// 下载中断了!!
 			luat_fs_fclose(http_ctrl->fd);
@@ -132,12 +130,13 @@ static int32_t l_http_callback(lua_State *L, void* ptr){
 		// 下载失败, 返回错误码
 		lua_pushinteger(L, -1);
 		luat_cbcwait(L, idp, 3); // code, headers, body
-		return 0;
+		goto exit;
 	} else {
 		// 非下载模式
 		lua_pushlstring(L, http_ctrl->body, http_ctrl->body_len);
 		luat_cbcwait(L, idp, 3); // code, headers, body
 	}
+exit:
 	http_close(http_ctrl);
 	return 0;
 }
@@ -164,22 +163,22 @@ error:
 	}
 }
 
-int on_message_begin(http_parser* parser){
+static int on_message_begin(http_parser* parser){
 	LLOGD("on_message_begin");
     return 0;
 }
 
-int on_url(http_parser* parser, const char *at, size_t length){
+static int on_url(http_parser* parser, const char *at, size_t length){
 	LLOGD("on_url:%.*s",length,at);
     return 0;
 }
 
-int on_status(http_parser* parser, const char *at, size_t length){
+static int on_status(http_parser* parser, const char *at, size_t length){
     LLOGD("on_status:%.*s",length,at);
     return 0;
 }
 
-int on_header_field(http_parser* parser, const char *at, size_t length){
+static int on_header_field(http_parser* parser, const char *at, size_t length){
     LLOGD("on_header_field:%.*s",length,at);
 	luat_http_ctrl_t *http_ctrl =(luat_http_ctrl_t *)parser->data;
 	if (http_ctrl->headers_complete){
@@ -199,7 +198,7 @@ int on_header_field(http_parser* parser, const char *at, size_t length){
     return 0;
 }
 	
-int on_header_value(http_parser* parser, const char *at, size_t length){
+static int on_header_value(http_parser* parser, const char *at, size_t length){
     LLOGD("on_header_value:%.*s",length,at);
 	char tmp[16] = {0};
 	luat_http_ctrl_t *http_ctrl =(luat_http_ctrl_t *)parser->data;
@@ -218,7 +217,7 @@ int on_header_value(http_parser* parser, const char *at, size_t length){
     return 0;
 }
 
-int on_headers_complete(http_parser* parser){
+static int on_headers_complete(http_parser* parser){
     LLOGD("on_headers_complete");
 	luat_http_ctrl_t *http_ctrl =(luat_http_ctrl_t *)parser->data;
 	if (http_ctrl->headers_complete){
@@ -236,7 +235,7 @@ int on_headers_complete(http_parser* parser){
     return 0;
 }
 
-int on_body(http_parser* parser, const char *at, size_t length){
+static int on_body(http_parser* parser, const char *at, size_t length){
 	LLOGD("on_body:%.*s",length,at);
 	luat_http_ctrl_t *http_ctrl =(luat_http_ctrl_t *)parser->data;
 
@@ -263,7 +262,7 @@ int on_body(http_parser* parser, const char *at, size_t length){
     return 0;
 }
 
-int on_message_complete(http_parser* parser){
+static int on_message_complete(http_parser* parser){
     LLOGD("on_message_complete");
 	luat_http_ctrl_t *http_ctrl =(luat_http_ctrl_t *)parser->data;
 	// http_ctrl->body[http_ctrl->body_len] = 0x00;
@@ -282,16 +281,17 @@ int on_message_complete(http_parser* parser){
     return 0;
 }
 
-int on_chunk_header(http_parser* parser){
+static int on_chunk_header(http_parser* parser){
 	LLOGD("on_chunk_header");
 	LLOGD("content_length:%lld",parser->content_length);
-	luat_http_ctrl_t *http_ctrl =(luat_http_ctrl_t *)parser->data;
-	http_ctrl->is_chunk = 1;
+	// luat_http_ctrl_t *http_ctrl =(luat_http_ctrl_t *)parser->data;
+	// http_ctrl->is_chunk = 1;
     return 0;
 }
 
-int on_chunk_complete(http_parser* parser){
-	LLOGD("on_chunk_complete");
+static int on_chunk_complete(http_parser* parser){
+	LLOGD("on_chunk_complete CALL on_message_complete");
+	on_message_complete(parser);
     return 0;
 }
 
@@ -344,10 +344,6 @@ static void http_send_message(luat_http_ctrl_t *http_ctrl){
 		luat_heap_free(http_ctrl->uri);
 		http_ctrl->uri = NULL;
 	}
-	if (http_ctrl->method){
-		luat_heap_free(http_ctrl->method);
-		http_ctrl->method = NULL;
-	}
 	if (http_ctrl->req_header){
 		luat_heap_free(http_ctrl->req_header);
 		http_ctrl->req_header = NULL;
@@ -391,35 +387,14 @@ next:
 				if (result)
 					goto next;
 				if (rx_len == 0||result!=0) {
+					luat_heap_free(resp_buff);
 					http_resp_error(http_ctrl, HTTP_ERROR_RX);
 					return -1;
 				}
 				
 				int nParseBytes = http_parser_execute(&http_ctrl->parser, &http_ctrl->parser_settings, resp_buff, total_len);
 				
-				// if (http_ctrl->is_chunk){
-				// 	if (http_ctrl->is_download){
-				// 		if (http_ctrl->fd == NULL){
-				// 			luat_fs_remove(http_ctrl->dst);
-				// 			http_ctrl->fd = luat_fs_fopen(http_ctrl->dst, "w+");
-				// 			if (http_ctrl->fd == NULL) {
-				// 				LLOGE("open download file fail %s", http_ctrl->dst);
-				// 				http_resp_error(http_ctrl, HTTP_ERROR_DOWNLOAD);
-				// 				return -1;
-				// 			}
-				// 		}
-				// 		luat_fs_fwrite(resp_buff, total_len, 1, http_ctrl->fd);
-				// 	}else{
-				// 		if (!http_ctrl->body){
-				// 			http_ctrl->body = luat_heap_malloc(total_len+1);
-				// 		}else{
-				// 			LLOGD("chunk realloc len:%d",http_ctrl->body_len+total_len+1);
-				// 			http_ctrl->body = luat_heap_realloc(http_ctrl->body,http_ctrl->body_len+total_len+1);
-				// 		}
-				// 		memcpy(http_ctrl->body+http_ctrl->body_len,resp_buff,total_len);
-				// 		http_ctrl->body_len += total_len;
-				// 	}
-				// }
+				LLOGD("nParseBytes %d total_len %d", nParseBytes, total_len);
 				
 				luat_heap_free(resp_buff);
 
@@ -440,6 +415,7 @@ next:
 	}
 
 	ret = network_wait_event(http_ctrl->netc, NULL, 0, NULL);
+	LLOGD("network_wait_event %d", ret);
 	if (ret < 0){
 		http_resp_error(http_ctrl, HTTP_ERROR_CLOSE);
 		return -1;
@@ -569,7 +545,7 @@ static int l_http_request(lua_State *L) {
 	const char *client_cert = NULL;
 	const char *client_key = NULL;
 	const char *client_password = NULL;
-	int adapter_index;
+	int adapter_index = -1;
 	char body_len[6] = {0};
 	// mbedtls_debug_set_threshold(4);
 	luat_http_ctrl_t *http_ctrl = (luat_http_ctrl_t *)luat_heap_malloc(sizeof(luat_http_ctrl_t));
@@ -617,6 +593,7 @@ static int l_http_request(lua_State *L) {
 	}
 
 	if (adapter_index < 0 || adapter_index >= NW_ADAPTER_QTY){
+		LLOGD("bad network adapter index %d", adapter_index);
 		goto error;
 	}
 
@@ -650,9 +627,11 @@ static int l_http_request(lua_State *L) {
 
 
 	const char *method = luaL_optlstring(L, 1, "GET", &len);
-	http_ctrl->method = luat_heap_malloc(len + 1);
-	memset(http_ctrl->method, 0, len + 1);
-	memcpy(http_ctrl->method, method, len);
+	if (len > 11) {
+		LLOGE("method is too long %s", method);
+		goto error;
+	}
+	memcpy(http_ctrl->method, method, len + 1);
 	// LLOGD("method:%s",http_ctrl->method);
 
 	const char *url = luaL_checklstring(L, 2, &len);
@@ -717,15 +696,6 @@ static int l_http_request(lua_State *L) {
 		network_deinit_tls(http_ctrl->netc);
 	}
 
-	if (!strncmp("GET", http_ctrl->method, strlen("GET"))) {
-        LLOGI("HTTP GET");
-    }
-    else if (!strncmp("POST", http_ctrl->method, strlen("POST"))) {
-        LLOGI("HTTP POST");
-    }else {
-        LLOGI("only GET/POST supported %s", http_ctrl->method);
-        goto error;
-    }
 #ifdef LUAT_USE_LWIP
 	http_ctrl->ip_addr.type = 0xff;
 #else

+ 259 - 0
components/network/libsntp/luat_sntp.c

@@ -0,0 +1,259 @@
+/*
+@module  socket
+@summary 网络接口
+@version 1.0
+@date    2022.11.13
+*/
+
+#include "luat_base.h"
+
+#include "luat_network_adapter.h"
+#include "luat_rtos.h"
+#include "luat_msgbus.h"
+
+#include "luat_malloc.h"
+#include "luat_rtc.h"
+
+#include "luat_sntp.h"
+
+#define LUAT_LOG_TAG "sntp"
+#include "luat_log.h"
+
+#define SNTP_SERVER_COUNT       3
+#define SNTP_SERVER_LEN_MAX     32
+
+static char sntp_server[SNTP_SERVER_COUNT][SNTP_SERVER_LEN_MAX] = {
+    "ntp.aliyun.com",
+    "ntp1.aliyun.com",
+    "ntp2.aliyun.com"
+};
+
+static unsigned int sntp_server_num = 0;
+
+static const uint8_t sntp_packet[48]={0x1b};
+
+#define NTP_UPDATE 1
+#define NTP_ERROR  2
+
+static int l_sntp_event_handle(lua_State* L, void* ptr) {
+    rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
+    if (lua_getglobal(L, "sys_pub") != LUA_TFUNCTION) {
+        return 0;
+    };
+    switch (msg->arg1)
+    {
+/*
+@sys_pub socket
+时间已经同步
+NTP_UPDATE
+@usage
+sys.subscribe("NTP_UPDATE", function()
+    log.info("socket", "sntp", os.date())
+end)
+*/
+    case NTP_UPDATE:
+        lua_pushstring(L, "NTP_UPDATE");
+        break;
+/*
+@sys_pub socket
+时间同步失败
+NTP_ERROR
+@usage
+sys.subscribe("NTP_ERROR", function()
+    log.info("socket", "sntp error")
+end)
+*/
+    case NTP_ERROR:
+        lua_pushstring(L, "NTP_ERROR");
+        break;
+    default:
+        return 0;
+    }
+    lua_call(L, 1, 0);
+    return 0;
+}
+
+
+int luat_sntp_connect(network_ctrl_t *sntp_netc){
+    int ret;
+    luat_ip_addr_t ip_addr;
+#ifdef LUAT_USE_LWIP
+	ip_addr.type = 0xff;
+#else
+	ip_addr.is_ipv6 = 0xff;
+#endif
+    if (sntp_server_num >= sizeof(sntp_server))
+        return -1;
+#ifdef LUAT_USE_LWIP
+	ret = network_connect(sntp_netc, sntp_server[sntp_server_num], strlen(sntp_server[sntp_server_num]), (0xff == ip_addr.type)?NULL:&(ip_addr), 123, 1000);
+#else
+	ret = network_connect(sntp_netc, sntp_server[sntp_server_num], strlen(sntp_server[sntp_server_num]), (0xff == ip_addr.is_ipv6)?NULL:&(ip_addr), 123, 1000);
+#endif
+    sntp_server_num++;
+	// LLOGD("network_connect ret %d", ret);
+	if (ret < 0) {
+        network_close(sntp_netc, 0);
+        return -1;
+    }
+    return 0;
+}
+
+int luat_sntp_close_socket(network_ctrl_t *sntp_netc){
+    if (sntp_netc){
+		network_force_close_socket(sntp_netc);
+	}
+    if (sntp_server_num == 0){
+#ifdef __LUATOS__
+        rtos_msg_t msg;
+        msg.handler = l_sntp_event_handle;
+        msg.arg1 = NTP_UPDATE;
+        luat_msgbus_put(&msg, 0);
+#endif
+        network_release_ctrl(sntp_netc);
+        return 0;
+	}
+	if (sntp_server_num < sizeof(sntp_server)){
+		luat_sntp_connect(sntp_netc);
+	}else{
+        network_release_ctrl(sntp_netc);
+        sntp_server_num = 0;
+#ifdef __LUATOS__
+        rtos_msg_t msg;
+        msg.handler = l_sntp_event_handle;
+        msg.arg1 = NTP_ERROR;
+        luat_msgbus_put(&msg, 0);
+#endif
+    }
+    return 0;
+}
+
+int32_t luat_sntp_callback(void *data, void *param) {
+	OS_EVENT *event = (OS_EVENT *)data;
+	network_ctrl_t *sntp_netc =(network_ctrl_t *)param;
+	int ret = 0;
+    uint32_t tx_len = 0;
+
+	// LLOGD("LINK %d ON_LINE %d EVENT %d TX_OK %d CLOSED %d",EV_NW_RESULT_LINK & 0x0fffffff,EV_NW_RESULT_CONNECT & 0x0fffffff,EV_NW_RESULT_EVENT & 0x0fffffff,EV_NW_RESULT_TX & 0x0fffffff,EV_NW_RESULT_CLOSE & 0x0fffffff);
+	// LLOGD("network sntp cb %8X %s %8X",event->ID & 0x0ffffffff, event2str(event->ID & 0x0ffffffff) ,event->Param1);
+	if (event->ID == EV_NW_RESULT_LINK){
+		return 0; // 这里应该直接返回, 不能往下调用network_wait_event
+	}else if(event->ID == EV_NW_RESULT_CONNECT){
+        network_tx(sntp_netc, sntp_packet, sizeof(sntp_packet), 0, NULL, 0, &tx_len, 0);
+        // LLOGD("luat_sntp_callback tx_len:%d",tx_len);
+	}else if(event->ID == EV_NW_RESULT_EVENT){
+		uint32_t total_len = 0;
+		uint32_t rx_len = 0;
+		int result = network_rx(sntp_netc, NULL, 0, 0, NULL, NULL, &total_len);
+		// LLOGD("result:%d total_len:%d",result,total_len);
+		if (0 == result){
+			if (total_len>0){
+				uint8_t* resp_buff = luat_heap_malloc(total_len + 1);
+				resp_buff[total_len] = 0x00;
+next:
+				result = network_rx(sntp_netc, resp_buff, total_len, 0, NULL, NULL, &rx_len);
+				// LLOGD("result:%d rx_len:%d",result,rx_len);
+				// LLOGD("resp_buff:%.*s len:%d",total_len,resp_buff,total_len);
+				if (result)
+					goto next;
+				if (rx_len == 0||result!=0) {
+                    luat_heap_free(resp_buff);
+					luat_sntp_close_socket(sntp_netc);
+					return -1;
+				}
+                const uint8_t *p = (const uint8_t *)resp_buff+40;
+                uint32_t time =  (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+                if (time > 0x83AA7E80){
+                    time -= 0x83AA7E80;
+                }else{
+                    time += 0x7C558180;
+                }
+                luat_rtc_set_tamp32(time);
+                LLOGD("Unix timestamp:%d",time);
+                sntp_server_num = 0;
+                luat_sntp_close_socket(sntp_netc);
+                luat_heap_free(resp_buff);
+                return 0;
+			}
+		}else{
+			luat_sntp_close_socket(sntp_netc);
+			return -1;
+		}
+	}else if(event->ID == EV_NW_RESULT_TX){
+
+	}else if(event->ID == EV_NW_RESULT_CLOSE){
+
+	}
+	if (event->Param1){
+		// LLOGW("sntp_callback param1 %d, closing socket", event->Param1);
+		luat_sntp_close_socket(sntp_netc);
+	}
+	ret = network_wait_event(sntp_netc, NULL, 0, NULL);
+	if (ret < 0){
+		// LLOGW("network_wait_event ret %d, closing socket", ret);
+		luat_sntp_close_socket(sntp_netc);
+		return -1;
+	}
+    return 0;
+}
+
+int ntp_get(void){
+	int adapter_index = network_get_last_register_adapter();
+	if (adapter_index < 0 || adapter_index >= NW_ADAPTER_QTY){
+		return -1;
+	}
+	network_ctrl_t *sntp_netc = network_alloc_ctrl(adapter_index);
+	if (!sntp_netc){
+		LLOGE("network_alloc_ctrl fail");
+		return -1;
+	}
+	network_init_ctrl(sntp_netc, NULL, luat_sntp_callback, sntp_netc);
+	network_set_base_mode(sntp_netc, 0, 10000, 0, 0, 0, 0);
+	network_set_local_port(sntp_netc, 0);
+	network_deinit_tls(sntp_netc);
+    return luat_sntp_connect(sntp_netc);
+}
+
+/*
+sntp时间同步
+@api    socket.sntp(sntp_server)
+@string/table sntp服务器地址 选填
+@tag LUAT_USE_SNTP
+@usage
+socket.sntp()
+sys.subscribe("NTP_UPDATE", function()
+    log.info("sntp", "time", os.date())
+end)
+*/
+
+int l_sntp_get(lua_State *L){
+    size_t len = 0;
+	if (lua_isstring(L, 1)){
+        const char * server_addr = luaL_checklstring(L, 1, &len);
+        if (len < SNTP_SERVER_LEN_MAX){
+            memcpy(sntp_server[0], server_addr, len);
+            sntp_server[0][len] = 0x00;
+        }else{
+            LLOGE("server_addr too lang");
+        }
+	}else if(lua_istable(L, 1)){
+        size_t count = lua_rawlen(L, 1);
+        if (count > sizeof(sntp_server)){
+            count = sizeof(sntp_server);
+        }
+		for (size_t i = 0; i < count; i++){
+			lua_geti(L, 1, i);
+			const char * server_addr = luaL_checklstring(L, -1, &len);
+            if (len < SNTP_SERVER_LEN_MAX){
+                memcpy(sntp_server[i], server_addr, len);
+                sntp_server[i][len] = 0x00;
+            }else{
+                LLOGE("server_addr too lang");
+            }
+			lua_pop(L, 1);
+		}
+	}
+    ntp_get();
+	return 0;
+}
+
+

+ 8 - 0
components/network/libsntp/luat_sntp.h

@@ -0,0 +1,8 @@
+#ifndef LUAT_SNTP_H
+#define LUAT_SNTP_H
+
+
+int ntp_get(void);
+int l_sntp_get(lua_State *L);
+
+#endif

+ 26 - 5
components/shell/luat_shell.c

@@ -14,11 +14,6 @@ LuatOS Shell -- LuatOS 控制台
 #include "luat_mcu.h"
 #endif
 
-#ifdef LUAT_USE_I2CTOOLS
-#include "i2c_utils.h"
-extern void i2c_tools(const char * data,size_t len);
-#endif
-
 #include "luat_shell.h"
 #include "luat_str.h"
 #include "luat_cmux.h"
@@ -55,6 +50,9 @@ static void cmd_loadstr(char* uart_buff, size_t len);
 
 static void cmd_i2c_tools(char* uart_buff, size_t len);
 
+static void cmd_ping(char* uart_buff, size_t len);
+static void cmd_sntp(char* uart_buff, size_t len);
+
 // 文件操作
 static void cmd_ry(char* uart_buff, size_t len);
 static void cmd_lsdir(char* uart_buff, size_t len);
@@ -83,6 +81,12 @@ const luat_shell_cmd_reg_t cmd_regs[] = {
 #endif
 #ifdef LUAT_USE_YMODEM
     {"ry\r",       cmd_ry},
+#endif
+#ifdef LUAT_USE_SNTP
+    {"sntp", cmd_sntp},
+#endif
+#ifdef LUAT_USE_PING
+    {"ping", cmd_ping},
 #endif
     {"lsdir ",     cmd_lsdir},
     {"fread ",     cmd_fread},
@@ -198,11 +202,28 @@ static void cmd_cmux_cmd_init(char* uart_buff, size_t len) {
 }
 
 #ifdef LUAT_USE_I2CTOOLS
+#include "i2c_utils.h"
+extern void i2c_tools(const char * data,size_t len);
 static void cmd_i2c_tools(char* uart_buff, size_t len) {
     i2c_tools(uart_buff, len);
 }
 #endif
 
+#ifdef LUAT_USE_SNTP
+#include "luat_sntp.h"
+static void cmd_sntp(char* uart_buff, size_t len){
+    ntp_get();
+}
+#endif
+
+#ifdef LUAT_USE_PING
+#include "luat_ping.h"
+static void cmd_ping(char* uart_buff, size_t len){
+    char* buff = (char*)memchr(uart_buff, ' ', len);
+    l_ping(buff+1, len-(buff-uart_buff)-1);
+}
+#endif
+
 #ifdef LUAT_USE_LOADSTR
 static void cmd_loadstr(char* uart_buff, size_t len) {
     size_t slen =  len - strlen("loadstr ") + 1;

+ 4 - 0
demo/crypto/main.lua

@@ -91,6 +91,10 @@ sys.taskInit(function()
         log.info("hmac_sha256", crypto.md_file("SHA256", "/luadb/logo.jpg", "123456"))
     end
 
+    if crypto.checksum then
+        log.info("checksum", "OK", string.char(crypto.checksum("OK")):toHex())
+    end
+
     log.info("crypto", "ALL Done")
     sys.wait(100000)
 end)

+ 111 - 0
demo/http/main.lua

@@ -0,0 +1,111 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "httpdemo"
+VERSION = "1.0.0"
+
+--[[
+本demo需要http库, 大部分能联网的设备都具有这个库
+http也是内置库, 无需require
+]]
+
+-- sys库是标配
+_G.sys = require("sys")
+--[[特别注意, 使用http库需要下列语句]]
+_G.sysplus = require("sysplus")
+
+sys.taskInit(function()
+    -----------------------------
+    -- 统一联网函数, 可自行删减
+    ----------------------------
+    if rtos.bsp():startsWith("ESP32") then
+        -- wifi 联网, ESP32系列均支持
+        local ssid = "uiot"
+        local password = "12345678"
+        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.HSPI_0, 24000000, pin.PC14, pin.PC01, pin.PC00)
+        w5500.config() --默认是DHCP模式
+        w5500.bind(socket.ETH0)
+        LED = gpio.setup(62, 0, gpio.PULLUP)
+        sys.wait(1000)
+        -- TODO 获取mac地址作为device_id
+    elseif rtos.bsp() == "EC618" then
+        -- Air780E/Air600E系列
+        --mobile.simid(2)
+        LED = gpio.setup(27, 0, gpio.PULLUP)
+        device_id = mobile.imei()
+        sys.waitUntil("IP_READY", 30000)
+    end
+
+    -- 打印一下支持的加密套件, 通常来说, 固件已包含常见的99%的加密套件
+    if crypto.cipher_suites then
+        log.info("cipher", "suites", json.encode(crypto.cipher_suites()))
+    end
+
+    -------------------------------------
+    -------- HTTP 演示代码 --------------
+    -------------------------------------
+
+    while 1 do
+        -- 最普通的Http GET请求
+        -- local code, headers, body = http.request("GET", "https://site0.cn/").wait()
+        -- local code, headers, body = http.request("GET", "https://air32.cn/").wait()
+        local code, headers, body = http.request("GET", "https://www.baidu.com/").wait() -- 留意末尾的.wait()
+        -- local code, headers, body = http.request("GET", "https://www.luatos.com/").wait()
+
+        -- 按需打印
+        -- code 响应值, 若大于等于 100 为服务器响应, 小于的均为错误代码
+        -- headers是个table, 一般作为调试数据存在
+        -- body是字符串. 注意lua的字符串是带长度的byte[]/char*, 是可以包含不可见字符的
+        log.info("http", code, json.encode(headers or {}), body)
+
+        -- -- POST request 演示
+        -- local req_headers = {}
+        -- req_headers["Content-Type"] = "application/json"
+        -- local body = json.encode({name="LuatOS"})
+        -- local code, headers, body = http.request("POST","http://site0.cn/api/httptest/simple/date", 
+        --         req_headers,
+        --         body -- POST请求所需要的body, string, zbuff, file均可
+        -- ).wait()
+        -- log.info("http.post", code, headers, body)
+    
+        -- -- POST and download, task内的同步操作
+        -- local opts = {}                 -- 额外的配置项
+        -- opts["dst"] = "/data.bin"       -- 下载路径,可选
+        -- opts["timeout"] = 30            -- 超时时长,单位秒,可选
+        -- opts["adapter"] = socket.ETH0  -- 使用哪个网卡,可选
+        -- local code, headers, body = http.request("POST","http://site0.cn/api/httptest/simple/date", 
+        --         {}, -- 请求所添加的 headers, 可以是nil
+        --         "", 
+        --         opts
+        -- ).wait()
+        -- log.info("http.post", code, headers, body) -- 只返回code和headers
+    
+        -- local f = io.open("/data.bin", "rb")
+        -- if f then
+        --     local data = f:read("*a")
+        --     log.info("fs", "data", data, data:toHex())
+        -- end
+        
+        -- -- GET request, 开个task让它自行执行去吧, 不管执行结果了
+        -- sys.taskInit(http.request("GET","http://site0.cn/api/httptest/simple/time").wait)
+
+        log.info("sys", rtos.meminfo("sys"))
+        log.info("lua", rtos.meminfo("lua"))
+        sys.wait(5000)
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 136 - 39
demo/libgnss/main.lua

@@ -1,56 +1,153 @@
-
 -- LuaTools需要PROJECT和VERSION这两个信息
-PROJECT = "libgnssdemo"
+PROJECT = "gnss"
 VERSION = "1.0.0"
 
--- sys库是标配
-_G.sys = require("sys")
-
---[[ 
-demo适用于air530z, 演示挂载在uart 2的情况, 如果挂载在其他端口, 修改gps_uart_id
+--[[
+本demo是演示定位数据处理的
 ]]
 
+-- sys库是标配
+local sys = require("sys")
+require("sysplus")
+
 local gps_uart_id = 2
+libgnss.clear() -- 清空数据,兼初始化
 
-uart.on(gps_uart_id, "recv", function(id, len)
-    local data = uart.read(gps_uart_id, 1024)
-     if data then
-        libgnss.parse(data)
+sys.taskInit(function()
+    -- Air780EG工程样品的GPS的默认波特率是9600, 量产版是115200,以下是临时代码
+    log.info("GPS", "start")
+    pm.power(pm.GPS, true)
+    uart.setup(gps_uart_id, 115200)
+    libgnss.bind(gps_uart_id) -- 绑定uart,底层自动处理GNSS数据
+    sys.wait(200) -- GPNSS芯片启动需要时间
+    -- 调试日志,可选
+    libgnss.debug(true)
+    -- 增加显示的语句
+    uart.write(gps_uart_id, "$CFGMSG,0,1,1\r\n") -- GLL
+    sys.wait(10)
+    uart.write(gps_uart_id, "$CFGMSG,0,5,1\r\n") -- VTG
+    -- 定位成功后,使用GNSS时间设置RTC, 暂不可用
+    -- libgnss.rtcAuto(true)
+    -- 读取之前的位置信息
+    local gnssloc = io.readFile("/gnssloc")
+    if gnssloc then
+        uart.write(gps_uart_id, "$AIDPOS," .. gnssloc .. "\r\n")
+        gnssloc = nil
+    end
+    sys.wait(100)
+    if http then
+        -- TODO AGNSS 未调通
+        -- while 1 do
+        --     local code, headers, body = http.request("GET", "http://download.openluat.com/9501-xingli/HXXT_GPS_BDS_AGNSS_DATA.dat").wait()
+        --     -- local code, headers, body = http.request("GET", "http://nutzam.com/6228.bin").wait()
+        --     log.info("gnss", "AGNSS", code, body and #body or 0)
+        --     if code == 200 and body and #body > 1024 then
+        --         for offset=1,#body,1024 do
+        --             log.info("gnss", "AGNSS", "write >>>")
+        --             uart.write(gps_uart_id, body:sub(offset, 1024))
+        --             sys.wait(5)
+        --         end
+        --         io.writeFile("/6228.bin", body)
+        --         break
+        --     end
+        --     sys.wait(60*1000)
+        -- end
     end
 end)
 
--- Air530Z默认波特率是9600, 主动切换一次
-uart.setup(gps_uart_id, 9600)
-uart.write(gps_uart_id, "$PCAS01,5*19\r\n")
-uart.setup(gps_uart_id, 115200)
+sys.timerLoopStart(function()
+    -- 6228CI, 查询产品信息, 可选
+    -- uart.write(gps_uart_id, "$PDTINFO,*62\r\n")
+    -- uart.write(gps_uart_id, "$AIDINFO\r\n")
+    -- uart.write(gps_uart_id, "$CFGSYS\r\n")
+    -- uart.write(gps_uart_id, "$CFGMSG,6,4\r\n")
+    log.info("RMC", json.encode(libgnss.getRmc(2) or {}))
+    -- log.info("GGA", json.encode(libgnss.getGga(2) or {}))
+    -- log.info("GLL", json.encode(libgnss.getGll(2) or {}))
+    -- log.info("GSA", json.encode(libgnss.getGsa(2) or {}))
+    -- log.info("GSV", json.encode(libgnss.getGsv(2) or {}))
+    -- log.info("VTG", json.encode(libgnss.getVtg(2) or {}))
+    -- log.info("date", os.date())
+    log.info("sys", rtos.meminfo("sys"))
+    log.info("lua", rtos.meminfo("lua"))
+end, 5000)
 
--- sys.timerLoopStart(function()
---     log.info("GPS", libgnss.getIntLocation())
---     local rmc = libgnss.getRmc()
---     log.info("rmc", json.encode(rmc))
---     --log.info("rmc", rmc.lat, rmc.lng, rmc.year, rmc.month, rmc.day, rmc.hour, rmc.min, rmc.sec)
---     rtc.set({year=rmc.year,mon=rmc.month,day=rmc.day,hour=rmc.hour,min=rmc.min,sec=rmc.sec})
--- end, 3000) -- 两秒打印一次
+-- 订阅GNSS状态编码
+sys.subscribe("GNSS_STATE", function(event, ticks)
+    -- event取值有 
+    -- FIXED 定位成功
+    -- LOSE  定位丢失
+    -- ticks是事件发生的时间,一般可以忽略
+    log.info("gnss", "state", event, ticks)
+    if event == "FIXED" then
+        local locStr = libgnss.locStr()
+        log.info("gnss", "pre loc", locStr)
+        if locStr then
+            io.writeFile("/gnssloc", locStr)
+        end
+    end
+end)
 
-sys.taskInit(function()
-    sys.wait(3000)
-    -- libgnss.parse("$GNGGA,220134.000,2232.12578,N,11356.85838,E,1,08,1.7,33.1,M,-3.4,M,,*67\r\n")
-    -- libgnss.parse("$GNGLL,2232.12578,N,11356.85838,E,220134.000,A,A*47\r\n")
-    -- libgnss.parse("$GNGSA,A,3,10,22,31,32,194,,,,,,,,2.7,1.7,2.1,1*0F\r\n")
-    -- libgnss.parse("$GNGSA,A,3,06,21,36,,,,,,,,,,2.7,1.7,2.1,4*34\r\n")
-    libgnss.parse("$GPGSV,2,1,07,03,,,31,10,28,174,18,22,62,017,33,25,,,29,0*6E\r\n")
-    libgnss.parse("$GPGSV,2,2,07,31,51,343,38,32,63,069,42,194,65,061,37,0*6A\r\n")
-    -- libgnss.parse("$BDGSV,2,1,07,04,,,33,06,55,003,34,19,05,147,09,21,66,307,42,0*41\r\n")
-    -- libgnss.parse("$BDGSV,2,2,07,22,55,157,,36,16,045,30,39,,,40,0*7E\r\n")
-    -- libgnss.parse("$GNRMC,220134.000,A,2232.12578,N,11356.85838,E,0.06,0.00,120422,,,A,V*0B\r\n")
-    -- libgnss.parse("$GNVTG,0.00,T,,M,0.06,N,0.12,K,A*26\r\n")
-    -- libgnss.parse("$GNZDA,220134.000,12,04,2022,00,00*4B\r\n")
-    -- libgnss.parse("$GPTXT,01,01,01,ANTENNA OK*35\r\n")
-    -----------------------------------------------------------------------------------------
-    local gsv = libgnss.getGsv()
-    log.info("---gsv", json.encode(gsv))
+sys.subscribe("NTP_UPDATE", function()
+    if not libgnss.isFix() then
+        -- "$AIDTIME,year,month,day,hour,minute,second,millisecond"
+        local date = os.date("!*t")
+        local str = string.format("$AIDTIME,%d,%d,%d,%d,%d,%d,000", 
+                             date["year"], date["month"], date["day"], date["hour"], date["min"], date["sec"])
+        log.info("gnss", str)
+        uart.write(gps_uart_id, str .. "\r\n")
+    end
 end)
 
+if socket and socket.sntp then
+    sys.subscribe("IP_READY", function()
+    socket.sntp()
+    end)
+end
+
+-- 定期重启GPS, 测试AGNSS
+-- sys.taskInit(function()
+--     while 1 do
+--         sys.wait(120 * 1000)
+--         log.info("GPS", "stop")
+--         pm.power(pm.GPS, false)
+--         pm.power(pm.GPS_ANT, false)
+--         sys.wait(500)
+--         log.info("GPS", "start")
+--         pm.power(pm.GPS, true)
+--         pm.power(pm.GPS_ANT, true)
+--         sys.wait(300) -- 输出产品日志大概是150ms左右,这里延时一下
+--         -- 写入时间
+--         local date = os.date("!*t")
+--         if date["year"] > 2021 then
+--             local str = string.format("$AIDTIME,%d,%d,%d,%d,%d,%d,000", 
+--                              date["year"], date["month"], date["day"], date["hour"], date["min"], date["sec"])
+--             log.info("gnss", str)
+--             uart.write(gps_uart_id, str .. "\r\n")
+--         end
+--         -- 读取并写入辅助坐标
+--         local gnssloc = io.readFile("/gnssloc")
+--         if gnssloc then
+--             uart.write(gps_uart_id, "$AIDPOS," .. gnssloc .. "\r\n")
+--             gnssloc = nil
+--         end
+--         -- 写入星历
+--         local body = io.readFile("/6228.bin")
+--         if body then
+--             for offset=1,#body,1024 do
+--                 log.info("gnss", "AGNSS", "write >>>")
+--                 uart.write(gps_uart_id, body:sub(offset, 1024))
+--                 sys.wait(5)
+--             end
+--         end
+--         log.info("AGNSS", "write complete")
+--         -- 查询一下辅助定位成功没
+--         sys.wait(300)
+--         uart.write(gps_uart_id, "$AIDINFO\r\n")
+--     end
+
+-- end)
+
 -- 用户代码已结束---------------------------------------------
 -- 结尾总是这一句
 sys.run()

+ 29 - 0
demo/profiler/main.lua

@@ -0,0 +1,29 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "memtest"
+VERSION = "1.0.0"
+
+--[[
+lua内存分析库, 未完成
+]]
+
+-- sys库是标配
+_G.sys = require("sys")
+
+sys.taskInit(function()
+    sys.wait(1000)
+    collectgarbage()
+    collectgarbage()
+    sys.wait(1000)
+    profiler.start()
+    while 1 do
+        log.info("sys", rtos.meminfo("sys"))
+        log.info("lua", rtos.meminfo("lua"))
+        sys.wait(3000)
+    end
+end)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 2 - 0
luat/include/luat_libs.h

@@ -141,5 +141,7 @@ LUAMOD_API int luaopen_ftp( lua_State *L );
 LUAMOD_API int luaopen_hmeta( lua_State *L );
 LUAMOD_API int luaopen_sms( lua_State *L );
 LUAMOD_API int luaopen_errdump( lua_State *L );
+LUAMOD_API int luaopen_profiler( lua_State *L );
+LUAMOD_API int luaopen_fskv( lua_State *L );
 
 #endif

+ 1 - 0
luat/include/luat_rtc.h

@@ -7,5 +7,6 @@ int luat_rtc_set(struct tm *tblock);
 int luat_rtc_get(struct tm *tblock);
 int luat_rtc_timer_start(int id, struct tm *tblock);
 int luat_rtc_timer_stop(int id);
+void luat_rtc_set_tamp32(uint32_t tamp);
 
 #endif

+ 33 - 0
luat/modules/luat_lib_crypto.c

@@ -670,6 +670,38 @@ static int l_crypto_md(lua_State *L) {
     return 1;
 }
 
+/*
+计算checksum校验和
+@api crypto.checksum(data)
+@string 待计算的数据,必选
+@return int checksum值,校验和
+@usage
+-- 本函数在 2022.12.28 添加
+-- 单纯计算checksum值
+local ck = crypto.checksum("OK")
+log.info("checksum", "ok", string.format("%02X", ck))
+*/
+static int l_crypt_checksum(lua_State *L) {
+    size_t len = 0;
+    uint8_t checksum = 0x00;
+    const char* sentence = luaL_checklstring(L, 1, &len);
+    for (size_t i = 0; i < len; i++)
+    {
+        checksum ^= *sentence++;
+    }
+    // if (lua_isboolean(L, 2) && lua_toboolean(L, 2) == 1) {
+    //     luaL_Buffer buff;
+    //     luaL_buffinitsize(L, &buff, len + 1);
+    //     luaL_addlstring(&buff, sentence, len);
+    //     luaL_addchar(&buff, checksum);
+    //     luaL_pushresult(&buff);
+    // }
+    // else {
+        lua_pushinteger(L, checksum);
+    // }
+    return 1;
+}
+
 #include "rotable2.h"
 static const rotable_Reg_t reg_crypto[] =
 {
@@ -696,6 +728,7 @@ static const rotable_Reg_t reg_crypto[] =
     { "base64_decode",  ROREG_FUNC(l_str_fromBase64)},
     { "md_file",        ROREG_FUNC(l_crypto_md_file)},
     { "md",             ROREG_FUNC(l_crypto_md)},
+    { "checksum",       ROREG_FUNC(l_crypt_checksum)},
 
 	{ NULL,             ROREG_INT(0) }
 };

+ 0 - 26
luat/modules/luat_lib_pm.c

@@ -243,32 +243,6 @@ pm.reboot()
 int l_rtos_reboot(lua_State *L);
 int l_rtos_standby(lua_State *L);
 
-
-// static int luat_pm_msg_handler(lua_State *L, void* ptr) {
-//     rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
-//     if (lua_event_cb == 0) {
-//         return 0;
-//     }
-//     lua_geti(L, LUA_REGISTRYINDEX, lua_event_cb);
-//     if (lua_isfunction(L, -1)) {
-//         lua_pushinteger(L, msg->arg1);
-//         lua_pushinteger(L, msg->arg2);
-//         lua_call(L, 2, 0);
-//     }
-//     return 0;
-// }
-
-// void luat_pm_cb(int event, int arg, void* args) {
-//     if (lua_event_cb != 0) {
-//         rtos_msg_t msg;
-//         msg.handler = luat_pm_msg_handler;
-//         msg.arg1 = event;
-//         msg.arg2 = arg;
-//         msg.ptr = NULL;
-//         luat_msgbus_put(&msg, 0);
-//     }
-// }
-
 /**
 开启内部的电源控制,注意不是所有的平台都支持,可能部分平台支持部分选项,看硬件
 @api pm.power(id, onoff)

+ 13 - 0
luat/modules/luat_lib_uart.c

@@ -26,7 +26,17 @@ typedef struct luat_uart_cb {
 } luat_uart_cb_t;
 static luat_uart_cb_t uart_cbs[MAX_DEVICE_COUNT];
 
+static luat_uart_recv_callback_t uart_app_recvs[MAX_DEVICE_COUNT];
+
+void luat_uart_set_app_recv(int id, luat_uart_recv_callback_t cb) {
+    if (luat_uart_exist(id)) {
+        uart_app_recvs[id] = cb;
+        luat_setup_cb(id, 1, 0); // 暂时覆盖
+    }
+}
+
 int l_uart_handler(lua_State *L, void* ptr) {
+    (void)ptr;
     //LLOGD("l_uart_handler");
     rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
     lua_pop(L, 1);
@@ -47,6 +57,9 @@ int l_uart_handler(lua_State *L, void* ptr) {
         }
     }
     else {
+        if (uart_app_recvs[uart_id]) {
+            uart_app_recvs[uart_id](uart_id, msg->arg2);
+        }
         if (uart_cbs[uart_id].received) {
             lua_geti(L, LUA_REGISTRYINDEX, uart_cbs[uart_id].received);
             if (lua_isfunction(L, -1)) {

+ 16 - 2
luat/modules/luat_main.c

@@ -16,6 +16,10 @@
 #include "luat_errdump.h"
 #endif
 
+#ifdef LUAT_USE_PROFILER
+#include "luat_profiler.h"
+#endif
+
 static int report (lua_State *L, int status);
 
 lua_State *L;
@@ -109,9 +113,15 @@ static void l_message (const char *pname, const char *msg) {
 ** is a string, as it was either generated by Lua or by 'msghandler'.
 */
 static int report (lua_State *L, int status) {
+  size_t len = 0;
   if (status != LUA_OK) {
-    const char *msg = lua_tostring(L, -1);
-    l_message("LUAT", msg);
+    const char *msg = lua_tolstring(L, -1, &len);
+    //luaL_traceback(L, L, msg, 1);
+    //msg = lua_tolstring(L, -1, &len);
+    LLOGE("Luat: ");
+    LLOGE("%s", msg);
+    // LLOGD("MSG2 ==> %s %d", msg, len);
+    // l_message("LUAT", msg);
 #ifdef LUAT_USE_ERRDUMP
     luat_errdump_save_file(msg, strlen(msg));
 #endif
@@ -131,7 +141,11 @@ int luat_main_call(void) {
   // 4. init Lua State
   int status = 0;
   int result = 0;
+#ifdef LUAT_USE_PROFILER
+  L = lua_newstate(luat_profiler_alloc, NULL);
+#else
   L = lua_newstate(luat_heap_alloc, NULL);
+#endif
   if (L == NULL) {
     l_message("lua", "cannot create state: not enough memory\n");
     goto _exit;

+ 6 - 1
tools/make_doc_file.py

@@ -12,7 +12,12 @@ bsp_header_list = [
 print("getting bsp.h files...")
 for bsp in bsp_header_list:
     print("getting "+bsp["name"]+"...")
-    bsp["url"] = requests.get(bsp["url"]).text
+    res = ""
+    #有时候获取不到完整的数据,看看啥原因,后需要改 todo
+    while len(res) < 200:
+        print(res)
+        res = requests.get(bsp["url"]).text
+    bsp["url"] = res
     print("done "+ str(len(bsp["url"])) + " bytes")
 
 def get_tags(tag, is_api = False):