aldas / modbus-tcp-client

PHP client for Modbus TCP and Modbus RTU over TCP (can be used for serial)
Apache License 2.0
196 stars 56 forks source link

Explain the the data that we get #100

Closed newfaboc closed 2 years ago

newfaboc commented 2 years ago

Hi aldas, I'm a newbie for PLC. I have try modbus-tcp-lient and it sucess get the data. But i don't know how to read the data. I want read the temperature of chiller. i have read the document of modbus.

here is the part of the document (content ):

Data frame format

Device address       Function code          Data content           CRC16 parity code
8bits                      8bits              N*8bits                     16bits

Device address:That is Unit address,ranging from 0x01 by default. Please refer to the communication
Address Setting for more information.
Function code:Read or Write;
Data content:Data read or wrote corresponding to the function code;
CRC check:Data check method for every frame

I want Get the value of the address 40003 , 40004, and 40005 (temperature of chiller).

may you help me ? what should i do to get it ?

Thank you

examplemodbus

aldas commented 2 years ago

Address 32004 means that it should be requested with FC3 (Read Holding Registers) and from address 2004 Address 40003 means that FC4 should be used and set the start address as 3.

examples/index.php is using FC3 for request but you can change it to FC4 by

$packet = new ReadHoldingRegistersRequest($startAddress, $quantity, $unitId);

to

$packet = new ReadInputRegistersRequest($startAddress, $quantity, $unitId);

NB: make sure to import that class also use ModbusTcpClient\Packet\ModbusFunction\ReadInputRegistersRequest;

I assume that 40003 is int8 type as that value may contain negative values. 40004 and 40005 are probably uint8 only positive values.

This is modified version of examples/fc4.php

<?php

use ModbusTcpClient\Network\BinaryStreamConnection;
use ModbusTcpClient\Packet\ModbusFunction\ReadInputRegistersRequest;
use ModbusTcpClient\Packet\ModbusFunction\ReadInputRegistersResponse;
use ModbusTcpClient\Packet\ResponseFactory;

require __DIR__ . '/../vendor/autoload.php';

$connection = BinaryStreamConnection::getBuilder()
    ->setPort(502)
    ->setHost('127.0.0.1')
    ->build();

$startAddress = 3;
$quantity = 3;
$packet = new ReadInputRegistersRequest($startAddress, $quantity);
echo 'Packet to be sent (in hex): ' . $packet->toHex() . PHP_EOL;

try {
    $binaryData = $connection->connect()
        ->sendAndReceive($packet);
    echo 'Binary received (in hex):   ' . unpack('H*', $binaryData)[1] . PHP_EOL;

    /**
     * @var $response ReadInputRegistersResponse
     */
    $response = ResponseFactory::parseResponseOrThrow($binaryData);
    echo 'Parsed packet (in hex):     ' . $response->toHex() . PHP_EOL;
    echo 'Data parsed from packet (bytes):' . PHP_EOL;
    print_r($response->getData());

    // set internal index to match start address to simplify array access
    $responseWithStartAddress = $response->withStartAddress($startAddress);
    print_r($responseWithStartAddress[3]->getInt16()); // 40003
    print_r($responseWithStartAddress[4]->getUInt16()); // 40004
    print_r($responseWithStartAddress[5]->getUInt16()); // 40005

} catch (Exception $exception) {
    echo 'An exception occurred' . PHP_EOL;
    echo $exception->getMessage() . PHP_EOL;
    echo $exception->getTraceAsString() . PHP_EOL;
} finally {
    $connection->close();
}

NB: your images seems to reference CRC this is implies that RTU is used but I do not understand how in this case you can get valid response with examples/index.php? Does it support TCP?


Bits of register can be checked with print_r($responseWithStartAddress[5]->isBitSet(15)); // is register 40005 bit 15 set?

aldas commented 2 years ago

Note if you need only 0-14 bits from register you can use (i could be wrong here. did not test this)

print_r($responseWithStartAddress[4]->getUInt16() & 0x7FFF); // get 0-14 bits of register 40004
newfaboc commented 2 years ago

NB: your images seems to reference CRC this is implies that RTU is used but I do not understand how in this case you can get valid response with examples/index.php? Does it support TCP? -> i think yes. Like the image of exampel fc3. I enter the ip address of plc and it is response.

I will try your advice. thank you very much aldas.

newfaboc commented 2 years ago

Have you an email ? i will send a document if you want to read it. Thank you aldas.

aldas commented 2 years ago

nope :) but try with FC3 and FC4 starting from address 0. It could be that documentation has some copy-paste error and that 40001 maybe needs to be 30001.

One thing that I forgot - when documents talk about 40001 it would mean that address 1 should be 0 in this library. Modbus docs usually reference addresses starting from 1 but in packet level addresses start from 0 so you need to subtract 1.

and this picture from index.php seems ok to me. dunno if these 130 and 400 values are ok.

newfaboc commented 2 years ago

i have try the modified the example/fc4.php

Packet to be sent (in hex): 2f5d00000006040400030003 Binary received (in hex): 2f5d00000009040406433031325631 Parsed packet (in hex): 2f5d00000009040406433031325631 Data parsed from packet (bytes): Array ( [0] => 67 [1] => 48 [2] => 49 [3] => 50 [4] => 86 [5] => 49 ) responseWithStartAddress[3] -> 17200 responseWithStartAddress[4] -> 12594 responseWithStartAddress[4] -> 22065

i have send after 1 minute but the "print_r" still same result but the Packet and Binary 2f5d.. always different (4 digit first ). i dont know how to read this. because i see the chiller the temperature "water in -> 11.2 C" and "the water out about -> 14.4 C".

May you help me ? what should next i do ?

modified version of examples/fc4.php

aldas commented 2 years ago

dec 22065 -> hex 0x56 0x31-> binary 0101 0110 0011 0001 <-- does not seem to have 15th bit as 1

assuming that 15th bit shows if value is valid then you need to get 0-14 bits. Try this.

    print_r($responseWithStartAddress[3]->isBitSet(15)); // is register 40003 valid?
    print_r($responseWithStartAddress[3]->getUInt16() & 0x7FFF); // 40003 first 14 bits

    print_r($responseWithStartAddress[4]->getUInt16() & 0x7FFF); // 40004 first 14 bits
    print_r($responseWithStartAddress[5]->getUInt16() & 0x7FFF); // 40005 first 14 bits
newfaboc commented 2 years ago
print_r($responseWithStartAddress[3]->isBitSet(15)); // is register 40003 valid?
    print_r($responseWithStartAddress[3]->getUInt16() & 0x7FFF); // 40003 first 14 bits

    print_r($responseWithStartAddress[4]->getUInt16() & 0x7FFF); // 40004 first 14 bits
    print_r($responseWithStartAddress[5]->getUInt16() & 0x7FFF); // 40005 first 14 bits

Packet to be sent (in hex): 293e00000006040400030003 Binary received (in hex): 293e00000009040406433031325631 Parsed packet (in hex): 293e00000009040406433031325631 Data parsed from packet (bytes): Array ( [0] => 67 [1] => 48 [2] => 49 [3] => 50 [4] => 86 [5] => 49 ) isvalid -> responseWithStartAddress[3] -> 17200 responseWithStartAddress[4] -> 12594 responseWithStartAddress[5] -> 22065

the value of "print_r($responseWithStartAddress[3]->isBitSet(15)); // is register 40003 valid? " --> empty

aldas commented 2 years ago

I have no idea. Maybe it is endianess. By default we use big endian but maybe these values are in Little endian.

This is same thing with little endian

// should be added after reuires statements
Endian::$defaultEndian = Endian::LITTLE_ENDIAN;

or

    print_r($responseWithStartAddress[3]->getUInt16(2) & 0x7FFF); // 40003 first 14 bits

    print_r($responseWithStartAddress[4]->getUInt16(2) & 0x7FFF); // 40004 first 14 bits
    print_r($responseWithStartAddress[5]->getUInt16(2) & 0x7FFF); // 40005 first 14 bits
newfaboc commented 2 years ago
print_r($responseWithStartAddress[3]->getUInt16(2) & 0x7FFF); // 40003 first 14 bits

    print_r($responseWithStartAddress[4]->getUInt16(2) & 0x7FFF); // 40004 first 14 bits
    print_r($responseWithStartAddress[5]->getUInt16(2) & 0x7FFF); // 40005 first 14 bits

i have try this and the result is : responseWithStartAddress[3] -> 0 responseWithStartAddress[4] -> 0 responseWithStartAddress[5] -> 0

but if i use big endian the code of this.. the result is : responseWithStartAddress[3] -> 12355 responseWithStartAddress[4] -> 12849 responseWithStartAddress[5] -> 12630

what it means ? I dont know why i have send again after several time it doesnt change.

aldas commented 2 years ago

no idea but try FC3 also with same addresses ($startAddress = 0; $quantity = 3). with and without little endian.

    print_r($responseWithStartAddress[0]->getUInt16(2) & 0x7FFF); // 40003 first 14 bits  / little endian
    print_r($responseWithStartAddress[1]->getUInt16(2) & 0x7FFF); // 40004 first 14 bits
    print_r($responseWithStartAddress[2]->getUInt16(2) & 0x7FFF); // 40005 first 14 bits

    print_r($responseWithStartAddress[0]->getUInt16(1) & 0x7FFF); // 40003 first 14 bits / big endian
    print_r($responseWithStartAddress[1]->getUInt16(1) & 0x7FFF); // 40004 first 14 bits
    print_r($responseWithStartAddress[2]->getUInt16(1) & 0x7FFF); // 40005 first 14 bits
newfaboc commented 2 years ago
print_r($responseWithStartAddress[0]->getUInt16(2) & 0x7FFF); // 40003 first 14 bits  / little endian
    print_r($responseWithStartAddress[1]->getUInt16(2) & 0x7FFF); // 40004 first 14 bits
    print_r($responseWithStartAddress[2]->getUInt16(2) & 0x7FFF); // 40005 first 14 bits

    print_r($responseWithStartAddress[0]->getUInt16(1) & 0x7FFF); // 40003 first 14 bits / big endian
    print_r($responseWithStartAddress[1]->getUInt16(1) & 0x7FFF); // 40004 first 14 bits
    print_r($responseWithStartAddress[2]->getUInt16(1) & 0x7FFF); // 40005 first 14 bits

the result is with FC4: little endian : responseWithStartAddress[0] -> 256 responseWithStartAddress[1] -> 25600 responseWithStartAddress[2] -> 16717 Big Endian : responseWithStartAddress[0] -> 1 responseWithStartAddress[1] -> 100 responseWithStartAddress[2] -> 19777

the result is with FC3: little endian : responseWithStartAddress[0] -> 256 responseWithStartAddress[1] -> 256 responseWithStartAddress[2] -> 512 Big Endian : responseWithStartAddress[0] -> 1 responseWithStartAddress[1] -> 1 responseWithStartAddress[2] -> 130

the value doesnt change after i send 1 minute.

newfaboc commented 2 years ago

hi aldas, after i change From "print_r($responseWithStartAddress[3]->isBitSet(15)); // is register 40003 valid?" to print_r($responseWithStartAddress[3]->isBitSet(14)); // is register 40003 valid?

it return 1. it means 14 bit ?

aldas commented 2 years ago

You are correct. I got this part wrong. The method implementation can be seen here.

https://github.com/aldas/modbus-tcp-client/blob/c39a0e51519c5ef76f6ff0f2fb8e36fb34de2935/src/Utils/Types.php#L236-L244

aldas commented 2 years ago

@newfaboc you got it working? can I close the issue?

newfaboc commented 2 years ago

yes. it works @aldas . Thank you very much.