ESP32-S3 呼微信接通后没声音之解决办法上一篇移芯 EC718PM 上说话一卡一卡之解决办法本文代码来自开源仓库github.com/tangeai/xiaotai-esp32主工程lckfb-szpi-esp32s3-tirtc/。代码有节选常量和结构没改。把微信语音通话接进开发板最气人的一步连得上却没声音。信令显示一切正常手机端来电能收到、能接听WHIP 也报成功结果两边都不出声随后界面自动回到首页。这个现象在starter_runtime.c里有明确的注释答案/* * WHIP 成功仅表示传输连接建立。微信 VoIP 与参考工程一致必须 * 再等待服务端的 0x2000 / CALL_CONNECTED收到后才允许启动 Opus * 媒体过早发音频会被服务端关闭表现为无声后自动回首页。 */遇到这个现象可以先查媒体是不是启动得太早——源码注释直接点名了这个原因。这篇把设备呼叫微信的信令流程走一遍标出每一步会踩的坑。一、先得让平台知道你会 VoIPprofile 上报设备开机后第一件相关的事是把 VoIP 能力上报给平台拨号排在它后面。starter_runtime.c把设备信息拼成 JSON发到/v1/device/profile关键的一段是profiles里的voip屏幕 320×240、单声道音频、上下行媒体类型intprofile_lengthsnprintf(profile,sizeof(profile),{\hardware\:{\chip_model\:\%s\,\board_model\:\%s\},\firmware_version\:\%s\,\profiles\:{...\voip\:{\screen_width\:320,\screen_height\:240,\audio_rate\:%u,\audio_channels\:1,\down_audio_mt\:\%s\,\up_video_mt\:\none\,\down_video_mt\:\none\,...上报成功后有一行日志ESP_LOGI(TAG,device profile accepted api/v1/device/profile scenesstream,call,voip; WeChat calling is ready);没上报成功就拨号呼叫会在本地直接被拒连请求都发不出去if(contact-sourceSTARTER_CONTACT_WECHAT!s_voip_profile_ready){ESP_LOGW(TAG,WeChat call blocked until VoIP profile is reported);returnfalse;}MQTT 侧同样拦了一遍——收到微信来电但 profile 没就绪忽略这条信令并重新请求 profileif(wechatstrcmp(signal,call_incoming)0!s_voip_profile_ready){ESP_LOGW(TAG,ignoring WeChat call before VoIP profile readiness);cJSON_Delete(root);request_voip_profile();return;}所以排查微信呼叫打不出去第一件事是看串口里有没有WeChat calling is ready这行。重试间隔是 15 秒VOIP_PROFILE_RETRY_MS 15000网络慢时要等。二、拨号微信和设备互呼走的是两条路点联系人呼叫后begin_call_common()先结束当前会话并清理连接结束 AI 会话、关掉是否允许 H5 连接的开关、断开旧连接然后递增用来区分新旧通话的编号。之后按联系人类型分两条路if(s_call_wechat){(void)snprintf(body,sizeof(body),{\device_id\:\%s\,\wx_app_id\:\%s\,\wx_user_openid\:\%s\,\wx_model_id\:\%s\,\wx_room_type\:\%s\,\wx_version_type\:0},s_device_id,s_call_wx_app_id,s_call_peer_id,s_call_wx_model_id,video?video:voice);servicePLATFORM_SERVICE_VOIP;path/v1/voip/device/call;stageCALL_HTTP_VOIP_DIAL;}else{esp_err_terrsubmit_device_call();...}微信走/v1/voip/device/call设备互呼走/v1/call/request。注意一个容易想错的点微信拨号的 HTTP 响应在代码里只读取了call_id真正建连要用的 peer、token 要等 MQTT 的call_incoming推过来if(event-commandCALL_HTTP_VOIP_DIAL){(void)copy_json_string(data,call_id,s_call_id,sizeof(s_call_id),false);cJSON_Delete(root);/* 真正 peer/token/room 来自匹配的 MQTT call_incoming。 */s_deadline_msnow_ms()CALL_CONNECT_TIMEOUT_MS;return;}也就是说主叫流程是HTTP 拨号 → 等 MQTT 回铃回铃里才带建连要用的凭据。等不到回铃HTTP 侧再成功也没用。三、WHIP 建连在本工程放在独立任务里注释点名了原因MQTT 的call_incoming微信侧字段是wx_room_id/peer_id/token解析完就该发起 WHIP 建连了。这里有个栈深度的坑注释写得很直白/* * MQTT call_incoming 的解析帧里有多组 1 KiB peer/token 临时缓冲。不能在 * 该深栈函数中再进入 TiRtcWhipConnect否则 SDK 的握手栈会压穿 * starter_session。下一轮事件在解析帧已退出后才提交 WHIP。 */所以 WHIP 不在 MQTT 回调里调而是 calloc 一个请求对象丢给独立任务去执行。任务本体很短调starter_tirtc_voip_connect(peer_id, token, generation)把连接结果写入EVENT_VOIP_CONNECT_RESULT事件再放入队列然后删掉自己。两个细节值得抄走。第一任务栈从 PSRAM 分配MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT不动内部那块本来就紧张的 RAM——启动时仓库专门给 TiRTC 预留了一段连续的内部堆留给这类握手。第二这个任务用xTaskCreateWithCaps创建结束时对应vTaskDeleteWithCaps注释里提醒普通vTaskDelete会把静态注册的栈和 TCB 每次漏掉一份。四、最大的坑WHIP 成功只算一半WHIP 建连成功事件进来状态还是接通中if(event-modeSTARTER_TIRTC_VOIP){s_call_waiting_confirmtrue;s_deadline_msnow_ms()VOIP_CONNECTED_WAIT_TIMEOUT_MS;ESP_LOGI(TAG,VoIP WHIP connected; awaiting CALL_CONNECTED generation%lu,(unsignedlong)s_connection_generation);return;}注意s_call_waiting_confirm true和那行日志WHIP 成功只表示传输连接建立。真正的接通确认是服务端下发的0x2000CALL_COMMAND_CONNECT收到它才允许启动 Opus 媒体。命令字一共两个0x2000接通、0x2001挂断挂断那条同样从这条消息通道进来。if(event-command!CALL_COMMAND_CONNECT||state!STARTER_RUNTIME_CALL_CONNECTING||!s_call_waiting_confirm){return;}...starter_tirtc_mode_tmodes_call_wechat?STARTER_TIRTC_VOIP:STARTER_TIRTC_CALL;if(starter_media_start(mode,s_connection_generation)!ESP_OK){finish_session(ESP_ERR_INVALID_STATE);return;}s_call_waiting_confirmfalse;s_deadline_ms0;publish_state(STARTER_RUNTIME_CALL_ACTIVE);把这两个时序搞反就是开头那个现象媒体先起来服务端不认把会话关掉手机端表现为无声后自动回首页。等0x2000的超时是 35 秒VOIP_CONNECTED_WAIT_TIMEOUT_MS 35000。设备互呼非微信还要更严一层主叫要等对方已接听加P2P 已连上两个条件都满足、并且拿到非零连接编号才启动媒体。条件没齐就不开等齐了才开if(s_call_wechat||!s_call_outgoing||state!STARTER_RUNTIME_CALL_CONNECTING||!s_call_peer_answered||!s_call_p2p_connected||s_connection_generation0U){return;}五、被叫侧回铃不能当成新来电主叫微信时MQTT 会推一条回铃。这条回铃的字段跟真实来电几乎一样不处理就可能弹两次来电界面。代码用四个条件识别这是我自己外呼的回铃/* 微信外呼的 MQTT 回铃是同一流程不再弹一次来电。 */bool outgoing_wechatwechatstateSTARTER_RUNTIME_CALL_CONNECTINGs_call_wechats_call_outgoing(from_device[0]\0||strcmp(from_device,s_device_id)0)(s_call_id[0]\0||call_id[0]\0||strcmp(s_call_id,call_id)0);紧挨着还有一条防护已经取消的外呼迟到的回推不能再显示成来电。这条判断要求消息来自微信、from_device是本机而且不符合前面的外呼回铃条件——四个条件都查了只要有一条对不上就当垃圾消息丢掉。两种判定写在同一个函数里相隔不到 10 行处理的都是同一种消息自己外呼产生的回铃。另外两类来电会被直接拒掉一是房间类型超出 voice/audio 的范围S3 只做语音注释原话是避免出现’能接听却无视频’的假能力二是忙线。拒接时微信侧带原因码本工程用到的取值不支持的房间类型传 7忙线传 8。六、音频流号对不上订阅就收不到还有一处跟有连接没声音直接相关TiRTC 收到音频订阅回调时按当前模式和流号过滤。微信 VoIP 的音频流号是 10#defineAI_AUDIO_STREAM1U#defineCALL_AUDIO_STREAM10Ubool accepted(modeSTARTER_TIRTC_H5stream_idH5_AUDIO_STREAM)||((modeSTARTER_TIRTC_AI||modeSTARTER_TIRTC_ROOM)stream_idAI_AUDIO_STREAM)||((modeSTARTER_TIRTC_VOIP||modeSTARTER_TIRTC_CALL)stream_idCALL_AUDIO_STREAM);...ESP_LOGI(TAG,audio subscribe mode%d stream%u accepted%d,(int)mode,(unsigned)stream_id,accepted);模式不对或流号不对accepted0对面说什么你都收不到——这是另一类单向无声的来源连接是通的订阅没过。看这行日志能直接定位。自己跑一遍微信通话要在真实设备上验证模拟器没有微信链路绑定的微信账号授权也过不去所以这篇的验证点全部依赖实机。路径需要什么做什么设备模拟器一台电脑不用装 ESP-IDF拉tirtc-server-example仓库thing-connect/device-sim/device-sim-py/下跑device_sim_main.py --mac AA:BB:CC:DD:EE:FF验证的是通话流程框架微信链路验不了浏览器烧固件立创实战派 ESP32-S3 V1.0.1/N16R8 USB 线不用装 ESP-IDFRelease 页下载*-full-16MB.bin浏览器打开 espressif.github.io/esptool-js地址填0x0Flash 三项都选keep烧完连XiaoTai-XXXX热点浏览器开 192.168.6.1 配网改代码自己编ESP-IDF 环境在starter_runtime.c的信令路径里加日志重新编译对着本文对照看本文的验证点在串口日志按信令顺序profile 上报成功device profile accepted api/v1/device/profile scenesstream,call,voip; WeChat calling is ready——没有这行后面全免。拨号提交[DEBUG-call] dial submitted target... typewechat stage...WHIP 完成[DEBUG-voip] WHIP worker completed generation... rc0等确认VoIP WHIP connected; awaiting CALL_CONNECTED generation...——这行出现后要等到第 5 步那行订阅日志才算真接通。订阅通过audio subscribe mode3 stream10 accepted1mode 3 是 VOIP。accepted0表示当前模式和流号的组合没通过检查。诚实限制微信链路有两端设备侧日志只能看到设备这一半服务端怎么处理0x2000仓库里看不到只能从行为反推。另外信令的时延跟网络有关本文不测时延。代码仓库github.com/tangeai/xiaotai-esp32本文代码都在lckfb-szpi-esp32s3-tirtc/components/starter_runtime/src/starter_runtime.c与components/starter_tirtc/src/starter_tirtc.c。国内访问慢可以用 Gitee 镜像gitee.com/tange-ai/xiaotai-esp32。代码开源、可自由取用。手边没有板子也能先看效果。把微信通话接进自己设备时先查这几件事先确认 profile 上报成功没有WeChat calling is ready就别往下查。WHIP 成功不是接通。等0x2000再启动媒体等确认的超时是 35 秒。主叫微信时建连凭据在 MQTT 回铃里拨号 HTTP 的响应在代码里只读了call_id。别在 MQTT 解析函数没返回时直接调 SDK 握手本工程的做法是独立任务加 PSRAM 栈。收不到对方声音先看audio subscribe ... accepted这行检查当前模式和流号的组合。挂断要走统一的通话结束函数媒体、连接、H5 连接开关、超时定时器一起复位漏一样就是下次通话的隐患。
企业数字化 ERP 产品动态
相关推荐
中亚跨境物流避坑指南:渠道选型、密度计费与清关实操全拆解 中亚跨境物流避坑指南:渠道选型、密度计费与清关实操全拆解
做跨境供应链这几年,接触过上百位发中亚、俄线的卖家和采购商,发现 90% 的新手第一次发货都会踩同一个坑:只看单价,忽略计费规则、清关能力和渠道稳定性&… · 2026/11/3 19:22:21
知漫剧解决人物漂移:角色锁定与素材库双保险 AI 漫剧连载生产中,人物形象漂移是长期困扰创作者的难点,反复调试提示词也很难稳住人设。知漫剧(zz.jiaxunai.cn)依托角色锁定机制与素材库管理形成双重保障,在批量生产漫剧时稳定角色外观,搭配全链路工作台… · 2026/11/3 19:22:21
2. 编程语言-Java-JDK安全漏洞 文章目录前言一、Java高危漏洞:CVE-2026-91403一、官方公告二、这个漏洞到底是什么?三、哪些版本受影响?公告点名的(已给补丁线)没写进表的老版本呢?四、怎么做?(按这个顺序… · 2026/11/3 19:21:21
TypeScript Monorepo规范与最佳实践 1. 为什么需要TypeScript monorepo约定?在大型前端项目中,monorepo已经成为管理多包依赖的主流方案。我经历过一个从零搭建的monorepo项目,最初没有制定任何规范,结果三个月后就陷入了配置地狱:不同包的tsconfig互相覆… · 2026/11/3 20:38:02
基于Next.js与Tailwind CSS构建现代化博客系统 1. 项目概述与核心价值十年前我刚入行时,搭建个人博客是每个前端开发者必经的成长仪式。如今虽然各种现成方案层出不穷,但亲手实现完整博客系统仍然是掌握现代前端技术栈的最佳实践。这个项目将带你从零开始,用最新技术栈构建支持Markdown写作… · 2026/11/3 20:38:02
深度学习模型部署优化实战:量化、剪枝与蒸馏的工程实践 1. 项目缘起:模型精度达标了,线上却跑不动 做模型优化这件事,多半不是主动想做的,而是被线上环境逼出来的。我接手这个 Model-Optimizer 项目的时候,团队里的检测模型已经在 GPU 集群上跑了好几个月,离线指… · 2026/11/3 20:38:02
Halcon二维码识别全流程:预处理、建模参数与调参排查实战 做机器视觉这行的人大概都碰到过这种场面:产线上相机架好了、光源调了半天,图像看着挺干净,结果一跑Halcon的二维码识别算子,返回的字符串是空的,连个候选框都没给。更气人的是,同一套脚本换个工位、换张图… · 2026/11/3 20:38:02
社会网络仿真技术的伦理与法律合规实践 1. 社会网络仿真技术概述NetLogo作为一款多主体建模工具,在社会网络仿真领域已经应用了二十余年。我最初接触这个工具是在2010年研究城市交通流模拟时,后来发现它在社会网络分析方面有着独特的优势。社会网络仿真本质上是通过计算模型来模拟人与人之间的… · 2026/11/3 20:38:02
搜狗输入法精简优化指南:性能提升与隐私保护 1. 为什么需要精简优化版输入法在Windows平台上,搜狗拼音输入法长期占据着市场领先地位。但近年来,官方版本逐渐变得臃肿——安装包从早期的20MB膨胀到现在的80MB,后台常驻进程从2个增加到5个,内存占用从80MB飙升到300MB。这种&qu… · 2026/11/3 20:37:01
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46