PHP 串行连接读取超时

PHP Serial connection read timeout

我正在想办法解决这个问题

我有一个 'sequence' 要通过串行端口(在 RPI 上)执行。

我在 Laravel 运行 中有一个受监督的 PHP 命令连接到 MQTT 代理。

当我向该代理发送消息时,RPI 会拾取并处理它。现在,我有时间等待用户交互。这里的问题是,有时用户不与系统交互,PI 一直“等待”串行数据。当用户按下按钮时,我会得到我可以处理的串行数据。

我尝试使用 while (true) {} 循环读取串行数据,但它突然停止了。这是一些示例代码;

$configure = new TTYConfigure();
$configure->removeOption("9600");
$configure->setOption("115200");

$this->serialPort = new SerialPort(new SeparatorParser("\n"), $configure);

$serialDevice = config('app.device_type') === 'usb' ? '/dev/ttyACM0' : '/dev/ttyAMA0';

$this->serialPort->open($serialDevice);

// this is a special one, we add an timeout here of 15 seconds, to prevent that the machine would get stuck.
$timeoutStart = time();
$timeout = $timeoutStart + 15; // 15 seconds of timeout.
$aborted = false;

while (true) {
    $data2 = $this->serialPort->read();

    if (Str::contains($data2, "Whatever I want to check for")) {
        // Process the data and get out of this loop via a 'break;' statement
    }


    // check if 15 seconds have passed, if so, then we want to stop the vend sequence.
    if (time() >= $timeout) {
        $this->serialPort->write("C,STOP\n"); // STOP vending
        $aborted = true;
        $this->alert("vending sequence stopped");
    }
}

当我将日志置于真正的循环中时,我看到它在循环,但突然停止循环(我敢打赌这是 $data2 = $this->serialPort->read(); 只是“停止”读取或继续读取串行端口。

我希望能够终止循环并执行 API 调用以恢复在该操作之前发生的一些更改。

这可能吗?如果有怎么办?

我使用的软件包:

如果您查看 lepiaf\SerialPort 的源代码,您会发现它将流设置为非阻塞模式,但是 read 方法执行无限循环,直到找到分隔符。这意味着它永远不会 return 除非收到分隔符,并且根据您的配置,一旦达到 php 最大执行时间,您的脚本将被终止。由于库非常简单,更好的选择是编辑 read 方法并添加超时参数。编辑文件“lepiaf/SerialPort/SerialPort.php”,向下滚动到读取方法(第107行)并更改如下:

public function read($maxElapsed = 'infinite')
{
    $this->ensureDeviceOpen();
    
    $chars = [];
    $timeout = $maxElapsed == 'infinite' ? 1.7976931348623E+308 : (microtime(true) + $maxElapsed);
    do {
        $char = fread($this->fd, 1);
        if ($char === '') {
            if (microtime(true) > $timeout) return false;
            usleep(100);    //Why waste CPU?
            continue;
        }
        $chars[] = $char;
    } while ($char !== $this->getParser()->getSeparator());

    return $this->getParser()->parse($chars);
}

然后在您的代码中将方法调用为:

$data2 = $this->serialPort->read(15);
if ($data2 === false) {
    //Timeout occurred
} elseif (Str::contains($data2, "Whatever I want to check for")) {
    //String found
} else {
    //Data received but string not found
}
if (Str::contains($data2, "Whatever I want to check for"))

以上代码是你的罪魁祸首。

$data2 = $this->serialPort->read();

可能不会一次读取整个字符串,它会在进入读取缓冲区时提供数据。所以最好在内部缓冲区中收集数据并根据您的情况检查缓冲区。

$data2 .= $this->serialPort->read();

确保在循环之前初始化 data2。