news 2026/8/29 8:39:19

Nexus 5变身GSM语音网关:Ubuntu下AT指令实现同时接听来电与去电

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Nexus 5变身GSM语音网关:Ubuntu下AT指令实现同时接听来电与去电

GSM 话机、Linux 网关、自动化语音系统之间一直有一个常见需求:把一台 Nexus 5 放到 Ubuntu 服务器旁边,让 Ubuntu 接管这台手机的来去电能力,从而实现自动拨号、自动接听,甚至在通话中处理第二个来电。标题里的“同时接听来电+去电”不是双 SIM 并发,而是指系统在无人操作的情况下,既能发起去电,也能响应来电,并且在呼叫等待时保持当前通话、接通新呼叫。

这类场景常见于呼叫中心、测试机群、应急通知网关、物联网语音报警等。开发者在网上搜“Nexus 5 Ubuntu 同时接听来电+去电”,往往是因为已经确认了设备,却卡在串口不识别、AT 命令返回 ERROR、电话通了没有声音这几个环节。这篇内容会按实际落地的顺序展开:先把 Nexus 5 在 Ubuntu 中的角色讲清楚,再准备环境,然后打通 AT 命令、音频路由和并发呼叫,最后给出一个最小 Python 脚本和排查清单。

1. 先理清 Nexus 5 在 Ubuntu 系统中的角色

1.1 Nexus 5 本质是一个 GSM Modem,而不是普通手机

Nexus 5 使用高通基带处理器,负责 GSM/WCDMA/LTE 网络注册、语音编解码、射频收发。Android 系统跑在应用处理器上,通过底层的 modem 接口与基带通信。常规使用中,用户通过电话 App 拨号,App 再调用系统电话服务,系统最终把 AT/QMI 指令下发给基带。

当我们要在 Ubuntu 侧接管电话能力时,目标不是去模拟点击 Android 屏幕,而是要跨过 Android 上层,让 Ubuntu 直接访问基带提供的调制解调器接口。Linux 内核中常见的 modem 设备节点是/dev/ttyUSB0/dev/ttyACM0,它们会以串口或者 ACM 设备形式出现。Nexus 5 在部分 ROM 和内核配置下,通过 USB 连接电脑后会枚举为串口设备;如果只看到 MTP 或 RNDIS 网络设备,说明当前没有暴露 AT 命令通道,需要先调整 USB 配置或换用支持该模式的系统镜像。

1.2 “同时接听来电和去电”到底是什么状态

一块 GSM 调制解调器通常只有一路射频资源。一边正在通话,一边又来新来电时,网络侧通过呼叫等待(Call Waiting)把第二个呼叫送到 modem。modem 上报+CCWA通知事件,主控端可以选择保持当前通话、接听新呼叫,也可以拒绝新呼叫。

所以从系统状态来看,“同时”并不代表射频并行,而是 modem 在同一时间维持多个呼叫状态:一路处于通话中(Active),另一路处于保持中(Held)或等待接听状态。实现的关键在于 AT 指令要支持AT+CHLD,运营商要开通呼叫等待业务,SIM 卡所在网络也要支持这项功能。

1.3 为什么用 AT 命令而不是 Android 上层

Android 上层提供的电话 API 需要在系统内运行 App,而且 Nexus 5 如果刷入原生 Android,远程控制需要 adb、root 权限或者编写 Android App。这些方式都不适合 Ubuntu 侧做独立的自动语音网关。

AT 命令是传统 modem 的标准控制方式。只要串口通路打通,Ubuntu 就可以发送ATD去电、ATA接听、ATH挂断,并解析RING+CLIP+CCWA等主动上报。它不依赖手机屏幕是否亮着、不依赖某个 App 是否运行,更适合无人值守环境。

注意:AT 命令控制的是信令层。电话里的语音需要通过音频通路进入 Ubuntu,因此后面必须单独处理音频路由。

2. Ubuntu 端环境准备:驱动、依赖和串口权限

2.1 安装串口和 modem 调试工具

本文以 Ubuntu 20.04/22.04/24.04 为例。先用包管理器安装基础工具:

sudo apt update sudo apt install minicom picocom python3 python3-pip python3-serial modemmanager usb-modeswitch

这里有几个工具的角色不同:

  • minicompicocom:终端串口调试工具,用来先确认 AT 通道是否正常。
  • python3-serial:Python 脚本读写串口时要用的库。
  • modemmanager:Ubuntu 自带的 modem 管理服务。实际开发时它可能自动占用串口,所以调试阶段建议先停掉。
  • usb-modeswitch:部分 USB 设备需要切换到 modem 模式时会用到。

如果后面还要处理音频,建议安装:

sudo apt install pulseaudio pulseaudio-utils alsa-utils

2.2 停掉 ModemManager,避免串口被占用

Ubuntu 上的 ModemManager 会自动探测新接入的串口 modem。它存在的好处是方便图形化网络连接管理,但对 AT 命令调试会造成干扰:你打开串口发AT,ModemManager 可能已经抢占了设备,导致没有任何响应。

临时停止服务:

sudo systemctl stop ModemManager sudo systemctl disable ModemManager

disable 会让服务开机不启动。如果只是临时调试,用 stop 就够了;如果要长期作为语音网关运行,建议保留 stop 状态,用自己写的守护进程管理 modem。

2.3 确认串口设备号和权限

接入 Nexus 5 后,用dmesg查看内核日志:

dmesg | tail -n 30

常见输出是:

usb 1-1.2: new high-speed USB device number 8 using xhci_hcd usb 1-1.2: New USB device found: idVendor=18d1, idProduct=4ee1 usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3 cdc_acm 1-1.2:1.0: ttyACM0: USB ACM device

此时设备节点是/dev/ttyACM0。不同的内核和 ROM 也可能是/dev/ttyUSB0。查看设备:

ls -l /dev/ttyACM0

一般输出crw-rw---- 1 root dialout ...。当前用户要加入dialout组才能读写串口:

sudo usermod -aG dialout $USER

重新登录后使用picocom测试:

picocom -b 115200 /dev/ttyACM0

如果串口通,输入AT回车,会回显OK。如果不通,先检查设备号、权限、ModemManager 是否已停止。

如果是在虚拟机里跑 Ubuntu,还要把 Nexus 5 的 USB 设备从宿主机透传到虚拟机。VMware 的“虚拟机 -> 可移动设备”和 VirtualBox 的“设备 -> USB”菜单里都能找到设备名称。

3. 用 AT 命令打通来电和去电

3.1 必须掌握的 AT 命令组

AT 命令是文本指令,以\r结尾。手动调试时最容易忘的就是换行符和结果码。下面这组命令是语音网关最常用的。

命令作用典型返回
AT检测 modem 是否在线OK
ATI查询设备信息Nexus 5...或厂商信息
AT+CPIN?查询 SIM 卡 PIN 状态+CPIN: READY
AT+CSQ查询信号强度+CSQ: 18,99
AT+CREG?查询网络注册状态+CREG: 0,1
AT+CLIP=1开启来电号码显示OK
AT+CCWA=1开启呼叫等待提示OK
ATD13800138000;语音拨号,注意分号OKCONNECT
ATA接听来电OKCONNECT
ATH0挂断电话OK
AT+CHUP强制挂断当前呼叫OK
AT+CLCC列出当前呼叫状态+CLCC: 1,...
AT+CHLD=2保持当前通话,接听新来电OK
AT+CHLD=3发起多方通话OK

3.2 去电的完整流程

用串口工具连接后,拨一个号码:

ATD13800138000;

注意末尾分号不能省。AT 命令里,ATD后面直接跟号码再加分号,表示语音呼叫;不加分号会被当成数据呼叫,在很多 modem 上会返回错误或进入数据连接流程。

拨号后的状态结果有两种常见情况:

  • 返回OK:呼叫已建立成功,或者系统还在等待对方接听。
  • 返回CONNECT:某些 modem 在通话建立时会上报这个结果码。

要确认当前通话状态,可以主动查询:

AT+CLCC

可能的返回:

+CLCC: 1,0,0,0,0,"13800138000",129

字段含义分别是:呼叫序号、呼叫方向(0 表示去电)、状态(0 表示正在呼叫)、模式(0 表示语音)、多媒体属性、号码、号码类型。挂断使用:

ATH0

也可以使用AT+CHUP强制挂断,后者在异常状态下的容忍度更高。

3.3 来电接听的完整流程

来电时 modem 会主动上报:

RING +CLIP: "13800138000",129,"",0,"",0

RING是响铃提示,+CLIP是来电号码。如果看不到+CLIP,可以先执行AT+CLIP=1开启来电显示。

接听命令:

ATA

此时电话接通,双方可以开始通话。挂断同样使用:

ATH0

如果来电时不想接听,直接发送ATH0可以拒接。注意发送ATA前要保证串口没有其他未完成指令,否则 modem 可能处于忙状态,接听命令会被忽略。

3.4 并发呼叫:通话中来电怎么处理

通话状态下,如果第二个电话呼入,modem 会上报+CCWA

+CCWA: "13800138000",1,1

此时需要根据业务需求选择处理方式。

命令效果使用场景
AT+CHLD=1释放当前通话,接听新来电只保留最新呼叫
AT+CHLD=2保持当前通话,接听新来电需要处理多个呼叫
AT+CHLD=3将两路通话并成会议需要撮合多方通话

在“同时接听来电+去电”的网关场景中,最常见的策略是AT+CHLD=2:保持第一通电话,接起第二通电话,然后再根据业务录音或转接。可以在收到+CCWA时自动执行AT+CHLD=2

4. 语音通话的音频通路:没有声音的电话没有意义

4.1 AT 命令只控制信令,声音需要单独路由

AT 命令完成的是“建立呼叫”“接听呼叫”这类控制动作。电话接通后,语音会以 PCM 编码形式在基带芯片和音频设备之间流动。Nexus 5 的音频输出路径可以是耳机孔、蓝牙 SCO 或 USB 音频设备。

在 Ubuntu 侧,音频路由决定了两件事:

  • Ubuntu 能不能听到对方说话。
  • 对方能不能听到 Ubuntu 上的麦克风输入。

如果不配置音频通路,就会出现“电话拨通了、对方听不到声音、自己也听不到声音”的现象,这在调试中非常容易混淆。

4.2 用 ALSA/PulseAudio 选择音频设备

先用arecord -l查看系统中可用的录音设备:

arecord -l

如果 Nexus 5 通过 USB 暴露为声卡,会多出一个 USB Audio 设备。查看当前 PulseAudio 源:

pactl list short sources pactl list short sinks

将默认录音源和播放输出切到 nexus:

pactl set-default-source alsa_input.usb-LGE_Nexus_5_...analog-stereo pactl set-default-sink alsa_output.usb-LGE_Nexus_5_...analog-stereo

如果是蓝牙通路,要先配对:

bluetoothctl pair 11:22:33:44:55:66 connect 11:22:33:44:55:66 trust 11:22:33:44:55:66

蓝牙耳机配好后,PulseAudio 默认会使用 A2DP,但通话语音需要的是 HFP/HSP 的 SCO 音频,所以还要手动切换 profile:

pactl set-card-profile bluez_card.11_22_33_44_55_66 headset_head_unit

不同 Ubuntu 版本的名字和命令略有差别,实际项目要以pactl list cards返回的卡名为准。

4.3 音频切换必须和呼叫状态机配合

音频路由不能随便切。比如去电时,应该在发送ATD之后等对方接听,再切音频设备和麦克风;如果提前切到还没接通的设备,会出现一串错误提示或静音。

4.43 一个简单的语音路由示例

#!/bin/bash # 去电并接通后调用 pactl set-default-source "your_nexus_source_name" pactl set-default-sink "your_nexus_sink_name"

实际项目中,可以在 Python 脚本的dial()之后检测OK,再调用subprocess.run()切换 PulseAudio 设备。

5. 一个最小 Python 脚本:自动拨号、自动接听、处理呼叫等待

5.1 脚本设计

Python 脚本需要同时处理两件事:用户主动发起的去电,以及 modem 主动上报的来电。串口是单通道,多个线程同时写会导致命令交叉,所以读写必须加锁。这里采用一个简单方案:一个监听线程循环读串口,一个主线程发起去电或执行其他业务。

核心点:

  • threading.Lock保护串口写操作。
  • 监听线程解析RING+CLIP+CCWA
  • 当前没有通话时,RING触发ATA
  • 当前已有通话时,+CCWA触发AT+CHLD=2

5.2 核心代码

#!/usr/bin/env python3 import serial import threading import time class NexusModem: def __init__(self, port, baud=115200): self.ser = serial.Serial(port, baud, timeout=1) self.lock = threading.Lock() self.active_calls = 0 def cmd(self, command, wait=2): response = b"" with self.lock: self.ser.reset_input_buffer() self.ser.write((command + "\r").encode()) end = time.time() + wait while time.time() < end: chunk = self.ser.read(1) if chunk: response += chunk if b"OK" in response or b"ERROR" in response or b"CONNECT" in response: break return response.decode(errors="ignore") def dial(self, number): print(f"去电: {number}") return self.cmd(f"ATD{number};", 5) def answer(self): print("接听来电") return self.cmd("ATA", 3) def hangup(self): print("挂断") return self.cmd("ATH0", 2) def hold_and_answer(self): print("保持当前通话,接听新来电") return self.cmd("AT+CHLD=2", 3) def monitor(self): while True: try: line = self.ser.readline().decode(errors="ignore").strip() except serial.SerialException: print("串口异常,等待重连") break if not line: continue print(f"< {line}") if line.startswith("RING"): if self.active_calls > 0: self.hold_and_answer() else: self.answer() self.active_calls += 1 elif line.startswith("+CCWA"): self.hold_and_answer() elif line.startswith("NO CARRIER"): self.active_calls = max(0, self.active_calls - 1) if __name__ == "__main__": modem = NexusModem("/dev/ttyACM0", 115200) print(modem.cmd("AT")) print(modem.cmd("AT+CLIP=1")) print(modem.cmd("AT+CCWA=1")) listener = threading.Thread(target=modem.monitor, daemon=True) listener.start() # 示例:启动后拨出一个号码 # modem.dial("13800138000") while True: time.sleep(1)

这段脚本只做演示。它把active_calls简单增删,实际项目中还要根据+CLCC返回的每一路呼叫状态维护精确状态机。

5.3 运行和验证

先给脚本执行权限并运行:

chmod +x gateway.py python3 gateway.py /dev/ttyACM0

验证方式:

  1. 用另一台手机拨打 Nexus 5 的号码。
  2. 观察终端出现RING+CLIP
  3. 脚本自动发送ATA
  4. 如果音频路由已经配置,双方应能正常通话。
  5. 在通话中让第三方拨打该号码,观察+CCWAAT+CHLD=2

如果只是测试去电,取消脚本中modem.dial("13800138000")的注释,或者单独写一个测试脚本调用dial()

6. 常见问题排查:从串口不通到电话无声音

6.1 常见问题速查表

问题现象常见原因检查方式处理建议
没有/dev/ttyACM0节点USB 枚举为 MTP/RNDIS,或者驱动未加载lsusbdmesg安装 usb-modeswitch;确认 ROM 支持 modem 模式
AT 命令没响应ModemManager 占用了串口sudo systemctl status ModemManager停止 ModemManager,断开重连
AT 命令返回 ERROR波特率不对,或者命令格式错误尝试 9600/115200/460800使用AT测试,确认返回 OK 再执行业务指令
拨号返回 ERRORSIM 卡 PIN 锁定AT+CPIN?先解锁 SIM 或关闭 PIN
去电后对方不响号码末尾没有分号发送ATD13800138000;确认命令末尾有英文分号
来电没有 RING未开启 CLIP/CCWAAT+CLIP=1AT+CCWA=1开启来电显示和呼叫等待
电话通了没声音音频路由未切到 USB/蓝牙pactl list short sources切换默认音频源和输出
同时接听失败运营商未开通呼叫等待AT+CCWA?联系运营商开通呼叫等待业务
串口输入卡在半个命令多线程同时写检查threading.Lock所有写串口操作都要加锁

6.2 串口设备不出现的排查链路

先确认物理连接和枚举:

lsusb

如果设备列表里看不到 Nexus 5,先检查 USB 线和接口。如果看到的是18d1:4ee1这类 Android 设备,需要继续看dmesg判断内核将设备识别成什么。

dmesg | grep -E "usb|tty|ACM"

如果内核已经创建了ttyACM0,说明设备枚举成功,下一步就是权限问题。如果只创建了rndis_host网络接口,说明当前模式不是 modem 模式,需要切换或刷入支持 modem 模式的系统镜像。

6.3 通话建立但没有声音的处理顺序

通话没有声音时,先不要把问题归到 AT 命令。按以下顺序检查:

  1. 确认通话是否真的建立:使用AT+CLCC看呼叫状态是否为0(Active)。
  2. 确认音频设备列表里有没有 USB/蓝牙设备:arecord -lpactl list short sources
  3. 确认 Ubuntu 默认录音源和输出是否正确。
  4. 确认音量没有静音:alsamixer里检查 PCM、Microphone 静音状态。
  5. 确认 USB 音频或蓝牙 SCO 的 profile 是通话模式,而不是 A2DP 播放模式。

7. 用于生产环境还需要补齐的工程能力

7.1 从调试脚本到自启动服务

上面这个 Python 脚本只能算最小演示。生产环境会面对串口断开、SIM 卡松动、modem 死锁、电源断电等问题。建议增加以下模块:

  • 串口断线重连:监听SerialException,延迟后重新打开设备。
  • 看门狗:周期性发送AT,连续多次无响应则重启脚本或调用usb_modeswitch重置设备。
  • 呼叫状态机:不要用简单的active_calls计数,要基于AT+CLCC结果维护真实状态。
  • 日志:所有 AT 收发、音频切换、异常恢复都要记录到文件,方便事后排查。
  • 告警:语音网关无人值守,重要呼叫失败要能推送钉钉、企业微信或邮件。

7.2 发布前检查清单

下面是一份可以直接使用的上线前检查清单。

检查项是否通过
SIM 卡已安装,PIN 已关闭
串口设备节点固定,不使用动态变化的 tty 名
ModemManager 已禁用,不抢占串口
ATAT+CPIN?AT+CSQAT+CREG?均正常返回
来电显示AT+CLIP=1已开启
呼叫等待AT+CCWA=1已开启
音频设备名称已确认,PulseAudio 路由脚本可执行
去电和来电两种场景均已人工验证
通话中第二个来电已验证AT+CHLD=2
脚本异常退出后有重启机制
关键日志已落盘,且不无限增长

7.3 进一步扩展方向

Nexus 5 在 Ubuntu 侧一旦稳定工作,就可以继续向两个方向扩展。

一个是上层业务集成。把 AT 指令封装成 HTTP 或 MQTT 接口,让其他系统调用“拨打”“接听”“挂断”“发起会议”能力。例如一个网页后台点击按钮,服务器通过 Python 脚本控制 Nexus 5 拨打电话,并把通话状态实时回传。

另一个是通信平台对接。Asterisk、FreeSWITCH 这类软交换系统通过 chan_dongle、SIP 网关或 DAHDI 接入 GSM modem,可以把 Nexus 5 当作一个外线通道,实现 GSM 和 SIP 的互通。此时并发呼叫处理不再是简单的一路保持,而是进入完整的多路呼叫路由逻辑。

回到最初的问题:“Nexus 5+Nexus 5 Ubuntu 同时接听来电+去电”。实际上要完成的是设备枚举、AT 信令、音频路由、并发呼叫状态机四层工作。只要这四层打通,Nexus 5 就不再是一部手机,而是一个可以被 Ubuntu 系统任意控制的标准 GSM 语音网关。建议初学者先不要急着接并发呼叫,而是用ATDATA把单路去话、单路来话跑通,再从+CCWA事件开始处理呼叫等待,这样排查问题时不会把信令问题和音频问题混在一起。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/29 8:36:01

弹窗首帧不卡顿:Godot PackedScene 动态实例化场景管理上手指南

弹窗首帧不卡顿&#xff1a;Godot PackedScene 动态实例化场景管理上手指南 【免费下载链接】godot Godot Engine – Multi-platform 2D and 3D game engine 项目地址: https://gitcode.com/GitHub_Trending/go/godot 上次给聊天系统加第 4 种弹窗时&#xff0c;首帧直接…

作者头像 李华
网站建设 2026/8/29 8:35:15

MAS激活脚本指南:Windows免费激活最快路径

MAS激活脚本指南&#xff1a;Windows免费激活最快路径 【免费下载链接】Microsoft-Activation-Scripts Open-source Windows and Office activator featuring HWID, Ohook, TSforge, and Online KMS activation methods, along with advanced troubleshooting. 项目地址: htt…

作者头像 李华
网站建设 2026/8/29 8:34:12

车企造人形机器人:技术复用与量产挑战

最近几年&#xff0c;新能源车企的竞争已经不只是“车”本身&#xff0c;而是开始往“人形机器人”方向延伸。特斯拉、小鹏、小米、比亚迪等新造车或新能源产业链玩家&#xff0c;陆续展示机器人原型、投资初创公司、申请专利&#xff0c;甚至成立独立事业部。行业里把这件事叫…

作者头像 李华
网站建设 2026/8/29 8:31:08

Ant Design 紧凑模式:3 种开关 + 4 个坑一次讲清

Ant Design 紧凑模式&#xff1a;3 种开关 4 个坑一次讲清 【免费下载链接】ant-design An enterprise-class UI design language and React UI library 项目地址: https://gitcode.com/GitHub_Trending/an/ant-design 屏幕空间有限、内容却越堆越多&#xff1f;Ant De…

作者头像 李华
网站建设 2026/8/29 8:30:43

MAS激活脚本:Windows 与 Office 一条命令激活完整教程

MAS激活脚本&#xff1a;Windows 与 Office 一条命令激活完整教程 【免费下载链接】Microsoft-Activation-Scripts Open-source Windows and Office activator featuring HWID, Ohook, TSforge, and Online KMS activation methods, along with advanced troubleshooting. 项…

作者头像 李华