PHPackages                             aldas/modbus-tcp-client - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. aldas/modbus-tcp-client

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

aldas/modbus-tcp-client
=======================

Modbus TCP protocol client library

3.6.0(3mo ago)212130.3k↓11.9%59[2 issues](https://github.com/aldas/modbus-tcp-client/issues)Apache-2.0PHPPHP ^8.0CI passing

Since Oct 10Pushed 3mo ago16 watchersCompare

[ Source](https://github.com/aldas/modbus-tcp-client)[ Packagist](https://packagist.org/packages/aldas/modbus-tcp-client)[ RSS](/packages/aldas-modbus-tcp-client/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (6)Versions (38)Used By (0)

Modbus TCP and RTU over TCP protocol client
===========================================

[](#modbus-tcp-and-rtu-over-tcp-protocol-client)

[![Latest Version](https://camo.githubusercontent.com/980390f0f31527060e9afed53e9a34538786a2c508e9ee8e1a9d7dce45ebea4d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f616c6461732f6d6f646275732d7463702d636c69656e742e737667)](https://packagist.org/packages/aldas/modbus-tcp-client)[![Packagist](https://camo.githubusercontent.com/f54c17ef7d6e6230a6110f404573a70f7a6ee0d10aa89a67523b145d052ba4c7/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646d2f616c6461732f6d6f646275732d7463702d636c69656e742e737667)](https://packagist.org/packages/aldas/modbus-tcp-client)[![Software License](https://camo.githubusercontent.com/2d029a29c9063a4915dd7e4e0a75cdcd38695f4c69d289ebad5dadebd0b56607/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f616c6461732f6d6f646275732d7463702d636c69656e742e737667)](LICENSE)[![codecov](https://camo.githubusercontent.com/f3479b06da70ea1f1b9c8d9575d5f7165c07a9c18c15b794d5b90e47e5b91817/68747470733a2f2f636f6465636f762e696f2f67682f616c6461732f6d6f646275732d7463702d636c69656e742f6272616e63682f6d61737465722f67726170682f62616467652e737667)](https://codecov.io/gh/aldas/modbus-tcp-client)

- Modbus TCP/IP specification:
- Modbus TCP/IP and RTU simpler description:

Installation
------------

[](#installation)

Use [Composer](https://getcomposer.org/) to install this library as dependency.

```
composer require aldas/modbus-tcp-client
```

Supported functions
-------------------

[](#supported-functions)

- FC1 - Read Coils ([ReadCoilsRequest](src/Packet/ModbusFunction/ReadCoilsRequest.php) / [ReadCoilsResponse](src/Packet/ModbusFunction/ReadCoilsResponse.php))
- FC2 - Read Input Discretes ([ReadInputDiscretesRequest](src/Packet/ModbusFunction/ReadInputDiscretesRequest.php) / [ReadInputDiscretesResponse](src/Packet/ModbusFunction/ReadInputDiscretesResponse.php))
- FC3 - Read Holding Registers ([ReadHoldingRegistersRequest](src/Packet/ModbusFunction/ReadHoldingRegistersRequest.php) / [ReadHoldingRegistersResponse](src/Packet/ModbusFunction/ReadHoldingRegistersResponse.php))
- FC4 - Read Input Registers ([ReadInputRegistersRequest](src/Packet/ModbusFunction/ReadInputRegistersRequest.php) / [ReadInputRegistersResponse](src/Packet/ModbusFunction/ReadInputRegistersResponse.php))
- FC5 - Write Single Coil ([WriteSingleCoilRequest](src/Packet/ModbusFunction/WriteSingleCoilRequest.php) / [WriteSingleCoilResponse](src/Packet/ModbusFunction/WriteSingleCoilResponse.php))
- FC6 - Write Single Register ([WriteSingleRegisterRequest](src/Packet/ModbusFunction/WriteSingleRegisterRequest.php) / [WriteSingleRegisterResponse](src/Packet/ModbusFunction/WriteSingleRegisterResponse.php))
- FC11 - Get Communication Event Counter ([GetCommEventCounterRequest](src/Packet/ModbusFunction/GetCommEventCounterRequest.php) / [GetCommEventCounterResponse](src/Packet/ModbusFunction/GetCommEventCounterResponse.php))
- FC15 - Write Multiple Coils ([WriteMultipleCoilsRequest](src/Packet/ModbusFunction/WriteMultipleCoilsRequest.php) / [WriteMultipleCoilsResponse](src/Packet/ModbusFunction/WriteMultipleCoilsResponse.php))
- FC16 - Write Multiple Registers ([WriteMultipleRegistersRequest](src/Packet/ModbusFunction/WriteMultipleRegistersRequest.php) / [WriteMultipleRegistersResponse](src/Packet/ModbusFunction/WriteMultipleRegistersResponse.php))
- FC17 - Report Server ID ([ReportServerIDRequest](src/Packet/ModbusFunction/ReportServerIDRequest.php) / [ReportServerIDResponse](src/Packet/ModbusFunction/ReportServerIDResponse.php))
- FC22 - Mask Write Register ([MaskWriteRegisterRequest](src/Packet/ModbusFunction/MaskWriteRegisterRequest.php) / [MaskWriteRegisterResponse](src/Packet/ModbusFunction/MaskWriteRegisterResponse.php))
- FC23 - Read / Write Multiple Registers ([ReadWriteMultipleRegistersRequest](src/Packet/ModbusFunction/ReadWriteMultipleRegistersRequest.php) / [ReadWriteMultipleRegistersResponse](src/Packet/ModbusFunction/ReadWriteMultipleRegistersResponse.php))

### Utility functions

[](#utility-functions)

- [Packet::isCompleteLength](src/Utils/Packet.php) - checks if data is complete Modbus TCP request or response packet
- [Packet::isCompleteLengthRTU()](src/Utils/Packet.php) - checks if data is complete Modbus RTU response packet
- [ErrorResponse::is](src/Packet/ErrorResponse.php) - checks if data is Modbus TCP error packet

Requirements
------------

[](#requirements)

- PHP 8.0+
- Release [2.4.0](https://github.com/aldas/modbus-tcp-client/tree/2.4.0) was last to support PHP 7 (7.4 might work with v3.0.0)
- Release [0.2.0](https://github.com/aldas/modbus-tcp-client/tree/0.2.0) was last to support PHP 5.6

Intention
---------

[](#intention)

This library is influenced by [phpmodbus](https://github.com/adduc/phpmodbus) library and meant to be provide decoupled Modbus protocol (request/response packets) and networking related features so you could build modbus client with our own choice of networking code (ext\_sockets/streams/Reactphp/Amp asynchronous streams) or use library provided networking classes (php Streams)

Addresses
---------

[](#addresses)

This library uses PDU / protocol level address numbers (`0` is very first register/coil). This is different from numbering schemes (`0xxxxx`, `1xxxxx`, `3xxxxx`, and `4xxxxx`). The `x` denotes coil or register address starting from `1`.

So when your documentation gives you address as `300102` you should interpret it in this library context as: FC3, address `101`

Endianness
----------

[](#endianness)

Applies to multibyte data that are stored in Word/Double/Quad word registers basically everything that is not (u)int16/byte/char.

So if we receive from network 0x12345678 (bytes: ABCD) and want to convert that to a 32 bit register there could be 4 different ways to interpret bytes and word order depending on modbus server architecture and client architecture. NB: TCP, and UDP, are transmitted in big-endian order so we choose this as base for examples

Library supports following byte and word orders:

- Big endian (ABCD - word1 = 0x1234, word2 = 0x5678)
- Big endian low word first (CDAB - word1 = 0x5678, word2 = 0x1234) (used by Wago-750)
- Little endian (DCBA - word1 = 0x3412, word2 = 0x7856)
- Little endian low word first (BADC - word1 = 0x7856, word2 = 0x3412)

Default (global) endianess used for parsing can be changed with:

```
Endian::$defaultEndian = Endian::BIG_ENDIAN_LOW_WORD_FIRST;
```

For non-global cases see API methods argument list if method support using custom endianess.

See [Endian.php](src/Utils/Endian.php) for additional info and [Types.php](src/Utils/Types.php) for supported data types.

Data types
----------

[](#data-types)

Modbus is binary protocol which revolves about addresses of Registers/Word (16bit, 2 byte of data) and Coils (1 bit of data). Coils are booleans but Register/Word or multiple Registers can hold different data types. Following is ways to access different data types from Registers:

Most of the for data types have optional arguments for providing Endian type. By default data is parsed as being Big Endian Low Word first endian.

Exaple: getting uint32 value as Little Endian Low Word First

```
$dword->getUInt32(Endian::LITTLE_ENDIAN | Endian::LOW_WORD_FIRST);
```

### 1bit - 16bit data types

[](#1bit---16bit-data-types)

1-16bit data types are hold by `Word` class which hold 2 bytes of data.

```
$address = 100;
$word = $response->getWordAt($address);
```

Following methods exists to get different types out of single `Word` instance:

- `boolean` - 1bit, true/false, `$word->isBitSet(11)`
- `byte` - 8bit, 1 byte, range 0 to 255
    - first byte of Word (0) `$word->getHighByteAsInt()`
    - last byte of Word (1) `$word->getLowByteAsInt()`
- `uint16` - 16bit, 2 byte, range 0 to 65535 `$word->getUInt16()`
- `int16` - 16bit, 2 byte, range -32768 to 32767 `$word->getInt16()`

and following additional methods:

- `$word->getBytes()` return Words as array of 2 integers (0-255)

### 32bit data types

[](#32bit-data-types)

17-32bit data types are hold by `DoubleWord` class which hold 4 bytes of data.

```
$address = 100;
$dword = $response->getDoubleWordAt($address);
```

Following methods exists to get different types out of single `DoubleWord` instance:

- `uint32` - 32bit, 4 bytes, range 0 to 4294967295, `$dword->getUInt32()`
- `int32` - 64bit, 8 bytes, range -2147483648 to 2147483647, `$dword->getInt32()`
- `float` - 64bit, 8 bytes, range -3.4e+38 to 3.4e+38, `$dword->getFloat()`

and following additional methods:

- `$dword->getBytes()` return DoubleWord as array of 4 integers (0-255)
- `$dword->getHighBytesAsWord()` returns first 2 bytes as `Word`
- `$dword->getLowBytesAsWord()` returns last 2 bytes as `Word`

### 64bit data types

[](#64bit-data-types)

64bit data types are hold by `QuadWord` class which hold 8 bytes of data. NB: 64-bit PHP supports only up to 63-bit (signed) integers.

```
$address = 100;
$qword = $response->getQuadWordAt($address);
```

Following methods exists to get different types out of single `QuadWord` instance:

- `uint32` - 64bit, 8 bytes, range 0 to 9223372036854775807, `$dword->getUInt64()`
- `int32` - 64bit, 8 bytes, range -9223372036854775808 to 9223372036854775807, `$dword->getInt64()`
- `double` - 64bit, 8 bytes, range 2.2250738585072e-308 to 1.7976931348623e+308, `$dword->getDouble()`

and following additional methods:

- `$qword->getBytes()` return `QuadWord` as array of 8 integers (0-255)
- `$qword->getHighBytesAsDoubleWord()` returns first 4 bytes as `DoubleWord`
- `$qword->getLowBytesAsDoubleWord()` returns last 4 bytes as `DoubleWord`

### Strings

[](#strings)

ASCII (8bit character) string can be extracted from response as utf-8 string

```
$address = 20;
$length = 10;
$string = $response->getAsciiStringAt($address, $length);
```

Example of Modbus TCP (fc3 - read holding registers)
----------------------------------------------------

[](#example-of-modbus-tcp-fc3---read-holding-registers)

Some of the Modbus function examples are in [examples/](examples) folder

Advanced usage:

- command line poller with ReachPHP [examples/example\_cli\_poller.php](examples/example_cli_poller.php)
- send/recieve packets parallel using non-blocking IO:
    - using [ReactPHP](https://reactphp.org/) see 'examples/[example\_parallel\_requests\_reactphp.php](examples/example_parallel_requests_reactphp.php)'
    - using [Amp](https://amphp.org/amp/) see 'examples/[example\_parallel\_requests\_amp.php](examples/example_parallel_requests_amp.php)'

Request multiple packets with higher level API:

```
$address = 'tcp://127.0.0.1:5022';
$unitID = 0; // also known as 'slave ID'
$fc3 = ReadRegistersBuilder::newReadHoldingRegisters($address, $unitID)
    ->unaddressableRanges([[100,110], [1512]])
    ->bit(256, 15, 'pump2_feedbackalarm_do')
    // will be split into 2 requests as 1 request can return only range of 124 registers max
    ->int16(657, 'battery3_voltage_wo')
    // will be another request as uri is different for subsequent int16 register
    ->useUri('tcp://127.0.0.1:5023')
    ->string(
        669,
        10,
        'username_plc2',
        function ($value, $address, $response) {
            return 'prefix_' . $value; // optional: transform value after extraction
        },
        function (\Exception $exception, Address $address, $response) {
            // optional: callback called then extraction failed with an error
            return $address->getType() === Address::TYPE_STRING ? '' : null; // does not make sense but gives you an idea
        }
    )
    ->build(); // returns array of 3 ReadHoldingRegistersRequest requests

// this will use PHP non-blocking stream io to recieve responses
$responseContainer = (new NonBlockingClient(['readTimeoutSec' => 0.2]))->sendRequests($fc3);
print_r($responseContainer->getData()); // array of assoc. arrays (keyed by address name)
print_r($responseContainer->getErrors());
```

Response structure

```
[
    [ 'pump2_feedbackalarm_do' => true, ],
    [ 'battery3_voltage_wo' => 12, ],
    [ 'username_plc2' => 'prefix_admin', ]
]
```

Low level - send packets:

```
$connection = BinaryStreamConnection::getBuilder()
    ->setHost('192.168.0.1')
    ->build();

$packet = new ReadHoldingRegistersRequest(256, 8); //create FC3 request packet

try {
    $binaryData = $connection->connect()->sendAndReceive($packet);

    //parse binary data to response object
    $response = ResponseFactory::parseResponseOrThrow($binaryData);

    //same as 'foreach ($response->getWords() as $word) {'
    foreach ($response as $word) {
        print_r($word->getInt16());
    }
    // print registers as double words in big endian low word first order (as WAGO-750 does)
    foreach ($response->getDoubleWords() as $dword) {
        print_r($dword->getInt32(Endian::BIG_ENDIAN_LOW_WORD_FIRST));
    }

    // set internal index to match start address to simplify array access
    $responseWithStartAddress = $response->withStartAddress(256);
    print_r($responseWithStartAddress[256]->getBytes()); // use array access to get word
    print_r($responseWithStartAddress->getDoubleWordAt(257)->getFloat());
} catch (Exception $exception) {
    echo $exception->getMessage() . PHP_EOL;
} finally {
    $connection->close();
}
```

Example of Modbus RTU over TCP
------------------------------

[](#example-of-modbus-rtu-over-tcp)

Difference between Modbus RTU and Modbus TCP is that:

1. RTU header contains only slave id. TCP/IP header contains of transaction id, protocol id, length, unitid
2. RTU packed has 2 byte CRC appended

See  for more detailsed explanation

This library was/is originally meant for Modbus TCP but it has support to convert packet to RTU and from RTU. See this [examples/rtu.php](examples/rtu.php) for example.

```
$rtuBinaryPacket = RtuConverter::toRtu(new ReadHoldingRegistersRequest($startAddress, $quantity, $slaveId));
$binaryData = $connection->connect()->sendAndReceive($rtuBinaryPacket);
$responseAsTcpPacket = RtuConverter::fromRtu($binaryData);
```

Example of Modbus RTU over USB to Serial (RS485) adapter
--------------------------------------------------------

[](#example-of-modbus-rtu-over-usb-to-serial-rs485-adapter)

See Linux example in 'examples/[rtu\_usb\_to\_serial.php](examples/rtu_usb_to_serial.php)'

Example of Modbus RTU over TCP + higher level API usage
-------------------------------------------------------

[](#example-of-modbus-rtu-over-tcp--higher-level-api-usage)

See example in 'examples/[rtu\_over\_tcp\_with\_higherlevel\_api.php](examples/rtu_over_tcp_with_higherlevel_api.php)'

Example of non-blocking socket IO with ReactPHP/Amp (i.e. modbus request are run in 'parallel')
-----------------------------------------------------------------------------------------------

[](#example-of-non-blocking-socket-io-with-reactphpamp-ie-modbus-request-are-run-in-parallel)

- 'examples/[example\_parallel\_requests\_reactphp.php](examples/example_parallel_requests_reactphp.php) - example of non-blocking socket IO with ReactPHP socket library ()
- 'examples/[example\_parallel\_requests\_amp.php](examples/example_parallel_requests_amp.php) - example of non-blocking socket IO with Amp socket library

Example Modbus server (accepting requests) with ReactPHP
--------------------------------------------------------

[](#example-modbus-server-accepting-requests-with-reactphp)

- 'examples/[example\_response\_server.php](examples/example_response_server.php) - example of modbus server

Try communication with PLCs quickly using php built-in web server
-----------------------------------------------------------------

[](#try-communication-with-plcs-quickly-using-php-built-in-web-server)

Examples folder has [index.php](examples/index.php) which can be used with php built-in web server to test out communication with our own PLCs.

```
git clone https://github.com/aldas/modbus-tcp-client.git
cd modbus-tcp-client
composer install
php -S localhost:8080 -t examples/

```

Now open  in browser. See additional query parameters from [index.php](examples/index.php).

Changelog
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md)

Tests
-----

[](#tests)

- all `composer test`
- unit tests `composer test-unit`
- integration tests `composer test-integration`

For Windows users:

- all ` vendor/bin/phpunit`
- unit tests ` vendor/bin/phpunit --testsuite 'unit-tests'`
- integration tests ` vendor/bin/phpunit --testsuite 'integration-tests'`

Static analysis
===============

[](#static-analysis)

Run [PHPStan](https://phpstan.org/) analysis `compose check`

###  Health Score

63

—

FairBetter than 99% of packages

Maintenance79

Regular maintenance activity

Popularity52

Moderate usage in the ecosystem

Community23

Small or concentrated contributor base

Maturity80

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 97.1% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~97 days

Recently: every ~239 days

Total

32

Last Release

110d ago

Major Versions

0.2.1 → 1.0.0-rc2018-06-02

1.1.2 → 2.0.02020-04-07

2.4.0 → 3.0.02022-04-11

PHP version history (4 changes)0.0.1PHP ^5.6 || ^7.0

1.0.0-rcPHP ^7.0

2.1.1PHP ^7.0 || ^8.0

3.0.0PHP ^8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/701db8b9f90e314e74d35cf5b2a88f5308eb036aa74d08bb4b7a54a04055d53d?d=identicon)[aldas](/maintainers/aldas)

---

Top Contributors

[![aldas](https://avatars.githubusercontent.com/u/2320301?v=4)](https://github.com/aldas "aldas (167 commits)")[![harli91](https://avatars.githubusercontent.com/u/3657605?v=4)](https://github.com/harli91 "harli91 (1 commits)")[![leoshtika](https://avatars.githubusercontent.com/u/1560028?v=4)](https://github.com/leoshtika "leoshtika (1 commits)")[![Phenix789](https://avatars.githubusercontent.com/u/330834?v=4)](https://github.com/Phenix789 "Phenix789 (1 commits)")[![samvdb](https://avatars.githubusercontent.com/u/632452?v=4)](https://github.com/samvdb "samvdb (1 commits)")[![WeeSee](https://avatars.githubusercontent.com/u/3421590?v=4)](https://github.com/WeeSee "WeeSee (1 commits)")

---

Tags

modbusmodbus-clientmodbus-rtumodbus-serialmodbus-tcpphpmodbus

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/aldas-modbus-tcp-client/health.svg)

```
[![Health](https://phpackages.com/badges/aldas-modbus-tcp-client/health.svg)](https://phpackages.com/packages/aldas-modbus-tcp-client)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
