macOS 虚拟串口通信技术笔记

一、目标

在 macOS 环境下,使用 socat 工具虚拟创建一对相互连接的串口(如 /dev/ttys001/dev/ttys002),并使用 Python 验证其双向通信能力。

二、环境准备

1. 安装 socat

socat 是实现虚拟串口的核心工具,推荐使用 Homebrew 安装。

# 检查 Homebrew 是否安装
# brew --version

# 如果未安装 socat,执行安装
brew install socat

2. 安装 Python 串口库

使用 pippip3 安装 pyserial 库。

pip3 install pyserial

三、使用 socat 创建虚拟串口对

socat 通过创建两个伪终端(PTY)并自动连接它们,实现数据的双向转发。

关键命令

在一个独立的 Terminal 窗口中运行此命令,并保持其运行状态,不要关闭。

socat -d -d pty,raw,echo=0,link=/dev/ttys001 pty,raw,echo=0,link=/dev/ttys002

命令说明

参数/路径 含义
-d -d 开启调试模式,显示 socat 创建的底层设备名。
pty 创建一个伪终端设备(Pseudo Terminal)。
raw,echo=0 设置终端为原始模式(Raw Mode),并关闭回显,适合数据通信。
link=/dev/ttys001 /dev/ 目录下创建符号链接 ttys001 指向第一个 PTY。
link=/dev/ttys002 /dev/ 目录下创建符号链接 ttys002 指向第二个 PTY。

注意: 实际在 macOS 上,socat 可能会在 /tmp/ 下创建链接,例如 /tmp/ttyV0。您的脚本中使用了 /dev/ttys001,这表示 socat 创建的链接已经指向了 /dev 下的设备。请以 socat 实际输出为准。您的成功案例使用了 /dev/ttys001/dev/ttys002

四、Python 验证脚本 (serial_test.py)

使用 Python 脚本验证两个虚拟串口是否能双向通信。

脚本代码 (已验证)

from re import search
import serial
import time

# 端口配置,与 socat 创建的链接路径一致
PORT_A = '/dev/ttys001'
PORT_B = '/dev/ttys002'
BAUDRATE = 9600
TEST_DATA = b"Hello from Serial Port A!"

def setup_serial_port(port):
    """尝试打开指定端口"""
    try:
        # timeout=1 表示读取操作最多等待 1 秒
        ser = serial.Serial(port=port, baudrate=BAUDRATE, timeout=1)
        print(f"成功打开端口: {port}")
        return ser
    except serial.SerialException as e:
        print(f"无法打开端口: {e}")
        return None

def test_communication():
    """测试 A 发送,B 接收的单向通信"""
    ser_a = setup_serial_port(PORT_A)
    ser_b = setup_serial_port(PORT_B)

    if not ser_a or not ser_b:
        print("至少一个端口打开失败,请检查 socat 是否正在运行")
        return

    print("-" * 60)

    try:
        # 1. 端口A发送数据
        print(f"{PORT_A} 发送数据: {TEST_DATA.decode()}")
        ser_a.write(TEST_DATA)
        ser_a.flush() # 确保数据立即发送
        time.sleep(0.1)
        
        # 2. 端口B接收数据
        received_data = ser_b.read(len(TEST_DATA))
        
        # 3. 验证结果
        print(f"{PORT_B} 接收数据: {received_data.decode()}")

        if received_data == TEST_DATA:
            print("\n通信测试成功! 两个虚拟串口可以相互通信")
        else:
            print("\n通信测试失败, 接收到的数据不匹配")
    except Exception as e:
        print(f"发生错误: {e}")
    finally:
        # 4. 关闭端口
        if ser_a and ser_a.is_open:
            ser_a.close()
        if ser_b and ser_b.is_open:
            ser_b.close()
        print("-" * 60)
        print("所有端口已关闭")

if __name__ == "__main__":
    test_communication()

运行和结果

python3 serial_test.py
# 您的成功结果
成功打开端口: /dev/ttys001
成功打开端口: /dev/ttys002
------------------------------------------------------------
/dev/ttys001 发送数据: Hello from Serial Port A!
/dev/ttys002 接收数据: Hello from Serial Port A!

通信测试成功! 两个虚拟串口可以相互通信
------------------------------------------------------------
所有端口已关闭

五、关键故障排除回顾

1. Python 模块问题

  • 错误: ModuleNotFoundError: No module named 'serial'
  • 解决: 缺少 pyserial 库。运行 pip install pyserialpip3 install pyserial

2. C#/.NET 引用问题 (针对未来 C# 开发)

  • 错误: CS1069CS0234 (找不到 SerialPortSystem.Text)
  • 解决: 确保项目引用了必要的 NuGet 包和命名空间。
    • dotnet add package System.IO.Ports
    • 在 C# 文件顶部添加 using System.IO.Ports;using System.Text;

3. 串口端口问题

  • 问题: 无法打开端口: [Errno 16] Resource busy
  • 解决: 端口已被其他程序占用。确保 socat 进程正在运行且没有其他程序(如 IDE 的内置工具)尝试访问这些端口
  • 问题: 无法打开端口: [Errno 2] No such file or directory
  • 解决: 检查 Python 脚本中的 PORT_A/B 路径是否与 socat 命令中 link= 指定的路径完全一致。
posted @ 2025-11-30 19:16  非法关键字  阅读(0)  评论(0)    收藏  举报