Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP Serial connection read timeout

I'm trying to figure something out here

I have a 'sequence' to be executed via a serial port (on an RPI).

I have an supervisored PHP command in Laravel running that connects to a MQTT broker.

When I send a message to that broker, the RPI picks it up and processes it. Now, I have a moment in which I wait for user interaction. The issue here is, sometimes the user does not interact with the system and the PI keeps "waiting" for serial data. When a user presses a button, I get serial data, which I can process.

I tried to use a while (true) {} loop that reads the serial data, but it just stops suddenly. Here is some example code;

$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");
    }
}

When I place logs in the true loop, I see it loops, but suddenly stops looping (I bet it's the $data2 = $this->serialPort->read(); that just "stops" reading or keeps reading the serial port.

I want to be able to kill the loops and do an API call to revert some changes that hapend before that action.

Is this possible? If so how?

Packages I use:

  • Laravel lumen
  • PhpMqtt
  • lepiaf\SerialPort
like image 883
Robin Avatar asked Oct 28 '21 07:10

Robin


Video Answer


1 Answers

If you look at the source of lepiaf\SerialPort you'll find it sets the stream in non blocking mode, however the read method does an infinite loop until it finds the separator. This means it will never return unless the separator is received, and depending on your configuration your script will be killed once the php max execution time is reached. Since the library is very simple the better option is to edit the read method adding a timeout parameter. Edit the file "lepiaf/SerialPort/SerialPort.php", scroll down to the read method (line 107) and change it as follows:

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);
}

Then in your code call the method as:

$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
}
like image 135
Daniels118 Avatar answered Oct 19 '22 18:10

Daniels118