Просмотр исходного кода

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

梁健 8 месяцев назад
Родитель
Сommit
4414786035
66 измененных файлов с 2741 добавлено и 887 удалено
  1. 244 0
      components/airtalk/binding/luat_lib_airtalk.c
  2. 375 0
      components/airtalk/core/airtalk_demo_mqtt.c
  3. 377 0
      components/airtalk/core/airtalk_mqtt.c
  4. 5 0
      components/airtalk/core/airtalk_network.c
  5. 34 0
      components/airtalk/include/airtalk_api.h
  6. 20 0
      components/airtalk/include/airtalk_def.h
  7. 28 0
      components/airtalk/include/luat_airtalk.h
  8. 364 0
      components/airtalk/platform/ec7xx/airtalk_speech_ec7xx.c
  9. 49 0
      components/airtalk/platform/ec7xx/vem.c
  10. 189 0
      components/airtalk/platform/ec7xx/vem_cfg.c
  11. 56 21
      components/network/adapter_lwip2/net_lwip2.c
  12. 4 0
      components/network/adapter_lwip2/net_lwip2.h
  13. 5 0
      components/network/netdrv/src/luat_netdrv.c
  14. 106 0
      components/network/rtp/luat_rtp.c
  15. 73 0
      components/network/rtp/luat_rtp.h
  16. 4 8
      components/network/ulwip/binding/luat_lib_ulwip.c
  17. 15 8
      components/tp/luat_lib_tp.c
  18. 0 0
      module/Air724UG/.keep
  19. 0 0
      module/Air780EHM_Air780EHV_Air780EGH/demo/lowpower/lowpower_dissipation.lua
  20. 2 2
      module/Air780EHM_Air780EHV_Air780EGH/demo/lowpower/main.lua
  21. 0 0
      module/Air780EPM/demo/lowpower/lowpower_dissipation.lua
  22. 3 3
      module/Air780EPM/demo/lowpower/main.lua
  23. 5 27
      module/Air8000/README.md
  24. 1 1
      module/Air8000/demo/luatos_framework/hello_luatos/hello_luatos.lua
  25. 1 1
      module/Air8000/demo/luatos_framework/hello_luatos/main.lua
  26. 1 1
      module/Air8000/demo/luatos_framework/hello_luatos/readme.md
  27. 1 1
      module/Air8101/demo/libnetif/libnetif.lua
  28. 1 1
      module/Air8101/demo/lowpower/low_power.lua
  29. 2 2
      module/Air8101/demo/lowpower/main.lua
  30. 1 1
      module/Air8101/demo/lowpower/normal_power.lua
  31. 3 3
      module/Air8101/demo/lowpower/psm+_power.lua
  32. 1 1
      module/Air8101/demo/lowpower/tcp_client_main.lua
  33. 2 2
      module/Air8101/demo/lowpower/wifi_app.lua
  34. 1 1
      module/Air8101/demo/socket/client/long_connection/sntp_app.lua
  35. 1 1
      module/Air8101/demo/socket/client/long_connection/tcp_client_main.lua
  36. 1 1
      module/Air8101/demo/socket/client/long_connection/tcp_ssl_ca_main.lua
  37. 1 1
      module/Air8101/demo/socket/client/long_connection/tcp_ssl_main.lua
  38. 1 1
      module/Air8101/demo/socket/client/long_connection/udp_client_main.lua
  39. 2 2
      module/Air8101/demo/socket/client/long_connection/wifi_app.lua
  40. 2 2
      module/Air8101/project/5inch_800x480_dev_board/app/http_app.lua
  41. 3 3
      module/Air8101/project/5inch_800x480_dev_board/app/wifi_app.lua
  42. 5 4
      module/Air8101/project/5inch_800x480_dev_board/ui/welcome_win.lua
  43. 0 170
      module/Air8101/project/5inch_box_dev_board/AirCAMERA_1030.lua
  44. 0 37
      module/Air8101/project/5inch_box_dev_board/AirFONTS_1000.lua
  45. 0 95
      module/Air8101/project/5inch_box_dev_board/AirLCD_1020.lua
  46. 0 7
      module/Air8101/project/5inch_box_dev_board/AirMICROSD_1000.lua
  47. 0 78
      module/Air8101/project/5inch_box_dev_board/http_app.lua
  48. 0 95
      module/Air8101/project/5inch_box_dev_board/lcd_vector_font_app.lua
  49. 0 75
      module/Air8101/project/5inch_box_dev_board/main.lua
  50. 0 58
      module/Air8101/project/5inch_box_dev_board/readme.md
  51. 0 83
      module/Air8101/project/5inch_box_dev_board/tf_app.lua
  52. 0 34
      module/Air8101/project/5inch_box_dev_board/wifi_app.lua
  53. 7 6
      module/Air8101/project/core_accessory_board/AirCAMERA_1020/http_app.lua
  54. 2 2
      module/Air8101/project/core_accessory_board/AirCAMERA_1020/wifi_app.lua
  55. 6 5
      module/Air8101/project/core_accessory_board/AirCAMERA_1030/http_app.lua
  56. 2 2
      module/Air8101/project/core_accessory_board/AirCAMERA_1030/readme.md
  57. 2 2
      module/Air8101/project/core_accessory_board/AirCAMERA_1030/wifi_app.lua
  58. 7 8
      module/Air8101/project/core_accessory_board/AirETH_1000/http_app.lua
  59. 0 2
      module/Air8101/project/core_accessory_board/AirETH_1000/net_app.lua
  60. 12 8
      module/Air8101/project/core_accessory_board/AirETH_1000/readme.md
  61. 7 8
      module/Air8101/project/core_accessory_board/AirPHY_1000/http_app.lua
  62. 2 2
      module/Air8101/project/core_accessory_board/AirUSBHUB_1000/http_app.lua
  63. 2 2
      module/Air8101/project/core_accessory_board/AirUSBHUB_1000/wifi_app.lua
  64. 1 1
      module/Air8101/project/readme.md
  65. 8 8
      script/libs/fota_wifi.lua
  66. 694 0
      script/libs/libnetif.lua

+ 244 - 0
components/airtalk/binding/luat_lib_airtalk.c

@@ -0,0 +1,244 @@
+/*
+@module  airtalk
+@summary 设备之间,设备与PC、手机,对讲处理
+@catalog 综合应用API
+@version 1.0
+@date    2025.07.1
+@demo airtalk
+@tag LUAT_USE_AIRTALK
+@usage
+-- 本库仅部分BSP支持
+-- 主要是 Air8000 和 Air780EXX 系列
+-- 详细用法请参考demo
+*/
+
+
+#include "luat_base.h"
+#include "libemqtt.h"
+#include "luat_airtalk.h"
+#include "luat_malloc.h"
+#include "luat_mqtt.h"
+#include "luat_audio.h"
+#include "airtalk_api.h"
+
+#define LUAT_LOG_TAG "airtalk"
+#include "luat_log.h"
+
+#include "rotable.h"
+#define LUAT_MQTT_CTRL_TYPE "MQTTCTRL*"
+
+
+static int l_airtalk_cb;
+static int l_airtalk_protocol;
+static int l_airtalk_handler(lua_State *L, void* ptr) {
+    (void)ptr;
+    rtos_msg_t* msg = (rtos_msg_t*)lua_topointer(L, -1);
+    lua_pop(L, 1);
+
+    if (l_airtalk_cb) {
+        lua_geti(L, LUA_REGISTRYINDEX, l_airtalk_cb);
+        if (lua_isfunction(L, -1)) {
+            lua_pushinteger(L, msg->arg1);
+            lua_pushinteger(L, msg->arg2);
+            lua_call(L, 2, 0);
+        }
+    }
+    // 给rtos.recv方法返回个空数据
+    lua_pushinteger(L, 0);
+    return 1;
+}
+
+
+/*
+配置airtalk参数
+@api airtalk.config(protocol,netc,cache_time,encode_cnt,decode_cnt,audio_pm_mode_when_stop)
+@int 协议类型,见airtalk.PROTOCOL_XXX
+@userdata network_ctrl或者mqtt客户端,如果协议是mqtt类型,传入mqtt.create返回值,如果是其他类型,传入socket.create的返回值
+@int 缓冲时间,单位ms,默认500ms,值越小,delay越小,抗网络波动能力越差
+@int 单次编码帧数,默认值5,不能低于2,不能高于5
+@int 单次解码帧数,如果缓冲没有足够的帧数,自动补0,默认值5,不能低于2,不能高于10,不能低于encode_cnt, decode_cnt * 4 必须是 encode_cnt的整数倍
+@int 对讲停止后,audio的pm状态,默认是audio.SHUTDOWN
+@return nil
+@usage
+mqttc = mqtt.create(nil,"120.55.137.106", 1884)
+airtalk.config(airtalk.PROTOCOL_DEMO_MQTT_8K, mqttc)
+*/
+static int l_airtalk_config(lua_State *L)
+{
+	l_airtalk_protocol = luaL_optinteger(L, 1, LUAT_AIRTALK_PROTOCOL_DEMO_MQTT_8K);
+	int cache_time = luaL_optinteger(L, 3, 500);
+	int encode_cnt = luaL_optinteger(L, 4, 5);
+	int decode_cnt = luaL_optinteger(L, 5, 5);
+	int audio_pm_mode_when_stop = luaL_optinteger(L, 6, LUAT_AUDIO_PM_SHUTDOWN);
+	luat_mqtt_ctrl_t * mqtt_ctrl;
+	switch (l_airtalk_protocol)
+	{
+	case LUAT_AIRTALK_PROTOCOL_DEMO_MQTT_8K:
+	case LUAT_AIRTALK_PROTOCOL_DEMO_MQTT_16K:
+		if (luaL_testudata(L, 2, LUAT_MQTT_CTRL_TYPE)){
+			mqtt_ctrl = ((luat_mqtt_ctrl_t *)luaL_checkudata(L, 2, LUAT_MQTT_CTRL_TYPE));
+		}else{
+			mqtt_ctrl = ((luat_mqtt_ctrl_t *)lua_touserdata(L, 2));
+		}
+		if (!mqtt_ctrl)
+		{
+			LLOGE("protocol %d no mqttc", l_airtalk_protocol);
+			return 0;
+		}
+		luat_airtalk_net_param_config(cache_time);
+		luat_airtalk_net_set_mqtt_ctrl(mqtt_ctrl);
+		luat_airtalk_speech_audio_param_config(0, audio_pm_mode_when_stop);
+		luat_airtalk_speech_set_one_block_frame_cnt(decode_cnt, encode_cnt);
+		break;
+	case LUAT_AIRTALK_PROTOCOL_AIRM2M:
+		LLOGE("protocol %d no support!", l_airtalk_protocol);
+		break;
+	default:
+		LLOGE("protocol %d no support!", l_airtalk_protocol);
+		break;
+
+	}
+
+    return 0;
+}
+
+/*
+注册airtalk事件回调
+@api airtalk.on(func)
+@function 回调方法
+@return nil 无返回值
+@usage
+airtalk.on(function(event, param)
+    log.info("airtalk event", event, param)
+end)
+*/
+static int l_airtalk_on(lua_State *L) {
+	if (l_airtalk_cb)
+	{
+		luaL_unref(L, LUA_REGISTRYINDEX, l_airtalk_cb);
+		l_airtalk_cb = 0;
+	}
+    if (lua_isfunction(L, 1)) {
+        lua_pushvalue(L, 1);
+        l_airtalk_cb = luaL_ref(L, LUA_REGISTRYINDEX);
+    }
+    return 0;
+}
+
+/*
+airtalk启动
+@api airtalk.start(uid,ctrl_url,ctrl_port)
+@string 用于确认身份的唯一id,不超过15字节,如果是演示协议,随意填写一个不重复的即可
+@string 如果协议是非MQTT类型是服务器url,如果是mqtt演示协议,则是通话topic,不填则使用默认topic
+@int  服务器端口,如果是mqtt协议,不需要填写,mqtt.create已经传入
+@return nil
+@usage
+mqttc = mqtt.create(nil,"120.55.137.106", 1884)
+airtalk.config(airtalk.PROTOCOL_DEMO_MQTT_8K, mqttc)
+airtalk.on(function(event, param)
+    log.info("airtalk event", event, param)
+end)
+--airtalk.start("123456789012345", "xxxxxx")	--用户用mqtt测试协议时,应该自己定义topic,防止被别人听
+airtalk.start("123456789012345")
+*/
+static int l_airtalk_start(lua_State *L)
+{
+    size_t len;
+    const char *id = lua_tolstring(L, 1, &len);//取出字符串数据;
+    luat_airtalk_net_set_device_id(id, len);
+    luat_airtalk_speech_init();
+    switch (l_airtalk_protocol)
+    {
+    case LUAT_AIRTALK_PROTOCOL_DEMO_MQTT_8K:
+    case LUAT_AIRTALK_PROTOCOL_DEMO_MQTT_16K:
+    	if (lua_isstring(L, 2))
+    	{
+    		id = lua_tolstring(L, 2, &len);
+    		luat_airtalk_net_set_mqtt_topic(id, len + 1);
+    	}
+    	luat_airtalk_net_demo_mqtt_init(l_airtalk_protocol);
+    	break;
+    }
+    return 0;
+}
+
+/*
+airtalk上行控制
+@api airtalk.uplink(on_off)
+@boolean  录音上行控制,true开始,false停止
+@return nil
+@usage
+--开始录音
+airtalk.uplink(true)
+--停止录音
+airtalk.uplink(false)
+*/
+static int l_airtalk_uplink(lua_State *L)
+{
+	luat_airtalk_speech_record_switch(lua_toboolean(L, 1), l_airtalk_protocol);
+    return 0;
+}
+
+/*
+airtalk的详细调试信息开关
+@api airtalk.debug(on_off)
+@boolean 调试信息开关,true打开,false关闭
+@return nil
+*/
+static int l_airtalk_debug(lua_State *L)
+{
+	uint8_t on_off = lua_toboolean(L, 1);
+	luat_airtalk_net_debug_switch(on_off);
+	luat_airtalk_speech_debug_switch(on_off);
+    return 0;
+}
+
+#include "rotable2.h"
+static const rotable_Reg_t reg_airtalk[] =
+{
+    { "config",      ROREG_FUNC(l_airtalk_config)},
+    { "on",         ROREG_FUNC(l_airtalk_on)},
+    { "start",      ROREG_FUNC(l_airtalk_start)},
+    { "uplink",      ROREG_FUNC(l_airtalk_uplink)},
+	{ "debug",      ROREG_FUNC(l_airtalk_debug)},
+	//@const PROTOCOL_DEMO_MQTT_8K number 演示用MQTT协议,音频采样率8K
+    { "PROTOCOL_DEMO_MQTT_8K",        ROREG_INT(LUAT_AIRTALK_PROTOCOL_DEMO_MQTT_8K)},
+	//@const PROTOCOL_DEMO_MQTT_16K number 演示用MQTT协议,音频采样率16K
+    { "PROTOCOL_DEMO_MQTT_16K",        ROREG_INT(LUAT_AIRTALK_PROTOCOL_DEMO_MQTT_16K)},
+	//@const EVENT_OFF_LINE number airtalk离线
+    { "EVENT_OFF_LINE",       ROREG_INT(LUAT_AIRTALK_CB_ON_LINE_IDLE)},
+	//@const EVENT_ON_LINE_IDLE number airtalk在线处于空闲状态
+    { "EVENT_ON_LINE_IDLE",       ROREG_INT(LUAT_AIRTALK_CB_ON_LINE_IDLE)},
+	//@const EVENT_PLAY_START number airtalk下行播放开始
+    { "EVENT_PLAY_START",       ROREG_INT(LUAT_AIRTALK_CB_PLAY_START)},
+	//@const EVENT_PLAY_END number airtalk下行播放结束
+    { "EVENT_PLAY_END",       ROREG_INT(LUAT_AIRTALK_CB_PLAY_END)},
+	//@const EVENT_RECORD_START number airtalk录音上行开始
+    { "EVENT_RECORD_START",       ROREG_INT(LUAT_AIRTALK_CB_RECORD_START)},
+	//@const EVENT_RECORD_END number airtalk录音上行结束
+    { "EVENT_RECORD_END",       ROREG_INT(LUAT_AIRTALK_CB_RECORD_END)},
+	//@const EVENT_AUDIO_START number airtalk audio启动,只要上行和下行有一个开始就启动
+    { "EVENT_AUDIO_START",       ROREG_INT(LUAT_AIRTALK_CB_AUDIO_START)},
+	//@const EVENT_AUDIO_END number airtalk audio停止,上行和下行都结束才停止
+    { "EVENT_AUDIO_END",       ROREG_INT(LUAT_AIRTALK_CB_AUDIO_END)},
+	//@const EVENT_ERROR number airtalk发生异常,后续param为异常值
+    { "EVENT_ERROR",       ROREG_INT(LUAT_AIRTALK_CB_ERROR)},
+
+    { NULL,         ROREG_INT(0) }
+};
+
+LUAMOD_API int luaopen_airtalk(lua_State *L)
+{
+    luat_newlib2(L, reg_airtalk);
+    return 1;
+}
+
+LUAT_WEAK void luat_airtalk_callback(uint32_t event, void *param, uint32_t param_len)
+{
+    rtos_msg_t msg = {0};
+    msg.handler = l_airtalk_handler;
+    msg.ptr = param;
+    msg.arg1 = event;
+    msg.arg2 = param_len;
+    luat_msgbus_put(&msg, 0);
+}

+ 375 - 0
components/airtalk/core/airtalk_demo_mqtt.c

@@ -0,0 +1,375 @@
+#include "csdk.h"
+#include "airtalk_def.h"
+#include "airtalk_api.h"
+#include "luat_airtalk.h"
+
+#include "libemqtt.h"
+#include "luat_mqtt.h"
+
+typedef struct
+{
+	llist_head node;
+	uint64_t remote_tamp;
+	uint64_t local_tamp;
+	uint32_t total_len;
+	uint8_t amr_save_data[];
+}net_data_struct;
+
+typedef struct
+{
+	luat_mqtt_ctrl_t *mqtt_ctrl;
+	luat_rtos_task_handle mqtt_task_handle;
+	luat_rtos_timer_t download_check_timer;
+	Buffer_Struct uplink;
+	llist_head download_cache_head;				//下行数据接收缓存队列
+	uint32_t download_cache_time;
+	Buffer_Struct topic;
+	char self_id[15];
+	uint8_t data_sync_ok;
+	uint8_t uplink_ready;
+	uint8_t speech_on;
+	uint8_t is_16k;
+	uint8_t debug_on_off;
+}demo_mqtt_ctrl_t;
+
+static demo_mqtt_ctrl_t prv_demo_mqtt;
+
+
+//播放完成
+static void end_broadcast_play(void)
+{
+	net_data_struct *net_cache;
+	luat_airtalk_speech_stop_play();
+	luat_stop_rtos_timer(prv_demo_mqtt.download_check_timer);
+	while(!llist_empty(&prv_demo_mqtt.download_cache_head))
+	{
+		net_cache = (net_data_struct *)prv_demo_mqtt.download_cache_head.next;
+		llist_del(&net_cache->node);
+		luat_heap_free(net_cache);
+	}
+	prv_demo_mqtt.speech_on = 0;
+}
+
+static void download_check_timer(void *param)
+{
+	luat_rtos_event_send(prv_demo_mqtt.mqtt_task_handle, AIRTALK_EVENT_MQTT_FORCE_STOP, 0, 0, 0, 0);
+}
+
+
+static void airtalk_mqtt_cb(luat_mqtt_ctrl_t *mqtt_ctrl, uint16_t event)
+{
+	int ret;
+	if (event != MQTT_MSG_PUBLISH)
+	{
+		luat_rtos_event_send(prv_demo_mqtt.mqtt_task_handle, AIRTALK_EVENT_MQTT_MSG, event, 0, 0, 0);
+	}
+	else
+	{
+		const uint8_t* ptr;
+		uint32_t len;
+		uint8_t *topic = NULL;
+		uint8_t *payload = NULL;
+		len = mqtt_parse_pub_topic_ptr(prv_demo_mqtt.mqtt_ctrl->mqtt_packet_buffer, &ptr);
+		topic = luat_heap_calloc(len + 1, 1);
+		memcpy(topic, ptr, len);
+		len = mqtt_parse_pub_msg_ptr(prv_demo_mqtt.mqtt_ctrl->mqtt_packet_buffer, &ptr);
+		if (len)
+		{
+			payload = luat_heap_malloc(len);
+			memcpy(payload, ptr, len);
+		}
+		luat_rtos_event_send(prv_demo_mqtt.mqtt_task_handle, AIRTALK_EVENT_MQTT_DOWNLINK_DATA, (uint32_t)topic, (uint32_t)payload, len, 0);
+	}
+	return;
+}
+
+static void airtalk_demo_mqtt_task(void *param)
+{
+	uint64_t tamp;
+	uint32_t local_time_diff, remote_time_diff;
+	luat_event_t event;
+	net_data_struct *net_cache;
+	int ret = -1;
+	uint8_t *p;
+	char *packet_id;
+	int i;
+	uint16_t msgid = 0;
+	char remote_client[16] = {0};
+	INIT_LLIST_HEAD(&prv_demo_mqtt.download_cache_head);
+	prv_demo_mqtt.download_check_timer = luat_create_rtos_timer(download_check_timer, NULL, NULL);
+	if (!prv_demo_mqtt.download_cache_time)
+	{
+		prv_demo_mqtt.download_cache_time = 500;
+	}
+	if (!prv_demo_mqtt.topic.Data)
+	{
+		OS_BufferWrite(&prv_demo_mqtt.topic, "speech_demo/all", 16);
+	}
+	LUAT_DEBUG_PRINT("device id, %.*s topic %s", sizeof(prv_demo_mqtt.self_id), prv_demo_mqtt.self_id, prv_demo_mqtt.topic.Data);
+	while(1){
+		luat_rtos_event_recv(prv_demo_mqtt.mqtt_task_handle, 0, &event, NULL, LUAT_WAIT_FOREVER);
+		switch(event.id)
+		{
+		case AIRTALK_EVENT_MQTT_DOWNLINK_DATA:
+			if (memcmp(prv_demo_mqtt.topic.Data, (char *)event.param1, prv_demo_mqtt.topic.Pos - 1))
+			{
+				LUAT_DEBUG_PRINT("topic %s", (char *)event.param1);
+			}
+			else
+			{
+				packet_id = (char *)event.param2;
+				p = (uint8_t *)event.param2;
+				if (packet_id[15] > 1)
+				{
+					goto RX_DATA_DONE;
+				}
+				if (!memcmp(packet_id, prv_demo_mqtt.self_id, 15))
+				{
+#ifdef SELF_TEST
+#else
+					goto RX_DATA_DONE;
+#endif
+				}
+				if (packet_id[15])
+				{
+					if (prv_demo_mqtt.speech_on)
+					{
+						remote_client[0] = 0;
+						end_broadcast_play();
+						goto RX_DATA_DONE;
+					}
+					else
+					{
+						LUAT_DEBUG_PRINT("speech already stop!");
+						goto RX_DATA_DONE;
+					}
+				}
+				memcpy(&tamp, p + 16, 8);
+				if (!remote_client[0])
+				{
+					prv_demo_mqtt.speech_on = 1;
+					prv_demo_mqtt.data_sync_ok = 0;
+					net_cache = luat_heap_malloc(sizeof(net_data_struct) + event.param3);
+					net_cache->total_len = event.param3 - 24;
+					net_cache->remote_tamp = tamp;
+					net_cache->local_tamp = luat_mcu_tick64_ms();
+					memcpy(net_cache->amr_save_data, p + 24, net_cache->total_len);
+					llist_add_tail(&net_cache->node, &prv_demo_mqtt.download_cache_head);
+					memcpy(remote_client, packet_id, 15);
+					LUAT_DEBUG_PRINT("sync start remote %s %llu %llu", remote_client, net_cache->remote_tamp, net_cache->local_tamp);
+				}
+				else
+				{
+					if (memcmp(remote_client, packet_id, 15))
+					{
+						goto RX_DATA_DONE;
+					}
+				}
+				{
+					event.param3 -= 24;	//data_len
+					p += 24; //data
+					if (prv_demo_mqtt.data_sync_ok)
+					{
+						luat_airtalk_speech_save_downlink_data(p, event.param3);
+					}
+					else
+					{
+						net_cache = luat_heap_malloc(sizeof(net_data_struct) + event.param3);
+						net_cache->total_len = event.param3;
+						net_cache->remote_tamp = tamp;
+						net_cache->local_tamp = luat_mcu_tick64_ms();
+						memcpy(net_cache->amr_save_data, p, net_cache->total_len);
+						llist_add_tail(&net_cache->node, &prv_demo_mqtt.download_cache_head);
+
+						net_cache = (net_data_struct *)prv_demo_mqtt.download_cache_head.next;
+						remote_time_diff = (uint32_t)(tamp - net_cache->remote_tamp);
+						if (remote_time_diff >= (prv_demo_mqtt.download_cache_time - 20))
+						{
+							local_time_diff = (uint32_t)(luat_mcu_tick64_ms() - net_cache->local_tamp);
+							if (local_time_diff >= (prv_demo_mqtt.download_cache_time - 20))
+							{
+								LUAT_DEBUG_PRINT("sync ok");
+								prv_demo_mqtt.data_sync_ok = 1;
+								while(!llist_empty(&prv_demo_mqtt.download_cache_head))
+								{
+									net_cache = (net_data_struct *)prv_demo_mqtt.download_cache_head.next;
+									llist_del(&net_cache->node);
+									luat_airtalk_speech_save_downlink_data(net_cache->amr_save_data, net_cache->total_len);
+									luat_heap_free(net_cache);
+								}
+								luat_airtalk_speech_sync_ok();
+							}
+							else
+							{
+								LUAT_DEBUG_PRINT("sync failed %u, %u", remote_time_diff, local_time_diff);
+								net_cache = (net_data_struct *)prv_demo_mqtt.download_cache_head.next;
+								llist_del(&net_cache->node);
+								luat_heap_free(net_cache);
+								net_cache = (net_data_struct *)prv_demo_mqtt.download_cache_head.next;
+								LUAT_DEBUG_PRINT("resync start remote %s %llu %llu", remote_client, net_cache->remote_tamp, net_cache->local_tamp);
+							}
+						}
+					}
+					luat_airtalk_speech_start_play(prv_demo_mqtt.is_16k);
+					luat_start_rtos_timer(prv_demo_mqtt.download_check_timer, 3000, 0);
+				}
+			}
+RX_DATA_DONE:
+			luat_heap_free((char *)event.param1);
+			luat_heap_free((char *)event.param2);
+			break;
+		case AIRTALK_EVENT_MQTT_UPLINK_DATA:
+			if (prv_demo_mqtt.uplink_ready)
+			{
+				mqtt_publish(&(prv_demo_mqtt.mqtt_ctrl->broker), (char *)prv_demo_mqtt.topic.Data, prv_demo_mqtt.uplink.Data, prv_demo_mqtt.uplink.Pos, 0);
+			}
+			break;
+		case AIRTALK_EVENT_MQTT_UPLINK_END:
+			if (prv_demo_mqtt.uplink_ready)
+			{
+				prv_demo_mqtt.uplink.Pos = 16;
+				prv_demo_mqtt.uplink.Data[15] = 1;
+				mqtt_publish(&(prv_demo_mqtt.mqtt_ctrl->broker), (char *)prv_demo_mqtt.topic.Data, prv_demo_mqtt.uplink.Data, prv_demo_mqtt.uplink.Pos, 0);
+			}
+			break;
+		case AIRTALK_EVENT_MQTT_FORCE_SYNC:
+			LUAT_DEBUG_PRINT("sync lost resync!");
+			remote_client[0] = 0;
+			break;
+		case AIRTALK_EVENT_MQTT_FORCE_STOP:
+			LUAT_DEBUG_PRINT("broadcast long time no data!");
+			remote_client[0] = 0;
+			end_broadcast_play();
+			break;
+		case AIRTALK_EVENT_MQTT_MSG:
+			switch(event.param1)
+			{
+			case MQTT_MSG_TCP_TX_DONE:
+				//如果用QOS0发送,可以作为发送成功的初步判断依据
+				break;
+			case MQTT_MSG_CONNACK:
+				if(prv_demo_mqtt.mqtt_ctrl->mqtt_packet_buffer[3] != 0x00){
+					LUAT_DEBUG_PRINT("CONACK 0x%02x",prv_demo_mqtt.mqtt_ctrl->mqtt_packet_buffer[3]);
+					prv_demo_mqtt.mqtt_ctrl->error_state = prv_demo_mqtt.mqtt_ctrl->mqtt_packet_buffer[3];
+	                luat_mqtt_close_socket(prv_demo_mqtt.mqtt_ctrl);
+	                break;
+	            }
+				mqtt_subscribe(&(prv_demo_mqtt.mqtt_ctrl->broker), (char *)prv_demo_mqtt.topic.Data, &msgid, 0);
+				msgid++;
+				break;
+			case MQTT_MSG_SUBACK:
+				if(prv_demo_mqtt.mqtt_ctrl->mqtt_packet_buffer[4] > 0x02){
+					LUAT_DEBUG_PRINT("SUBACK 0x%02x",prv_demo_mqtt.mqtt_ctrl->mqtt_packet_buffer[4]);
+	                luat_mqtt_close_socket(prv_demo_mqtt.mqtt_ctrl);
+	                break;
+	            }
+				LUAT_DEBUG_PRINT("mqtt_subscribe ok");
+				OS_ReInitBuffer(&prv_demo_mqtt.uplink, 1024);
+				OS_BufferWrite(&prv_demo_mqtt.uplink, prv_demo_mqtt.self_id, 15);
+				prv_demo_mqtt.data_sync_ok = 0;
+				prv_demo_mqtt.uplink_ready = 1;
+				luat_airtalk_callback(LUAT_AIRTALK_CB_ON_LINE_IDLE, NULL, 0);
+				break;
+			case MQTT_MSG_DISCONNECT:
+				LUAT_DEBUG_PRINT("airtalk_mqtt_cb mqtt disconnect");
+				prv_demo_mqtt.uplink_ready = 0;
+				end_broadcast_play();
+				luat_airtalk_callback(LUAT_AIRTALK_CB_OFF_LINE, NULL, 0);
+				break;
+			case MQTT_MSG_TIMER_PING:
+				break;
+			case MQTT_MSG_RECONNECT:
+				break;
+			case MQTT_MSG_CLOSE :
+				prv_demo_mqtt.uplink_ready = 0;
+				luat_airtalk_callback(LUAT_AIRTALK_CB_OFF_LINE, NULL, 0);
+				break;
+			}
+			break;
+		}
+	}
+}
+
+void luat_airtalk_net_demo_mqtt_init(uint8_t is_16k)
+{
+	prv_demo_mqtt.is_16k = is_16k;
+	OS_InitBuffer(&prv_demo_mqtt.uplink, 1024);
+	luat_rtos_task_create(&prv_demo_mqtt.mqtt_task_handle, 8 * 1024, 90, "airtalk_mqtt", airtalk_demo_mqtt_task, NULL, 0);
+}
+
+void luat_airtalk_net_param_config(uint32_t download_cache_time)
+{
+	prv_demo_mqtt.download_cache_time = download_cache_time;
+}
+
+void luat_airtalk_net_uplink_start(void)
+{
+	prv_demo_mqtt.data_sync_ok = 0;
+}
+
+void luat_airtalk_net_force_sync_downlink(void)
+{
+	if (prv_demo_mqtt.data_sync_ok)
+	{
+		luat_rtos_event_send(prv_demo_mqtt.mqtt_task_handle, AIRTALK_EVENT_MQTT_FORCE_SYNC, 0, 0, 0, 0);
+	}
+}
+
+void luat_airtalk_net_save_uplink_head(uint64_t record_time)
+{
+	if (!prv_demo_mqtt.uplink_ready) return;
+	prv_demo_mqtt.uplink.Pos = 16;
+	if (record_time)
+	{
+		prv_demo_mqtt.uplink.Data[15] = 0;
+		OS_BufferWrite(&prv_demo_mqtt.uplink, &record_time, 8);
+	}
+	else
+	{
+		prv_demo_mqtt.uplink.Data[15] = 1;
+	}
+}
+
+void luat_airtalk_net_save_uplink_data(uint8_t *data, uint32_t len)
+{
+	if (!prv_demo_mqtt.uplink_ready) return;
+	OS_BufferWrite(&prv_demo_mqtt.uplink, data, len);
+}
+
+void luat_airtalk_net_uplink_once(void)
+{
+	if (!prv_demo_mqtt.uplink_ready) return;
+	luat_rtos_event_send(prv_demo_mqtt.mqtt_task_handle, AIRTALK_EVENT_MQTT_UPLINK_DATA, 0, 0, 0, 0);
+}
+
+void luat_airtalk_net_uplink_end(void)
+{
+	if (!prv_demo_mqtt.uplink_ready) return;
+	luat_rtos_event_send(prv_demo_mqtt.mqtt_task_handle, AIRTALK_EVENT_MQTT_UPLINK_END, 0, 0, 0, 0);
+}
+
+int luat_airtalk_net_set_device_id(char *id, uint32_t len)
+{
+	if (prv_demo_mqtt.uplink_ready) return -ERROR_DEVICE_BUSY;
+	if (len > 15) len = 15;
+	memset(prv_demo_mqtt.self_id, 0, sizeof(prv_demo_mqtt.self_id));
+	memcpy(prv_demo_mqtt.self_id, id, len);
+	return 0;
+}
+
+void luat_airtalk_net_set_mqtt_ctrl(void *ctrl)
+{
+	prv_demo_mqtt.mqtt_ctrl = ctrl;
+	prv_demo_mqtt.mqtt_ctrl->app_cb = airtalk_mqtt_cb;
+}
+
+void luat_airtalk_net_set_mqtt_topic(const void *data, uint32_t len)
+{
+	OS_ReInitBuffer(&prv_demo_mqtt.topic, len);
+	OS_BufferWrite(&prv_demo_mqtt.topic, data, len);
+}
+
+void luat_airtalk_net_debug_switch(uint8_t on_off)
+{
+	prv_demo_mqtt.debug_on_off = on_off;
+}

+ 377 - 0
components/airtalk/core/airtalk_mqtt.c

@@ -0,0 +1,377 @@
+#include "csdk.h"
+#include "airtalk_def.h"
+#include "airtalk_api.h"
+#include "luat_airtalk.h"
+
+#include "libemqtt.h"
+#include "luat_mqtt.h"
+#if 0
+typedef struct
+{
+	llist_head node;
+	uint64_t remote_tamp;
+	uint64_t local_tamp;
+	uint32_t total_len;
+	uint8_t amr_save_data[];
+}net_data_struct;
+
+typedef struct
+{
+	luat_mqtt_ctrl_t *mqtt_ctrl;
+	luat_rtos_task_handle mqtt_task_handle;
+	luat_rtos_timer_t download_check_timer;
+	Buffer_Struct uplink;
+	llist_head download_cache_head;				//下行数据接收缓存队列
+	uint32_t download_cache_time;
+	Buffer_Struct topic;
+	char self_id[15];
+	uint8_t data_sync_ok;
+	uint8_t uplink_ready;
+	uint8_t speech_on;
+	uint8_t is_16k;
+	uint8_t debug_on_off;
+}demo_mqtt_ctrl_t;
+
+static demo_mqtt_ctrl_t prv_demo_mqtt;
+
+
+//播放完成
+static void end_broadcast_play(void)
+{
+	net_data_struct *net_cache;
+	luat_airtalk_speech_stop_play();
+	luat_stop_rtos_timer(prv_demo_mqtt.download_check_timer);
+	while(!llist_empty(&prv_demo_mqtt.download_cache_head))
+	{
+		net_cache = (net_data_struct *)prv_demo_mqtt.download_cache_head.next;
+		llist_del(&net_cache->node);
+		luat_heap_free(net_cache);
+	}
+	prv_demo_mqtt.speech_on = 0;
+}
+
+static void download_check_timer(void *param)
+{
+	luat_rtos_event_send(prv_demo_mqtt.mqtt_task_handle, AIRTALK_EVENT_MQTT_FORCE_STOP, 0, 0, 0, 0);
+}
+
+
+static void airtalk_mqtt_cb(luat_mqtt_ctrl_t *mqtt_ctrl, uint16_t event)
+{
+	int ret;
+	if (event != MQTT_MSG_PUBLISH)
+	{
+		luat_rtos_event_send(prv_demo_mqtt.mqtt_task_handle, AIRTALK_EVENT_MQTT_MSG, event, 0, 0, 0);
+	}
+	else
+	{
+		const uint8_t* ptr;
+		uint32_t len;
+		uint8_t *topic = NULL;
+		uint8_t *payload = NULL;
+		len = mqtt_parse_pub_topic_ptr(prv_demo_mqtt.mqtt_ctrl->mqtt_packet_buffer, &ptr);
+		topic = luat_heap_calloc(len + 1, 1);
+		memcpy(topic, ptr, len);
+		len = mqtt_parse_pub_msg_ptr(prv_demo_mqtt.mqtt_ctrl->mqtt_packet_buffer, &ptr);
+		if (len)
+		{
+			payload = luat_heap_malloc(len);
+			memcpy(payload, ptr, len);
+		}
+		luat_rtos_event_send(prv_demo_mqtt.mqtt_task_handle, AIRTALK_EVENT_MQTT_DOWNLINK_DATA, (uint32_t)topic, (uint32_t)payload, len, 0);
+	}
+	return;
+}
+
+static void airtalk_demo_mqtt_task(void *param)
+{
+	uint64_t tamp;
+	uint32_t local_time_diff, remote_time_diff;
+	luat_event_t event;
+	net_data_struct *net_cache;
+	int ret = -1;
+	uint8_t *p;
+	char *packet_id;
+	int i;
+	uint16_t msgid = 0;
+	char remote_client[16] = {0};
+	INIT_LLIST_HEAD(&prv_demo_mqtt.download_cache_head);
+	prv_demo_mqtt.download_check_timer = luat_create_rtos_timer(download_check_timer, NULL, NULL);
+	if (!prv_demo_mqtt.download_cache_time)
+	{
+		prv_demo_mqtt.download_cache_time = 500;
+	}
+	if (!prv_demo_mqtt.topic.Data)
+	{
+		OS_BufferWrite(&prv_demo_mqtt.topic, "speech_demo/all", 16);
+	}
+	LUAT_DEBUG_PRINT("device id, %.*s topic %s", sizeof(prv_demo_mqtt.self_id), prv_demo_mqtt.self_id, prv_demo_mqtt.topic.Data);
+	while(1){
+		luat_rtos_event_recv(prv_demo_mqtt.mqtt_task_handle, 0, &event, NULL, LUAT_WAIT_FOREVER);
+		switch(event.id)
+		{
+		case AIRTALK_EVENT_MQTT_DOWNLINK_DATA:
+			if (memcmp(prv_demo_mqtt.topic.Data, (char *)event.param1, prv_demo_mqtt.topic.Pos - 1))
+			{
+				LUAT_DEBUG_PRINT("topic %s", (char *)event.param1);
+			}
+			else
+			{
+				packet_id = (char *)event.param2;
+				p = (uint8_t *)event.param2;
+				if (packet_id[15] > 1)
+				{
+					goto RX_DATA_DONE;
+				}
+				if (!memcmp(packet_id, prv_demo_mqtt.self_id, 15))
+				{
+#ifdef SELF_TEST
+#else
+					goto RX_DATA_DONE;
+#endif
+				}
+				if (packet_id[15])
+				{
+					if (prv_demo_mqtt.speech_on)
+					{
+						remote_client[0] = 0;
+						end_broadcast_play();
+						goto RX_DATA_DONE;
+					}
+					else
+					{
+						LUAT_DEBUG_PRINT("speech already stop!");
+						goto RX_DATA_DONE;
+					}
+				}
+				memcpy(&tamp, p + 16, 8);
+				if (!remote_client[0])
+				{
+					prv_demo_mqtt.speech_on = 1;
+					prv_demo_mqtt.data_sync_ok = 0;
+					net_cache = luat_heap_malloc(sizeof(net_data_struct) + event.param3);
+					net_cache->total_len = event.param3 - 24;
+					net_cache->remote_tamp = tamp;
+					net_cache->local_tamp = luat_mcu_tick64_ms();
+					memcpy(net_cache->amr_save_data, p + 24, net_cache->total_len);
+					llist_add_tail(&net_cache->node, &prv_demo_mqtt.download_cache_head);
+					memcpy(remote_client, packet_id, 15);
+					LUAT_DEBUG_PRINT("sync start remote %s %llu %llu", remote_client, net_cache->remote_tamp, net_cache->local_tamp);
+				}
+				else
+				{
+					if (memcmp(remote_client, packet_id, 15))
+					{
+						goto RX_DATA_DONE;
+					}
+				}
+				{
+					event.param3 -= 24;	//data_len
+					p += 24; //data
+					if (prv_demo_mqtt.data_sync_ok)
+					{
+						luat_airtalk_speech_save_downlink_data(p, event.param3);
+					}
+					else
+					{
+						net_cache = luat_heap_malloc(sizeof(net_data_struct) + event.param3);
+						net_cache->total_len = event.param3;
+						net_cache->remote_tamp = tamp;
+						net_cache->local_tamp = luat_mcu_tick64_ms();
+						memcpy(net_cache->amr_save_data, p, net_cache->total_len);
+						llist_add_tail(&net_cache->node, &prv_demo_mqtt.download_cache_head);
+
+						net_cache = (net_data_struct *)prv_demo_mqtt.download_cache_head.next;
+						remote_time_diff = (uint32_t)(tamp - net_cache->remote_tamp);
+						if (remote_time_diff >= (prv_demo_mqtt.download_cache_time - 20))
+						{
+							local_time_diff = (uint32_t)(luat_mcu_tick64_ms() - net_cache->local_tamp);
+							if (local_time_diff >= (prv_demo_mqtt.download_cache_time - 20))
+							{
+								LUAT_DEBUG_PRINT("sync ok");
+								prv_demo_mqtt.data_sync_ok = 1;
+								while(!llist_empty(&prv_demo_mqtt.download_cache_head))
+								{
+									net_cache = (net_data_struct *)prv_demo_mqtt.download_cache_head.next;
+									llist_del(&net_cache->node);
+									luat_airtalk_speech_save_downlink_data(net_cache->amr_save_data, net_cache->total_len);
+									luat_heap_free(net_cache);
+								}
+								luat_airtalk_speech_sync_ok();
+							}
+							else
+							{
+								LUAT_DEBUG_PRINT("sync failed %u, %u", remote_time_diff, local_time_diff);
+								net_cache = (net_data_struct *)prv_demo_mqtt.download_cache_head.next;
+								llist_del(&net_cache->node);
+								luat_heap_free(net_cache);
+								net_cache = (net_data_struct *)prv_demo_mqtt.download_cache_head.next;
+								LUAT_DEBUG_PRINT("resync start remote %s %llu %llu", remote_client, net_cache->remote_tamp, net_cache->local_tamp);
+							}
+						}
+					}
+					luat_airtalk_speech_start_play(prv_demo_mqtt.is_16k);
+					luat_start_rtos_timer(prv_demo_mqtt.download_check_timer, 3000, 0);
+				}
+			}
+RX_DATA_DONE:
+			luat_heap_free((char *)event.param1);
+			luat_heap_free((char *)event.param2);
+			break;
+		case AIRTALK_EVENT_MQTT_UPLINK_DATA:
+			if (prv_demo_mqtt.uplink_ready)
+			{
+				mqtt_publish(&(prv_demo_mqtt.mqtt_ctrl->broker), (char *)prv_demo_mqtt.topic.Data, prv_demo_mqtt.uplink.Data, prv_demo_mqtt.uplink.Pos, 0);
+			}
+			break;
+		case AIRTALK_EVENT_MQTT_UPLINK_END:
+			if (prv_demo_mqtt.uplink_ready)
+			{
+				prv_demo_mqtt.uplink.Pos = 16;
+				prv_demo_mqtt.uplink.Data[15] = 1;
+				mqtt_publish(&(prv_demo_mqtt.mqtt_ctrl->broker), (char *)prv_demo_mqtt.topic.Data, prv_demo_mqtt.uplink.Data, prv_demo_mqtt.uplink.Pos, 0);
+			}
+			break;
+		case AIRTALK_EVENT_MQTT_FORCE_SYNC:
+			LUAT_DEBUG_PRINT("sync lost resync!");
+			remote_client[0] = 0;
+			break;
+		case AIRTALK_EVENT_MQTT_FORCE_STOP:
+			LUAT_DEBUG_PRINT("broadcast long time no data!");
+			remote_client[0] = 0;
+			end_broadcast_play();
+			break;
+		case AIRTALK_EVENT_MQTT_MSG:
+			switch(event.param1)
+			{
+			case MQTT_MSG_TCP_TX_DONE:
+				//如果用QOS0发送,可以作为发送成功的初步判断依据
+				break;
+			case MQTT_MSG_CONNACK:
+				if(prv_demo_mqtt.mqtt_ctrl->mqtt_packet_buffer[3] != 0x00){
+					LUAT_DEBUG_PRINT("CONACK 0x%02x",prv_demo_mqtt.mqtt_ctrl->mqtt_packet_buffer[3]);
+					prv_demo_mqtt.mqtt_ctrl->error_state = prv_demo_mqtt.mqtt_ctrl->mqtt_packet_buffer[3];
+	                luat_mqtt_close_socket(prv_demo_mqtt.mqtt_ctrl);
+	                break;
+	            }
+				mqtt_subscribe(&(prv_demo_mqtt.mqtt_ctrl->broker), (char *)prv_demo_mqtt.topic.Data, &msgid, 0);
+				msgid++;
+				break;
+			case MQTT_MSG_SUBACK:
+				if(prv_demo_mqtt.mqtt_ctrl->mqtt_packet_buffer[4] > 0x02){
+					LUAT_DEBUG_PRINT("SUBACK 0x%02x",prv_demo_mqtt.mqtt_ctrl->mqtt_packet_buffer[4]);
+	                luat_mqtt_close_socket(prv_demo_mqtt.mqtt_ctrl);
+	                break;
+	            }
+				LUAT_DEBUG_PRINT("mqtt_subscribe ok");
+				OS_ReInitBuffer(&prv_demo_mqtt.uplink, 1024);
+				OS_BufferWrite(&prv_demo_mqtt.uplink, prv_demo_mqtt.self_id, 15);
+				prv_demo_mqtt.data_sync_ok = 0;
+				prv_demo_mqtt.uplink_ready = 1;
+				luat_airtalk_callback(LUAT_AIRTALK_CB_ON_LINE_IDLE, NULL, 0);
+				break;
+			case MQTT_MSG_DISCONNECT:
+				LUAT_DEBUG_PRINT("airtalk_mqtt_cb mqtt disconnect");
+				prv_demo_mqtt.uplink_ready = 0;
+				end_broadcast_play();
+				luat_airtalk_callback(LUAT_AIRTALK_CB_OFF_LINE, NULL, 0);
+				break;
+			case MQTT_MSG_TIMER_PING:
+				break;
+			case MQTT_MSG_RECONNECT:
+				break;
+			case MQTT_MSG_CLOSE :
+				prv_demo_mqtt.uplink_ready = 0;
+				luat_airtalk_callback(LUAT_AIRTALK_CB_OFF_LINE, NULL, 0);
+				break;
+			}
+			break;
+		}
+	}
+}
+
+void luat_airtalk_net_demo_mqtt_init(uint8_t is_16k)
+{
+	prv_demo_mqtt.is_16k = is_16k;
+	OS_InitBuffer(&prv_demo_mqtt.uplink, 1024);
+	luat_rtos_task_create(&prv_demo_mqtt.mqtt_task_handle, 8 * 1024, 90, "airtalk_mqtt", airtalk_demo_mqtt_task, NULL, 0);
+}
+
+void luat_airtalk_net_param_config(uint32_t download_cache_time)
+{
+	prv_demo_mqtt.download_cache_time = download_cache_time;
+}
+
+void luat_airtalk_net_uplink_start(void)
+{
+	prv_demo_mqtt.data_sync_ok = 0;
+}
+
+void luat_airtalk_net_force_sync_downlink(void)
+{
+	if (prv_demo_mqtt.data_sync_ok)
+	{
+		luat_rtos_event_send(prv_demo_mqtt.mqtt_task_handle, AIRTALK_EVENT_MQTT_FORCE_SYNC, 0, 0, 0, 0);
+	}
+}
+
+void luat_airtalk_net_save_uplink_head(uint64_t record_time)
+{
+	if (!prv_demo_mqtt.uplink_ready) return;
+	prv_demo_mqtt.uplink.Pos = 16;
+	if (record_time)
+	{
+		prv_demo_mqtt.uplink.Data[15] = 0;
+		OS_BufferWrite(&prv_demo_mqtt.uplink, &record_time, 8);
+	}
+	else
+	{
+		prv_demo_mqtt.uplink.Data[15] = 1;
+	}
+}
+
+void luat_airtalk_net_save_uplink_data(uint8_t *data, uint32_t len)
+{
+	if (!prv_demo_mqtt.uplink_ready) return;
+	OS_BufferWrite(&prv_demo_mqtt.uplink, data, len);
+}
+
+void luat_airtalk_net_uplink_once(void)
+{
+	if (!prv_demo_mqtt.uplink_ready) return;
+	luat_rtos_event_send(prv_demo_mqtt.mqtt_task_handle, AIRTALK_EVENT_MQTT_UPLINK_DATA, 0, 0, 0, 0);
+}
+
+void luat_airtalk_net_uplink_end(void)
+{
+	if (!prv_demo_mqtt.uplink_ready) return;
+	luat_rtos_event_send(prv_demo_mqtt.mqtt_task_handle, AIRTALK_EVENT_MQTT_UPLINK_END, 0, 0, 0, 0);
+}
+
+int luat_airtalk_net_set_device_id(char *id, uint32_t len)
+{
+	if (prv_demo_mqtt.uplink_ready) return -ERROR_DEVICE_BUSY;
+	if (len > 15) len = 15;
+	memset(prv_demo_mqtt.self_id, 0, sizeof(prv_demo_mqtt.self_id));
+	memcpy(prv_demo_mqtt.self_id, id, len);
+	return 0;
+}
+
+void luat_airtalk_net_set_mqtt_ctrl(void *ctrl)
+{
+	prv_demo_mqtt.mqtt_ctrl = ctrl;
+	prv_demo_mqtt.mqtt_ctrl->app_cb = airtalk_mqtt_cb;
+}
+
+void luat_airtalk_net_set_mqtt_topic(const void *data, uint32_t len)
+{
+	OS_ReInitBuffer(&prv_demo_mqtt.topic, len);
+	OS_BufferWrite(&prv_demo_mqtt.topic, data, len);
+}
+
+void luat_airtalk_net_debug_switch(uint8_t on_off)
+{
+	prv_demo_mqtt.debug_on_off = on_off;
+}
+
+#endif

+ 5 - 0
components/airtalk/core/airtalk_network.c

@@ -0,0 +1,5 @@
+#include "csdk.h"
+#include "airtalk_def.h"
+#include "airtalk_api.h"
+
+

+ 34 - 0
components/airtalk/include/airtalk_api.h

@@ -0,0 +1,34 @@
+/*
+ * airtalk_api.h
+ *
+ *  Created on: 2025年6月26日
+ *      Author: Administrator
+ */
+
+#ifndef AIRTALK_INCLUDE_AIRTALK_API_H_
+#define AIRTALK_INCLUDE_AIRTALK_API_H_
+
+void luat_airtalk_net_demo_mqtt_init(uint8_t is_16k);
+int luat_airtalk_net_set_device_id(char *id, uint32_t len);
+void luat_airtalk_net_set_mqtt_ctrl(void *ctrl);
+void luat_airtalk_net_set_mqtt_topic(const void *data, uint32_t len);
+void luat_airtalk_net_param_config(uint32_t download_cache_time);
+void luat_airtalk_net_debug_switch(uint8_t on_off);
+void luat_airtalk_net_uplink_start(void);
+void luat_airtalk_net_save_uplink_head(uint64_t record_time);
+void luat_airtalk_net_save_uplink_data(uint8_t *data, uint32_t len);
+void luat_airtalk_net_uplink_once(void);
+void luat_airtalk_net_uplink_end(void);
+void luat_airtalk_net_force_sync_downlink(void);
+
+
+void luat_airtalk_speech_init(void);
+void luat_airtalk_speech_audio_param_config(int multimedia_id, uint8_t audio_sleep_mode);
+void luat_airtalk_speech_debug_switch(uint8_t on_off);
+int luat_airtalk_speech_set_one_block_frame_cnt(uint8_t decode_frame_cnt, uint8_t encode_frame_cnt);
+void luat_airtalk_speech_start_play(uint8_t is_16k);
+void luat_airtalk_speech_stop_play(void);
+void luat_airtalk_speech_record_switch(uint8_t on_off, uint8_t is_16k);
+void luat_airtalk_speech_sync_ok(void);
+void luat_airtalk_speech_save_downlink_data(uint8_t *data, uint32_t len);
+#endif /* AIRTALK_INCLUDE_AIRTALK_API_H_ */

+ 20 - 0
components/airtalk/include/airtalk_def.h

@@ -0,0 +1,20 @@
+#ifndef __AIRTALK_H__
+#define __AIRTALK_H__
+
+enum
+{
+	AIRTALK_EVENT_AMR_ENCODE_ONCE = 1,
+	AIRTALK_EVENT_AMR_DECODE_ONCE,
+	AIRTALK_EVENT_AMR_START,			//audio处理流程开始
+	AIRTALK_EVENT_AMR_RECORD_STOP,		//录音停止
+	AIRTALK_EVENT_AMR_PLAY_STOP,		//播放停止
+
+	AIRTALK_EVENT_MQTT_MSG,				//MQTT除了上下行数据外其他消息
+	AIRTALK_EVENT_MQTT_DOWNLINK_DATA,	//MQTT下行数据,需要解码播放
+	AIRTALK_EVENT_MQTT_UPLINK_DATA,		//MQTT上行数据,已经编码过了
+	AIRTALK_EVENT_MQTT_UPLINK_END,
+	AIRTALK_EVENT_MQTT_FORCE_SYNC,		//重新同步数据
+	AIRTALK_EVENT_MQTT_FORCE_STOP,		//停止对讲流程
+
+};
+#endif

+ 28 - 0
components/airtalk/include/luat_airtalk.h

@@ -0,0 +1,28 @@
+/*
+ * luat_airtalk.h
+ *
+ *  Created on: 2025年6月26日
+ *      Author: Administrator
+ */
+
+#ifndef AIRTALK_INCLUDE_LUAT_AIRTALK_H_
+#define AIRTALK_INCLUDE_LUAT_AIRTALK_H_
+enum
+{
+	LUAT_AIRTALK_PROTOCOL_DEMO_MQTT_8K = 0,
+	LUAT_AIRTALK_PROTOCOL_DEMO_MQTT_16K,
+	LUAT_AIRTALK_PROTOCOL_AIRM2M,
+
+	LUAT_AIRTALK_CB_OFF_LINE = 0,
+	LUAT_AIRTALK_CB_ON_LINE_IDLE,
+	LUAT_AIRTALK_CB_PLAY_START,
+	LUAT_AIRTALK_CB_PLAY_END,
+	LUAT_AIRTALK_CB_RECORD_START,
+	LUAT_AIRTALK_CB_RECORD_END,
+	LUAT_AIRTALK_CB_AUDIO_START,
+	LUAT_AIRTALK_CB_AUDIO_END,
+	LUAT_AIRTALK_CB_ERROR
+};
+void luat_airtalk_callback(uint32_t event, void *param, uint32_t param_len);
+
+#endif /* AIRTALK_INCLUDE_LUAT_AIRTALK_H_ */

+ 364 - 0
components/airtalk/platform/ec7xx/airtalk_speech_ec7xx.c

@@ -0,0 +1,364 @@
+#include "csdk.h"
+#include "airtalk_def.h"
+#include "airtalk_api.h"
+#include "luat_airtalk.h"
+#include "luat_multimedia.h"
+
+#define PCM_BLOCK_LEN (prv_speech.one_frame_len)
+#define DOWNLOAD_CACHE_MASK	(14)		//下行数据缓存1<<14Byte (16K)
+#define PCM_PLAY_FRAME_LOOP_CNT	(4)	//4个播放缓冲区循环使用
+#define SPEECH_ONE_CACHE_MAX	(6400)	//16K单声道200ms的数据量
+static const uint8_t  amr_nb_byte_len[16] = {12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0};
+static const uint8_t  amr_wb_byte_len[16] = {17, 23, 32, 36, 40, 46, 50, 58, 60, 5,0, 0, 0, 0, 0, 0};
+typedef struct
+{
+
+	BSP_FifoStruct download_data_fifo;
+	luat_rtos_task_handle speech_task_handle;
+	int multimedia_id;
+	void *audio_handle;
+	const uint8_t *amr_byte_len;
+	uint32_t one_frame_len;
+	uint32_t play_data_cache[SPEECH_ONE_CACHE_MAX];
+	uint32_t record_data_cache[SPEECH_ONE_CACHE_MAX >> 1];
+	uint8_t *play_data_buffer;				//播放缓冲区
+	uint8_t *record_data_buffer;
+	uint16_t decode_frame_cnt;
+	uint16_t encode_frame_cnt;
+	uint16_t total_play_frame;							//播放缓冲区总共的帧数
+	uint16_t ref_frame_start_pos;							//回声抑制参考起始帧位置
+	volatile uint16_t record_frame_pos;					//已经缓存的录音帧数
+	volatile uint16_t play_frame_pos;					//已经播放的帧数
+	uint8_t record_enable;						//允许录音
+	uint8_t play_enable;						//允许播放
+	uint8_t audio_sleep_mode;
+	uint8_t decode_sync_ok;
+	volatile uint8_t record_buffer_pos;
+	uint8_t debug_on_off;
+	uint8_t download_data_buffer[1 << DOWNLOAD_CACHE_MASK];					//测试用的播放缓冲区,正常播放不要使用
+
+}speech_ctrl_t;
+
+static speech_ctrl_t prv_speech;
+
+static __USER_FUNC_IN_RAM__ int airtalk_record_cb(uint8_t id ,luat_i2s_event_t event, uint8_t *rx_data, uint32_t rx_len, void *param)
+{
+
+	if (!prv_speech.audio_handle) return 0;
+	switch(event)
+	{
+	case LUAT_I2S_EVENT_RX_DONE:
+		memcpy(&prv_speech.record_data_buffer[SPEECH_ONE_CACHE_MAX * prv_speech.record_buffer_pos + prv_speech.record_frame_pos * prv_speech.one_frame_len], rx_data, rx_len);
+		prv_speech.record_frame_pos++;
+		prv_speech.play_frame_pos++;
+		if (prv_speech.record_frame_pos >= prv_speech.encode_frame_cnt)
+		{
+			if (prv_speech.record_enable)
+			{
+				luat_rtos_event_send(prv_speech.speech_task_handle, AIRTALK_EVENT_AMR_ENCODE_ONCE, prv_speech.record_buffer_pos, prv_speech.ref_frame_start_pos, 0, 0);
+			}
+			prv_speech.record_frame_pos = 0;
+			prv_speech.record_buffer_pos = !prv_speech.record_buffer_pos;
+			prv_speech.ref_frame_start_pos += prv_speech.encode_frame_cnt;
+			if (prv_speech.ref_frame_start_pos >= prv_speech.total_play_frame)
+			{
+				prv_speech.ref_frame_start_pos = 0;
+			}
+		}
+		if (prv_speech.play_frame_pos >= prv_speech.decode_frame_cnt)
+		{
+			luat_rtos_event_send(prv_speech.speech_task_handle, AIRTALK_EVENT_AMR_DECODE_ONCE, 0, 0, 0, 0);
+			prv_speech.play_frame_pos = 0;
+		}
+		break;
+	case LUAT_I2S_EVENT_TRANSFER_DONE:
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+extern void log_on(void);
+static void speech_task(void *param)
+{
+	if (!prv_speech.audio_sleep_mode)
+	{
+		prv_speech.audio_sleep_mode = LUAT_AUDIO_PM_SHUTDOWN;
+	}
+	if (!prv_speech.decode_frame_cnt)
+	{
+		prv_speech.decode_frame_cnt = 5;
+	}
+	if (!prv_speech.encode_frame_cnt)
+	{
+		prv_speech.encode_frame_cnt = 5;
+	}
+	prv_speech.play_data_buffer = (uint8_t *)prv_speech.play_data_cache;
+	prv_speech.record_data_buffer = (uint8_t *)prv_speech.record_data_cache;
+	luat_audio_conf_t* audio_conf = luat_audio_get_config(prv_speech.multimedia_id);
+	luat_i2s_conf_t *i2s = luat_i2s_get_config(audio_conf->codec_conf.i2s_id);
+
+	OS_InitFifo(&prv_speech.download_data_fifo, prv_speech.download_data_buffer, DOWNLOAD_CACHE_MASK);
+	uint8_t *ref_input;
+	luat_event_t event;
+	uint32_t decode_pos = 0;
+	uint32_t current_play_cnt = 0;					//当前播放缓冲区,用于解码数据存入下一个缓存
+	uint32_t i;
+	uint32_t data_pos;
+	PV_Union u_point;
+	uint8_t out_len, lost_data, temp_len, need_stop_record, need_stop_play, wait_stop_play,is_amr_wb;
+	uint8_t amr_buff[64];
+
+	need_stop_record = 0;
+	need_stop_play = 0;
+	wait_stop_play = 0;
+	temp_len = 0;
+
+	while (1)
+	{
+		luat_rtos_event_recv(prv_speech.speech_task_handle, 0, &event, NULL, LUAT_WAIT_FOREVER);
+		switch(event.id)
+		{
+		case AIRTALK_EVENT_AMR_ENCODE_ONCE:
+			if (!prv_speech.audio_handle)
+			{
+				break;
+			}
+			if (prv_speech.record_enable)
+			{
+				if (prv_speech.debug_on_off)
+				{
+					LUAT_DEBUG_PRINT("ref point %d, record cnt %d", event.param2, event.param1);
+				}
+				u_point.pu8 = &prv_speech.record_data_buffer[SPEECH_ONE_CACHE_MAX * event.param1];
+				luat_airtalk_net_save_uplink_head(luat_mcu_tick64_ms());
+				for(i = 0; i < prv_speech.encode_frame_cnt; i++)
+				{
+					ref_input = &prv_speech.play_data_buffer[PCM_BLOCK_LEN * event.param2 + i * PCM_BLOCK_LEN];
+					//录音时刻对应的放音数据作为回声消除的参考数据输入,可以完美消除回声
+					luat_audio_inter_amr_encode_with_ref(&u_point.pu16[(i * PCM_BLOCK_LEN) >> 1], amr_buff, &out_len, ref_input);
+					luat_airtalk_net_save_uplink_data(amr_buff, out_len);
+				}
+				luat_airtalk_net_uplink_once();
+				//如果有停止录音的请求,让MQTT上行一次终止包
+				if (need_stop_record)
+				{
+					LUAT_DEBUG_PRINT("upload stop!");
+					need_stop_record = 0;
+					prv_speech.record_enable = 0;
+					luat_airtalk_callback(LUAT_AIRTALK_CB_RECORD_END, NULL, 0);
+					luat_airtalk_net_uplink_end();
+				}
+			}
+			break;
+		case AIRTALK_EVENT_AMR_DECODE_ONCE:
+			if (!prv_speech.audio_handle)
+			{
+				break;
+			}
+			current_play_cnt = (current_play_cnt + 1) & 0x3;
+			decode_pos = (current_play_cnt + 1) & 0x03;
+			if (prv_speech.debug_on_off)
+			{
+				LUAT_DEBUG_PRINT("play pos %u, decode pos %u", current_play_cnt, decode_pos);
+			}
+			if (prv_speech.decode_sync_ok)
+			{
+				lost_data = 0;
+				for(i = 0; i < prv_speech.decode_frame_cnt; i++)
+				{
+					if (OS_CheckFifoUsedSpace(&prv_speech.download_data_fifo))
+					{
+						data_pos = (uint32_t)(prv_speech.download_data_fifo.RPoint & prv_speech.download_data_fifo.Mask);
+						temp_len = prv_speech.amr_byte_len[(prv_speech.download_data_buffer[data_pos] >> 3) & 0x0f];
+						OS_ReadFifo(&prv_speech.download_data_fifo, amr_buff, temp_len + 1);
+						u_point.pu8 = &prv_speech.play_data_buffer[PCM_BLOCK_LEN * prv_speech.decode_frame_cnt * decode_pos + i * PCM_BLOCK_LEN];
+						luat_audio_inter_amr_coder_decode(prv_speech.audio_handle, u_point.pu16, amr_buff, &out_len);
+					}
+					else
+					{
+						memset(&prv_speech.play_data_buffer[PCM_BLOCK_LEN * prv_speech.decode_frame_cnt * decode_pos + i * PCM_BLOCK_LEN], 0, PCM_BLOCK_LEN);
+						lost_data = 1;
+
+					}
+				}
+				if (lost_data)
+				{
+					LUAT_DEBUG_PRINT("lost");
+					prv_speech.decode_sync_ok = 0;
+					luat_airtalk_net_force_sync_downlink();
+				}
+			}
+			else
+			{
+				if (prv_speech.debug_on_off)
+				{
+					LUAT_DEBUG_PRINT("no decode");
+				}
+				memset(&prv_speech.play_data_buffer[PCM_BLOCK_LEN * prv_speech.decode_frame_cnt * decode_pos], 0, prv_speech.decode_frame_cnt * PCM_BLOCK_LEN);
+			}
+
+			if (wait_stop_play)
+			{
+				wait_stop_play = 0;
+				prv_speech.play_enable = 0;
+				LUAT_DEBUG_PRINT("play stop!");
+				luat_airtalk_callback(LUAT_AIRTALK_CB_PLAY_END, NULL, 0);
+			}
+			else if (need_stop_play)
+			{
+				LUAT_DEBUG_PRINT("play wait stop!");
+				wait_stop_play = 1;
+				need_stop_play = 0;
+			}
+			//既没有播放也没有录音,就直接停止audio
+			if (!prv_speech.record_enable && !prv_speech.play_enable)
+			{
+				LUAT_DEBUG_PRINT("audio stop!");
+				luat_audio_record_stop(prv_speech.multimedia_id);
+				luat_audio_pm_request(prv_speech.multimedia_id, prv_speech.audio_sleep_mode);
+				luat_audio_inter_amr_coder_deinit(prv_speech.audio_handle);
+				luat_i2s_load_old_config(audio_conf->codec_conf.i2s_id);
+				prv_speech.audio_handle = NULL;
+				prv_speech.decode_sync_ok = 0;
+				luat_airtalk_callback(LUAT_AIRTALK_CB_AUDIO_END, NULL, 0);
+			}
+			break;
+		case AIRTALK_EVENT_AMR_START:
+			if (!prv_speech.audio_handle)
+			{
+				is_amr_wb = event.param1?1:0;
+				LUAT_DEBUG_PRINT("play start %s!", is_amr_wb?"amr-wb":"amr-nb");
+				prv_speech.one_frame_len = 320 * (is_amr_wb + 1);
+				luat_audio_pm_request(prv_speech.multimedia_id, LUAT_AUDIO_PM_RESUME);
+				prv_speech.amr_byte_len = is_amr_wb?amr_wb_byte_len:amr_nb_byte_len;
+				memset(prv_speech.play_data_buffer, 0, sizeof(prv_speech.play_data_cache));
+				memset(prv_speech.record_data_buffer, 0, sizeof(prv_speech.record_data_cache));
+				prv_speech.record_buffer_pos = 0;
+				prv_speech.record_frame_pos = 0;
+				prv_speech.play_frame_pos = 0;
+				prv_speech.ref_frame_start_pos = 0;
+				prv_speech.total_play_frame = prv_speech.decode_frame_cnt * PCM_PLAY_FRAME_LOOP_CNT;
+				prv_speech.audio_handle = luat_audio_inter_amr_coder_init(is_amr_wb, 7 + is_amr_wb);
+				prv_speech.download_data_fifo.RPoint  = 0;
+				prv_speech.download_data_fifo.WPoint  = 0;
+				memset(prv_speech.play_data_cache, 0, sizeof(prv_speech.play_data_cache));
+				current_play_cnt = 0;
+
+				luat_i2s_save_old_config(audio_conf->codec_conf.i2s_id);
+				i2s->cb_rx_len = PCM_BLOCK_LEN;
+				i2s->luat_i2s_event_callback = airtalk_record_cb;
+				luat_audio_record_and_play(prv_speech.multimedia_id, 8000 * (is_amr_wb + 1), prv_speech.play_data_buffer, prv_speech.decode_frame_cnt * PCM_BLOCK_LEN, PCM_PLAY_FRAME_LOOP_CNT);
+				luat_airtalk_callback(LUAT_AIRTALK_CB_PLAY_START, NULL, 0);
+				if (prv_speech.record_enable)//已经请求录音了,那么就开始录音了
+				{
+					luat_airtalk_net_uplink_start();
+					luat_airtalk_callback(LUAT_AIRTALK_CB_RECORD_START, NULL, 0);
+				}
+				luat_airtalk_callback(LUAT_AIRTALK_CB_AUDIO_START, NULL, 0);
+			}
+
+			break;
+		case AIRTALK_EVENT_AMR_RECORD_STOP:
+			if (prv_speech.record_enable)
+			{
+				need_stop_record = 1;
+				LUAT_DEBUG_PRINT("record require stop!");
+			}
+			break;
+		case AIRTALK_EVENT_AMR_PLAY_STOP:
+			if (prv_speech.play_enable && !need_stop_play)
+			{
+				need_stop_play = 1;
+				LUAT_DEBUG_PRINT("play require stop!");
+			}
+			break;
+		}
+	}
+}
+
+void luat_airtalk_speech_init(void)
+{
+#if defined (FEATURE_AMR_CP_ENABLE) || defined (FEATURE_VEM_CP_ENABLE)
+	if (!prv_speech.speech_task_handle)
+	{
+		luat_rtos_task_create(&prv_speech.speech_task_handle, 4096, 100, "airtalk_speech", speech_task, NULL, 64);
+	}
+#else
+	LUAT_DEBUG_PRINT("sdk no audio function, stop!!!");
+#endif
+}
+
+void luat_airtalk_speech_audio_param_config(int multimedia_id, uint8_t audio_sleep_mode)
+{
+	prv_speech.multimedia_id = multimedia_id;
+	prv_speech.audio_sleep_mode = audio_sleep_mode;
+}
+
+void luat_airtalk_speech_start_play(uint8_t is_16k)
+{
+	if (!prv_speech.audio_handle)
+	{
+		prv_speech.play_enable = 1;
+		luat_rtos_event_send(prv_speech.speech_task_handle, AIRTALK_EVENT_AMR_START, is_16k, 0, 0, 0);
+	}
+}
+
+void luat_airtalk_speech_stop_play()
+{
+	LUAT_DEBUG_PRINT("broadcast play end!");
+	luat_rtos_event_send(prv_speech.speech_task_handle, AIRTALK_EVENT_AMR_PLAY_STOP, 0, 0, 0, 0);
+}
+
+void luat_airtalk_speech_sync_ok(void)
+{
+	prv_speech.decode_sync_ok = 1;
+}
+
+int luat_airtalk_speech_set_one_block_frame_cnt(uint8_t decode_frame_cnt, uint8_t encode_frame_cnt)
+{
+	if (prv_speech.audio_handle) return -ERROR_DEVICE_BUSY;
+	if (decode_frame_cnt > 10) return -ERROR_PARAM_INVALID;
+	if (decode_frame_cnt < 2) return -ERROR_PARAM_INVALID;
+	if (encode_frame_cnt < 2) return -ERROR_PARAM_INVALID;
+	if (encode_frame_cnt > 5) return -ERROR_PARAM_INVALID;
+	prv_speech.decode_frame_cnt = decode_frame_cnt;
+	prv_speech.encode_frame_cnt = encode_frame_cnt;
+	return 0;
+}
+
+void luat_airtalk_speech_save_downlink_data(uint8_t *data, uint32_t len)
+{
+	OS_WriteFifo(&prv_speech.download_data_fifo, data, len);
+}
+
+void luat_airtalk_speech_record_switch(uint8_t on_off, uint8_t is_16k)
+{
+	if (on_off)
+	{
+		if (!prv_speech.audio_handle)
+		{
+			prv_speech.record_enable = 1;
+			luat_rtos_event_send(prv_speech.speech_task_handle, AIRTALK_EVENT_AMR_START, is_16k, 0, 0, 0);
+		}
+		else
+		{
+			if (!prv_speech.record_enable)
+			{
+				prv_speech.record_enable = 1;
+				luat_airtalk_callback(LUAT_AIRTALK_CB_RECORD_START, NULL, 0);
+			}
+		}
+	}
+	else
+	{
+		luat_rtos_event_send(prv_speech.speech_task_handle, AIRTALK_EVENT_AMR_RECORD_STOP, 0, 0, 0, 0);
+	}
+}
+
+void luat_airtalk_speech_debug_switch(uint8_t on_off)
+{
+	prv_speech.debug_on_off = on_off;
+	if (on_off) 	log_on();
+}

+ 49 - 0
components/airtalk/platform/ec7xx/vem.c

@@ -0,0 +1,49 @@
+#include "csdk.h"
+#include "luat_audio_play.h"
+
+#include "audioCfg.h"   //struct AudioConfig_t
+#include "mw_nvm_audio.h"
+extern void ShareInfoAPGetCPAudioLogCtrl(AudioParaCfgLogControl_t *audioLogCfg);
+extern void ShareInfoAPSetCPAudioLogCtrl(AudioParaCfgLogControl_t audioLogCfg);
+void log_on(void)
+{
+	AudioParaCfgLogControl_t audioLogCfg = {0};
+	AudioParaCfgCommon_t mAudioCfgCommon = {0};
+	AecConfig_t    MwNvmAudioSphTxAEC;
+	ecAudioCfgTlvStore *pMwNvmAudioCfg = NULL;
+	AudioParaCfgLogControl_t MwNvmAudioLogCtrl;
+	pMwNvmAudioCfg  = (ecAudioCfgTlvStore *)luat_heap_malloc(sizeof(ecAudioCfgTlvStore)+ sizeof(AudioParaSphEQBiquard_t)*EC_ADCFG_SPEECH_EQ_BIQUARD_NUMB*EC_ADCFG_SPEECH_TX_NUMB
+					+ sizeof(AudioParaSphEQBiquard_t)*EC_ADCFG_SPEECH_EQ_BIQUARD_NUMB*EC_ADCFG_SPEECH_RX_NUMB + sizeof(UINT16)*EC_ADCFG_SPEECH_ANS_EQ_BAND_NUMB*EC_ADCFG_SPEECH_RX_NUMB
+					 + sizeof(UINT16)*EC_ADCFG_SPEECH_ANS_EQ_BAND_NUMB*EC_ADCFG_SPEECH_TX_NUMB);
+
+	if (mwNvmAudioCfgRead(pMwNvmAudioCfg) == FALSE)
+	{
+		if (mwNvmAudioCfgRead(pMwNvmAudioCfg) == FALSE)
+		{
+			LUAT_DEBUG_PRINT("read config failed");
+		}
+	}
+	mwNvmAudioCfgLogControlGet(&MwNvmAudioLogCtrl, pMwNvmAudioCfg);
+	mwNvmAudioCfgSpeechGetTxAEC(&mAudioCfgCommon, &MwNvmAudioSphTxAEC, pMwNvmAudioCfg);
+	if (!MwNvmAudioLogCtrl.TxBeforeVem)
+	{
+		MwNvmAudioLogCtrl.TxBeforeVem = 1;
+		MwNvmAudioLogCtrl.TxAfterVem = 1;
+		MwNvmAudioLogCtrl.RxBeforeVem = 1;
+		MwNvmAudioLogCtrl.RxAfterVem = 1;
+		MwNvmAudioLogCtrl.RxBeforeDecoder = 1;
+		MwNvmAudioLogCtrl.TxAfterEncoder = 1;
+		mwNvmAudioCfgLogControlSet(&MwNvmAudioLogCtrl, pMwNvmAudioCfg);
+		LUAT_DEBUG_PRINT("log on");
+	}
+
+	audioLogCfg.TxBeforeVem = 1;
+	audioLogCfg.TxAfterVem = 1;
+	audioLogCfg.RxBeforeVem = 1;
+	audioLogCfg.RxAfterVem = 1;
+	audioLogCfg.RxBeforeDecoder = 1;
+	audioLogCfg.TxAfterEncoder = 1;
+	ShareInfoAPSetCPAudioLogCtrl(audioLogCfg);
+	luat_heap_free(pMwNvmAudioCfg);
+}
+

+ 189 - 0
components/airtalk/platform/ec7xx/vem_cfg.c

@@ -0,0 +1,189 @@
+
+#include "audioCfg.h"   //struct AudioConfig_t
+
+const AudioConfig_t audio_cfg_default =
+{
+	.amrEncodeBypass = 0,
+	.amrDecodeBypass = 0,
+	.amrAllowDtx 	 = 1,
+
+	.speechCfgTx =
+	{
+
+#if defined(TX_AEC)
+	    .CVT_AEC = {
+	    .bypass = 0,
+	    .delay = 0,//yww if>= 0,use this value//ori = 100, modify to 10(10*4=40ms)
+	    .cngMode = 1,
+	    .echoMode = 3,//ori=1,default=3
+	    .nlpFlag = 1,
+	    },
+#endif
+
+#if defined(TX_ANS)
+	.CVT_ANS = {
+		.bypass = 0,
+		.mode = 0,
+		.eqBypass = 0,
+		.eqBand[0] = 32767,//20240229	//[0-32767]
+		.eqBand[1] = 32767,
+		.eqBand[2] = 32767,
+		.eqBand[3] = 32767,
+		.eqBand[4] = 32767,
+		.eqBand[5] = 32767,
+		.eqBand[6] = 32767,
+		.eqBand[7] = 32767,
+		.eqBand[8] = 32767,
+		.eqBand[9] = 32767,
+		.eqBand[10] = 32767,
+		.eqBand[11] = 32767,
+		.eqBand[12] = 32767,
+		.eqBand[13] = 32767,
+		.eqBand[14] = 32767,
+		.eqBand[15] = 32767,
+		.eqBand[16] = 32767,
+		.eqBand[17] = 32767,
+		.eqBand[18] = 32767,
+		.eqBand[19] = 32767,
+		.eqBand[20] = 32767,
+		.eqBand[21] = 32767,
+		.eqBand[22] = 32767,
+		.eqBand[23] = 32767,
+		.eqBand[24] = 32767,
+		.eqBand[25] = 32767,
+		.eqBand[26] = 32767,
+		.eqBand[27] = 32767,
+		.eqBand[28] = 32767,
+		.eqBand[29] = 32767,
+		.eqBand[30] = 32767,
+		.eqBand[31] = 32767,
+		},
+#endif
+
+#if defined(TX_DRC)
+	    .CVT_DRC = {
+	    .bypass = 1,
+	    .compThreshold = -960,	//-15*2^6=960,Q6
+	    .compRatio = 32,	//=(1-1/r)*2^6=(1-1/2)*2^6=32; Q6
+	    .expandThreshold = -2880,	// -45*2^6=-2880;Q6
+	    .expandRatio = -51,	//=(1-1/r)*2^6=(1-1/0.555f)*2^6=-51; Q6
+	    .attackTime = 14459,	//type 1, exp(1/(attack_time * sample_rate))=exp(-1/0.001*8000)*2^14=
+	    .releaseTime = 16046,	//type 1,exp(1/(release_time * sample_rate))=exp(-1/0.006*8000)*2^14=
+	    .makeupGain = 1,	// 10^(old/20)=10^(6/20)=1.9953,Q?	//last gain, fake ,2 be deleted
+	    },
+
+#endif
+
+#if defined(TX_AGC)
+	    .CVT_AGC = {
+	    .bypass             = 0,
+	    .targetLevel       = 3,
+	    .compressionGain   = 6,
+	    .limiterEnable     = 1,
+	    },
+#endif
+
+#if defined(TX_EQ)
+	    .CVT_EQ = {
+	    .bypass = 0,
+	    .gain = 0,
+	    .num = 1,
+	    .params = 
+		    {
+			    {	
+			    	.type = IIR_BIQUARD_QTY,
+			    	.filt = { .design = { 500, 0, 5790 } }
+			    },// can Add more filters
+		    },
+
+	    },
+#endif
+	},
+
+	.speechCfgRx = 
+	{
+
+#if defined(RX_ANS)
+	.CVT_ANS_RX = {
+		.bypass = 1,
+		.mode = 0,
+		.eqBypass = 0,
+		.eqBand[0] = 32767,//20240229	//[0-32767]
+		.eqBand[1] = 32767,
+		.eqBand[2] = 32767,
+		.eqBand[3] = 32767,
+		.eqBand[4] = 32767,
+		.eqBand[5] = 32767,
+		.eqBand[6] = 32767,
+		.eqBand[7] = 32767,
+		.eqBand[8] = 32767,
+		.eqBand[9] = 32767,
+		.eqBand[10] = 32767,
+		.eqBand[11] = 32767,
+		.eqBand[12] = 32767,
+		.eqBand[13] = 32767,
+		.eqBand[14] = 32767,
+		.eqBand[15] = 32767,
+		.eqBand[16] = 32767,
+		.eqBand[17] = 32767,
+		.eqBand[18] = 32767,
+		.eqBand[19] = 32767,
+		.eqBand[20] = 32767,
+		.eqBand[21] = 32767,
+		.eqBand[22] = 32767,
+		.eqBand[23] = 32767,
+		.eqBand[24] = 32767,
+		.eqBand[25] = 32767,
+		.eqBand[26] = 32767,
+		.eqBand[27] = 32767,
+		.eqBand[28] = 32767,
+		.eqBand[29] = 32767,
+		.eqBand[30] = 32767,
+		.eqBand[31] = 32767,
+		},
+#endif
+
+#if defined(RX_DRC)
+
+	    .CVT_DRC_RX = {
+	    .bypass = 0,
+	    //.type = 1,
+	    .compThreshold = -960,	//-15*2^6=960,Q6
+	    .compRatio = 32,	//=(1-1/r)*2^6=(1-1/2)*2^6=32; Q6
+	    .expandThreshold = -2880,	// -45*2^6=-2880;Q6
+	    .expandRatio = -51,	//=(1-1/r)*2^6=(1-1/0.555f)*2^6=-51; Q6
+	    .attackTime = 14459,	//type 1, exp(1/(attack_time * sample_rate))=exp(-1/0.001*8000)*2^14=
+	    .releaseTime = 16046,	//type 1,exp(1/(release_time * sample_rate))=exp(-1/0.006*8000)*2^14=
+	    .makeupGain = 1,	// 10^(old/20)=10^(6/20)=1.9953,Q?	//last gain, fake ,2 be deleted
+	    //.delay = 32,
+	    //.tav = 0,	//fake,2 be deleted
+	    },
+
+#endif
+
+#if defined(RX_AGC)
+	    .CVT_AGC_RX = {
+	    .bypass = 0,
+	    .targetLevel = 3,
+	    .compressionGain = 9,
+	    .limiterEnable = 1,
+	    },
+#endif
+
+#if defined(RX_EQ)
+	    .CVT_EQ_RX = {
+	    .bypass = 0,
+	    .gain = 0,
+	    .num = 1,
+	    .params = {
+		    { 	
+		    	.type = IIR_BIQUARD_QTY,
+		    	.filt = { .design = { 500, 0, 5790 } }
+		    },// can Add more filters
+	    },
+
+	    },
+#endif
+	},
+};
+

+ 56 - 21
components/network/adapter_lwip2/net_lwip2.c

@@ -106,7 +106,7 @@ void net_lwip2_set_netif(uint8_t adapter_index, struct netif *netif) {
 		#endif
 	}
 	if (NULL == prvlwip.dns_client[adapter_index]) {
-		prvlwip.dns_client[adapter_index] = luat_heap_malloc(sizeof(dns_client_t));
+		prvlwip.dns_client[adapter_index] = luat_heap_zalloc(sizeof(dns_client_t));
 		// memset(prvlwip.dns_client[adapter_index], 0, sizeof(dns_client_t));
 		dns_init_client(prvlwip.dns_client[adapter_index]);
 	}
@@ -146,7 +146,7 @@ static int net_lwip2_next_data_cache(void *p, void *u)
 
 static socket_data_t * net_lwip2_create_data_node(uint8_t socket_id, uint8_t *data, uint32_t len, luat_ip_addr_t *remote_ip, uint16_t remote_port)
 {
-	socket_data_t *p = (socket_data_t *)luat_heap_malloc(sizeof(socket_data_t));
+	socket_data_t *p = (socket_data_t *)luat_heap_zalloc(sizeof(socket_data_t));
 	if (p)
 	{
 		memset(p, 0, sizeof(socket_data_t));
@@ -163,7 +163,7 @@ static socket_data_t * net_lwip2_create_data_node(uint8_t socket_id, uint8_t *da
 		p->tag = prvlwip.socket[socket_id].tag;
 		if (data && len)
 		{
-			p->data = luat_heap_malloc(len);
+			p->data = luat_heap_zalloc(len);
 			if (p->data)
 			{
 				memcpy(p->data, data, len);
@@ -228,7 +228,7 @@ static int net_lwip2_rx_data(int socket_id, struct pbuf *p, const ip_addr_t *add
 	socket_data_t *data_p = net_lwip2_create_data_node(socket_id, NULL, 0, addr, port);
 	if (data_p)
 	{
-		data_p->data = luat_heap_malloc(p->tot_len);
+		data_p->data = luat_heap_zalloc(p->tot_len);
 		if (data_p->data)
 		{
 			data_p->len = pbuf_copy_partial(p, data_p->data, p->tot_len, 0);
@@ -949,7 +949,7 @@ static void net_lwip2_task(void *param)
 
 static void platform_send_event(void *p, uint32_t id, uint32_t param1, uint32_t param2, uint32_t param3)
 {
-	OS_EVENT *event = luat_heap_malloc(sizeof(OS_EVENT));
+	OS_EVENT *event = luat_heap_zalloc(sizeof(OS_EVENT));
 	event->ID = id;
 	event->Param1 = param1;
 	event->Param2 = param2;
@@ -965,6 +965,8 @@ static void platform_send_event(void *p, uint32_t id, uint32_t param1, uint32_t
 static void net_lwip2_check_network_ready(uint8_t adapter_index)
 {
 	luat_ip_addr_t addr = {0};
+	dns_client_t *dns_client = prvlwip.dns_client[adapter_index];
+	dhcp_client_info_t* dhcpc = prvlwip.dhcpc[adapter_index];
 	char ip_string[64] = {0};
 	if (prvlwip.lwip_netif[adapter_index] == NULL)
 		return;
@@ -985,22 +987,42 @@ static void net_lwip2_check_network_ready(uint8_t adapter_index)
 	{
 		NET_DBG("network ready %d", adapter_index);
 		uint32_t tmp = adapter_index;
-		if (prvlwip.lwip_netif[adapter_index] != NULL && !ip_addr_isany(&prvlwip.lwip_netif[adapter_index]->gw)) {
-			ip4addr_ntoa_r(&prvlwip.lwip_netif[adapter_index]->gw, ip_string, 32);
-			//NET_DBG("使用网关作为默认DNS服务器 %s", ip_string);
-			net_lwip2_set_dns_server(0, &prvlwip.lwip_netif[adapter_index]->gw, (void*)tmp);
+		luat_ip_addr_t addr = {0};
+		uint8_t dns0_set = 0;
+		uint8_t dns1_set = 0;
+		// LLOGD("开始设置DNS服务器 %d static? %d %d %d %d", adapter_index, dns_client->is_static_dns[0], dns_client->is_static_dns[1], prvlwip.dhcpc[adapter_index] ? prvlwip.dhcpc[adapter_index]->dns_server[0] : 0, prvlwip.dhcpc[adapter_index] ? prvlwip.dhcpc[adapter_index]->dns_server[1] : 0);
+		if (dns_client->is_static_dns[0] == 0) {
+			if (dhcpc && dhcpc->dns_server[0]) {
+				network_set_ip_ipv4(&dns_client->dns_server[0], dhcpc->dns_server[0]);
+				dns0_set = 1;
+				// LLOGD("使用DHCP分配的DNS服务器作为首选DNS服务器");
+			}
+		}
+		else {
+			dns0_set = 1; // 静态DNS服务器, 就不更新了
+		}
+		if (dns_client->is_static_dns[1] == 0) {
+			if (dhcpc && dhcpc->dns_server[1]) {
+				network_set_ip_ipv4(&dns_client->dns_server[1], dhcpc->dns_server[1]);
+				dns1_set = 1;
+				// LLOGD("使用DHCP分配的DNS服务器作为次选DNS服务器");
+			}
+		}
+		else {
+			dns1_set = 1; // 静态DNS服务器, 就不更新了
+		}
+
+		if (dns0_set == 0) {
+			// LLOGD("使用网关作为首选DNS服务器");
+			memcpy(&dns_client->dns_server[0], &prvlwip.lwip_netif[adapter_index]->gw, sizeof(luat_ip_addr_t));
 		}
 		else {
-			//NET_DBG("使用223.5.5.5作为默认DNS服务器");
-			ip4addr_aton("223.5.5.5", &addr);
-			net_lwip2_set_dns_server(0, &addr, (void*)tmp);
-		}
-		ip4addr_aton("114.114.114.114", &addr);
-		net_lwip2_set_dns_server(1, &addr, (void*)tmp);
-		ip4addr_aton("223.5.5.5", &addr);
-		net_lwip2_set_dns_server(2, &addr, (void*)tmp);
-		ip4addr_aton("119.29.29.29", &addr);
-		net_lwip2_set_dns_server(3, &addr, (void*)tmp);
+			// LLOGI("首选DNS服务器 %s", ipaddr_ntoa(&prvlwip.dns_client[adapter_index]->dns_server[0]));
+		}
+		
+		if (dns1_set == 0) {
+			network_set_ip_ipv4(&dns_client->dns_server[1], (114 << 24) | (114 << 16) | (114 << 8) | 114); // 默认DNS服务器
+		}
 		net_lwip2_callback_to_nw_task(adapter_index, EV_NW_STATE, 0, 1, adapter_index);
 	}
 }
@@ -1479,8 +1501,11 @@ static int net_lwip2_set_dns_server(uint8_t server_index, luat_ip_addr_t *ip, vo
 	if (adapter_index >= NW_ADAPTER_INDEX_LWIP_NETIF_QTY) return -1;
 	if (server_index >= MAX_DNS_SERVER) return -1;
 	if (prvlwip.dns_client[adapter_index] == NULL) return -1;
-	prvlwip.dns_client[adapter_index]->dns_server[server_index] = *ip;
+	memcpy(&prvlwip.dns_client[adapter_index]->dns_server[server_index], ip, sizeof(luat_ip_addr_t));
 	prvlwip.dns_client[adapter_index]->is_static_dns[server_index] = 1;
+	char buff[64] = {0};
+	ipaddr_ntoa_r(ip, buff, 64);
+	NET_DBG("设置DNS服务器 id %d index %d ip %s", adapter_index, server_index, buff);
 	return 0;
 }
 
@@ -1497,7 +1522,7 @@ static int net_lwip2_set_static_ip(luat_ip_addr_t *ip, luat_ip_addr_t *submask,
 	uint8_t index = (uint32_t)user_data;
 	if (index >= NW_ADAPTER_INDEX_LWIP_NETIF_QTY) return -1;
 	if (!prvlwip.lwip_netif[index]) return -1;
-	luat_ip_addr_t *p_ip = luat_heap_malloc(sizeof(luat_ip_addr_t) * 4);
+	luat_ip_addr_t *p_ip = luat_heap_zalloc(sizeof(luat_ip_addr_t) * 4);
 	if (p_ip == NULL) {
 		NET_ERR("net_lwip2_set_static_ip malloc fail");
 		return -1;
@@ -1670,3 +1695,13 @@ static ip_addr_t *net_lwip2_get_ip6(uint8_t adapter_index)
 	#endif
 	return NULL;
 }
+
+void net_lwip2_set_dhcp_client(uint8_t adapter_index, dhcp_client_info_t *dhcp_client) {
+	if (adapter_index >= NW_ADAPTER_INDEX_LWIP_NETIF_QTY) {
+		return; // 超范围了
+	}
+	if (prvlwip.dhcpc[adapter_index]) {
+		return;
+	}
+	prvlwip.dhcpc[adapter_index] = dhcp_client;
+}

+ 4 - 0
components/network/adapter_lwip2/net_lwip2.h

@@ -4,6 +4,7 @@
 #include "luat_base.h"
 #include "dns_def.h"
 #include "luat_network_adapter.h"
+#include "dhcp_def.h"
 
 #ifdef LWIP_NUM_SOCKETS
 #if LWIP_NUM_SOCKETS > 16
@@ -46,6 +47,7 @@ typedef struct
 	HANDLE dns_timer[NW_ADAPTER_INDEX_LWIP_NETIF_QTY];
 	uint8_t next_socket_index;
 	HANDLE arp_timer;
+	dhcp_client_info_t *dhcpc[NW_ADAPTER_INDEX_LWIP_NETIF_QTY];
 }net_lwip2_ctrl_struct;
 
 
@@ -62,4 +64,6 @@ struct netif * net_lwip2_get_netif(uint8_t adapter_index);
  */
 void net_lwip2_set_link_state(uint8_t adapter_index, uint8_t updown);
 
+void net_lwip2_set_dhcp_client(uint8_t adapter_index, dhcp_client_info_t *dhcp_client);
+
 #endif

+ 5 - 0
components/network/netdrv/src/luat_netdrv.c

@@ -259,6 +259,7 @@ void luat_netdrv_debug_set(int id, int enable) {
 }
 
 #include "lwip/etharp.h"
+#include "netif/ethernet.h"
 // #include "luat_netdrv_etharp.h"
 extern err_t luat_netdrv_ethernet_input(struct pbuf *p, struct netif *netif);
 err_t luat_netdrv_netif_input_main(struct pbuf *p, struct netif *inp)
@@ -270,7 +271,11 @@ err_t luat_netdrv_netif_input_main(struct pbuf *p, struct netif *inp)
 
 #if LWIP_ETHERNET
   if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
+    #if defined(LUAT_USE_NETDRV_LWIP_ARP)
     return luat_netdrv_ethernet_input(p, inp);
+    #else
+    return ethernet_input(p, inp);
+    #endif /* LUAT_USE_NETDRV_LWIP_ARP */
   } else
 #endif /* LWIP_ETHERNET */
     return ip_input(p, inp);

+ 106 - 0
components/network/rtp/luat_rtp.c

@@ -0,0 +1,106 @@
+#include "luat_rtp.h"
+
+int luat_unpack_rtp_head(const uint32_t *input, uint32_t input_len, rtp_base_head_t *base_head, uint32_t **csrc)
+{
+	if (input_len < 12) return -ERROR_PARAM_OVERFLOW;
+	Buffer_Struct buf = {0};
+	buf.Data = (uint8_t *)input;
+	base_head->byte0 = buf.Data[0];
+	if (base_head->version != 2)
+	{
+		return -ERROR_PARAM_INVALID;
+	}
+	base_head->byte1 = buf.Data[1];
+	buf.Pos = 2;
+	base_head->sn = BytesGetBe16FromBuf(&buf);
+	base_head->time_tamp = BytesGetBe32FromBuf(&buf);
+	base_head->ssrc = BytesGetBe32FromBuf(&buf);
+	if (base_head->csrc_count)
+	{
+		*csrc = (uint32_t *)(&input[3]);
+		uint32_t t = base_head->csrc_count;
+		buf.Pos += t * 4;
+	}
+	else
+	{
+		*csrc = NULL;
+	}
+	if (buf.Pos > input_len)
+	{
+		return -ERROR_BUFFER_FULL;
+	}
+	return buf.Pos;
+}
+
+int luat_unpack_rtp_extern_head(const uint32_t *input, uint32_t input_len, rtp_extern_head_t *extern_head, uint32_t **data)
+{
+	if (input_len < 4) return -ERROR_PARAM_OVERFLOW;
+	Buffer_Struct buf = {0};
+	buf.Data = (uint8_t *)input;
+	extern_head->profile_id = BytesGetBe16FromBuf(&buf);
+	extern_head->length = BytesGetBe16FromBuf(&buf);
+	if (extern_head->length)
+	{
+		*data = (uint32_t *)(&input[1]);
+		uint32_t t = extern_head->length;
+		buf.Pos += t * 4;
+	}
+	else
+	{
+		*data = NULL;
+	}
+	if (buf.Pos > input_len)
+	{
+		return -ERROR_BUFFER_FULL;
+	}
+	return buf.Pos;
+}
+
+int luat_pack_rtp(rtp_base_head_t *base_head, rtp_extern_head_t *extern_head, const void *payload, uint32_t payload_len, uint8_t *output, uint32_t output_max_len)
+{
+	uint32_t t1 = base_head->csrc_count;
+	uint32_t t2 = 0;
+	t1 = t1 * 4 + 12;
+	if (extern_head)
+	{
+		t2 = extern_head->length;
+		t1 += t2 * 4 + 4;
+	}
+	if ((t1 + payload_len) > output_max_len)
+	{
+		return -ERROR_BUFFER_FULL;
+	}
+	Buffer_Struct buf = {0};
+	buf.Data = output;
+	buf.MaxLen = output_max_len;
+	if (extern_head)
+	{
+		base_head->extension = 1;
+	}
+	BytesPut8ToBuf(&buf, base_head->byte0);
+	BytesPut8ToBuf(&buf, base_head->byte1);
+	BytesPutBe16ToBuf(&buf, base_head->sn);
+	BytesPutBe32ToBuf(&buf, base_head->time_tamp);
+	BytesPutBe32ToBuf(&buf, base_head->ssrc);
+	if (base_head->csrc_count)
+	{
+		for(int i = 0; i < base_head->csrc_count; i++)
+		{
+			BytesPutBe32ToBuf(&buf, base_head->csrc[i]);
+		}
+	}
+	if (extern_head)
+	{
+		BytesPutBe16ToBuf(&buf, extern_head->profile_id);
+		BytesPutBe16ToBuf(&buf, extern_head->length);
+		if (extern_head->length)
+		{
+			for(int i = 0; i < extern_head->length; i++)
+			{
+				BytesPutBe32ToBuf(&buf, extern_head->data[i]);
+			}
+		}
+	}
+	OS_BufferWrite(&buf, payload, payload_len);
+	return buf.Pos;
+}

+ 73 - 0
components/network/rtp/luat_rtp.h

@@ -0,0 +1,73 @@
+#ifndef __LUAT_RTP_H__
+#define __LUAT_RTP_H__
+#include "luat_base.h"
+#if defined(__SOC_BSP__) || defined(LUAT_EC7XX_CSDK) || defined(CHIP_EC618) || defined(__AIR105_BSP__) || defined(CONFIG_SOC_8910) || defined(CONFIG_SOC_8850)
+#include "bsp_common.h"
+#endif
+#ifndef __BSP_COMMON_H__
+#include "c_common.h"
+#endif
+typedef struct
+{
+	union
+	{
+		struct
+		{
+			uint8_t version:2;
+			uint8_t padding:1;
+			uint8_t extension:1;
+			uint8_t csrc_count:4;
+		};
+		uint8_t byte0;
+	};
+	union
+	{
+		struct
+		{
+			uint8_t maker:1;
+			uint8_t payload_type:7;
+		};
+		uint8_t byte1;
+	};
+	uint16_t sn;
+	uint32_t time_tamp;
+	uint32_t ssrc;
+	uint32_t csrc[];
+}rtp_base_head_t;
+
+typedef struct
+{
+	uint16_t profile_id;
+	uint16_t length;
+	uint32_t data[];
+}rtp_extern_head_t;
+/**
+ * 从输入数据解析出RTP包头
+ * @param input,输入数据首地址,必须是32bit对齐
+ * @param input_len,输入数据长度
+ * @param base_head,输出RTP包头
+ * @param ccrc,返回ccrc数据的起始位置,如果有就是input+12,否则返回NULL
+ * @return 成功返回已处理的字节数量,失败则<=0
+ */
+int luat_unpack_rtp_head(const uint32_t *input, uint32_t input_len, rtp_base_head_t *base_head, uint32_t **csrc);
+/**
+ * 从输入数据解析出RTP扩展包头
+ * @param input,输入数据首地址,必须是32bit对齐,不能包含包头
+ * @param input_len,输入数据长度
+ * @param extern_head,输出RTP扩展包头
+ * @param data,返回扩展数据的起始位置,如果有就是input+4,否则返回NULL
+ * @return 成功返回已处理的字节数量,失败则<0
+ */
+int luat_unpack_rtp_extern_head(const uint32_t *input, uint32_t input_len, rtp_extern_head_t *extern_head, uint32_t **data);
+/**
+ * 打包RTP数据
+ * @param base_head,RTP包头数据,连同CSRC,如果有的话
+ * @param extern_head,RTP扩展包头数据
+ * @param payload,有效数据
+ * @param payload_len,有效数据长度
+ * @param output,输出数据缓存
+ * @param output_max_len,缓存区长度,必须有足够的长度,否则会失败
+ * @return 成功返回最终数据长度,失败<=0
+ */
+int luat_pack_rtp(rtp_base_head_t *base_head, rtp_extern_head_t *extern_head, const void *payload, uint32_t payload_len, uint8_t *output, uint32_t output_max_len);
+#endif

+ 4 - 8
components/network/ulwip/binding/luat_lib_ulwip.c

@@ -93,15 +93,11 @@ int ulwip_netif_ip_event(ulwip_ctx_t* ctx) {
     ready_now &= netif_is_up(netif);
     luat_ip_addr_t ip = {0};
 
-    net_lwip2_set_link_state(ctx->adapter_index, ready_now);
-    if (ctx->dhcp_client->dns_server[0]) {
-        network_set_ip_ipv4(&ip, ctx->dhcp_client->dns_server[0]);
-        network_set_dns_server(ctx->adapter_index, 0, &ip);
-    }
-    if (ctx->dhcp_client->dns_server[1]) {
-        network_set_ip_ipv4(&ip, ctx->dhcp_client->dns_server[1]);
-        network_set_dns_server(ctx->adapter_index, 1, &ip);
+    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) {
         return 0;
     }

+ 15 - 8
components/tp/luat_lib_tp.c

@@ -94,15 +94,22 @@ int l_tp_callback(luat_tp_config_t* luat_tp_config, luat_tp_data_t* luat_tp_data
 /*
 触摸初始化
 @api tp.init(tp, args)
-@string tp类型,当前支持:<br>gt911 <br>gt9157 <br>jd9261t
-@table 附加参数,与具体设备有关:<br>port 驱动方式<br>port:硬件i2c端口,例如0,1,2...如果为软件i2c对象<br>pin_rst:复位引脚<br>pin_int:中断引脚<br>w:宽度<br>h:高度
-@function 回调函数, 回调参数:tp_device,tp_data:触摸数据,内部为多个触摸点数据表,每个表中有参数event:触摸事件 x:x坐标 y:y坐标 
-@return tp_device
-
+@string 触摸芯片型号,当前支持:<br>gt911 <br>gt9157 <br>jd9261t
+@table 附加参数,与具体设备有关:<br>port 驱动方式<br>port:硬件i2c端口,例如0,1,2...如果为软件i2c对象<br>pin_rst:复位引脚<br>pin_int:中断引脚<br>w:宽度(可选,默认会寻找已初始化的lcd的数据)<br>h:高度(可选,默认会寻找已初始化的lcd的数据)
+@function 回调函数(可选,使用lvgl时可不传入,lvgl会自动处理), 回调参数: <br>tp_device: userdata tp.init返回的触摸设备对象 <br>tp_data: table 触摸数据,内部为多个触摸点数据的表,每个表中包括参数有: event: number 触摸事件,见文档上方的触摸事件常量 x: number 触摸位置x坐标 y: number 触摸位置y坐标 
+@return userdata tp_device:触摸设备对象
 @usage
-// tp.init("gt911",{port=0,pin_rst = 22,pin_int = 23,w = 320,h = 480})
-// local softI2C = i2c.createSoft(20, 21)
-// tp.init("gt911",{port=softI2C,pin_rst = 22,pin_int = 23,w = 320,h = 480})
+    local function tp_callBack(tp_device, tp_data)
+        log.info("TP", tp_data[1].x, tp_data[1].y, tp_data[1].event)
+        sys.publish("TP", tp_device, tp_data)
+    end
+
+    -- 硬件i2c驱动
+    --tp.init("gt911",{port=0,pin_rst = 22,pin_int = 23},tp_callBack)
+
+    -- 软件i2c驱动
+    --local softI2C = i2c.createSoft(20, 21)
+    --tp.init("gt911",{port=softI2C,pin_rst = 22,pin_int = 23},tp_callBack)
 */
 
 static int l_tp_init(lua_State* L){

+ 0 - 0
module/Air724UG/.keep


+ 0 - 0
module/Air780EHM_Air780EHV_Air780EGH/demo/lowpower/low_power_dissipation.lua → module/Air780EHM_Air780EHV_Air780EGH/demo/lowpower/lowpower_dissipation.lua


+ 2 - 2
module/Air780EHM_Air780EHV_Air780EGH/demo/lowpower/main.lua

@@ -8,8 +8,8 @@ _G.sysplus = require("sysplus")
 log.style(1)
 
 --require "normal" --正常模式
---require "low_power_dissipation" --低功耗模式
- require "ultra_low_power" --超低功耗模式(PSM+模式)
+--require "lowpower_dissipation" --低功耗模式
+require "ultra_low_power" --超低功耗模式(PSM+模式)
 
 -- 用户代码已结束---------------------------------------------
 -- 结尾总是这一句

+ 0 - 0
module/Air780EPM/demo/lowpower/low_power_dissipation.lua → module/Air780EPM/demo/lowpower/lowpower_dissipation.lua


+ 3 - 3
module/Air780EPM/demo/lowpower/main.lua

@@ -7,9 +7,9 @@ _G.sys = require("sys")
 _G.sysplus = require("sysplus")
 log.style(1)
 
---require "normal" --正常模式
---require "low_power_dissipation" --低功耗模式
- require "ultra_low_power" --超低功耗模式(PSM+模式)
+-- require "normal" --正常模式
+-- require "lowpower_dissipation" --低功耗模式
+require "ultra_low_power" --超低功耗模式(PSM+模式)
 
 -- 用户代码已结束---------------------------------------------
 -- 结尾总是这一句

+ 5 - 27
module/Air8000/README.md

@@ -1,10 +1,10 @@
-# LuatOS-Air780EPM
+# LuatOS-Air8000
 
 ## 介绍
 
-本代码库 是 合宙 Air780EPM 模组的代码中心, 包括演示代码demo, 案例代码project等
+本代码库 是 合宙 Air8000 模组的代码中心, 包括演示代码demo, 案例代码project等
 
-如需查阅文档, 请访问合宙文档中心的[Air780EPM模块文档中心](https://docs.openluat.com/air780epm/)
+如需查阅文档, 请访问合宙文档中心的[Air8000模块文档中心](https://docs.openluat.com/air8000/)
 
 ## 目录说明
 
@@ -13,25 +13,9 @@
 
 ## 固件说明
 
-1. Air780EPM 只有1种32位固件,没有64位固件;
+1. Air8000 当前有22种固件,32位和64位各11种;
 
-2. Air780EHM,Air780EHV,Air780EGH 当前有22种固件,32位和64位各11种;
-
-3. 关于差分升级的相关说明;
-
-差分升级只能在同类固件之间进行,
-
-固件1 只能差分升级为固件1,
-
-固件2 只能差分升级为固件2,
-
-固件X 只能差分升级为固件X。## 固件说明
-
-1. Air780EPM 只有1种32位固件,没有64位固件;
-
-2. Air780EHM,Air780EHV,Air780EGH 当前有22种固件,32位和64位各11种;
-
-3. 关于差分升级的相关说明;
+2. 关于差分升级的相关说明;
 
 差分升级只能在同类固件之间进行,
 
@@ -41,12 +25,6 @@
 
 固件X 只能差分升级为固件X。
 
-![输入图片说明](LuatOS%E5%A4%9A%E5%9B%BA%E4%BB%B6%E7%AD%96%E7%95%A5%E8%AF%B4%E6%98%8E.png)
-
-![输入图片说明](LuatOS%E5%A4%9A%E5%9B%BA%E4%BB%B6%E5%8A%9F%E8%83%BD%E5%8C%BA%E5%88%AB.png)
-
-![输入图片说明](LuatOS%E6%89%A9%E5%B1%95%E5%BA%93%E7%AE%80%E8%A6%81%E8%AF%B4%E6%98%8E.png)
-
 ## demo使用说明
 
 [**JT808**]:本demo演示使用string.pack与unpack函数,实现JT808 终端注册协议数据生成与解析,适用于车辆定位和监控系统。

+ 1 - 1
module/Air8000/demo/luatos_framework/hello_luatos/hello_luatos.lua

@@ -7,7 +7,7 @@
 @usage
 本文件为hello_luatos应用功能模块,核心业务逻辑为:
 1、创建一个task;
-2、在task中的任务处理函数中,每隔一秒钟在日志中输出一次Hello, LuatOS;
+2、在task的任务处理函数中,每隔一秒钟通过日志输出一次Hello, LuatOS;
 
 本文件没有对外接口,直接在main.lua中require "hello_luatos"就可以加载运行;
 ]]

+ 1 - 1
module/Air8000/demo/luatos_framework/hello_luatos/main.lua

@@ -6,7 +6,7 @@
 @author  朱天华
 @usage
 本demo演示的核心功能为:
-每隔一秒钟在日志中输出一次Hello, LuatOS
+每隔一秒钟通过日志输出一次Hello, LuatOS
 更多说明参考本目录下的readme.md文件
 ]]
 

+ 1 - 1
module/Air8000/demo/luatos_framework/hello_luatos/readme.md

@@ -3,7 +3,7 @@
 
 1、创建一个task;
 
-2、在task中的任务处理函数中,每隔一秒钟在日志中输出一次Hello, LuatOS;
+2、在task中的任务处理函数中,每隔一秒钟通过日志输出一次Hello, LuatOS;
 
 
 ## 演示硬件环境

+ 1 - 1
module/Air8101/demo/libnetif/libnetif.lua

@@ -254,7 +254,7 @@ function libnetif.set_priority_order(networkConfigs)
         if config.LWIP_GP then
             --开启4G
             table.insert(new_priority, socket.LWIP_GP)
-            available[socket.LWIP_GP] = connection_states.CONNECTING
+            available[socket.LWIP_GP] = connection_states.OPENED
         end
     end
 

+ 1 - 1
module/Air8101/demo/lowpower/low_power.lua

@@ -45,7 +45,7 @@ end
 -- 定义一个发送心跳信息功能函数。
 function send_tcp_heartbeat_func()
     -- 通过网卡状态判断WIFI是否连接成功,WIFI连接成功后再运行消息发送。
-    while not socket.adapter(socket.LWIP_STA) do
+    while not socket.adapter(socket.dft()) do
         -- 在此处阻塞等待WIFI连接成功的消息"IP_READY",避免联网过快,丢失了"IP_READY"信息而导致一直被卡住。
         -- 或者等待30秒超时退出阻塞等待状态
         log.warn("tcp_client_main_task_func", "wait IP_READY")

+ 2 - 2
module/Air8101/demo/lowpower/main.lua

@@ -66,8 +66,8 @@ end
 
 
 --选择需要体验的功耗模式,注释另外两个代码即可!快捷键Ctrl + /
--- require "normal"
-require "module.Air8000.demo.lowpower.low_power_dissipation"
+-- require "normal_power"
+require "low_power"
 -- require "psm+_power"
 
 

+ 1 - 1
module/Air8101/demo/lowpower/normal.lua → module/Air8101/demo/lowpower/normal_power.lua

@@ -42,7 +42,7 @@ end
 -- 定义一个发送心跳信息功能函数。
 function send_tcp_heartbeat_func()
     -- 通过网卡状态判断WIFI是否连接成功,WIFI连接成功后再运行消息发送。
-    while not socket.adapter(socket.LWIP_STA) do
+    while not socket.adapter(socket.dft()) do
         -- 在此处阻塞等待WIFI连接成功的消息"IP_READY",避免联网过快,丢失了"IP_READY"信息而导致一直被卡住。
         -- 或者等待30秒超时退出阻塞等待状态
         log.warn("tcp_client_main_task_func", "wait IP_READY")

+ 3 - 3
module/Air8101/demo/lowpower/psm+_power.lua

@@ -25,7 +25,7 @@ local heart_data = string.rep("1234567890", 3) -- 心跳包数据内容,可自
 function send_result(a1, b1)
     log.info("发送状态 :", a1, b1)
     -- 获取tcp_client_sender发送状态后推送信息解除psm_power_func()的sys.waitUntil挂起状态。并返回发送状态a1。
-    sys.publish("send_success", a1, b1)
+    sys.publish("send_result", a1, b1)
 end
 
 -- 定义一个表,将回调函数放在表中,因为tcp_client_sender处理回调函数时考虑到带参执行,所以要将函数和参数都放在表里
@@ -43,7 +43,7 @@ function psm_power_func()
         if tcp_mode then
             -- 导入tcp客户端收发功能模块,运行tcp客户端连接,自动处理TCP收发消息。
             require "tcp_client_main"
-            while not socket.adapter(socket.LWIP_STA) do
+            while not socket.adapter(socket.dft()) do
                 log.warn("tcp_client_main_task_func", "wait IP_READY")
                 -- 在此处阻塞等待WIFI连接成功的消息"IP_READY",避免联网过快,丢失了"IP_READY"信息而导致一直被卡住。
                 -- 或者等待30秒超时退出阻塞等待状态
@@ -55,7 +55,7 @@ function psm_power_func()
             -- 第一个值为sys.waitUntil的结果,是否信息唤醒。
             -- 第二个值为发送是否成功
             -- 第三个值为回调函数带的参数
-            local _, a, b = sys.waitUntil("send_success")
+            local _, a, b = sys.waitUntil("send_result")
             -- 通过发送状态判断发送是否成功,并打印变量 a 的赋值
             if a then
                 log.info("发送成功!", a)

+ 1 - 1
module/Air8101/demo/lowpower/tcp_client_main.lua

@@ -43,7 +43,7 @@ local function tcp_client_main_task_func()
 
     while true do
         -- 如果WIFI还没有连接成功,一直在这里循环等待
-        while not socket.adapter(socket.LWIP_STA) do
+        while not socket.adapter(socket.dft()) do
             log.warn("tcp_client_main_task_func", "wait IP_READY")
             -- 在此处阻塞等待WIFI连接成功的消息"IP_READY"
             -- 或者等待30秒超时退出阻塞等待状态

+ 2 - 2
module/Air8101/demo/lowpower/wifi_app.lua

@@ -52,8 +52,8 @@ wlan.connect(ssid, password, 1)
 
 -- WIFI联网成功(做为STATION成功连接AP,并且获取到了IP地址)后,内核固件会产生一个"IP_READY"消息
 -- 各个功能模块可以订阅"IP_READY"消息实时处理WIFI联网成功的事件
--- 也可以在任何时刻调用socket.adapter(socket.LWIP_STA)来获取WIFI网络是否连接成功
+-- 也可以在任何时刻调用socket.adapter(socket.dft())来获取WIFI网络是否连接成功
 
 -- WIFI断网后,内核固件会产生一个"IP_LOSE"消息
 -- 各个功能模块可以订阅"IP_LOSE"消息实时处理WIFI断网的事件
--- 也可以在任何时刻调用socket.adapter(socket.LWIP_STA)来获取WIFI网络是否连接成功
+-- 也可以在任何时刻调用socket.adapter(socket.dft())来获取WIFI网络是否连接成功

+ 1 - 1
module/Air8101/demo/socket/client/long_connection/sntp_app.lua

@@ -18,7 +18,7 @@ local function sntp_task_func()
 
     while true do
         -- 如果WIFI还没有连接成功,一直在这里循环等待
-        while not socket.adapter(socket.LWIP_STA) do
+        while not socket.adapter(socket.dft()) do
             log.warn("sntp_task_func", "wait IP_READY")
             -- 在此处阻塞等待WIFI连接成功的消息"IP_READY"
             -- 或者等待30秒超时退出阻塞等待状态

+ 1 - 1
module/Air8101/demo/socket/client/long_connection/tcp_client_main.lua

@@ -43,7 +43,7 @@ local function tcp_client_main_task_func()
 
     while true do
         -- 如果WIFI还没有连接成功,一直在这里循环等待
-        while not socket.adapter(socket.LWIP_STA) do
+        while not socket.adapter(socket.dft()) do
             log.warn("tcp_client_main_task_func", "wait IP_READY")
             -- 在此处阻塞等待WIFI连接成功的消息"IP_READY"
             -- 或者等待30秒超时退出阻塞等待状态

+ 1 - 1
module/Air8101/demo/socket/client/long_connection/tcp_ssl_ca_main.lua

@@ -50,7 +50,7 @@ local function tcp_ssl_ca_main_task_func()
 
     while true do
         -- 如果WIFI还没有连接成功,一直在这里循环等待
-        while not socket.adapter(socket.LWIP_STA) do
+        while not socket.adapter(socket.dft()) do
             log.warn("tcp_ssl_ca_main_task_func", "wait IP_READY")
             -- 在此处阻塞等待WIFI连接成功的消息"IP_READY"
             -- 或者等待30秒超时退出阻塞等待状态

+ 1 - 1
module/Air8101/demo/socket/client/long_connection/tcp_ssl_main.lua

@@ -43,7 +43,7 @@ local function tcp_ssl_main_task_func()
 
     while true do
         -- 如果WIFI还没有连接成功,一直在这里循环等待
-        while not socket.adapter(socket.LWIP_STA) do
+        while not socket.adapter(socket.dft()) do
             log.warn("tcp_ssl_main_task_func", "wait IP_READY")
             -- 在此处阻塞等待WIFI连接成功的消息"IP_READY"
             -- 或者等待30秒超时退出阻塞等待状态

+ 1 - 1
module/Air8101/demo/socket/client/long_connection/udp_client_main.lua

@@ -43,7 +43,7 @@ local function udp_client_main_task_func()
 
     while true do
         -- 如果WIFI还没有连接成功,一直在这里循环等待
-        while not socket.adapter(socket.LWIP_STA) do
+        while not socket.adapter(socket.dft()) do
             log.warn("udp_client_main_task_func", "wait IP_READY")
             -- 在此处阻塞等待WIFI连接成功的消息"IP_READY"
             -- 或者等待30秒超时退出阻塞等待状态

+ 2 - 2
module/Air8101/demo/socket/client/long_connection/wifi_app.lua

@@ -41,8 +41,8 @@ wlan.connect("茶室-降功耗,找合宙!", "Air123456", 1)
 
 --WIFI联网成功(做为STATION成功连接AP,并且获取到了IP地址)后,内核固件会产生一个"IP_READY"消息
 --各个功能模块可以订阅"IP_READY"消息实时处理WIFI联网成功的事件
---也可以在任何时刻调用socket.adapter(socket.LWIP_STA)来获取WIFI网络是否连接成功
+--也可以在任何时刻调用socket.adapter(socket.dft())来获取WIFI网络是否连接成功
 
 --WIFI断网后,内核固件会产生一个"IP_LOSE"消息
 --各个功能模块可以订阅"IP_LOSE"消息实时处理WIFI断网的事件
---也可以在任何时刻调用socket.adapter(socket.LWIP_STA)来获取WIFI网络是否连接成功
+--也可以在任何时刻调用socket.adapter(socket.dft())来获取WIFI网络是否连接成功

+ 2 - 2
module/Air8101/project/5inch_800x480_dev_board/app/http_app.lua

@@ -39,9 +39,9 @@ local function http_upload_photo_task_func()
         end
 
         --检查WIFI连接状态
-        log.info("http_upload_photo_task_func", "socket.adapter(socket.LWIP_STA)", socket.adapter(socket.LWIP_STA))
+        log.info("http_upload_photo_task_func", "socket.adapter(socket.dft())", socket.adapter(socket.dft()))
         --如果WIFI还没有连接成功
-        if not socket.adapter(socket.LWIP_STA) then
+        if not socket.adapter(socket.dft()) then
             --在此处阻塞等待WIFI连接成功的消息"IP_READY"
             --或者等待30秒超时退出阻塞等待状态
             --如果没有等到"IP_READY"消息,关闭摄像头,并且直接退出这个函数

+ 3 - 3
module/Air8101/project/5inch_800x480_dev_board/app/wifi_app.lua

@@ -17,7 +17,7 @@ end
 local function wifi_net_led_task_func()
     local led_set_func = gpio.setup(WIFI_NET_LED_GPIO_ID, 0)
     while true do
-        if socket.adapter(socket.LWIP_STA) then
+        if socket.adapter(socket.dft()) then
             led_set_func(1)
             sys.wait(1000)
         else
@@ -52,11 +52,11 @@ wlan.init()
 
 --WIFI联网成功(做为STATION成功连接AP,并且获取到了IP地址)后,内核固件会产生一个"IP_READY"消息
 --各个功能模块可以订阅"IP_READY"消息实时处理WIFI联网成功的事件
---也可以在任何时刻调用socket.adapter(socket.LWIP_STA)来获取WIFI网络是否连接成功
+--也可以在任何时刻调用socket.adapter(socket.dft())来获取WIFI网络是否连接成功
 
 --WIFI断网后,内核固件会产生一个"IP_LOSE"消息
 --各个功能模块可以订阅"IP_LOSE"消息实时处理WIFI断网的事件
---也可以在任何时刻调用socket.adapter(socket.LWIP_STA)来获取WIFI网络是否连接成功
+--也可以在任何时刻调用socket.adapter(socket.dft())来获取WIFI网络是否连接成功
 wlan.connect("茶室-降功耗,找合宙!", "Air123456", 1)
 -- wlan.connect("ChinaNet-2aWX", "zzij6udd", 1)
 

+ 5 - 4
module/Air8101/project/5inch_800x480_dev_board/ui/welcome_win.lua

@@ -28,6 +28,7 @@ local function welcome_win_task_func()
     local font_zoom_interval = 5
 
     lcd.setColor(lcd_device.bg_color, lcd_device.fg_color)    
+    
 
     for i=1,font_zoom_cnt do
         -- 清屏
@@ -39,9 +40,9 @@ local function welcome_win_task_func()
         -- 第三个参数固定为4
         -- 第四个参数为显示的左上角x坐标
         -- 第五个参数为显示的左上角y坐标
-        lcd.drawGtfontUtf8(first_line_text,
+        lcd.drawGtfontUtf8Gray(first_line_text,
             font_max_size-(i-1)*font_sub_size,
-            -- 4,
+            4,
             math.floor(central_pos_x-4*(font_max_size-(i-1)*font_sub_size)/2),
             central_pos_y-vertical_space-(font_max_size-(i-1)*font_sub_size))
 
@@ -52,9 +53,9 @@ local function welcome_win_task_func()
         -- 第三个参数固定为4
         -- 第四个参数为显示的左上角x坐标
         -- 第五个参数为显示的左上角y坐标
-        lcd.drawGtfontUtf8(second_line_text,
+        lcd.drawGtfontUtf8Gray(second_line_text,
             font_max_size-(i-1)*font_sub_size,
-            -- 4,
+            4,
             math.floor(central_pos_x-5*(font_max_size-(i-1)*font_sub_size)/2),
             central_pos_y+vertical_space)
 

+ 0 - 170
module/Air8101/project/5inch_box_dev_board/AirCAMERA_1030.lua

@@ -1,170 +0,0 @@
-
-local AirCAMERA_1030 =
-{
-    id = camera.USB,
-    -- capture_photo_buff:拍照使用的zbuff缓冲区
-    -- opend:是否已经成功打开
-}
-
--- 摄像头事件回调函数
-local function camera_scan_cbfunc(id, str)
-    log.info("camera_scan_cbfunc", id, str)
-    --str为string类型时,表示扫码模式下扫码结果的回调
-    --str为扫码识别后的解码字符串
-    if type(str) == 'string' then
-        log.info("scan code result", str)
-    --str为false时,表示摄像头没有正常工作
-    elseif str == false then
-        log.error("no data")
-    --str为number类型时,表示拍摄到的照片字节大小
-    else
-        log.info("capture photo data", str)
-        sys.publish("AirCAMERA_1030_CAPTURE_IND", true)
-    end
-end
-
-
-
---打开AirCAMERA_1030摄像头;
-
---usb_port_id:number类型;
---       表示USB端口号,当存在USB HUB时,才有意义,如果没有USB HUB,使用1即可;
---       取值范围:1到4;
---       如果没有传入此参数,则默认为1;
---width:number类型;
---       表示摄像头拍照时的宽度,单位为像素;
---       取值范围:大于0,并且和height的乘积不能超过1280*720;
---       如果没有传入此参数,则默认为1280;
---height:number类型;
---       表示摄像头拍照时的宽度,单位为像素;
---       取值范围:大于0,并且和width的乘积不能超过1280*720;
---       如果没有传入此参数,则默认为720;
-
---返回值:成功返回true,失败返回false
-function AirCAMERA_1030.open(usb_port_id, width, height)
-    --如果没有传入参数,usb_port_id,width和height都使用默认值
-    usb_port_id = usb_port_id or 1
-    width = width or 1280
-    height = height or 720
-
-    --判断usb_port_id参数的合法性
-    if not (usb_port_id>=1 and usb_port_id<=4) then
-        log.error("AirCAMERA_1030.open error", "invalid usb_port_id", usb_port_id)
-        return false
-    end
-    
-    --判断width和height参数的合法性
-    if width<0 or height<0 or width*height>1280*720 then
-        log.error("AirCAMERA_1030.open error", "invalid width or height", width, height)
-        return false
-    end
-
-    --如果没有分配过存储照片的内存数据,此处申请200KB的zbuff内存空间
-    --如果拍照过程中,发现200KB的空间不够使用,会自动扩充空间
-    if AirCAMERA_1030.capture_photo_buff==nil then
-        AirCAMERA_1030.capture_photo_buff = zbuff.create(200 * 1024, 0, zbuff.HEAP_PSRAM)
-        if AirCAMERA_1030.capture_photo_buff == nil then
-            log.error("AirCAMERA_1030.open error", "malloc mem fail")
-            return false
-        end
-    end
-
-    --初始化摄像头
-    if not camera.init({id = AirCAMERA_1030.id, sensor_width = width, sensor_height = height, usb_port = usb_port_id}) then
-        log.error("AirCAMERA_1030.open error", "camera.init fail")
-        AirCAMERA_1030.capture_photo_buff:free()
-        AirCAMERA_1030.capture_photo_buff = nil
-        return false
-    end
-
-    --注册摄像头事件回调函数camera_scan_cbfunc
-    --摄像头的异步事件都会通过回调函数通知结果
-    camera.on(AirCAMERA_1030.id, "scanned", camera_scan_cbfunc)
-
-    --设置摄像头已经成功打开的标志
-    AirCAMERA_1030.opend = true
-
-    return true
-end
-
-
---使用AirCAMERA_1030摄像头拍照;必须在task中使用
-
---返回值:成功返回照片的zbuff内存数据,失败返回false
-function AirCAMERA_1030.capture()
-
-    --检查是否运行在task中
-    local co, is_main = coroutine.running()
-    -- Lua 5.1: 直接返回协程(主协程返回 nil)
-    -- Lua 5.2+: 返回协程和是否是主协程
-    if type(co) == "thread" and is_main then
-        log.error("AirCAMERA_1030.capture error", "must in task", type(co) == "thread", is_main)
-        return false
-    end
-    if co == nil then
-        log.error("AirCAMERA_1030.capture error", "must in task", co)
-        return false
-    end
-
-    --如果摄像头没有打开
-    if not AirCAMERA_1030.opend then
-        log.error("AirCAMERA_1030.capture error", "camera isn't opend")
-        return false
-    end
-
-    if not camera.start(AirCAMERA_1030.id) then
-        log.error("AirCAMERA_1030.capture error", "camera.start fail")
-        return false
-    end
-
-    --启动拍照动作,拍照质量90%,如果拍照成功,照片数据存储到AirCAMERA_1030.capture_photo_buff的zbuff内存中
-    if not camera.capture(AirCAMERA_1030.id, AirCAMERA_1030.capture_photo_buff, 1) then
-        log.error("AirCAMERA_1030.capture error", "camera.capture sync fail")
-        return false
-    end
-    
-    --阻塞等待拍照结果,如果5秒钟没有等到结果,超时失败退出阻塞等待状态
-    result = sys.waitUntil("AirCAMERA_1030_CAPTURE_IND", 5000)
-    --打印拍摄的照片字节大小
-    log.info("AirCAMERA_1030.capture", "photo size", AirCAMERA_1030.capture_photo_buff:used())
-    if not result then
-        log.error("AirCAMERA_1030.capture error", "camera.capture async fail")
-        return false
-    end
-
-    --停止拍照
-    camera.stop(AirCAMERA_1030.id)
-
-    --返回存储照片数据的zbuff内存
-    return AirCAMERA_1030.capture_photo_buff
-end
-
-
---关闭AirCAMERA_1030摄像头
-
---返回值:成功返回true,失败返回false
-function AirCAMERA_1030.close()    
-    --如果摄像头还没有打开,直接返回成功
-    if not AirCAMERA_1030.opend then
-        log.info("AirCAMERA_1030.close ok", "no open, needn't close")
-        return true
-    end
-
-    --关闭摄像头
-    camera.close(AirCAMERA_1030.id)
-
-    --释放存储照片数据的zbuff内存
-    if AirCAMERA_1030.capture_photo_buff ~= nil then
-        AirCAMERA_1030.capture_photo_buff:free()
-        AirCAMERA_1030.capture_photo_buff = nil
-    end
-
-    --复位摄像头已经成功打开的标志
-    AirCAMERA_1030.opend = false
-
-    return true
-end
-
-
-return AirCAMERA_1030
-

+ 0 - 37
module/Air8101/project/5inch_box_dev_board/AirFONTS_1000.lua

@@ -1,37 +0,0 @@
-local AirFONTS_1000 = {}
-
---初始化AirFONTS_1000的SPI配置
---AirFONTS_1000通过SPI接口(SCK CS MOSI MISO)和主控相连
---主控设备为SPI主设备,AirFONTS_1000为SPI从设备
-
---spi_id:number类型;
---     表示主设备的SPI ID;
---     取值范围:主控产品上有效的SPI ID值,例如Air8101上的取值范围为0和1;
---     如果没有传入此参数或者传入了nil,则使用默认值1;
---spi_cs:number类型;
---     表示cs引脚的GPIO ID;
---     取值范围:主控产品上有效的GPIO ID值,例如Air8101上的取值范围为0到9,12,14到55;
---     如果没有传入此参数或者传入了nil,则使用默认值3;
-
---返回值:成功返回true,失败返回false
-function AirFONTS_1000.init(spi_id, spi_cs)
-    --创建一个SPI设备对象
-    AirFONTS_1000.spi_gtfont = spi.deviceSetup(spi_id or 1, spi_cs or 3, 0, 0, 8, 20*1000*1000, spi.MSB, 1, 0)
-
-    --检查SPI设备对象是否创建成功
-    if type(AirFONTS_1000.spi_gtfont) ~= "userdata" then
-        log.error("AirFONTS_1000.init", "spi.deviceSetup error", type(AirFONTS_1000.spi_gtfont))
-        return false
-    end
-
-    --初始化矢量字库
-    if not gtfont.init(AirFONTS_1000.spi_gtfont) then
-        log.error("AirFONTS_1000.init", "gtfont.init error")
-        return false
-    end
-
-    return true
-end
-
-
-return AirFONTS_1000

+ 0 - 95
module/Air8101/project/5inch_box_dev_board/AirLCD_1020.lua

@@ -1,95 +0,0 @@
-local AirLCD_1020 = 
-{
-    -- i2c_soft_device = , --触摸面板使用的软件I2C对象,必须使用全局变量存储,不能使用local类型的局部变量
-    -- tp_device = , --触摸面板设备
-}
-
---AirLCD_1020显示屏的分辨率为800*480
-local WIDTH, HEIGHT = 800, 480
-
-
---初始化AirLCD_1020的LCD配置
-
---返回值:nil
-function AirLCD_1020.init_lcd()
-    -- st7265
-    lcd.init("custom",
-        {port = lcd.RGB, hbp = 8, hspw = 4, hfp = 8, vbp = 16, vspw = 4, vfp = 16,
-        bus_speed = 30*1000*1000, direction = 0, w = WIDTH, h = HEIGHT, xoffset = 0, yoffset = 0})
-end
-
---关闭AirLCD_1020的LCD
-
---返回值:nil
-function AirLCD_1020.close_lcd()
-    lcd.close()
-end
-
-
---初始化AirLCD_1020的TP配置
---无论使用硬件i2c还是软件io模拟i2c,都是传入引脚的GPIO ID
-
---int:number类型;
---     表示中断引脚GPIO ID;
---     取值范围:主控产品(例如Air8101)上有效的GPIO ID值;
---     如果没有传入此参数或者传入了nil,则使用默认值7;
---rst:number类型;
---     表示复位控制引脚GPIO ID;
---     取值范围:主控产品(例如Air8101)上有效的GPIO ID值;
---     如果没有传入此参数或者传入了nil,则使用默认值28;
---sda:number类型;
---     表示数据引脚GPIO ID;
---     取值范围:主控产品(例如Air8101)上有效的GPIO ID值;
---     如果没有传入此参数或者传入了nil,则使用默认值1;
---scl:number类型;
---     表示时钟引脚脚GPIO ID;
---     取值范围:主控产品(例如Air8101)上有效的GPIO ID值;
---     如果没有传入此参数或者传入了nil,则使用默认值0;
---cb: function类型;
---     表示触摸事件的回调函数,回调函数的定义格式如下:
---     function cb_func(tp_device, tp_data)
---         --tp_device:产生触摸事件的触摸设备,和AirLCD_1020.init_tp函数的返回值一致
---         --tp_data:触摸事件的数据结构
---                  tp_data[1].event:触摸事件,number类型,tp.EVENT_DOWN(按下事件,值为1),tp.EVENT_UP(弹起事件,值为2),tp.EVENT_MOVE(移动事件,值为3)
---                  tp_data[1].x:触摸的x坐标,number类型
---                  tp_data[1].y:触摸的y坐标,number类型
---                  tp_data[1].timestamp:触摸时的系统tick数;每秒钟包含的tick数量可以通过mcu.hz()获取,例如Air8101上,每秒的tick数量是500
---     end
---     允许为空,没有默认值;
-
---返回值:成功返回触摸设备对象(非nil),失败返回nil
-function AirLCD_1020.init_tp(int, rst, sda, scl, cb)
-    AirLCD_1020.i2c_soft_device = i2c.createSoft(scl or 0, sda or 1)  
-
-    AirLCD_1020.tp_device = tp.init("gt911",{port=AirLCD_1020.i2c_soft_device, pin_rst = rst or 28, pin_int = int or 7, w = WIDTH, h = HEIGHT}, cb)
-
-    return AirLCD_1020.tp_device
-end
-
-
---打开AirLCD_1020的背光
-
---gpio_id:number类型;
---         表示控制LCD背光的GPIO ID;
---         取值范围:主控产品(例如Air8101)上有效的GPIO ID值;
---         如果没有传入此参数或者传入了nil,则使用默认值8;
-
---返回值:nil
-function AirLCD_1020.open_backlight(gpio_id)
-    gpio.setup(gpio_id or 8, 1)
-end
-
---关闭AirLCD_1020的背光
-
---gpio_id:number类型;
---         表示控制LCD背光的GPIO ID;
---         取值范围:主控产品(例如Air8101)上有效的GPIO ID值;
---         如果没有传入此参数或者传入了nil,则使用默认值8;
-
---返回值:nil
-function AirLCD_1020.close_backlight(gpio_id)
-    gpio.setup(gpio_id or 8, 0)
-end
-
-
-return AirLCD_1020

+ 0 - 7
module/Air8101/project/5inch_box_dev_board/AirMICROSD_1000.lua

@@ -1,7 +0,0 @@
-local AirMICROSD_1000 = {}
-
-
-
-return AirMICROSD_1000
-
-

+ 0 - 78
module/Air8101/project/5inch_box_dev_board/http_app.lua

@@ -1,78 +0,0 @@
-local httpplus = require "httpplus"
---加载AirCAMERA_1030驱动文件
-local air_camera = require "AirCAMERA_1030"
-
---打开AirCAMERA_1030摄像头
---拍摄一张1280*720分辨率的照片,通过http上传到服务器;
---上传结束后,等待10秒钟重复执行拍照和上传的动作;
-local function http_upload_photo_task_func() 
-
-    -- camera.config(0, camera.CONF_PREVIEW_ENABLE, 1)
-
-    --打开摄像头
-    local result = air_camera.open()
-    --如果打开失败,直接退出这个函数
-    if not result then
-        log.error("http_upload_photo_task_func error", "air_camera.open fail")
-        return
-    end
-
-    --拍摄一张1280*720分辨率的照片,通过http上传到服务器;
-    --上传结束后,等待10秒钟重复执行拍照和上传的动作;
-    while true do
-
-        --拍摄一张1280*720分辨率的照片
-        --如果拍摄成功,result中存储是照片数据
-        --如果拍摄失败,result为false
-        result = air_camera.capture()
-        --如果拍摄失败,关闭摄像头,并且直接退出这个函数
-        if not result then
-            log.error("http_upload_photo_task_func error", "air_camera.capture fail")
-            air_camera.close()
-            return
-        end
-
-        --检查WIFI连接状态
-        log.info("http_upload_photo_task_func", "socket.adapter(socket.LWIP_STA)", socket.adapter(socket.LWIP_STA))
-        --如果WIFI还没有连接成功
-        if not socket.adapter(socket.LWIP_STA) then
-            --在此处阻塞等待WIFI连接成功的消息"IP_READY"
-            --或者等待30秒超时退出阻塞等待状态
-            --如果没有等到"IP_READY"消息,关闭摄像头,并且直接退出这个函数
-            if not sys.waitUntil("IP_READY", 30000) then
-                log.error("http_upload_photo_task_func error", "ip network timeout")
-                air_camera.close()
-                return
-            end
-        end
-
-        -- 通过WIFI网络将拍摄到的照片数据result上传到服务器upload.air32.cn
-        -- 如果上传成功,电脑上浏览器打开https://www.air32.cn/upload/data/jpg/,打开对应的测试日期目录,点击具体的测试时间照片,可以查看摄像头拍照上传的照片
-        -- 执行httpplus.request后,等待服务器的http应答,此处会阻塞当前task,等待整个过程成功结束或者出现错误异常结束
-        -- code表示结果,number类型,详细说明参考API手册,一般来说:
-        --             200表示成功
-        --             小于0的值表示出错,例如-8表示超时错误
-        --             其余结果值参考API手册
-        local code = httpplus.request({
-            url = "http://upload.air32.cn/api/upload/jpg",
-            method = "POST",
-            body = result
-        })
-        log.info("http_upload_photo_task_func", "httpplus.request", code)
-
-        -- 打印内存信息, 调试用
-        log.info("sys", rtos.meminfo())
-        log.info("sys", rtos.meminfo("sys"))
-        log.info("psram", rtos.meminfo("psram"))
-
-        -- 等待10秒钟
-        sys.wait(10000)
-    end
-
-    --关闭摄像头
-    air_camera.close()
-end
-
---创建并且启动一个task
---运行这个task的主体函数http_upload_photo_task_func
-sys.taskInit(http_upload_photo_task_func)

+ 0 - 95
module/Air8101/project/5inch_box_dev_board/lcd_vector_font_app.lua

@@ -1,95 +0,0 @@
---加载AirFONTS_1000驱动文件
-local air_vetor_fonts = require "AirFONTS_1000"
---加载AirLCD_1020驱动文件
-local air_lcd = require "AirLCD_1020"
---TP设备
-local tp_device
-
-
---lcd显示矢量字体的task
---自动刷新显示不同字号矢量字体以及不同bit的灰度显示效果
-local function lcd_vector_font_app_task_func()
-    log.info("lcd_vector_font_app_task_func enter")
-    -- 开启显示缓冲区, 刷屏速度会加快, 但也消耗2倍屏幕分辨率的内存(2*宽*高 字节)
-    -- 第一个参数无意义,直接填nil即可
-    -- 第二个参数true表示使用sys中的内存
-    lcd.setupBuff(nil, true)
-    --禁止自动刷新
-    --需要刷新时需要主动调用lcd.flush()接口,才能将缓冲区中的数据显示到lcd上
-    lcd.autoFlush(false)
-
-    lvgl.init()
-    lvgl.indev_drv_register("pointer", "touch", tp_device)
-
-    local scr = lvgl.obj_create(nil, nil)
-
-    -- 加载16号和32号矢量字体
-    local counter_label_font_size = 16
-    local font_counter_label = lvgl.font_load(air_vetor_fonts.spi_gtfont, counter_label_font_size)
-    local font32 = lvgl.font_load(air_vetor_fonts.spi_gtfont, 32)
-
-    -- 创建一个按钮
-    local btn = lvgl.btn_create(scr)  -- 在屏幕上创建一个按钮
-    lvgl.obj_set_size(btn, 240, 50)  -- 设置按钮大小为240x50
-    lvgl.obj_align(btn, nil, lvgl.ALIGN_CENTER, 0, 200)  -- 将按钮左右居中,向下偏移200像素
-
-    -- 在按钮上添加一个标签
-    local btn_label = lvgl.label_create(btn)  -- 在按钮上创建一个标签
-    lvgl.obj_set_style_local_text_font(btn_label, lvgl.LABEL_PART_MAIN, lvgl.STATE_DEFAULT, font32)  -- 设置为32号矢量字体
-    lvgl.label_set_text(btn_label, "点我")  -- 设置按钮标签的文本为“点我”
-    lvgl.obj_align(btn_label, nil, lvgl.ALIGN_CENTER, 0, 0)  -- 将标签居中显示在按钮上
-
-    -- 创建一个标签用于显示数字
-    local counter_label = lvgl.label_create(scr)  -- 在屏幕上创建一个标签
-    lvgl.obj_set_style_local_text_font(counter_label, lvgl.LABEL_PART_MAIN, lvgl.STATE_DEFAULT, font_counter_label) -- 初始设置为16号矢量字体
-    lvgl.label_set_text(counter_label, counter_label_font_size.."号字体")  -- 设置标签的初始文本为“16号字体”
-    lvgl.obj_align(counter_label, nil, lvgl.ALIGN_CENTER, 0, -100)  -- 将标签左右居中,向上偏移100像素
-
-    -- 按钮点击事件回调函数
-    local function btn_event_cb(obj, event)
-        --颜色表,依次是黑色,红色,绿色,蓝色
-        local color_table = {lvgl.color_hex(0x000000), lvgl.color_hex(0xFF0000), lvgl.color_hex(0x00FF00), lvgl.color_hex(0x0000FF)}
-        if event == lvgl.EVENT_CLICKED then  -- 如果按钮被点击
-            log.info("button clicked")
-  
-            -- 要显示的矢量字体字号加一
-            counter_label_font_size = counter_label_font_size+1
-            -- 如果大于32,复位到默认值16
-            if counter_label_font_size>32 then counter_label_font_size = 16 end
-
-            -- 加载counter_label_font_size字号大小的矢量字体
-            font_counter_label = lvgl.font_load(air_vetor_fonts.spi_gtfont, counter_label_font_size)
-            -- 设置标签的显示字体为counter_label_font_size字号大小的矢量字体
-            lvgl.obj_set_style_local_text_font(counter_label, lvgl.LABEL_PART_MAIN, lvgl.STATE_DEFAULT, font_counter_label) 
-            -- 设置标签上显示内容的颜色,依次在黑,红,绿,蓝之间循环
-            lvgl.obj_set_style_local_text_color(counter_label, lvgl.LABEL_PART_MAIN, lvgl.STATE_DEFAULT, color_table[counter_label_font_size%4+1])
-
-            -- 更新标签上显示的内容
-            lvgl.label_set_text(counter_label, counter_label_font_size .. "号字体")
-        end
-    end
-    
-
-    -- 将回调函数绑定到按钮的点击事件
-    lvgl.obj_set_event_cb(btn, btn_event_cb, lvgl.EVENT_CLICKED, nil)
-    
-    
-    lvgl.scr_load(scr)
-end
-
-
---初始化矢量字库
--- air_vetor_fonts.init()
-air_vetor_fonts.init(0, 34)
-
---初始化LCD
-air_lcd.init_lcd()
---初始化TP触摸面板
-tp_device = air_lcd.init_tp()
---打开LCD背光
-air_lcd.open_backlight()
-
---创建并且启动一个task
---task的主函数为lcd_vector_font_app_task_func
-sys.taskInit(lcd_vector_font_app_task_func)
-

+ 0 - 75
module/Air8101/project/5inch_box_dev_board/main.lua

@@ -1,75 +0,0 @@
---[[
-必须定义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进行远程升级,根据自己项目的需求,自定义格式即可
-
-AirCAMERA_1030是合宙设计生产的一款USB摄像头配件板;
-本demo演示的核心功能为:
-Air8101核心板+AirCAMERA_1030配件板,演示USB摄像头100万像素拍照+http上传照片+电脑浏览器查看照片的功能;
-更多说明参考本目录下的readme.md文件
-]]
-
-PROJECT = "AirCAMERA_1030"
-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)
-
--- 加载WIFI网络连接管理功能模块
--- require "wifi_app"
--- 加载lcd和矢量字体显示应用模块
--- require "lcd_vector_font_app"
--- 加载AirCAMERA_1030拍照并且通过http上传照片的功能模块
--- require "http_app"
--- 加载tf卡应用模块
--- require "tf_app"
--- 加载lcd和摄像头预览应用模块
-require "lcd_camera_app"
-
-
-
-
-
--- 用户代码已结束---------------------------------------------
--- 结尾总是这一句
-sys.run()
--- sys.run()之后不要加任何语句!!!!!因为添加的任何语句都不会被执行

+ 0 - 58
module/Air8101/project/5inch_box_dev_board/readme.md

@@ -1,58 +0,0 @@
-
-## 演示功能概述
-
-AirCAMERA_1030是合宙设计生产的一款USB摄像头配件板;
-
-本demo演示的核心功能为:
-
-Air8101核心板+AirCAMERA_1030配件板,演示USB摄像头100万像素拍照+http上传照片+电脑浏览器查看照片的功能;
-
-
-## 核心板+配件板资料
-
-[Air8101核心板+配件板相关资料](https://docs.openluat.com/air8101/product/shouce/#air8101_1)
-
-
-## 演示硬件环境
-
-![](https://docs.openluat.com/air8101/product/file/AirCAMERA_1030/hw_connection.jpg)
-
-1、Air8101核心板
-
-2、AirCAMERA_1030配件板(带USB摄像头+数据连接线)
-
-3、Air8101核心板和AirCAMERA_1030配件板的硬件接线方式为
-
-- Air8101核心板通过TYPE-C USB口供电;(核心板背面的功耗测试开关拨到OFF一端)
-
-- 如果测试发现软件频繁重启,可能是供电不足,此时再通过直流稳压电源对核心板的VIN管脚进行5V供电;
-
-- Air8101核心板上的3.3V和5V拨动开关,拨到5V的一端;为了演示方便,所以Air8101核心板的上电后直接给AirCAMERA_1030配件板提供了供电;
-
-- 客户在设计实际项目时,一般来说,需要通过一个GPIO来控制LDO给摄像头供电,这样可以灵活地控制摄像头的供电,可以使项目的整体功耗降到最低;
-
-- Air8101核心板的USB-A母座和AirCAMERA_1030配件板的USB-A公座相连;
-
-
-## 演示软件环境
-
-1、Luatools下载调试工具
-
-2、[Air8101 V1003版本固件](https://docs.openluat.com/air8101/luatos/firmware/)(理论上最新版本固件也可以,如果使用最新版本的固件不可以,可以烧录V1003固件对比验证)
-
-
-## 演示操作步骤
-
-1、搭建好演示硬件环境
-
-2、demo脚本代码wifi_app.lua中的wlan.connect("茶室-降功耗,找合宙!", "Air123456", 1),前两个参数,修改为自己测试时wifi热点的名称和密码;注意:仅支持2.4G的wifi,不支持5G的wifi
-
-3、Luatools烧录内核固件和修改后的demo脚本代码
-
-4、烧录成功后,自动开机运行
-
-5、观察Luatools的运行日志,如果输出 http_upload_photo_task_func httpplus.request 200表示测试正常
-
-6、电脑上浏览器打开[https://www.air32.cn/upload/data/jpg/](https://www.air32.cn/upload/data/jpg/),打开对应的测试日期目录,点击具体的测试时间照片,可以查看摄像头拍照上传的照片
-   
-

+ 0 - 83
module/Air8101/project/5inch_box_dev_board/tf_app.lua

@@ -1,83 +0,0 @@
-
-sys.taskInit(function()
-    sys.wait(1000)
-    -- fatfs.debug(1) -- 若挂载失败,可以尝试打开调试信息,查找原因
-
-    fatfs.mount(fatfs.SDIO, "/sd", 0, nil, 24 * 1000 * 1000) --挂载fatfs
-
-    local data, err = fatfs.getfree("/sd")  --获取可用空间信息
-    if data then
-        log.info("fatfs", "getfree", json.encode(data))
-    else
-        log.info("fatfs", "err", err)
-    end
-
-    -- #################################################
-    -- 文件操作测试
-    -- #################################################
-    local f = io.open("/sd/boottime", "rb") --
-    local c = 0
-    if f then
-        local data = f:read("*a")
-        log.info("fs", "data", data, data:toHex())
-        c = tonumber(data)
-        f:close()
-    end
-    log.info("fs", "boot count", c)
-    if c == nil then
-        c = 0
-    end
-    c = c + 1
-    f = io.open("/sd/boottime", "wb")
-    if f ~= nil then
-        log.info("fs", "write c to file", c, tostring(c))
-        f:write(tostring(c))
-        f:close()
-    else
-        log.warn("sdio", "mount not good?!")
-    end
-    if fs then
-        log.info("fsstat", fs.fsstat("/")) -- 打印根分区的信息
-        log.info("fsstat", fs.fsstat("/sd"))  --打印sd卡文件系统分区信息
-    end
-
-    -- 测试一下追加, fix in 2021.12.21
-    os.remove("/sd/test_a")
-    sys.wait(50)
-    f = io.open("/sd/test_a", "w")
-    if f then
-        f:write("ABC")
-        f:close()
-    end
-    f = io.open("/sd/test_a", "a+")
-    if f then
-        f:write("def")
-        f:close()
-    end
-    f = io.open("/sd/test_a", "r")
-    if  f then
-        local data = f:read("*a")
-        log.info("data", data, data == "ABCdef")
-        f:close()
-    end
-
-    -- 测试一下按行读取, fix in 2022-01-16
-    f = io.open("/sd/testline", "w")
-    if f then
-        f:write("abc\n")
-        f:write("123\n")
-        f:write("wendal\n")
-        f:close()
-    end
-    sys.wait(100)
-    f = io.open("/sd/testline", "r")
-    if f then
-        log.info("sdio", "line1", f:read("*l"))
-        log.info("sdio", "line2", f:read("*l"))
-        log.info("sdio", "line3", f:read("*l"))
-        f:close()
-    end
-
-    -- #################################################
-end)
-

+ 0 - 34
module/Air8101/project/5inch_box_dev_board/wifi_app.lua

@@ -1,34 +0,0 @@
-
-local function ip_ready_func()
-    log.info("wlan_connect.ip_ready_func", "IP_READY")
-end
-
-local function ip_lose_func()
-    log.info("wlan_connect.ip_lose_func", "IP_LOSE")
-end
-
-
-
---此处订阅"IP_READY"和"IP_LOSE"两种消息
---在消息的处理函数中,仅仅打印了一些信息,便于实时观察WIFI的连接状态
---也可以根据自己的项目需求,在消息处理函数中增加自己的业务逻辑控制,例如可以在连网状态发生改变时更新网络图标
-sys.subscribe("IP_READY", ip_ready_func)
-sys.subscribe("IP_LOSE", ip_lose_func)
-
-
-
-
-wlan.init()
---连接WIFI热点,连接结果会通过"IP_READY"或者"IP_LOSE"消息通知
---Air8101仅支持2.4G的WIFI,不支持5G的WIFI
---此处前两个参数表示WIFI热点名称以及密码,更换为自己测试时的真实参数即可
---第三个参数1表示WIFI连接异常时,内核固件会自动重连
-wlan.connect("茶室-降功耗,找合宙!", "Air123456", 1)
-
---WIFI联网成功(做为STATION成功连接AP,并且获取到了IP地址)后,内核固件会产生一个"IP_READY"消息
---各个功能模块可以订阅"IP_READY"消息实时处理WIFI联网成功的事件
---也可以在任何时刻调用socket.adapter(socket.LWIP_STA)来获取WIFI网络是否连接成功
-
---WIFI断网后,内核固件会产生一个"IP_LOSE"消息
---各个功能模块可以订阅"IP_LOSE"消息实时处理WIFI断网的事件
---也可以在任何时刻调用socket.adapter(socket.LWIP_STA)来获取WIFI网络是否连接成功

+ 7 - 6
module/Air8101/project/core_accessory_board/AirCAMERA_1020/http_app.lua

@@ -30,11 +30,12 @@ local function http_upload_photo_task_func()
             return
         end
 
-        --检查WIFI连接状态
-        log.info("http_upload_photo_task_func", "socket.adapter(socket.LWIP_STA)", socket.adapter(socket.LWIP_STA))
-        --如果WIFI还没有连接成功
-        if not socket.adapter(socket.LWIP_STA) then
-            --在此处阻塞等待WIFI连接成功的消息"IP_READY"
+        --检查网卡(本demo使用的是socket.LWIP_STA网卡)连接状态
+        --socket.dft()为当前使用的网卡
+        log.info("http_upload_photo_task_func", "socket.adapter(socket.dft())", socket.adapter(socket.dft()))
+        --如果网卡(本demo使用的是socket.LWIP_STA网卡)还没有连接成功
+        if not socket.adapter(socket.dft()) then
+            --在此处阻塞等待网卡(本demo使用的是socket.LWIP_STA网卡)连接成功的消息"IP_READY"
             --或者等待30秒超时退出阻塞等待状态
             --如果没有等到"IP_READY"消息,关闭摄像头,并且直接退出这个函数
             if not sys.waitUntil("IP_READY", 30000) then
@@ -44,7 +45,7 @@ local function http_upload_photo_task_func()
             end
         end
 
-        -- 通过WIFI网络将拍摄到的照片数据result上传到服务器upload.air32.cn
+        -- 通过网卡(本demo使用的是socket.LWIP_STA网卡)将拍摄到的照片数据result上传到服务器upload.air32.cn
         -- 如果上传成功,电脑上浏览器打开https://www.air32.cn/upload/data/jpg/,打开对应的测试日期目录,点击具体的测试时间照片,可以查看摄像头拍照上传的照片
         -- 执行httpplus.request后,等待服务器的http应答,此处会阻塞当前task,等待整个过程成功结束或者出现错误异常结束
         -- code表示结果,number类型,详细说明参考API手册,一般来说:

+ 2 - 2
module/Air8101/project/core_accessory_board/AirCAMERA_1020/wifi_app.lua

@@ -27,8 +27,8 @@ wlan.connect("茶室-降功耗,找合宙!", "Air123456", 1)
 
 --WIFI联网成功(做为STATION成功连接AP,并且获取到了IP地址)后,内核固件会产生一个"IP_READY"消息
 --各个功能模块可以订阅"IP_READY"消息实时处理WIFI联网成功的事件
---也可以在任何时刻调用socket.adapter(socket.LWIP_STA)来获取WIFI网络是否连接成功
+--也可以在任何时刻调用socket.adapter(socket.dft())来获取WIFI网络是否连接成功
 
 --WIFI断网后,内核固件会产生一个"IP_LOSE"消息
 --各个功能模块可以订阅"IP_LOSE"消息实时处理WIFI断网的事件
---也可以在任何时刻调用socket.adapter(socket.LWIP_STA)来获取WIFI网络是否连接成功
+--也可以在任何时刻调用socket.adapter(socket.dft())来获取WIFI网络是否连接成功

+ 6 - 5
module/Air8101/project/core_accessory_board/AirCAMERA_1030/http_app.lua

@@ -30,10 +30,11 @@ local function http_upload_photo_task_func()
             return
         end
 
-        --检查WIFI连接状态
-        log.info("http_upload_photo_task_func", "socket.adapter(socket.LWIP_STA)", socket.adapter(socket.LWIP_STA))
-        --如果WIFI还没有连接成功
-        if not socket.adapter(socket.LWIP_STA) then
+        --检查网卡(本demo使用的是socket.LWIP_STA网卡)连接状态
+        --socket.dft()为当前使用的网卡
+        log.info("http_upload_photo_task_func", "socket.adapter(socket.dft())", socket.adapter(socket.dft()))
+        --如果网卡(本demo使用的是socket.LWIP_STA网卡)还没有连接成功
+        if not socket.adapter(socket.dft()) then
             --在此处阻塞等待WIFI连接成功的消息"IP_READY"
             --或者等待30秒超时退出阻塞等待状态
             --如果没有等到"IP_READY"消息,关闭摄像头,并且直接退出这个函数
@@ -44,7 +45,7 @@ local function http_upload_photo_task_func()
             end
         end
 
-        -- 通过WIFI网络将拍摄到的照片数据result上传到服务器upload.air32.cn
+        -- 通过网卡(本demo使用的是socket.LWIP_STA网卡)将拍摄到的照片数据result上传到服务器upload.air32.cn
         -- 如果上传成功,电脑上浏览器打开https://www.air32.cn/upload/data/jpg/,打开对应的测试日期目录,点击具体的测试时间照片,可以查看摄像头拍照上传的照片
         -- 执行httpplus.request后,等待服务器的http应答,此处会阻塞当前task,等待整个过程成功结束或者出现错误异常结束
         -- code表示结果,number类型,详细说明参考API手册,一般来说:

+ 2 - 2
module/Air8101/project/core_accessory_board/AirCAMERA_1030/readme.md

@@ -23,9 +23,9 @@ Air8101核心板+AirCAMERA_1030配件板,演示USB摄像头100万像素拍照+
 
 3、Air8101核心板和AirCAMERA_1030配件板的硬件接线方式为
 
-- Air8101核心板通过TYPE-C USB口供电(核心板背面的功耗测试开关拨到OFF一端)
+- Air8101核心板通过TYPE-C USB口供电(核心板背面的功耗测试开关拨到OFF一端)
 
-- 如果测试发现软件频繁重启,可能是供电不足,此时再通过直流稳压电源对核心板的VIN管脚进行5V供电;
+- 如果测试发现软件重启,并且日志中出现  poweron reason 0,表示供电不足,此时再通过直流稳压电源对核心板的VIN管脚进行5V供电;
 
 - Air8101核心板上的3.3V和5V拨动开关,拨到5V的一端;为了演示方便,所以Air8101核心板的上电后直接给AirCAMERA_1030配件板提供了供电;
 

+ 2 - 2
module/Air8101/project/core_accessory_board/AirCAMERA_1030/wifi_app.lua

@@ -27,8 +27,8 @@ wlan.connect("茶室-降功耗,找合宙!", "Air123456", 1)
 
 --WIFI联网成功(做为STATION成功连接AP,并且获取到了IP地址)后,内核固件会产生一个"IP_READY"消息
 --各个功能模块可以订阅"IP_READY"消息实时处理WIFI联网成功的事件
---也可以在任何时刻调用socket.adapter(socket.LWIP_STA)来获取WIFI网络是否连接成功
+--也可以在任何时刻调用socket.adapter(socket.dft())来获取WIFI网络是否连接成功
 
 --WIFI断网后,内核固件会产生一个"IP_LOSE"消息
 --各个功能模块可以订阅"IP_LOSE"消息实时处理WIFI断网的事件
---也可以在任何时刻调用socket.adapter(socket.LWIP_STA)来获取WIFI网络是否连接成功
+--也可以在任何时刻调用socket.adapter(socket.dft())来获取WIFI网络是否连接成功

+ 7 - 8
module/Air8101/project/core_accessory_board/AirETH_1000/http_app.lua

@@ -1,10 +1,10 @@
 
 --这个task的核心业务逻辑是:每隔一段时间发送一次http get请求,测试http数传是否正常
 local function http_get_task_func()
-    --检查以太网连接状态
-    log.info("http_get_task_func", "socket.adapter(socket.LWIP_USER1)",socket.adapter(socket.LWIP_USER1))
-    --如果以太网还没有连接成功
-    if not socket.adapter(socket.LWIP_USER1) then
+    --检查当前使用的网卡(本demo使用的是以太网卡socket.LWIP_USER1)的连接状态
+    log.info("http_get_task_func", "socket.adapter(socket.dft())", socket.adapter(socket.dft()))
+    --如果当前使用的网卡(本demo使用的是以太网卡socket.LWIP_USER1)还没有连接成功
+    if not socket.adapter(socket.dft()) then
         --net_app.lua中的以太网配置和启动结束后,一旦以太网卡准备就绪,就会产生一个"IP_READY"消息
         --在此处阻塞等待以太网连接成功的消息"IP_READY"
         --或者等待30秒超时退出阻塞等待状态
@@ -24,7 +24,6 @@ local function http_get_task_func()
         --wait()表示在此处阻塞等待整个过程的结束
 
         --具体到此处的代码,对部分参数以及返回值做如下解释
-        --adapter=socket.LWIP_USER1表示当前正在使用的网络适配器编号
         --timeout=3000表示超时时间为3秒,如果3秒内没有成功结束或者异常结束整个过程,则会超时结束;
         --整个过程结束后,http.request().wait()有三个返回值code,headers,body
         --code表示结果,number类型,详细说明参考API手册,一般来说:
@@ -33,14 +32,14 @@ local function http_get_task_func()
         --             其余结果值参考API手册
         --headers表示服务器返回的应答头,table类型
         --body表示服务器返回的应答题,具体到这里的代码使用方式,为string类型
-        log.info("http", http.request("GET", "http://httpbin.air32.cn/get", nil, nil, {adapter=socket.LWIP_USER1,timeout=3000}).wait())
+        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"))
 
-        --打印以太网卡下的本地IP,网关,子网掩码,网关IP信息
-        log.info("ip", socket.localIP(socket.LWIP_USER1))
+        --打印当前使用的网卡(本demo使用的是以太网卡socket.LWIP_USER1)下的本地IP,网关,子网掩码,网关IP信息
+        log.info("ip", socket.dft(), socket.localIP(socket.dft()))
 
         --等待6秒钟
         sys.wait(6000)

+ 0 - 2
module/Air8101/project/core_accessory_board/AirETH_1000/net_app.lua

@@ -24,8 +24,6 @@ gpio.setup(13, 1, gpio.PULLUP)
 
 --这个task的核心业务逻辑是:初始化SPI,初始化以太网卡,并在以太网上开启动态主机配置协议
 local function spi_eth_init_task_func()
-
-    sys.wait(6000)
     -- 初始化SPI
     local result = spi.setup(
         0,--spi_id

+ 12 - 8
module/Air8101/project/core_accessory_board/AirETH_1000/readme.md

@@ -1,7 +1,7 @@
 
 ## 演示功能概述
 
-AirETH_1000是合宙设计生产的一款搭载CH390H芯片的以太网配件板;
+AirETH_1000是合宙设计生产的一款搭载CH390H芯片的SPI接口的以太网配件板;
 
 本demo演示的核心功能为:
 
@@ -29,16 +29,20 @@ Air8101核心板+AirETH_1000配件板,使用配件板上的以太网口通过
 
 5、Air8101核心板和AirETH_1000配件板的硬件接线方式为
 
-- Air8101核心板通过TYPE-C USB口供电(核心板背面的功耗测试开关拨到OFF一端);如果测试发现软件重启,并且日志中出现  poweron reason 0,表示供电不足,此时再通过直流稳压电源对核心板的VIN管脚进行5V供电;
+- Air8101核心板通过TYPE-C USB口供电(核心板背面的功耗测试开关拨到OFF一端);
 
-| Air8101核心板   | AirETH_1000配件板 |
-| ------------ | ------------------ |
+- 如果测试发现软件重启,并且日志中出现  poweron reason 0,表示供电不足,此时再通过直流稳压电源对核心板的VIN管脚进行5V供电;
+
+- 客户在设计实际项目时,一般来说,需要通过一个GPIO来控制LDO给配件板供电,这样可以灵活地控制配件板的供电,可以使项目的整体功耗降到最低;
+
+| Air8101核心板   |  AirETH_1000配件板 |
+| --------------- | ----------------- |
 | 59/3V3          | 3.3v              |
 | gnd             | gnd               |
-| 28/DCLK | SCK               |
-| 54/DISP | CSS               |
-| 55/HSYN | SDO               |
-| 57/DE | SDI               |
+| 28/DCLK         | SCK               |
+| 54/DISP         | CSS               |
+| 55/HSYN         | SDO               |
+| 57/DE           | SDI               |
 | 14/GPIO8        | INT               |
 
 

+ 7 - 8
module/Air8101/project/core_accessory_board/AirPHY_1000/http_app.lua

@@ -1,10 +1,10 @@
 
 --这个task的核心业务逻辑是:每隔一段时间发送一次http get请求,测试http数传是否正常
 local function http_get_task_func()
-    --检查以太网连接状态
-    log.info("http_get_task_func", "socket.adapter(socket.LWIP_ETH)",socket.adapter(socket.LWIP_ETH))
-    --如果以太网还没有连接成功
-    if not socket.adapter(socket.LWIP_ETH) then
+    --检查当前使用的网卡(本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秒超时退出阻塞等待状态
@@ -24,7 +24,6 @@ local function http_get_task_func()
         --wait()表示在此处阻塞等待整个过程的结束
 
         --具体到此处的代码,对部分参数以及返回值做如下解释
-        --adapter=socket.LWIP_ETH表示使用的是以太网卡
         --timeout=3000表示超时时间为3秒,如果3秒内没有成功结束或者异常结束整个过程,则会超时结束;
         --整个过程结束后,http.request().wait()有三个返回值code,headers,body
         --code表示结果,number类型,详细说明参考API手册,一般来说:
@@ -33,14 +32,14 @@ local function http_get_task_func()
         --             其余结果值参考API手册
         --headers表示服务器返回的应答头,table类型
         --body表示服务器返回的应答题,具体到这里的代码使用方式,为string类型
-        log.info("http", http.request("GET", "http://httpbin.air32.cn/get", nil, nil, {adapter=socket.LWIP_ETH,timeout=3000}).wait())
+        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"))
 
-        --打印以太网卡下的本地IP,网关,子网掩码,网关IP信息
-        log.info("ip", socket.localIP(socket.LWIP_ETH))
+        --打印当前使用的网卡(本demo使用的是以太网卡socket.LWIP_ETH)下的本地IP,网关,子网掩码,网关IP信息
+        log.info("ip", socket.dft(), socket.localIP(socket.dft()))
 
         --等待6秒钟
         sys.wait(6000)

+ 2 - 2
module/Air8101/project/core_accessory_board/AirUSBHUB_1000/http_app.lua

@@ -40,9 +40,9 @@ local function http_upload_photo_task_func()
         end
 
         --检查WIFI连接状态
-        log.info("http_upload_photo_task_func", "socket.adapter(socket.LWIP_STA)", socket.adapter(socket.LWIP_STA))
+        log.info("http_upload_photo_task_func", "socket.adapter(socket.dft())", socket.adapter(socket.dft()))
         --如果WIFI还没有连接成功
-        if not socket.adapter(socket.LWIP_STA) then
+        if not socket.adapter(socket.dft()) then
             --在此处阻塞等待WIFI连接成功的消息"IP_READY"
             --或者等待30秒超时退出阻塞等待状态
             --如果没有等到"IP_READY"消息,关闭当前摄像头,跳转到continue标签,打开USB HUB上的下一个AirCAMERA_1030摄像头

+ 2 - 2
module/Air8101/project/core_accessory_board/AirUSBHUB_1000/wifi_app.lua

@@ -27,8 +27,8 @@ wlan.connect("茶室-降功耗,找合宙!", "Air123456", 1)
 
 --WIFI联网成功(做为STATION成功连接AP,并且获取到了IP地址)后,内核固件会产生一个"IP_READY"消息
 --各个功能模块可以订阅"IP_READY"消息实时处理WIFI联网成功的事件
---也可以在任何时刻调用socket.adapter(socket.LWIP_STA)来获取WIFI网络是否连接成功
+--也可以在任何时刻调用socket.adapter(socket.dft())来获取WIFI网络是否连接成功
 
 --WIFI断网后,内核固件会产生一个"IP_LOSE"消息
 --各个功能模块可以订阅"IP_LOSE"消息实时处理WIFI断网的事件
---也可以在任何时刻调用socket.adapter(socket.LWIP_STA)来获取WIFI网络是否连接成功
+--也可以在任何时刻调用socket.adapter(socket.dft())来获取WIFI网络是否连接成功

+ 1 - 1
module/Air8101/project/readme.md

@@ -16,7 +16,7 @@
 
  |
 
- |------5inch_box_dev_board:5寸整机开发板
+ |------5inch_800x480_dev_board:5寸整机开发板
 
  |
 

+ 8 - 8
script/demolib/fota_wifi.lua → script/libs/fota_wifi.lua

@@ -1,12 +1,12 @@
 --[[
--- @module fota_wifi
--- @summary 用于Air8000/8000A/8000W型号模组自动升级WIFI
--- @version 1.0.1
--- @date    2025.6.26
--- @author  tuoyiheng
--- @usage
---注:使用时在创建的一个task处理函数中直接调用fota_wifi.request()即可开始执行WiFi升级任务
---用法实例
+@module fota_wifi
+@summary 用于Air8000/8000A/8000W型号模组自动升级WIFI
+@version 1.0.1
+@date    2025.6.26
+@author  tuoyiheng
+@usage
+注:使用时在创建的一个task处理函数中直接调用fota_wifi.request()即可开始执行WiFi升级任务
+-- 用法实例
 local fota_wifi = require("fota_wifi")
 
 local function wifi_fota_task_func()

+ 694 - 0
script/libs/libnetif.lua

@@ -0,0 +1,694 @@
+--[[
+@module libnetif
+@summary libnetif 控制网络优先级(以太网->WIFI->4G)根据优先级选择上网的网卡。简化开启多网融合的操作,4G作为数据出口给WIFI,以太网设备上网,以太网作为数据出口给WIFI,Air8000上网,WIFI作为数据出口给Air8000,以太网上网。
+@version 1.0
+@date    2025.06.26
+@author  wjq
+@usage
+本文件的对外接口有4个:
+1、libnetif.set_priority_order(networkConfigs):设置网络优先级顺序并初始化对应网络(需要在task中调用)
+2、libnetif.notify_status(cb_fnc):设置网络状态变化回调函数
+3、libnetif.setproxy(adapter, main_adapter,other_configs):配置网络代理实现多网融合(需要在task中调用)
+4、libnetif.check_network_status(interval),检测间隔时间ms(选填),不填时只检测一次,填写后将根据间隔时间循环检测,会提高模块功耗
+]]
+local libnetif = {}
+
+dnsproxy = require("dnsproxy")
+dhcpsrv = require("dhcpsrv")
+httpdns = require("httpdns")
+-- 设置pingip
+local wifi_ping_ip
+local eth_ping_ip
+
+
+local ping_time = 10000
+-- 连接状态
+local connection_states = {
+    DISCONNECTED = 0,
+    CONNECTING = 1,
+    CONNECTED = 2,
+    OPENED = 3
+}
+
+-- 状态回调函数
+local states_cbfnc = function(net_type) end
+-- 当前优先级
+local current_priority = { socket.LWIP_ETH, socket.LWIP_STA, socket.LWIP_GP }
+-- 连接状态
+local available = {
+    [socket.LWIP_STA] = connection_states.DISCONNECTED,
+    [socket.LWIP_ETH] = connection_states.DISCONNECTED,
+    [socket.LWIP_GP] = connection_states.DISCONNECTED,
+    [socket.LWIP_USER1] = connection_states.DISCONNECTED
+}
+-- 当前使用的网卡
+local current_active = socket.LWIP_USER0
+
+-- 网络类型转字符串
+local function type_to_string(net_type)
+    local type_map = {
+        [socket.LWIP_STA] = "WiFi",
+        [socket.LWIP_ETH] = "Ethernet",
+        [socket.LWIP_GP] = "4G",
+        [socket.LWIP_USER1] = "8101SPIETH"
+    }
+    return type_map[net_type] or "Unknown"
+end
+
+-- 状态更改后重新设置默认网卡
+local function apply_priority()
+    local usable = false
+    -- 查找优先级最高的可用网络
+    for _, net_type in ipairs(current_priority) do
+        -- log.info("网卡顺序",type_to_string(net_type),available[net_type])
+        if available[net_type] == connection_states.CONNECTED then
+            usable = true
+            -- 设置优先级高的网卡
+            if current_active ~= net_type then
+                log.info("设置网卡", type_to_string(net_type))
+                if current_active ~= socket.LWIP_USER0 then
+                    states_cbfnc(type_to_string(net_type), net_type) -- 默认网卡改变的回调函数
+                end
+                socket.dft(net_type)
+                current_active = net_type
+            end
+            break
+        end
+    end
+    if usable == false then
+        states_cbfnc(nil, -1)
+    end
+end
+
+--打开以太网Wan功能
+local function setup_eth(config)
+    eth_ping_ip = config.ping_ip
+    if type(config.ping_time) == "number" then
+        ping_time = config.ping_time
+    end
+    log.info("初始化以太网")
+    available[socket.LWIP_ETH] = connection_states.OPENED
+    -- 打开CH390供电
+    gpio.setup(config.pwrpin, 1, gpio.PULLUP)
+    sys.wait(100)
+    if config.tp == nil then
+        log.info("8101以太网")
+        netdrv.setup(socket.LWIP_ETH)
+    else
+        log.info("config.opts.spi",config.opts.spi,",config.type",config.tp)
+        -- 配置SPI和初始化网络驱动
+        local result = spi.setup(config.opts.spi, -- spi id
+        nil, 0, -- CPHA
+        0, -- CPOL
+        8, -- 数据宽度
+        51200000 -- ,--波特率
+        )
+        log.info("main", "open spi", result)
+        if result ~= 0 then -- 返回值为0,表示打开成功
+            log.info("main", "spi open error", result)
+            gpio.close(config.pwrpin)
+            return false
+        end
+        -- 初始化指定netdrv设备,
+        -- socket.LWIP_ETH 网络适配器编号
+        -- netdrv.CH390外挂CH390
+        -- SPI ID 1, 片选 GPIO12
+        netdrv.setup(socket.LWIP_ETH, config.tp, config.opts)
+    end
+    netdrv.dhcp(socket.LWIP_ETH, true)
+    log.info("以太网初始化完成")
+    return true
+end
+
+--打开8101spi以太网Wan功能
+local function setup_eth_user1(config)
+    eth_ping_ip = config.ping_ip
+    if type(config.ping_time) == "number" then
+        ping_time = config.ping_time
+    end
+    log.info("初始化以太网")
+    available[socket.LWIP_USER1] = connection_states.OPENED
+    -- 打开CH390供电
+    gpio.setup(config.pwrpin, 1, gpio.PULLUP)
+    sys.wait(100)
+    log.info("config.opts.spi", config.opts.spi, ",config.type", config.tp)
+    -- 配置SPI和初始化网络驱动
+    local result = spi.setup(config.opts.spi,     -- spi id
+        nil, 0,                                   -- CPHA
+        0,                                        -- CPOL
+        8,                                        -- 数据宽度
+        51200000                                  -- ,--波特率
+    )
+    log.info("main", "open spi", result)
+    if result ~= 0 then     -- 返回值为0,表示打开成功
+        log.info("main", "spi open error", result)
+        gpio.close(config.pwrpin)
+        return false
+    end
+    -- 初始化指定netdrv设备,
+    -- socket.LWIP_ETH 网络适配器编号
+    -- netdrv.CH390外挂CH390
+    -- SPI ID 1, 片选 GPIO12
+    netdrv.setup(socket.LWIP_USER1, config.tp, config.opts)
+    netdrv.dhcp(socket.LWIP_USER1, true)
+    log.info("以太网初始化完成")
+    return true
+end
+
+--连接wifi(STA模式)
+local function set_wifi_info(config)
+    wifi_ping_ip = config.ping_ip
+    if type(config.ping_time) == "number" then
+        ping_time = config.ping_time
+    end
+    log.info("WiFi名称:", config.ssid)
+    log.info("密码     :", config.password)
+    log.info("ping_ip  :", config.ping_ip)
+    wlan.init()
+    available[socket.LWIP_STA] = connection_states.OPENED
+    -- 尝试连接Wi-Fi,并处理可能出现的错误
+    local success = wlan.connect(config.ssid, config.password)
+    if not success then
+        log.error("WiFi连接失败")
+        return false
+    end
+    log.info("WiFi STA初始化完成")
+    return true
+end
+
+--[[
+设置网络优先级,相应网卡获取到ip且网络正常视为网卡可用,丢失ip视为网卡不可用.(需要在task中调用)
+@api libnetif.set_priority_order(new_priority)
+@table 网络优先级列表
+@return boolean 成功返回true,失败返回false
+@usage
+libnetif.set_priority_order({
+    { -- 最高优先级网络
+        WIFI = { -- WiFi配置
+            ssid = "your_ssid",       -- WiFi名称(string)
+            password = "your_pwd",    -- WiFi密码(string)
+            ping_ip = "112.125.89.8", -- 连通性检测IP(选填参数),默认使用httpdns获取baidu.com的ip作为判断条件,
+                                      -- 注:如果填写ip,则ping通作为判断网络是否可用的条件,
+                                      -- 所以需要根据网络环境填写内网或者外网ip,
+                                      -- 填写外网ip的话要保证外网ip始终可用,
+                                      -- 填写局域网ip的话要确保相应ip固定且能够被ping通
+            ping_time = 10000         -- 填写ping_ip且未ping通时的检测间隔(ms, 可选,默认为10秒)
+                                      -- 定时ping将会影响模块功耗,使用低功耗模式的话可以适当延迟间隔时间
+        }
+    },
+    { -- 次优先级网络
+        ETHERNET = { -- 以太网配置
+            pwrpin = 140,             -- 供电使能引脚(number)
+            ping_ip = "112.125.89.8", -- 连通性检测IP(选填参数),默认使用httpdns获取baidu.com的ip作为判断条件,
+                                      -- 注:如果填写ip,则ping通作为判断网络是否可用的条件,
+                                      -- 所以需要根据网络环境填写内网或者外网ip,
+                                      -- 填写外网ip的话要保证外网ip始终可用,
+                                      -- 填写局域网ip的话要确保相应ip固定且能够被ping通
+            ping_time = 10000         -- 填写ping_ip且未ping通时的检测间隔(ms, 可选,默认为10秒)
+                                      -- 定时ping将会影响模块功耗,使用低功耗模式的话可以适当延迟间隔时间
+            tp = netdrv.CH390         -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
+            opts = { spi = 1, cs = 12 }   -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
+        }
+    },
+    { -- 最低优先级网络
+        LWIP_GP = true  -- 启用4G网络
+    }
+})
+]]
+function libnetif.set_priority_order(networkConfigs)
+    --判断表中数据个数
+    if #networkConfigs <2 then
+        log.error("请至少添加两个网络")
+        return false
+    end
+    local new_priority = {}
+    for _, config in ipairs(networkConfigs) do
+        if type(config.WIFI) == "table" then
+            --开启wifi
+            local res = set_wifi_info(config.WIFI)
+            if res == false then
+                log.error("wifi连接失败")
+                return false
+            end
+            table.insert(new_priority, socket.LWIP_STA)
+        end
+        if type(config.ETHUSER1) == "table" then
+            --开启以太网
+            local res = setup_eth_user1(config.ETHUSER1)
+            if res == false then
+                log.error("以太网打开失败")
+                return false
+            end
+            table.insert(new_priority, socket.LWIP_USER1)
+        end
+        if type(config.ETHERNET) == "table" then
+            --开启以太网
+            local res = setup_eth(config.ETHERNET)
+            if res == false then
+                log.error("以太网打开失败")
+                return false
+            end
+            table.insert(new_priority, socket.LWIP_ETH)
+        end
+        if config.LWIP_GP then
+            --开启4G
+            table.insert(new_priority, socket.LWIP_GP)
+            available[socket.LWIP_GP] = connection_states.CONNECTING
+        end
+    end
+
+    -- 设置新优先级
+    current_priority = new_priority
+    apply_priority()
+
+    return true
+end
+
+--[[
+设置网络状态变化回调函数。触发条件:网卡切换或者所有网卡都断网。返回值为:1. 当有可用网络的时候,返回当前使用网卡、网卡id;2. 当没有可用网络的时候,返回 nil、-1 。
+@api libnetif.notify_status(cb_fnc)
+@function 回调函数
+@usage
+    libnetif.notify_status(function(net_type,adapter)
+    log.info("可以使用优先级更高的网络:", net_type,adapter)
+    end)
+]]
+function libnetif.notify_status(cb_fnc)
+    log.info("notify_status", type(cb_fnc))
+    if type(cb_fnc) ~= "function" then
+        log.error("notify_status设置错误,请传入一个函数")
+        return
+    end
+    states_cbfnc = cb_fnc
+end
+
+--[[
+设置多网融合模式,例如4G作为数据出口给WIFI或以太网设备上网(需要在task中调用)
+@api libnetif.setproxy(adapter, main_adapter,other_configs)
+@adapter 需要使用网络的网卡,例如socket.LWIP_ETH
+@adapter 提供网络的网卡,例如socket.LWIP_GP
+@table 其他设置参数(选填参数),
+{
+        ssid = "Hotspot",                -- WiFi名称(string),网卡包含wifi时填写
+        password = "password123",        -- WiFi密码(string),网卡包含wifi时填写
+        tp = netdrv.CH390,               -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
+        opts = { spi = 1, cs = 12},      -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
+        ethpower_en = 140,               -- 以太网模块的pwrpin引脚(gpio编号)
+        adapter_addr = "192.168.5.1",    -- adapter网卡的ip地址(选填),需要自定义ip和网关ip时填写
+        adapter_gw= { 192, 168, 5, 1 },   -- adapter网卡的网关地址(选填),需要自定义ip和网关ip时填写
+        ap_opts={                        -- AP模式下配置项(选填参数)
+        hidden = false,                  -- 是否隐藏SSID, 默认false,不隐藏
+        max_conn = 4 },                  -- 最大客户端数量, 默认4
+        channel=6                        -- AP建立的通道, 默认6
+}
+@usage
+    --典型应用:
+    -- 4G作为出口供WiFi和以太网设备上网
+    libnetif.setproxy(socket.LWIP_AP, socket.LWIP_GP, {
+        ssid = "Hotspot",                -- WiFi名称(string),网卡包含wifi时填写
+        password = "password123",        -- WiFi密码(string),网卡包含wifi时填写
+        adapter_addr = "192.168.5.1",    -- adapter网卡的ip地址(选填),需要自定义ip和网关ip时填写
+        adapter_gw= { 192, 168, 5, 1 },   -- adapter网卡的网关地址(选填),需要自定义ip和网关ip时填写
+        ap_opts={                        -- AP模式下配置项(选填参数)
+        hidden = false,                  -- 是否隐藏SSID, 默认false,不隐藏
+        max_conn = 4 },                  -- 最大客户端数量, 默认4
+        channel=6                        -- AP建立的通道, 默认6
+    })
+    libnetif.setproxy(socket.LWIP_ETH, socket.LWIP_GP, {
+        tp = netdrv.CH390,               -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
+        opts = { spi = 1, cs = 12},      -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
+        ethpower_en = 140,               -- 以太网模块的pwrpin引脚(gpio编号)
+        adapter_addr = "192.168.5.1",    -- adapter网卡的ip地址(选填),需要自定义ip和网关ip时填写
+        adapter_gw= { 192, 168, 5, 1 },   -- adapter网卡的网关地址(选填),需要自定义ip和网关ip时填写
+    })
+    -- 以太网作为出口供WiFi设备上网
+    libnetif.setproxy(socket.LWIP_AP, socket.LWIP_ETH, {
+        ssid = "Hotspot",                -- WiFi名称(string),网卡包含wifi时填写
+        password = "password123",        -- WiFi密码(string),网卡包含wifi时填写
+        tp = netdrv.CH390,               -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
+        opts = { spi = 1, cs = 12},      -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
+        ethpower_en = 140,               -- 以太网模块的pwrpin引脚(gpio编号)
+    })
+    -- 4G作为出口供以太网设备上网
+    libnetif.setproxy(socket.LWIP_ETH, socket.LWIP_GP, {
+        tp = netdrv.CH390,               -- 网卡芯片型号(选填参数),仅spi方式外挂以太网时需要填写。
+        opts = { spi = 1, cs = 12},      -- 外挂方式,需要额外的参数(选填参数),仅spi方式外挂以太网时需要填写。
+        ethpower_en = 140,               -- 以太网模块的pwrpin引脚(gpio编号)
+    })
+]]
+function libnetif.setproxy(adapter, main_adapter, other_configs)
+    if adapter == socket.LWIP_ETH then
+        log.info("ch390", "打开LDO供电", other_configs.ethpower_en)
+        gpio.setup(other_configs.ethpower_en, 1, gpio.PULLUP)
+        -- 打开LAN功能
+        -- 配置 SPI 参数,Air8000 使用 SPI 接口与以太网模块进行通信。
+        if other_configs.tp then
+            log.info("netdrv spi挂载以太网", "初始化LAN功能")
+            local result = spi.setup(
+                other_configs.opts.spi, -- spi id
+                nil, 0,                 -- CPHA
+                0,                      -- CPOL
+                8,                      -- 数据宽度
+                51200000                -- ,--波特率
+            )
+            log.info("main", "open spi", result)
+            if result ~= 0 then -- 返回值为 0,表示打开成功
+                log.error("main", "spi open error", result)
+                gpio.close(other_configs.ethpower_en)
+                return false
+            end
+        end
+        -- 初始化以太网,Air8000 指定使用 CH390 芯片。
+        log.info("netdrv", "初始化以太网", other_configs.tp, other_configs.opts)
+        netdrv.setup(socket.LWIP_ETH, other_configs.tp, other_configs.opts)
+        log.info("netdrv", "等待以太网就绪")
+        sys.wait(1000)
+        -- 设置以太网的 IP 地址、子网掩码、网关地址
+        log.info("netdrv", "自定义以太网IP地址", other_configs.adapter_addr, "网关地址", other_configs.adapter_gw)
+        netdrv.ipv4(socket.LWIP_ETH, other_configs.adapter_addr or "192.168.5.1", "255.255.255.0", "0.0.0.0")
+        -- 获取以太网网络状态,连接后返回 true,否则返回 false,如果不存在就返回 nil。
+        local count = 1
+        while netdrv.ready(socket.LWIP_ETH) ~= true do
+            if count > 600 then
+                log.error("以太网连接超时,请检查配置")
+                gpio.close(other_configs.ethpower_en)
+                return false
+            end
+            count = count + 1
+            -- log.info("netdrv", "等待以太网就绪") -- 若以太网设备没有连上,可打开此处注释排查。
+            sys.wait(100)
+        end
+        log.info("netdrv", "以太网就绪")
+        -- 创建 DHCP 服务器,为连接到以太网的设备分配 IP 地址。
+        log.info("netdrv", "创建dhcp服务器, 供以太网使用")
+        if other_configs.adapter_gw then
+            dhcpsrv.create({ adapter = socket.LWIP_ETH, gw = other_configs.adapter_gw })
+        else
+            dhcpsrv.create({ adapter = socket.LWIP_ETH, gw = { 192, 168, 5, 1 } })
+        end
+        -- 创建 DNS 代理服务,使得以太网接口上的设备可以通过 4G 网络访问互联网。
+        log.info("netdrv", "创建dns代理服务, 供以太网使用")
+    elseif adapter == socket.LWIP_AP then
+        wlan.setMode(wlan.APSTA)
+        -- 打开AP功能,设置混合模式
+        log.info("执行AP创建操作", airlink.ready(), "正常吗?")
+        wlan.createAP(other_configs.ssid, other_configs.password, other_configs.adapter_addr or "192.168.4.1",
+            "255.255.255.0",
+            other_configs.channel, other_configs.ap_opts)
+        -- 设置 AP 的 IP 地址、子网掩码、网关地址
+        netdrv.ipv4(socket.LWIP_AP, other_configs.adapter_addr or "192.168.4.1", "255.255.255.0", "0.0.0.0")
+        -- 获取 WiFi AP 网络状态,连接后返回 true,否则返回 false,如果不存在就返回 nil。
+        log.info("netdrv", "等待AP就绪")
+        local count = 1
+        while netdrv.ready(socket.LWIP_AP) ~= true do
+            -- log.info("netdrv", "等待AP就绪")
+            if count > 600 then
+                log.error("AP创建超时,请检查配置")
+                return false
+            end
+            sys.wait(100)
+            count = count + 1
+        end
+        -- 创建 DHCP 服务器,为连接到 WiFi AP 的设备分配 IP 地址。
+        log.info("netdrv", "创建dhcp服务器, 供AP使用")
+        if other_configs.adapter_gw then
+            dhcpsrv.create({ adapter = socket.LWIP_AP, gw = other_configs.adapter_gw })
+        else
+            dhcpsrv.create({ adapter = socket.LWIP_AP })
+        end
+    elseif adapter == socket.LWIP_USER1 then
+        log.info("ch390", "打开LDO供电", other_configs.ethpower_en)
+        gpio.setup(other_configs.ethpower_en, 1, gpio.PULLUP)
+        -- 打开LAN功能
+        -- 配置 SPI 参数,Air8101 使用 SPI 接口与以太网模块进行通信。
+        log.info("netdrv spi挂载以太网", "初始化LAN功能")
+        local result = spi.setup(
+            other_configs.opts.spi,     -- spi id
+            nil, 0,                     -- CPHA
+            0,                          -- CPOL
+            8,                          -- 数据宽度
+            51200000                    -- ,--波特率
+        )
+        log.info("main", "open spi", result)
+        if result ~= 0 then     -- 返回值为 0,表示打开成功
+            log.error("main", "spi open error", result)
+            gpio.close(other_configs.ethpower_en)
+            return false
+        end
+        -- 初始化以太网,Air8000 指定使用 CH390 芯片。
+        log.info("netdrv", "初始化以太网", other_configs.tp, other_configs.opts)
+        netdrv.setup(socket.LWIP_USER1, other_configs.tp, other_configs.opts)
+        log.info("netdrv", "等待以太网就绪")
+        sys.wait(1000)
+        -- 设置以太网的 IP 地址、子网掩码、网关地址
+        log.info("netdrv", "自定义以太网IP地址", other_configs.adapter_addr, "网关地址", other_configs.adapter_gw)
+        netdrv.ipv4(socket.LWIP_USER1, other_configs.adapter_addr or "192.168.5.1", "255.255.255.0", "0.0.0.0")
+        -- 获取以太网网络状态,连接后返回 true,否则返回 false,如果不存在就返回 nil。
+        local count = 1
+        while netdrv.ready(socket.LWIP_USER1) ~= true do
+            if count > 600 then
+                log.error("以太网连接超时,请检查配置")
+                gpio.close(other_configs.ethpower_en)
+                return false
+            end
+            count = count + 1
+            -- log.info("netdrv", "等待以太网就绪") -- 若以太网设备没有连上,可打开此处注释排查。
+            sys.wait(100)
+        end
+        log.info("netdrv", "以太网就绪")
+        -- 创建 DHCP 服务器,为连接到以太网的设备分配 IP 地址。
+        log.info("netdrv", "创建dhcp服务器, 供以太网使用")
+        if other_configs.adapter_gw then
+            dhcpsrv.create({ adapter = socket.LWIP_USER1, gw = other_configs.adapter_gw })
+        else
+            dhcpsrv.create({ adapter = socket.LWIP_USER1, gw = { 192, 168, 5, 1 } })
+        end
+        -- 创建 DNS 代理服务,使得以太网接口上的设备可以通过 4G 网络访问互联网。
+        log.info("netdrv", "创建dns代理服务, 供以太网使用")
+    end
+
+
+    if main_adapter == socket.LWIP_ETH and available[socket.LWIP_ETH] == connection_states.DISCONNECTED then
+        -- 打开WAN功能
+        log.info("ch390", "打开LDO供电", other_configs.ethpower_en)
+        available[socket.LWIP_ETH] = connection_states.OPENED
+        -- 打开CH390供电
+        gpio.setup(other_configs.ethpower_en, 1, gpio.PULLUP)
+        sys.wait(100)
+        if other_configs.tp == nil then
+            log.info("8101以太网")
+            netdrv.setup(socket.LWIP_ETH)
+        else
+            log.info("config.opts.spi", other_configs.opts.spi, ",config.type", other_configs.tp)
+            -- 配置SPI和初始化网络驱动
+            local result = spi.setup(other_configs.opts.spi, -- spi id
+                nil, 0,                                      -- CPHA
+                0,                                           -- CPOL
+                8,                                           -- 数据宽度
+                51200000                                     -- ,--波特率
+            )
+            log.info("main", "open spi", result)
+            if result ~= 0 then -- 返回值为0,表示打开成功
+                log.info("main", "spi open error", result)
+                gpio.close(other_configs.ethpower_en)
+                return false
+            end
+            -- 初始化指定netdrv设备,
+            local success = netdrv.setup(socket.LWIP_ETH, other_configs.tp, other_configs.opts)
+            if not success then
+                log.error("以太网初始化失败")
+                return false
+            end
+        end
+        netdrv.dhcp(socket.LWIP_ETH, true)
+        local count = 1
+        while 1 do
+            local ip = netdrv.ipv4(socket.LWIP_ETH)
+            if ip and ip ~= "0.0.0.0" then break end
+            if count > 600 then
+                log.error("以太网连接超时,请检查配置")
+                gpio.close(other_configs.ethpower_en)
+                return false
+            end
+            sys.wait(100)
+            count = count + 1
+        end
+    elseif main_adapter == socket.LWIP_USER1 and available[socket.LWIP_USER1] == connection_states.DISCONNECTED then
+        log.info("初始化以太网")
+        -- 打开CH390供电
+        gpio.setup(other_configs.ethpower_en, 1, gpio.PULLUP)
+        sys.wait(100)
+        log.info("config.opts.spi", other_configs.opts.spi, ",config.type", other_configs.tp)
+        available[socket.LWIP_USER1] = connection_states.OPENED
+        -- 配置SPI和初始化网络驱动
+        local result = spi.setup(other_configs.opts.spi, -- spi id
+            nil, 0,                               -- CPHA
+            0,                                    -- CPOL
+            8,                                    -- 数据宽度
+            51200000                              -- ,--波特率
+        )
+        log.info("main", "open spi", result)
+        if result ~= 0 then -- 返回值为0,表示打开成功
+            log.info("main", "spi open error", result)
+            gpio.close(other_configs.ethpower_en)
+            return false
+        end
+        -- 初始化指定netdrv设备,
+        -- socket.LWIP_ETH 网络适配器编号
+        -- netdrv.CH390外挂CH390
+        -- SPI ID 1, 片选 GPIO12
+        netdrv.setup(socket.LWIP_USER1, other_configs.tp, other_configs.opts)
+        netdrv.dhcp(socket.LWIP_USER1, true)
+        log.info("以太网初始化完成")
+        local count = 1
+        while 1 do
+            local ip = netdrv.ipv4(socket.LWIP_USER1)
+            if ip and ip ~= "0.0.0.0" then break end
+            if count > 600 then
+                log.error("以太网连接超时,请检查配置")
+                gpio.close(other_configs.ethpower_en)
+                return false
+            end
+            sys.wait(100)
+            count = count + 1
+        end
+    elseif main_adapter == socket.LWIP_STA and available[socket.LWIP_STA] == connection_states.DISCONNECTED then
+        -- 打开STA功能,设置混合模式
+        wlan.init()
+        wlan.setMode(wlan.APSTA)
+        available[socket.LWIP_STA] = connection_states.OPENED
+        -- 尝试连接Wi-Fi,并处理可能出现的错误
+        wlan.connect(other_configs.ssid, other_configs.password)
+        -- 等待获取IP地址
+        local count = 1
+        while 1 do
+            local ip = netdrv.ipv4(socket.LWIP_STA)
+            if ip and ip ~= "0.0.0.0" then
+                log.info("WiFi STA已连接,IP:", ip)
+                break
+            end
+            if count > 600 then
+                log.error("WiFi STA连接超时,请检查配置")
+                return false
+            end
+            sys.wait(100)
+            count = count + 1
+        end
+        log.info("WiFi STA初始化完成")
+    end
+    dnsproxy.setup(adapter, main_adapter)
+    netdrv.napt(main_adapter)
+    return true
+end
+--httpdns域名解析测试
+local function http_dnstest(adaptertest)
+    local ip = httpdns.ali("baidu.com", { adapter = adaptertest, timeout = 3000 })
+    if ip ~= nil then
+        available[adaptertest] = connection_states.CONNECTED
+    end
+    log.info("httpdns", "baidu.com", ip)
+end
+-- ping操作
+local function ping_request(adaptertest)
+    log.info("dns_request",type_to_string(adaptertest))
+    if adaptertest == socket.LWIP_ETH or adaptertest == socket.LWIP_USER1 then
+        if eth_ping_ip == nil then
+           http_dnstest(adaptertest)
+        else
+            icmp.setup(adaptertest)
+            icmp.ping(adaptertest, eth_ping_ip)
+        end
+    end
+    if adaptertest == socket.LWIP_STA then
+        if wifi_ping_ip == nil then
+            http_dnstest(adaptertest)
+        else
+            icmp.setup(adaptertest)
+            icmp.ping(adaptertest, wifi_ping_ip)
+        end
+    end
+    if adaptertest == socket.LWIP_GP then
+        if eth_ping_ip ~= nil then
+            icmp.setup(adaptertest)
+            icmp.ping(adaptertest, eth_ping_ip)
+        elseif wifi_ping_ip ~= nil then
+            icmp.setup(adaptertest)
+            icmp.ping(adaptertest, wifi_ping_ip)
+        else
+            http_dnstest(adaptertest)
+        end
+    end
+    apply_priority()
+end
+-- 网卡上线回调函数
+local function ip_ready_handle(ip, adapter)
+    log.info("ip_ready_handle", ip, type_to_string(adapter),"state",available[adapter])
+    -- 需要ping操作,ping通后认为网络可用
+    if available[adapter] == connection_states.OPENED then
+        available[adapter] = connection_states.CONNECTING
+    end
+    -- ping_request(adapter)
+end
+-- 网卡下线回调函数
+local function ip_lose_handle(adapter)
+    log.info("ip_lose_handle", type_to_string(adapter))
+    available[adapter] = connection_states.OPENED
+    if current_active == adapter then
+        log.info(type_to_string(adapter) .. " 失效,切换到其他网络")
+        apply_priority()
+    end
+end
+
+--CONNECTING的网卡需要定时ping
+sys.taskInit(function()
+    while true do
+        for _, net_type in ipairs(current_priority) do
+            -- log.info("网卡顺序",type_to_string(net_type),available[net_type])
+            if available[net_type] == connection_states.CONNECTING then
+                ping_request(net_type)
+                log.info(type_to_string(net_type) .. "网卡未ping通,需要定时ping")
+                sys.wait(ping_time)
+            end
+        end
+        sys.wait(1000)
+    end
+end)
+
+local interval_time = nil
+
+--[[
+对正常状态的网卡进行ping测试
+@api libnetif.check_network_status(interval),
+@int 检测间隔时间ms(选填),不填时只检测一次,填写后将根据间隔时间循环检测,会提高模块功耗
+]]
+function libnetif.check_network_status(interval)
+    if interval ~= nil then
+        interval_time = interval
+    end
+    for _, net_type in ipairs(current_priority) do
+        if available[net_type] == connection_states.CONNECTED then
+            available[net_type] = connection_states.CONNECTING
+        end
+    end
+end
+
+--循环ping检测任务,默认不启用
+sys.taskInit(function()
+    while true do
+        if interval_time ~= nil then
+            sys.wait(interval_time)
+            libnetif.check_network_status()
+        end
+        sys.wait(1000)
+    end
+end)
+
+sys.subscribe("PING_RESULT", function(id, time, dst)
+    log.info("ping", type_to_string(id), time, dst);
+    available[id] = connection_states.CONNECTED
+    apply_priority()
+end)
+-- 订阅网络状态变化的消息
+sys.subscribe("IP_READY", ip_ready_handle)
+sys.subscribe("IP_LOSE", ip_lose_handle)
+return libnetif