传统固网电话是一个在移动互联网时代正濒临淘汰的电信业务。这种使用模拟信号传输音频的设备电路实现简单,也顺便蹭到了模拟信号的一切缺点,例如通话质量不稳定、容易受到干扰等。但是在过去的百年里,它承担了千家万户的语音通话需求,为人们的生活和工作带来了极大的便利。也直到今天,这种最基础的电信基建设施仍作为历史遗留在很多地方被大规模应用。
当然,现在的固网电话早就和百年前的那些古董玩意完全不一样了:有了新的拨号方式,有了新的交换方式,传输线和电气规范也变得更适合连接千万家。再通过在线路上调制一些额外的信号,来电显示的功能也实现了。那么,如何使用现代计算机获得这些电话的来电显示呢?
除了启动 OpenCV,然后把你的摄像头对准来电话机屏幕以外。最简单可靠的方案是利用老式的 56K 调制解调器 Modem 来实现这个功能。这种可以插入电话线,并对电话线上的调制信号进行解调的设备可以说是完成该需求的不二之选。
使用 Modem 获取来电显示(Caller ID)
来电显示信号本质是电信局在响铃期间发送的几行文本。目前世界上有两种广泛使用的来电显示信号调制方法,分别是 FSK 和 DTMF。其中国内广泛应用的 FSK 也是目前兼容性最好,功能最强大的方案。它可以直接传送 ASCII 码从而实现很多高级功能。大多数今天能够成功安装驱动的调制解调器都可以处这种理来电显示信号,为了开启这样的功能,我们不需要面向调制解调器驱动程序进行开发,只需打开调制解调器的串口,并发送一条命令就好:AT+VCID=1\r\n
但某些基于 Conexant 芯片组(例如 HSFi CX11252-11)的调制解调器声称支持来电显示,(即发送 AT 命令后 return OK
)但实际上并不支持此功能。比较稳妥的选择是购买 USB 接口的 Conexant 调制解调器,这些新玩意肯定是完美支持的。
当使用 AT 命令正确配置调制解调器后,当电话响起,你应该可以通过串口收到这样格式的返回:
RING
DATE = 0903
TIME = 2300
NMBR = 139********
其中第一个 RING 在电话铃响起时出现,下面的时间日期和电话号码则是来电显示里所包含的信息,这些信息将在第一声铃声和第二声铃声之间接收到,包含的时间日期也是来电显示电话能够自动校时的实现原理。其中的 NMBR 是我们比较感兴趣的信息。
以 serialport.js 为例,我们可以编写这样一个程序处理来电:
// 监听来自 Modem 的信息,当出现 NMBR 开头的文本便是我们需要的信息
const parser = port.pipe(new ReadlineParser({ delimiter: "\r\n" }));
parser.on("data", (line) => {
if (line === "OK") {
console.log("Modem is ready");
}
if (line.substring(0, 4) === "NMBR") {
onRing(line.substring(7)); // 来电号码处理函数
}
port.resume(); // 在我的机器上需要多这一步才能稳定运行
});
// 使用 AT 命令初始化 Modem,使 Modem 监听来电显示信息
port.write(“AT+VCID=1\r\n“, (err) => {
if (err) {
return console.log("Serial port error on write");
}
console.log("Configuration the modem...");
});
当然,不同品牌的调制解调器往往有着不太一样的 AT 命令,可以先用串口助手把下面的每个都试一试,有哪个返回 OK 哪个就是适合你的命令。不过 AT 命令是需要用 \r\n 结尾的,记得加上。
AT#CID=1
AT%CCID=1
AT+VCID=1
AT#CC1
AT*ID1
最后,我用这个技术,实现了一整套用于办公室的来电推送和电话簿/纸质通讯录生成工具,酷。
想象一下,再往前算二十年,那个每台电脑上都有的调制解调器,对座机非常依赖的年代。用最新潮的 .NET Framework 做出推送客户端,再配合 IIS+ ASP + Access,实现这堆玩意,你就是微软全家桶、办公自动化弄潮儿!