Explorar o código

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

Wendal Chen hai 9 meses
pai
achega
7681dacd76

+ 19 - 9
README.md

@@ -3,25 +3,35 @@
 [![fork](https://gitee.com/openLuat/LuatOS/badge/fork.svg?theme=gvp)](https://gitee.com/openLuat/LuatOS/members)
 [![license](https://img.shields.io/github/license/openLuat/LuatOS)](/LICENSE)
 
-LuatOS : Powerful embedded Lua Engine for IoT devices, with many components and low memory requirements (16K RAM, 128K Flash)
+LuatOS,基于强大的Lua引擎架构设计,Lua脚本开发,快速实现业务逻辑;
+
+LuatOS,在合宙历史上经历了两个阶段,一个是LuatOS-Air,一个是LuatOS-SoC,也就是现在的LuatOS;
+
+LuatOS-Air,典型硬件为Air724UG,为合宙上一代的产品;
+
+LuatOS,也就是曾经命名过的LuatOS-SoC,是合宙现在主推的开发方式,所支持的硬件如下:
+
+Air8000系列,包括Air8000/Air8000W/Air8000G/Air8000T/Air8000A/Air8000D等;
+
+Air8101系列,包括Air8101/Air8101A等;
+
+Air780Exx系列,包括Air780EPM/Air780EHM/Air780EHV/Air780EGH等;
+
+**LuatOS,现已支持74个核心库,55个扩展卡,1000余个API,100多个基于场景的demo,让你开发智能设备像做PPT一样轻松。**
 
-强大的Lua引擎, 为mcu和物联网设备高度优化, 支持众多组件, 非常低的内存需求(最低16K RAM, 128K Flash).
 
 ## 快速入门
 
 1. 使用或购买支持的开发板
-    * [Air780EPM-4G Cat.1开发板](https://luat.taobao.com)
-    * [Air8000引擎 开发板](https://luat.taobao.com)
-    * [Air8101引擎 开发板](https://luat.taobao.com)
+    * [合宙官方淘宝店](https://luat.taobao.com)
     * [PC - 无需开发板的模拟器](https://gitee.com/openLuat/luatos-soc-pc)
-2. 掌握[刷机](https://wiki.luatos.com/boardGuide/flash.html)
-3. 尝试[各种demo](https://gitee.com/openLuat/LuatOS/tree/master/demo), 浏览[API](https://wiki.luatos.com/api/index.html), [30分钟入门lua语法(视频)](https://www.bilibili.com/video/BV1vf4y1L7Rb?spm_id_from=333.999.0.0)
+2. 掌握[刷机](https://docs.openluat.com/air780epm/common/Luatools/)
+3. 尝试[学习LuatOS](https://docs.openluat.com/osapi/luatos_framework/), 浏览[API](https://docs.openluat.com/osapi/), [30分钟入门lua语法(视频)](https://docs.openluat.com/air780epm/product/video/)
 4. 愉快地写业务代码
 
 ## 资料大全
 
-* [wiki@luatos](https://wiki.luatos.com)
-* [LuatOS文档池](https://gitee.com/openLuat/luatos-doc-pool)
+* [docs@luatos](https://docs.openluat.com/osapi/luatos_framework/)
 
 ## 授权协议
 

+ 1 - 1
components/airlink/src/exec/luat_airlink_cmd_exec_gpio.c

@@ -88,11 +88,11 @@ int luat_airlink_cmd_exec_gpio_driver_yhm27xx_reqinfo(luat_airlink_cmd_t* cmd, v
     uint8_t pin = cmd->data[8];
     uint8_t chip_id = cmd->data[9];
     uint8_t params[9] = {0};
+    if(pin >= 128)  pin -= 128;
     for (uint8_t i = 0; i < 9; i++)
     {
         luat_gpio_driver_yhm27xx(pin, chip_id, i, 1, &(params[i]));
     }
-    
     // 反馈数据, 走sys_pub
     uint8_t buff[256] = {0};
     int ret = 0;

+ 4 - 1
components/bluetooth/include/luat_ble.h

@@ -163,7 +163,7 @@ typedef struct{
 typedef struct{
     uint8_t actv_idx;     /**< The index of the activity */
     uint8_t evt_type;     /**< Event type (see enum \ref adv_report_info and see enum \ref adv_report_type)*/
-    uint8_t adv_addr_type;/**< Advertising address type: public/random */
+    uint8_t adv_addr_type;  /**< Advertising address type: public/random */
     int8_t rssi;         /**< RSSI value for advertising packet (in dBm, between -127 and +20 dBm) */
     uint8_t *data;        /**< Data of advertising packet */
     uint8_t data_len;     /**< Data length in advertising packet */
@@ -239,6 +239,7 @@ typedef struct {
 }luat_ble_scan_cfg_t;
 
 struct luat_ble{
+    uint8_t actv_idx;
     luat_ble_actv_state state;
     luat_ble_cb_t cb;
     int lua_cb;
@@ -287,6 +288,8 @@ int luat_ble_stop_scanning(luat_ble_t* luat_ble);
 
 int luat_ble_delete_scanning(luat_ble_t* luat_ble);
 
+int luat_ble_connect(luat_ble_t* luat_ble, uint8_t* adv_addr,uint8_t adv_addr_type);
 
+int luat_ble_disconnect(luat_ble_t* luat_ble, uint8_t conn_idx);
 
 #endif

+ 4 - 0
components/bluetooth/include/luat_bluetooth.h

@@ -7,6 +7,8 @@
 #define LUAT_BLUETOOTH_TYPE "BLUETOOTH*"
 #define LUAT_BLE_TYPE "BLE*"
 
+#define LUAT_BLUETOOTH_MAC_LEN    6
+
 typedef struct luat_bluetooth{
     luat_ble_t* luat_ble;
     luat_bt_t* luat_bt;
@@ -21,4 +23,6 @@ int luat_bluetooth_deinit(luat_bluetooth_t* luat_bluetooth);
 int luat_bluetooth_get_mac(luat_bluetooth_t* luat_bluetooth, uint8_t *addr);
 int luat_bluetooth_set_mac(luat_bluetooth_t* luat_bluetooth, uint8_t *addr, uint8_t len);
 
+void luat_bluetooth_mac_swap(uint8_t* mac);
+
 #endif

+ 1 - 4
components/bluetooth/src/luat_ble.c

@@ -1,6 +1,6 @@
 #include "luat_base.h"
 #include "luat_mem.h"
-#include "luat_ble.h"
+#include "luat_bluetooth.h"
 
 #include "luat_log.h"
 #define LUAT_LOG_TAG "ble"
@@ -26,6 +26,3 @@ int luat_ble_uuid_swap(uint8_t* uuid_data, luat_ble_uuid_type uuid_type){
 }
 
 
-
-
-

+ 18 - 0
components/bluetooth/src/luat_bluetooth.c

@@ -0,0 +1,18 @@
+#include "luat_base.h"
+#include "luat_mem.h"
+#include "luat_bluetooth.h"
+
+#include "luat_log.h"
+#define LUAT_LOG_TAG "bluetooth"
+
+void luat_bluetooth_mac_swap(uint8_t* mac){
+    uint8_t len = 0;
+    uint8_t tmp_mac[LUAT_BLUETOOTH_MAC_LEN] = {0};
+    memcpy(tmp_mac, mac, LUAT_BLUETOOTH_MAC_LEN);
+    for(int i=0;i<LUAT_BLUETOOTH_MAC_LEN;i++){
+        mac[i] = tmp_mac[LUAT_BLUETOOTH_MAC_LEN-1-i];
+    }
+}
+
+
+

+ 1 - 1
components/bluetooth/src/luat_bt.c

@@ -1,6 +1,6 @@
 #include "luat_base.h"
 #include "luat_mem.h"
-#include "luat_bt.h"
+#include "luat_bluetooth.h"
 
 #include "luat_log.h"
 #define LUAT_LOG_TAG "bt"

+ 25 - 1
components/bluetooth/src/luat_lib_ble.c

@@ -70,6 +70,9 @@ static int luatos_ble_callback(lua_State *L, void* ptr){
             lua_pushliteral(L, "rssi"); 
             lua_pushinteger(L, adv_req->rssi);
             lua_settable(L, -3);
+            lua_pushliteral(L, "addr_type"); 
+            lua_pushinteger(L, adv_req->adv_addr_type);
+            lua_settable(L, -3);
             lua_pushliteral(L, "adv_addr"); 
             lua_pushlstring(L, (const char *)adv_req->adv_addr, 6);
             lua_settable(L, -3);
@@ -78,7 +81,6 @@ static int luatos_ble_callback(lua_State *L, void* ptr){
             lua_settable(L, -3);
     // uint8_t actv_idx;     /**< The index of the activity */
     // uint8_t evt_type;     /**< Event type (see enum \ref adv_report_info and see enum \ref adv_report_type)*/
-    // uint8_t adv_addr_type;/**< Advertising address type: public/random */
 
             lua_call(L, 3, 0);
             if (adv_req->data){
@@ -452,6 +454,25 @@ static int l_ble_scanning_stop(lua_State* L) {
     return 1;
 }
 
+static int l_ble_connect(lua_State* L) {
+    luat_ble_t* luat_ble = (luat_ble_t *)luaL_checkudata(L, 1, LUAT_BLE_TYPE);
+    size_t len;
+    uint8_t* adv_addr = luaL_checklstring(L, 2, &len);
+    uint8_t adv_addr_type = luaL_checknumber(L, 3);
+    LLOGD(" adv_addr_type:%d, adv_addr:%02x:%02x:%02x:%02x:%02x:%02x",
+        adv_addr_type, adv_addr[0], adv_addr[1], adv_addr[2],
+        adv_addr[3], adv_addr[4], adv_addr[5]);
+    lua_pushboolean(L, luat_ble_connect(luat_ble, adv_addr, adv_addr_type)?0:1);
+    return 1;
+}
+
+static int l_ble_disconnect(lua_State* L) {
+    luat_ble_t* luat_ble = (luat_ble_t *)luaL_checkudata(L, 1, LUAT_BLE_TYPE);
+    uint8_t conn_idx = luaL_checknumber(L, 2);
+    lua_pushboolean(L, luat_ble_disconnect(luat_ble, conn_idx)?0:1);
+    return 1;
+}
+
 static int _ble_struct_newindex(lua_State *L);
 
 void luat_ble_struct_init(lua_State *L) {
@@ -477,6 +498,9 @@ static const rotable_Reg_t reg_ble[] = {
     {"scan_start",                  ROREG_FUNC(l_ble_scanning_start)},
     {"scan_stop",                   ROREG_FUNC(l_ble_scanning_stop)},
 
+    {"connect",                   ROREG_FUNC(l_ble_connect)},
+    {"disconnect",                   ROREG_FUNC(l_ble_disconnect)},
+
     // BLE_EVENT
     {"EVENT_NONE",                  ROREG_INT(LUAT_BLE_EVENT_NONE)},
     {"EVENT_INIT",                  ROREG_INT(LUAT_BLE_EVENT_INIT)},

+ 0 - 70
luat/demo/2712/main.lua

@@ -1,70 +0,0 @@
-
--- LuaTools需要PROJECT和VERSION这两个信息
-PROJECT = "2712_demo"
-VERSION = "1.0.0"
-
--- sys库是标配
-_G.sys = require("sys")
-
---[[
-充电IC的相关逻辑
-]]
-
-local gpio_pin = 24
-gpio.setup(gpio_pin, 1, gpio.PULLUP)
-sys.taskInit(function()
-    sys.wait(1000)
-    local result, data = sensor.yhm27xx(gpio_pin, 0x04, 0x08)
-    sys.wait(200)
-    log.info("yhm27xxx", result, data)
-    if result == true and data ~= nil then
-        log.info("yhm27xxx", "yhm27xx存在--")
-
-        sys.wait(200)
-        result, data = sensor.yhm27xx(gpio_pin, 0x04, 0x00)
-        log.info("yhm27xxx 0x00 读取数据为:" , data, result)
-
-        -- 写入V_CTRL寄存器 设置成 4.25v
-        result = sensor.yhm27xx(gpio_pin, 0x04, 0x00, 0x20)
-        if result == true then
-            sys.wait(200)
-            result, data = sensor.yhm27xx(gpio_pin, 0x04, 0x00)
-            log.info("yhm27xxx 写入V_CTRL成功:" , data, result)
-        else
-            log.info("yhm27xxx", "写入V_CTRL失败, ", result)
-        end
-
-        sys.wait(200)
-        result, data = sensor.yhm27xx(gpio_pin, 0x04, 0x01)
-        log.info("yhm27xxx 0x01 读取数据为:" , data, result)
-        sys.wait(200)
-        result, data = sensor.yhm27xx(gpio_pin, 0x04, 0x02)
-        log.info("yhm27xxx 0x02 读取数据为:" , data, result)
-        sys.wait(200)
-        result, data = sensor.yhm27xx(gpio_pin, 0x04, 0x03)
-        log.info("yhm27xxx 0x03 读取数据为:" , data, result)
-        sys.wait(200)
-        result, data = sensor.yhm27xx(gpio_pin, 0x04, 0x04)
-        log.info("yhm27xxx 0x04 读取数据为:" , data, result)
-        sys.wait(200)
-        result, data = sensor.yhm27xx(gpio_pin, 0x04, 0x05)
-        log.info("yhm27xxx 0x05 读取数据为:" , data, result)
-        sys.wait(200)
-        result, data = sensor.yhm27xx(gpio_pin, 0x04, 0x06)
-        log.info("yhm27xxx 0x06 读取数据为:" , data, result)
-        sys.wait(200)
-        result, data = sensor.yhm27xx(gpio_pin, 0x04, 0x07)
-        log.info("yhm27xxx 0x07 读取数据为:" , data, result)
-        sys.wait(200)
-        result, data = sensor.yhm27xx(gpio_pin, 0x04, 0x08)
-        log.info("yhm27xxx 0x08 读取数据为:" , data, result)
-
-    else
-        log.warn("yhm27xxx", "yhm27xx不存在")
-    end
-end)
-
--- 用户代码已结束---------------------------------------------
--- 结尾总是这一句
-sys.run()
--- sys.run()之后后面不要加任何语句!!!!!

+ 68 - 83
luat/demo/yhm27xx/main.lua

@@ -6,106 +6,91 @@ VERSION = "1.0.0"
 sys = require("sys")
 log.info("main", PROJECT, VERSION)
 
---[[
-充电IC的相关逻辑
-]] local gpio_pin = 15 -- 
+local gpio_pin = 152 
 -- gpio.setup(gpio_pin, 1, gpio.PULLUP)
+local sensor_addr = 0x04
+--电压控制寄存器地址
+local V_ctrl_register = 0x00
+--电流控制寄存器地址
+local I_ctrl_register = 0x01
+--模式寄存器地址
+local mode_register = 0x02
+--配置寄存器,默认为0x00
+local config_register = 0x03
+-------------------注意:0x04寄存器无含义
+--状态寄存器
+local status1_register = 0x05   --只读
+local status2_register = 0x06   --只读
+local status3_register = 0x07   --只读
+--id寄存器
+local id_register = 0x08        --只读
+--充电电压常用参数,默认门限电压为4.35V
+local set_4V = 0xE0         --4V
+local set_4V25 = 0x20       --4.25V
+local set_4V35 = 0x60       --4.35V
+local set_4V45 = 0xA0       --4.45V
+--充电电流常用参数,默认充电电流为0.5倍yhm27xx芯片SNS管脚的电流
+local set_0I5 = 0x00        --0.5倍
+local set_0I7 = 0x40        --0.7倍
+local set_I = 0x80          --1倍
+local set_1I5 = 0xA0        --1.5倍
+local set_2I = 0xC0         --2倍
+
 sys.taskInit(function()
     sys.wait(1000)
-    local result, data = yhm27xx.cmd(gpio_pin, 0x04, 0x08)
-    sys.wait(200)
-    log.info("yhm27xxx", result, data)
+    local result, data = yhm27xx.cmd(gpio_pin, sensor_addr, id_register)
+    -- log.info("yhm27xxx", result, data)
     if result == true and data ~= nil then
         log.info("yhm27xxx", "yhm27xx存在--")
+
         sys.wait(200)
-        result = yhm27xx.cmd(gpio_pin, 0x04, 0x01, 0x02)
+        --设置充电电压为4V
+        result,data = sensor.yhm27xx(gpio_pin, sensor_addr, V_ctrl_register, set_4V)
         if result == true then
-            result, data = yhm27xx.cmd(gpio_pin, 0x04, 0x01)
-            if data ~= 2 then
-                log.info("yhm27xxx", "写入失败", data)
-            else
-                log.info("yhm27xxx", "测试成功", data)
-            end
-        else
-            log.info("yhm27xxx", "读取失败", result)
-        end
-
-        log.info("开始读所有寄存器的值")
-        table_reg = {0x01, 0x02, 0x03, 0x04, 0x05, 0x05, 0x06, 0x07, 0x08}
-
-        local result, data = yhm27xx.cmd(gpio_pin, 0x04, 0x00)
-        if result then
-            log.info("00寄存器的值", string.format("Value: 0x%02X", data))
-        else
-            log.info("00寄存器没读到")
-        end
-        local result, data = yhm27xx.cmd(gpio_pin, 0x04, 0x01)
-        if result then
-            log.info("01寄存器的值", string.format("Value: 0x%02X", data))
-        else
-            log.info("01寄存器没读到")
-        end
-        local result, data = yhm27xx.cmd(gpio_pin, 0x04, 0x02)
-        if result then
-            log.info("02寄存器的值", string.format("Value: 0x%02X", data))
-        else
-            log.info("02寄存器没读到")
-        end
-        local result, data = yhm27xx.cmd(gpio_pin, 0x04, 0x03)
-        if result then
-            log.info("03寄存器的值", string.format("Value: 0x%02X", data))
-        else
-            log.info("03寄存器没读到")
-        end
-        local result, data = yhm27xx.cmd(gpio_pin, 0x04, 0x04)
-        if result then
-            log.info("04寄存器的值", string.format("Value: 0x%02X", data))
-        else
-            log.info("04寄存器没读到")
-        end
-        local result, data = yhm27xx.cmd(gpio_pin, 0x04, 0x05)
-        if result then
-            log.info("05寄存器的值", string.format("Value: 0x%02X", data))
-        else
-            log.info("05寄存器没读到")
-        end
-        local result, data = yhm27xx.cmd(gpio_pin, 0x04, 0x06)
-        if result then
-            log.info("06寄存器的值", string.format("Value: 0x%02X", data))
-        else
-            log.info("06寄存器没读到")
-        end
-        local result, data = yhm27xx.cmd(gpio_pin, 0x04, 0x07)
-        if result then
-            log.info("07寄存器的值", string.format("Value: 0x%02X", data))
+            log.info("yhm27xxx 设置电压成功")
         else
-            log.info("07寄存器没读到")
+            log.info("yhm27xxx 设置电压失败")
         end
-        local result, data = yhm27xx.cmd(gpio_pin, 0x04, 0x08)
-        if result then
-            log.info("08寄存器的值", string.format("Value: 0x%02X", data))
-        else
-            log.info("08寄存器没读到")
-        end
-        log.info("等待10S后修改01寄存器的值为0x07<<5")
-        sys.wait(10*1000)
-        result = yhm27xx.cmd(gpio_pin, 0x04, 0x01, 0x07 << 5)
+        sys.wait(200)
+        --充电电流设置为1倍
+        result,data = yhm27xx.cmd(gpio_pin, sensor_addr, I_ctrl_register, set_I)
 
         if result == true then
-            log.info("修改01寄存器成功")
-            local result, data = yhm27xx.cmd(gpio_pin, 0x04, 0x01)
-            log.info("让我看看现在01寄存器的值", string.format("Value: 0x%02X", data))
+            log.info("yhm27xxx 设置电流成功")
         else
-            log.info("修改01寄存器失败")
+            log.info("yhm27xxx 设置电流失败")
         end
-        log.info("等待10s后修改")
-        sys.wait(10 * 1000)
+
+        log.info("开始读所有寄存器的值")
+        yhm27xx.reqinfo(gpio_pin, sensor_addr)
+        sys.subscribe("YHM27XX_REG", function(data)
+            -- log.info("yhm27xx", data and data:toHex())
+            if data then
+                Data_reg00 = data:byte(1)
+                Data_reg01 = data:byte(2)
+                Data_reg02 = data:byte(3)
+                Data_reg03 = data:byte(4)
+                Data_reg04 = data:byte(5)
+                Data_reg05 = data:byte(6)
+                Data_reg06 = data:byte(7)
+                Data_reg07 = data:byte(8)
+                Data_reg08 = data:byte(9)
+                log.info("yhm27xxx 0x00 读取数据为:" , Data_reg00)
+                log.info("yhm27xxx 0x01 读取数据为:" , Data_reg01)
+                log.info("yhm27xxx 0x02 读取数据为:" , Data_reg02)
+                log.info("yhm27xxx 0x03 读取数据为:" , Data_reg03)
+                log.info("yhm27xxx 0x04(无含义) 读取数据为:" , Data_reg04)
+                log.info("yhm27xxx 0x05 读取数据为:" , Data_reg05)
+                log.info("yhm27xxx 0x06 读取数据为:" , Data_reg06)
+                log.info("yhm27xxx 0x07 读取数据为:" , Data_reg07)
+                log.info("yhm27xxx 0x08 读取数据为:" , Data_reg08)
+
+            end
+        end)
 
     else
         log.warn("yhm27xx", "yhm27xx不存在")
     end
 end)
 
--- result = yhm27xx.cmd(gpio_pin, 0x04, 0x00, 0x08)
-
 sys.run()

+ 3 - 11
luat/modules/luat_lib_sensor.c

@@ -973,24 +973,16 @@ static int l_sensor_yhm27xx(lua_State *L)
     is_read = 0;
     data = luaL_checkinteger(L, 4);
   }
-  if(pin >= 128)
-  {
+
     #ifdef LUAT_USE_DRV_GPIO
     if(luat_drv_gpio_driver_yhm27xx(pin, chip_id, reg, is_read, &data))
-    {
-      lua_pushboolean(L, 0);
-      return 1;
-    }
-    #endif
-  }
-  else
-  {
+    #else
     if(luat_gpio_driver_yhm27xx(pin, chip_id, reg, is_read, &data))
+    #endif
     {
       lua_pushboolean(L, 0);
       return 1;
     }
-  }
   lua_pushboolean(L, 1);
   if (is_read)
   {

+ 101 - 0
module/Air780EGH/demo/fota2/main.lua

@@ -0,0 +1,101 @@
+
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "fotademo"
+-- iot限制,只能上传xxx.yyy.zzz格式的三位数的版本号,但实际上现在只用了XXX和ZZZ,中间yyy暂未使用
+-- 需要注意的是,因为yyy不生效,所以111.222.333版本和111.444.333版本,对iot平台来说都一样,所以建议中间那一位永远写000
+VERSION = "001.000.000"
+
+-- 使用合宙iot平台时需要这个参数
+PRODUCT_KEY = "123" -- 到 iot.openluat.com 创建项目,获取正确的项目id
+
+sys = require "sys"
+libfota2 = require "libfota2"
+
+-- 联网函数, 可自行删减
+sys.taskInit(function()
+    -- 默认都等到联网成功
+    sys.waitUntil("IP_READY")
+    log.info("4G网络链接成功")
+    sys.publish("net_ready")
+end)
+
+-- 循环打印版本号, 方便看版本号变化, 非必须
+sys.taskInit(function()
+    while 1 do
+        sys.wait(5000)
+        log.info("降功耗 找合宙")
+        -- log.info("fota", "脚本版本号", VERSION)
+        log.info("fota", "脚本版本号", VERSION, "core版本号", rtos.version())
+    end
+end)
+
+-- 升级结果的回调函数
+-- 功能:获取fota的回调函数
+-- 参数:
+-- result:number类型
+--   0表示成功
+--   1表示连接失败
+--   2表示url错误
+--   3表示服务器断开
+--   4表示接收报文错误
+--   5表示使用iot平台VERSION需要使用 xxx.yyy.zzz形式
+local function fota_cb(ret)
+    log.info("fota", ret)
+    if ret == 0 then
+        log.info("升级包下载成功,重启模块")
+        rtos.reboot()
+    elseif ret == 1 then
+        log.info("连接失败", "请检查url拼写或服务器配置(是否为内网)")
+    elseif ret == 2 then
+        log.info("url错误", "检查url拼写")
+    elseif ret == 3 then
+        log.info("服务器断开", "检查服务器白名单配置")
+    elseif ret == 4 then
+        log.info("接收报文错误", "检查模块固件或升级包内文件是否正常")
+    elseif ret == 5 then
+        log.info("版本号书写错误", "iot平台版本号需要使用xxx.yyy.zzz形式")
+    else
+        log.info("不是上面几种情况 ret为", ret)
+    end
+end
+
+local ota_opts = {}
+
+-- 使用合宙iot平台进行升级,不需要管下面这段代码
+-- 使用第三方服务器时打开下面这段代码
+--[[local ota_opts = {
+    url = "", 
+    -- 合宙IOT平台的默认升级URL, 不填就是这个默认值
+    -- 如果是自建的OTA服务器, 则需要填写正确的URL, 例如 http://192.168.1.5:8000/update
+    -- 如果自建OTA服务器,且url包含全部参数,不需要额外添加参数, 请在url前面添加 ### 
+    -- 如果不加###,则默认会上传如下参数
+    -- 1. opts.version string 版本号, 默认是 BSP版本号.x.z格式
+    -- 2. opts.timeout int 请求超时时间, 默认300000毫秒,单位毫秒
+    -- 3. opts.project_key string 合宙IOT平台的项目key, 默认取全局变量PRODUCT_KEY. 自建服务器不用填
+    -- 4. opts.imei string 设备识别码, 默认取IMEI(Cat.1模块)或WLAN MAC地址(wifi模块)或MCU唯一ID
+    -- 5. opts.firmware_name string 底层版本号
+    -- 请求的版本号, 合宙IOT有一套版本号体系,不传就是合宙规则, 自建服务器的话当然是自行约定版本号了
+    version = ""
+    -- 其他更多参数, 请查阅libfota2的文档 https://wiki.luatos.com/api/libs/libfota2.html
+}]]--
+sys.taskInit(function()
+    -- 这个判断是提醒要设置PRODUCT_KEY的,实际生产请删除
+    if "123" == _G.PRODUCT_KEY and not ota_opts.url then
+        while 1 do
+            sys.wait(1000)
+            log.info("fota", "请修改正确的PRODUCT_KEY")
+        end
+    end
+    -- 等待网络就行后开始检查升级
+    sys.waitUntil("net_ready")
+    log.info("开始检查升级")
+    sys.wait(500)
+    libfota2.request(fota_cb, ota_opts)
+end)
+-- 演示定时自动升级, 每隔4小时自动检查一次
+sys.timerLoopStart(libfota2.request, 4 * 3600000, fota_cb, ota_opts)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 0 - 0
module/Air780EHM/.keep


+ 140 - 0
module/Air8000/demo/modbus/master_rtu/main.lua

@@ -0,0 +1,140 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "modbus_master_rtu"
+VERSION = "1.0.0"
+log.style(1)
+log.info("main", PROJECT, VERSION)
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+
+
+--初始化通讯串口
+local uartid = 1        -- 根据实际设备选取不同的uartid
+local uart485Pin = 17   -- 用于控制485接收和发送的使能引脚
+gpio.setup(16, 1)        --打开电源(开发板485供电脚是gpio16,用开发板测试需要开机初始化拉高gpio16)
+uart.setup(uartid, 115200, 8, 1, uart.NONE, uart.LSB, 1024, uart485Pin, 0, 2000)
+
+
+-- 创建主站设备,RTU模式
+mb_rtu = modbus.create_master(modbus.MODBUS_RTU, uartid,5000,3000,2)
+-- 创建主站设备,ASCII模式
+-- mb_rtu = modbus.create_master(modbus.MODBUS_ASCII, uartid)
+
+-- 为主站添加从站,从站ID为1
+
+mb_slave1 = modbus.add_slave(mb_rtu, 1)
+-- 为主站添加从站,从站ID为2
+-- mb_slave2 = modbus.add_slave(mb_rtu, 2)
+
+-- 为从站1创建数据存储区,并创建通讯消息
+slave1_msg1_buf = zbuff.create(1)
+mb_slave1_msg1 = modbus.create_msg(mb_rtu, mb_slave1, modbus.REGISTERS, modbus.READ, 0, 10, slave1_msg1_buf)
+-- mb_slave1_msg1 = modbus.create_msg(mb_rtu, mb_slave1, modbus.REGISTERS, modbus.READ, 0, 10, slave1_msg1_buf,1,modbus.EXEC)
+slave1_msg1_buf:clear()
+
+
+
+
+
+-- slave1_msg2_buf = zbuff.create(1)
+-- mb_slave1_msg2 = modbus.create_msg(mb_rtu, mb_slave1, modbus.CIOLS, modbus.WRITE, 0, 10, slave1_msg2_buf)
+-- slave1_msg2_buf:clear()
+
+-- 为从站2创建数据存储区,并创建通讯消息
+-- slave2_msg1_buf = zbuff.create(1)
+-- mb_slave2_msg1 = modbus.create_msg(mb_rtu, mb_slave2, modbus.REGISTERS, modbus.WRITE, 0, 10, slave2_msg1_buf)
+-- slave2_msg1_buf:clear()
+
+-- slave2_msg2_buf = zbuff.create(1)
+-- mb_slave2_msg2 = modbus.create_msg(mb_rtu, mb_slave2, modbus.CIOLS, modbus.WRITE, 0, 10, slave2_msg2_buf)
+-- slave2_msg2_buf:clear()
+
+-- 启动Modubs设备
+modbus.master_start(mb_rtu)
+
+-- modbus.set_comm_interval_time(mb_rtu, 5000)
+
+-- modbus.set_comm_timeout(mb_rtu, 3000)
+
+-- modbus.set_comm_resend_count(mb_rtu,2)
+
+sys.timerLoopStart(function()
+    local status = modbus.get_all_slave_state(mb_rtu)
+    log.info("modbus", status)
+end, 5000)
+
+sys.timerLoopStart(function()
+    local status = modbus.get_slave_state(mb_slave1)
+    log.info("modbus1", status)
+end, 5000)
+
+-- sys.timerLoopStart(function()
+--     local status = modbus.get_slave_state(mb_slave2)
+--     log.info("modbus2", status)
+-- end, 5000)
+
+
+-- sys.timerLoopStart(function()
+--     local status=modbus.exec(mb_rtu, mb_slave1_msg1)
+--     log.info("msg",status)
+-- end,5000)
+
+-- modbus.set_msg_comm_period(mb_slave1_msg1, 2)
+
+-- sys.timerStart(function()
+--     local status = modbus.remove_slave(mb_rtu, mb_slave1)
+--     log.info("modbus", "slave remove after 3 minutes")
+--     log.info("remove", status)
+    
+--     -- 移除从站后,5秒后重新启动Modbus主站
+--     sys.timerStart(function()
+--         modbus.master_start(mb_rtu)
+--         log.info("modbus", "Modbus master restarted after slave removal")
+--     end, 5000)
+-- end, 180000) 
+
+
+
+-- 修改和读取modbus值
+addvar = 0
+function modify_data()
+    -- 获取变量值
+    slave1_msg1_buf:seek(0)
+    log.info("slave1 reg: ", slave1_msg1_buf:readU16(), slave1_msg1_buf:readU16(),
+                             slave1_msg1_buf:readU16(), slave1_msg1_buf:readU16())
+
+    -- slave2_msg1_buf:seek(0)
+    -- log.info("slave2 reg: ", slave2_msg1_buf:readU16(), slave2_msg1_buf:readU16(),
+    --                          slave2_msg1_buf:readU16(), slave2_msg1_buf:readU16())
+
+    -- 写入变量值
+    -- slave2_msg1_buf:seek(0)
+    -- slave2_msg1_buf:writeU16(addvar)
+    -- slave2_msg1_buf:writeU16(addvar)
+    -- slave2_msg1_buf:writeU16(addvar)
+    -- slave2_msg1_buf:writeU16(addvar)
+    -- slave2_msg1_buf:writeU16(addvar)
+    -- slave2_msg1_buf:writeU16(addvar)
+    -- addvar=addvar+1
+	
+	-- 每周期值取反
+    -- slave1_msg2_buf:seek(0)
+	-- slave1_msg2_buf:writeU8(addvar%2)
+	-- slave1_msg2_buf:writeU8(addvar%2)
+	-- slave1_msg2_buf:writeU8(addvar%2)
+	-- slave1_msg2_buf:writeU8(addvar%2)
+	-- slave1_msg2_buf:writeU8(addvar%2)
+end
+sys.timerLoopStart(modify_data,1000)
+
+
+-- sys.timerStart(function()
+--     modbus.master_stop(mb_rtu)
+--     log.info("modbus", "Modbus stopped after 2 minutes")
+-- end, 120000) 
+
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 302 - 0
module/Air8000/demo/modbus/master_tcp/dhcpsrv.lua

@@ -0,0 +1,302 @@
+
+local dhcpsrv = {}
+
+local udpsrv = require("udpsrv")
+
+local TAG = "dhcpsrv"
+
+----
+-- 参考地址
+-- https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol
+
+local function dhcp_decode(buff)
+    -- buff:seek(0)
+    local dst = {}
+    -- 开始解析dhcp
+    dst.op = buff[0]
+    dst.htype = buff[1]
+    dst.hlen = buff[2]
+    dst.hops = buff[3]
+    buff:seek(4)
+    dst.xid = buff:read(4)
+
+    _, dst.secs = buff:unpack(">H")
+    _, dst.flags = buff:unpack(">H")
+    dst.ciaddr = buff:read(4)
+    dst.yiaddr = buff:read(4)
+    dst.siaddr = buff:read(4)
+    dst.giaddr = buff:read(4)
+    dst.chaddr = buff:read(16)
+
+    -- 跳过192字节
+    buff:seek(192, zbuff.SEEK_CUR)
+
+    -- 解析magic
+    _, dst.magic = buff:unpack(">I")
+
+    -- 解析option
+    local opt = {}
+    while buff:len() > buff:used() do
+        local tag = buff:read(1):byte()
+        if tag ~= 0 then
+            local len = buff:read(1):byte()
+            if tag == 0xFF or len == 0 then
+                break
+            end
+            local data = buff:read(len)
+            if tag == 53 then
+                -- 53: DHCP Message Type
+                dst.msgtype = data:byte()
+            end
+            table.insert(opt, {tag, data})
+            -- log.info(TAG, "tag", tag, "data", data:toHex())
+        end
+    end
+    if dst.msgtype == nil then
+        return -- 没有解析到msgtype,直接返回
+    end
+    dst.opts = opt
+    return dst
+end
+
+local function dhcp_buff2ip(buff)
+    return string.format("%d.%d.%d.%d", buff:byte(1), buff:byte(2), buff:byte(3), buff:byte(4))
+end
+
+local function dhcp_print_pkg(pkg)
+    log.info(TAG, "XID",  pkg.xid:toHex())
+    log.info(TAG, "secs", pkg.secs)
+    log.info(TAG, "flags", pkg.flags)
+    log.info(TAG, "chaddr", pkg.chaddr:sub(1, pkg.hlen):toHex())
+    log.info(TAG, "yiaddr", dhcp_buff2ip(pkg.yiaddr))
+    log.info(TAG, "siaddr", dhcp_buff2ip(pkg.siaddr))
+    log.info(TAG, "giaddr", dhcp_buff2ip(pkg.giaddr))
+    log.info(TAG, "ciaddr", dhcp_buff2ip(pkg.ciaddr))
+    log.info(TAG, "magic", string.format("%08X", pkg.magic))
+    for _, opt in pairs(pkg.opts) do
+        if opt[1] == 53 then
+            log.info(TAG, "msgtype", opt[2]:byte())
+        elseif opt[1] == 60 then
+            log.info(TAG, "auth", opt[2])
+        elseif opt[1] == 57 then
+            log.info(TAG, "Maximum DHCP message size", opt[2]:byte() * 256 + opt[2]:byte(2))
+        elseif opt[1] == 61 then
+            log.info(TAG, "Client-identifier", opt[2]:toHex())
+        elseif opt[1] == 55 then
+            log.info(TAG, "Parameter request list", opt[2]:toHex())
+        elseif opt[1] == 12 then
+            log.info(TAG, "Host name", opt[2])
+        -- elseif opt[1] == 58 then
+        --     log.info(TAG, "Renewal (T1) time value", opt[2]:unpack(">I"))
+        end
+    end
+end
+
+local function dhcp_encode(pkg, buff)
+    -- 合成DHCP包
+    buff:seek(0)
+    buff[0] = pkg.op
+    buff[1] = pkg.htype
+    buff[2] = pkg.hlen
+    buff[3] = pkg.hops
+    buff:seek(4)
+    -- 写入XID
+    buff:write(pkg.xid)
+    -- 几个重要的参数
+    buff:pack(">H", pkg.secs)
+    buff:pack(">H", pkg.flags)
+    buff:write(pkg.ciaddr)
+    buff:write(pkg.yiaddr)
+    buff:write(pkg.siaddr)
+    buff:write(pkg.giaddr)
+    -- 写入MAC地址
+    buff:write(pkg.chaddr)
+    -- 跳过192字节
+    buff:seek(192, zbuff.SEEK_CUR)
+    -- 写入magic
+    buff:pack(">I", pkg.magic)
+    -- 写入option
+    for _, opt in pairs(pkg.opts) do
+        buff:write(opt[1])
+        buff:write(#opt[2])
+        buff:write(opt[2])
+    end
+    buff:write(0xFF, 0x00)
+end
+
+----
+
+local function dhcp_send_x(srv, pkg, client, msgtype)
+    local buff = zbuff.create(300)
+    pkg.op = 2
+    pkg.ciaddr = "\0\0\0\0"
+    pkg.yiaddr = string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], client.ip)
+    pkg.siaddr = string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], srv.opts.gw[4])
+    pkg.giaddr = "\0\0\0\0"
+    pkg.secs = 0
+
+    pkg.opts = {} -- 复位option
+    table.insert(pkg.opts, {53, string.char(msgtype)})
+    table.insert(pkg.opts, {1, string.char(srv.opts.mark[1], srv.opts.mark[2], srv.opts.mark[3], srv.opts.mark[4])})
+    table.insert(pkg.opts, {3, string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], srv.opts.gw[4])})
+    table.insert(pkg.opts, {51, "\x00\x00\x1E\x00"}) -- 7200秒, 大概
+    table.insert(pkg.opts, {54, string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], srv.opts.gw[4])})
+    table.insert(pkg.opts, {6, string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], srv.opts.gw[4])})
+
+    dhcp_encode(pkg, buff)
+
+    local dst = "255.255.255.255"
+    if 4 == msgtype then
+        dst = string.format("%d.%d.%d.%d", srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], client.ip)
+    end
+    -- log.info(TAG, "发送", msgtype, dst, buff:query():toHex())
+    srv.udp:send(buff, dst, 68)
+end
+
+local function dhcp_send_offer(srv, pkg, client)
+    dhcp_send_x(srv, pkg, client, 2)
+end
+
+local function dhcp_send_ack(srv, pkg, client)
+    dhcp_send_x(srv, pkg, client, 5)
+end
+
+local function dhcp_handle_discover(srv, pkg)
+    local mac = pkg.chaddr:sub(1, pkg.hlen)
+    -- 看看是不是已经分配了ip
+    for _, client in pairs(srv.clients) do
+        if client.mac == mac then
+            log.info(TAG, "发现已经分配的mac地址, send offer")
+            dhcp_send_offer(srv, pkg, client)
+            return
+        end
+    end
+    -- TODO 清理已经过期的IP分配记录
+    -- 分配一个新的ip
+    if #srv.clients >= (srv.opts.ip_end - srv.opts.ip_start) then
+        log.info(TAG, "没有可分配的ip了")
+        return
+    end
+    local ip = nil
+    for i = srv.opts.ip_start, srv.opts.ip_end, 1 do
+        if srv.clients[i] == nil then
+            ip = i
+            break
+        end
+    end
+    if ip == nil then
+        log.info(TAG, "没有可分配的ip了")
+        return
+    end
+    log.info(TAG, "分配ip", mac:toHex(), string.format("%d.%d.%d.%d", srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], ip))
+    local client = {
+        mac = mac,
+        ip = ip,
+        tm = mcu.ticks() // mcu.hz(),
+        stat = 1
+    }
+    srv.clients[ip] = client
+    log.info(TAG, "send offer")
+    dhcp_send_offer(srv, pkg, client)
+end
+
+local function dhcp_handle_request(srv, pkg)
+    local mac = pkg.chaddr:sub(1, pkg.hlen)
+    -- 看看是不是已经分配了ip
+    for _, client in pairs(srv.clients) do
+        if client.mac == mac then
+            log.info(TAG, "request,发现已经分配的mac地址, send ack")
+            client.tm = mcu.ticks() // mcu.hz()
+            stat = 3
+            dhcp_send_ack(srv, pkg, client)
+            return
+        end
+    end
+end
+
+local function dhcp_pkg_handle(srv, pkg)
+    -- 进行基本的检查
+    if pkg.magic ~= 0x63825363 then
+        log.warn(TAG, "dhcp数据包的magic不对劲,忽略该数据包", pkg.magic)
+        return
+    end
+    if pkg.op ~= 1 then
+        log.info(TAG, "op不对,忽略该数据包", pkg.op)
+        return
+    end
+    if pkg.htype ~= 1 or pkg.hlen ~= 6 then
+        log.warn(TAG, "htype/hlen 不认识, 忽略该数据包")
+        return
+    end
+    -- 看看是不是能处理的类型, 当前只处理discover/request
+    if pkg.msgtype == 1 or pkg.msgtype == 3 then
+    else
+        log.warn(TAG, "msgtype不是discover/request, 忽略该数据包", pkg.msgtype)
+        return
+    end
+    -- 检查一下mac地址是否合法
+    local mac = pkg.chaddr:sub(1, pkg.hlen)
+    if mac == "\0\0\0\0\0\0" or mac == "\xFF\xFF\xFF\xFF\xFF\xFF" then
+        log.warn(TAG, "mac地址为空, 忽略该数据包")
+        return
+    end
+
+    -- 处理discover包
+    if pkg.msgtype == 1 then
+        log.info(TAG, "是discover包")
+        dhcp_handle_discover(srv, pkg)
+    elseif pkg.msgtype == 3 then
+        log.info(TAG, "是request包")
+        dhcp_handle_request(srv, pkg)
+    end
+    -- TODO 处理结束, 打印一下客户的列表?
+end
+
+local function dhcp_task(srv)
+    while 1 do
+        -- log.info("ulwip", "等待DHCP数据")
+        local result, data = sys.waitUntil(srv.udp_topic, 1000)
+        if result then
+            -- log.info("ulwip", "收到dhcp数据包", data:toHex())
+            -- 解析DHCP数据包
+            local pkg = dhcp_decode(zbuff.create(#data, data))
+            if pkg then
+                -- dhcp_print_pkg(pkg)
+                dhcp_pkg_handle(srv, pkg)
+            end
+        end
+    end
+end
+function dhcpsrv.create(opts)
+    local srv = {}
+    if not opts then
+        opts = {}
+    end
+    srv.udp_topic = "dhcpd_inc"
+    -- 补充参数
+    if not opts.mark then
+        opts.mark = {255, 255, 255, 0}
+    end
+    if not opts.gw then
+        opts.gw = {192, 168, 4, 1}
+    end
+    if not opts.dns then
+        opts.dns = opts.gw
+    end
+    if not opts.ip_start then
+        opts.ip_start = 100
+    end
+    if not opts.ip_end then
+        opts.ip_end = 200
+    end
+
+    srv.clients = {}
+    srv.opts = opts
+
+    srv.udp = udpsrv.create(67, srv.udp_topic, opts.adapter)
+    srv.task = sys.taskInit(dhcp_task, srv)
+    return srv
+end
+
+
+return dhcpsrv

+ 113 - 0
module/Air8000/demo/modbus/master_tcp/dnsproxy.lua

@@ -0,0 +1,113 @@
+--[[
+@module dnsproxy
+@summary DNS代理转发
+@version 1.0
+@date    2024.4.20
+@author  wendal
+@demo    socket
+@tag LUAT_USE_NETWORK
+@usage
+-- 具体用法请查阅demo
+]]
+
+local sys = require "sys"
+
+local dnsproxy = {}
+dnsproxy.map = {}
+dnsproxy.txid = 0x123
+dnsproxy.rxbuff = zbuff.create(1500)
+
+function dnsproxy.on_request(sc, event)
+    if event == socket.EVENT then
+        local rxbuff = dnsproxy.rxbuff
+        while 1 do
+            rxbuff:seek(0)
+            local succ, data_len, remote_ip, remote_port = socket.rx(sc, rxbuff)
+            if succ and data_len and data_len > 0 then
+                -- log.info("dnsproxy", "收到DNS查询数据", rxbuff:query():toHex())
+                if remote_ip and #remote_ip == 5 then
+                    local ip1,ip2,ip3,ip4 = remote_ip:byte(2),remote_ip:byte(3),remote_ip:byte(4),remote_ip:byte(5)
+                    remote_ip = string.format("%d.%d.%d.%d", ip1, ip2, ip3, ip4)
+                    local txid_request = rxbuff[0] + rxbuff[1] * 256
+                    local txid_map = dnsproxy.txid
+                    dnsproxy.txid = dnsproxy.txid + 1
+                    if dnsproxy.txid > 65000 then
+                        dnsproxy.txid = 0x123
+                    end
+                    table.insert(dnsproxy.map, {txid_request, txid_map, remote_ip, remote_port})
+                    rxbuff[0] = txid_map % 256
+                    rxbuff[1] = txid_map // 256
+                    socket.tx(dnsproxy.main_sc, rxbuff, "223.5.5.5", 53)
+                end
+            else
+                break
+            end
+        end
+    end
+end
+
+function dnsproxy.on_response(sc, event)
+    if event == socket.EVENT then
+        local rxbuff = dnsproxy.rxbuff
+        while 1 do
+            rxbuff:seek(0)
+            local succ, data_len = socket.rx(sc, rxbuff)
+            if succ and data_len and data_len > 0 then
+                if true then
+                    -- local ip1,ip2,ip3,ip4 = remote_ip:byte(2),remote_ip:byte(3),remote_ip:byte(4),remote_ip:byte(5)
+                    -- remote_ip = string.format("%d.%d.%d.%d", ip1, ip2, ip3, ip4)
+                    local txid_resp = rxbuff[0] + rxbuff[1] * 256
+                    local index = -1
+                    for i, mapit in pairs(dnsproxy.map) do
+                        if mapit[2] == txid_resp then
+                            local txid_request = mapit[1]
+                            local remote_ip = mapit[3]
+                            local remote_port = mapit[4]
+                            rxbuff[0] = txid_request % 256
+                            rxbuff[1] = txid_request // 256
+                            socket.tx(dnsproxy.sc, rxbuff, remote_ip, remote_port)
+                            index = i
+                            break
+                        end
+                    end
+                    if index > 0 then
+                        table.remove(dnsproxy.map, index)
+                    end
+                end
+            else
+                break
+            end
+        end
+    end
+end
+
+--[[
+创建UDP服务器
+@api dnsproxy.create(adapter, main_adapter)
+@int 监听的网络适配器id
+@int 网络适配编号, 默认为nil,可选
+@return table UDP服务的实体, 若创建失败会返回nil
+]]
+function dnsproxy.setup(adapter, main_adapter)
+    log.info("dnsproxy", adapter, main_adapter)
+    dnsproxy.adapter = adapter
+    dnsproxy.main_adapter = main_adapter
+    dnsproxy.sc = socket.create(dnsproxy.adapter, dnsproxy.on_request)
+    dnsproxy.main_sc = socket.create(dnsproxy.main_adapter, dnsproxy.on_response)
+    socket.config(dnsproxy.sc, 53, true)
+    socket.config(dnsproxy.main_sc, 1053, true)
+    dnsproxy.on_ip_ready()
+    return true
+end
+
+function dnsproxy.on_ip_ready()
+    socket.close(dnsproxy.sc)
+    socket.close(dnsproxy.main_sc)
+    log.info("dnsproxy", "开启DNS代理")
+    socket.connect(dnsproxy.sc, "255.255.255.255", 0)
+    socket.connect(dnsproxy.main_sc, "223.5.5.5", 53)
+end
+
+sys.subscribe("IP_READY", dnsproxy.on_ip_ready)
+
+return dnsproxy

+ 57 - 0
module/Air8000/demo/modbus/master_tcp/lan.lua

@@ -0,0 +1,57 @@
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+sysplus = require("sysplus")
+
+dhcps = require "dhcpsrv"
+dnsproxy = require "dnsproxy"
+
+sys.taskInit(function ()
+    -- sys.wait(3000)
+    local result = spi.setup(
+        0,--串口id
+        nil,
+        0,--CPHA
+        0,--CPOL
+        8,--数据宽度
+        51200000--,--频率
+        -- spi.MSB,--高低位顺序    可选,默认高位在前
+        -- spi.master,--主模式     可选,默认主
+        -- spi.full--全双工       可选,默认全双工
+    )
+    log.info("main", "open",result)
+    if result ~= 0 then--返回值为0,表示打开成功
+        log.info("main", "spi open error",result)
+        return
+    end
+
+    netdrv.setup(socket.LWIP_ETH, netdrv.CH390, {spi=0,cs=8})
+    sys.wait(3000)
+    local ipv4,mark, gw = netdrv.ipv4(socket.LWIP_ETH, "192.168.0.155", "255.255.255.0", "192.168.0.1")
+    log.info("ipv4", ipv4,mark, gw)
+    while netdrv.link(socket.LWIP_ETH) ~= true do
+        sys.wait(100)
+    end
+    while netdrv.link(socket.LWIP_GP) ~= true do
+        sys.wait(100)
+    end
+    dhcps.create({adapter=socket.LWIP_ETH})
+    dnsproxy.setup(socket.LWIP_ETH, socket.LWIP_GP)
+    netdrv.napt(socket.LWIP_GP)
+    if iperf then
+        log.info("启动iperf服务器端")
+        iperf.server(socket.LWIP_ETH)
+    end
+end)
+
+
+sys.taskInit(function()
+    -- sys.waitUntil("IP_READY")
+    while 1 do
+        sys.wait(300000)
+        -- log.info("http", http.request("GET", "http://httpbin.air32.cn/bytes/4096", nil, nil, {adapter=socket.LWIP_ETH}).wait())
+        log.info("lua", rtos.meminfo())
+        log.info("sys", rtos.meminfo("sys"))
+        -- log.info("psram", rtos.meminfo("psram"))
+    end
+end)

+ 125 - 0
module/Air8000/demo/modbus/master_tcp/main.lua

@@ -0,0 +1,125 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "modbus_master_tcp"
+VERSION = "1.0.0"
+log.style(1)
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+_G.sys = require("sys")
+_G.sysplus = require("sysplus")
+
+--初始化网络
+log.info("ch390", "打开LDO供电")
+gpio.setup(140, 1)  --打开开发板lan供电
+
+require "lan"
+
+-- 创建主站设备,TCP模式
+mb_tcp = modbus.create_master(modbus.MODBUS_TCP, socket.LWIP_ETH)
+
+-- 为主站添加从站,从站ID为1,ip地址为 192.168.0.104,端口号为 6000
+mb_slave1 = modbus.add_slave(mb_tcp, 1, "192.168.4.100", 6001)
+-- 为主站添加从站,从站ID为2,ip地址为 192.168.0.104,端口号为 6001
+-- mb_slave2 = modbus.add_slave(mb_tcp, 2, "192.168.4.100", 6002)
+
+-- 为从站1创建数据存储区,并创建通讯消息
+slave1_msg1_buf = zbuff.create(1)
+-- mb_slave1_msg1 = modbus.create_msg(mb_tcp, mb_slave1, modbus.REGISTERS, modbus.READ, 0, 10, slave1_msg1_buf,1,modbus.EXEC)
+mb_slave1_msg1 = modbus.create_msg(mb_tcp, mb_slave1, modbus.REGISTERS, modbus.READ, 0, 10, slave1_msg1_buf)
+slave1_msg1_buf:clear()
+
+-- slave1_msg2_buf = zbuff.create(1)
+-- mb_slave1_msg2 = modbus.create_msg(mb_tcp, mb_slave1, modbus.CIOLS, modbus.WRITE, 0, 10, slave1_msg2_buf)
+-- slave1_msg2_buf:clear()
+
+-- 为从站2创建数据存储区,并创建通讯消息
+-- slave2_msg1_buf = zbuff.create(1)
+-- mb_slave2_msg1 = modbus.create_msg(mb_tcp, mb_slave2, modbus.REGISTERS, modbus.WRITE, 0, 10, slave2_msg1_buf)
+-- slave2_msg1_buf:clear()
+
+-- 启动Modubs设备
+modbus.master_start(mb_tcp)
+log.info("start modbus master")
+
+sys.timerLoopStart(function()
+    local status = modbus.get_all_slave_state(mb_tcp)
+    log.info("modbus", status)
+end, 5000)
+
+sys.timerLoopStart(function()
+    local status = modbus.get_slave_state(mb_slave1)
+    log.info("modbus1", status)
+end, 5000)
+
+-- sys.timerLoopStart(function()
+--     local status = modbus.get_slave_state(mb_slave2)
+--     log.info("modbus2", status)
+-- end, 5000)
+
+-- modbus.set_comm_interval_time(mb_tcp,2000)
+
+-- modbus.set_comm_timeout(mb_tcp, 3000)
+
+-- modbus.set_comm_resend_count(mb_tcp,3)
+
+-- modbus.set_msg_comm_period(mb_slave1_msg1, 2)
+
+-- modbus.set_comm_reconnection_time(mb_tcp, 6000)
+
+-- sys.timerLoopStart(function()
+--     local status=modbus.exec(mb_tcp, mb_slave1_msg1)
+--     log.info("msg",status)
+-- end,5000)
+
+-- sys.timerStart(function()
+--     local status = modbus.remove_slave(mb_tcp, mb_slave2)
+--     log.info("modbus", "slave remove after 3 minutes")
+--     log.info("remove", status)
+    
+--     -- 移除从站后,5秒后重新启动Modbus主站
+--     sys.timerStart(function()
+--         modbus.master_start(mb_tcp)
+--         log.info("modbus", "Modbus master restarted after slave removal")
+--     end, 5000)
+-- end, 180000) 
+
+-- 修改和读取modbus值
+addvar = 0
+function modify_data()
+    -- 获取变量值
+    slave1_msg1_buf:seek(0)
+    log.info("slave1 reg: ", slave1_msg1_buf:readU16(), slave1_msg1_buf:readU16(),
+                             slave1_msg1_buf:readU16(), slave1_msg1_buf:readU16())
+    
+    -- slave2_msg1_buf:seek(0)
+    -- log.info("slave2 reg: ", slave2_msg1_buf:readU16(), slave2_msg1_buf:readU16(),
+    --                          slave2_msg1_buf:readU16(), slave2_msg1_buf:readU16())
+    -- 写入变量值
+    -- slave2_msg1_buf:seek(0)
+    -- slave2_msg1_buf:writeU16(addvar)
+    -- slave2_msg1_buf:writeU16(addvar)
+    -- slave2_msg1_buf:writeU16(addvar)
+    -- slave2_msg1_buf:writeU16(addvar)
+    -- slave2_msg1_buf:writeU16(addvar)
+    -- slave2_msg1_buf:writeU16(addvar)
+    -- addvar=addvar+1
+
+	-- 每周期值取反
+    -- slave1_msg2_buf:seek(0)
+	-- slave1_msg2_buf:writeU8(addvar%2)
+	-- slave1_msg2_buf:writeU8(addvar%2)
+	-- slave1_msg2_buf:writeU8(addvar%2)
+	-- slave1_msg2_buf:writeU8(addvar%2)
+	-- slave1_msg2_buf:writeU8(addvar%2)
+end
+sys.timerLoopStart(modify_data,1000)
+
+-- sys.timerStart(function()
+--     modbus.master_stop(mb_tcp)
+--     log.info("modbus", "Modbus stopped after 2 minutes")
+-- end, 120000) 
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 70 - 0
module/Air8000/demo/modbus/slave_rtu/main.lua

@@ -0,0 +1,70 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "modbus_slave_rtu"
+VERSION = "1.0.0"
+log.style(1)
+log.info("main", PROJECT, VERSION)
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+
+--初始化通讯串口
+local uartid = 1        -- 根据实际设备选取不同的uartid
+local uart485Pin = 17   -- 用于控制485接收和发送的使能引脚
+gpio.setup(16, 1)        --打开电源(开发板485供电脚是gpio16,用开发板测试需要开机初始化拉高gpio16)
+uart.setup(uartid, 9600, 8, 1, uart.NONE, uart.LSB, 1024, uart485Pin, 0, 2000)
+
+
+-- 创建从站设备
+local slave_id = 1
+mb_rtu_s = modbus.create_slave(modbus.MODBUS_RTU, slave_id, uartid)
+-- mb_rtu_s = modbus.create_slave(modbus.MODBUS_ASCII, slave_id, uartid)
+
+
+-- 创建寄存器数据区
+registers = zbuff.create(1)
+modbus.add_block(mb_rtu_s, modbus.REGISTERS, 0, 32, registers)
+registers:clear()
+
+-- 创建线圈数据区
+ciols = zbuff.create(1)
+modbus.add_block(mb_rtu_s, modbus.CIOLS, 0, 32, ciols)
+ciols:clear()
+
+-- 启动通讯
+modbus.slave_start(mb_rtu_s)
+
+local counter = 0
+
+-- 修改和读取modbus值
+function modify_data()
+    counter = counter + 1
+    
+    -- 写入寄存器数据 (16位无符号整数)
+    registers:seek(0)
+    for i=0,31 do
+        registers:writeU16((counter + i) % 65536)  -- 写入递增数字,限制在0-65535
+    end
+    
+    -- 写入线圈数据 (1位布尔值)
+    ciols:seek(0)
+    for i=0,31 do
+        ciols:writeU8((counter + i) % 2)  -- 交替写入0和1
+    end
+  
+    -- 读取并打印部分数据用于调试
+    registers:seek(0)
+    ciols:seek(0)
+    log.info("registers:", registers:readU16(), registers:readU16(), registers:readU16(), registers:readU16(), registers:readU16())
+    log.info("ciols    :", ciols:readU8(), ciols:readU8(), ciols:readU8(), ciols:readU8(), ciols:readU8())
+end
+sys.timerLoopStart(modify_data,1000)
+
+-- sys.timerStart(function()
+--     modbus.slave_stop(mb_rtu_s)
+--     log.info("Modbus", "2分钟时间到,停止Modbus从站")
+-- end, 2 * 60 * 1000)  -- 2分钟(单位:毫秒)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 302 - 0
module/Air8000/demo/modbus/slave_tcp/dhcpsrv.lua

@@ -0,0 +1,302 @@
+
+local dhcpsrv = {}
+
+local udpsrv = require("udpsrv")
+
+local TAG = "dhcpsrv"
+
+----
+-- 参考地址
+-- https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol
+
+local function dhcp_decode(buff)
+    -- buff:seek(0)
+    local dst = {}
+    -- 开始解析dhcp
+    dst.op = buff[0]
+    dst.htype = buff[1]
+    dst.hlen = buff[2]
+    dst.hops = buff[3]
+    buff:seek(4)
+    dst.xid = buff:read(4)
+
+    _, dst.secs = buff:unpack(">H")
+    _, dst.flags = buff:unpack(">H")
+    dst.ciaddr = buff:read(4)
+    dst.yiaddr = buff:read(4)
+    dst.siaddr = buff:read(4)
+    dst.giaddr = buff:read(4)
+    dst.chaddr = buff:read(16)
+
+    -- 跳过192字节
+    buff:seek(192, zbuff.SEEK_CUR)
+
+    -- 解析magic
+    _, dst.magic = buff:unpack(">I")
+
+    -- 解析option
+    local opt = {}
+    while buff:len() > buff:used() do
+        local tag = buff:read(1):byte()
+        if tag ~= 0 then
+            local len = buff:read(1):byte()
+            if tag == 0xFF or len == 0 then
+                break
+            end
+            local data = buff:read(len)
+            if tag == 53 then
+                -- 53: DHCP Message Type
+                dst.msgtype = data:byte()
+            end
+            table.insert(opt, {tag, data})
+            -- log.info(TAG, "tag", tag, "data", data:toHex())
+        end
+    end
+    if dst.msgtype == nil then
+        return -- 没有解析到msgtype,直接返回
+    end
+    dst.opts = opt
+    return dst
+end
+
+local function dhcp_buff2ip(buff)
+    return string.format("%d.%d.%d.%d", buff:byte(1), buff:byte(2), buff:byte(3), buff:byte(4))
+end
+
+local function dhcp_print_pkg(pkg)
+    log.info(TAG, "XID",  pkg.xid:toHex())
+    log.info(TAG, "secs", pkg.secs)
+    log.info(TAG, "flags", pkg.flags)
+    log.info(TAG, "chaddr", pkg.chaddr:sub(1, pkg.hlen):toHex())
+    log.info(TAG, "yiaddr", dhcp_buff2ip(pkg.yiaddr))
+    log.info(TAG, "siaddr", dhcp_buff2ip(pkg.siaddr))
+    log.info(TAG, "giaddr", dhcp_buff2ip(pkg.giaddr))
+    log.info(TAG, "ciaddr", dhcp_buff2ip(pkg.ciaddr))
+    log.info(TAG, "magic", string.format("%08X", pkg.magic))
+    for _, opt in pairs(pkg.opts) do
+        if opt[1] == 53 then
+            log.info(TAG, "msgtype", opt[2]:byte())
+        elseif opt[1] == 60 then
+            log.info(TAG, "auth", opt[2])
+        elseif opt[1] == 57 then
+            log.info(TAG, "Maximum DHCP message size", opt[2]:byte() * 256 + opt[2]:byte(2))
+        elseif opt[1] == 61 then
+            log.info(TAG, "Client-identifier", opt[2]:toHex())
+        elseif opt[1] == 55 then
+            log.info(TAG, "Parameter request list", opt[2]:toHex())
+        elseif opt[1] == 12 then
+            log.info(TAG, "Host name", opt[2])
+        -- elseif opt[1] == 58 then
+        --     log.info(TAG, "Renewal (T1) time value", opt[2]:unpack(">I"))
+        end
+    end
+end
+
+local function dhcp_encode(pkg, buff)
+    -- 合成DHCP包
+    buff:seek(0)
+    buff[0] = pkg.op
+    buff[1] = pkg.htype
+    buff[2] = pkg.hlen
+    buff[3] = pkg.hops
+    buff:seek(4)
+    -- 写入XID
+    buff:write(pkg.xid)
+    -- 几个重要的参数
+    buff:pack(">H", pkg.secs)
+    buff:pack(">H", pkg.flags)
+    buff:write(pkg.ciaddr)
+    buff:write(pkg.yiaddr)
+    buff:write(pkg.siaddr)
+    buff:write(pkg.giaddr)
+    -- 写入MAC地址
+    buff:write(pkg.chaddr)
+    -- 跳过192字节
+    buff:seek(192, zbuff.SEEK_CUR)
+    -- 写入magic
+    buff:pack(">I", pkg.magic)
+    -- 写入option
+    for _, opt in pairs(pkg.opts) do
+        buff:write(opt[1])
+        buff:write(#opt[2])
+        buff:write(opt[2])
+    end
+    buff:write(0xFF, 0x00)
+end
+
+----
+
+local function dhcp_send_x(srv, pkg, client, msgtype)
+    local buff = zbuff.create(300)
+    pkg.op = 2
+    pkg.ciaddr = "\0\0\0\0"
+    pkg.yiaddr = string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], client.ip)
+    pkg.siaddr = string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], srv.opts.gw[4])
+    pkg.giaddr = "\0\0\0\0"
+    pkg.secs = 0
+
+    pkg.opts = {} -- 复位option
+    table.insert(pkg.opts, {53, string.char(msgtype)})
+    table.insert(pkg.opts, {1, string.char(srv.opts.mark[1], srv.opts.mark[2], srv.opts.mark[3], srv.opts.mark[4])})
+    table.insert(pkg.opts, {3, string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], srv.opts.gw[4])})
+    table.insert(pkg.opts, {51, "\x00\x00\x1E\x00"}) -- 7200秒, 大概
+    table.insert(pkg.opts, {54, string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], srv.opts.gw[4])})
+    table.insert(pkg.opts, {6, string.char(srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], srv.opts.gw[4])})
+
+    dhcp_encode(pkg, buff)
+
+    local dst = "255.255.255.255"
+    if 4 == msgtype then
+        dst = string.format("%d.%d.%d.%d", srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], client.ip)
+    end
+    -- log.info(TAG, "发送", msgtype, dst, buff:query():toHex())
+    srv.udp:send(buff, dst, 68)
+end
+
+local function dhcp_send_offer(srv, pkg, client)
+    dhcp_send_x(srv, pkg, client, 2)
+end
+
+local function dhcp_send_ack(srv, pkg, client)
+    dhcp_send_x(srv, pkg, client, 5)
+end
+
+local function dhcp_handle_discover(srv, pkg)
+    local mac = pkg.chaddr:sub(1, pkg.hlen)
+    -- 看看是不是已经分配了ip
+    for _, client in pairs(srv.clients) do
+        if client.mac == mac then
+            log.info(TAG, "发现已经分配的mac地址, send offer")
+            dhcp_send_offer(srv, pkg, client)
+            return
+        end
+    end
+    -- TODO 清理已经过期的IP分配记录
+    -- 分配一个新的ip
+    if #srv.clients >= (srv.opts.ip_end - srv.opts.ip_start) then
+        log.info(TAG, "没有可分配的ip了")
+        return
+    end
+    local ip = nil
+    for i = srv.opts.ip_start, srv.opts.ip_end, 1 do
+        if srv.clients[i] == nil then
+            ip = i
+            break
+        end
+    end
+    if ip == nil then
+        log.info(TAG, "没有可分配的ip了")
+        return
+    end
+    log.info(TAG, "分配ip", mac:toHex(), string.format("%d.%d.%d.%d", srv.opts.gw[1], srv.opts.gw[2], srv.opts.gw[3], ip))
+    local client = {
+        mac = mac,
+        ip = ip,
+        tm = mcu.ticks() // mcu.hz(),
+        stat = 1
+    }
+    srv.clients[ip] = client
+    log.info(TAG, "send offer")
+    dhcp_send_offer(srv, pkg, client)
+end
+
+local function dhcp_handle_request(srv, pkg)
+    local mac = pkg.chaddr:sub(1, pkg.hlen)
+    -- 看看是不是已经分配了ip
+    for _, client in pairs(srv.clients) do
+        if client.mac == mac then
+            log.info(TAG, "request,发现已经分配的mac地址, send ack")
+            client.tm = mcu.ticks() // mcu.hz()
+            stat = 3
+            dhcp_send_ack(srv, pkg, client)
+            return
+        end
+    end
+end
+
+local function dhcp_pkg_handle(srv, pkg)
+    -- 进行基本的检查
+    if pkg.magic ~= 0x63825363 then
+        log.warn(TAG, "dhcp数据包的magic不对劲,忽略该数据包", pkg.magic)
+        return
+    end
+    if pkg.op ~= 1 then
+        log.info(TAG, "op不对,忽略该数据包", pkg.op)
+        return
+    end
+    if pkg.htype ~= 1 or pkg.hlen ~= 6 then
+        log.warn(TAG, "htype/hlen 不认识, 忽略该数据包")
+        return
+    end
+    -- 看看是不是能处理的类型, 当前只处理discover/request
+    if pkg.msgtype == 1 or pkg.msgtype == 3 then
+    else
+        log.warn(TAG, "msgtype不是discover/request, 忽略该数据包", pkg.msgtype)
+        return
+    end
+    -- 检查一下mac地址是否合法
+    local mac = pkg.chaddr:sub(1, pkg.hlen)
+    if mac == "\0\0\0\0\0\0" or mac == "\xFF\xFF\xFF\xFF\xFF\xFF" then
+        log.warn(TAG, "mac地址为空, 忽略该数据包")
+        return
+    end
+
+    -- 处理discover包
+    if pkg.msgtype == 1 then
+        log.info(TAG, "是discover包")
+        dhcp_handle_discover(srv, pkg)
+    elseif pkg.msgtype == 3 then
+        log.info(TAG, "是request包")
+        dhcp_handle_request(srv, pkg)
+    end
+    -- TODO 处理结束, 打印一下客户的列表?
+end
+
+local function dhcp_task(srv)
+    while 1 do
+        -- log.info("ulwip", "等待DHCP数据")
+        local result, data = sys.waitUntil(srv.udp_topic, 1000)
+        if result then
+            -- log.info("ulwip", "收到dhcp数据包", data:toHex())
+            -- 解析DHCP数据包
+            local pkg = dhcp_decode(zbuff.create(#data, data))
+            if pkg then
+                -- dhcp_print_pkg(pkg)
+                dhcp_pkg_handle(srv, pkg)
+            end
+        end
+    end
+end
+function dhcpsrv.create(opts)
+    local srv = {}
+    if not opts then
+        opts = {}
+    end
+    srv.udp_topic = "dhcpd_inc"
+    -- 补充参数
+    if not opts.mark then
+        opts.mark = {255, 255, 255, 0}
+    end
+    if not opts.gw then
+        opts.gw = {192, 168, 4, 1}
+    end
+    if not opts.dns then
+        opts.dns = opts.gw
+    end
+    if not opts.ip_start then
+        opts.ip_start = 100
+    end
+    if not opts.ip_end then
+        opts.ip_end = 200
+    end
+
+    srv.clients = {}
+    srv.opts = opts
+
+    srv.udp = udpsrv.create(67, srv.udp_topic, opts.adapter)
+    srv.task = sys.taskInit(dhcp_task, srv)
+    return srv
+end
+
+
+return dhcpsrv

+ 113 - 0
module/Air8000/demo/modbus/slave_tcp/dnsproxy.lua

@@ -0,0 +1,113 @@
+--[[
+@module dnsproxy
+@summary DNS代理转发
+@version 1.0
+@date    2024.4.20
+@author  wendal
+@demo    socket
+@tag LUAT_USE_NETWORK
+@usage
+-- 具体用法请查阅demo
+]]
+
+local sys = require "sys"
+
+local dnsproxy = {}
+dnsproxy.map = {}
+dnsproxy.txid = 0x123
+dnsproxy.rxbuff = zbuff.create(1500)
+
+function dnsproxy.on_request(sc, event)
+    if event == socket.EVENT then
+        local rxbuff = dnsproxy.rxbuff
+        while 1 do
+            rxbuff:seek(0)
+            local succ, data_len, remote_ip, remote_port = socket.rx(sc, rxbuff)
+            if succ and data_len and data_len > 0 then
+                -- log.info("dnsproxy", "收到DNS查询数据", rxbuff:query():toHex())
+                if remote_ip and #remote_ip == 5 then
+                    local ip1,ip2,ip3,ip4 = remote_ip:byte(2),remote_ip:byte(3),remote_ip:byte(4),remote_ip:byte(5)
+                    remote_ip = string.format("%d.%d.%d.%d", ip1, ip2, ip3, ip4)
+                    local txid_request = rxbuff[0] + rxbuff[1] * 256
+                    local txid_map = dnsproxy.txid
+                    dnsproxy.txid = dnsproxy.txid + 1
+                    if dnsproxy.txid > 65000 then
+                        dnsproxy.txid = 0x123
+                    end
+                    table.insert(dnsproxy.map, {txid_request, txid_map, remote_ip, remote_port})
+                    rxbuff[0] = txid_map % 256
+                    rxbuff[1] = txid_map // 256
+                    socket.tx(dnsproxy.main_sc, rxbuff, "223.5.5.5", 53)
+                end
+            else
+                break
+            end
+        end
+    end
+end
+
+function dnsproxy.on_response(sc, event)
+    if event == socket.EVENT then
+        local rxbuff = dnsproxy.rxbuff
+        while 1 do
+            rxbuff:seek(0)
+            local succ, data_len = socket.rx(sc, rxbuff)
+            if succ and data_len and data_len > 0 then
+                if true then
+                    -- local ip1,ip2,ip3,ip4 = remote_ip:byte(2),remote_ip:byte(3),remote_ip:byte(4),remote_ip:byte(5)
+                    -- remote_ip = string.format("%d.%d.%d.%d", ip1, ip2, ip3, ip4)
+                    local txid_resp = rxbuff[0] + rxbuff[1] * 256
+                    local index = -1
+                    for i, mapit in pairs(dnsproxy.map) do
+                        if mapit[2] == txid_resp then
+                            local txid_request = mapit[1]
+                            local remote_ip = mapit[3]
+                            local remote_port = mapit[4]
+                            rxbuff[0] = txid_request % 256
+                            rxbuff[1] = txid_request // 256
+                            socket.tx(dnsproxy.sc, rxbuff, remote_ip, remote_port)
+                            index = i
+                            break
+                        end
+                    end
+                    if index > 0 then
+                        table.remove(dnsproxy.map, index)
+                    end
+                end
+            else
+                break
+            end
+        end
+    end
+end
+
+--[[
+创建UDP服务器
+@api dnsproxy.create(adapter, main_adapter)
+@int 监听的网络适配器id
+@int 网络适配编号, 默认为nil,可选
+@return table UDP服务的实体, 若创建失败会返回nil
+]]
+function dnsproxy.setup(adapter, main_adapter)
+    log.info("dnsproxy", adapter, main_adapter)
+    dnsproxy.adapter = adapter
+    dnsproxy.main_adapter = main_adapter
+    dnsproxy.sc = socket.create(dnsproxy.adapter, dnsproxy.on_request)
+    dnsproxy.main_sc = socket.create(dnsproxy.main_adapter, dnsproxy.on_response)
+    socket.config(dnsproxy.sc, 53, true)
+    socket.config(dnsproxy.main_sc, 1053, true)
+    dnsproxy.on_ip_ready()
+    return true
+end
+
+function dnsproxy.on_ip_ready()
+    socket.close(dnsproxy.sc)
+    socket.close(dnsproxy.main_sc)
+    log.info("dnsproxy", "开启DNS代理")
+    socket.connect(dnsproxy.sc, "255.255.255.255", 0)
+    socket.connect(dnsproxy.main_sc, "223.5.5.5", 53)
+end
+
+sys.subscribe("IP_READY", dnsproxy.on_ip_ready)
+
+return dnsproxy

+ 57 - 0
module/Air8000/demo/modbus/slave_tcp/lan.lua

@@ -0,0 +1,57 @@
+
+-- 引入必要的库文件(lua编写), 内部库不需要require
+sys = require("sys")
+sysplus = require("sysplus")
+
+dhcps = require "dhcpsrv"
+dnsproxy = require "dnsproxy"
+
+sys.taskInit(function ()
+    -- sys.wait(3000)
+    local result = spi.setup(
+        0,--串口id
+        nil,
+        0,--CPHA
+        0,--CPOL
+        8,--数据宽度
+        51200000--,--频率
+        -- spi.MSB,--高低位顺序    可选,默认高位在前
+        -- spi.master,--主模式     可选,默认主
+        -- spi.full--全双工       可选,默认全双工
+    )
+    log.info("main", "open",result)
+    if result ~= 0 then--返回值为0,表示打开成功
+        log.info("main", "spi open error",result)
+        return
+    end
+
+    netdrv.setup(socket.LWIP_ETH, netdrv.CH390, {spi=0,cs=8})
+    sys.wait(3000)
+    local ipv4,mark, gw = netdrv.ipv4(socket.LWIP_ETH, "192.168.0.155", "255.255.255.0", "192.168.0.1")
+    log.info("ipv4", ipv4,mark, gw)
+    while netdrv.link(socket.LWIP_ETH) ~= true do
+        sys.wait(100)
+    end
+    while netdrv.link(socket.LWIP_GP) ~= true do
+        sys.wait(100)
+    end
+    dhcps.create({adapter=socket.LWIP_ETH})
+    dnsproxy.setup(socket.LWIP_ETH, socket.LWIP_GP)
+    netdrv.napt(socket.LWIP_GP)
+    if iperf then
+        log.info("启动iperf服务器端")
+        iperf.server(socket.LWIP_ETH)
+    end
+end)
+
+
+sys.taskInit(function()
+    -- sys.waitUntil("IP_READY")
+    while 1 do
+        sys.wait(300000)
+        -- log.info("http", http.request("GET", "http://httpbin.air32.cn/bytes/4096", nil, nil, {adapter=socket.LWIP_ETH}).wait())
+        log.info("lua", rtos.meminfo())
+        log.info("sys", rtos.meminfo("sys"))
+        -- log.info("psram", rtos.meminfo("psram"))
+    end
+end)

+ 66 - 0
module/Air8000/demo/modbus/slave_tcp/main.lua

@@ -0,0 +1,66 @@
+-- LuaTools需要PROJECT和VERSION这两个信息
+PROJECT = "modbus_slave_tcp"
+VERSION = "1.0.0"
+log.style(1)
+log.info("main", PROJECT, VERSION)
+
+-- sys库是标配
+_G.sys = require("sys")
+_G.sysplus = require("sysplus")
+
+log.info("ch390", "打开LDO供电")
+gpio.setup(140, 1)  --打开开发板lan供电
+require "lan"
+
+-- 创建从站设备
+local slave_id = 1
+mb_tcp_s = modbus.create_slave(modbus.MODBUS_TCP, slave_id, 53, socket.LWIP_ETH)
+
+-- 创建寄存器数据区
+registers = zbuff.create(1)
+modbus.add_block(mb_tcp_s, modbus.REGISTERS, 0, 32, registers)
+registers:clear()
+-- 创建线圈数据区
+ciols = zbuff.create(1)
+modbus.add_block(mb_tcp_s, modbus.CIOLS, 0, 32, ciols)
+ciols:clear()
+
+-- 启动通讯
+modbus.slave_start(mb_tcp_s)
+log.info("start modbus slave")
+
+local counter = 0
+
+-- 修改和读取modbus值
+function modify_data()
+    counter = counter + 1
+    
+    -- 写入寄存器数据 (16位无符号整数)
+    registers:seek(0)
+    for i=0,31 do
+        registers:writeU16((counter + i) % 65536)  -- 写入递增数字,限制在0-65535
+    end
+    
+    -- 写入线圈数据 (1位布尔值)
+    ciols:seek(0)
+    for i=0,31 do
+        ciols:writeU8((counter + i) % 2)  -- 交替写入0和1
+    end
+    
+    -- 读取并打印部分数据用于调试
+    registers:seek(0)
+    ciols:seek(0)
+    log.info("registers:", registers:readU16(), registers:readU16(), registers:readU16(), registers:readU16(), registers:readU16())
+    log.info("ciols    :", ciols:readU8(), ciols:readU8(), ciols:readU8(), ciols:readU8(), ciols:readU8())
+end
+sys.timerLoopStart(modify_data,1000)
+
+-- sys.timerStart(function()
+--     modbus.slave_stop(mb_rtu_s)
+--     log.info("Modbus", "2分钟时间到,停止Modbus从站")
+-- end, 120000)
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!