PHP串口读取超时:lepiaf\SerialPort读取阻塞循环终止如何解决?
问题根源
你遇到的循环卡住是因为 lepiaf\SerialPort 库的 read() 方法默认使用阻塞模式读取串口,只要串口没有返回新数据,程序会一直停在 $data2 = $this->serialPort->read(); 这行,不会执行后续的超时判断逻辑。你原有代码里的超时判断就算被触发,也没有加break跳出循环,会继续执行死循环。
解决方案
1. 开启串口非阻塞读取
打开串口后,手动设置底层流为非阻塞模式,这样没有数据时read()会直接返回空字符串,不会卡住程序:
// 你原有打开串口的代码 $serialDevice = config('app.device_type') === 'usb' ? '/dev/ttyACM0' : '/dev/ttyAMA0'; $this->serialPort->open($serialDevice); // 新增:设置串口流为非阻塞模式 stream_set_blocking($this->serialPort->getStream(), false);
2. 调整循环逻辑
修改循环,增加空数据判断、超时后跳出循环、以及短休眠避免CPU被空循环占满:
while (true) { $data2 = $this->serialPort->read(); // 只有读到非空数据才做匹配判断 if (!empty($data2) && Str::contains($data2, "Whatever I want to check for")) { // 处理数据逻辑 break; } // 超时判断 if (time() >= $timeout) { $this->serialPort->write("C,STOP\n"); // 停止售卖 $aborted = true; $this->alert("vending sequence stopped"); // 新增:超时后跳出循环 break; } // 休眠100毫秒,避免空循环占用100% CPU usleep(100000); }
3. 增加超时回滚逻辑
跳出循环后判断是否为超时终止,直接调用回滚接口即可,可以用Laravel自带的Http客户端实现:
if ($aborted) { // 示例:调用回滚API \Http::post('你的回滚接口地址', [ 'order_sn' => $this->currentOrderSn, 'status' => 'timeout_cancel' ]); // 其他本地回滚逻辑,比如修改数据库订单状态等 }
4. 兜底异常处理
因为你用supervisord托管进程,可以在supervisord对应任务的配置中增加startsecs=30参数,限制单次任务最多运行30秒,超时后自动杀掉进程。同时可以注册信号处理函数,捕获进程被终止的信号做兜底回滚:
// 在命令逻辑开头注册信号处理函数 pcntl_signal(SIGTERM, function () { // 进程被强制杀死时的兜底回滚逻辑 if (isset($this->serialPort) && $this->serialPort->isOpen()) { $this->serialPort->write("C,STOP\n"); } // 调用回滚接口 exit; });
内容的提问来源于stack exchange,提问作者Robin
相关产品推荐
相关产品推荐

