PHP串口连接读取超时

Rob*_*bin 7 php serial-port laravel raspberry-pi

我正在尝试在这里解决一些问题

我有一个要通过串行端口(在 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");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

这可能吗?如果是这样怎么办?

我使用的包:

  • Laravel 流明
  • PhpMqtt
  • lepiaf\串口

Dan*_*118 3

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

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

$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
}
Run Code Online (Sandbox Code Playgroud)