ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ESP32-S3智能门铃实战:5分钟实现音视频双向对讲

ESP32-S3智能门铃实战:5分钟实现音视频双向对讲 最近我把家里那台只会响铃的老门铃拆了换了一块ESP32-S3顺手把访客影像和双向语音都加了进去。标题里的“5分钟”我先说清楚不是从零开始5分钟做出成品而是开发环境备好、代码写完的前提下从烧录到在手机上看到画面、听到声音确实可以控制在5分钟内。这篇文章会按我实际手搓的顺序把智能门铃音视频通话的硬件选型、软件架构、摄像头与音频两个核心模块的代码实现以及调试过程中踩过的坑都捋一遍。想用手头ESP32-S3开发板快速做一个能看、能说、能听的入口设备Demo的朋友看这篇就行。1. 项目梳理智能门铃到底需要什么1.1 门铃场景的三个基本需求智能门铃和普通监控摄像头不一样它服务的场景是“有人按门铃你在屋里甚至在外面通过手机和他对话”。这个场景拆开来看有三个基本需求是绕不开的第一是影像可见。访客长什么样、穿了什么衣服、手里有没有拿东西门铃端要能把画面送到室内机或手机上。夜间还要考虑补光或红外但Demo阶段我们先聚焦白天光线正常的画面采集。第二是双向语音。你不仅能听到访客说话还得能隔着门和他讲话让他放快递、等他找房东、或者说一句“我不在家”。很多入门方案只做了单向喊话这不算通话只能算广播。真正到能用的程度麦克风采集、喇叭播放、回声抑制、音频缓冲缺一不可。第三是低延迟。门铃对讲是强交互场景画面延迟超过1秒人站在门口对着一块空屏幕说话会非常尴尬。这个延迟约束直接决定了技术选型的方向也是后面选择轻量协议而不是标准视频流协议的核心原因。1.2 为什么选ESP32-S3我手头有ESP32-C3、经典ESP32和ESP32-S3三块板子最后选了S3不是因为它最新而是它最适合这个场景。先看算力。ESP32-S3是双核Xtensa LX7主频最高240MHz对比ESP32-C3单核160MHz跑图像采集、JPEG编码、TCP/IP协议栈和应用逻辑并行时S3的余量明显大。门铃不是裸跑单任务会同时有摄像头驱动、音频I2S、WiFi网络、HTTP服务四个线程在抢CPU双核优势在这种并发场景里是实打实的。再看内存。S3最大支持外挂16MB八线PSRAM我用的N16R8型号有8MB PSRAM。摄像头在VGA分辨率下一张JPEG图大约20~40KB一帧原始RGB565要600KB左右没有PSRAM根本玩不转。ESP32-C3很多模组不带PSRAM跑起来连帧缓冲都分配不出来。最后看生态。ESP32-S3的官方组件覆盖了camera、音频、AI推理等主要外设esp32-camera库对OV2640、OV3660这些DVP摄像头支持非常成熟遇到问题能搜到大量案例。选成熟芯片和成熟库比一切自己从头造要省力得多。1.3 音视频链路方案选型门铃音视频通话可以走很多条技术路线我梳理过三种比较典型的方案各有适用场景。方案延迟复杂度适用场景缺点完整WebRTC100~300ms极高标准视频通话S3算力紧张开发周期长RTSP/RTMP推流1~3s中监控直播延迟高不适合对讲轻量私有协议200~500ms低智能门铃/局域网对讲需要自己定制信令我最终选了第三条路门铃端内置HTTP/WebSocket服务摄像头输出MJPEG连续帧音频用ADPCM压缩后走WebSocket通道手机端通过浏览器或简单客户端直连门铃的局域网IP。这样不需要搭建服务器不依赖公网调试起来也最直接。这里也解释一下为什么不用标准WebRTC。WebRTC需要Opus音频编码、SRTP加密、ICE/STUN打洞、NACK重传这些模块在电脑上跑没什么感觉但塞进S3这种资源有限的MCU光是内存和代码量就是很大的负担。不是说S3完全不能跑而是为了一个5分钟能跑通的Demo投入产出比太低。2. 硬件准备与开发环境搭建2.1 物料清单与引脚分配这个项目的硬件不复杂大部分模块都是常见的开发板配件我列一个自己实际使用的清单模块型号用途备注主控ESP32-S3-DevKitC-1N16R8主控8MB PSRAM16MB Flash摄像头OV2640 DVP 200万像素视频采集30针FPC排线接口麦克风INMP441 I2S数字麦克风采集访客声音数字输出抗干扰强功放MAX98357A I2S D类功放驱动喇叭3W输出带增益跳线喇叭3W/4Ω小喇叭播放对讲声音门铃体积内可安装电源5V/2A USB电源供电注意喇叭瞬态电流引脚分配我用了一张自己的接线表实际代码里都可以通过宏定义修改摄像头OV2640走DVP并口引脚按esp32-camera库的S3模板接这里不逐一重复官方示例里有现成的s3 devkitc引脚映射。I2S麦克风SCKGPIO4WSGPIO5SDGPIO6。I2S功放BCLKGPIO15LRCGPIO16DINGPIO17。开锁继电器GPIO18低电平触发。这里有两个很容易踩的坑。一个是OV2640模块的排线质量排线过长或接触不良会出现初始化失败或者画面花屏我的经验是排线尽量控制在10cm以内。另一个是INMP441的L/R引脚必须接GND表示左声道输出如果不接或者接反麦克风采集到的数据会变成左右声道混合声音听上去发闷甚至像啸叫。2.2 开发环境VSCode ESP-IDF 搭建开发环境我强烈推荐VSCode搭配乐鑫官方ESP-IDF插件。插件装好后执行“ESP-IDF: Configure ESP-IDF Extension”下载一个稳定版本。这里不要追新选v5.2.x或v5.1.x就够用因为esp32-camera等第三方组件的更新节奏不一定跟得上最新master用太新版本可能遇到API不兼容。工程我建议从官方示例模板创建不要手工建空工程。在插件面板打开“ESP-IDF: Show Example Projects”找一个带WiFi或摄像头的示例做底子然后按自己的模块增删文件。我的工程目录结构大致是这样smart_doorbell/ ├── CMakeLists.txt ├── main/ │ ├── CMakeLists.txt │ ├── main.c │ ├── app_wifi.c │ ├── app_wifi.h │ ├── camera_stream.c │ ├── camera_stream.h │ ├── audio_duplex.c │ ├── audio_duplex.h │ └── http_server.c ├── components/ │ └── esp32-camera/ └── sdkconfigesp32-camera组件目前不能直接用组件管理器拉取需要从官方GitHub仓库手动clone到components目录再把路径写进工程的CMakeLists.txt。main/CMakeLists.txt里要注册所有源文件和头文件目录idf_component_register( SRCS main.c app_wifi.c camera_stream.c audio_duplex.c http_server.c INCLUDE_DIRS . )编译烧录用插件提供的图形化按钮即可第一次编译会下载工具链时间可能比较长后面增量编译就快了。我建议烧录前先打开串口监视器方便看log定位问题。2.3 WiFi与网络参数配置门铃要联网才能和手机通信。我这里把门铃配成STA模式连接家里路由器手机在同一局域网内直接访问门铃IP。如果要实现人在外面也能看到家里门口画面那需要更复杂的公网中转方案属于产品化阶段的事。网络配置有几个关键选项必须调整否则延迟会明显变大。在menuconfig中找到以下路径并开启Component config → LWIP → Enable TCP_NODELAY: ON Component config → WiFi → WiFi modem sleep: OFF Component config → WiFi → Maximum WiFi TX power: 20dBmTCP_NODELAY关闭Nagle算法小数据包不用等待合包直接发送对音频这种小块数据特别重要。WiFi modem sleep如果不关省电模式下会出现几十到几百毫秒的随机延迟画面会突然卡一下。门铃通常是常供电设备这里牺牲功耗换稳定是很值的。WiFi连接代码本身不复杂我用事件组做一个同步点等拿到IP再进入业务逻辑static EventGroupHandle_t s_wifi_event_group; #define WIFI_CONNECTED_BIT BIT0 static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { if (event_base WIFI_EVENT event_id WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_base WIFI_EVENT event_id WIFI_EVENT_STA_DISCONNECTED) { esp_wifi_connect(); } else if (event_base IP_EVENT event_id IP_EVENT_STA_GOT_IP) { ip_event_got_ip_t* event (ip_event_got_ip_t*) event_data; ESP_LOGI(WIFI, got ip: IPSTR, IP2STR(event-ip_info.ip)); xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); } } void app_wifi_start(void) { s_wifi_event_group xEventGroupCreate(); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_create_default_wifi_sta(); wifi_init_config_t cfg WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(cfg)); ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL)); wifi_config_t wifi_config { .sta { .ssid CONFIG_WIFI_SSID, .password CONFIG_WIFI_PASSWORD, }, }; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); }这里有个我自己特别强调的点WiFi的SSID和密码不要硬编码在C文件里通过menuconfig定义成CONFIG_开头的宏这样烧录给朋友时不用改代码重新编译直接改配置就行。3. 视频采集与画面传输的实现3.1 摄像头初始化关键参数解读视频部分我直接复用了esp32-camera组件它在ESP-IDF里的集成度很高。初始化代码的核心是一个camera_config_t结构体每个字段都值得仔细调static camera_config_t camera_config { .pin_pwdn -1, .pin_reset -1, .pin_xclk 10, .pin_sccb_sda 40, .pin_sccb_scl 39, .pin_d7 48, .pin_d6 11, .pin_d5 12, .pin_d4 14, .pin_d3 16, .pin_d2 8, .pin_d1 3, .pin_d0 46, .pin_vsync 9, .pin_href 13, .pin_pclk 15, .xclk_freq_hz 20000000, .ledc_timer LEDC_TIMER_0, .ledc_channel LEDC_CHANNEL_0, .pixel_format PIXFORMAT_JPEG, .frame_size FRAMESIZE_VGA, .jpeg_quality 12, .fb_count 2, .grab_mode CAMERA_GRAB_LATEST, };xclk_freq_hz是摄像头主时钟频率我用20MHz这是OV2640最稳的频率。调到24MHz偶尔能跑但在我板子上出过花屏不建议一开始就往高调。frame_size用VGA也就是640x480门铃看人脸够用了再高分辨率会拖慢帧率。jpeg_quality是JPEG质量值越低质量越好、文件越大我用12~18之间太高质量画质提升不明显反而增加码率。fb_count是帧缓冲数2个缓冲是性能与内存的平衡点如果PSRAM只有4MB建议只开2个8MB PSRAM可以开3~4个减少掉帧。grab_mode设置成CAMERA_GRAB_LATEST也非常关键它的意思是摄像头上层始终拿最新一帧即使上一帧还没被处理完也直接覆盖宁可丢帧也不要积压延迟。对实时通话来说旧帧的处理没有意义拿到手就应该立刻发送。3.2 MJPEG流推送浏览器直接预览视频传输我最开始用的是MJPEG over HTTP也就是multipart/x-mixed-replace分块推送。这种方式有个巨大的好处手机或电脑浏览器不需要装任何插件直接打开URL就能看到实时视频。对于快速验证来说是效率最高的。核心代码逻辑在camera_stream.c里大致如下static void camera_stream_task(void *arg) { httpd_handle_t server (httpd_handle_t)arg; while (true) { camera_fb_t *fb esp_camera_fb_get(); if (!fb) { ESP_LOGE(CAM, Camera capture failed); vTaskDelay(pdMS_TO_TICKS(20)); continue; } // 设置multipart边界头只设置一次 httpd_resp_set_type(server, multipart/x-mixed-replace; boundaryframe); char part_header[64]; int hlen snprintf(part_header, sizeof(part_header), --frame\r\nContent-Type: image/jpeg\r\n Content-Length: %u\r\n\r\n, fb-len); httpd_resp_send_chunk(server, part_header, hlen); httpd_resp_send_chunk(server, (const char *)fb-buf, fb-len); httpd_resp_send_chunk(server, \r\n, 2); esp_camera_fb_return(fb); vTaskDelay(pdMS_TO_TICKS(33)); // 约30fps上限 } }实际发送时有一个很重要的检查httpd_resp_send_chunk的返回值必须判断如果返回ESP_FAIL说明客户端已经断开这时候要立刻退出循环并释放所有资源。有一个阶段我没做这个判断客户端刷新几次页面后门铃就死机原因是socket资源一直被无效连接占着不释放最后把internal RAM耗尽。MJPEG的缺点是单帧体积比H.264大很多VGA质量12的一帧大约20~40KB按15fps算带宽3~5Mbps局域网没有任何压力但公网传输就会比较吃力。作为Demo阶段验证功能这个代价完全可以接受。3.3 内存与帧率的平衡调优跑通第一版之后最先要看的不是画面而是串口日志里Free heap和Internal RAM的使用量。esp32-camera初始化成功后会有类似这样的日志Camera: Allocating 2 x 76KB buffers for DMA说明PSRAM分配成功。如果看到line 0 err: 2 Fail to allocate fb: ...那就是内存不足尤其是内部RAM不够。这时候别急着加分辨率先做三件事第一把fb_count调回2第二把jpeg_quality调高到15以上JPEG文件变小能显著减少内存与带宽压力第三检查sdkconfig里是否开了PSRAM并且确认malloc默认先分配PSRAM。ESP32-S3的PSRAM和内部RAM分配策略可以通过menuconfig控制很多看着像摄像头驱动问题的故障实际都是内存分配策略没配好。帧率上不去还有一个容易忽略的因素就是WiFi发送阻塞。我在调试时遇到一个情况电脑浏览器开着预览页面但切到后台标签页WiFi发送就会不断超时重试摄像头帧缓冲一直被占住帧率直接掉到5fps以下。后来我把帧发送逻辑放到低优先级任务里并缩短了HTTP socket的发送超时情况才缓解。这个问题的本质是实时流系统里发送端必须接受“宁可丢帧也不要追旧帧”的原则否则旧帧会卡住整个管道。4. 双向音频链路的实现4.1 I2S麦克风采集与喇叭播放音频部分是整个门铃里最容易让人头疼的地方。ESP32-S3的I2S外设要做全双工时要么用支持全双工的音频编解码芯片比如ES8311要么像我这样用两个独立的I2S外设分别管采集和播放。我这里用INMP441数字麦克风负责采集MAX98357A功放负责播放两个I2S外设各干各的互不干扰。INMP441采集端的I2S配置如下i2s_config_t i2s_rx_config { .mode I2S_MODE_MASTER | I2S_MODE_RX, .sample_rate 16000, .bits_per_sample I2S_BITS_PER_SAMPLE_16BIT, .channel_format I2S_CHANNEL_FMT_ONLY_LEFT, .communication_format I2S_COMM_FORMAT_STAND_I2S, .intr_alloc_flags ESP_INTR_FLAG_LEVEL1, .dma_buf_count 8, .dma_buf_len 1024, .use_apll false, };有几个参数需要特别说明。采样率我选了16kHz这是语音通话的常见标准既能保住语音清晰度又不会产生太多数据量。channel_format必须设置成ONLY_LEFT因为INMP441的L/R引脚接GND时只输出左声道数据如果不设置对会读到两个声道的重复数据。dma_buf_count和dma_buf_len决定DMA缓冲大小8个1024样本的配置大约占用16KB内部RAM如果内存紧张可以降到4个但声音可能出现卡顿。播放端MAX98357A的配置和采集端基本一样只是mode变成I2S_MODE_TX。MAX98357A是I2S数字输入D类功放直接把数字音频流给它就行它会自己完成DAC转换和放大不需要再外接音频Codec。4.2 音频传输与音画同步策略音频数据不能像视频那样直接一大块一大块地推它讲究实时性和低延迟。我的做法是门铃端每次采集20ms的PCM数据也就是16kHz采样率下320字节然后做ADPCM压缩压成160字节的小包打上序号和时间戳后通过WebSocket推给手机端。手机端收到后解码直接喂给音频播放接口。ADPCM压缩是语音通话里很常见的一招。PCM 16bit的数据直接传的话速率是16kHz乘以2字节等于32KB/s用ADPCM 4bit压缩后变成16KB/s音质几乎没有明显下降。ADPCM编码器本身很小几十行C代码就能实现不需要引入重量级第三方库。这对MCU来说非常友好。音画同步是我在这个项目里花时间比较多的地方。因为视频走的是HTTP分块推送音频走的是独立WebSocket两条链路的网络路径不同到达手机端的延迟天然不一致。我的解决方案是视频每一帧和音频每一个小包都带上门铃端的本地时间戳接收端以视频帧的时间戳为基准如果音频包的缓冲时间超过300ms就主动丢弃一部分旧音频数据保证嘴巴动作和声音尽量对上。这个策略虽然牺牲了一些音频连续性但在门铃对讲场景里比让声音越拖越远要强得多。4.3 快速验证半双工录音回放如果你第一次调I2S音频不建议直接上全双工。我自己的调试路径是先做半双工验证按一下按键开始录音松手之后把录到的数据通过I2S播放出来。这个流程能把问题限制在单条链路上排查起来非常清晰。具体做法是先把I2S初始化为RX模式采集两秒数据存到缓冲区然后重新初始化I2S为TX模式把缓冲区的数据播放出来。如果录音回放清晰说明麦克风、功放、喇叭、I2S配置这一整条链路都是通的。接下来再改成全双工就只需要关注两条链路之间的配合问题而不是怀疑硬件坏了。全双工打开后很快会遇到一个新问题回声。门铃的喇叭和麦克风相距很近对方的声音从喇叭放出来被麦克风又采进去再传回对方耳朵对方就听到自己的回声了。产品级门铃一般会用AEC回声消除算法ESP32平台上可以通过ESP-ADF框架集成。Demo阶段我采用了一个土办法把喇叭音量降到50%以下麦克风和喇叭朝向相反方向能明显减轻回声至少不影响功能验证。5. 调试实录常见问题与优化方向5.1 音视频链路常见问题速查表我把实际调试中遇到的典型问题整理成了一张速查表后续不管是自己维护还是帮朋友排查对着看就行现象可能原因解决办法摄像头初始化失败排线接触不良、供电不足、SCCB上拉缺失换短排线检查3.3V供电加1k上拉画面花屏/条纹xclk频率过高、PSRAM带宽不足降到20MHzfb_count改为2画面卡顿/掉帧严重发送阻塞、WiFi未关modem sleep打开TCP_NODELAY关闭WiFi modem sleep有画面无声音I2S初始化失败、喇叭接线错先跑半双工自测检查功放供电声音嗡嗡响/啸叫麦克风与喇叭距离近、增益过高降低喇叭音量麦克风远离喇叭手机无法访问门铃局域网隔离、IP变化确认同一网段串口打印IP后访问程序重启循环内存不足、看门狗超时查看panic日志降低分辨率、优化heap这七个问题里有一个是新手最容易忽略的程序莫名重启。ESP32-S3的内存分为内部RAM和PSRAM摄像头帧缓冲默认用PSRAM但WiFi协议栈、FreeRTOS任务栈、I2S DMA缓冲这些都抢内部RAM。如果工程里还开了很多用不到的组件比如SD卡驱动、TLS支持内部RAM会非常紧张最终表现为跑几分钟看门狗重启。排查方法是打开menuconfig里的“Component config → ESP System Settings → Panic handler behaviour”让它打印调用栈然后根据log把任务栈调小或者关掉不用的组件。5.2 延迟优化四板斧实测下来局域网条件下我这套Demo的端到端延迟大约在200~400ms。视频约250ms音频约200ms正常对讲没有明显别扭感。如果还想继续压缩延迟按收益排序有四件事可以做第一是调视频编码参数。jpeg_quality从12改到15帧率从15fps降到10fps肉眼几乎看不出画质差异带宽和延迟都能下降20%左右。第二是开启TCP_NODELAY并关闭Nagle算法这个能省掉小包在发送队列里的等待时间对音频包尤其明显。第三是把PCM音频换成更高效的G.711或ADPCM减少传输字节数避免音频包在网络队列里排队。第四是优化发送策略比如视频帧发送放到独立任务发送超时时间缩短旧帧直接丢弃不阻塞新帧。这四件事做完延迟再往下压就很难了因为瓶颈开始转向MCU的编码能力和WiFi半双工特性。如果想突破200ms就得考虑H.264硬件编码和专用语音处理芯片那就是另一个量级的产品方向了。5.3 实测数据与资源占用我用的N16R8模组最终跑出来的数据供大家参考参数数值视频分辨率VGA 640x480视频帧率12~15fps视频码率3~5Mbps音频采样率16kHz/16bit音频编码ADPCM 4bit音频码率16KB/s内部RAM占用约150KBPSRAM占用约600KBFlash占用约1.2MB内部RAM占用150KB这个数字需要格外重视因为ESP32-S3总内部RAM只有512KB左右WiFi协议栈和FreeRTOS已经吃掉不少留给应用的空间非常有限。我最后把摄像头发送任务栈设为4096字节音频采集任务2048字节主任务4096字节这个配置在我这版工程里刚好够用。大家不要照抄要根据自己日志里的Stack high-water mark来调。6. 从Demo到产品化的延伸6.1 供电与低功耗Demo跑通后如果你真的想把它装到门口第一个要面对的问题就是供电。门铃位置通常没有电源插座很多老房子的门铃是两线制的门铃变压器输出交流电压给电磁铃直接接ESP32-S3需要另外设计电源部分。如果改用电池供电事情就复杂了。ESP32-S3加上摄像头全速工作时的峰值电流能到300~400mA几节AAA电池撑不了几天。通常的做法是让门铃平时处于深度睡眠只保留极低功耗的待机中断唤醒。有人按下门铃按钮时用PIR或者按钮信号把芯片唤醒20ms内完成摄像头初始化和WiFi重连通话结束后再进入休眠。这种低功耗方案在乐鑫的light sleep文档里有现成的案例但需要结合门铃的实际使用频率来设计不是简单把WiFi关了就行。6.2 远程访问与安全局域网Demo只能在同一个WiFi下玩出门在外想看门口画面就需要公网中转。安全的做法不是把门铃的HTTP端口直接映射到公网而是让门铃主动连接到云端的信令服务器再由服务器帮忙中继音视频流这其实就是WebRTC里TURN服务器的思路。好处是门铃不开放任何入站端口外部攻击者很难直接扫描到你的设备。另外Demo里我用的HTTP和WebSocket都是明文传输在自家局域网问题不大但一旦上了公网必须加TLS和身份认证。代码层面至少要做到每个设备分配随机token手机连接时校验token所有音视频流量走加密通道。否则别人如果蹭到你的网络就能直接看门口画面这个安全隐患在产品化阶段是绝对不能妥协的。6.3 本地AI能力的想象空间ESP32-S3官方主打的方向之一就是端侧AIesp-dl组件里有人脸检测、人脸识别、人体检测这些轻量级模型。门铃加上人脸识别后可以实现“家里人在门口自动开锁”“陌生人逗留检测并推送通知到手机”这些体验更好的功能。我在另一个项目里单独验证过S3加8MB PSRAM跑轻量人脸检测模型可以达到实时性等把几个模型调好参数再单独写一篇分享。最后再分享一个我自己的调试习惯每次改动硬件或者接线之前先把串口日志完整存一份在日志里标记好时间和当时改了什么再动手。音视频项目里很多问题其实不是代码的原因而是排线松了、电源纹波大了、模块个体差异这些硬件因素。有文档记录能帮你迅速缩小排查范围而不是在“花屏还是爆音”里反复折腾。这个项目最大的价值不是“5分钟搞定”这个噱头而是把ESP32-S3最常用的几个核心外设——摄像头、I2S音频、WiFi网络——串在了一起让你在跑通的同时理解它们之间的协作关系。后续不管你是做可视门铃、婴儿监视器还是室内对讲机这套底子都能直接复用。我下一步打算把音频部分换成ESP-ADF的AEC模块把回声问题彻底处理掉到时候再来更新。
返回列表