Procházet zdrojové kódy

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

??? před 5 měsíci
rodič
revize
c3b721cd66
61 změnil soubory, kde provedl 1480 přidání a 349 odebrání
  1. 1 1
      components/network/adapter/luat_lib_socket.c
  2. 10 16
      components/network/libftp/luat_ftp_client.c
  3. 1 1
      components/network/libftp/luat_lib_ftp.c
  4. 21 1
      components/network/netdrv/binding/luat_lib_netdrv.c
  5. 4 2
      components/network/netdrv/src/luat_netdrv.c
  6. 3 3
      components/network/ulwip/binding/luat_lib_ulwip.c
  7. 1 1
      components/network/ulwip/include/luat_ulwip.h
  8. 27 23
      components/network/ulwip/src/ulwip_dhcp_client.c
  9. 9 0
      components/pins/include/luat_pins.h
  10. 26 5
      components/pins/src/luat_pins.c
  11. 1 0
      luat/include/luat_mcu.h
  12. 1 1
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirSHT30_1000/AirSHT30_1000.lua
  13. 1 1
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirSHT30_1000/main.lua
  14. 12 2
      module/Air780EHM_Air780EHV_Air780EGH/demo/airtalk/extalk.lua
  15. 1 1
      module/Air780EPM/demo/accessory_board/AirSHT30_1000/main.lua
  16. 114 0
      module/Air8000/demo/accessory_board/AirSHT30_1000/AirSHT30_1000.lua
  17. 5 7
      module/Air8000/demo/accessory_board/AirSHT30_1000/main.lua
  18. 57 0
      module/Air8000/demo/accessory_board/AirSHT30_1000/readme.md
  19. 35 0
      module/Air8000/demo/accessory_board/AirSHT30_1000/sht30_app.lua
  20. 12 2
      module/Air8000/demo/airtalk/extalk.lua
  21. 0 26
      module/Air8000/demo/airtalk/main.lua
  22. 11 0
      module/Air8000/demo/airtalk/talk.lua
  23. 28 0
      module/Air8000/demo/ble/central/check_wifi.lua
  24. 1 1
      module/Air8000/demo/ble/central/main.lua
  25. 28 0
      module/Air8000/demo/ble/ibeacon/check_wifi.lua
  26. 1 1
      module/Air8000/demo/ble/ibeacon/main.lua
  27. 28 0
      module/Air8000/demo/ble/peripheral/check_wifi.lua
  28. 1 1
      module/Air8000/demo/ble/peripheral/main.lua
  29. 28 0
      module/Air8000/demo/ble/scan/check_wifi.lua
  30. 1 1
      module/Air8000/demo/ble/scan/main.lua
  31. 27 0
      module/Air8000/demo/config_wifi_network/ble_config_wifi/check_wifi.lua
  32. 1 1
      module/Air8000/demo/config_wifi_network/ble_config_wifi/main.lua
  33. 27 0
      module/Air8000/demo/wlan/AP/check_wifi.lua
  34. 1 1
      module/Air8000/demo/wlan/AP/main.lua
  35. 27 0
      module/Air8000/demo/wlan/Power_Save/check_wifi.lua
  36. 1 1
      module/Air8000/demo/wlan/Power_Save/main.lua
  37. 27 0
      module/Air8000/demo/wlan/STA/check_wifi.lua
  38. 1 1
      module/Air8000/demo/wlan/STA/main.lua
  39. 27 0
      module/Air8000/demo/wlan/wifi_configuration_network_by_ap/check_wifi.lua
  40. 1 1
      module/Air8000/demo/wlan/wifi_configuration_network_by_ap/main.lua
  41. 27 0
      module/Air8000/demo/wlan/wifi_scan/check_wifi.lua
  42. 1 1
      module/Air8000/demo/wlan/wifi_scan/main.lua
  43. 27 0
      module/Air8000/project/wifi_ap_read_file/check_wifi.lua
  44. 5 0
      module/Air8000/project/wifi_ap_read_file/main.lua
  45. 63 36
      module/Air8000/project/整机开发板出厂工程/user/extalk.lua
  46. 1 1
      module/Air8000/project/整机开发板出厂工程/user/main.lua
  47. 14 23
      module/Air8000/project/整机开发板出厂工程/user/talk.lua
  48. 0 1
      module/Air8101/demo/accessory_board/AirETH_1000/network_routing/wifi_out_ethernet_in_wifi_in/netif_app.lua
  49. 141 0
      module/Air8101/demo/accessory_board/AirPHY_1000/http/http_app.lua
  50. 79 0
      module/Air8101/demo/accessory_board/AirPHY_1000/http/main.lua
  51. 77 0
      module/Air8101/demo/accessory_board/AirPHY_1000/http/netdrv/netdrv_eth_rmii.lua
  52. 105 0
      module/Air8101/demo/accessory_board/AirPHY_1000/http/netdrv/netdrv_multiple.lua
  53. 25 0
      module/Air8101/demo/accessory_board/AirPHY_1000/http/netdrv_device.lua
  54. 85 0
      module/Air8101/demo/accessory_board/AirPHY_1000/http/readme.md
  55. 0 53
      module/Air8101/demo/accessory_board/AirPHY_1000/http_app.lua
  56. 71 0
      module/Air8101/demo/accessory_board/AirPHY_1000/network_routing/wifi_out_ethernet_in_wifi_in/main.lua
  57. 53 0
      module/Air8101/demo/accessory_board/AirPHY_1000/network_routing/wifi_out_ethernet_in_wifi_in/netif_app.lua
  58. 73 0
      module/Air8101/demo/accessory_board/AirPHY_1000/network_routing/wifi_out_ethernet_in_wifi_in/readme.md
  59. 0 42
      module/Air8101/demo/accessory_board/AirPHY_1000/phy_app.lua
  60. 5 84
      module/Air8101/demo/accessory_board/AirPHY_1000/readme.md
  61. 19 6
      script/libs/exfotawifi.lua

+ 1 - 1
components/network/adapter/luat_lib_socket.c

@@ -801,7 +801,7 @@ static int l_socket_rx(lua_State *L)
 }
 
 /*
-读取数据(非zbuff版本)
+读取数据(非zbuff版本,已废弃)
 @api socket.read(netc, len)
 @userdata socket.create得到的ctrl
 @int        限制读取数据长度,可选,不传就是读出全部

+ 10 - 16
components/network/libftp/luat_ftp_client.c

@@ -398,7 +398,7 @@ static int pasv_recv(void)
 		LLOGE("pasv_recv %s error:%d", g_s_ftp.network->cmd_recv_data, ret);
 		return -1;
 	}
-	LLOGD("%.*s", g_s_ftp.network->cmd_recv_len, g_s_ftp.network->cmd_recv_data);
+	LLOGD("%s %d %.*s",__FUNCTION__ ,__LINE__ ,g_s_ftp.network->cmd_recv_len, g_s_ftp.network->cmd_recv_data);
 	g_s_ftp.network->cmd_recv_data[g_s_ftp.network->cmd_recv_len] = 0;
 	if (memcmp(g_s_ftp.network->cmd_recv_data, FTP_FILE_STATUS_OK, 3) && memcmp(g_s_ftp.network->cmd_recv_data, FTP_DATA_CON_OPEN, 3)){
 		return -1;
@@ -414,15 +414,15 @@ static int pasv_recv(void)
 			rx_finish = 1;
 		}
 	}
-	// LLOGD("rx_finish:%d data_netc_online:%d Pos:%d ", rx_finish, g_s_ftp.network->data_netc_online, g_s_ftp.result_buffer.Pos);
-	while(!rx_finish)	//data通道未断开或者已经接收到数据了
+	LLOGD("%s %d rx_finish:%d data_netc_online:%d Pos:%d ",__FUNCTION__ ,__LINE__ ,rx_finish, g_s_ftp.network->data_netc_online, g_s_ftp.result_buffer.Pos);
+    while(!rx_finish)	//data通道未断开或者已经接收到数据了
 	{
 		ret = luat_ftp_cmd_recv(&g_s_ftp,g_s_ftp.network->cmd_recv_data,&g_s_ftp.network->cmd_recv_len,FTP_SOCKET_TIMEOUT);
-		if (ret){
-			LLOGD("rx error!%d", ret);
+		if (ret<0){
+			LLOGD("%s %d rx error!%d",__FUNCTION__ ,__LINE__ , ret);
 			return -1;
 		} else if (!ret) {
-			LLOGD("%.*s", g_s_ftp.network->cmd_recv_len, g_s_ftp.network->cmd_recv_data);
+			LLOGD("%s %d %.*s",__FUNCTION__ ,__LINE__ , g_s_ftp.network->cmd_recv_len, g_s_ftp.network->cmd_recv_data);
 			if (memcmp(g_s_ftp.network->cmd_recv_data, FTP_CLOSE_CONNECT, 3)){
 				return -1;
 			}
@@ -435,17 +435,11 @@ static int pasv_recv(void)
 		LLOGD("???");
 		return -1;
 	}
-	//等服务器关闭接收通道
-	if (g_s_ftp.network->data_netc_online) {
-		ret = luat_ftp_cmd_recv(&g_s_ftp,g_s_ftp.network->cmd_recv_data,&g_s_ftp.network->cmd_recv_len,1000);
-		if (ret) {
-			LLOGE("pasv_recv %s error:%d", g_s_ftp.network->cmd_recv_data, ret);
-			return -1;
-		}
-	}
 	//主动关闭掉接收
-	if (g_s_ftp.network->data_netc_online && g_s_ftp.network->data_netc) {
-		network_close(g_s_ftp.network->data_netc, 0);
+	if (g_s_ftp.network->data_netc_online && g_s_ftp.network->data_netc){
+		network_force_close_socket(g_s_ftp.network->data_netc);
+		network_release_ctrl(g_s_ftp.network->data_netc);
+		g_s_ftp.network->data_netc = NULL;
 	}
 	return 0;
 }

+ 1 - 1
components/network/libftp/luat_lib_ftp.c

@@ -1,6 +1,6 @@
 /*
 @module  ftp
-@summary ftp 客户端
+@summary ftp 客户端 (服务器推荐使用vsftpd,其他暂不做支持)
 @version 1.0
 @date    2022.09.05
 @demo    ftp

+ 21 - 1
components/network/netdrv/binding/luat_lib_netdrv.c

@@ -122,17 +122,37 @@ static int l_netdrv_setup(lua_State *L) {
 
 /*
 开启或关闭DHCP
-@api netdrv.dhcp(id, enable)
+@api netdrv.dhcp(id, enable, name)
 @int 网络适配器编号, 例如 socket.LWIP_ETH
 @boolean 开启或者关闭
+@string dhcp主机名称, 可选, 最长31字节,填""清除
 @return boolean 成功与否
 @usgae
 -- 注意, 并非所有网络设备都支持关闭DHCP, 例如4G Cat.1
+-- name参数于2025.9.23添加
 netdrv.dhcp(socket.LWIP_ETH, true)
+netdrv.dhcp(socket.LWIP_ETH, true, "LuatOS")
 */
 static int l_netdrv_dhcp(lua_State *L) {
     int id = luaL_checkinteger(L, 1);
     int enable = lua_toboolean(L, 2);
+    if (lua_isstring(L, 3)) {
+        size_t len = 0;
+        const char* data = NULL;
+        luat_netdrv_t *drv = NULL;
+        data = luaL_checklstring(L, 3, &len);
+        drv = luat_netdrv_get(id);
+        if(((len + 1) > 32) || (drv == NULL) || (drv->ulwip == NULL)) {
+            LLOGD("dhcp name set fail");
+            lua_pushboolean(L, 0);
+            return -1;
+        }
+        if(0 == len){
+            memset(drv->ulwip->dhcp_client.name, 0x00, 32);
+        } else {
+            memcpy(drv->ulwip->dhcp_client.name, data, len + 1);
+        }
+    }
     int ret = luat_netdrv_dhcp(id, enable);
     lua_pushboolean(L, ret == 0);
     return 1;

+ 4 - 2
components/network/netdrv/src/luat_netdrv.c

@@ -3,6 +3,8 @@
 #include "luat_network_adapter.h"
 #include "luat_mem.h"
 #include "luat_mcu.h"
+#include "lwip/ip.h"
+#include "lwip/tcpip.h"
 
 #ifdef LUAT_USE_AIRLINK
 #include "luat_airlink.h"
@@ -342,10 +344,10 @@ int luat_netdrv_dhcp_opt(luat_netdrv_t* drv, void* userdata, int enable) {
         return 0;
     }
     if (enable) {
-        ulwip_dhcp_client_start(drv->ulwip);
+        tcpip_callback_with_block(ulwip_dhcp_client_start, drv->ulwip, 1);
     }
     else {
-        ulwip_dhcp_client_stop(drv->ulwip);
+        tcpip_callback_with_block(ulwip_dhcp_client_stop, drv->ulwip, 1);
     }
     return 0;
 }

+ 3 - 3
components/network/ulwip/binding/luat_lib_ulwip.c

@@ -93,9 +93,9 @@ int ulwip_netif_ip_event(ulwip_ctx_t* ctx) {
     ready_now &= netif_is_up(netif);
     // luat_ip_addr_t ip = {0};
 
-    if (ctx->dhcp_client) {
-        net_lwip2_set_dhcp_client(ctx->adapter_index, ctx->dhcp_client);
-    }
+    // if (ctx->dhcp_client) {
+    net_lwip2_set_dhcp_client(ctx->adapter_index, &ctx->dhcp_client);
+    // }
 
     net_lwip2_set_link_state(ctx->adapter_index, ready_now);
     if (ctx->ip_ready == ready_now) {

+ 1 - 1
components/network/ulwip/include/luat_ulwip.h

@@ -47,7 +47,7 @@ typedef struct ulwip_ctx
     uint16_t use_zbuff_out;
     uint16_t mtu;
     uint8_t hwaddr[ETH_HWADDR_LEN];
-    dhcp_client_info_t *dhcp_client;
+    dhcp_client_info_t dhcp_client;
     luat_rtos_timer_t dhcp_timer;
     ulwip_event_cb event_cb;
 }ulwip_ctx_t;

+ 27 - 23
components/network/ulwip/src/ulwip_dhcp_client.c

@@ -27,7 +27,7 @@ static ulwip_ctx_t* s_ctxs[NW_ADAPTER_INDEX_LWIP_NETIF_QTY];
 static int ulwip_dhcp_client_run(ulwip_ctx_t* ctx, char* rxbuff, size_t len) {
     PV_Union uIP;
     // 检查dhcp的状态
-    dhcp_client_info_t* dhcp = ctx->dhcp_client;
+    dhcp_client_info_t* dhcp = (&ctx->dhcp_client);
     u8_t adapter_index = ctx->adapter_index;
     struct netif* netif = ctx->netif;
 
@@ -174,7 +174,7 @@ static int ulwip_dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const
     for (size_t i = 0; i < NW_ADAPTER_INDEX_LWIP_NETIF_QTY; i++)
     {
         ctx = s_ctxs[i];
-        if (ctx == NULL || ctx->dhcp_client == NULL || ctx->netif == NULL) {
+        if (ctx == NULL || ctx->netif == NULL) {
             continue;
         }
 
@@ -217,7 +217,7 @@ static void ulwip_dhcp_client_run_proxy(void* ctx) {
 static void dhcp_client_timer_cb(void *arg) {
     ulwip_ctx_t *ctx = (ulwip_ctx_t *)arg;
     // 简单防御一下
-    if (ctx->dhcp_client == NULL || ctx->dhcp_enable == 0) {
+    if (ctx->dhcp_enable == 0) {
         return;
     }
     #if NO_SYS
@@ -228,19 +228,23 @@ static void dhcp_client_timer_cb(void *arg) {
 }
 
 static void reset_dhcp_client(ulwip_ctx_t *ctx) {
-    memset(ctx->dhcp_client, 0, sizeof(dhcp_client_info_t));
-    memcpy(ctx->dhcp_client->mac, ctx->netif->hwaddr, 6);
+    char tmp[32] = {0};
+    memcpy(tmp, ctx->dhcp_client.name, 32);
+    memset(&ctx->dhcp_client, 0, sizeof(dhcp_client_info_t));
+    memcpy(&ctx->dhcp_client.mac, ctx->netif->hwaddr, 6);
     ctx->ip_ready = 0;
-    luat_crypto_trng((char*)&ctx->dhcp_client->xid, sizeof(ctx->dhcp_client->xid));
+    luat_crypto_trng((char*)&ctx->dhcp_client.xid, sizeof(ctx->dhcp_client.xid));
     #if LWIP_NETIF_HOSTNAME
     if (ctx->netif && ctx->netif->hostname) {
-        strncpy(ctx->dhcp_client->name, ctx->netif->hostname, strlen(ctx->dhcp_client->name) + 1);
+        strncpy(ctx->dhcp_client.name, ctx->netif->hostname, strlen(ctx->dhcp_client.name) + 1);
     }
     #endif
-    if (ctx->dhcp_client->name[0] == 0) {
-        sprintf_(ctx->dhcp_client->name, "LuatOS_%02X%02X%02X%02X%02X%02X",
-                ctx->dhcp_client->mac[0],ctx->dhcp_client->mac[1], ctx->dhcp_client->mac[2],
-                ctx->dhcp_client->mac[3],ctx->dhcp_client->mac[4], ctx->dhcp_client->mac[5]);
+    if (tmp[0] == 0) {
+        sprintf_(ctx->dhcp_client.name, "LuatOS_%02X%02X%02X%02X%02X%02X",
+                ctx->dhcp_client.mac[0],ctx->dhcp_client.mac[1], ctx->dhcp_client.mac[2],
+                ctx->dhcp_client.mac[3],ctx->dhcp_client.mac[4], ctx->dhcp_client.mac[5]);
+    } else {
+        memcpy(ctx->dhcp_client.name, tmp, 32);
     }
 }
 
@@ -258,21 +262,21 @@ void ulwip_dhcp_client_start(ulwip_ctx_t *ctx) {
         udp_connect(s_ulwip_dhcp, IP4_ADDR_ANY, 67);
         udp_recv(s_ulwip_dhcp, ulwip_dhcp_recv, ctx);
     }
-    if (!ctx->dhcp_client) {
-        ctx->dhcp_client = luat_heap_malloc(sizeof(dhcp_client_info_t));
-        reset_dhcp_client(ctx);
-        net_lwip2_set_dhcp_client(ctx->adapter_index, ctx->dhcp_client);
-        luat_rtos_timer_create(&ctx->dhcp_timer);
-        s_ctxs[ctx->adapter_index] = ctx; // 保存到全局数组中
-    }
+    // if (!ctx->dhcp_client) {
+        // ctx->dhcp_client = luat_heap_malloc(sizeof(dhcp_client_info_t));
+    reset_dhcp_client(ctx);
+    net_lwip2_set_dhcp_client(ctx->adapter_index, &ctx->dhcp_client);
+    luat_rtos_timer_create(&ctx->dhcp_timer);
+    s_ctxs[ctx->adapter_index] = ctx; // 保存到全局数组中
+    // }
     ip4_addr_t ipaddr = {0};
     ip4_addr_t netmask = {0};
     ip4_addr_t gw = {0};
     if (ctx->netif) {
         netif_set_addr(ctx->netif, &ipaddr, &netmask, &gw);
     }
-    ctx->dhcp_client->state = DHCP_STATE_DISCOVER;
-    ctx->dhcp_client->discover_cnt = 0;
+    ctx->dhcp_client.state = DHCP_STATE_DISCOVER;
+    ctx->dhcp_client.discover_cnt = 0;
     if (!luat_rtos_timer_is_active(ctx->dhcp_timer))
     {
         luat_rtos_timer_start(ctx->dhcp_timer, 1000, 1, dhcp_client_timer_cb, ctx);
@@ -287,10 +291,10 @@ void ulwip_dhcp_client_stop(ulwip_ctx_t *ctx) {
         
     }
     if (ctx->dhcp_enable) {
-        if (ctx->dhcp_client) {
+        // if (ctx->dhcp_client) {
             // 重置dhcp客户端
-            reset_dhcp_client(ctx);
-        }
+        reset_dhcp_client(ctx);
+        // }
         if (ctx->netif) {
             ip4_addr_t ipaddr = {0};
             ip4_addr_t netmask = {0};

+ 9 - 0
components/pins/include/luat_pins.h

@@ -53,6 +53,11 @@ typedef enum
 	LUAT_PIN_QSPI_CS,
 	LUAT_PIN_QSPI_QTY,
 
+	LUAT_PIN_SIM_IO = 0,
+	LUAT_PIN_SIM_CLK,
+	LUAT_PIN_SIM_RST,
+	LUAT_PIN_SIM_QTY,
+
 	LUAT_PIN_ONLY_ONE_QTY = 1,
 	LUAT_PIN_FUNCTION_MAX = LUAT_PIN_SDIO_QTY,
 	LUAT_PIN_ALT_FUNCTION_MAX = 9,
@@ -130,6 +135,10 @@ typedef struct
 	luat_pin_iomux_info pin_list[LUAT_PIN_QSPI_QTY];
 }luat_qspi_pin_iomux_t;
 
+typedef struct
+{
+	luat_pin_iomux_info pin_list[LUAT_PIN_SIM_QTY];
+}luat_sim_pin_iomux_t;
 /**
  * @brief 获取某种外设的全部pin复用信息
  * @param type 外设类型,见LUAT_MCU_PERIPHERAL_E

+ 26 - 5
components/pins/src/luat_pins.c

@@ -39,7 +39,7 @@ static luat_pin_peripheral_function_description_u luat_pin_function_analyze(char
 	size_t org_len = len;
 	size_t offset = 0;
 	const char *peripheral_names[LUAT_MCU_PERIPHERAL_QTY] = {
-			"UART","I2C","SPI","PWM","CAN","GPIO","I2S","SDIO","LCD","CAMERA","ONEWIRE","KEYBORAD"
+			"UART","I2C","SPI","PWM","CAN","GPIO","I2S","SDIO","LCD","CAMERA","ONEWIRE","KEYBORAD","ETH","QSPI","USIM"
 	};
 	const char *function0_names[3] = {
 			"RX","SCL","MOSI"
@@ -53,11 +53,11 @@ static luat_pin_peripheral_function_description_u luat_pin_function_analyze(char
 	const char *function3_names[3] = {
 			"CTS","CS","LRCLK"
 	};
-	const char *function4_names[2] = {
-			"MCLK","CMD"
+	const char *function4_names[4] = {
+			"MCLK","CMD","IO","DAT"
 	};
-	const char *function5_names[1] = {
-			"SCLK"
+	const char *function5_names[2] = {
+			"SCLK","RST"
 	};
 	description.code = 0;
 	for(description.peripheral_type = 0; description.peripheral_type < LUAT_MCU_PERIPHERAL_QTY; description.peripheral_type++)
@@ -253,6 +253,27 @@ static luat_pin_peripheral_function_description_u luat_pin_function_analyze(char
 					goto LUAT_PIN_FUNCTION_ANALYZE_DONE;
 				}
 				break;
+			case LUAT_MCU_PERIPHERAL_SIM:
+				if (description.peripheral_id) description.peripheral_id = 1;
+				function_id = search(string, len, function2_names, sizeof(function2_names)/4);
+				if (function_id >= 0)
+				{
+					description.function_id = 1;
+					goto LUAT_PIN_FUNCTION_ANALYZE_DONE;
+				}
+				function_id = search(string, len, function4_names, sizeof(function4_names)/4);
+				if (function_id >= 0)
+				{
+					description.function_id = 0;
+					goto LUAT_PIN_FUNCTION_ANALYZE_DONE;
+				}
+				function_id = search(string, len, function5_names, sizeof(function5_names)/4);
+				if (function_id >= 0)
+				{
+					description.function_id = 2;
+					goto LUAT_PIN_FUNCTION_ANALYZE_DONE;
+				}
+				break;
 			default:
 				break;
 			}

+ 1 - 0
luat/include/luat_mcu.h

@@ -18,6 +18,7 @@ typedef enum
 	LUAT_MCU_PERIPHERAL_KEYBORAD,
 	LUAT_MCU_PERIPHERAL_ETH,
 	LUAT_MCU_PERIPHERAL_QSPI,
+	LUAT_MCU_PERIPHERAL_SIM,
 	LUAT_MCU_PERIPHERAL_QTY
 }LUAT_MCU_PERIPHERAL_E;
 

+ 1 - 1
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirSHT30_1000/AirSHT30_1000.lua

@@ -1,4 +1,4 @@
---本文件中的主机是指I2C主机,具体指Air780EPM
+--本文件中的主机是指I2C主机,具体指Air780EHV
 --本文件中的从机是指I2C从机,具体指AirSHT30_1000配件板上的sht30温湿度传感器芯片
 
 local AirSHT30_1000 = 

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

@@ -10,7 +10,7 @@ VERSION:项目版本号,ascii string类型
 
 AirSHT30_1000是合宙设计生产的一款I2C接口的SHT30温湿度传感器配件板;
 本demo演示的核心功能为:
-Air8101核心板+AirSHT30_1000配件板,每隔1秒读取1次温湿度数据;
+Air780EHV核心板+AirSHT30_1000配件板,每隔1秒读取1次温湿度数据;
 更多说明参考本目录下的readme.md文件
 ]]
 PROJECT = "AirSHT30_1000"

+ 12 - 2
module/Air780EHM_Air780EHV_Air780EGH/demo/airtalk/extalk.lua

@@ -51,6 +51,7 @@ local SUCC = "success"
 local g_state = SP_T_NO_READY   -- 设备状态
 local g_mqttc = nil             -- mqtt客户端
 local g_local_id                -- 本机ID
+local g_stask_start = false                -- 本机ID
 local g_remote_id               -- 对端ID
 local g_s_type                  -- 对讲的模式,字符串形式
 local g_s_topic                 -- 对讲用的topic
@@ -301,6 +302,7 @@ local function handle_auth_result(obj)
     else
         sys.sendMsg(AIRTALK_TASK_NAME, MSG_AUTH_IND, false, 
             "鉴权失败" .. (obj and obj["info"] or "")) 
+        log.error("鉴权失败,可能是没有修改PRODUCT_KEY")
     end
 end
 
@@ -373,7 +375,7 @@ local function mqtt_cb(mqttc, event, topic, payload)
         extalk.speech_off(false, true)
         g_state = SP_T_NO_READY
     elseif event == "error" then
-        log.error("MQTT错误发生")
+        log.error("MQTT错误发生",topic,payload)
     end
 end
 
@@ -390,7 +392,7 @@ end
 local function airtalk_event_cb(event, param)
     log.info("airtalk event", event, param)
     if event == airtalk.EVENT_ERROR then
-        if param == airtalk.ERROR_NO_DATA then
+        if param == airtalk.ERROR_NO_DATA  and g_s_mode == airtalk.MODE_PERSON then
             log.error("长时间没有收到音频数据")
             extalk.speech_off(true, true)
         end
@@ -399,6 +401,12 @@ end
 
 -- MQTT任务主循环
 local function airtalk_mqtt_task()
+    if g_stask_start  then
+        log.info("airtalk task 已经初始化了")
+        return true
+    end
+    
+    g_stask_start = true
     local msg, online = nil, false
     
     -- 初始化本地ID
@@ -490,6 +498,7 @@ end
 
 -- 模块初始化
 function extalk.setup(extalk_configs)
+
     if not extalk_configs or type(extalk_configs) ~= "table" then
         log.error("AirTalk配置必须为table类型")
         return false
@@ -523,6 +532,7 @@ end
 
 -- 开始对讲
 function extalk.start(id)
+
     if g_state ~= SP_T_IDLE then
         log.warn("正在对讲无法开始,当前状态:", g_state)
         return false

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

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

+ 114 - 0
module/Air8000/demo/accessory_board/AirSHT30_1000/AirSHT30_1000.lua

@@ -0,0 +1,114 @@
+--本文件中的主机是指I2C主机,具体指Air8000
+--本文件中的从机是指I2C从机,具体指AirSHT30_1000配件板上的sht30温湿度传感器芯片
+
+local AirSHT30_1000 = 
+{
+    -- i2c_id:主机的i2c id;
+}   
+
+-- 从机地址为0x44
+local slave_addr = 0x44
+
+-- 计算数据表data中所有数据元素的crc8校验值
+local function crc8(data)
+    local crc = 0xFF
+    for i = 1, #data do
+        crc = bit.bxor(crc, data[i])
+        for j = 1, 8 do
+            crc = crc * 2
+            if crc >= 0x100 then
+                crc = bit.band(bit.bxor(crc, 0x31), 0xff)
+            end
+        end
+    end
+    return crc
+end
+
+
+--打开AirSHT30_1000;
+
+--i2c_id:number类型;
+--        主机使用的I2C ID,用来控制AirSHT30_1000;
+--        取值范围:仅支持0和1;
+--        如果没有传入此参数,则默认为0;
+
+--返回值:成功返回true,失败返回false
+function AirSHT30_1000.open(i2c_id)
+    --如果i2c_id为nil,则赋值为默认值0
+    if i2c_id==nil then i2c_id=0 end
+
+    --检查参数的合法性
+    if not (i2c_id == 0 or i2c_id == 1) then
+        log.error("AirSHT30_1000.open", "invalid i2c_id", i2c_id)
+        return false
+    end
+
+    AirSHT30_1000.i2c_id = i2c_id
+    
+    --初始化I2C
+    if i2c.setup(i2c_id, i2c.FAST) ~= 1 then
+        log.error("AirSHT30_1000.open", "i2c.setup error", i2c_id)
+        return false
+    end
+
+    return true
+end
+
+--读取温湿度数据;
+
+--返回值:失败返回false;
+--       成功返回两个值,第一个为摄氏温度值(number类型,例如23.6表示23.6摄氏度),第二个为百分比湿度值(number类型,例如67表示67%的湿度)
+function AirSHT30_1000.read()
+
+    -- 发送启动测量命令(高精度)
+    i2c.send(AirSHT30_1000.i2c_id, slave_addr, {0x24, 0x00})
+        
+    -- 等待测量完成(SHT30高精度测量需~15ms)
+    sys.wait(20)
+    
+    -- 读取6字节数据(温度高/低 + CRC,湿度高/低 + CRC)
+    local data = i2c.recv(AirSHT30_1000.i2c_id, slave_addr, 6)
+
+    -- 如果没有读取到6字节数据
+    if type(data)~="string" or data:len()~=6 then
+        log.error("AirSHT30_1000.read", "i2c.recv error")
+        return false
+    end
+
+    -- log.info("AirSHT30_1000.read", data:toHex())
+
+    --如果校验值正确
+    if crc8({data:byte(1), data:byte(2)}) == data:byte(3) and crc8({data:byte(4), data:byte(5)}) == data:byte(6) then 
+        -- 提取原始温度值
+        local temp_raw = (data:byte(1) << 8) | data:byte(2)
+        -- 提取原始湿度值
+        local hum_raw = (data:byte(4) << 8) | data:byte(5)
+        
+        -- 转换为实际值(根据SHT30数据手册公式)
+        local temprature = (-45 + 175 * temp_raw / 65535.0)
+        local humidity = (100 * hum_raw / 65535.0)
+        
+        -- 打印输出结果(保留2位小数)
+        -- log.info("AirSHT30_1000.read", "temprature", string.format("%.2f ℃", temprature))
+        -- log.info("AirSHT30_1000.read", "temprature", string.format("%.2f %%RH", humidity))
+
+        return temprature, humidity
+    else
+        log.error("AirSHT30_1000.read", "crc error", i2c_id)
+        return false
+    end
+end
+
+
+--关闭AirSHT30_1000
+
+--返回值:成功返回true,失败返回false
+function AirSHT30_1000.close()
+    --close接口没有返回值,理论上不会关闭失败
+    i2c.close(AirSHT30_1000.i2c_id)
+
+    return true
+end
+
+
+return AirSHT30_1000

+ 5 - 7
module/Air8101/demo/accessory_board/AirPHY_1000/main.lua → module/Air8000/demo/accessory_board/AirSHT30_1000/main.lua

@@ -8,12 +8,12 @@ VERSION:项目版本号,ascii string类型
             因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
         如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
 
-AirPHY_1000是合宙设计生产的一款搭载LAN8720Ai芯片的以太网配件板;
+AirSHT30_1000是合宙设计生产的一款I2C接口的SHT30温湿度传感器配件板;
 本demo演示的核心功能为:
-Air8101核心板+AirPHY_1000配件板,使用配件板上的以太网口通过网线连接路由器,演示以太网数传功能
+Air8000核心板+AirSHT30_1000配件板,每隔1秒读取1次温湿度数据
 更多说明参考本目录下的readme.md文件
 ]]
-PROJECT = "AirPHY_1000"
+PROJECT = "AirSHT30_1000"
 VERSION = "001.000.000"
 
 
@@ -53,10 +53,8 @@ end
 --     log.info("mem.sys", rtos.meminfo("sys"))
 --  end, 3000)
 
--- 加载以太网连接管理功能模块
-require "phy_app"
--- 加载http get应用的功能模块
-require "http_app"
+ -- 加载sht30应用模块
+ require "sht30_app"
 
 
 -- 用户代码已结束---------------------------------------------

+ 57 - 0
module/Air8000/demo/accessory_board/AirSHT30_1000/readme.md

@@ -0,0 +1,57 @@
+
+## 演示功能概述
+
+AirSHT30_1000是合宙设计生产的一款I2C接口的SHT30温湿度传感器配件板;
+
+本demo演示的核心功能为:
+
+Air8000开发板+AirSHT30_1000配件板,每隔1秒读取1次温湿度数据;
+
+
+## 核心板+配件板资料
+
+[Air8000开发板+配件板相关资料](https://docs.openluat.com/air8000/product/shouce/#air8000_1)
+
+
+## 演示硬件环境
+
+![](https://docs.openluat.com/accessory/AirSHT30_1000/image/connect_8000.jpg)
+
+![](https://docs.openluat.com/accessory/AirSHT30_1000/image/8000.png)
+
+1、Air8000开发板
+
+2、AirSHT30_1000配件板
+
+3、母对母的杜邦线4根
+
+| Air8000开发板 | AirSHT30_1000配件板|
+| ------------ | ------------------ |
+|     VDD_EXT     |         3V3        |
+|     GND   |         GND        |
+| I2C1_SDA  |         SDA        |
+| I2C1_SCL |         SCL        |
+
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+2、[Air8000最新版本的内核固件](https://docs.openluat.com/air8000/luatos/firmware/)
+
+
+## 演示操作步骤
+
+1、搭建好演示硬件环境
+
+2、不需要修改demo脚本代码
+
+3、Luatools烧录内核固件和demo脚本代码
+
+4、烧录成功后,自动开机运行
+
+5、通过观察Luatools的运行日志,每隔1秒出现一次类似于下面的打印,就表示测试正常
+
+``` lua
+[2025-09-23 14:56:38.486][000000007.559] I/user.read_sht30_task_func temprature 27.13 ℃
+[2025-09-23 14:56:38.486][000000007.559] I/user.read_sht30_task_func humidity 70.86 %RH

+ 35 - 0
module/Air8000/demo/accessory_board/AirSHT30_1000/sht30_app.lua

@@ -0,0 +1,35 @@
+--加载AirSHT30_1000驱动文件
+local air_sht30 = require "AirSHT30_1000"
+
+
+--每隔1秒读取一次温湿度数据
+local function read_sht30_task_func()
+    
+    --打开sht30硬件
+    air_sht30.open(1)
+
+    while true do
+        --读取温湿度数据
+        local temprature, humidity = air_sht30.read()
+
+        --读取成功
+        if temprature then
+            -- 打印输出结果(保留2位小数)
+            log.info("read_sht30_task_func", "temprature", string.format("%.2f ℃", temprature))
+            log.info("read_sht30_task_func", "humidity", string.format("%.2f %%RH", humidity))
+        --读取失败
+        else
+            log.error("read_sht30_task_func", "read error")
+        end
+
+        --等待1秒
+        sys.wait(1000)
+    end
+
+    --关闭sht30硬件
+    air_sht30.close()
+end
+
+--创建一个task,并且运行task的主函数read_sht30_task_func
+sys.taskInit(read_sht30_task_func)
+

+ 12 - 2
module/Air8000/demo/airtalk/extalk.lua

@@ -51,6 +51,7 @@ local SUCC = "success"
 local g_state = SP_T_NO_READY   -- 设备状态
 local g_mqttc = nil             -- mqtt客户端
 local g_local_id                -- 本机ID
+local g_stask_start = false                -- 本机ID
 local g_remote_id               -- 对端ID
 local g_s_type                  -- 对讲的模式,字符串形式
 local g_s_topic                 -- 对讲用的topic
@@ -301,6 +302,7 @@ local function handle_auth_result(obj)
     else
         sys.sendMsg(AIRTALK_TASK_NAME, MSG_AUTH_IND, false, 
             "鉴权失败" .. (obj and obj["info"] or "")) 
+        log.error("鉴权失败,可能是没有修改PRODUCT_KEY")
     end
 end
 
@@ -373,7 +375,7 @@ local function mqtt_cb(mqttc, event, topic, payload)
         extalk.speech_off(false, true)
         g_state = SP_T_NO_READY
     elseif event == "error" then
-        log.error("MQTT错误发生")
+        log.error("MQTT错误发生",topic,payload)
     end
 end
 
@@ -390,7 +392,7 @@ end
 local function airtalk_event_cb(event, param)
     log.info("airtalk event", event, param)
     if event == airtalk.EVENT_ERROR then
-        if param == airtalk.ERROR_NO_DATA then
+        if param == airtalk.ERROR_NO_DATA  and g_s_mode == airtalk.MODE_PERSON then
             log.error("长时间没有收到音频数据")
             extalk.speech_off(true, true)
         end
@@ -399,6 +401,12 @@ end
 
 -- MQTT任务主循环
 local function airtalk_mqtt_task()
+    if g_stask_start  then
+        log.info("airtalk task 已经初始化了")
+        return true
+    end
+    
+    g_stask_start = true
     local msg, online = nil, false
     
     -- 初始化本地ID
@@ -490,6 +498,7 @@ end
 
 -- 模块初始化
 function extalk.setup(extalk_configs)
+
     if not extalk_configs or type(extalk_configs) ~= "table" then
         log.error("AirTalk配置必须为table类型")
         return false
@@ -523,6 +532,7 @@ end
 
 -- 开始对讲
 function extalk.start(id)
+
     if g_state ~= SP_T_IDLE then
         log.warn("正在对讲无法开始,当前状态:", g_state)
         return false

+ 0 - 26
module/Air8000/demo/airtalk/main.lua

@@ -43,32 +43,6 @@ if wdt then
     sys.timerLoopStart(wdt.feed, 3000)
 end
 
--- exnetif.set_priority_order({ { -- 次优先级:WiFi
---     WIFI = {
-
---         ssid = "机房-降功耗,找合宙!",
-
---         password = "Air123456", 
-
---     }
--- }})
-
--- -- 设置网络状态回调
-
--- exnetif.notify_status(function(net_type, adapter)
-
---     log.info("网络切换至:", net_type)
-
--- end)
-
--- -- wifi的STA相关事件
--- sys.subscribe("WLAN_STA_INC", function(evt, data)
---     -- evt 可能的值有: "CONNECTED", "DISCONNECTED"
---     -- 当evt=CONNECTED, data是连接的AP的ssid, 字符串类型
---     -- 当evt=DISCONNECTED, data断开的原因, 整数类型
---     log.info("收到STA事件", evt, data)
--- end)
-
 require "talk"            --  启动airtalk
 
 -- 音频对内存影响较大,不断的打印内存,用于判断是否异常

+ 11 - 0
module/Air8000/demo/airtalk/talk.lua

@@ -168,6 +168,16 @@ local function handle_key_press(is_power_key)
 end
 
 
+local function lower_enter()     -- 如果需要进入低功耗,请在task 中调用此函数
+    -- WiFi模组进入低功耗模式
+    pm.power(pm.WORK_MODE, 1, 1)
+    -- 同时4G进入低功耗模式
+    pm.power(pm.WORK_MODE, 1)
+    sys.wait(20)
+    -- 暂停airlink通信,进一步降低功耗
+    airlink.pause(1)
+    
+end
 
 -- 用户主任务
 local function user_main_task()
@@ -187,6 +197,7 @@ local function user_main_task()
     end
     log.info("extalk初始化成功")
     LED(0)
+    -- lower_enter()               -- 如果需要进入低功耗,请打开此函数
     -- 等待按键消息并处理
     while true do
         local msg = sys.waitMsg(USER_TASK_NAME, MSG_KEY_PRESS)

+ 28 - 0
module/Air8000/demo/ble/central/check_wifi.lua

@@ -0,0 +1,28 @@
+--[[
+@module  check_wifi
+@summary 远程升级wifi固件模块
+@version 1.1
+@date    2025.09.23
+@author  拓毅恒
+@usage
+检查WiFi版本并自动升级
+功能:检查当前Air8000模组的WiFi固件是否为最新版本,若不是则自动启动升级(需插入可联网的SIM卡)。
+说明:Air8000的蓝牙功能依赖WiFi协处理器,需确保WiFi固件为最新版本。
+注意:升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定。
+
+本文件没有对外接口,直接在main.lua中require "check_wifi"就可以加载运行。
+]]
+
+local exfotawifi = require("exfotawifi")
+
+local function fota_wifi_task()
+    local result = exfotawifi.request()
+    if result then
+        log.info("exfotawifi", "升级任务执行成功")
+    else
+        log.info("exfotawifi", "升级任务执行失败")
+    end
+end
+
+-- 在设备启动时检查SIM卡状态
+sys.taskInit(fota_wifi_task)

+ 1 - 1
module/Air8000/demo/ble/central/main.lua

@@ -63,7 +63,7 @@ end
 
 -- Air8000蓝牙依赖WiFi协处理器,如果蓝牙功能使用异常需要打开此注释更新WiFi固件
 -- 升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定
--- require "exfotawifi" 
+require "check_wifi" 
 
 -- 加载BLE Central(中心设备)主控制模块
 require "ble_client_main"

+ 28 - 0
module/Air8000/demo/ble/ibeacon/check_wifi.lua

@@ -0,0 +1,28 @@
+--[[
+@module  check_wifi
+@summary 远程升级wifi固件模块
+@version 1.1
+@date    2025.09.23
+@author  拓毅恒
+@usage
+检查WiFi版本并自动升级
+功能:检查当前Air8000模组的WiFi固件是否为最新版本,若不是则自动启动升级(需插入可联网的SIM卡)。
+说明:Air8000的蓝牙功能依赖WiFi协处理器,需确保WiFi固件为最新版本。
+注意:升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定。
+
+本文件没有对外接口,直接在main.lua中require "check_wifi"就可以加载运行。
+]]
+
+local exfotawifi = require("exfotawifi")
+
+local function fota_wifi_task()
+    local result = exfotawifi.request()
+    if result then
+        log.info("exfotawifi", "升级任务执行成功")
+    else
+        log.info("exfotawifi", "升级任务执行失败")
+    end
+end
+
+-- 在设备启动时检查SIM卡状态
+sys.taskInit(fota_wifi_task)

+ 1 - 1
module/Air8000/demo/ble/ibeacon/main.lua

@@ -62,7 +62,7 @@ end
 
 -- Air8000蓝牙依赖WiFi协处理器,如果蓝牙功能使用异常需要打开此注释更新WiFi固件
 -- 升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定
--- require "exfotawifi" 
+-- require "check_wifi" 
 
 -- 加载 ibeacon 蓝牙功能模块
 require "ble_ibeacon"

+ 28 - 0
module/Air8000/demo/ble/peripheral/check_wifi.lua

@@ -0,0 +1,28 @@
+--[[
+@module  check_wifi
+@summary 远程升级wifi固件模块
+@version 1.1
+@date    2025.09.23
+@author  拓毅恒
+@usage
+检查WiFi版本并自动升级
+功能:检查当前Air8000模组的WiFi固件是否为最新版本,若不是则自动启动升级(需插入可联网的SIM卡)。
+说明:Air8000的蓝牙功能依赖WiFi协处理器,需确保WiFi固件为最新版本。
+注意:升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定。
+
+本文件没有对外接口,直接在main.lua中require "check_wifi"就可以加载运行。
+]]
+
+local exfotawifi = require("exfotawifi")
+
+local function fota_wifi_task()
+    local result = exfotawifi.request()
+    if result then
+        log.info("exfotawifi", "升级任务执行成功")
+    else
+        log.info("exfotawifi", "升级任务执行失败")
+    end
+end
+
+-- 在设备启动时检查SIM卡状态
+sys.taskInit(fota_wifi_task)

+ 1 - 1
module/Air8000/demo/ble/peripheral/main.lua

@@ -64,7 +64,7 @@ end
 
 -- Air8000蓝牙依赖WiFi协处理器,如果蓝牙功能使用异常需要打开此注释更新WiFi固件
 -- 升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定
--- require "exfotawifi" 
+-- require "check_wifi" 
 
 -- 加载BLE peripheral(外围设备)主控制模块
 require "ble_server_main"

+ 28 - 0
module/Air8000/demo/ble/scan/check_wifi.lua

@@ -0,0 +1,28 @@
+--[[
+@module  check_wifi
+@summary 远程升级wifi固件模块
+@version 1.1
+@date    2025.09.23
+@author  拓毅恒
+@usage
+检查WiFi版本并自动升级
+功能:检查当前Air8000模组的WiFi固件是否为最新版本,若不是则自动启动升级(需插入可联网的SIM卡)。
+说明:Air8000的蓝牙功能依赖WiFi协处理器,需确保WiFi固件为最新版本。
+注意:升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定。
+
+本文件没有对外接口,直接在main.lua中require "check_wifi"就可以加载运行。
+]]
+
+local exfotawifi = require("exfotawifi")
+
+local function fota_wifi_task()
+    local result = exfotawifi.request()
+    if result then
+        log.info("exfotawifi", "升级任务执行成功")
+    else
+        log.info("exfotawifi", "升级任务执行失败")
+    end
+end
+
+-- 在设备启动时检查SIM卡状态
+sys.taskInit(fota_wifi_task)

+ 1 - 1
module/Air8000/demo/ble/scan/main.lua

@@ -59,7 +59,7 @@ end
 
 -- Air8000蓝牙依赖WiFi协处理器,如果蓝牙功能使用异常需要打开此注释更新WiFi固件
 -- 升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定
--- require "exfotawifi" 
+-- require "check_wifi" 
 
 -- 加载 scan 蓝牙功能模块
 require "ble_scan"

+ 27 - 0
module/Air8000/demo/config_wifi_network/ble_config_wifi/check_wifi.lua

@@ -0,0 +1,27 @@
+--[[
+@module  check_wifi
+@summary 远程升级wifi固件模块
+@version 1.1
+@date    2025.09.23
+@author  拓毅恒
+@usage
+检查WiFi版本并自动升级
+功能:检查当前Air8000模组的WiFi固件是否为最新版本,若不是则自动启动升级(需插入可联网的SIM卡)。
+注意:升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定。
+
+本文件没有对外接口,直接在main.lua中require "check_wifi"就可以加载运行。
+]]
+
+local exfotawifi = require("exfotawifi")
+
+local function fota_wifi_task()
+    local result = exfotawifi.request()
+    if result then
+        log.info("exfotawifi", "升级任务执行成功")
+    else
+        log.info("exfotawifi", "升级任务执行失败")
+    end
+end
+
+-- 在设备启动时检查SIM卡状态
+sys.taskInit(fota_wifi_task)

+ 1 - 1
module/Air8000/demo/config_wifi_network/ble_config_wifi/main.lua

@@ -82,7 +82,7 @@ end
 -- 使用蓝牙配网功能需要WIFI版本≥14
 -- 如果模组中WiFi版本<14,则需要打开此功能启动升级
 -- 升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定
--- require "exfotawifi" 
+-- require "check_wifi" 
 
 -- 加载 ble_config_wifi 主应用功能模块
 require "ble_config_wifi"

+ 27 - 0
module/Air8000/demo/wlan/AP/check_wifi.lua

@@ -0,0 +1,27 @@
+--[[
+@module  check_wifi
+@summary 远程升级wifi固件模块
+@version 1.1
+@date    2025.09.23
+@author  拓毅恒
+@usage
+检查WiFi版本并自动升级
+功能:检查当前Air8000模组的WiFi固件是否为最新版本,若不是则自动启动升级(需插入可联网的SIM卡)。
+注意:升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定。
+
+本文件没有对外接口,直接在main.lua中require "check_wifi"就可以加载运行。
+]]
+
+local exfotawifi = require("exfotawifi")
+
+local function fota_wifi_task()
+    local result = exfotawifi.request()
+    if result then
+        log.info("exfotawifi", "升级任务执行成功")
+    else
+        log.info("exfotawifi", "升级任务执行失败")
+    end
+end
+
+-- 在设备启动时检查SIM卡状态
+sys.taskInit(fota_wifi_task)

+ 1 - 1
module/Air8000/demo/wlan/AP/main.lua

@@ -9,7 +9,7 @@ httpplus = require("httpplus")
 
 -- 如果无法使用AP功能,可以开启此功能升级WiFi固件版本后再次尝试
 -- 升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定
--- local exfotawifi = require("exfotawifi") 
+-- require "check_wifi" 
 
 function test_ap()
     log.info("执行AP创建操作")

+ 27 - 0
module/Air8000/demo/wlan/Power_Save/check_wifi.lua

@@ -0,0 +1,27 @@
+--[[
+@module  check_wifi
+@summary 远程升级wifi固件模块
+@version 1.1
+@date    2025.09.23
+@author  拓毅恒
+@usage
+检查WiFi版本并自动升级
+功能:检查当前Air8000模组的WiFi固件是否为最新版本,若不是则自动启动升级(需插入可联网的SIM卡)。
+注意:升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定。
+
+本文件没有对外接口,直接在main.lua中require "check_wifi"就可以加载运行。
+]]
+
+local exfotawifi = require("exfotawifi")
+
+local function fota_wifi_task()
+    local result = exfotawifi.request()
+    if result then
+        log.info("exfotawifi", "升级任务执行成功")
+    else
+        log.info("exfotawifi", "升级任务执行失败")
+    end
+end
+
+-- 在设备启动时检查SIM卡状态
+sys.taskInit(fota_wifi_task)

+ 1 - 1
module/Air8000/demo/wlan/Power_Save/main.lua

@@ -8,7 +8,7 @@ dhcpsrv = require("dhcpsrv")
 
 -- 如果无法使用此功能,可以开启此功能升级WiFi固件版本后再次尝试
 -- 升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定
--- local exfotawifi = require("exfotawifi") 
+-- require "check_wifi" 
 
 -- 通过boot按键方便刷Air8000S
 function PWR8000S(val)

+ 27 - 0
module/Air8000/demo/wlan/STA/check_wifi.lua

@@ -0,0 +1,27 @@
+--[[
+@module  check_wifi
+@summary 远程升级wifi固件模块
+@version 1.1
+@date    2025.09.23
+@author  拓毅恒
+@usage
+检查WiFi版本并自动升级
+功能:检查当前Air8000模组的WiFi固件是否为最新版本,若不是则自动启动升级(需插入可联网的SIM卡)。
+注意:升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定。
+
+本文件没有对外接口,直接在main.lua中require "check_wifi"就可以加载运行。
+]]
+
+local exfotawifi = require("exfotawifi")
+
+local function fota_wifi_task()
+    local result = exfotawifi.request()
+    if result then
+        log.info("exfotawifi", "升级任务执行成功")
+    else
+        log.info("exfotawifi", "升级任务执行失败")
+    end
+end
+
+-- 在设备启动时检查SIM卡状态
+sys.taskInit(fota_wifi_task)

+ 1 - 1
module/Air8000/demo/wlan/STA/main.lua

@@ -9,7 +9,7 @@ httpplus = require("httpplus")
 
 -- 如果无法使用STA功能,可以开启此功能升级WiFi固件版本后再次尝试
 -- 升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定
--- local exfotawifi = require("exfotawifi") 
+-- require "check_wifi" 
 
 -- wifi的STA相关事件
 sys.subscribe("WLAN_STA_INC", function(evt, data)

+ 27 - 0
module/Air8000/demo/wlan/wifi_configuration_network_by_ap/check_wifi.lua

@@ -0,0 +1,27 @@
+--[[
+@module  check_wifi
+@summary 远程升级wifi固件模块
+@version 1.1
+@date    2025.09.23
+@author  拓毅恒
+@usage
+检查WiFi版本并自动升级
+功能:检查当前Air8000模组的WiFi固件是否为最新版本,若不是则自动启动升级(需插入可联网的SIM卡)。
+注意:升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定。
+
+本文件没有对外接口,直接在main.lua中require "check_wifi"就可以加载运行。
+]]
+
+local exfotawifi = require("exfotawifi")
+
+local function fota_wifi_task()
+    local result = exfotawifi.request()
+    if result then
+        log.info("exfotawifi", "升级任务执行成功")
+    else
+        log.info("exfotawifi", "升级任务执行失败")
+    end
+end
+
+-- 在设备启动时检查SIM卡状态
+sys.taskInit(fota_wifi_task)

+ 1 - 1
module/Air8000/demo/wlan/wifi_configuration_network_by_ap/main.lua

@@ -11,7 +11,7 @@ httpplus = require("httpplus")
 
 -- 如果无法使用此功能,可以开启此功能升级WiFi固件版本后再次尝试
 -- 升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定
--- local exfotawifi = require("exfotawifi") 
+-- require "check_wifi" 
 
 -- 初始化LED灯, 这里演示控制Air8000核心板蓝灯,其他开发板请查看硬件原理图自行修改(如果使用整机开发板可以用GPIO146)
 local LEDA = gpio.setup(20, 0, gpio.PULLUP)

+ 27 - 0
module/Air8000/demo/wlan/wifi_scan/check_wifi.lua

@@ -0,0 +1,27 @@
+--[[
+@module  check_wifi
+@summary 远程升级wifi固件模块
+@version 1.1
+@date    2025.09.23
+@author  拓毅恒
+@usage
+检查WiFi版本并自动升级
+功能:检查当前Air8000模组的WiFi固件是否为最新版本,若不是则自动启动升级(需插入可联网的SIM卡)。
+注意:升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定。
+
+本文件没有对外接口,直接在main.lua中require "check_wifi"就可以加载运行。
+]]
+
+local exfotawifi = require("exfotawifi")
+
+local function fota_wifi_task()
+    local result = exfotawifi.request()
+    if result then
+        log.info("exfotawifi", "升级任务执行成功")
+    else
+        log.info("exfotawifi", "升级任务执行失败")
+    end
+end
+
+-- 在设备启动时检查SIM卡状态
+sys.taskInit(fota_wifi_task)

+ 1 - 1
module/Air8000/demo/wlan/wifi_scan/main.lua

@@ -8,7 +8,7 @@ require "sysplus"
 
 -- 如果无法使用此功能,可以开启此功能升级WiFi固件版本后再次尝试
 -- 升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定
--- local exfotawifi = require("exfotawifi") 
+-- require "check_wifi" 
 
 function test_scan()
     while 1 do

+ 27 - 0
module/Air8000/project/wifi_ap_read_file/check_wifi.lua

@@ -0,0 +1,27 @@
+--[[
+@module  check_wifi
+@summary 远程升级wifi固件模块
+@version 1.1
+@date    2025.09.23
+@author  拓毅恒
+@usage
+检查WiFi版本并自动升级
+功能:检查当前Air8000模组的WiFi固件是否为最新版本,若不是则自动启动升级(需插入可联网的SIM卡)。
+注意:升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定。
+
+本文件没有对外接口,直接在main.lua中require "check_wifi"就可以加载运行。
+]]
+
+local exfotawifi = require("exfotawifi")
+
+local function fota_wifi_task()
+    local result = exfotawifi.request()
+    if result then
+        log.info("exfotawifi", "升级任务执行成功")
+    else
+        log.info("exfotawifi", "升级任务执行失败")
+    end
+end
+
+-- 在设备启动时检查SIM卡状态
+sys.taskInit(fota_wifi_task)

+ 5 - 0
module/Air8000/project/wifi_ap_read_file/main.lua

@@ -76,6 +76,11 @@ end
 --     log.info("mem.sys", rtos.meminfo("sys"))
 -- end, 3000)
 
+-- 使用文件管理系统功能需要WIFI版本≥14
+-- 如果模组中WiFi版本<14,则需要打开此功能启动升级
+-- 升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定
+-- require "check_wifi" 
+
 -- 引入任务控制模块
 require"task_control"
 

+ 63 - 36
module/Air8000/project/整机开发板出厂工程/user/extalk.lua

@@ -51,6 +51,7 @@ local SUCC = "success"
 local g_state = SP_T_NO_READY   -- 设备状态
 local g_mqttc = nil             -- mqtt客户端
 local g_local_id                -- 本机ID
+local g_stask_start = false                -- 本机ID
 local g_remote_id               -- 对端ID
 local g_s_type                  -- 对讲的模式,字符串形式
 local g_s_topic                 -- 对讲用的topic
@@ -76,6 +77,24 @@ local function check_param(param, expected_type, name)
     return true
 end
 
+-- MQTT消息发布函数,集中处理所有发布操作并打印日志
+local function publish_message(topic, payload)
+    if g_mqttc then
+        log.info("MQTT发布 - 主题:", topic, "内容:", payload)
+        g_mqttc:publish(topic, payload)
+    else
+        log.error("MQTT客户端未初始化,无法发布消息")
+    end
+end
+
+
+-- 对讲超时处理
+function extalk.wait_speech_to()
+    log.info("主动请求对讲超时无应答")
+    extalk.speech_off(true, false)
+end
+
+
 -- 发送鉴权消息
 local function auth()
     if g_state == SP_T_NO_READY and g_mqttc then
@@ -84,19 +103,22 @@ local function auth()
             ["key"] = extalk_configs_local.key, 
             ["device_type"] = 1
         })
-        g_mqttc:publish(topic, payload)
+        publish_message(topic, payload)
     end
 end
 
 -- 发送心跳消息
 local function heart()
-    if g_state == SP_T_CONNECTED and g_mqttc then
+    if  g_mqttc then
+        adc.open(adc.CH_VBAT)
+        local vbat = adc.get(adc.CH_VBAT)
+        adc.close(adc.CH_VBAT)
         local topic = string.format("ctrl/uplink/%s/0005", g_local_id)
         local payload = json.encode({
-            ["from"] = g_local_id, 
-            ["to"] = g_remote_id
+            ["csq"] = mobile.csq(), 
+            ["battery"] = vbat
         })
-        g_mqttc:publish(topic, payload)
+        publish_message(topic, payload)
     end
 end
 
@@ -109,12 +131,12 @@ local function speech_on(ssrc, sample)
     log.info("对讲模式", g_s_mode)
     airtalk.speech(true, g_s_mode, sample)
     sys.sendMsg(AIRTALK_TASK_NAME, MSG_SPEECH_ON_IND, true) 
-    sys.timerLoopStart(heart, extalk_configs_local.heart_break_time * 1000)
-    sys.timerStopAll(wait_speech_to)
+    -- sys.timerLoopStart(heart, extalk_configs_local.heart_break_time * 1000)
+    sys.timerStopAll(extalk.wait_speech_to)
 end
 
 -- 结束对讲
-local function speech_off(need_upload, need_ind)
+function extalk.speech_off(need_upload, need_ind)
     if g_state == SP_T_CONNECTED then
         g_mqttc:unsubscribe(g_s_topic)
         airtalk.speech(false)
@@ -123,12 +145,12 @@ local function speech_off(need_upload, need_ind)
     
     g_state = SP_T_IDLE
     sys.timerStopAll(auth)
-    sys.timerStopAll(heart)
-    sys.timerStopAll(wait_speech_to)
+
+    sys.timerStopAll(extalk.wait_speech_to)
     
     if need_upload and g_mqttc then
         local topic = string.format("ctrl/uplink/%s/0004", g_local_id)
-        g_mqttc:publish(topic, json.encode({["to"] = g_remote_id}))
+        publish_message(topic, json.encode({["to"] = g_remote_id}))
     end
 
     if need_ind then
@@ -136,11 +158,6 @@ local function speech_off(need_upload, need_ind)
     end
 end
 
--- 对讲超时处理
-local function wait_speech_to()
-    log.info("主动请求对讲超时无应答")
-    speech_off(true, false)
-end
 
 -- 命令处理:请求对讲应答
 local function handle_speech_response(obj)
@@ -171,7 +188,7 @@ local function handle_incoming_call(obj)
             ["topic"] = obj and obj["topic"] or "", 
             ["info"] = "无效的请求参数"
         }
-        g_mqttc:publish(string.format("ctrl/uplink/%s/8102", g_local_id), json.encode(response))
+        publish_message(string.format("ctrl/uplink/%s/8102", g_local_id), json.encode(response))
         return
     end
 
@@ -183,7 +200,7 @@ local function handle_incoming_call(obj)
             ["topic"] = obj["topic"], 
             ["info"] = "device is busy"
         }
-        g_mqttc:publish(string.format("ctrl/uplink/%s/8102", g_local_id), json.encode(response))
+        publish_message(string.format("ctrl/uplink/%s/8102", g_local_id), json.encode(response))
         return
     end
 
@@ -197,7 +214,7 @@ local function handle_incoming_call(obj)
             ["topic"] = obj["topic"], 
             ["info"] = "topic error"
         }
-        g_mqttc:publish(string.format("ctrl/uplink/%s/8102", g_local_id), json.encode(response))
+        publish_message(string.format("ctrl/uplink/%s/8102", g_local_id), json.encode(response))
         return
     end
 
@@ -242,7 +259,7 @@ local function handle_incoming_call(obj)
     end
 
     -- 发送响应
-    g_mqttc:publish(string.format("ctrl/uplink/%s/8102", g_local_id), json.encode(response))
+    publish_message(string.format("ctrl/uplink/%s/8102", g_local_id), json.encode(response))
 end
 
 -- 命令处理:对端挂断
@@ -255,13 +272,13 @@ local function handle_remote_hangup(obj)
         log.info("0103", obj, obj["type"], g_s_type)
         if obj and obj["type"] == g_s_type then
             response = {["result"] = SUCC, ["info"] = ""}
-            speech_off(false, true)
+            extalk.speech_off(false, true)
         else
             response = {["result"] = "failed", ["info"] = "type mismatch"}
         end
     end
     
-    g_mqttc:publish(string.format("ctrl/uplink/%s/8103", g_local_id), json.encode(response))
+    publish_message(string.format("ctrl/uplink/%s/8103", g_local_id), json.encode(response))
 end
 
 -- 命令处理:更新设备列表
@@ -274,16 +291,18 @@ local function handle_device_list_update(obj)
         response = {["result"] = "failed", ["info"] = "json info error"}
     end
     
-    g_mqttc:publish(string.format("ctrl/uplink/%s/8101", g_local_id), json.encode(response))
+    publish_message(string.format("ctrl/uplink/%s/8101", g_local_id), json.encode(response))
 end
 
 -- 命令处理:鉴权结果
 local function handle_auth_result(obj)
     if obj and obj["result"] == SUCC then
-        g_mqttc:publish(string.format("ctrl/uplink/%s/0002", g_local_id), "")  -- 更新列表
+        publish_message(string.format("ctrl/uplink/%s/0002", g_local_id), "")  -- 更新列表
+        sys.timerLoopStart(heart, extalk_configs_local.heart_break_time * 1000)   --  发起心跳
     else
         sys.sendMsg(AIRTALK_TASK_NAME, MSG_AUTH_IND, false, 
             "鉴权失败" .. (obj and obj["info"] or "")) 
+        log.error("鉴权失败,可能是没有修改PRODUCT_KEY")
     end
 end
 
@@ -344,7 +363,7 @@ local function mqtt_cb(mqttc, event, topic, payload)
                     "订阅失败" .. "ctrl/downlink/" .. g_local_id .. "/#") 
             end
         elseif g_state == SP_T_CONNECTED and not topic then
-            speech_off(false, true)
+            extalk.speech_off(false, true)
         end
     elseif event == "recv" then
         local result = string.match(topic, g_dl_topic)
@@ -353,17 +372,17 @@ local function mqtt_cb(mqttc, event, topic, payload)
             analyze_v1(result, topic, obj)
         end
     elseif event == "disconnect" then
-        speech_off(false, true)
+        extalk.speech_off(false, true)
         g_state = SP_T_NO_READY
     elseif event == "error" then
-        log.error("MQTT错误发生")
+        log.error("MQTT错误发生",topic,payload)
     end
 end
 
 -- 任务消息处理
 local function task_cb(msg)
     if msg[1] == MSG_SPEECH_CONNECT_TO then
-        speech_off(true, false)
+        extalk.speech_off(true, false)
     else
         log.info("未处理消息", msg[1], msg[2], msg[3], msg[4])
     end
@@ -373,15 +392,21 @@ end
 local function airtalk_event_cb(event, param)
     log.info("airtalk event", event, param)
     if event == airtalk.EVENT_ERROR then
-        if param == airtalk.ERROR_NO_DATA then
+        if param == airtalk.ERROR_NO_DATA  and g_s_mode == airtalk.MODE_PERSON then
             log.error("长时间没有收到音频数据")
-            speech_off(true, true)
+            extalk.speech_off(true, true)
         end
     end
 end
 
 -- MQTT任务主循环
 local function airtalk_mqtt_task()
+    if g_stask_start  then
+        log.info("airtalk task 已经初始化了")
+        return true
+    end
+    
+    g_stask_start = true
     local msg, online = nil, false
     
     -- 初始化本地ID
@@ -445,7 +470,7 @@ local function airtalk_mqtt_task()
                     if g_state ~= SP_T_CONNECTING and g_state ~= SP_T_CONNECTED then
                         log.info("没有对讲", g_state)
                     else
-                        speech_off(true, false)
+                        extalk.speech_off(true, false)
                     end
                 elseif msg[1] == MSG_SPEECH_ON_IND then
                     if extalk_configs_local.state_cbfnc then
@@ -473,6 +498,7 @@ end
 
 -- 模块初始化
 function extalk.setup(extalk_configs)
+
     if not extalk_configs or type(extalk_configs) ~= "table" then
         log.error("AirTalk配置必须为table类型")
         return false
@@ -506,6 +532,7 @@ end
 
 -- 开始对讲
 function extalk.start(id)
+
     if g_state ~= SP_T_IDLE then
         log.warn("正在对讲无法开始,当前状态:", g_state)
         return false
@@ -520,9 +547,9 @@ function extalk.start(id)
         g_s_topic = string.format("audio/%s/all/%s", 
             g_local_id, string.sub(tostring(mcu.ticks()), -4, -1))
         
-        g_mqttc:publish(string.format("ctrl/uplink/%s/0003", g_local_id), 
+        publish_message(string.format("ctrl/uplink/%s/0003", g_local_id), 
             json.encode({["topic"] = g_s_topic, ["type"] = g_s_type}))
-        sys.timerStart(wait_speech_to, 15000)
+        sys.timerStart(extalk.wait_speech_to, 15000)
     else
         -- 一对一模式
         log.info("向", id, "主动发起对讲")
@@ -538,9 +565,9 @@ function extalk.start(id)
         g_s_topic = string.format("audio/%s/%s/%s", 
             g_local_id, id, string.sub(tostring(mcu.ticks()), -4, -1))
         
-        g_mqttc:publish(string.format("ctrl/uplink/%s/0003", g_local_id), 
+        publish_message(string.format("ctrl/uplink/%s/0003", g_local_id), 
             json.encode({["topic"] = g_s_topic, ["type"] = g_s_type}))
-        sys.timerStart(wait_speech_to, 15000)
+        sys.timerStart(extalk.wait_speech_to, 15000)
     end
     
     return true
@@ -554,7 +581,7 @@ function extalk.stop()
     end
 
     log.info("主动断开对讲")
-    speech_off(true, false)
+    extalk.speech_off(true, false)
     return true
 end
 

+ 1 - 1
module/Air8000/project/整机开发板出厂工程/user/main.lua

@@ -1,6 +1,6 @@
 PROJECT = "startupv13"
 VERSION = "1.0.0"
-PRODUCT_KEY = "29uptfBkJMwFC7x7QeW10UPO3LecPYFu"
+PRODUCT_KEY = "NrkXcjWwjcc5EFdCrrYnvypBCyJlEaIO"
 
 log.info("main", PROJECT, VERSION)
 

+ 14 - 23
module/Air8000/project/整机开发板出厂工程/user/talk.lua

@@ -155,31 +155,22 @@ function talk.run()
         else
             if current_page == "main" then
                 lcd.clear(_G.bkcolor) 
-                if  speech_topic  == nil then
-                    lcd.drawStr(0, 80, "所有要对讲的设备,要保持在线")
-                    lcd.drawStr(0, 100, "方案介绍:airtalk.luatos.com")
-                    lcd.drawStr(0, 120, "平台端网址:airtalk.openluat.com/talk/")
-                    lcd.drawStr(0, 140, "本机ID:" .. local_id)
-                    lcd.showImage(32, 250, "/luadb/input_topic.jpg")
-                    lcd.showImage(32, 300, "/luadb/broadcast.jpg")
-                    lcd.showImage(104, 400, "/luadb/stop.jpg")
-                    
-                else
-                    -- lcd.drawStr(0, 80, "对端ID:"..speech_topic )
-                    lcd.drawStr(0, 100, "方案介绍:airtalk.luatos.com")
-                    lcd.drawStr(0, 120, "平台端网址:airtalk.openluat.com/talk/")
-                    lcd.drawStr(0, 140, "所有要对讲的设备,要保持在线")
-                    lcd.drawStr(0, 160, talk_state)
-                    lcd.drawStr(0, 180, "事件:" .. event)
-                    lcd.drawStr(0, 200, "本机ID:" .. local_id)
-                    lcd.drawQrcode(185, 148, "https://airtalk.openluat.com/talk/", 82)
-                    lcd.drawStr(185, 242, "扫码进入网页端",0x0000)
-                    -- 显示输入法入口按钮
+                if  speech_topic   then
                     lcd.showImage(175, 300, "/luadb/datacall.jpg")
-                    lcd.showImage(32, 300, "/luadb/broadcast.jpg")
-                    lcd.showImage(104, 400, "/luadb/stop.jpg")
-                    lcd.showImage(0, 448, "/luadb/Lbottom.jpg")
                 end
+                lcd.drawStr(0, 100, "方案介绍:airtalk.luatos.com")
+                lcd.drawStr(0, 120, "平台端网址:airtalk.openluat.com/talk/")
+                lcd.drawStr(0, 140, "所有要对讲的设备,要保持在线")
+                lcd.drawStr(0, 160, talk_state)
+                lcd.drawStr(0, 180, "事件:" .. event)
+                lcd.drawStr(0, 200, "本机ID:" .. local_id)
+                lcd.drawQrcode(185, 148, "https://airtalk.openluat.com/talk/", 82)
+                lcd.drawStr(185, 242, "扫码进入网页端",0x0000)
+                -- 显示输入法入口按钮
+                
+                lcd.showImage(32, 300, "/luadb/broadcast.jpg")
+                lcd.showImage(104, 400, "/luadb/stop.jpg")
+                lcd.showImage(0, 448, "/luadb/Lbottom.jpg")
                 
                 -- 显示通讯录按钮 (位置x10,y250)
                 lcd.showImage(175, 250, "/luadb/addresslist.jpg")

+ 0 - 1
module/Air8101/demo/accessory_board/AirETH_1000/network_routing/wifi_out_ethernet_in_wifi_in/netif_app.lua

@@ -12,7 +12,6 @@
 ]] 
 exnetif = require "exnetif"
 
--- gpio.setup(13, 1, gpio.PULLUP)
 function netif_app_task_func()
     local res
     -- 设置多网融合功能,wifi提供网络供wifi设备上网

+ 141 - 0
module/Air8101/demo/accessory_board/AirPHY_1000/http/http_app.lua

@@ -0,0 +1,141 @@
+--[[
+@module  http_app
+@summary http应用功能模块
+@version 1.0
+@date    2025.09.17
+@author  王城钧
+@usage
+本文件为http应用功能模块,核心业务逻辑为:基于不同的应用场景,演示http核心库的使用方式;
+本文件没有对外接口,直接在main.lua中require "http_app"就可以加载运行;
+]]
+
+--[[
+此处先详细解释下http.request接口的使用方法
+
+接口定义:
+    http.request(method, url, headers, body, opts, server_ca_cert, client_cert, client_key, client_password)
+
+使用方法:
+    local code, headers, body = http.request(method, url, headers, body, opts, server_ca_cert, client_cert, client_key, client_password).wait()
+    只能在task中使用
+    发送http请求到服务器,等待服务器的http应答,此处会阻塞当前task,等待整个过程成功结束或者出现错误异常结束或者超时结束
+
+参数定义:
+    method,stirng类型,必须包含此参数,表示HTTP请求方法,支持"GET"、"POST"、"HEAD"等所有HTTP请求方法
+    url,string类型,必须包含此参数,表示HTTP请求URL地址,支持HTTP、HTTPS,支持域名、IP地址,支持自定义端口,标准的HTTP URL格式都支持
+    headers,table或者nil类型,可选包含此参数,表示HTTP请求头,例如 {["Content-Type"] = "application/x-www-form-urlencoded", ["self_defined_key"] = "self_defined_value"}
+    body,string或者zbuff或者nil类型,可选包含此参数,表示HTTP请求体,如果请求体是一个文件中的内容,要把文件内容读出来,赋值给body使用
+    opts,table或者nil类型,可选包含此参数,表示HTTP请求的一些额外配置,包含以下内容
+    {
+        timeout    -- -- number或者nil类型,单位毫秒,可选包含此参数,表示从发送请求到读取到服务器响应整个过程的超时时间,如果传入0,表示永久等待;如果没有传入此参数或者传入nil,则使用默认值10分钟
+        dst        -- 下载路径,string类型,当HTTP请求的数据需要保存到文件中时,此处填写完整的文件路径
+        adapter    -- 使用的网卡ID,number类型,例如4G网卡,SPI外挂以太网卡,WIFI网卡等;如果没有传入此参数,内核固件会自动选择当前时间点其他功能模块设置的默认网卡
+                    -- 除非你HTTP请求时,一定要使用某一种网卡,才设置此参数;如果没什么特别要求,不要使用此参数,使用系统中设置的默认网卡即可
+                    -- 这个参数和本demo中的netdrv_device.lua关系比较大,netdrv_device会设置默认网卡,此处http不要设置adapter参数,直接使用netdrv_device设置的默认网卡就行
+        debug      -- 调试开关,bool类型,true表示打开debug调试信息日志,false表示关闭debug调试信息日志,默认为关闭状态
+        ipv6       -- 是否为ipv6,bool类型,true表示使用ipv6,false表示不使用ipv6,默认为false
+        userdata   -- 下载回调函数使用的用户自定义回调参数,做为callback回调函数的第三个参数使用
+        callback   -- 下载回调函数,function类型,当下载数据时,无论是保存到内存中,还是保存到文件系统中,如果设置了callback,内核固件中每收到一包body数据,都会自动执行一次callback回调函数
+                    -- 回调函数的调用形式为callback(content_len, body_len, userdata)
+                    --     content_len:number类型,数据总长度
+                    --     body_len:number类型,已经下载的数据长度
+                    --     userdata:下载回调函数使用的用户自定义回调参数
+    }
+    server_ca_cert,string类型,服务器ca证书数据,可选包含此参数,当客户端需要验证服务器证书时,需要此参数,如果证书数据在一个文件中,要把文件内容读出来,赋值给server_ca_cert
+    client_cert,string类型,客户端证书数据,可选包含此参数,当服务器需要验证客户端证书时,需要此参数,如果证书数据在一个文件中,要把文件内容读出来,赋值给client_cert
+    client_key, string类型,客户端加密后的私钥数据,可选包含此参数,当服务器需要验证客户端证书时,需要此参数,如果加密后的私钥数据在一个文件中,要把文件内容读出来,赋值给client_key
+    client_password,string类型,客户端私钥口令数据,可选包含此参数,当服务器需要验证客户端证书时,需要此参数,如果私钥口令数据在一个文件中,要把文件内容读出来,赋值给client_password
+
+返回值定义:
+
+    http.request().wait()有三个返回值code,headers,body
+    code表示执行结果,number类型,有以下两种含义:
+        1、code大于等于100时,表示服务器返回的HTTP状态码,例如200表示成功,详细说明可以通过搜索引擎搜索“HTTP状态码”自行了解
+        2、code小于0时,表示内核固件中检测到通信异常,有如下几种:
+            -1 HTTP_ERROR_STATE 错误的状态, 一般是底层异常,请报issue
+            -2 HTTP_ERROR_HEADER 错误的响应头部, 通常是服务器问题
+            -3 HTTP_ERROR_BODY 错误的响应体,通常是服务器问题
+            -4 HTTP_ERROR_CONNECT 连接服务器失败, 未联网,地址错误,域名错误
+            -5 HTTP_ERROR_CLOSE 提前断开了连接, 网络或服务器问题
+            -6 HTTP_ERROR_RX 接收数据报错, 网络问题
+            -7 HTTP_ERROR_DOWNLOAD 下载文件过程报错, 网络问题或下载路径问题
+            -8 HTTP_ERROR_TIMEOUT 超时, 包括连接超时,读取数据超时
+            -9 HTTP_ERROR_FOTA fota功能报错,通常是更新包不合法
+    headers有以下两种含义:
+        1、当code的返回值大于等于100时,headers表示服务器返回的应答头,table类型
+        2、当code的返回值小于0时,headers为nil
+    body有以下三种含义
+        1、当code的返回值大于等于100时,如果请求的body数据不需要保存到文件中,而是直接保存到内存中,则body表示请求到的数据内容,string类型
+        2、当code的返回值大于等于100时,如果请求的body数据需要保存到文件中,则body表示保存请求数据后的文件的大小,number类型
+        3、当code的返回值小于0时,body为nil
+]]
+
+
+-- 普通的http get请求功能演示
+-- 请求的body数据保存到内存变量中,在内存够用的情况下,最大支持32KB的数据存储到内存中
+-- timeout可以设置超时时间
+-- callback可以设置回调函数,可用于实时检测body数据的下载进度
+local function http_app_get()
+    -- https get请求https://www.air32.cn/网页内容
+    -- 如果请求成功,请求的数据保存到body中
+    local code, headers, body = http.request("GET", "https://www.air32.cn/").wait()
+    log.info("http_app_get1",
+        code == 200 and "success" or "error",
+        code,
+        json.encode(headers or {}),
+        body and (body:len() > 512 and body:len() or body) or "nil")
+
+    -- https get请求https://www.luatos.com/网页内容,超时时间为10秒
+    -- 请求超时时间为10秒,用户自己写代码时,不要照抄10秒,根据自己业务逻辑的需要设置合适的超时时间
+    -- 回调函数为http_cbfunc,回调函数使用的第三个回调参数为"http_app_get2"
+    -- 如果请求成功,请求的数据保存到body中
+    code, headers, body = http.request("GET", "https://www.luatos.com/", nil, nil,
+        { timeout = 10000, userdata = "http_app_get2", callback = http_cbfunc }).wait()
+    log.info("http_app_get2",
+        code == 200 and "success" or "error",
+        code,
+        json.encode(headers or {}),
+        body and (body:len() > 512 and body:len() or body) or "nil")
+
+    -- http get请求http://httpbin.air32.cn/get网页内容,超时时间为3秒
+    -- 请求超时时间为3秒,用户自己写代码时,不要照抄3秒,根据自己业务逻辑的需要设置合适的超时时间
+    -- 回调函数为http_cbfunc,回调函数使用的第三个回调参数为"http_app_get3"
+    -- 如果请求成功,请求的数据保存到body中
+    code, headers, body = http.request("GET", "http://httpbin.air32.cn/get", nil, nil,
+        { timeout = 3000, userdata = "http_app_get3", callback = http_cbfunc }).wait()
+    log.info("http_app_get3",
+        code == 200 and "success" or "error",
+        code,
+        json.encode(headers or {}),
+        body and (body:len() > 512 and body:len() or body) or "nil")
+end
+
+-- http app task 的任务处理函数
+local function http_app_task_func()
+    while true do
+        -- 如果当前时间点设置的默认网卡还没有连接成功,一直在这里循环等待
+        while not socket.adapter(socket.dft()) do
+            log.warn("http_app_task_func", "wait IP_READY", socket.dft())
+            -- 在此处阻塞等待默认网卡连接成功的消息"IP_READY"
+            -- 或者等待1秒超时退出阻塞等待状态;
+            -- 注意:此处的1000毫秒超时不要修改的更长;
+            -- 因为当使用exnetif.set_priority_order配置多个网卡连接外网的优先级时,会隐式的修改默认使用的网卡
+            -- 当exnetif.set_priority_order的调用时序和此处的socket.adapter(socket.dft())判断时序有可能不匹配
+            -- 此处的1秒,能够保证,即使时序不匹配,也能1秒钟退出阻塞状态,再去判断socket.adapter(socket.dft())
+            sys.waitUntil("IP_READY", 1000)
+        end
+
+        -- 检测到了IP_READY消息
+        log.info("http_app_task_func", "recv IP_READY", socket.dft())
+
+        -- 普通的http get请求功能演示
+        http_app_get()
+
+        -- 50秒之后,循环测试
+        sys.wait(50000)
+    end
+end
+
+--创建并且启动一个task
+--运行这个task的处理函数http_app_task_func
+sys.taskInit(http_app_task_func)

+ 79 - 0
module/Air8101/demo/accessory_board/AirPHY_1000/http/main.lua

@@ -0,0 +1,79 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 1.0
+@date    2025.09.22
+@author  王城钧
+@usage
+本demo演示的核心功能为:
+1、分别使用http核心库和httpplus扩展库,演示以下一种应用场景的使用方式
+   (1) 普通的http get请求功能演示;;
+2、netdrv_device:配置连接外网使用的网卡,目前支持以下四种选择(二选一)
+   (3) netdrv_eth_spi:通过SPI外挂CH390H芯片的以太网卡
+   (4) netdrv_multiple:支持以上三种网卡,可以配置三种网卡的优先级
+
+更多说明参考本目录下的readme.md文件
+]]
+
+
+--[[
+必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
+PROJECT:项目名,ascii string类型
+        可以随便定义,只要不使用,就行
+VERSION:项目版本号,ascii string类型
+        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
+            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
+            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
+        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
+]]
+PROJECT = "HTTP"
+VERSION = "001.000.000"
+
+
+-- 在日志中打印项目名和项目版本号
+log.info("main", PROJECT, VERSION)
+
+
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
+end
+
+
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
+
+
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
+
+
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
+
+-- 加载网络驱动设备功能模块
+require "netdrv_device"
+
+-- 加载http应用功能模块
+require "http_app"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 77 - 0
module/Air8101/demo/accessory_board/AirPHY_1000/http/netdrv/netdrv_eth_rmii.lua

@@ -0,0 +1,77 @@
+--[[
+@module  netdrv_eth_rmii
+@summary “通过MAC层的rmii接口外挂PHY芯片(LAN8720Ai)的以太网卡”驱动模块 
+@version 1.0
+@date    2025.07.24
+@author  马梦阳
+@usage
+本文件为“通过MAC层的rmii接口外挂PHY芯片(LAN8720Ai)的以太网卡”驱动模块 ,核心业务逻辑为:
+1、打开PHY芯片供电开关;
+2、初始化以太网卡,并且在以太网卡上开启DHCP(动态主机配置协议);
+3、以太网卡的连接状态发生变化时,在日志中进行打印;
+
+Air8101核心板和AirPHY_1000配件板的硬件接线方式为:
+Air8101核心板通过TYPE-C USB口供电(核心板背面的功耗测试开关拨到OFF一端);
+如果测试发现软件重启,并且日志中出现  poweron reason 0,表示供电不足,此时再通过直流稳压电源对核心板的VIN管脚进行5V供电;
+| Air8101核心板 |  AirPHY_1000配件板  |
+| ------------ | ------------------ |
+|    59/3V3    |         3.3v       |
+|     gnd      |         gnd        |
+|     5/D2     |         RX1        |
+|    72/D1     |         RX0        |
+|    71/D3     |         CRS        |
+|     4/D0     |         MDIO       |
+|     6/D4     |         TX0        |
+|    74/PCK    |         MDC        |
+|    70/D5     |         TX1        |
+|     7/D6     |         TXEN       |
+|     不接     |          NC        |
+|    69/D7     |         CLK        |
+
+本文件没有对外接口,直接在其他功能模块中require "netdrv_eth_rmii"就可以加载运行;
+]]
+
+local function ip_ready_func(ip, adapter)
+    if adapter == socket.LWIP_ETH then
+        log.info("netdrv_eth_rmii.ip_ready_func", "IP_READY", socket.localIP(socket.LWIP_ETH))
+    end
+end
+
+local function ip_lose_func(adapter)
+    if adapter == socket.LWIP_ETH then
+        log.warn("netdrv_eth_rmii.ip_lose_func", "IP_LOSE")
+    end
+end
+
+
+-- 此处订阅"IP_READY"和"IP_LOSE"两种消息
+-- 在消息的处理函数中,仅仅打印了一些信息,便于实时观察“通过MAC层的rmii接口外挂PHY芯片(LAN8720Ai)的以太网卡”的连接状态
+-- 也可以根据自己的项目需求,在消息处理函数中增加自己的业务逻辑控制,例如可以在连网状态发生改变时更新网络图标
+sys.subscribe("IP_READY", ip_ready_func)
+sys.subscribe("IP_LOSE", ip_lose_func)
+
+
+-- 设置默认网卡为socket.LWIP_ETH
+socket.dft(socket.LWIP_ETH)
+
+
+-- 本demo测试使用的是核心板的VDD 3V3引脚对AirPHY_1000配件板进行供电
+-- VDD 3V3引脚是Air8101内部的LDO输出引脚,最大输出电流300mA
+-- GPIO13在Air8101内部使能控制这个LDO的输出
+-- 所以在此处GPIO13输出高电平打开这个LDO
+gpio.setup(13, 1, gpio.PULLUP)
+
+
+--初始化以太网卡
+
+--以太网联网成功(成功连接路由器,并且获取到了IP地址)后,内核固件会产生一个"IP_READY"消息
+--各个功能模块可以订阅"IP_READY"消息实时处理以太网联网成功的事件
+--也可以在任何时刻调用socket.adapter(socket.LWIP_ETH)来获取以太网是否连接成功
+
+--以太网断网后,内核固件会产生一个"IP_LOSE"消息
+--各个功能模块可以订阅"IP_LOSE"消息实时处理以太网断网的事件
+--也可以在任何时刻调用socket.adapter(socket.LWIP_ETH)来获取以太网是否连接成功
+netdrv.setup(socket.LWIP_ETH)
+
+--在以太网卡上开启动态主机配置协议
+netdrv.dhcp(socket.LWIP_ETH, true)

+ 105 - 0
module/Air8101/demo/accessory_board/AirPHY_1000/http/netdrv/netdrv_multiple.lua

@@ -0,0 +1,105 @@
+--[[
+@module  netdrv_multiple
+@summary 多网卡(WIFI STA网卡、通过MAC层的rmii接口外挂PHY芯片(LAN8720Ai)的以太网卡)驱动模块
+@version 1.0
+@date    2025.07.24
+@author  马梦阳
+@usage
+本文件为多网卡驱动模块 ,核心业务逻辑为:
+1、调用exnetif.set_priority_order配置多网卡的控制参数以及优先级;
+
+
+通过MAC层的rmii接口外挂PHY芯片(LAN8720Ai)的以太网卡:
+Air8101核心板和AirPHY_1000配件板的硬件接线方式为:
+Air8101核心板通过TYPE-C USB口供电(核心板背面的功耗测试开关拨到OFF一端);
+如果测试发现软件重启,并且日志中出现  poweron reason 0,表示供电不足,此时再通过直流稳压电源对核心板的VIN管脚进行5V供电;
+| Air8101核心板 | AirPHY_1000配件板  |
+| ------------ | ------------------ |
+|    59/3V3    |         3.3v       |
+|     gnd      |         gnd        |
+|     5/D2     |         RX1        |
+|    72/D1     |         RX0        |
+|    71/D3     |         CRS        |
+|     4/D0     |         MDIO       |
+|     6/D4     |         TX0        |
+|    74/PCK    |         MDC        |
+|    70/D5     |         TX1        |
+|     7/D6     |         TXEN       |
+|     不接     |          NC        |
+|    69/D7     |         CLK        |
+
+本文件没有对外接口,直接在其他功能模块中require "netdrv_multiple"就可以加载运行;
+]]
+
+
+local exnetif = require "exnetif"
+
+-- 网卡状态变化通知回调函数
+-- 当exnetif中检测到网卡切换或者所有网卡都断网时,会触发调用此回调函数
+-- 当网卡切换切换时:
+--     net_type:string类型,表示当前使用的网卡字符串
+--     adapter:number类型,表示当前使用的网卡id
+-- 当所有网卡断网时:
+--     net_type:为nil
+--     adapter:number类型,为-1
+local function netdrv_multiple_notify_cbfunc(net_type,adapter)
+    if type(net_type)=="string" then
+        log.info("netdrv_multiple_notify_cbfunc", "use new adapter", net_type, adapter)
+    elseif type(net_type)=="nil" then
+        log.warn("netdrv_multiple_notify_cbfunc", "no available adapter", net_type, adapter)
+    else
+        log.warn("netdrv_multiple_notify_cbfunc", "unknown status", net_type, adapter)
+    end
+end
+
+
+local function netdrv_multiple_task_func()
+    --设置网卡优先级
+    exnetif.set_priority_order(
+        {
+            -- “通过MAC层的rmii接口外挂PHY芯片(LAN8720Ai)”的以太网卡,可以使用Air8101核心板+AirPHY_1000配件板验证
+            {
+                ETHERNET = {
+                    -- 供电使能GPIO,此demo使用的59脚3V3供电,受GPIO13控制
+                    pwrpin = 13,
+                    -- 设置的多个“已经IP READY,但是还没有ping通”网卡,循环执行ping动作的间隔(单位毫秒,可选)
+                    -- 如果没有传入此参数,exnetif会使用默认值10秒
+                    ping_time = 3000,
+
+                    -- 连通性检测ip(选填参数);
+                    -- 如果没有传入ip地址,exnetif中会默认使用httpdns能否成功获取baidu.com的ip作为是否连通的判断条件;
+                    -- 如果传入,一定要传入可靠的并且可以ping通的ip地址;
+                    -- ping_ip = "填入可靠的并且可以ping通的ip地址",
+                }
+            },
+
+            -- WIFI STA网卡
+            {
+                WIFI = {
+                    -- 要连接的WIFI路由器名称
+                    ssid = "iPhone",
+                    -- 要连接的WIFI路由器密码
+                    password = "xiaoshuai", 
+
+                    -- 连通性检测ip(选填参数);
+                    -- 如果没有传入ip地址,exnetif中会默认使用httpdns能否成功获取baidu.com的ip作为是否连通的判断条件;
+                    -- 如果传入,一定要传入可靠的并且可以ping通的ip地址;
+                    -- ping_ip = "填入可靠的并且可以ping通的ip地址",
+                }
+            }
+        }
+    )    
+end
+
+-- 设置网卡状态变化通知回调函数netdrv_multiple_notify_cbfunc
+exnetif.notify_status(netdrv_multiple_notify_cbfunc)
+
+-- 如果存在udp网络应用,并且udp网络应用中,根据应用层的心跳能够判断出来udp数据通信出现了异常;
+-- 可以在判断出现异常的位置,调用一次exnetif.check_network_status()接口,强制对当前正式使用的网卡进行一次连通性检测;
+-- 如果存在tcp网络应用,不需要用户调用exnetif.check_network_status()接口去控制,exnetif会在tcp网络应用通信异常时自动对当前使用的网卡进行连通性检测。
+
+
+-- 启动一个task,task的处理函数为netdrv_multiple_task_func
+-- 在处理函数中调用exnetif.set_priority_order设置网卡优先级
+-- 因为exnetif.set_priority_order要求必须在task中被调用,所以此处启动一个task
+sys.taskInit(netdrv_multiple_task_func)

+ 25 - 0
module/Air8101/demo/accessory_board/AirPHY_1000/http/netdrv_device.lua

@@ -0,0 +1,25 @@
+--[[
+@module  netdrv_device
+@summary 网络驱动设备功能模块
+@version 1.0
+@date    2025.09.24
+@author  王城钧
+@usage
+本文件为网络驱动设备功能模块,核心业务逻辑为:根据项目需求,选择并且配置合适的网卡(网络适配器)
+1、netdrv_ethernet_spi:socket.LWIP_ETH,通过SPI外挂CH390H芯片的以太网卡;
+2、netdrv_multiple:可以配置多种网卡的优先级,按照优先级配置,使用其中一种网卡连接外网;
+
+根据自己的项目需求,只需要require以上其中的一种即可;
+
+
+本文件没有对外接口,直接在main.lua中require "netdrv_device"就可以加载运行;
+]]
+
+
+-- 根据自己的项目需求,只需要require以下其中的一种即可;
+
+-- 加载“通过MAC层的rmii接口外挂PHY芯片(LAN8720Ai)的以太网卡”驱动模块
+-- require "netdrv_eth_rmii"
+
+-- 加载“可以配置优先级的多种网卡”驱动模块
+require "netdrv_multiple"

+ 85 - 0
module/Air8101/demo/accessory_board/AirPHY_1000/http/readme.md

@@ -0,0 +1,85 @@
+## 功能模块介绍:
+
+1、main.lua:主程序入口;
+
+2、netdrv_device.lua:加载网络驱动设备功能模块;
+
+3、http_app.lua:加载http应用模块
+
+## 演示功能概述
+
+1、以太网给模组供网,通过连接http测试连通。
+
+## 演示硬件环境
+
+1、Air8101核心板一块+可上网的sim卡一张+网线一根+AirPHY_1000板子一个;
+
+[](https://docs.openLuat.com/cdn/image/8101_AirPHY1000.jpg)
+
+![lan](https://docs.openLuat.com/cdn/image/8101_AirPHY1000.jpg)
+
+2、TYPE-C USB数据线一根 + 杜邦线若干;
+
+* Air8101核心板通过TYPE-C USB口供电;(外部供电/USB供电 拨动开关 拨到 USB供电一端)
+
+* TYPE-C USB数据线直接插到核心板的TYPE-C USB座子,另外一端连接电脑USB口;
+
+* AirPHY_1000板子网口与路由器网口通过网线连接;
+
+3、Air8101核心板和AirPHY_1000配件板的硬件接线方式为:
+
+| Air8101核心板 | AirPHY_1000配件板 |
+| ---------- | -------------- |
+| 59/3V3     | 3.3v           |
+| gnd        | gnd            |
+| 5/D2       | RX1            |
+| 72/D1      | RX0            |
+| 71/D3      | CRS            |
+| 4/D0       | MDIO           |
+| 6/D4       | TX0            |
+| 74/PCK     | MDC            |
+| 70/D5      | TX1            |
+| 7/D6       | TXEN           |
+| 不接         | NC             |
+| 69/D7      | CLK            |
+
+演示软件环境
+
+1、Luatools下载调试工具
+
+2、[Air8101 V2014版本固件](https://docs.openluat.com/air8101/luatos/firmware/)(理论上,2025年9月12日之后发布的固件都可以)
+
+## 演示核心步骤
+
+1、搭建好硬件环境,按接线图连接硬件。
+
+2、烧录内核固件和本项目的Lua脚本:main.lua:主程序入口(需要在main.lua文件中打开require"netdrv_device"和require"http_app")
+
+3、启动设备,观察日志输出:
+
+```
+出现类似如下打印,就表示成功。
+
+[2025-09-17 14:35:59.774][000000005.877] D/ulwip IP_READY 4 192.168.3.99
+
+[2025-09-17 14:35:59.777][000000005.878] I/user.netdrv_PHY_spi.ip_ready_func IP_READY 192.168.3.99 255.255.255.0 
+192.168.3.1 nil
+
+[2025-09-17 14:35:59.783][000000005.879] I/user.http_app_task_func recv IP_READY 4 4
+
+[2025-09-17 14:35:59.786][000000005.883] dns_run 676:www.air32.cn state 0 id 1 ipv6 0 use dns server0, try 0
+
+[2025-09-17 14:35:59.789][000000005.883] D/net adatper 4 dns server 192.168.3.1
+
+[2025-09-17 14:35:59.793][000000005.883] D/net dns udp sendto 192.168.3.1:53 from 192.168.3.99
+
+[2025-09-17 14:35:59.799][000000005.891] dns_run 693:dns all done ,now stop
+
+[2025-09-17 14:35:59.802][000000005.891] D/net connect 49.232.89.122:443 TCP
+
+[2025-09-17 14:36:00.215][000000006.395] I/user.http_app_get1 success 200 {"Transfer-Encoding":"chunked","Date":"Wed, 17 Sep 2025 06:36:02 GMT","Connection":"keep-alive","Server":"openresty\/1.27.1.2","Content-Type":"text\/html"} 2416
+
+[2025-09-17 14:36:00.226][000000006.396] dns_run 676:www.luatos.com state 0 id 2 ipv6 0 use dns server0, try 0
+
+
+```

+ 0 - 53
module/Air8101/demo/accessory_board/AirPHY_1000/http_app.lua

@@ -1,53 +0,0 @@
-
---这个task的核心业务逻辑是:每隔一段时间发送一次http get请求,测试http数传是否正常
-local function http_get_task_func()
-    --检查当前使用的网卡(本demo使用的是以太网卡socket.LWIP_ETH)的连接状态
-    log.info("http_get_task_func", "socket.adapter(socket.dft())", socket.adapter(socket.dft()))
-    --如果当前使用的网卡(本demo使用的是以太网卡socket.LWIP_ETH)还没有连接成功
-    if not socket.adapter(socket.dft()) then
-        --phy_app.lua中的以太网配置和启动结束后,一旦以太网卡准备就绪,就会产生一个"IP_READY"消息
-        --在此处阻塞等待以太网连接成功的消息"IP_READY"
-        --或者等待30秒超时退出阻塞等待状态
-        --如果没有等到"IP_READY"消息,直接退出这个函数
-        if not sys.waitUntil("IP_READY", 30000) then
-            log.error("http_get_task_func error", "ip network timeout")
-            return
-        end
-    end
-
-    
-    --每6秒执行一次循环
-    while true do
-        --发送http get请求服务器,等待服务器的http应答,此处会阻塞当前task,等待整个过程成功结束或者出现错误异常结束
-        --此处使用了http.request().wait()的形式
-        --http.request()的详细说明参考API文档
-        --wait()表示在此处阻塞等待整个过程的结束
-
-        --具体到此处的代码,对部分参数以及返回值做如下解释
-        --timeout=3000表示超时时间为3秒,如果3秒内没有成功结束或者异常结束整个过程,则会超时结束;
-        --整个过程结束后,http.request().wait()有三个返回值code,headers,body
-        --code表示结果,number类型,详细说明参考API手册,一般来说:
-        --             200表示成功
-        --             小于0的值表示出错,例如-8表示超时错误
-        --             其余结果值参考API手册
-        --headers表示服务器返回的应答头,table类型
-        --body表示服务器返回的应答题,具体到这里的代码使用方式,为string类型
-        log.info("http", http.request("GET", "http://httpbin.air32.cn/get", nil, nil, {timeout=3000}).wait())
-
-        --打印使用的内存信息,方便分析内存使用情况
-        log.info("lua", rtos.meminfo())
-        log.info("sys", rtos.meminfo("sys"))
-
-        --打印当前使用的网卡(本demo使用的是以太网卡socket.LWIP_ETH)下的本地IP,网关,子网掩码,网关IP信息
-        log.info("ip", socket.dft(), socket.localIP(socket.dft()))
-
-        --等待6秒钟
-        sys.wait(6000)
-    end
-
-end
-
---创建并且启动一个task
---task的主函数为http_get_task_func
-sys.taskInit(http_get_task_func)
-

+ 71 - 0
module/Air8101/demo/accessory_board/AirPHY_1000/network_routing/wifi_out_ethernet_in_wifi_in/main.lua

@@ -0,0 +1,71 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 1.0
+@date    2025.09.22
+@author  王城钧
+@usage
+本demo演示的核心功能为:
+1.设置多网融合功能,wifi提供网络供wifi和以太网设备上网
+更多说明参考本目录下的readme.md文件
+]]
+--[[
+必须定义PROJECT和VERSION变量,Luatools工具会用到这两个变量,远程升级功能也会用到这两个变量
+PROJECT:项目名,ascii string类型
+        可以随便定义,只要不使用,就行
+VERSION:项目版本号,ascii string类型
+        如果使用合宙iot.openluat.com进行远程升级,必须按照"XXX.YYY.ZZZ"三段格式定义:
+            X、Y、Z各表示1位数字,三个X表示的数字可以相同,也可以不同,同理三个Y和三个Z表示的数字也是可以相同,可以不同
+            因为历史原因,YYY这三位数字必须存在,但是没有任何用处,可以一直写为000
+        如果不使用合宙iot.openluat.com进行远程升级,根据自己项目的需求,自定义格式即可
+]]
+PROJECT = "wifi_out_ethernet_in_wifi_in"
+VERSION = "001.000.000"
+
+
+-- 在日志中打印项目名和项目版本号
+log.info("main", PROJECT, VERSION)
+
+
+-- 如果内核固件支持wdt看门狗功能,此处对看门狗进行初始化和定时喂狗处理
+-- 如果脚本程序死循环卡死,就会无法及时喂狗,最终会自动重启
+if wdt then
+    --配置喂狗超时时间为9秒钟
+    wdt.init(9000)
+    --启动一个循环定时器,每隔3秒钟喂一次狗
+    sys.timerLoopStart(wdt.feed, 3000)
+end
+
+
+-- 如果内核固件支持errDump功能,此处进行配置,【强烈建议打开此处的注释】
+-- 因为此功能模块可以记录并且上传脚本在运行过程中出现的语法错误或者其他自定义的错误信息,可以初步分析一些设备运行异常的问题
+-- 以下代码是最基本的用法,更复杂的用法可以详细阅读API说明文档
+-- 启动errDump日志存储并且上传功能,600秒上传一次
+-- if errDump then
+--     errDump.config(true, 600)
+-- end
+
+
+-- 使用LuatOS开发的任何一个项目,都强烈建议使用远程升级FOTA功能
+-- 可以使用合宙的iot.openluat.com平台进行远程升级
+-- 也可以使用客户自己搭建的平台进行远程升级
+-- 远程升级的详细用法,可以参考fota的demo进行使用
+
+
+-- 启动一个循环定时器
+-- 每隔3秒钟打印一次总内存,实时的已使用内存,历史最高的已使用内存情况
+-- 方便分析内存使用是否有异常
+-- sys.timerLoopStart(function()
+--     log.info("mem.lua", rtos.meminfo())
+--     log.info("mem.sys", rtos.meminfo("sys"))
+-- end, 3000)
+
+
+
+-- 开启多网融合功能
+require "netif_app"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后后面不要加任何语句!!!!!

+ 53 - 0
module/Air8101/demo/accessory_board/AirPHY_1000/network_routing/wifi_out_ethernet_in_wifi_in/netif_app.lua

@@ -0,0 +1,53 @@
+--[[
+@module  netif_app
+@summary netif_app 网络管理模块,开启多网融合功能,wifi提供网络供以太网和wifi设备上网
+@version 1.0
+@date    2025.09.22
+@author  王城钧
+@usage
+本文件为网络管理模块,核心业务逻辑为:
+1.设置多网融合功能,wifi提供网络供wifi和以太网设备上网
+2、http测试wifi网络
+本文件没有对外接口,直接在main.lua中require "netif_app"就可以加载运行;
+]] 
+exnetif = require "exnetif"
+
+function netif_app_task_func()
+    local res
+    -- 设置多网融合功能,wifi提供网络供wifi设备上网
+    res = exnetif.setproxy(socket.LWIP_AP, socket.LWIP_STA, {
+        ssid = "test2",                 -- AP热点名称(string),网卡包含wifi时填写
+        password = "HZ88888888",        -- AP热点密码(string),网卡包含wifi时填写
+        ap_opts = {                     -- AP模式下配置项(选填参数)
+            hidden = false,             -- 是否隐藏SSID, 默认false,不隐藏
+            max_conn = 4
+        },                              -- 最大客户端数量, 默认4
+        channel = 6,                    -- AP建立的通道, 默认6
+        main_adapter = {                -- 提供网络的网卡开启参数
+            ssid = "iPhone", 
+            password = "xiaoshuai"
+        }
+    })
+    -- 设置多网融合功能,wifi提供网络供以太网设备上网,RMII方式外挂
+    res = exnetif.setproxy(socket.LWIP_ETH, socket.LWIP_STA, {
+        ethpower_en = 13,               -- 以太网模块的pwrpin引脚(gpio编号)
+        main_adapter = {                -- 提供网络的网卡开启参数
+            ssid = "iPhone", 
+            password = "xiaoshuai"
+        }
+    })
+
+    if res then
+        log.info("exnetif", "setproxy success")
+    else
+        log.info("开启失败,请检查配置项是否正确,日志中是否打印了错误信息")
+    end
+    -- 每5秒进行HTTPS连接测试,实时监测wifi网络连接状态, 仅供测试需要,量产不需要,用来判断当前网络是否可用,需要的话可以打开注释
+    -- while 1 do
+    --     local code, headers, body = http.request("GET", "https://httpbin.air32.cn/bytes/2048", nil, nil, {adapter=socket.LWIP_STA,timeout=5000,debug=false}).wait()
+    --     log.info("http执行结果", code, headers, body and #body)
+    --     sys.wait(5000)
+    -- end
+end
+
+sys.taskInit(netif_app_task_func)

+ 73 - 0
module/Air8101/demo/accessory_board/AirPHY_1000/network_routing/wifi_out_ethernet_in_wifi_in/readme.md

@@ -0,0 +1,73 @@
+## 功能模块介绍
+
+1、main.lua:主程序入口;
+
+2、netif_app: 网络管理模块,开启多网融合功能,wifi提供网络供以太网和wifi设备上网;
+
+## 演示功能概述
+
+1、开启多网融合模式,WIFI连接外部网络,支持以太网lan模式为其他以太网设备提供接入,支持生成WiFi热点为WiFi终端设备提供接入
+
+2、​网络监控​,每5秒进行HTTPS连接测试,实时监测WIFI网络的连接状态
+
+## 演示硬件环境
+
+![](https://docs.openluat.com/air8101/product/file/AirPHY_1000/hw_connection.jpg)
+
+![](https://docs.openluat.com/air8101/product/file/AirPHY_1000/hw_connection1.jpg)
+
+1、Air8101核心板
+
+2、AirPHY_1000配件板
+
+3、公对母的杜邦线11根(连接核心板和配件板)
+
+4、网线1根(一端接配件板,一端接路由器)
+
+5、Air8101核心板和AirPHY_1000配件板的硬件接线方式为
+
+- Air8101核心板通过TYPE-C USB口供电(核心板背面的功耗测试开关拨到OFF一端);如果测试发现软件重启,并且日志中出现  poweron reason 0,表示供电不足,此时再通过直流稳压电源对核心板的VIN管脚进行5V供电;
+
+Air8101核心板和AirPHY_1000配件板的硬件接线方式为:
+
+| Air8101核心板 | AirPHY_1000配件板 |
+| ---------- | -------------- |
+| 59/3V3     | 3.3v           |
+| gnd        | gnd            |
+| 5/D2       | RX1            |
+| 72/D1      | RX0            |
+| 71/D3      | CRS            |
+| 4/D0       | MDIO           |
+| 6/D4       | TX0            |
+| 74/PCK     | MDC            |
+| 70/D5      | TX1            |
+| 7/D6       | TXEN           |
+| 不接         | NC             |
+| 69/D7      | CLK            |
+
+## 演示软件环境
+
+1、Luatools下载调试工具
+
+2、[Air8101 V1005版本固件](https://docs.openluat.com/air8101/luatos/firmware/)(理论上最新版本固件也可以,如果使用最新版本的固件不可以,可以烧录V1005固件对比验证)
+
+## 演示核心步骤
+
+1、搭建好硬件环境,按接线图连接硬件,
+
+2、按需修改WiFi配置(在netif_app.lua中):
+ssid = "WIFI名称"
+password = "WiFi密码"
+
+3、如果使用spi方式外挂网卡,打开SPI方式外挂网卡的代码,注释掉RMII方式外挂网卡的代码
+
+4、内核固件和本项目的Lua脚本:main.lua:主程序入口,netif_app.lua:网络管理模块
+
+5、启动设备,观察日志输出:
+
+```lua
+[INFO] exnetif setproxy success
+[INFO] http执行结果 200 ... 
+```
+
+6、其他设备通过wifi或以太网接入Air8101,其他设备都能正常上网,则表示验证成功。

+ 0 - 42
module/Air8101/demo/accessory_board/AirPHY_1000/phy_app.lua

@@ -1,42 +0,0 @@
-
-local function ip_ready_func()
-    log.info("phy connect.ip_ready_func", "IP_READY")
-end
-
-local function ip_lose_func()
-    log.info("phy connect.ip_lose_func", "IP_LOSE")
-end
-
-
-
---此处订阅"IP_READY"和"IP_LOSE"两种消息
---在消息的处理函数中,仅仅打印了一些信息,便于实时观察以太网的连接状态
---也可以根据自己的项目需求,在消息处理函数中增加自己的业务逻辑控制,例如可以在连网状态发生改变时更新网络图标
-sys.subscribe("IP_READY", ip_ready_func)
-sys.subscribe("IP_LOSE", ip_lose_func)
-
-
-
-
---本demo测试使用的是核心板的VDD 3V3引脚对AirPHY_1000配件板进行供电
---VDD 3V3引脚是Air8101内部的LDO输出引脚,最大输出电流300mA
---GPIO13在Air8101内部使能控制这个LDO的输出
---所以在此处GPIO13输出高电平打开这个LDO
-gpio.setup(13, 1, gpio.PULLUP) 
-
-
-
---初始化以太网卡
-
---以太网联网成功(成功连接路由器,并且获取到了IP地址)后,内核固件会产生一个"IP_READY"消息
---各个功能模块可以订阅"IP_READY"消息实时处理以太网联网成功的事件
---也可以在任何时刻调用socket.adapter(socket.LWIP_ETH)来获取以太网是否连接成功
-
---以太网断网后,内核固件会产生一个"IP_LOSE"消息
---各个功能模块可以订阅"IP_LOSE"消息实时处理以太网断网的事件
---也可以在任何时刻调用socket.adapter(socket.LWIP_ETH)来获取以太网是否连接成功
-netdrv.setup(socket.LWIP_ETH)
-
---在以太网上开启动态主机配置协议
-netdrv.dhcp(socket.LWIP_ETH, true)
-

+ 5 - 84
module/Air8101/demo/accessory_board/AirPHY_1000/readme.md

@@ -1,88 +1,9 @@
+## 演示功能概述:
 
-## 演示功能概述
+本文件下共有两个示例demo
 
-AirPHY_1000是合宙设计生产的一款搭载LAN8720Ai芯片的以太网配件板;
+1、http:通过外挂AirPHY_1000小板使用WAN功能或者多网切换模式连接http测试网络连通性。
 
-本demo演示的核心功能为:
-
-Air8101核心板+AirPHY_1000配件板,使用配件板上的以太网口通过网线连接路由器,演示以太网数传功能;
-
-
-## 核心板+配件板资料
-
-[Air8101核心板+配件板相关资料](https://docs.openluat.com/air8101/product/shouce/#air8101_1)
-
-
-## 演示硬件环境
-
-![](https://docs.openluat.com/air8101/product/file/AirPHY_1000/hw_connection.jpg)
-
-![](https://docs.openluat.com/air8101/product/file/AirPHY_1000/hw_connection1.jpg)
-
-1、Air8101核心板
-
-2、AirPHY_1000配件板
-
-3、公对母的杜邦线11根(连接核心板和配件板)
-
-4、网线1根(一端接配件板,一端接路由器)
-
-5、Air8101核心板和AirPHY_1000配件板的硬件接线方式为
-
-- Air8101核心板通过TYPE-C USB口供电(核心板背面的功耗测试开关拨到OFF一端);
-
-- 如果测试发现软件重启,并且日志中出现  poweron reason 0,表示供电不足,此时再通过直流稳压电源对核心板的VIN管脚进行5V供电;
-
-| Air8101核心板 | AirPHY_1000配件板  |
-| ------------ | ------------------ |
-|    59/3V3    |         3.3v       |
-|     gnd      |         gnd        |
-|     5/D2     |         RX1        |
-|    72/D1     |         RX0        |
-|    71/D3     |         CRS        |
-|     4/D0     |         MDIO       |
-|     6/D4     |         TX0        |
-|    74/PCK    |         MDC        |
-|    70/D5     |         TX1        |
-|     7/D6     |         TXEN       |
-|     不接     |          NC        |
-|    69/D7     |         CLK        |
-
-
-## 演示软件环境
-
-1、Luatools下载调试工具
-
-2、[最新版本的内核固件](https://docs.openluat.com/air8101/luatos/firmware/)
-
-
-## 演示操作步骤
-
-1、搭建好演示硬件环境
-
-2、不需要修改demo脚本代码
-
-3、Luatools烧录内核固件和demo脚本代码
-
-4、烧录成功后,自动开机运行
-
-   (1) 配件板上网口水晶头位置处的橙色灯常亮,表示配件板和路由器的连接正常;
-
-   (2) 配件板上网口水晶头位置处的绿色灯常亮或者闪烁,表示配件板和核心板的供电连接正常;
-
-   (3) 观察Luatools的运行日志,如果出现类似于下面的日志,表示软件功能正常:
-
-```lua
-user.http	200	table: 608FD678	{
-   "args": {}, 
-   "headers": {
-      "Accept-Encoding": "gzip", 
-      "Host": "httpbin.air32.cn:80", 
-      "X-Forwarded-Host": "httpbin.air32.cn:80", 
-      "X-Forwarded-Server": "c4a1487bcf14"
-   }, 
-   "origin": "10.0.0.24", 
-   "url": "http://httpbin.air32.cn:80/get"
-}
-```
+2、network_routing:
 
+(1)使用网络路由功能,以太网提供网络供以太网和wifi设备上网

+ 19 - 6
script/libs/exfotawifi.lua

@@ -1,15 +1,31 @@
 --[[
 @module exfotawifi
 @summary 用于Air8000/8000A/8000W型号模组自动升级WIFI
-@version 1.0.2
-@date    2025.9.16
+@version 1.0.3
+@date    2025.9.23
 @author  拓毅恒
 @usage
-注:使用时在中直接调用 require"exfotawifi" 即可开始执行WiFi升级任务
+注:使用时在创建的一个task处理函数中直接调用exfotawifi.request()即可开始执行WiFi升级任务
 升级完毕后最好取消调用,防止后期版本升级过高导致程序使用不稳定
 
 -- 用法实例
 local exfotawifi = require("exfotawifi")
+
+local function fota_wifi_task()
+    -- ...此处省略很多代码
+
+    local result = exfotawifi.request()
+    if result then
+        log.info("exfotawifi", "升级任务执行成功")
+    else
+        log.info("exfotawifi", "升级任务执行失败")
+    end
+
+    -- ...此处省略很多代码
+end
+
+-- 启动WiFi自动更新任务
+sys.taskInit(fota_wifi_task)
 ]]
 local exfotawifi = {}
 local is_request = false -- 标记是否正在执行request任务
@@ -202,7 +218,4 @@ function exfotawifi.request()
     return fota_result
 end
 
--- 启动WiFi fota任务
-sys.taskInit(exfotawifi.request)
-
 return exfotawifi