Difference between revisions of "Modbus Server"

From HSYCO
Jump to navigation Jump to search
 
(55 intermediate revisions by 4 users not shown)
Line 8: Line 8:
  
 
== HSYCO Configuration ==
 
== HSYCO Configuration ==
 +
Add a MODBUSTCPSERVER I/O Server in the [[Settings#I/O Servers|I/O Servers section of the Settings]] and set its parameters:
 +
 +
=== Communication ===
 +
*'''IP Address''': local IP address where to accept connections, leave blank to accept connection on any LAN port and IP address
 +
*'''IP Port''': TCP/IP port to use, leave blank to use default port 502. If you set the port, you should also set the IP address. The address 0.0.0.0 can be used to accept connections on all LAN ports.
 +
 +
{{note|if more than one MODBUSTCPSERVER I/O servers are defined they have to be on different ports or IP addresses.}}
 +
 +
=== High Availability ===
 +
*'''Shutdown when inactive''': defaults to false.
 +
 
=== Options ===
 
=== Options ===
  
Line 15: Line 26:
 
!Values
 
!Values
 
!Description
 
!Description
 +
|-
 +
 +
|rowspan="2"|connectionslog
 +
|rowspan="2"|false
 +
|true
 +
|log all connections and disconnects
 +
|-
 +
|false
 +
|don't log connections
 
|-
 
|-
  
Line 24: Line 44:
 
|false
 
|false
 
|Modbus events for this gateway are not written in the log files, even if the eventsLog option is true in hsyco.ini
 
|Modbus events for this gateway are not written in the log files, even if the eventsLog option is true in hsyco.ini
 +
|-
 +
 +
|rowspan="2"|forcedevents
 +
|rowspan="2"|true
 +
|true
 +
|generate forced events on any write command
 +
|-
 +
|false
 +
|generate events only on registers' data changes
 +
|-
 +
 +
|rowspan="1"|maxconnections
 +
|rowspan="1"|128
 +
|> 0
 +
|maximum number of concurrent connections
 
|-
 
|-
  
Line 53: Line 88:
 
*16: Write Multiple Registers
 
*16: Write Multiple Registers
 
*22: Mask Write Register
 
*22: Mask Write Register
 +
*23: Read/Write Multiple Registers.
  
 
These functions read and write data on four distinct data tables:
 
These functions read and write data on four distinct data tables:
Line 88: Line 124:
 
|16-bit word
 
|16-bit word
 
|Read Write
 
|Read Write
|03 06 16 22
+
|03 06 16 22 23
 
|-
 
|-
  
Line 110: Line 146:
 
|uint
 
|uint
 
|unsigned integer
 
|unsigned integer
|0 ... (232 - 1)
+
|0 ... (2<sup>32</sup> - 1)
 
|2
 
|2
 
|-
 
|-
Line 116: Line 152:
 
|ulong
 
|ulong
 
|unsigned long integer
 
|unsigned long integer
|0 ... (264 - 1)
+
|0 ... (2<sup>64</sup> - 1)
 
|4
 
|4
 
|-
 
|-
  
|short signed
+
|short  
|short integer
+
|signed short integer
 
| -32768 ... +32767
 
| -32768 ... +32767
 
|1
 
|1
Line 128: Line 164:
 
|int
 
|int
 
|signed integer
 
|signed integer
| -231 ... (231 - 1)
+
| -2<sup>31</sup> ... (2<sup>31</sup> - 1)
 
|2
 
|2
 
|-
 
|-
Line 134: Line 170:
 
|long
 
|long
 
|signed long integer
 
|signed long integer
| -263 ... (263 - 1)
+
| -2<sup>63</sup> ... (2<sup>63</sup> - 1)
 
|4
 
|4
 
|-
 
|-
Line 140: Line 176:
 
|float
 
|float
 
|signed IEEE 754 floating point
 
|signed IEEE 754 floating point
| +/- 2-149 ... (2-2-23)x2127 2
+
| +/- 2<sup>-149</sup> ... (2-2<sup>-23</sup>)x2<sup>127</sup>
 +
|2 (high word first)
 
|-
 
|-
  
 
|}
 
|}
 +
 +
 +
=== Discrete Inputs Data Table ===
 +
The internal discrete inputs data table can be read by a Modbus client, and written locally to set the discrete inputs’ values.
  
 
{| class="wikitable"
 
{| class="wikitable"
Line 151: Line 192:
 
!Description
 
!Description
 
|-
 
|-
 +
 +
|rowspan="3"|unitid.address.di
 +
|bit:0<br/>bit:false<br/>bit:off<br/>
 +
|W
 +
|sets a single bit in the discrete inputs data table to off
 +
|-
 +
|bit:1<br/>bit:true<br/>bit:on<br/>
 +
|W
 +
|sets a single bit in the discrete inputs data table to on
 +
|-
 +
|bits:<nowiki><0|1|-...</nowiki> <br/> '-' characters in the sequence are skipped
 +
|W
 +
|sets multiple bits in the discrete inputs data table. <br/> The left-most bit in the sequence is the discrete input at <address>
 +
|-
 
|}
 
|}
  
== User Interface ==
+
=== Coils Data Table ===
 +
The internal coils data table can be read or written by a Modbus client, and written locally to set the coils’ values.
  
=== Airzone Object ===
+
{| class="wikitable"
 +
!ID
 +
!Value
 +
!R/W
 +
!Description
 +
|-
  
 +
|rowspan="6"|unitid.address.co
 +
|bit:0<br/>bit:false<br/>bit:off<br/>
 +
|W
 +
|sets a single coil in the coils data table to off
 +
|-
 +
|bit:1<br/>bit:true<br/>bit:on<br/>
 +
|W
 +
|sets a single coil in the coils data table to on
 +
|-
 +
|bits:<nowiki><0|1|-...></nowiki> <br/> '-' characters in the sequence are skipped
 +
|W
 +
|sets multiple coils in the coils data table. <br/> The left-most bit in the sequence is the coil at <address>
 +
|-
 +
|subscribe
 +
|W
 +
|subscribes one coil at <address> for event notification
 +
|-
 +
|subscribe:<n>
 +
|W
 +
|subscribes <n> consecutive coils starting at <address> for event notification
 +
|-
  
{{:Airzone_(UI Object)}}
+
|0 <br/> 1
 +
|R
 +
|the status of the coil at <address>. Events are generated, only for coils that have been subscribed, when the coil is written by a Modbus client, even if there is no change of value
 +
|-
 +
 
 +
|}
 +
 
 +
The <n> value after subscribecoils: can also be a variable, that will be replaced with its numeric content.
  
=== UISET Actions ===
+
=== Input Registers Data Table ===
You can use any object that accepts a text attribute, usually [[text]], but also [[marquee]] and others, to display the status of the system according to the following table:
+
The internal input registers data table can be read by a Modbus client, and written locally to set the registers’ values.
  
 
{| class="wikitable"
 
{| class="wikitable"
 
!ID
 
!ID
!Attribute
+
!Value
!colspan="2"|Set to
+
!R/W
 +
!Description
 
|-
 
|-
  
=== USER Commands ===
+
|rowspan="9"|unitid.address.ir
 +
|ushort:<value>
 +
|W
 +
|writes an unsigned short number to the  input registers table
 +
|-
 +
|uint:<value>
 +
|W
 +
|writes an unsigned integer number to the  input registers table
 +
|-
 +
|ulong:<value>
 +
|W
 +
|writes an unsigned long number to the  input registers table
 +
|-
 +
|short:<value>
 +
|W
 +
|writes a signed short number to the  input registers table
 +
|-
 +
|int:<value>
 +
|W
 +
|writes a signed integer number to the  input registers table
 +
|-
 +
|long:<value>
 +
|W
 +
|writes a signed long number to the  input registers table
 +
|-
 +
|float:<value>
 +
|W
 +
|writes a floating-point number using the IEEE Standard 754 representation to the  input registers table
 +
|-
 +
|hex:<value>
 +
|W
 +
|writes raw hex bytes to the input registers table
 +
|-
 +
|bits:<nowiki><0|1|-...></nowiki> <br/> '-' characters in the sequence are skipped
 +
|W
 +
|writes 16 bits to a single register in the input registers table. The leftmost bit is the MSB
 +
|-
  
The CSx75 I/O Server supports a few system commands to be inserted within the Web interface. The controls are directly supported as ordinary [[user]] objects, by setting the ''name'' and ''param'' fields according to the following table:
+
|}
 +
 
 +
=== Holding Registers Data Table ===
 +
The internal holding registers data table can be read or written by a Modbus client, and written locally to set the registers’ values.
  
 
{| class="wikitable"
 
{| class="wikitable"
!Name
+
!ID
!Param
+
!Value
!Action
+
!R/W
 +
!Description
 
|-
 
|-
 +
 +
|rowspan="19"|unitid.address.hr
 +
|ushort:<value>
 +
|W
 +
|writes an unsigned short number to the holding registers table
 +
|-
 +
|uint:<value>
 +
|W
 +
|writes an unsigned integer number to the  holding registers table
 +
|-
 +
|ulong:<value>
 +
|W
 +
|writes an unsigned long number to the  holding registers table
 +
|-
 +
|short:<value>
 +
|W
 +
|writes a signed short number to the  holding registers table
 +
|-
 +
|int:<value>
 +
|W
 +
|writes a signed integer number to the  holding registers table
 +
|-
 +
|long:<value>
 +
|W
 +
|writes a signed long number to the  holding registers table
 +
|-
 +
|float:<value>
 +
|W
 +
|writes a floating-point number using the IEEE Standard 754 representation to the  holding registers table
 +
|-
 +
|hex:<value>
 +
|W
 +
|writes raw hex bytes to the holding registers table
 +
|-
 +
|bits:<nowiki><0|1|-...></nowiki> <br/> '-' characters in the sequence are skipped
 +
|W
 +
|writes 16 bits to a single register in the holding registers table. The leftmost bit is the MSB
 +
|-
 +
|subscribe:ushort
 +
|W
 +
|subscribes an unsigned short at <address> for event notification
 +
|-
 +
|subscribe:uint
 +
|W
 +
|subscribes an unsigned int starting at <address> for event notification
 +
|-
 +
|subscribe:ulong
 +
|W
 +
|subscribes an unsigned long starting at <address> for event notification
 +
|-
 +
|subscribe:short
 +
|W
 +
|subscribes a signed short integer at <address> for event notification
 +
|-
 +
|subscribe:int
 +
|W
 +
|subscribes a signed int starting at <address> for event notification
 +
|-
 +
|subscribe:long
 +
|W
 +
|subscribes a signed long starting at <address> for event notification
 +
|-
 +
|subscribe:float
 +
|W
 +
|subscribes a floating-point number using the IEEE Standard 754 representation starting at <address> for event notification
 +
|-
 +
|subscribe:hex
 +
|W
 +
|subscribes a single register at <address> for event notification using the hex notation
 +
|-
 +
|subscribe:bits
 +
|W
 +
|subscribes the 16 individual bits of a single register at <address> for event notification
 +
|-
 +
|data type value
 +
|R
 +
| the decimal or hexadecimal value of the data type at <address>. Events are generated, based on subscribed data types and addresses, when the data is written by a Modbus client, even if there is no change of value
 +
|-
 +
 +
|style="white-space:nowrap"| unitid.address.hr.<n> <br/><br/> <n> from 1 to 16; <br/> <n>=1 is the LSB of first byte; <br/> <n>=8 is MSB of first byte; <br/> <n>=9 is LSB of second byte; <br/> <n>=16 is MSB of second byte
 +
|0 or 1 bit value
 +
|R
 +
|the individual bits of a holding register at <address>. Events are generated, for bits subscriptions only, when the data is written by a Modbus client, even if there is no change of value
 +
|-
 +
 
|}
 
|}
 +
 +
== Java Callback Methods API ==
 +
 +
=== ModbusEvent ===
 +
 +
static byte[] ModbusEvent(String name, String InetAddress addr, int unitid, byte[] pdu)
 +
 +
This event is triggered by any valid Modbus request from a client connected to the IP address and port of the HSYCO’s Modbus server, and can override the default server’s response.
 +
 +
This method should return a valid PDU, that will be sent back to the client. If the method returns null, the server will perform the Modbus function on its internal data tables.
 +
 +
'''Parameters:'''
 +
* name - the I/O Server name
 +
* addr - the client’s address
 +
* unitid - the Modbus unit id set in the client’s request
 +
* pdu - the full PDU byte array, starting with the function code.
 +
 +
== Examples ==
 +
 +
=== Subscribe request ===
 +
Subscribing in required whenever you need to generate ad IO event for a specified register. For example writing:
 +
 +
io modbusserver.connection = online : io plc.1.1.hr = subscribe:bits
 +
io modbusserver.connection = online : io plc.1.2.hr = subscribe:short
 +
 +
means that every time the registers 1 or 2 of the modbus unit with address 1 are changed the corresponding IO event is generated.
 +
 +
=== Write on the modbus table ===
 +
Here's and example of how to write a value in the modbus server registers table.
 +
 +
$value : io modbusserver.1.500.hr = "int:"$value
 +
 +
=== Read Coils request ===
 +
 +
<source lang="java">
 +
public static byte[] ModbusEvent(String name, InetAddress addr, int unitid, byte[] pdu) throws Exception {
 +
int registerAddress, quantity;
 +
 +
if (unitid == 1) {
 +
if (pdu.length == 5 && pdu[0] == 1) {
 +
registerAddress = ((pdu[1] & 0xff) << 8) + (pdu[2] & 0xff);
 +
quantity = ((pdu[3] & 0xff) << 8) + (pdu[4] & 0xff);
 +
if (quantity == 1) {
 +
if (registerAddress % 2 == 0) {
 +
byte[] returnPDU = {0x01, 0x01, 0x00};
 +
return returnPDU;
 +
} else {
 +
byte[] returnPDU = {0x01, 0x01, 0x01};
 +
return returnPDU;
 +
}
 +
}
 +
}
 +
}
 +
return null;
 +
}
 +
</source>
 +
 +
 +
 +
This quite useless code segment will respond to a Read Coils Modbus request to read a single coil value from unit 1. The response will be 0 when the requested coil address is an even number, and 1 for odd address numbers. The callback will respond with an exception PDU with exception code 04.
  
 
== Release Notes ==
 
== Release Notes ==
 +
=== 3.4.0 ===
 +
*added support for function 23, Read/Write Multiple Registers
 +
*new option forcedevents, set to false to generate events only on registers' data changes. Default is true
 +
*new option connectionslog, set to true to log all connections. Default is false
 +
*new option maxconnections, sets the maximum number of concurrent connection. Default is 128
 +
 +
=== 3.3.0 ===
 +
*initial release version
 +
 +
 +
----
 +
''Modbus is a registered trademark of Modbus Organization, Inc.''

Latest revision as of 18:01, 2 March 2016

The Modbus Protocol is a messaging structure. It is used to establish master-slave/client-server communication between intelligent devices. It is a de facto standard. The Modbus RTU specification defines the default communication protocol over serial lines. The Modbus TCP/IP specification takes the Modbus instruction set and wraps TCP/IP around it.

HSYCO’s MODBUS TCP SERVER I/O Server implements a Modbus TCP/IP server, allowing external Modbus clients to connect to HSYCO and perform any standard Modbus function.

This support is implemented as a simple Java API.

HSYCO Configuration

Add a MODBUSTCPSERVER I/O Server in the I/O Servers section of the Settings and set its parameters:

Communication

  • IP Address: local IP address where to accept connections, leave blank to accept connection on any LAN port and IP address
  • IP Port: TCP/IP port to use, leave blank to use default port 502. If you set the port, you should also set the IP address. The address 0.0.0.0 can be used to accept connections on all LAN ports.
if more than one MODBUSTCPSERVER I/O servers are defined they have to be on different ports or IP addresses.


High Availability

  • Shutdown when inactive: defaults to false.

Options

ID Default Values Description
connectionslog false true log all connections and disconnects
false don't log connections
eventslog false true if the general eventsLog option is also true in hsyco.ini, Modbus events for this gateway are written in the log files
false Modbus events for this gateway are not written in the log files, even if the eventsLog option is true in hsyco.ini
forcedevents true true generate forced events on any write command
false generate events only on registers' data changes
maxconnections 128 > 0 maximum number of concurrent connections

Modbus Server Integration

The Modbus TCP Server is implemented as a multi-threaded socket server that can accept multiple concurrent connections.

There are no hard-coded limits to the number of concurrent connections and no idle time-out mechanism to disconnect connections that don’t do any traffic for a certain amount of time. A socket-level keepalive mechanism is in place to ensure that a socket thread is closed and resources freed if the client drops the connection without a graceful close.

It is the clients’ responsibility to ensure the correct handling of connections to the server.

Whenever a client sends a Modbus request to the HSYCO server, the Modbus TCP Server will execute a Java callback method passing the complete PDU as a byte array, and respond with a PDU based on the returned byte array or, if the callback method is not defined or returns null, it will execute the Modbus read or write function using its internal data tables.

Datapoints

The naming convention HSYCO uses for the events is closely related to the Modbus addressing standards and data types.

The data point names always begin with the same format: <ioserver_id>.unitid.address, where unitid is the Modbus slave device address, from 1 to 247, and address is the initial register address you are reading.

The supported public function codes are:

  • 01: Read Coils
  • 02: Read Discrete Inputs
  • 03: Read Holding Registers
  • 04: Read Input Registers
  • 05: Write Single Coil
  • 06: Write Single Register
  • 15: Write Multiple Coils
  • 16: Write Multiple Registers
  • 22: Mask Write Register
  • 23: Read/Write Multiple Registers.

These functions read and write data on four distinct data tables:

Table Suffix Object Type Mode Functions
Discrete Inputs .di Single bit Read Only 02
Coils .co Single bit Read Write 01 05 15
Input Registers .ir 16-bit word Read Only 04
Holding Registers .hr 16-bit word Read Write 03 06 16 22 23

Operations on Input Registers and Holding Registers tables support the following data types:

Type code Description Return range Number of registers
ushort unsigned short integer 0 ... 65535 1
uint unsigned integer 0 ... (232 - 1) 2
ulong unsigned long integer 0 ... (264 - 1) 4
short signed short integer -32768 ... +32767 1
int signed integer -231 ... (231 - 1) 2
long signed long integer -263 ... (263 - 1) 4
float signed IEEE 754 floating point +/- 2-149 ... (2-2-23)x2127 2 (high word first)


Discrete Inputs Data Table

The internal discrete inputs data table can be read by a Modbus client, and written locally to set the discrete inputs’ values.

ID Value R/W Description
unitid.address.di bit:0
bit:false
bit:off
W sets a single bit in the discrete inputs data table to off
bit:1
bit:true
bit:on
W sets a single bit in the discrete inputs data table to on
bits:<0|1|-...
'-' characters in the sequence are skipped
W sets multiple bits in the discrete inputs data table.
The left-most bit in the sequence is the discrete input at <address>

Coils Data Table

The internal coils data table can be read or written by a Modbus client, and written locally to set the coils’ values.

ID Value R/W Description
unitid.address.co bit:0
bit:false
bit:off
W sets a single coil in the coils data table to off
bit:1
bit:true
bit:on
W sets a single coil in the coils data table to on
bits:<0|1|-...>
'-' characters in the sequence are skipped
W sets multiple coils in the coils data table.
The left-most bit in the sequence is the coil at <address>
subscribe W subscribes one coil at <address> for event notification
subscribe:<n> W subscribes <n> consecutive coils starting at <address> for event notification
0
1
R the status of the coil at <address>. Events are generated, only for coils that have been subscribed, when the coil is written by a Modbus client, even if there is no change of value

The <n> value after subscribecoils: can also be a variable, that will be replaced with its numeric content.

Input Registers Data Table

The internal input registers data table can be read by a Modbus client, and written locally to set the registers’ values.

ID Value R/W Description
unitid.address.ir ushort:<value> W writes an unsigned short number to the input registers table
uint:<value> W writes an unsigned integer number to the input registers table
ulong:<value> W writes an unsigned long number to the input registers table
short:<value> W writes a signed short number to the input registers table
int:<value> W writes a signed integer number to the input registers table
long:<value> W writes a signed long number to the input registers table
float:<value> W writes a floating-point number using the IEEE Standard 754 representation to the input registers table
hex:<value> W writes raw hex bytes to the input registers table
bits:<0|1|-...>
'-' characters in the sequence are skipped
W writes 16 bits to a single register in the input registers table. The leftmost bit is the MSB

Holding Registers Data Table

The internal holding registers data table can be read or written by a Modbus client, and written locally to set the registers’ values.

ID Value R/W Description
unitid.address.hr ushort:<value> W writes an unsigned short number to the holding registers table
uint:<value> W writes an unsigned integer number to the holding registers table
ulong:<value> W writes an unsigned long number to the holding registers table
short:<value> W writes a signed short number to the holding registers table
int:<value> W writes a signed integer number to the holding registers table
long:<value> W writes a signed long number to the holding registers table
float:<value> W writes a floating-point number using the IEEE Standard 754 representation to the holding registers table
hex:<value> W writes raw hex bytes to the holding registers table
bits:<0|1|-...>
'-' characters in the sequence are skipped
W writes 16 bits to a single register in the holding registers table. The leftmost bit is the MSB
subscribe:ushort W subscribes an unsigned short at <address> for event notification
subscribe:uint W subscribes an unsigned int starting at <address> for event notification
subscribe:ulong W subscribes an unsigned long starting at <address> for event notification
subscribe:short W subscribes a signed short integer at <address> for event notification
subscribe:int W subscribes a signed int starting at <address> for event notification
subscribe:long W subscribes a signed long starting at <address> for event notification
subscribe:float W subscribes a floating-point number using the IEEE Standard 754 representation starting at <address> for event notification
subscribe:hex W subscribes a single register at <address> for event notification using the hex notation
subscribe:bits W subscribes the 16 individual bits of a single register at <address> for event notification
data type value R the decimal or hexadecimal value of the data type at <address>. Events are generated, based on subscribed data types and addresses, when the data is written by a Modbus client, even if there is no change of value
unitid.address.hr.<n>

<n> from 1 to 16;
<n>=1 is the LSB of first byte;
<n>=8 is MSB of first byte;
<n>=9 is LSB of second byte;
<n>=16 is MSB of second byte
0 or 1 bit value R the individual bits of a holding register at <address>. Events are generated, for bits subscriptions only, when the data is written by a Modbus client, even if there is no change of value

Java Callback Methods API

ModbusEvent

static byte[] ModbusEvent(String name, String InetAddress addr, int unitid, byte[] pdu)

This event is triggered by any valid Modbus request from a client connected to the IP address and port of the HSYCO’s Modbus server, and can override the default server’s response.

This method should return a valid PDU, that will be sent back to the client. If the method returns null, the server will perform the Modbus function on its internal data tables.

Parameters:

  • name - the I/O Server name
  • addr - the client’s address
  • unitid - the Modbus unit id set in the client’s request
  • pdu - the full PDU byte array, starting with the function code.

Examples

Subscribe request

Subscribing in required whenever you need to generate ad IO event for a specified register. For example writing:

io modbusserver.connection = online : io plc.1.1.hr = subscribe:bits
io modbusserver.connection = online : io plc.1.2.hr = subscribe:short

means that every time the registers 1 or 2 of the modbus unit with address 1 are changed the corresponding IO event is generated.

Write on the modbus table

Here's and example of how to write a value in the modbus server registers table.

$value : io modbusserver.1.500.hr = "int:"$value

Read Coils request

public static byte[] ModbusEvent(String name, InetAddress addr, int unitid, byte[] pdu) throws Exception {
	int registerAddress, quantity;
		
	if (unitid == 1) {
		if (pdu.length == 5 && pdu[0] == 1) {
			registerAddress = ((pdu[1] & 0xff) << 8) + (pdu[2] & 0xff);
			quantity = ((pdu[3] & 0xff) << 8) + (pdu[4] & 0xff);
			if (quantity == 1) {
				if (registerAddress % 2 == 0) {
					byte[] returnPDU = {0x01, 0x01, 0x00};
					return returnPDU;
				} else {
					byte[] returnPDU = {0x01, 0x01, 0x01};
					return returnPDU;
				}
			}
		}
	}
	return null;
}


This quite useless code segment will respond to a Read Coils Modbus request to read a single coil value from unit 1. The response will be 0 when the requested coil address is an even number, and 1 for odd address numbers. The callback will respond with an exception PDU with exception code 04.

Release Notes

3.4.0

  • added support for function 23, Read/Write Multiple Registers
  • new option forcedevents, set to false to generate events only on registers' data changes. Default is true
  • new option connectionslog, set to true to log all connections. Default is false
  • new option maxconnections, sets the maximum number of concurrent connection. Default is 128

3.3.0

  • initial release version



Modbus is a registered trademark of Modbus Organization, Inc.