Search code examples
phpserial-portmodbusrs485

Get response from RS485 modbus using PHP


I'm trying to get a response from a command I'm sending using PHP to an RS485 device using the script below:

<?php
class rapidserial{
  function _blocking($device,$mode){
      stream_set_blocking($device, $mode);
      return true;
     }
  }
$rapidserial = new rapidserial();
$device = "COM3";
shell_exec("mode $device BAUD=9600 PARITY=n DATA=8 STOP=2 xon=off octs=off rts=off"); 
$comport = fopen($device, "r+b");
if ($comport === false)
    {
    die("Failed opening com port<br/>");
    }
    else
    {
    echo "Com Port Open<br/>"; 
    }
//Set non-blocking mode for writing
$rapidserial->_blocking($comport,0);
$atcmd = "\x0A\x04\x00\x00\x00\x49\x30\x87"; //
fputs($comport, $atcmd);
usleep(2000);
// Set blocking mode for reading
$rapidserial->_blocking($comport,1);
echo 'Response:';
$res = fgets($comport, 4017);
var_dump ($res);
fclose($comport);
?>

This was taken from a related post I made here : https://superuser.com/questions/1815811/issue-addressing-modbus-via-rs485-and-php-on-windows where I was helped to send a command, the script provided by the person that helped me works. Now I'm trying to send a command and get a response back but it's not working, the response is empty.

I have also tried using https://github.com/toggio/PhpSerialModbus but it tells me that it's "'Unable to open the device".

I know I can communicate with the device because if I use some modbus software I can send the command and get the response back. enter image description here

UPDATE : So I have moved from a Windows device to a Linux device to do this and I have now had some success using a different modbus class in PHP (https://github.com/toggio/PhpSerialModbus) but the issue is that I'm not receiving all of the data back, it seems truncated and even if I try to ask for a register further on (not starting at zero) I do not get all of the data back I'm expecting. I know it is there because if I use the modbus software as shown in the image I can see it.

My code now looks like this :

<?php
include("/var/www/html/PhpSerialModbus.php");
$modbus = new PhpSerialModbus();
$modbus->deviceInit('/dev/ttyUSB0',9600,'none',8,2,'none');
$modbus->deviceOpen();
$modbus->debug = false;
$rawquery="\x0A\x04\x00\x00\x00\x49\x30\x87";
$modbus->sendRawQuery($rawquery.$modbus->crc16($rawquery),false);
$result=$modbus->getResponse(true);
function hex2float($strHex) {
    $hex = sscanf($strHex, "%02x%02x%02x%02x%02x%02x%02x%02x");
    $bin = implode('', array_map('chr', $hex));
    $array = unpack("Gnum", $bin);
    return $array['num'];
}
$bin_result = bin2hex($result);
$bin_result = chunk_split($bin_result, 2, ',');
$bin_result = substr($bin_result, 9);
$myresults = print_r($bin_result, true);
$final_array = explode(",", $myresults);
print_r ($final_array);
?>

Which gives me this:

[0] => 42
[1] => 48
[2] => 33
[3] => 33
[4] => 43
[5] => d3
[6] => 92
[7] => 00
[8] => 43
[9] => 75
[10] => 54
[11] => 86
[12] => 40
[13] => 66
[14] => 66
[15] => 72
[16] => be
[17] => dc
[18] => 7a
[19] => de
[20] => 44
[21] => 5c
[22] => f0
[23] => 0a
[24] => 44
[25] => 34
[26] => 50
[27] => 09
[28] => 42
[29] => f2
[30] => cc
[31] => cd
[32] => be
[33] => f9
[34] => db
[35] => 24
[36] => 40
[37] => 25
[38] => c2
[39] => 8f
[40] => 42
[41] => 06
[42] => cc
[43] => cd
[44] => 42
[45] => 90
[46] => 00
[47] => 07
[48] => 42
[49] => ef
[50] => 99
[51] => 9a
[52] => 43
[53] => d3
[54] => 51
[55] => 65
[56] => 43
[57] => 73
[58] => c2
[59] => 9b
[60] => 40
[61] => 80
[62] =>

Solution

  • The reading loop performed by PhpSerialModbus.php is:

    while( ($byte = $this->serial->ReadPort()) && ((microtime(true)-$startTime)<3.0)) {
        $responseString=$responseString.$byte;
        usleep(50);
    }
    

    There's something wrong with it: it waits 50 microseconds only, which is way too short. No additional data has enough time to arrive and the loop terminates prematurely. Maybe the developer meant 50 milliseconds instead, so you could change it like this:

        usleep(50000);
    

    In case you don't want to alter PhpSerialModbus.php, you can do someting like this in your code:

    $result = $modbus->getResponse(true);
    do
    {
        usleep(50000);
        $data = $modbus->getResponse(true);
        $result .= $data;
    }
    while($data);