# Automation Professionals' Advanced Modbus Driver

**URL:** <https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312>\
**Category:** 3rd Party Modules\
**Created:** [December 8, 2020, 8:45pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312 "2020-12-08T20:45:46Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![kai\_brudevoll](https://avatars.discourse-cdn.com/v4/letter/k/59ef9b/32.png) [@kai\_brudevoll](https://forum.inductiveautomation.com/u/kai_brudevoll)\
**Post date:** [November 5, 2021, 1:53pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/21 "2021-11-05T13:53:51Z")

</div>

Hi

We have been experimenting with this module and its running as a modbus server.

The module seems to run fine on windows, but on linux I get this error:

ServerTCPChannel 05Nov2021 14:48:26 [ModbusTCPServer] Listener Error TCP Channel ServerSocket[unbound]  
java.net.BindException: Permission denied (Bind failed)

any tips on how to solve the permission issue? Im running Ubuntu 20.04.3 LTS and its bound to 0.0.0.0  
ufw has open port 502.

Solved( linux require root access for ports below 1024)

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [November 5, 2021, 5:46pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/22 "2021-11-05T17:46:51Z")

</div>

> [@kai\_brudevoll](#):
>
> Solved( linux require root access for ports below 1024)

Instead of running as root, I recommend adding the following in your systemd configuration:

```auto
[Service]
AmbientCapabilities=CAP_NET_BIND_SERVICE

```

---

<div class="post-metadata">

**Author:** ![plarochelle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/plarochelle/32/29608_2.png) [@plarochelle](https://forum.inductiveautomation.com/u/plarochelle)\
**Post date:** [December 2, 2021, 8:45pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/23 "2021-12-02T20:45:57Z")

</div>

Hi @pturmel any chance you’re Modbus Driver supports function code 23? I don’t see it listed above. We’ve had to use UCON in the past to build this out but would prefer something of the shelf. There is an oven controller we’re working with that requires it:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/e/1e8618ce969eaf0a1f3e4490de7a08aa4648c4b2.png)

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [December 2, 2021, 9:13pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/24 "2021-12-02T21:13:57Z")

</div>

No, sorry. I didn’t implement it because there’s no way in Ignition’s OPC server to signal that a read and a write belong together. If I added support for this, it would have to be through a scripting call.

Let me investigate.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [December 2, 2021, 9:46pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/25 "2021-12-02T21:46:24Z")

</div>

It looks like it would be relatively easy to add a script function of the following form:

```auto
system.opc.rawModbus(devName, unit, functionCode, payload, responseLen)

```

`devName` would be a string, `unit`, `functionCode`, and `responseLen` would be integers, and `payload` would be a byte array or list of bytes.

It would return a [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html), from which you would get the result and response payload. (As a QualifiedValue.)

You would have to construct the byte payload to send and decode the byte payload response.

Give me a few days to work this in.

---

<div class="post-metadata">

**Author:** ![plarochelle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/plarochelle/32/29608_2.png) [@plarochelle](https://forum.inductiveautomation.com/u/plarochelle)\
**Post date:** [December 3, 2021, 8:59pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/26 "2021-12-03T20:59:37Z")

</div>

We already have it built out in Kepware so don’t go too crazy. it would be nice to have an alternative to our custom protocol within ucon but not that big a deal.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [December 3, 2021, 10:17pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/27 "2021-12-03T22:17:53Z")

</div>

Too late. Already coded. Doing some testing. (:

Considering the approach allows supporting any exotic function desired, I think it is worth including.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [December 7, 2021, 7:52pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/28 "2021-12-07T19:52:27Z")

</div>

The scripting function `system.opc.rawModbus()` has now been implemented with the signature shown above. The user manual hasn’t been updated yet, but y’all can play with it:

~~For Ignition v8.1~~

 

~~For Ignition v7.9~~

Note that this function is available in Gateway scope only. For use elsewhere, like in the Designer’s script console, I recommend a gateway message handler of this form:

```auto
def handleMessage(payload):
	from java.lang import Throwable
	import traceback

	dev, unit, func, pay, rsplen = [payload.get(x, None) for x in ['devName', 'unit', 'functionCode', 'payload', 'responseLen']]
	try:
		return system.opc.rawModbus(dev, unit, func, pay, rsplen).get()
	except Throwable, t:
		return serialization.stringException(t)
	except:
		return traceback.format_exc()

```

In a designer script console, @plarochelle’s desired use of function 23 would look like this:

```auto
from java.io import ByteArrayInputStream, ByteArrayOutputStream, DataInputStream, DataOutputStream
from com.inductiveautomation.ignition.common.model.values import QualifiedValue

# Raw implementation of write/read multiple registers, function 23.
# Start by constructing the payload of the Modbus PDU. It is
# a UINT read starting address and a UINT number of read registers (<=125),
# then UINT write starting address and UINT number to write (<=121),
# then USINT bytes to follow, then INT/UINT per register.

baos = ByteArrayOutputStream()
dos = DataOutputStream(baos)
# Read start address and quantity
dos.writeShort(0x2006)
dos.writeShort(1)
# Write start address and quantity
dos.writeShort(0x2006)
dos.writeShort(11)
# Write registers bytes to follow
dos.writeByte(22)
# Write registers
dos.writeShort(0x5753)
dos.writeShort(1)
dos.writeShort(5)
dos.writeFloat(654.321)
dos.writeFloat(123.456)

# Ensure data is in the byte stream
dos.flush()

rpcPayload = {'devName': 'P540Rack485', 'unit': 1, 'functionCode': 23, 'payload': baos.toByteArray(), 'responseLen': 3}
retv = system.util.sendRequest('ModbusTest', 'RawModbus', rpcPayload)

if isinstance(retv, QualifiedValue):
	if retv.quality.good:
		bais = ByteArrayInputStream(retv.value)
		dis = DataInputStream(bais)
		bCount = dis.readByte()
		print "Bytes to follow=%d" % bCount
		while bCount > 0:
			bCount -= 2
			register = dis.readShort()
			print "Register = %d" % register
	else:
		print repr(retv)
else:
	print retv

```

I don’t actually have a device that accepts that function code, so I get a bad QV that indicates the function is unsupported. Testing from someone with such a device would be appreciated, of course.

If you wish to test with something more common, the following is a raw implementation of the common “read holding registers” function 3:

```auto
from java.io import ByteArrayInputStream, ByteArrayOutputStream, DataInputStream, DataOutputStream
from com.inductiveautomation.ignition.common.model.values import QualifiedValue

# Raw implementation of read multiple registers, function 3.
# Start by constructing the payload of the Modbus PDU. It is
# a UINT starting address and a UINT number of registers (<=125).

baos = ByteArrayOutputStream()
dos = DataOutputStream(baos)
dos.writeShort(0)
dos.writeShort(5)
dos.flush()

rpcPayload = {'devName': 'P540Rack485', 'unit': 2, 'functionCode': 3, 'payload': baos.toByteArray(), 'responseLen': 11}
retv = system.util.sendRequest('ModbusTest', 'RawModbus', rpcPayload)

if isinstance(retv, QualifiedValue):
	if retv.quality.good:
		bais = ByteArrayInputStream(retv.value)
		dis = DataInputStream(bais)
		bCount = dis.readByte()
		print "Bytes to follow=%d" % bCount
		while bCount > 0:
			bCount -= 2
			register = dis.readShort()
			print "Register = %d" % register
	else:
		print repr(retv)
else:
	print retv

```

{ Edited to remove links. See below. }

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [December 7, 2021, 7:55pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/29 "2021-12-07T19:55:02Z")

</div>

Ah, that message handler uses one of my common scripts to convert backtraces to strings so they can go through the network back to the designer or a Vision client. Available [here](https://www.automation-pros.com/ignition/serialization.py).

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [December 9, 2021, 7:20pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/30 "2021-12-09T19:20:06Z")

</div>

Manual has been updated to describe the `rawModbus` script function. Release version 1.0.0:

~~For Ignition v8.1.~~

 

~~For Ignition v7.9.~~

Prior version links removed. Purchase through my [Module Sales](https://www.automation-pros.com/modulesales.html) page.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [January 19, 2022, 4:34pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/31 "2022-01-19T16:34:47Z")

</div>

Bugfix version 1.0.1. Fixes a null pointer exception when a given register is subscribed in multiple places at the same pace, and any of them are the bare `IR`, `HR`, or `XR` datatype. /:

~~For Ignition v8.1.~~

 

~~For Ignition v7.9.~~

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [March 10, 2022, 7:12pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/32 "2022-03-10T19:12:06Z")

</div>

Bugfix version 1.0.3. @Kevin.Herron enlightened me on the use of the zero slave address in Modbus TCP, and caused me to review my assumptions. This version stops ignoring address zero as it isn't just the broadcast address.

For ~~Ignition v8.1~~.

For [Ignition v7.9](https://www.automation-pros.com/ignition/Advanced%20Modbus%20Driver-1.0.3.220691857.modl).

---

<div class="post-metadata">

**Author:** ![brandon1](https://avatars.discourse-cdn.com/v4/letter/b/ea666f/32.png) [@brandon1](https://forum.inductiveautomation.com/u/brandon1)\
**Post date:** [June 10, 2022, 4:22pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/33 "2022-06-10T16:22:31Z")

</div>

Hey @pturmel.

I’m trying to use your module and I think I’m doing something wrong.

I have one Ignition gateway running your Modbus Server device and it is bound to 127.0.0.1

It seems to have bound correctly to the port.

There is a separate gateway I’m trying to use to connect to that Modbus Server and it does get connected, but the values being read from the local gateway and the remote gateway are different.

Can you tell me if the Modbus Server, with unit 1 defined, will serve the same value to a local gateway tag as it does to an external Modbus client?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [June 10, 2022, 4:26pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/34 "2022-06-10T16:26:25Z")

</div>

> [@brandon1](#):
>
> 127.0.0.1

that is localhost. So it will only receive connections on the same server. I think you meant to use `0.0.0.0`. That's the wildcard to listen on all interfaces. Remote server will then point at the local server's external IP address.

---

<div class="post-metadata">

**Author:** ![brandon1](https://avatars.discourse-cdn.com/v4/letter/b/ea666f/32.png) [@brandon1](https://forum.inductiveautomation.com/u/brandon1)\
**Post date:** [June 10, 2022, 6:56pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/35 "2022-06-10T18:56:44Z")

</div>

So I changed the address to 0.0.0.0 but the values are still different between the two.

---

<div class="post-metadata">

**Author:** ![brandon1](https://avatars.discourse-cdn.com/v4/letter/b/ea666f/32.png) [@brandon1](https://forum.inductiveautomation.com/u/brandon1)\
**Post date:** [June 10, 2022, 6:59pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/36 "2022-06-10T18:59:02Z")

</div>

I am just trying a holding register:  
[PLC-FC1]HR2517

It is the same on both systems (local Ignition and remote)  
Both are showing up as connected and I can write and read from both but they do not match.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [June 10, 2022, 7:01pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/37 "2022-06-10T19:01:50Z")

</div>

Show me your configurations.

{ Just to clarify: Server device on one gateway listening on 0.0.0.0, _ **Client device** _ on other gateway looking at first gateway’s external IP. }

---

<div class="post-metadata">

**Author:** ![brandon1](https://avatars.discourse-cdn.com/v4/letter/b/ea666f/32.png) [@brandon1](https://forum.inductiveautomation.com/u/brandon1)\
**Post date:** [June 10, 2022, 7:06pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/38 "2022-06-10T19:06:38Z")

</div>

Client Side (my machine):

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/e/d/ed72f1a730c189bfac5ce65f15d2091a19d86640.png)

Server Side (running your module)

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/4/2/42ffda24598bb66d46ca62aa367e3c050c3bcae4.png)  
 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/f/8/f804fb400ae4fe1d54dd37b679de95c647ab599d.png)

Thanks

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [June 10, 2022, 7:08pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/39 "2022-06-10T19:08:56Z")

</div>

Ignition’s native client defaults to node 0 when left out. Mine defaults to node 1 when left out.

---

<div class="post-metadata">

**Author:** ![brandon1](https://avatars.discourse-cdn.com/v4/letter/b/ea666f/32.png) [@brandon1](https://forum.inductiveautomation.com/u/brandon1)\
**Post date:** [June 10, 2022, 7:10pm UTC](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312/40 "2022-06-10T19:10:48Z")

</div>

That was it. Thank you!

[Previous page](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312.md?page=1)

[Next page](https://forum.inductiveautomation.com/t/automation-professionals-advanced-modbus-driver/41312.md?page=3)
