Prechádzať zdrojové kódy

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

陈取德 3 mesiacov pred
rodič
commit
5061bb6f38
42 zmenil súbory, kde vykonal 1285 pridanie a 163 odobranie
  1. 7 7
      bsp/pc/test/004.http/main.lua
  2. 10 0
      bsp/pc/test/110.http_timeout/main.lua
  3. 1 1
      components/airlink/src/devinfo/luat_airlink_devinfo_wlan.c
  4. 70 17
      components/network/libftp/luat_ftp_client.c
  5. 3 1
      components/network/libhttp/luat_http.h
  6. 104 54
      components/network/libhttp/luat_http_client.c
  7. 7 9
      components/network/libhttp/luat_lib_http.c
  8. 4 3
      lua/src/loslib.c
  9. 1 1
      luat/vfs/luat_fs_posix.c
  10. 6 2
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/lcd/readme.md
  11. 12 6
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/lcd/ui/home_page.lua
  12. 98 43
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/lcd/ui/lcd_page.lua
  13. 1 1
      module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/lcd/ui/ui_main.lua
  14. 3 3
      module/Air780EHM_Air780EHV_Air780EGH/demo/audio/Air780EHM_Air780EGH/main.lua
  15. 0 1
      module/Air780EHM_Air780EHV_Air780EGH/demo/audio/Air780EHM_Air780EGH/play_file.lua
  16. 0 2
      module/Air780EHM_Air780EHV_Air780EGH/demo/audio/Air780EHM_Air780EGH/play_stream.lua
  17. 3 3
      module/Air780EHM_Air780EHV_Air780EGH/demo/audio/Air780EHM_Air780EGH/play_tts.lua
  18. 2 2
      module/Air780EHM_Air780EHV_Air780EGH/demo/audio/Air780EHV/main.lua
  19. BIN
      module/Air780EHM_Air780EHV_Air780EGH/demo/audio/Air780EHV/sample-6s.mp3
  20. 73 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/rsa/main.lua
  21. 27 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/rsa/privkey.pem
  22. 9 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/rsa/public.pem
  23. 58 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/rsa/readme.md
  24. 59 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/rsa/rsa_app.lua
  25. 73 0
      module/Air780EPM/demo/rsa/main.lua
  26. 27 0
      module/Air780EPM/demo/rsa/privkey.pem
  27. 9 0
      module/Air780EPM/demo/rsa/public.pem
  28. 54 0
      module/Air780EPM/demo/rsa/readme.md
  29. 59 0
      module/Air780EPM/demo/rsa/rsa_app.lua
  30. 73 0
      module/Air8000/demo/rsa/main.lua
  31. 27 0
      module/Air8000/demo/rsa/privkey.pem
  32. 9 0
      module/Air8000/demo/rsa/public.pem
  33. 54 0
      module/Air8000/demo/rsa/readme.md
  34. 59 0
      module/Air8000/demo/rsa/rsa_app.lua
  35. 73 0
      module/Air8101/demo/rsa/main.lua
  36. 27 0
      module/Air8101/demo/rsa/privkey.pem
  37. 9 0
      module/Air8101/demo/rsa/public.pem
  38. 54 0
      module/Air8101/demo/rsa/readme.md
  39. 59 0
      module/Air8101/demo/rsa/rsa_app.lua
  40. 49 5
      olddemo/ftp/main.lua
  41. 5 2
      script/libs/exaudio.lua
  42. 7 0
      script/libs/excloud.lua

+ 7 - 7
bsp/pc/test/004.http/main.lua

@@ -33,9 +33,9 @@ sys.taskInit(function()
     )
     local body = resp.body:query()
     -- log.info("http", code, json.encode(resp.headers), #body)
-    log.info("http", "body", #body)
-    log.info("http", "body", body)
-    log.info("http", "body", body:toHex())
+    log.info("http2", "body", #body)
+    log.info("http2", "body", body)
+    log.info("http2", "body", body:toHex())
 
     -- body = io.readFile("/luadb/gzip")
     -- log.info("gzip", #body)
@@ -43,11 +43,11 @@ sys.taskInit(function()
     -- log.info("http", miniz.uncompress(body:sub(11), 0))
     -- log.info("http", body:toHex())
 
-    -- local code, headers, body = http.request("GET", "http://quan.suning.com/getSysTime.do", {Accept="*/*", ["User-Agent"]="ABC"}, nil, {debug=true})
-    -- log.info("http", code, json.encode(headers), body)
+    local code, headers, body = http.request("GET", "http://httpbin.air32.cn/range/1024", nil, nil, {debug=false}).wait()
+    log.info("http3", code, json.encode(headers), body)
 
-    -- local code, resp = httpplus.request({url="http://quan.suning.com/getSysTime.do"})
-    -- log.info("http", code, resp.body:query())
+    local code, headers = http.request("GET", "http://httpbin.air32.cn/stream-bytes/20", nil, nil, {debug=true}).wait()
+    log.info("http4", code, headers, body)
 end)
 
 sys.run()

+ 10 - 0
bsp/pc/test/110.http_timeout/main.lua

@@ -0,0 +1,10 @@
+_G.sys = require("sys")
+
+sys.taskInit(function()
+    sys.wait(1000)
+    log.info("http_timeout", "start", "5s timeout test")
+    local code, headers, body = http.request("GET", "http://httpbin.air32.cn/delay/10", {timeout=5000}).wait()
+    log.info("http_timeout", code, body)
+end)
+
+sys.run()

+ 1 - 1
components/airlink/src/devinfo/luat_airlink_devinfo_wlan.c

@@ -143,7 +143,7 @@ void luat_airlink_devinfo_init(AIRLINK_DEV_INFO_UPDATE_CB cb)
 {
     send_devinfo_update_evt = cb;
     g_airlink_self_dev_info.tp = 0x01;
-    uint32_t fw_version = 18;
+    uint32_t fw_version = 19;
     memcpy(g_airlink_self_dev_info.wifi.version, &fw_version, sizeof(uint32_t));   // 版本
     g_airlink_wlan_evt_cb = wifi_evt_handler;
     send_devinfo_update_evt();

+ 70 - 17
components/network/libftp/luat_ftp_client.c

@@ -19,8 +19,9 @@
 luat_ftp_ctrl_t g_s_ftp = {0};
 
 static void l_ftp_cb(FTP_SUCCESS_STATE_e state){
+	luat_ftp_cb_t ftp_cb;
 	if (g_s_ftp.network->ftp_cb){
-		luat_ftp_cb_t ftp_cb = g_s_ftp.network->ftp_cb;
+		ftp_cb = (luat_ftp_cb_t)g_s_ftp.network->ftp_cb;
 		ftp_cb(&g_s_ftp,state);
 	}
 #ifndef __LUATOS__
@@ -49,7 +50,7 @@ static uint32_t luat_ftp_data_send(luat_ftp_ctrl_t *ftp_ctrl, uint8_t* send_data
 	if (send_len == 0)
 		return 0;
 	uint32_t tx_len = 0;
-	LLOGD("luat_ftp_data_send data:%d",send_len);
+	LLOGD("send %p data:%d", ftp_ctrl, send_len);
 	network_tx(g_s_ftp.network->data_netc, send_data, send_len, 0, NULL, 0, &tx_len, 0);
 	return tx_len;
 }
@@ -58,7 +59,7 @@ static uint32_t luat_ftp_cmd_send(luat_ftp_ctrl_t *ftp_ctrl, uint8_t* send_data,
 	if (send_len == 0)
 		return 0;
 	uint32_t tx_len = 0;
-	LLOGD("luat_ftp_cmd_send data:%.*s",send_len,send_data);
+	LLOGD("send %p cmd:%.*s", ftp_ctrl, send_len, send_data);
 	network_tx(g_s_ftp.network->cmd_netc, send_data, send_len, 0, NULL, 0, &tx_len, timeout_ms);
 	return tx_len;
 }
@@ -66,7 +67,7 @@ static uint32_t luat_ftp_cmd_send(luat_ftp_ctrl_t *ftp_ctrl, uint8_t* send_data,
 static int luat_ftp_cmd_recv(luat_ftp_ctrl_t *ftp_ctrl,uint8_t *recv_data,uint32_t *recv_len,uint32_t timeout_ms){
 	uint8_t is_break = 0,is_timeout = 0;
 	int ret = network_wait_rx(g_s_ftp.network->cmd_netc, timeout_ms, &is_break, &is_timeout);
-	LLOGD("luat_ftp_cmd_recv network_wait_rx ret:%d is_break:%d is_timeout:%d",ret,is_break,is_timeout);
+	LLOGD("cmd recv %o network_wait_rx ret:%d is_break:%d is_timeout:%d", ftp_ctrl, ret,is_break,is_timeout);
 	if (ret)
 		return -1;
 	if (is_timeout)
@@ -77,6 +78,7 @@ static int luat_ftp_cmd_recv(luat_ftp_ctrl_t *ftp_ctrl,uint8_t *recv_data,uint32
 }
 
 static int32_t luat_ftp_data_callback(void *data, void *param){
+	(void)param;
 	OS_EVENT *event = (OS_EVENT *)data;
 	uint8_t *rx_buffer;
 	int ret = 0;
@@ -218,9 +220,9 @@ static int luat_ftp_pasv_connect(luat_ftp_ctrl_t *ftp_ctrl,uint32_t timeout_ms){
 	if (ret){
 		return -1;
 	}else{
-		LLOGD("luat_ftp_pasv_connect cmd_recv_data %s",g_s_ftp.network->cmd_recv_data);
+		LLOGD("connect %p cmd_recv_data %.*s", ftp_ctrl, g_s_ftp.network->cmd_recv_len, g_s_ftp.network->cmd_recv_data);
 		if (memcmp(g_s_ftp.network->cmd_recv_data, FTP_ENTER_PASSIVE, 3)){
-			LLOGD("ftp pasv_connect wrong");
+			LLOGW("ftp pasv_connect wrong %.*s", g_s_ftp.network->cmd_recv_len, g_s_ftp.network->cmd_recv_data);
 			return -1;
 		}
 	}
@@ -317,8 +319,9 @@ static int luat_ftp_pasv_connect(luat_ftp_ctrl_t *ftp_ctrl,uint32_t timeout_ms){
 static int ftp_login(void)
 {
 	int ret;
-	if(network_connect(g_s_ftp.network->cmd_netc, g_s_ftp.network->addr, strlen(g_s_ftp.network->addr), NULL, g_s_ftp.network->port, FTP_SOCKET_TIMEOUT)){
-		LLOGE("ftp network_connect fail");
+	ret = network_connect(g_s_ftp.network->cmd_netc, g_s_ftp.network->addr, strlen(g_s_ftp.network->addr), NULL, g_s_ftp.network->port, FTP_SOCKET_TIMEOUT);
+	if (ret) {
+		LLOGE("ftp network_connect fail %d", ret);
 		return -1;
 	}
 	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);
@@ -450,6 +453,7 @@ static void ftp_task(void *param){
 	OS_EVENT task_event;
 	uint8_t is_timeout = 0;
 
+	luat_rtos_task_sleep(10); // 起来就等10ms, 给调用者一点时间运行完毕
 
 	g_s_ftp.is_run = 1;
 	luat_rtos_event_recv(g_s_ftp.task_handle, FTP_EVENT_LOGIN, &task_event, NULL, LUAT_WAIT_FOREVER);
@@ -691,6 +695,7 @@ wait_event_and_out:
 
 
 int luat_ftp_login(uint8_t adapter,const char * ip_addr,uint16_t port,const char * username,const char * password,luat_ftp_tls_t* luat_ftp_tls,luat_ftp_cb_t ftp_cb){
+	int result = 0;
 	if (g_s_ftp.network){
 		LLOGE("ftp already login, please close first");
 		return FTP_ERROR_STATE;
@@ -709,23 +714,48 @@ int luat_ftp_login(uint8_t adapter,const char * ip_addr,uint16_t port,const char
 		LLOGE("bad network adapter index %d", g_s_ftp.network->adapter_index);
 		return FTP_ERROR_STATE;
 	}
+	LLOGD("ftp adapter %d host %s port %d", g_s_ftp.network->adapter_index, ip_addr, port);
 	g_s_ftp.network->cmd_netc = network_alloc_ctrl(g_s_ftp.network->adapter_index);
 	if (!g_s_ftp.network->cmd_netc){
 		LLOGE("cmd_netc create fail");
 		return FTP_ERROR_NO_MEM;
 	}
 	g_s_ftp.network->port = port;
-	if (strlen(ip_addr) > 0 && strlen(ip_addr) < 64)
+
+	if (strlen(ip_addr) > 0 && strlen(ip_addr) < 64) {
 		memcpy(g_s_ftp.network->addr, ip_addr, strlen(ip_addr) + 1);
-	if (strlen(username) > 0 && strlen(username) < 64)
+	}
+	else {
+		LLOGE("invalid ip address length %d", strlen(ip_addr));
+		result = -20;
+		goto error;
+	}
+	if (strlen(username) > 0 && strlen(username) < 64) {
 		memcpy(g_s_ftp.network->username, username, strlen(username) + 1);
-	if (strlen(password) > 0 && strlen(password) < 64)
+	}
+	else {
+		LLOGE("invalid username length %d", strlen(username));
+		result = -21;
+		goto error;
+	}
+	if (strlen(password) > 0 && strlen(password) < 64) {
 		memcpy(g_s_ftp.network->password, password, strlen(password) + 1);
+	}
+	else {
+		LLOGE("invalid password length %d", strlen(password));
+		result = -22;
+		goto error;
+	}
+
 	if (luat_ftp_tls == NULL){
 		network_deinit_tls(g_s_ftp.network->cmd_netc);
-	}else{
+	}
+	else {
+		#if 0
 		if (network_init_tls(g_s_ftp.network->cmd_netc, (luat_ftp_tls->server_cert || luat_ftp_tls->client_cert)?2:0)){
-			return FTP_ERROR_CLOSE;
+			LLOGE("ftp tls init fail");
+			result = -23;
+			goto error;
 		}
 		if (luat_ftp_tls->server_cert){
 			network_set_server_cert(g_s_ftp.network->cmd_netc, (const unsigned char *)luat_ftp_tls->server_cert, strlen(luat_ftp_tls->server_cert)+1);
@@ -735,18 +765,36 @@ int luat_ftp_login(uint8_t adapter,const char * ip_addr,uint16_t port,const char
 					(const unsigned char *)luat_ftp_tls->client_key, strlen(luat_ftp_tls->client_key)+1,
 					(const unsigned char *)luat_ftp_tls->client_password, strlen(luat_ftp_tls->client_password)+1);
 		}
+		#else
+		LLOGE("ftp tls not support yet");
+		result = -24;
+		goto error;
+		#endif
 	}
-	network_set_ip_invaild(&g_s_ftp.network->ip_addr);
-	int result = luat_rtos_task_create(&g_s_ftp.task_handle, 2*1024, 10, "ftp", ftp_task, NULL, 16);
+
+	// task会主动等10ms
+	result = luat_rtos_task_create(&g_s_ftp.task_handle, 8*1024, 10, "ftp", ftp_task, NULL, 16);
 	if (result) {
 		LLOGE("创建ftp task失败!! %d", result);
-		return result;
+		goto error;
 	}
+	
+	network_set_ip_invaild(&g_s_ftp.network->ip_addr);
 	network_init_ctrl(g_s_ftp.network->cmd_netc,g_s_ftp.task_handle, ftp_task_cb, NULL);
 	network_set_base_mode(g_s_ftp.network->cmd_netc, 1, 30000, 0, 0, 0, 0);
 	network_set_local_port(g_s_ftp.network->cmd_netc, 0);
+	// g_s_ftp.network->cmd_netc->is_debug = 1;
+
 	luat_rtos_event_send(g_s_ftp.task_handle, FTP_EVENT_LOGIN, 0, 0, 0, LUAT_WAIT_FOREVER);
 	return 0;
+error:
+	// 清理资源
+	if (g_s_ftp.network->cmd_netc) {
+		network_force_close_socket(g_s_ftp.network->cmd_netc);
+		network_release_ctrl(g_s_ftp.network->cmd_netc);
+		g_s_ftp.network->cmd_netc = NULL;
+	}
+	return result;
 }
 
 int luat_ftp_command(const char * command){
@@ -838,4 +886,9 @@ int luat_ftp_push(const char * local_name,const char * remote_name){
 	return 0;
 }
 
-void luat_ftp_debug(uint8_t on_off) {g_s_ftp.debug_onoff = on_off;}
+void luat_ftp_debug(uint8_t on_off) {
+	g_s_ftp.debug_onoff = on_off;
+	if (NULL != g_s_ftp.network && g_s_ftp.network->cmd_netc) {
+		g_s_ftp.network->cmd_netc->is_debug = on_off;
+	}
+}

+ 3 - 1
components/network/libhttp/luat_http.h

@@ -147,7 +147,9 @@ typedef struct{
 	uint8_t new_data;
 	uint8_t context_len_vaild;
 	uint8_t luatos_mode;
-
+	// TCP连接是否已经关闭
+	uint8_t tcp_closed;
+	uint8_t http_body_is_finally;
 }luat_http_ctrl_t;
 
 //下面2个API是luatos内部使用,csdk不使用

+ 104 - 54
components/network/libhttp/luat_http_client.c

@@ -49,18 +49,17 @@ extern void luat_http_client_onevent(luat_http_ctrl_t *http_ctrl, int error_code
 
 static void http_send_message(luat_http_ctrl_t *http_ctrl);
 static int32_t luat_lib_http_callback(void *data, void *param);
+static void on_tcp_closed(luat_http_ctrl_t *http_ctrl);
 
 int strncasecmp(const char *string1, const char *string2, size_t count);
 
 static void http_close_nw(luat_http_ctrl_t *http_ctrl) {
-	LLOGI("http close nw %p", http_ctrl);
+	LLOGD("http close nw %p", http_ctrl);
 	if (http_ctrl->netc){
 		network_close(http_ctrl->netc, 0);
 	}
 	if (http_ctrl->timeout_timer){
 		luat_stop_rtos_timer(http_ctrl->timeout_timer);
-		luat_release_rtos_timer(http_ctrl->timeout_timer);
-    	http_ctrl->timeout_timer = NULL;
 	}
 }
 int http_close(luat_http_ctrl_t *http_ctrl){
@@ -187,20 +186,30 @@ static void http_network_close(luat_http_ctrl_t *http_ctrl)
 	}
 }
 
-
-
 static void http_resp_error(luat_http_ctrl_t *http_ctrl, int error_code) {
-	LLOGD("http_resp_error error_code:%d close_state:%d",error_code,http_ctrl->close_state);
+	LLOGD("report error(1) %d tcp_closed %d nw state %d",error_code, http_ctrl->tcp_closed, http_ctrl->netc->state);
+	if (0 == http_ctrl->tcp_closed && NW_STATE_DISCONNECTING == http_ctrl->netc->state) {
+		on_tcp_closed(http_ctrl);
+		return;
+	}
+	LLOGD("report error(2) %d tcp_closed %d nw state %d",error_code, http_ctrl->tcp_closed, http_ctrl->netc->state);
+	if (0 == http_ctrl->tcp_closed && NW_STATE_ONLINE == http_ctrl->netc->state) {
+		if (0 == http_ctrl->error_code) {
+			http_ctrl->error_code = error_code;
+		}
+		http_close_nw(http_ctrl);
+		return;
+	}
+	if (http_ctrl->error_code) {
+		error_code = http_ctrl->error_code;
+	}
 #ifdef LUAT_USE_FOTA
-	if (http_ctrl->isfota && error_code == HTTP_ERROR_FOTA && http_ctrl->close_state == 0){
-		http_ctrl->close_state = 1;
+	if (http_ctrl->isfota && error_code == HTTP_ERROR_FOTA){
 		luat_fota_end(0);
-		luat_http_client_onevent(http_ctrl, error_code, 0);
-		return;
 	}
 #endif
 	LLOGD("http_resp_error headers_complete:%d re_request_count:%d",http_ctrl->headers_complete,http_ctrl->re_request_count);
-	if (http_ctrl->close_state == 0 && http_ctrl->headers_complete==1 && http_ctrl->re_request_count < http_ctrl->retry_cnt_max){
+	if (http_ctrl->tcp_closed == 0 && http_ctrl->headers_complete==1 && http_ctrl->re_request_count < http_ctrl->retry_cnt_max){
 		#ifdef LUAT_USE_NETDRV
 		luat_netdrv_fire_socket_event_netctrl(EV_NW_TIMEOUT, http_ctrl->netc, 3);
 		#endif
@@ -211,14 +220,13 @@ static void http_resp_error(luat_http_ctrl_t *http_ctrl, int error_code) {
 			LLOGE("http_resp_error network_connect error");
 			goto error;
 		}
-	}else if (http_ctrl->close_state==0){
-		#ifdef LUAT_USE_NETDRV
-		luat_netdrv_fire_socket_event_netctrl(EV_NW_TIMEOUT, http_ctrl->netc, 3);
-		#endif
-error:
-		http_ctrl->close_state=1;
-		luat_http_client_onevent(http_ctrl, error_code, 0);
+		return;
 	}
+error:
+	#ifdef LUAT_USE_NETDRV
+	luat_netdrv_fire_socket_event_netctrl(EV_NW_SOCKET_ERROR, http_ctrl->netc, 3);
+	#endif
+	luat_http_client_onevent(http_ctrl, error_code, 0);
 }
 
 // body接收回调
@@ -309,6 +317,11 @@ static int on_headers_complete(http_parser* parser){
 		}
 	#ifdef LUAT_USE_FOTA
 		else if(http_ctrl->isfota){
+			if (parser->status_code != 200 && parser->status_code != 206){
+				LLOGE("fota http code error %d", parser->status_code);
+				http_resp_error(http_ctrl, HTTP_ERROR_FOTA);
+				return -1;
+			}
 			luat_fota_init(http_ctrl->address, http_ctrl->length, http_ctrl->spi_device, NULL, 0);
 		}
 	#endif
@@ -341,8 +354,8 @@ static int on_headers_complete(http_parser* parser){
 
 static int on_body(http_parser* parser, const char *at, size_t length){
 	luat_http_ctrl_t *http_ctrl =(luat_http_ctrl_t *)parser->data;
-	if (length > 512) {
-		LLOGD("on_body first 512byte:%.*s",512,at);
+	if (length > 128) {
+		LLOGD("on_body first 128byte:%.*s", 128, at);
 	} else {
 		LLOGD("on_body:%.*s",length,at);
 	}
@@ -368,8 +381,6 @@ static int on_body(http_parser* parser, const char *at, size_t length){
 	#ifdef LUAT_USE_FOTA
 		else if(http_ctrl->isfota && (parser->status_code == 200 || parser->status_code == 206)){
 			if (luat_fota_write((uint8_t*)at, length) < 0){
-				luat_fota_end(0);
-				http_network_close(http_ctrl);
 				http_resp_error(http_ctrl, HTTP_ERROR_FOTA);
 				return -1;
 			}
@@ -422,6 +433,15 @@ static int on_body(http_parser* parser, const char *at, size_t length){
 			http_cb(HTTP_STATE_GET_BODY, (void *)at, length, http_ctrl->http_cb_userdata);
 		}
 	}
+	if (http_ctrl->resp_content_len > 0 && http_ctrl->body_len >= http_ctrl->resp_content_len) {
+		http_ctrl->http_body_is_finally = 1;
+		LLOGD("http body recv done by content_length");
+		http_close_nw(http_ctrl);
+	}
+	else if (http_ctrl->http_body_is_finally) {
+		LLOGD("http body recv done by chunked end");
+		http_close_nw(http_ctrl);
+	}
     return 0;
 }
 
@@ -430,7 +450,6 @@ static int on_complete(http_parser* parser, luat_http_ctrl_t *http_ctrl){
 	// http_ctrl->body[http_ctrl->body_len] = 0x00;
 	LLOGD("status_code:%d",parser->status_code);
 	// LLOGD("content_length:%lld",parser->content_length);
-	(void)parser;
 	if (http_ctrl->luatos_mode) {
 		if (http_ctrl->fd != NULL) {
 			luat_fs_fclose(http_ctrl->fd);
@@ -463,39 +482,33 @@ static int on_complete(http_parser* parser, luat_http_ctrl_t *http_ctrl){
 						return -1;
 					}
 				}else{
-					luat_fota_end(0);
 					http_resp_error(http_ctrl, HTTP_ERROR_FOTA);
 					return -1;
 				}
 			}else{
-				luat_fota_end(0);
-				// http_ctrl->close_state = 1;
-				// network_close(http_ctrl->netc, 0);
 				http_resp_error(http_ctrl, HTTP_ERROR_FOTA);
 				return -1;
 			}
 		}
 	#endif
-		// http_ctrl->close_state = 1;
-		network_close(http_ctrl->netc, 0);
 	}
-	luat_http_client_onevent(http_ctrl, HTTP_OK, 0);
+	// network_close(http_ctrl->netc, 0);
     return 0;
 }
 
 static int on_message_complete(http_parser* parser){
 	luat_http_ctrl_t *http_ctrl =(luat_http_ctrl_t *)parser->data;
     LLOGD("on_message_complete");
-	http_ctrl->close_state = 1;
 	return 0;
 }
 
 static int on_chunk_header(http_parser* parser){
 	luat_http_ctrl_t *http_ctrl =(luat_http_ctrl_t *)parser->data;
-	LLOGD("on_chunk_header");
-	LLOGD("content_length:%lld",parser->content_length);
-	// luat_http_ctrl_t *http_ctrl =(luat_http_ctrl_t *)parser->data;
-	// http_ctrl->is_chunk = 1;
+	LLOGD("on_chunk_header content_length:%lld",parser->content_length);
+	if (parser->content_length == 0){
+		http_ctrl->http_body_is_finally = 1;
+		http_close_nw(http_ctrl);
+	}
     return 0;
 }
 
@@ -632,6 +645,35 @@ LUAT_RT_RET_TYPE luat_http_timer_callback(LUAT_RT_CB_PARAM){
 	}
 }
 
+static void on_tcp_closed(luat_http_ctrl_t *http_ctrl) {
+	LLOGI("on_tcp_closed %p", http_ctrl);
+	int ret = 0;
+	http_ctrl->tcp_closed = 1;
+	if (http_ctrl->http_body_is_finally == 0) { // 当没有解析完成
+		// 存在多种可能性
+		// 1. 没有content_length的情况, 又没有chunked
+		if (http_ctrl->resp_content_len == 0 && http_ctrl->headers_complete) {
+			http_ctrl->http_body_is_finally = 1;
+		}
+	}
+	if (http_ctrl->http_body_is_finally) {
+		ret = on_complete(&http_ctrl->parser, http_ctrl);
+		if (ret) {
+			return; // 结束,因为on_complete里面已经上报错误了
+		}
+	}
+	else {
+		if (http_ctrl->error_code == 0) {
+			http_ctrl->error_code = HTTP_ERROR_CLOSE;
+		}
+	}
+	if (http_ctrl->luatos_mode) {
+		http_resp_error(http_ctrl, HTTP_OK);
+	} else {
+		http_network_error(http_ctrl);
+	}
+}
+
 int32_t luat_lib_http_callback(void *data, void *param){
 	OS_EVENT *event = (OS_EVENT *)data;
 	luat_http_ctrl_t *http_ctrl =(luat_http_ctrl_t *)param;
@@ -643,17 +685,21 @@ int32_t luat_lib_http_callback(void *data, void *param){
 	        return 0;
 	    }
 	}
+	if (http_ctrl->tcp_closed){
+		LLOGD("http already closed %p", http_ctrl);
+		return 0;
+	}
 
 	//LLOGD("LINK %d ON_LINE %d EVENT %d TX_OK %d CLOSED %d",EV_NW_RESULT_LINK & 0x0fffffff,EV_NW_RESULT_CONNECT & 0x0fffffff,EV_NW_RESULT_EVENT & 0x0fffffff,EV_NW_RESULT_TX & 0x0fffffff,EV_NW_RESULT_CLOSE & 0x0fffffff);
-	LLOGD("luat_lib_http_callback %d %d %p",event->ID - EV_NW_RESULT_BASE,event->Param1, http_ctrl);
+	LLOGD("luat_lib_http_callback %08X %d %p",event->ID - EV_NW_RESULT_BASE, event->Param1, http_ctrl);
 	if (event->Param1){
 		//LLOGD("LINK %d ON_LINE %d EVENT %d TX_OK %d CLOSED %d",EV_NW_RESULT_LINK & 0x0fffffff,EV_NW_RESULT_CONNECT & 0x0fffffff,EV_NW_RESULT_EVENT & 0x0fffffff,EV_NW_RESULT_TX & 0x0fffffff,EV_NW_RESULT_CLOSE & 0x0fffffff);
 		LLOGE("error event %08X %d host %s port %d",event->ID - EV_NW_RESULT_BASE, event->Param1, http_ctrl->netc->domain_name, http_ctrl->netc->remote_port);
-		if (http_ctrl->luatos_mode) {
-			http_resp_error(http_ctrl, event->ID == EV_NW_RESULT_CONNECT ? HTTP_ERROR_CONNECT : HTTP_ERROR_CLOSE);
-		} else {
+		if (http_ctrl->tcp_closed == 0 && http_ctrl->error_code == 0) {
 			http_ctrl->error_code = event->ID == EV_NW_RESULT_CONNECT ? HTTP_ERROR_CONNECT : HTTP_ERROR_CLOSE;
-			http_network_error(http_ctrl);
+		}
+		if (http_ctrl->tcp_closed == 0) {
+			on_tcp_closed(http_ctrl);
 		}
 		return -1;
 	}
@@ -674,9 +720,9 @@ int32_t luat_lib_http_callback(void *data, void *param){
 			int result = network_rx(http_ctrl->netc, NULL, 0, 0, NULL, NULL, &total_len);
 			if (result) {
 				if (http_ctrl->luatos_mode) {
-					http_resp_error(http_ctrl, HTTP_ERROR_RX);
+					http_resp_error(http_ctrl, http_ctrl->http_body_is_finally ? HTTP_OK : HTTP_ERROR_RX);
 				} else {
-					http_ctrl->error_code = HTTP_ERROR_RX;
+					http_ctrl->error_code = http_ctrl->http_body_is_finally ? HTTP_OK : HTTP_ERROR_RX;
 					http_network_error(http_ctrl);
 				}
 				return -1;
@@ -689,9 +735,9 @@ int32_t luat_lib_http_callback(void *data, void *param){
 					// 能到这里的就是片段太长了
 					// 要么header太长, 要么chunked太长,拒绝吧
 					if (http_ctrl->luatos_mode) {
-						http_resp_error(http_ctrl, HTTP_ERROR_RX);
+						http_resp_error(http_ctrl, http_ctrl->http_body_is_finally ? HTTP_OK : HTTP_ERROR_RX);
 					} else {
-						http_ctrl->error_code = HTTP_ERROR_RX;
+						http_ctrl->error_code = http_ctrl->http_body_is_finally ? HTTP_OK : HTTP_ERROR_RX;
 						http_network_error(http_ctrl);
 					}
 					return -1;
@@ -701,9 +747,9 @@ int32_t luat_lib_http_callback(void *data, void *param){
 			LLOGD("result:%d rx_len:%d",result,rx_len);
 			if (rx_len == 0||result!=0) {
 				if (http_ctrl->luatos_mode) {
-					http_resp_error(http_ctrl, HTTP_ERROR_RX);
+					http_resp_error(http_ctrl, http_ctrl->http_body_is_finally ? HTTP_OK : HTTP_ERROR_RX);
 				} else {
-					http_ctrl->error_code = HTTP_ERROR_RX;
+					http_ctrl->error_code = http_ctrl->http_body_is_finally ? HTTP_OK : HTTP_ERROR_RX;
 					http_network_error(http_ctrl);
 				}
 				return -1;
@@ -739,11 +785,6 @@ int32_t luat_lib_http_callback(void *data, void *param){
 					LLOGW("http exit reason by errno: %d != HPE_OK!!!", http_ctrl->parser.http_errno);
 					return 0;
 				}
-				if (http_ctrl->close_state) {
-					http_ctrl->resp_buff_offset = 0;
-					on_complete(&http_ctrl->parser, http_ctrl);
-					return 0;
-				}
 				if (http_ctrl->resp_buff_offset <= nParseBytes) {
 					http_ctrl->resp_buff_offset = 0;
 				}
@@ -755,7 +796,7 @@ int32_t luat_lib_http_callback(void *data, void *param){
 			else {
 				LLOGD("wait headers %.*s", http_ctrl->resp_buff_offset, http_ctrl->resp_buff);
 			}
-			if (http_ctrl->close_state){
+			if (http_ctrl->tcp_closed){
 				return 0;
 			}
 		}
@@ -805,7 +846,12 @@ int32_t luat_lib_http_callback(void *data, void *param){
 		http_send_message(http_ctrl);
 		return 0;
     case EV_NW_RESULT_CLOSE:
-    	if (!http_ctrl->luatos_mode) {
+		if (http_ctrl->luatos_mode) {
+			if (http_ctrl->tcp_closed == 0) {
+				on_tcp_closed(http_ctrl);
+			}
+		}
+    	else {
 			if (http_ctrl->error_code && (http_ctrl->state != HTTP_STATE_DONE))
 			{
 				LLOGD("http network closed");
@@ -897,6 +943,10 @@ int luat_http_client_base_config(luat_http_ctrl_t* http_ctrl, uint32_t timeout,
 	http_ctrl->debug_onoff = debug_onoff;
 	http_ctrl->netc->is_debug = debug_onoff;
 	http_ctrl->retry_cnt_max = re_request_count;
+	// 如果retry_cnt_max大于0, 则警告将要废弃自动重试功能
+	if (re_request_count > 0) {
+		LLOGE("http client auto re-request is deprecated, please handle retry in your code!!!");
+	}
 	return 0;
 }
 
@@ -1095,7 +1145,7 @@ int luat_http_client_start(luat_http_ctrl_t *http_ctrl, const char *url, uint8_t
 		return -ERROR_PARAM_INVALID;
 	}
     http_ctrl->luatos_mode = 0;
-	http_ctrl->close_state = 0;
+	http_ctrl->tcp_closed = 0;
 	http_ctrl->data_mode = data_mode;
 	http_ctrl->re_request_count = 0;
 	http_ctrl->body_len = 0;

+ 7 - 9
components/network/libhttp/luat_lib_http.c

@@ -392,12 +392,7 @@ int32_t l_http_callback(lua_State *L, void* ptr){
     rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
     luat_http_ctrl_t *http_ctrl =(luat_http_ctrl_t *)msg->ptr;
 	uint64_t idp = http_ctrl->idp;
-	if (http_ctrl->timeout_timer){
-		luat_stop_rtos_timer(http_ctrl->timeout_timer);
-		luat_release_rtos_timer(http_ctrl->timeout_timer);
-		http_ctrl->timeout_timer = NULL;
-	}
-	LLOGD("l_http_callback arg1:%d is_download:%d idp:%d",msg->arg1,http_ctrl->is_download,idp);
+	LLOGI("l_http_callback arg1:%d is_download:%d idp:%d",msg->arg1,http_ctrl->is_download,idp);
 	if (msg->arg1!=0 && msg->arg1!=HTTP_ERROR_FOTA ){
 		if (msg->arg1 == HTTP_CALLBACK){
 			lua_geti(L, LUA_REGISTRYINDEX, (int)http_ctrl->http_cb);
@@ -477,10 +472,10 @@ int32_t l_http_callback(lua_State *L, void* ptr){
 exit:
 	if (http_ctrl->http_cb){
 		luaL_unref(L, LUA_REGISTRYINDEX, (int)http_ctrl->http_cb);
-		http_ctrl->http_cb = 0;
+		http_ctrl->http_cb = NULL;
 		if (http_ctrl->http_cb_userdata){
 			luaL_unref(L, LUA_REGISTRYINDEX, (int)http_ctrl->http_cb_userdata);
-			http_ctrl->http_cb_userdata = 0;
+			http_ctrl->http_cb_userdata = NULL;
 		}
 	}
 	http_close(http_ctrl);
@@ -488,8 +483,11 @@ exit:
 }
 
 void luat_http_client_onevent(luat_http_ctrl_t *http_ctrl, int error_code, int arg) {
-	// network_close(http_ctrl->netc, 0);
+	LLOGD("luat_http_client_onevent %p %d", http_ctrl, error_code);
 	if (!http_ctrl->luatos_mode) return;
+	if (http_ctrl->timeout_timer && error_code != HTTP_CALLBACK){
+		luat_stop_rtos_timer(http_ctrl->timeout_timer);
+	}
 	rtos_msg_t msg = {0};
 	msg.handler = l_http_callback;
 	msg.ptr = http_ctrl;

+ 4 - 3
lua/src/loslib.c

@@ -217,11 +217,12 @@ static int os_getenv (lua_State *L) {
 static int os_clock (lua_State *L) {
 #ifdef LUAT_USE_MCU
 #ifdef LUAT_CONF_VM_64bit
-  extern uint64_t luat_mcu_tick64(void);
-  lua_pushinteger(L, (lua_Integer)luat_mcu_tick64());
+  extern uint64_t luat_mcu_tick64_ms(void);
+  lua_pushinteger(L, (lua_Integer)luat_mcu_tick64_ms()/1000);
 #else
   extern long luat_mcu_ticks(void);
-  lua_pushinteger(L, (lua_Integer)luat_mcu_ticks());
+  extern uint32_t luat_mcu_hz(void);
+  lua_pushinteger(L, (lua_Integer)luat_mcu_ticks()/luat_mcu_hz());
 #endif // LUAT_CONF_VM_64bit
 #else
   lua_pushinteger(L, 0);

+ 1 - 1
luat/vfs/luat_fs_posix.c

@@ -146,7 +146,7 @@ FILE* luat_vfs_posix_fopen(void* userdata, const char *filename, const char *mod
     // LLOGD("fopen %s %s", filename + FILENAME_OFFSET, mode);
     FILE* fd = fopen(filename + FILENAME_OFFSET, mode);
     if (!fd) {
-        LLOGW("fopen %s %s fail", filename + FILENAME_OFFSET, mode);
+        // LLOGW("fopen %s %s fail", filename + FILENAME_OFFSET, mode);
     }
     return fd;
 }

+ 6 - 2
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/lcd/readme.md

@@ -51,7 +51,8 @@
 1. **基本图形绘制** - 展示点、线、矩形、圆形等基本图形绘制功能
 2. **图片显示** - 支持外部图片文件显示
 3. **二维码生成** - 动态生成并显示二维码
-4. **颜色示例** - 展示多种颜色显示效果
+4. **xbm格式位图示例** - 显示16*16 xbm点阵
+5. **中文、英文字体示例** - 显示12号中文字体和英文字体
 
 ### 4.2 GTFont矢量字体演示
 1. **矢量字体显示** - 使用AirFONTS_1000矢量字库小板显示平滑字体
@@ -98,6 +99,9 @@
 <tr> <td>Air780EHM/Air780EHV/Air780EGH 核心板</td><td>AirFONT_1000配件板</td></tr> <tr> <td>83/SPI0_CS</td><td>CS</td></tr> <tr> <td>84/SPI0_MISO</td><td>MISO</td></tr> <tr> <td>85/SPI0_MOSI</td><td>MOSI</td></tr> <tr> <td>86/SPI0_CLK</td><td>CLK</td></tr> <tr> <td>24/VDD_EXT</td><td>VCC</td></tr> 
 </table>
 
+#### 5.2.3 接线图
+![](https://docs.openLuat.com/cdn/image/Air780EHV_AirLCD_10010_AirFONTS_1000接线图.jpg)
+
 ## 六、演示软件环境
 
 ### 6.1 开发工具
@@ -185,7 +189,7 @@ require "ui_main"
 1. 查看基本图形绘制示例(点、线、矩形、圆形)
 2. 查看图片显示区域(显示logo图片)
 3. 查看二维码区域(合宙文档二维码)
-4. 查看颜色填充示例展示
+4. 查看位图和字体示例
 5. 点击"返回"按钮回到主页
 
 #### 7.4.3 GTFont演示页面

+ 12 - 6
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/lcd/ui/home_page.lua

@@ -17,6 +17,11 @@
 
 local home_page = {}
 
+-- 屏幕尺寸
+local width, height = lcd.getSize()
+
+local center_x = width / 2
+
 -- 按钮区域定义
 local buttons = {
     lcd_page = { x1 = 10, y1 = 350, x2 = 100, y2 = 420 },
@@ -39,19 +44,20 @@ function home_page.draw()
     lcd.clear()
     lcd.setColor(0xFFFF, 0x0000)
     lcd.setFont(lcd.font_opposansm12_chinese)
-    
+
     -- 显示标题
-    -- 后续会新增lcd.getStrWidth(title)接口获取文本宽度,对齐,居中,换行可使用
+    -- 后续V2020版本以上支持lcd核心库的固件会新增lcd.getStrWidth(title)接口获取文本宽度,对齐、居中、换行可使用
+    -- lcd.drawStr(center_x - lcd.getStrWidth(title) / 2, 50, title, 0x0000) -- 自动居中
     lcd.drawStr(106, 50, title, 0x0000)
 
     -- 显示说明文字
     lcd.drawStr(46, 68, content1, 0x0000)
-    
+
     -- 绘制LCD演示按钮
     lcd.fill(buttons.lcd_page.x1, buttons.lcd_page.y1,
         buttons.lcd_page.x2, buttons.lcd_page.y2, 0x001F)
     lcd.drawStr(15, 390, "lcd核心库演示", 0xFFFF)
-    
+
     -- 绘制GTFont演示按钮
     lcd.fill(buttons.gtfont_page.x1, buttons.gtfont_page.y1,
         buttons.gtfont_page.x2, buttons.gtfont_page.y2, 0xF800)
@@ -69,7 +75,7 @@ end
 
 @api home_page.handle_touch(x, y, switch_page)
 @number x 触摸点X坐标,范围0-319
-@number y 触摸点Y坐标,范围0-479  
+@number y 触摸点Y坐标,范围0-479
 @function switch_page 页面切换回调函数
 @return boolean 事件处理成功返回true,否则返回false
 ]]
@@ -98,4 +104,4 @@ function home_page.handle_touch(x, y, switch_page)
     return false
 end
 
-return home_page
+return home_page

+ 98 - 43
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/lcd/ui/lcd_page.lua

@@ -18,7 +18,7 @@
 local lcd_page = {}
 
 -- 按钮区域定义
-local back_button = {x1 = 10, y1 = 10, x2 = 80, y2 = 50}
+local back_button = { x1 = 10, y1 = 10, x2 = 80, y2 = 50 }
 
 --[[
 绘制LCD演示页面;
@@ -30,56 +30,111 @@ local back_button = {x1 = 10, y1 = 10, x2 = 80, y2 = 50}
 function lcd_page.draw()
     lcd.clear()
     lcd.setFont(lcd.font_opposansm12_chinese)
-    
+
     -- 绘制返回按钮
     lcd.fill(back_button.x1, back_button.y1, back_button.x2, back_button.y2, 0xC618)
     lcd.setColor(0x07E0, 0x0000)
     lcd.drawStr(35, 35, "返回", 0x0000)
-    
     -- 设置默认颜色
     lcd.setColor(0xFFFF, 0x0000)
-    
+
     -- 显示标题
-    lcd.drawStr(124, 20 + 12, "lcd核心库演示", 0x0000)
-    
-    -- 绘制各种图形
-    lcd.drawStr(20, 60 + 12, "基本图形绘制:", 0x0000)
-    
+    lcd.drawStr(120, 33, "LCD核心库演示", 0x0000)
+    lcd.drawLine(20, 55, 300, 55, 0x8410)
+
+    -- === 第一区域:基本图形 ===
+    lcd.drawStr(20, 75, "基本图形绘制:", 0x0000)
+
     -- 绘制点
-    lcd.drawPoint(30, 100, 0xFCC0)
-    lcd.drawPoint(40, 100, 0x07E0)
-    lcd.drawPoint(50, 100, 0x001F)
-    
+    lcd.drawStr(30, 98, "点:", 0x0000)
+    lcd.drawPoint(55, 98, 0xFCC0)
+    lcd.drawPoint(65, 98, 0x07E0)
+    lcd.drawPoint(75, 98, 0x001F)
+
     -- 绘制线
-    lcd.drawLine(70, 90, 150, 90, 0xFCC0)
-    lcd.drawLine(70, 100, 150, 110, 0x07E0)
-    lcd.drawLine(70, 110, 150, 90, 0x001F)
-    
-    -- 绘制矩形
-    lcd.drawRectangle(170, 90, 220, 120, 0x7296)
-    lcd.fill(230, 90, 280, 120, 0x07E0)
-    
-    -- 绘制圆
-    lcd.drawCircle(100, 180, 25, 0x001F)
-    lcd.drawCircle(180, 180, 25, 0xFCC0)
-    
-    -- 显示图片示例文字
-    lcd.drawStr(20, 220 + 12, "图片显示区域", 0x0000)
-    lcd.drawRectangle(20, 240, 150, 370, 0x0000)
-    lcd.showImage(45, 265, "/luadb/logo.jpg")
-    
-    -- 显示二维码示例文字
-    lcd.drawStr(170, 220 + 12, "二维码区域", 0x0000)
-    lcd.drawRectangle(170, 240, 300, 370, 0x0000)
-    lcd.drawQrcode(185, 255, "https://docs.openluat.com/air8000/", 100)
-    
-    -- 显示颜色示例
-    lcd.drawStr(20, 390 + 12, "颜色示例:", 0x0000)
-    lcd.fill(100, 390, 120, 410, 0xF800)
-    lcd.fill(130, 390, 150, 410, 0x07E0)
-    lcd.fill(160, 390, 180, 410, 0x001F)
-    lcd.fill(190, 390, 210, 410, 0xFCC0)
-    lcd.fill(220, 390, 240, 410, 0x0000)
+    lcd.drawStr(30, 125, "线:", 0x0000)
+    lcd.drawLine(55, 113, 115, 113, 0xFCC0)
+    lcd.drawLine(55, 118, 115, 123, 0x07E0)
+    lcd.drawLine(55, 123, 115, 118, 0x001F)
+
+    -- 绘制矩形(预留右侧空间)
+    lcd.drawStr(30, 160, "矩形:", 0x0000)
+    lcd.drawRectangle(65, 138, 105, 163, 0x7296)
+    lcd.fill(120, 138, 160, 163, 0x07E0)
+
+    -- 绘制圆(预留右侧空间)
+    lcd.drawStr(30, 200, "圆形:", 0x0000)
+    lcd.drawCircle(90, 193, 15, 0x001F)
+    lcd.drawCircle(130, 193, 15, 0xFCC0)
+
+    lcd.drawLine(170, 70, 170, 300, 0x8410) -- 垂直分隔线
+
+    -- === 第二区域:图片和二维码 ===
+    lcd.drawStr(180, 75, "图片/二维码:", 0x0000)
+
+    -- 图片显示区域 (80x80)
+    lcd.drawStr(180, 100, "LOGO:", 0x0000)
+    lcd.drawRectangle(180, 110, 270, 200, 0x0000) 
+    lcd.showImage(185, 115, "/luadb/logo.jpg")  
+
+    -- 二维码区域 (80x80)
+    lcd.drawStr(180, 215, "二维码:", 0x0000)
+    lcd.drawRectangle(180, 225, 270, 308, 0x0000)
+    lcd.drawQrcode(185, 226, "https://docs.openluat.com/air8000/", 80)
+
+    lcd.drawLine(20, 325, 300, 325, 0x8410)
+
+    -- === 第三区域:位图 ===
+    lcd.drawLine(20, 235, 160, 235, 0x8410)
+
+    -- 位图区域
+    lcd.setFont(lcd.font_opposansm12_chinese)
+    lcd.drawStr(20, 255, "位图示例:", 0x0000)
+
+    -- 绘制位图
+    local x_start = 30
+    local y_start = 265
+    lcd.drawXbm(x_start, y_start, 16, 16, string.char(
+        0x00, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x3F, 0x80, 0x00,
+        0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0xFE, 0x7F, 0x00, 0x00))
+
+    lcd.drawXbm(x_start + 20, y_start, 16, 16, string.char(
+        0x00, 0x00, 0x80, 0x00, 0xC4, 0x7F, 0x28, 0x00, 0x10, 0x00, 0xD0, 0x3F, 0x42, 0x20, 0x44, 0x22,
+        0x40, 0x24, 0xF0, 0xFF, 0x24, 0x20, 0x24, 0x22, 0x24, 0x20, 0xE2, 0x7F, 0x02, 0x20, 0x02, 0x1E))
+
+    lcd.drawXbm(x_start, y_start + 20, 16, 16, string.char(
+        0x00, 0x00, 0x00, 0x01, 0x80, 0x01, 0x40, 0x02, 0x20, 0x04, 0x18, 0x18, 0xF4, 0x6F, 0x02, 0x00,
+        0x00, 0x00, 0xF8, 0x1F, 0x08, 0x10, 0x08, 0x10, 0x08, 0x10, 0x08, 0x10, 0xF8, 0x1F, 0x08, 0x10))
+
+    lcd.drawXbm(x_start + 20, y_start + 20, 16, 16, string.char(
+        0x00, 0x00, 0x80, 0x00, 0x00, 0x01, 0xFE, 0x7F, 0x02, 0x40, 0x02, 0x40, 0x00, 0x01, 0xFC, 0x3F,
+        0x04, 0x21, 0x04, 0x21, 0xFC, 0x3F, 0x04, 0x21, 0x04, 0x21, 0x04, 0x21, 0xFC, 0x3F, 0x04, 0x20))
+
+    -- === 第四区域:字体 ===
+    lcd.drawStr(20, 345, "中英文字体示例:", 0x0000)
+
+    -- 中文字体
+    lcd.setFont(lcd.font_opposansm12_chinese)
+    lcd.drawStr(20, 368, "中文字体ABC123", 0x0000)
+
+    -- 英文字体
+    local font_y = 388
+    lcd.setFont(lcd.font_opposansm12)
+    lcd.drawStr(20, font_y, "12px: ABCabc123", 0x0000)
+    font_y = font_y + 18
+
+    lcd.setFont(lcd.font_opposansm16)
+    lcd.drawStr(20, font_y, "16px: ABCabc123", 0x0000)
+    font_y = font_y + 22
+
+    lcd.setFont(lcd.font_opposansm20)
+    lcd.drawStr(20, font_y, "20px: ABCabc123", 0x0000)
+    font_y = font_y + 26
+
+    lcd.setFont(lcd.font_opposansm24)
+    lcd.drawStr(20, font_y, "24px: ABCabc123", 0x0000)
+
+    lcd.drawLine(20, 470, 300, 470, 0x8410)
 end
 
 --[[
@@ -94,7 +149,7 @@ end
 function lcd_page.handle_touch(x, y, switch_page)
     -- 检查返回按钮
     if x >= back_button.x1 and x <= back_button.x2 and
-       y >= back_button.y1 and y <= back_button.y2 then
+        y >= back_button.y1 and y <= back_button.y2 then
         switch_page("home")
         return true
     end

+ 1 - 1
module/Air780EHM_Air780EHV_Air780EGH/demo/accessory_board/AirLCD_1010/lcd/ui/ui_main.lua

@@ -92,7 +92,7 @@ local function handle_touch_event(event, x, y)
         elseif current_page == "gtfont" then
             return gtfont_page.handle_touch(x, y, switch_page)
         elseif current_page == "customer_font_page" then
-            return gtfont_page.handle_touch(x, y, switch_page)
+            return customer_font_page.handle_touch(x, y, switch_page)
         end
     end
     return false

+ 3 - 3
module/Air780EHM_Air780EHV_Air780EGH/demo/audio/Air780EHM_Air780EGH/main.lua

@@ -74,9 +74,9 @@ end
 
 
 
- require "play_file"     --   播放音频文件,可支持wav,amr,mp3 格式音频
--- require "play_tts"      -- 支持文字转普通话输出需要固件支持
--- require "play_stream"        -- 流式播放音频,仅支持PCM 格式,可以将音频推流到云端,用来对接大模型或者流式录音的应用。
+-- require "play_file"     --   播放音频文件,可支持wav,amr,mp3 格式音频
+ require "play_tts"      -- 支持文字转普通话输出需要固件支持
+ -- require "play_stream"        -- 流式播放音频,仅支持PCM 格式,可以将音频推流到云端,用来对接大模型或者流式录音的应用。
 -- require "record_file"        -- 录音到文件
 -- require "record_stream"        -- 流式录音   
 

+ 0 - 1
module/Air780EHM_Air780EHV_Air780EGH/demo/audio/Air780EHM_Air780EGH/play_file.lua

@@ -42,7 +42,6 @@ local audio_play_param ={
     cbfnc = play_end,            -- 播放完毕回调函数
 }
 
-
 ---------------------------------
 ---通过BOOT 按键进行播放停止操作---
 ---------------------------------

+ 0 - 2
module/Air780EHM_Air780EHV_Air780EGH/demo/audio/Air780EHM_Air780EGH/play_stream.lua

@@ -72,7 +72,6 @@ end
 gpio.setup(gpio.PWR_KEY, down_volume, gpio.PULLUP, gpio.FALLING)
 gpio.debounce(gpio.PWR_KEY, 200, 1)   -- 防抖,防止频繁触发
 
-
 ---------------------------------
 ---------模拟获取音频task---------
 ---------------------------------
@@ -92,7 +91,6 @@ end
 
 sys.taskInitEx(audio_get_data, "audio_get_data")
 
-
 ---------------------------------
 ------------通过主task------------
 ---------------------------------

+ 3 - 3
module/Air780EHM_Air780EHV_Air780EGH/demo/audio/Air780EHM_Air780EGH/play_tts.lua

@@ -64,19 +64,19 @@ end
 gpio.setup(gpio.PWR_KEY, next_audio, gpio.PULLUP, gpio.FALLING)
 gpio.debounce(gpio.PWR_KEY, 200, 1)  -- 防抖,防止频繁触发
 
-
 ---------------------------------------------------------------------------------------------------
 ---------------主task------------------------------------------------------------------------------
 --- 关于TTS 音色设置请见: https://docs.openluat.com/air780epm/common/tts/
 ---------------------------------------------------------------------------------------------------
 
-
 local index_number = 1
 local audio_path = nil
 local function audio_task()
     log.info("开始播放TTS")
     if exaudio.setup(audio_setup_param) then
-        exaudio.play_start(audio_play_param) -- 仅仅支持task 中运行
+        --设置音量
+        exaudio.vol(70)    -- 默认音量,范围0-100
+        exaudio.play_start(audio_play_param) 
         while true do
             local msg = sys.waitMsg(taskName, MSG_KEY_PRESS)   -- 等待按键触发
             if msg[2] ==  "NEXT_AUDIO" then      

+ 2 - 2
module/Air780EHM_Air780EHV_Air780EGH/demo/audio/Air780EHV/main.lua

@@ -74,8 +74,8 @@ end
 
 
 
-require "play_file"     --   播放音频文件,可支持wav,amr,mp3 格式音频
--- require "play_tts"      -- 支持文字转普通话输出需要固件支持
+-- require "play_file"     --   播放音频文件,可支持wav,amr,mp3 格式音频
+ require "play_tts"      -- 支持文字转普通话输出需要固件支持
 -- require "play_stream"        -- 流式播放音频,仅支持PCM 格式,可以将音频推流到云端,用来对接大模型或者流式录音的应用。
 -- require "record_file"        -- 录音到文件
 -- require "record_stream"        -- 流式录音   

BIN
module/Air780EHM_Air780EHV_Air780EGH/demo/audio/Air780EHV/sample-6s.mp3


+ 73 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/rsa/main.lua

@@ -0,0 +1,73 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 1.0
+@date    2025.11.4
+@author  王城钧
+@usage
+本demo演示的核心功能为:
+1、rsa_app:普通的rsa加密解密、签名验签功能演示
+2、privkey.pem 和 public.pem:公钥私钥文件
+更多说明参考本目录下的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 = "RSA"
+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)
+
+
+-- 加载http应用功能模块
+require "rsa_app"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 27 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/rsa/privkey.pem

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAp0WakShvpE1/28hyZzz0TFiBpN/d6EU2Wkl5lrazTxK/LDdZ
+kAQuz6eA+kdPUqSueIpN0X8DT4Or5/0yD/ABvgW2t7A6g2/zQpzv9IoAl2f1Vhnx
+dd7iSnhWTeF80lywp4VlHHEVVp7ZCqod59qa9nyp45ycWPwV0VCdX6NwS7chPodC
+/Nf4UVbC1/JJ26WPVKqgOuSz0hIX58pnEaU/ntSDuJiLcKdCNUyIHJEzNYGaQVwx
+mqBoyolktkTOL04AW/OXlOTP8eZrEZpckrODDaWlLUOt5SARS6qUYJSvuyYry8a0
+rGOLbCnoIYVBvu8NX8I5hfeMGnYkEdpx6x/xYwIDAQABAoIBADfwehGDYVqkJFc/
+AKtv4g9KJgkaaN7NjrDBE62IagzOqypBVG1qSLFfRi3s/SUZN9POBNpDzLqhwTKz
+JTPZQuvmg0WI5Pihzst/KmwwXqRDuvNRd8PAhxL6jXo8J38+SkGrxbWuR8GRG+qK
+G7g3Dk3SQQqCjHLh0vYOLKMYSGy5RgfIQyve6yBK1LHeanzgGgcSbawsvCiiDOkb
+Y3I8CHasnK/Hxpmx/NzfNfY+r32AVXx4b+sN2YxB7A9OCxgdwYQHxYvKZ1JIiCh+
+Mv85wDHT25SJ5NLdP/jHILgGrgpyDN5+9c4fk3rjkoTrTs01wJ4rQfItkQ1Zlr0/
+9tub6mECgYEA2e7gerNRv53L0HkY9GS0B7Esi2FiUc/oh4xKRKPm+/ydXcroVbAK
+Mz3mCVlRNvkA+68P670s3AKVpeB671R70dCY+MQXTeNaxc0NlhGOKYoerPd9D/GN
+CF460AT2SQEWYTdIbin2dxeROEuC79zn1yczxnSwjAvqDwpvpHceP3ECgYEAxH1a
+hXk84U5PGKyUuAVMNMaQQll8rcxoFThxrei7iNT6SfVAeHQufCgeW9g1cOL5Vjmk
+9Te1caJt2VXaAhq883qZ1ABypQgcfUwLaK6q4BzAIPtYRIpkJstFKaYWqYaVwCGh
+QTqcP+ebEJ1BgKgxH4nXjlMUMwf8qzTGmQlO/BMCgYEAzZblG7uIlgyFVnC3Eu7h
+SxRgIkjHWMia4yx8b45zfCpORkoBrbw5kyeEmDMzQ3nZ7JS0nz5CUHb7t5UyRA7e
+FAwGEz/hgC/H1Svg8j4zb4qF78Q1rdHAqzFBqDXWJP6qnyFo6cwaXzTTYVkS97bc
+24J2/HPejO88ad39fhiFZ3ECgYEAvDJgaGU2BYrOwZBTJWqVkhr5g0NY4tJcgq68
+W1kFfkqXrAzGglitSWfXpBqTHRuYu5icwe5o0H1F/5t2IvvfLMmp2t/O7vi06OHU
+L6DUs7F16GE1KvjuciXRidG19QueFRdg7ywnCiJYaHJmkccGvfF1z7ENMM+el5EG
+AwBicZcCgYBdcmz3nmTyGb+BY6yHDNrJh97eSJaP5KOpfs1ZGM4Jx/Pa391q1X9Y
+WigaNEpW5eJKzTnyX0GlbEPPhTkBJS4ZwuKRZBvwzmLdoICA+i7gpjwfqDcrdJND
+IhEjjWogNyIvepre3eL1HMaJr6TS4j7wZTl7Ha4httCNBkQb0qJeWw==
+-----END RSA PRIVATE KEY-----

+ 9 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/rsa/public.pem

@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp0WakShvpE1/28hyZzz0
+TFiBpN/d6EU2Wkl5lrazTxK/LDdZkAQuz6eA+kdPUqSueIpN0X8DT4Or5/0yD/AB
+vgW2t7A6g2/zQpzv9IoAl2f1Vhnxdd7iSnhWTeF80lywp4VlHHEVVp7ZCqod59qa
+9nyp45ycWPwV0VCdX6NwS7chPodC/Nf4UVbC1/JJ26WPVKqgOuSz0hIX58pnEaU/
+ntSDuJiLcKdCNUyIHJEzNYGaQVwxmqBoyolktkTOL04AW/OXlOTP8eZrEZpckrOD
+DaWlLUOt5SARS6qUYJSvuyYry8a0rGOLbCnoIYVBvu8NX8I5hfeMGnYkEdpx6x/x
+YwIDAQAB
+-----END PUBLIC KEY-----

+ 58 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/rsa/readme.md

@@ -0,0 +1,58 @@
+功能模块介绍
+---------
+
+1、main.lua:主程序入口;
+
+2、rsa_app:rsa加密解密、签名验签功能演示;
+
+3、privkey.pem:PEM格式的RSA私钥文件
+
+4、public.pem:PEM格式的RSA公钥文件
+
+## 演示功能概述
+
+1、rsa加密解密、签名验签功能
+
+## 演示硬件环境
+
+1、Air780EHM/Air780EHV/Air780EGH核心板一块;
+
+![netdrv_multi](https://docs.openluat.com/accessory/AirSPINORFLASH_1000/image/780EHV.jpg)
+
+2、TYPE-C USB数据线一根
+
+* Air780EHM/Air780EHV/Air780EGH核心板通过 TYPE-C USB 口供电;
+* TYPE-C USB 数据线直接插到核心板的 TYPE-C USB 座子,另外一端连接电脑 USB 口;
+
+演示软件环境
+---------
+
+1、Luatools下载调试工具
+
+2、[Air780EHM V2012版本固件](https://docs.openluat.com/air780epm/luatos/firmware/version/)
+
+[Air780EHV V2012版本固件](https://docs.openluat.com/air780ehv/luatos/firmware/version/)
+
+[Air780EGH V2012版本固件](https://docs.openluat.com/air780egh/luatos/firmware/version/)
+
+## 演示核心步骤
+
+1、搭建好硬件环境
+
+2、Luatools烧录内核固件和demo脚本代码
+
+3、烧录成功后,自动开机运行
+
+4、可以看到代码运行结果如下:
+
+日志中如果出现以下类似以下打印则说明rsa加密解密、签名和验签功能正常
+
+```lua
+[2025-11-04 09:26:34.590][000000002.759] I/user.rsa encrypt 256 
+[2025-11-04 09:26:34.620][000000002.759] 7C4D741E29C7E3612C53B06602FBA562A7F3F1FF197519AECEE8B993E7B1C3EC709C41B3BD54A669836A13F9456322E6FB686398C961C8F283AEB8EFC775F2965CFB0FFC5E40CDADE301CFC9E0A94389C33090274CFC524CF54281565A89DFD6B6558C21F5AC1338BACFDA65057FF936AE1E60FC60A0809A9C1D45F641483F340B297D64BDF2BFBE46E1A34BE578C255F3AE04F1C1D19A28437B77AE98136D460C336CF6221BF33649731E85465721D255764936C9944DA199378CBC9CAAE07CD462EF3AEC0AA5E2F9C7FF68F9B2CAD5FE44C1A18F724CD637CCFE7B2E12BE93B78DC3B39C595C36B798050094B497D8F23AB32D33165861BD3EBCFA04CA0C26
+[2025-11-04 09:26:34.664][000000003.850] I/user.rsa decrypt 3 616263
+[2025-11-04 09:26:34.702][000000004.950] I/user.rsa sign 256 
+[2025-11-04 09:26:34.724][000000004.950] 2A526049B5D0FFFDF370EE1EB37E87A1C054B387386C635B4DD46E3970F90D08732D4CC1B338EA9153B6C52AE22602C55272C828D4F627E1BF8B994BC02C79DB1F0462F3A5A654D9DAF2794F4D8EC1A691FE6D0C455CA0DCE9B9ACDFC44C79D9CFFF46740248131EE58ACE00BE7DA8537F1E6550F17C204ADDD79C735C57D9FD4ACC7006BA22E248B9FEFE002E8FCCCF85B5A8DA0D133669D6463D24F8CA24C2E314CCCE39DF43A05EB33840BDC1298F0361D13FE2EEF3C87A76D826968873B8FEDD748DF54D70CB3D2A5072D137954BFA4FA990D2C01D8061FF0F2E27DF813DA8751A06F38C83827E574EDCC52F271A98EC2E6CD9A8A8AFC9DA0475EB7547D0
+[2025-11-04 09:26:34.750][000000004.968] I/user.rsa verify true
+
+```

+ 59 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/rsa/rsa_app.lua

@@ -0,0 +1,59 @@
+--[[
+@module  rsa_app
+@summary rsa应用功能模块
+@version 1.0
+@date    2025.11.4
+@author  王城钧
+@usage
+本文件为rsa应用功能模块,核心业务逻辑为:基于不同的应用场景,演示rsa核心库的使用方式;
+本文件没有对外接口,直接在main.lua中require "rsa_app"就可以加载运行;   
+]]
+
+--[[
+提醒: 本demo需要用到公钥和私钥, demo目录中的公钥私钥文件是演示用的, 实际使用请自行生成
+
+生成公钥私钥, 可使用openssl命令, 或者找个网页生成. 2048 是RSA位数, 最高支持4096,但不推荐,因为很慢.
+
+openssl genrsa -out privkey.pem 2048
+openssl rsa -in privkey.pem -pubout -out public.pem
+
+-- 下载脚本和资源到设备时, 务必加上本目录下的两个 pem 文件, 否则本demo无法运行.
+]]
+
+local function rsa_task()
+    -- 此处延时2秒是为了方便观察日志,非必须
+    sys.wait(2000)
+
+    -- 检查是否带rsa库, 没有就提醒一下吧
+    if not rsa then
+        log.warn("main", "this demo need rsa lib!!!")
+        return
+    end
+
+    -- 读取公钥并马上加密数据
+    local res = rsa.encrypt((io.readFile("/luadb/public.pem")), "abc")
+    -- 打印结果
+    log.info("rsa", "encrypt", res and #res or 0, res and res:toHex() or "")
+
+    -- 下面是解密, 通常不会在设备端进行, 这里主要是演示用法, 会很慢
+    if res then
+        -- 读取私钥, 然后解码数据
+        local dst = rsa.decrypt((io.readFile("/luadb/privkey.pem")), res, "")
+        log.info("rsa", "decrypt", dst and #dst or 0, dst and dst:toHex() or "")
+    end
+
+    -- 演示签名和验签
+    local hash = crypto.sha1("1234567890"):fromHex()
+    -- 签名通常很慢, 通常是服务器做
+    local sig = rsa.sign((io.readFile("/luadb/privkey.pem")), rsa.MD_SHA1, hash, "")
+    log.info("rsa", "sign", sig and #sig or 0, sig and sig:toHex() or "")
+    if sig then
+        -- 验签是很快的
+        local ret = rsa.verify((io.readFile("/luadb/public.pem")), rsa.MD_SHA1, hash, sig)
+        log.info("rsa", "verify", ret)
+    end
+end
+
+--创建并且启动一个task
+--运行这个task的处理函数rsa_task
+sys.taskInit(rsa_task)

+ 73 - 0
module/Air780EPM/demo/rsa/main.lua

@@ -0,0 +1,73 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 1.0
+@date    2025.11.4
+@author  王城钧
+@usage
+本demo演示的核心功能为:
+1、rsa_app:普通的rsa加密解密、签名验签功能演示
+2、privkey.pem 和 public.pem:公钥私钥文件
+更多说明参考本目录下的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 = "RSA"
+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)
+
+
+-- 加载http应用功能模块
+require "rsa_app"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 27 - 0
module/Air780EPM/demo/rsa/privkey.pem

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAp0WakShvpE1/28hyZzz0TFiBpN/d6EU2Wkl5lrazTxK/LDdZ
+kAQuz6eA+kdPUqSueIpN0X8DT4Or5/0yD/ABvgW2t7A6g2/zQpzv9IoAl2f1Vhnx
+dd7iSnhWTeF80lywp4VlHHEVVp7ZCqod59qa9nyp45ycWPwV0VCdX6NwS7chPodC
+/Nf4UVbC1/JJ26WPVKqgOuSz0hIX58pnEaU/ntSDuJiLcKdCNUyIHJEzNYGaQVwx
+mqBoyolktkTOL04AW/OXlOTP8eZrEZpckrODDaWlLUOt5SARS6qUYJSvuyYry8a0
+rGOLbCnoIYVBvu8NX8I5hfeMGnYkEdpx6x/xYwIDAQABAoIBADfwehGDYVqkJFc/
+AKtv4g9KJgkaaN7NjrDBE62IagzOqypBVG1qSLFfRi3s/SUZN9POBNpDzLqhwTKz
+JTPZQuvmg0WI5Pihzst/KmwwXqRDuvNRd8PAhxL6jXo8J38+SkGrxbWuR8GRG+qK
+G7g3Dk3SQQqCjHLh0vYOLKMYSGy5RgfIQyve6yBK1LHeanzgGgcSbawsvCiiDOkb
+Y3I8CHasnK/Hxpmx/NzfNfY+r32AVXx4b+sN2YxB7A9OCxgdwYQHxYvKZ1JIiCh+
+Mv85wDHT25SJ5NLdP/jHILgGrgpyDN5+9c4fk3rjkoTrTs01wJ4rQfItkQ1Zlr0/
+9tub6mECgYEA2e7gerNRv53L0HkY9GS0B7Esi2FiUc/oh4xKRKPm+/ydXcroVbAK
+Mz3mCVlRNvkA+68P670s3AKVpeB671R70dCY+MQXTeNaxc0NlhGOKYoerPd9D/GN
+CF460AT2SQEWYTdIbin2dxeROEuC79zn1yczxnSwjAvqDwpvpHceP3ECgYEAxH1a
+hXk84U5PGKyUuAVMNMaQQll8rcxoFThxrei7iNT6SfVAeHQufCgeW9g1cOL5Vjmk
+9Te1caJt2VXaAhq883qZ1ABypQgcfUwLaK6q4BzAIPtYRIpkJstFKaYWqYaVwCGh
+QTqcP+ebEJ1BgKgxH4nXjlMUMwf8qzTGmQlO/BMCgYEAzZblG7uIlgyFVnC3Eu7h
+SxRgIkjHWMia4yx8b45zfCpORkoBrbw5kyeEmDMzQ3nZ7JS0nz5CUHb7t5UyRA7e
+FAwGEz/hgC/H1Svg8j4zb4qF78Q1rdHAqzFBqDXWJP6qnyFo6cwaXzTTYVkS97bc
+24J2/HPejO88ad39fhiFZ3ECgYEAvDJgaGU2BYrOwZBTJWqVkhr5g0NY4tJcgq68
+W1kFfkqXrAzGglitSWfXpBqTHRuYu5icwe5o0H1F/5t2IvvfLMmp2t/O7vi06OHU
+L6DUs7F16GE1KvjuciXRidG19QueFRdg7ywnCiJYaHJmkccGvfF1z7ENMM+el5EG
+AwBicZcCgYBdcmz3nmTyGb+BY6yHDNrJh97eSJaP5KOpfs1ZGM4Jx/Pa391q1X9Y
+WigaNEpW5eJKzTnyX0GlbEPPhTkBJS4ZwuKRZBvwzmLdoICA+i7gpjwfqDcrdJND
+IhEjjWogNyIvepre3eL1HMaJr6TS4j7wZTl7Ha4httCNBkQb0qJeWw==
+-----END RSA PRIVATE KEY-----

+ 9 - 0
module/Air780EPM/demo/rsa/public.pem

@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp0WakShvpE1/28hyZzz0
+TFiBpN/d6EU2Wkl5lrazTxK/LDdZkAQuz6eA+kdPUqSueIpN0X8DT4Or5/0yD/AB
+vgW2t7A6g2/zQpzv9IoAl2f1Vhnxdd7iSnhWTeF80lywp4VlHHEVVp7ZCqod59qa
+9nyp45ycWPwV0VCdX6NwS7chPodC/Nf4UVbC1/JJ26WPVKqgOuSz0hIX58pnEaU/
+ntSDuJiLcKdCNUyIHJEzNYGaQVwxmqBoyolktkTOL04AW/OXlOTP8eZrEZpckrOD
+DaWlLUOt5SARS6qUYJSvuyYry8a0rGOLbCnoIYVBvu8NX8I5hfeMGnYkEdpx6x/x
+YwIDAQAB
+-----END PUBLIC KEY-----

+ 54 - 0
module/Air780EPM/demo/rsa/readme.md

@@ -0,0 +1,54 @@
+功能模块介绍
+---------
+
+1、main.lua:主程序入口;
+
+2、rsa_app:rsa加密解密、签名验签功能演示;
+
+3、privkey.pem:PEM格式的RSA私钥文件
+
+4、public.pem:PEM格式的RSA公钥文件
+
+## 演示功能概述
+
+1、rsa加密解密、签名验签功能
+
+## 演示硬件环境
+
+1、Air780EPM开发板一块;
+
+![netdrv_multi](https://docs.openLuat.com/cdn/image/Air780EPM开发板.jpg)
+
+2、TYPE-C USB数据线一根
+
+* Air780EPM开发板/核心板通过 TYPE-C USB 口供电;
+* TYPE-C USB 数据线直接插到核心板的 TYPE-C USB 座子,另外一端连接电脑 USB 口;
+
+演示软件环境
+---------
+
+1、Luatools下载调试工具
+
+2、[Air780EPM V2016版本]([固件和应用脚本Demo - luatos@air8000 - 合宙模组资料中心](https://docs.openluat.com/air780epm/luatos/firmware/))(理论上最新版本固件也可以,如果使用最新版本的固件不可以,可以烧录V2016-1固件对比验证)
+
+## 演示核心步骤
+
+1、搭建好硬件环境
+
+2、Luatools烧录内核固件和demo脚本代码
+
+3、烧录成功后,自动开机运行
+
+4、可以看到代码运行结果如下:
+
+日志中如果出现以下类似以下打印则说明rsa加密解密、签名和验签功能正常
+
+```lua
+[2025-11-04 09:26:34.590][000000002.759] I/user.rsa encrypt 256 
+[2025-11-04 09:26:34.620][000000002.759] 7C4D741E29C7E3612C53B06602FBA562A7F3F1FF197519AECEE8B993E7B1C3EC709C41B3BD54A669836A13F9456322E6FB686398C961C8F283AEB8EFC775F2965CFB0FFC5E40CDADE301CFC9E0A94389C33090274CFC524CF54281565A89DFD6B6558C21F5AC1338BACFDA65057FF936AE1E60FC60A0809A9C1D45F641483F340B297D64BDF2BFBE46E1A34BE578C255F3AE04F1C1D19A28437B77AE98136D460C336CF6221BF33649731E85465721D255764936C9944DA199378CBC9CAAE07CD462EF3AEC0AA5E2F9C7FF68F9B2CAD5FE44C1A18F724CD637CCFE7B2E12BE93B78DC3B39C595C36B798050094B497D8F23AB32D33165861BD3EBCFA04CA0C26
+[2025-11-04 09:26:34.664][000000003.850] I/user.rsa decrypt 3 616263
+[2025-11-04 09:26:34.702][000000004.950] I/user.rsa sign 256 
+[2025-11-04 09:26:34.724][000000004.950] 2A526049B5D0FFFDF370EE1EB37E87A1C054B387386C635B4DD46E3970F90D08732D4CC1B338EA9153B6C52AE22602C55272C828D4F627E1BF8B994BC02C79DB1F0462F3A5A654D9DAF2794F4D8EC1A691FE6D0C455CA0DCE9B9ACDFC44C79D9CFFF46740248131EE58ACE00BE7DA8537F1E6550F17C204ADDD79C735C57D9FD4ACC7006BA22E248B9FEFE002E8FCCCF85B5A8DA0D133669D6463D24F8CA24C2E314CCCE39DF43A05EB33840BDC1298F0361D13FE2EEF3C87A76D826968873B8FEDD748DF54D70CB3D2A5072D137954BFA4FA990D2C01D8061FF0F2E27DF813DA8751A06F38C83827E574EDCC52F271A98EC2E6CD9A8A8AFC9DA0475EB7547D0
+[2025-11-04 09:26:34.750][000000004.968] I/user.rsa verify true
+
+```

+ 59 - 0
module/Air780EPM/demo/rsa/rsa_app.lua

@@ -0,0 +1,59 @@
+--[[
+@module  rsa_app
+@summary rsa应用功能模块
+@version 1.0
+@date    2025.11.4
+@author  王城钧
+@usage
+本文件为rsa应用功能模块,核心业务逻辑为:基于不同的应用场景,演示rsa核心库的使用方式;
+本文件没有对外接口,直接在main.lua中require "rsa_app"就可以加载运行;   
+]]
+
+--[[
+提醒: 本demo需要用到公钥和私钥, demo目录中的公钥私钥文件是演示用的, 实际使用请自行生成
+
+生成公钥私钥, 可使用openssl命令, 或者找个网页生成. 2048 是RSA位数, 最高支持4096,但不推荐,因为很慢.
+
+openssl genrsa -out privkey.pem 2048
+openssl rsa -in privkey.pem -pubout -out public.pem
+
+-- 下载脚本和资源到设备时, 务必加上本目录下的两个 pem 文件, 否则本demo无法运行.
+]]
+
+local function rsa_task()
+    -- 此处延时2秒是为了方便观察日志,非必须
+    sys.wait(2000)
+
+    -- 检查是否带rsa库, 没有就提醒一下吧
+    if not rsa then
+        log.warn("main", "this demo need rsa lib!!!")
+        return
+    end
+
+    -- 读取公钥并马上加密数据
+    local res = rsa.encrypt((io.readFile("/luadb/public.pem")), "abc")
+    -- 打印结果
+    log.info("rsa", "encrypt", res and #res or 0, res and res:toHex() or "")
+
+    -- 下面是解密, 通常不会在设备端进行, 这里主要是演示用法, 会很慢
+    if res then
+        -- 读取私钥, 然后解码数据
+        local dst = rsa.decrypt((io.readFile("/luadb/privkey.pem")), res, "")
+        log.info("rsa", "decrypt", dst and #dst or 0, dst and dst:toHex() or "")
+    end
+
+    -- 演示签名和验签
+    local hash = crypto.sha1("1234567890"):fromHex()
+    -- 签名通常很慢, 通常是服务器做
+    local sig = rsa.sign((io.readFile("/luadb/privkey.pem")), rsa.MD_SHA1, hash, "")
+    log.info("rsa", "sign", sig and #sig or 0, sig and sig:toHex() or "")
+    if sig then
+        -- 验签是很快的
+        local ret = rsa.verify((io.readFile("/luadb/public.pem")), rsa.MD_SHA1, hash, sig)
+        log.info("rsa", "verify", ret)
+    end
+end
+
+--创建并且启动一个task
+--运行这个task的处理函数rsa_task
+sys.taskInit(rsa_task)

+ 73 - 0
module/Air8000/demo/rsa/main.lua

@@ -0,0 +1,73 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 1.0
+@date    2025.11.4
+@author  王城钧
+@usage
+本demo演示的核心功能为:
+1、rsa_app:普通的rsa加密解密、签名验签功能演示
+2、privkey.pem 和 public.pem:公钥私钥文件
+更多说明参考本目录下的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 = "RSA"
+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)
+
+
+-- 加载http应用功能模块
+require "rsa_app"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 27 - 0
module/Air8000/demo/rsa/privkey.pem

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAp0WakShvpE1/28hyZzz0TFiBpN/d6EU2Wkl5lrazTxK/LDdZ
+kAQuz6eA+kdPUqSueIpN0X8DT4Or5/0yD/ABvgW2t7A6g2/zQpzv9IoAl2f1Vhnx
+dd7iSnhWTeF80lywp4VlHHEVVp7ZCqod59qa9nyp45ycWPwV0VCdX6NwS7chPodC
+/Nf4UVbC1/JJ26WPVKqgOuSz0hIX58pnEaU/ntSDuJiLcKdCNUyIHJEzNYGaQVwx
+mqBoyolktkTOL04AW/OXlOTP8eZrEZpckrODDaWlLUOt5SARS6qUYJSvuyYry8a0
+rGOLbCnoIYVBvu8NX8I5hfeMGnYkEdpx6x/xYwIDAQABAoIBADfwehGDYVqkJFc/
+AKtv4g9KJgkaaN7NjrDBE62IagzOqypBVG1qSLFfRi3s/SUZN9POBNpDzLqhwTKz
+JTPZQuvmg0WI5Pihzst/KmwwXqRDuvNRd8PAhxL6jXo8J38+SkGrxbWuR8GRG+qK
+G7g3Dk3SQQqCjHLh0vYOLKMYSGy5RgfIQyve6yBK1LHeanzgGgcSbawsvCiiDOkb
+Y3I8CHasnK/Hxpmx/NzfNfY+r32AVXx4b+sN2YxB7A9OCxgdwYQHxYvKZ1JIiCh+
+Mv85wDHT25SJ5NLdP/jHILgGrgpyDN5+9c4fk3rjkoTrTs01wJ4rQfItkQ1Zlr0/
+9tub6mECgYEA2e7gerNRv53L0HkY9GS0B7Esi2FiUc/oh4xKRKPm+/ydXcroVbAK
+Mz3mCVlRNvkA+68P670s3AKVpeB671R70dCY+MQXTeNaxc0NlhGOKYoerPd9D/GN
+CF460AT2SQEWYTdIbin2dxeROEuC79zn1yczxnSwjAvqDwpvpHceP3ECgYEAxH1a
+hXk84U5PGKyUuAVMNMaQQll8rcxoFThxrei7iNT6SfVAeHQufCgeW9g1cOL5Vjmk
+9Te1caJt2VXaAhq883qZ1ABypQgcfUwLaK6q4BzAIPtYRIpkJstFKaYWqYaVwCGh
+QTqcP+ebEJ1BgKgxH4nXjlMUMwf8qzTGmQlO/BMCgYEAzZblG7uIlgyFVnC3Eu7h
+SxRgIkjHWMia4yx8b45zfCpORkoBrbw5kyeEmDMzQ3nZ7JS0nz5CUHb7t5UyRA7e
+FAwGEz/hgC/H1Svg8j4zb4qF78Q1rdHAqzFBqDXWJP6qnyFo6cwaXzTTYVkS97bc
+24J2/HPejO88ad39fhiFZ3ECgYEAvDJgaGU2BYrOwZBTJWqVkhr5g0NY4tJcgq68
+W1kFfkqXrAzGglitSWfXpBqTHRuYu5icwe5o0H1F/5t2IvvfLMmp2t/O7vi06OHU
+L6DUs7F16GE1KvjuciXRidG19QueFRdg7ywnCiJYaHJmkccGvfF1z7ENMM+el5EG
+AwBicZcCgYBdcmz3nmTyGb+BY6yHDNrJh97eSJaP5KOpfs1ZGM4Jx/Pa391q1X9Y
+WigaNEpW5eJKzTnyX0GlbEPPhTkBJS4ZwuKRZBvwzmLdoICA+i7gpjwfqDcrdJND
+IhEjjWogNyIvepre3eL1HMaJr6TS4j7wZTl7Ha4httCNBkQb0qJeWw==
+-----END RSA PRIVATE KEY-----

+ 9 - 0
module/Air8000/demo/rsa/public.pem

@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp0WakShvpE1/28hyZzz0
+TFiBpN/d6EU2Wkl5lrazTxK/LDdZkAQuz6eA+kdPUqSueIpN0X8DT4Or5/0yD/AB
+vgW2t7A6g2/zQpzv9IoAl2f1Vhnxdd7iSnhWTeF80lywp4VlHHEVVp7ZCqod59qa
+9nyp45ycWPwV0VCdX6NwS7chPodC/Nf4UVbC1/JJ26WPVKqgOuSz0hIX58pnEaU/
+ntSDuJiLcKdCNUyIHJEzNYGaQVwxmqBoyolktkTOL04AW/OXlOTP8eZrEZpckrOD
+DaWlLUOt5SARS6qUYJSvuyYry8a0rGOLbCnoIYVBvu8NX8I5hfeMGnYkEdpx6x/x
+YwIDAQAB
+-----END PUBLIC KEY-----

+ 54 - 0
module/Air8000/demo/rsa/readme.md

@@ -0,0 +1,54 @@
+功能模块介绍
+---------
+
+1、main.lua:主程序入口;
+
+2、rsa_app:rsa加密解密、签名验签功能演示;
+
+3、privkey.pem:PEM格式的RSA私钥文件
+
+4、public.pem:PEM格式的RSA公钥文件
+
+## 演示功能概述
+
+1、rsa加密解密、签名验签功能
+
+## 演示硬件环境
+
+1、Air8000开发板一块;
+
+![netdrv_multi](https://docs.openLuat.com/cdn/image/8000开发板.jpg)
+
+2、TYPE-C USB数据线一根
+
+* Air8000开发板/核心板通过 TYPE-C USB 口供电;
+* TYPE-C USB 数据线直接插到核心板的 TYPE-C USB 座子,另外一端连接电脑 USB 口;
+
+演示软件环境
+---------
+
+1、Luatools下载调试工具
+
+2、[Air8000 V2016版本]([固件和应用脚本Demo - luatos@air8000 - 合宙模组资料中心](https://docs.openluat.com/air8000/luatos/firmware/))(理论上最新版本固件也可以,如果使用最新版本的固件不可以,可以烧录V2016-1固件对比验证)
+
+## 演示核心步骤
+
+1、搭建好硬件环境
+
+2、Luatools烧录内核固件和demo脚本代码
+
+3、烧录成功后,自动开机运行
+
+4、可以看到代码运行结果如下:
+
+日志中如果出现以下类似以下打印则说明rsa加密解密、签名和验签功能正常
+
+```lua
+[2025-11-04 09:26:34.590][000000002.759] I/user.rsa encrypt 256 
+[2025-11-04 09:26:34.620][000000002.759] 7C4D741E29C7E3612C53B06602FBA562A7F3F1FF197519AECEE8B993E7B1C3EC709C41B3BD54A669836A13F9456322E6FB686398C961C8F283AEB8EFC775F2965CFB0FFC5E40CDADE301CFC9E0A94389C33090274CFC524CF54281565A89DFD6B6558C21F5AC1338BACFDA65057FF936AE1E60FC60A0809A9C1D45F641483F340B297D64BDF2BFBE46E1A34BE578C255F3AE04F1C1D19A28437B77AE98136D460C336CF6221BF33649731E85465721D255764936C9944DA199378CBC9CAAE07CD462EF3AEC0AA5E2F9C7FF68F9B2CAD5FE44C1A18F724CD637CCFE7B2E12BE93B78DC3B39C595C36B798050094B497D8F23AB32D33165861BD3EBCFA04CA0C26
+[2025-11-04 09:26:34.664][000000003.850] I/user.rsa decrypt 3 616263
+[2025-11-04 09:26:34.702][000000004.950] I/user.rsa sign 256 
+[2025-11-04 09:26:34.724][000000004.950] 2A526049B5D0FFFDF370EE1EB37E87A1C054B387386C635B4DD46E3970F90D08732D4CC1B338EA9153B6C52AE22602C55272C828D4F627E1BF8B994BC02C79DB1F0462F3A5A654D9DAF2794F4D8EC1A691FE6D0C455CA0DCE9B9ACDFC44C79D9CFFF46740248131EE58ACE00BE7DA8537F1E6550F17C204ADDD79C735C57D9FD4ACC7006BA22E248B9FEFE002E8FCCCF85B5A8DA0D133669D6463D24F8CA24C2E314CCCE39DF43A05EB33840BDC1298F0361D13FE2EEF3C87A76D826968873B8FEDD748DF54D70CB3D2A5072D137954BFA4FA990D2C01D8061FF0F2E27DF813DA8751A06F38C83827E574EDCC52F271A98EC2E6CD9A8A8AFC9DA0475EB7547D0
+[2025-11-04 09:26:34.750][000000004.968] I/user.rsa verify true
+
+```

+ 59 - 0
module/Air8000/demo/rsa/rsa_app.lua

@@ -0,0 +1,59 @@
+--[[
+@module  rsa_app
+@summary rsa应用功能模块
+@version 1.0
+@date    2025.11.4
+@author  王城钧
+@usage
+本文件为rsa应用功能模块,核心业务逻辑为:基于不同的应用场景,演示rsa核心库的使用方式;
+本文件没有对外接口,直接在main.lua中require "rsa_app"就可以加载运行;   
+]]
+
+--[[
+提醒: 本demo需要用到公钥和私钥, demo目录中的公钥私钥文件是演示用的, 实际使用请自行生成
+
+生成公钥私钥, 可使用openssl命令, 或者找个网页生成. 2048 是RSA位数, 最高支持4096,但不推荐,因为很慢.
+
+openssl genrsa -out privkey.pem 2048
+openssl rsa -in privkey.pem -pubout -out public.pem
+
+-- 下载脚本和资源到设备时, 务必加上本目录下的两个 pem 文件, 否则本demo无法运行.
+]]
+
+local function rsa_task()
+    -- 此处延时2秒是为了方便观察日志,非必须
+    sys.wait(2000)
+
+    -- 检查是否带rsa库, 没有就提醒一下吧
+    if not rsa then
+        log.warn("main", "this demo need rsa lib!!!")
+        return
+    end
+
+    -- 读取公钥并马上加密数据
+    local res = rsa.encrypt((io.readFile("/luadb/public.pem")), "abc")
+    -- 打印结果
+    log.info("rsa", "encrypt", res and #res or 0, res and res:toHex() or "")
+
+    -- 下面是解密, 通常不会在设备端进行, 这里主要是演示用法, 会很慢
+    if res then
+        -- 读取私钥, 然后解码数据
+        local dst = rsa.decrypt((io.readFile("/luadb/privkey.pem")), res, "")
+        log.info("rsa", "decrypt", dst and #dst or 0, dst and dst:toHex() or "")
+    end
+
+    -- 演示签名和验签
+    local hash = crypto.sha1("1234567890"):fromHex()
+    -- 签名通常很慢, 通常是服务器做
+    local sig = rsa.sign((io.readFile("/luadb/privkey.pem")), rsa.MD_SHA1, hash, "")
+    log.info("rsa", "sign", sig and #sig or 0, sig and sig:toHex() or "")
+    if sig then
+        -- 验签是很快的
+        local ret = rsa.verify((io.readFile("/luadb/public.pem")), rsa.MD_SHA1, hash, sig)
+        log.info("rsa", "verify", ret)
+    end
+end
+
+--创建并且启动一个task
+--运行这个task的处理函数rsa_task
+sys.taskInit(rsa_task)

+ 73 - 0
module/Air8101/demo/rsa/main.lua

@@ -0,0 +1,73 @@
+--[[
+@module  main
+@summary LuatOS用户应用脚本文件入口,总体调度应用逻辑 
+@version 1.0
+@date    2025.11.4
+@author  王城钧
+@usage
+本demo演示的核心功能为:
+1、rsa_app:普通的rsa加密解密、签名验签功能演示
+2、privkey.pem 和 public.pem:公钥私钥文件
+更多说明参考本目录下的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 = "RSA"
+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)
+
+
+-- 加载http应用功能模块
+require "rsa_app"
+
+-- 用户代码已结束---------------------------------------------
+-- 结尾总是这一句
+sys.run()
+-- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 27 - 0
module/Air8101/demo/rsa/privkey.pem

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAp0WakShvpE1/28hyZzz0TFiBpN/d6EU2Wkl5lrazTxK/LDdZ
+kAQuz6eA+kdPUqSueIpN0X8DT4Or5/0yD/ABvgW2t7A6g2/zQpzv9IoAl2f1Vhnx
+dd7iSnhWTeF80lywp4VlHHEVVp7ZCqod59qa9nyp45ycWPwV0VCdX6NwS7chPodC
+/Nf4UVbC1/JJ26WPVKqgOuSz0hIX58pnEaU/ntSDuJiLcKdCNUyIHJEzNYGaQVwx
+mqBoyolktkTOL04AW/OXlOTP8eZrEZpckrODDaWlLUOt5SARS6qUYJSvuyYry8a0
+rGOLbCnoIYVBvu8NX8I5hfeMGnYkEdpx6x/xYwIDAQABAoIBADfwehGDYVqkJFc/
+AKtv4g9KJgkaaN7NjrDBE62IagzOqypBVG1qSLFfRi3s/SUZN9POBNpDzLqhwTKz
+JTPZQuvmg0WI5Pihzst/KmwwXqRDuvNRd8PAhxL6jXo8J38+SkGrxbWuR8GRG+qK
+G7g3Dk3SQQqCjHLh0vYOLKMYSGy5RgfIQyve6yBK1LHeanzgGgcSbawsvCiiDOkb
+Y3I8CHasnK/Hxpmx/NzfNfY+r32AVXx4b+sN2YxB7A9OCxgdwYQHxYvKZ1JIiCh+
+Mv85wDHT25SJ5NLdP/jHILgGrgpyDN5+9c4fk3rjkoTrTs01wJ4rQfItkQ1Zlr0/
+9tub6mECgYEA2e7gerNRv53L0HkY9GS0B7Esi2FiUc/oh4xKRKPm+/ydXcroVbAK
+Mz3mCVlRNvkA+68P670s3AKVpeB671R70dCY+MQXTeNaxc0NlhGOKYoerPd9D/GN
+CF460AT2SQEWYTdIbin2dxeROEuC79zn1yczxnSwjAvqDwpvpHceP3ECgYEAxH1a
+hXk84U5PGKyUuAVMNMaQQll8rcxoFThxrei7iNT6SfVAeHQufCgeW9g1cOL5Vjmk
+9Te1caJt2VXaAhq883qZ1ABypQgcfUwLaK6q4BzAIPtYRIpkJstFKaYWqYaVwCGh
+QTqcP+ebEJ1BgKgxH4nXjlMUMwf8qzTGmQlO/BMCgYEAzZblG7uIlgyFVnC3Eu7h
+SxRgIkjHWMia4yx8b45zfCpORkoBrbw5kyeEmDMzQ3nZ7JS0nz5CUHb7t5UyRA7e
+FAwGEz/hgC/H1Svg8j4zb4qF78Q1rdHAqzFBqDXWJP6qnyFo6cwaXzTTYVkS97bc
+24J2/HPejO88ad39fhiFZ3ECgYEAvDJgaGU2BYrOwZBTJWqVkhr5g0NY4tJcgq68
+W1kFfkqXrAzGglitSWfXpBqTHRuYu5icwe5o0H1F/5t2IvvfLMmp2t/O7vi06OHU
+L6DUs7F16GE1KvjuciXRidG19QueFRdg7ywnCiJYaHJmkccGvfF1z7ENMM+el5EG
+AwBicZcCgYBdcmz3nmTyGb+BY6yHDNrJh97eSJaP5KOpfs1ZGM4Jx/Pa391q1X9Y
+WigaNEpW5eJKzTnyX0GlbEPPhTkBJS4ZwuKRZBvwzmLdoICA+i7gpjwfqDcrdJND
+IhEjjWogNyIvepre3eL1HMaJr6TS4j7wZTl7Ha4httCNBkQb0qJeWw==
+-----END RSA PRIVATE KEY-----

+ 9 - 0
module/Air8101/demo/rsa/public.pem

@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp0WakShvpE1/28hyZzz0
+TFiBpN/d6EU2Wkl5lrazTxK/LDdZkAQuz6eA+kdPUqSueIpN0X8DT4Or5/0yD/AB
+vgW2t7A6g2/zQpzv9IoAl2f1Vhnxdd7iSnhWTeF80lywp4VlHHEVVp7ZCqod59qa
+9nyp45ycWPwV0VCdX6NwS7chPodC/Nf4UVbC1/JJ26WPVKqgOuSz0hIX58pnEaU/
+ntSDuJiLcKdCNUyIHJEzNYGaQVwxmqBoyolktkTOL04AW/OXlOTP8eZrEZpckrOD
+DaWlLUOt5SARS6qUYJSvuyYry8a0rGOLbCnoIYVBvu8NX8I5hfeMGnYkEdpx6x/x
+YwIDAQAB
+-----END PUBLIC KEY-----

+ 54 - 0
module/Air8101/demo/rsa/readme.md

@@ -0,0 +1,54 @@
+功能模块介绍
+---------
+
+1、main.lua:主程序入口;
+
+2、rsa_app:rsa加密解密、签名验签功能演示;
+
+3、privkey.pem:PEM格式的RSA私钥文件
+
+4、public.pem:PEM格式的RSA公钥文件
+
+## 演示功能概述
+
+1、rsa加密解密、签名验签功能
+
+## 演示硬件环境
+
+1、Air8101核心板一块;
+
+![netdrv_multi](https://docs.openluat.com/accessory/AirSPINORFLASH_1000/image/8101.jpg)
+
+2、TYPE-C USB数据线一根
+
+* Air8101核心板通过 TYPE-C USB 口供电;
+* TYPE-C USB 数据线直接插到核心板的 TYPE-C USB 座子,另外一端连接电脑 USB 口;
+
+演示软件环境
+---------
+
+1、Luatools下载调试工具
+
+2、[Air8000 V2016版本]([固件和应用脚本Demo - luatos@air8000 - 合宙模组资料中心](https://docs.openluat.com/air8000/luatos/firmware/))(理论上最新版本固件也可以,如果使用最新版本的固件不可以,可以烧录V2016-1固件对比验证)
+
+## 演示核心步骤
+
+1、搭建好硬件环境
+
+2、Luatools烧录内核固件和demo脚本代码
+
+3、烧录成功后,自动开机运行
+
+4、可以看到代码运行结果如下:
+
+日志中如果出现以下类似以下打印则说明rsa加密解密、签名和验签功能正常
+
+```lua
+[2025-11-04 09:26:34.590][000000002.759] I/user.rsa encrypt 256 
+[2025-11-04 09:26:34.620][000000002.759] 7C4D741E29C7E3612C53B06602FBA562A7F3F1FF197519AECEE8B993E7B1C3EC709C41B3BD54A669836A13F9456322E6FB686398C961C8F283AEB8EFC775F2965CFB0FFC5E40CDADE301CFC9E0A94389C33090274CFC524CF54281565A89DFD6B6558C21F5AC1338BACFDA65057FF936AE1E60FC60A0809A9C1D45F641483F340B297D64BDF2BFBE46E1A34BE578C255F3AE04F1C1D19A28437B77AE98136D460C336CF6221BF33649731E85465721D255764936C9944DA199378CBC9CAAE07CD462EF3AEC0AA5E2F9C7FF68F9B2CAD5FE44C1A18F724CD637CCFE7B2E12BE93B78DC3B39C595C36B798050094B497D8F23AB32D33165861BD3EBCFA04CA0C26
+[2025-11-04 09:26:34.664][000000003.850] I/user.rsa decrypt 3 616263
+[2025-11-04 09:26:34.702][000000004.950] I/user.rsa sign 256 
+[2025-11-04 09:26:34.724][000000004.950] 2A526049B5D0FFFDF370EE1EB37E87A1C054B387386C635B4DD46E3970F90D08732D4CC1B338EA9153B6C52AE22602C55272C828D4F627E1BF8B994BC02C79DB1F0462F3A5A654D9DAF2794F4D8EC1A691FE6D0C455CA0DCE9B9ACDFC44C79D9CFFF46740248131EE58ACE00BE7DA8537F1E6550F17C204ADDD79C735C57D9FD4ACC7006BA22E248B9FEFE002E8FCCCF85B5A8DA0D133669D6463D24F8CA24C2E314CCCE39DF43A05EB33840BDC1298F0361D13FE2EEF3C87A76D826968873B8FEDD748DF54D70CB3D2A5072D137954BFA4FA990D2C01D8061FF0F2E27DF813DA8751A06F38C83827E574EDCC52F271A98EC2E6CD9A8A8AFC9DA0475EB7547D0
+[2025-11-04 09:26:34.750][000000004.968] I/user.rsa verify true
+
+```

+ 59 - 0
module/Air8101/demo/rsa/rsa_app.lua

@@ -0,0 +1,59 @@
+--[[
+@module  rsa_app
+@summary rsa应用功能模块
+@version 1.0
+@date    2025.11.4
+@author  王城钧
+@usage
+本文件为rsa应用功能模块,核心业务逻辑为:基于不同的应用场景,演示rsa核心库的使用方式;
+本文件没有对外接口,直接在main.lua中require "rsa_app"就可以加载运行;   
+]]
+
+--[[
+提醒: 本demo需要用到公钥和私钥, demo目录中的公钥私钥文件是演示用的, 实际使用请自行生成
+
+生成公钥私钥, 可使用openssl命令, 或者找个网页生成. 2048 是RSA位数, 最高支持4096,但不推荐,因为很慢.
+
+openssl genrsa -out privkey.pem 2048
+openssl rsa -in privkey.pem -pubout -out public.pem
+
+-- 下载脚本和资源到设备时, 务必加上本目录下的两个 pem 文件, 否则本demo无法运行.
+]]
+
+local function rsa_task()
+    -- 此处延时2秒是为了方便观察日志,非必须
+    sys.wait(2000)
+
+    -- 检查是否带rsa库, 没有就提醒一下吧
+    if not rsa then
+        log.warn("main", "this demo need rsa lib!!!")
+        return
+    end
+
+    -- 读取公钥并马上加密数据
+    local res = rsa.encrypt((io.readFile("/luadb/public.pem")), "abc")
+    -- 打印结果
+    log.info("rsa", "encrypt", res and #res or 0, res and res:toHex() or "")
+
+    -- 下面是解密, 通常不会在设备端进行, 这里主要是演示用法, 会很慢
+    if res then
+        -- 读取私钥, 然后解码数据
+        local dst = rsa.decrypt((io.readFile("/luadb/privkey.pem")), res, "")
+        log.info("rsa", "decrypt", dst and #dst or 0, dst and dst:toHex() or "")
+    end
+
+    -- 演示签名和验签
+    local hash = crypto.sha1("1234567890"):fromHex()
+    -- 签名通常很慢, 通常是服务器做
+    local sig = rsa.sign((io.readFile("/luadb/privkey.pem")), rsa.MD_SHA1, hash, "")
+    log.info("rsa", "sign", sig and #sig or 0, sig and sig:toHex() or "")
+    if sig then
+        -- 验签是很快的
+        local ret = rsa.verify((io.readFile("/luadb/public.pem")), rsa.MD_SHA1, hash, sig)
+        log.info("rsa", "verify", ret)
+    end
+end
+
+--创建并且启动一个task
+--运行这个task的处理函数rsa_task
+sys.taskInit(rsa_task)

+ 49 - 5
olddemo/ftp/main.lua

@@ -17,7 +17,7 @@ local server_port = 21 -- 服务器端口号
 local server_username = "ftp_user" -- 服务器登陆用户名
 local server_password = "3QujbiMG" -- 服务器登陆密码
 
-local is_ssl = flase -- 非ssl加密连接
+local is_ssl = false -- 非ssl加密连接
 -- local is_ssl =true --如果是不带证书的加密打开这句话
 
 -- local ssl_encrypt = {
@@ -31,7 +31,7 @@ local is_ssl = flase -- 非ssl加密连接
 
 -- 这里使用模块唯一ID来生成文件名,避免多个模块使用同一ftp服务器时文件名冲突
 local self_id = mcu.unique_id():toHex()
-local local_name = "/" .. self_id .. ".txt" -- 模块内部文件名及其路径
+local local_name = "/ram/" .. self_id .. ".txt" -- 模块内部文件名及其路径
 local remote_name = "/" .. self_id .. "_srv.txt" -- 服务器上的文件名及其路径
 
 sys.taskInit(function()
@@ -66,6 +66,7 @@ local function ftp_test()
     sys.waitUntil("net_ready") -- 死等到联网成功
     local result = false
     local adapter = nil -- 自动选择网络适配器
+    ftp.debug(true) -- 打开调试开关, 可以看到和服务器交互的明文命令和返回值
     while true do
         sys.wait(1000)
         log.info("ftp 启动")
@@ -94,28 +95,71 @@ local function ftp_test()
         -- 演示获取当前工作目录下的文件列表, 非必须的操作
         log.info("获取当前工作目录下的文件名列表", ftp.command("LIST").wait())
         
-        -- 生成一段随机数据, 确保每次是新数据, 保证测试的可用性
-        local test_data = crypto.trng(128)
+        -- 生成至少51KB的测试数据 (52KB = 53248字节)
+        local target_size = 52*1024
+        local test_data = ""
+        
+        -- 使用循环生成足够大的数据
+        log.info("开始生成测试数据,目标大小:", target_size, "字节")
+        while #test_data < target_size do
+            -- 每次生成1KB的随机数据,直到达到目标大小
+            local chunk_size = math.min(1024, target_size - #test_data)
+            local chunk = crypto.trng(chunk_size)
+            test_data = test_data .. chunk
+            -- log.info("已生成数据:", #test_data, "/", target_size, "字节")
+        end
+        
+        log.info("测试数据生成完成,总大小:", #test_data, "字节")
+        
         -- 把数据写到本地目录
         log.info("在本地创建一个文件", "文件名及其目录为" .. local_name)
         io.writeFile(local_name, test_data)
+        log.info("本地文件大小", #test_data, "字节")
+        log.info("本地文件大小(KB)", #test_data / 1024, "KB")
         
         -- 把文件上传到服务器去
         log.info("上传本地的" .. local_name, "到服务器上并且更名为" .. remote_name)
         result = ftp.push(local_name, remote_name).wait()
         log.info("上传结果是", result)
 
+        -- 打印一下服务器当前目录下的文件列表
+        log.info("获取当前工作目录下的文件名列表", ftp.command("LIST").wait())
+
         -- 从服务器上下载刚才上传的文件
         log.info("下载服务器上的" .. remote_name, "存放在" .. remote_name)
         result = ftp.pull(remote_name, remote_name).wait()
         log.info("下载结果是", result)
 
+        -- 检查下载的文件大小
+        local downloaded_data = io.readFile(remote_name)
+        if downloaded_data then
+            log.info("下载文件实际大小", #downloaded_data, "字节")
+            log.info("下载文件实际大小(KB)", #downloaded_data / 1024, "KB")
+            log.info("期望文件大小", #test_data, "字节")
+            log.info("期望文件大小(KB)", #test_data / 1024, "KB")
+            
+            if #downloaded_data ~= #test_data then
+                log.warn("警告:下载文件大小与期望大小不一致!")
+                log.warn("期望大小:", #test_data, "字节 (", #test_data / 1024, "KB)")
+                log.warn("实际大小:", #downloaded_data, "字节 (", #downloaded_data / 1024, "KB)")
+                log.warn("大小差异:", #test_data - #downloaded_data, "字节")
+            end
+        else
+            log.error("无法读取下载的文件")
+        end
+
         -- 比较一下上传和下载的数据是否一致
         local downloaded_data = io.readFile(remote_name)
-        if downloaded_data == test_data then
+        if downloaded_data and downloaded_data == test_data then
             log.info("上传和下载的数据一致性验证通过")
         else
             log.error("上传和下载的数据一致性验证失败")
+            if downloaded_data then
+                log.info("原始数据长度:", #test_data, "字节 (", #test_data / 1024, "KB)")
+                log.info("下载数据长度:", #downloaded_data, "字节 (", #downloaded_data / 1024, "KB)")
+            else
+                log.error("无法读取下载的数据")
+            end
         end
 
         -- 删除服务器上的测试文件

+ 5 - 2
script/libs/exaudio.lua

@@ -29,6 +29,7 @@ exaudio.PCM_8000 = 2
 exaudio.PCM_16000 = 3 
 exaudio.PCM_24000 = 4
 exaudio.PCM_32000 = 5
+exaudio.PCM_48000 = 6
 
 
 -- 默认配置参数
@@ -55,7 +56,7 @@ local audio_play_param = {
 }
 
 local audio_record_param = {
-    format = 0,               -- 录制格式,支持exaudio.AMR_NB,exaudio.AMR_WB,exaudio.PCM_8000,exaudio.PCM_16000,exaudio.PCM_24000,exaudio.PCM_32000
+    format = 0,               -- 录制格式,支持exaudio.AMR_NB,exaudio.AMR_WB,exaudio.PCM_8000,exaudio.PCM_16000,exaudio.PCM_24000,exaudio.PCM_32000,exaudio.PCM_48000
     time = 5,                 -- 录制时间(秒)
     path = nil,               -- 文件路径或流式回调
     cbfnc = nil               -- 录音完毕回调
@@ -434,7 +435,7 @@ function exaudio.record_start(recodConfigs)
         return false
     end
     -- 检查录音格式
-    if recodConfigs.format == nil or type(recodConfigs.format) ~= "number" or recodConfigs.format > 5 then
+    if recodConfigs.format == nil or type(recodConfigs.format) ~= "number" or recodConfigs.format > 6 then
         log.error("请指定正确的录音格式")
         return false
     end
@@ -474,6 +475,8 @@ function exaudio.record_start(recodConfigs)
         recod_format = 24000
     elseif audio_record_param.format == exaudio.PCM_32000 then
         recod_format = 32000
+    elseif audio_record_param.format == exaudio.PCM_48000 then
+        recod_format = 48000
     end
 
     -- 处理回调函数

+ 7 - 0
script/libs/excloud.lua

@@ -193,6 +193,13 @@ local FIELD_MEANINGS = {
     MTN_LOG_UPLOAD_STATUS_FIELD  = 791, -- 运维日志上传状态
     MTN_LOG_FILE_NAME            = 792, -- 运维日志文件名称
 
+    -- 工牌设备参数字段 (793-797) - 新增
+    BADGE_TOTAL_DISK             = 793, -- 工牌总磁盘空间
+    BADGE_AVAILABLE_DISK         = 794, -- 工牌剩余磁盘空间
+    BADGE_TOTAL_MEM              = 795, -- 工牌总内存
+    BADGE_AVAILABLE_MEM          = 796, -- 工牌剩余内存
+    BADGE_RECORD_COUNT           = 797, -- 工牌录音数量
+
     -- 软件数据类 (1024-1279)
     LUA_CORE_ERROR               = 1024, -- Lua核心库错误上报
     LUA_EXT_ERROR                = 1025, -- Lua扩展卡错误上报