PHP不直接支持串口通信,所谓“php485”实为通过系统设备节点或第三方扩展(如php_serial)间接操作RS-485转USB设备;Linux下用ls /dev/ttyUSB*等命令列设备,Windows下可用WMI或遍历COM端口;php_serial扩展是跨平台推荐方案;RS-485本身无设备发现机制,扫描需基于具体协议(如Modbus)轮询地址。

PHP 本身不直接支持串口通信,更没有内置的 php485 扩展——这是一个常见误解。所谓“php485”,通常指用 PHP 调用底层串口(如 RS-485 转 USB 设备)进行通信,实际依赖操作系统提供的串口设备节点(Linux)或 COM 端口(Windows),再通过第三方扩展(如 php_serial)或系统命令间接操作。
Linux 下用 shell 命令快速列出可用串口设备
RS-485 设备接入后一般表现为 /dev/ttyUSB*(CH340/CP2102)、/dev/ttyACM*(Arduino 类)或 /dev/ttyS*(原生串口)。PHP 可调用系统命令获取列表:
-
exec('ls /dev/ttyUSB* 2>/dev/null', $usb)—— 捕获所有 USB 串口 -
exec('ls /dev/ttyACM* 2>/dev/null', $acm)—— 常见于带 CDC ACM 协议的设备 -
exec('dmesg | grep -i "tty\|usb" | tail -10', $log)—— 查看最近内核识别记录,确认设备是否被正确加载驱动
注意:2>/dev/null 是为了屏蔽“无匹配文件”时的报错;PHP 进程需有读取 /dev/ 下设备节点的权限(通常需加入 dialout 用户组)。
Windows 下用 WMI 查询 COM 端口(PHP + COM 扩展)
Windows 环境下,若已启用 com_dotnet 扩展(PHP 7.4+ 默认禁用,需在 php.ini 中取消注释 extension=php_com_dotnet.dll),可通过 WMI 获取物理串口:
立即学习“PHP免费学习笔记(深入)”;
$wmi = new COM("winmgmts://./root/cimv2");
$ports = $wmi->ExecQuery("SELECT Name FROM Win32_SerialPort");
foreach ($ports as $port) {
echo $port->Name . "\n"; // 输出类似 "COM3", "COM4"
}但该方法仅返回“被系统识别为串口硬件”的设备(如原生 COM 口、部分 USB-serial 芯片),对某些 CH340 驱动可能不返回;更可靠的方式是遍历 COM1 到 COM20 并尝试打开:
- 用
fopen('COM3', 'rb+')尝试打开,捕获警告(需开启error_reporting(E_ALL & ~E_WARNING)) - 成功即视为可用,失败则跳过
- 注意:PHP 默认以阻塞方式打开,若端口被占用会卡住,建议配合
stream_set_timeout()或改用php_serial
用 php\_serial 扩展做设备扫描(推荐方案)
php_serial 是一个轻量 C 扩展,支持跨平台串口操作,能真实检测端口是否存在且可访问。安装后可这样扫描:
$serial = new phpSerial();
$available = [];
for ($i = 0; $i <= 20; $i++) {
$port = (PHP_OS_FAMILY === 'Windows') ? "COM" . ($i + 1) : "/dev/ttyUSB$i";
if (@$serial->deviceSet($port) && $serial->deviceOpen()) {
$available[] = $port;
$serial->deviceClose();
}
}
print_r($available); // 如:['/dev/ttyUSB0', '/dev/ttyACM0']关键点:
-
deviceSet()仅设置设备路径,不打开;deviceOpen()才真正尝试访问,失败会返回false - Linux 下需确保 PHP 进程对设备有读写权限(
sudo usermod -a -G dialout www-data) - 该扩展不支持热插拔检测,扫描需在设备插入后手动触发
为什么不能直接用 PHP 读取 RS-485 总线上的所有设备?
RS-485 是物理层标准,本身不提供设备发现机制(不像 USB 有枚举协议)。所谓“485 设备扫描”,本质是向总线发送预设地址轮询帧(如 Modbus RTU 的 0x03 读保持寄存器),再等待响应。这需要:
- 已知目标设备的通信协议(Modbus/自定义/KNX 等)
- 已知波特率、数据位、校验位等串口参数(否则发出去的帧对方无法解析)
- 逐个地址(如 1–247)发送请求,并处理超时与响应校验
PHP 可以做到,但不属于“获取串口列表”,而是上层协议交互——它依赖串口已打开且参数配置正确。漏掉任一参数(比如把 parity 设成 N 实际设备要求 E),就收不到任何响应,容易误判为“无设备”。
串口设备名只是入口,真正让 485 通信跑起来的,是协议细节和硬件握手状态。别在没确认 stty -F /dev/ttyUSB0 9600 cs8 -cstopb -parenb 是否生效前,就急着写 Modbus 请求。











