Difference between revisions of "Modbus"

From HSYCO
Jump to navigation Jump to search
 
(39 intermediate revisions by 4 users not shown)
Line 15: Line 15:
 
=== Modbus RTU ===
 
=== Modbus RTU ===
 
HSYCO supports Modbus RTU over RS485, using the FTDI’s USB-RS485 cable, a USB to RS485 levels serial UART converter cable incorporating FTDI’s FT232RQ USB to serial UART interface IC device which handles all the USB signaling and protocols. The cable provides a fast, simple way to connect devices with a RS485 interface to USB.
 
HSYCO supports Modbus RTU over RS485, using the FTDI’s USB-RS485 cable, a USB to RS485 levels serial UART converter cable incorporating FTDI’s FT232RQ USB to serial UART interface IC device which handles all the USB signaling and protocols. The cable provides a fast, simple way to connect devices with a RS485 interface to USB.
 +
 +
[[File:IO Server Modbus Cable1.png|center|250px]]
  
 
The USB-RS485 cable connects to any USB port of the HSYCO server, and doesn’t require external power supply. It offers an internal 120 Ohm resistor for line termination, and +5Vcc and ground level references that could become handy to connect pull-up and pull-down resistors for lines’ polarization (See the MODBUS over Serial Line Specification & Implementation guide for additional information about line polarization and termination).
 
The USB-RS485 cable connects to any USB port of the HSYCO server, and doesn’t require external power supply. It offers an internal 120 Ohm resistor for line termination, and +5Vcc and ground level references that could become handy to connect pull-up and pull-down resistors for lines’ polarization (See the MODBUS over Serial Line Specification & Implementation guide for additional information about line polarization and termination).
  
{{tip| The USB-RS485 cable doesn’t provide opto-isolated RS485 lines. Transient currents and voltage levels exceeding the cable’s specification could damage the USB-RS485 cable, HSYCO server and any Modbus device attached to the RS485 line.}}
+
[[File:IO Server Modbus Cable2.png|center|500px]]
 +
 
 +
{{note| The USB-RS485 cable doesn’t provide opto-isolated RS485 lines. Transient currents and voltage levels exceeding the cable’s specification could damage the USB-RS485 cable, HSYCO server and any Modbus device attached to the RS485 line.}}
  
 
== HSYCO Configuration ==
 
== HSYCO Configuration ==
 +
Add a MODBUSTCP or MODBUSRTU I/O Server in the [[Settings#I/O Servers|I/O Servers section of the Settings]] and set its parameters:
 +
 +
=== Communication ===
 +
==== Modbus TCP ====
 +
*'''IP Address''': IP address of the gateway
 +
*'''IP Port''': TCP/IP port to use, leave blank to use default port 502.
 +
 +
==== Modbus RTU ====
 +
*'''Comm ID''': select the comm port the device is connected to.
 +
 +
When creating the used comm port, its id depends on your HSYCO server configuration, and should be "ttyUSB0" if there are no other devices connected to the USB ports.
 +
You could also use the "ftdi-n-n.n" alias, that is unique for each physical USB port on the server. In this case, you should also list the full device name in the CommPortsList parameter.
 +
 +
The communication parameters should be set to the 9600,8,1,2,0 defaults, unless you want to use a different speed.
 +
 +
=== High Availability ===
 +
*'''Shutdown when inactive''': defaults to true.
 +
 
=== Options ===
 
=== Options ===
  
Line 71: Line 93:
 
*15: Write Multiple Coils
 
*15: Write Multiple Coils
 
*16: Write Multiple Registers
 
*16: Write Multiple Registers
 +
*22: Mask Write Register (only available using the modbusWriteMaskRegister() JavaScript and Java function)
 +
*23: Read/Write Multiple Registers (only available using the modbusReadWriteMultipleRegisters() JavaScript and Java function).
  
Read operations with functions 03 and 04 support the following data types:
+
Operations on Input Registers and Holding Registers tables support the following data types:
  
 
{| class="wikitable"
 
{| class="wikitable"
Line 79: Line 103:
 
!Return range
 
!Return range
 
!Number of registers
 
!Number of registers
 +
!Data format
 
|-
 
|-
  
Line 85: Line 110:
 
|0 ... 65535
 
|0 ... 65535
 
|1
 
|1
 +
|big-endian<sup>[[#note1|[Note 1]]]</sup>
 
|-
 
|-
  
Line 91: Line 117:
 
|0 ... (2<sup>32</sup> - 1)
 
|0 ... (2<sup>32</sup> - 1)
 
|2
 
|2
 +
|big-endian<sup>[[#note1|[Note 1]]]</sup>
 +
|-
 +
 +
|uintx
 +
|unsigned integer
 +
|0 ... (2<sup>32</sup> - 1)
 +
|2
 +
|big-endian, low word first<sup>[[#note2|[Note 2]]]</sup>
 
|-
 
|-
  
Line 97: Line 131:
 
|0 ... (2<sup>64</sup> - 1)
 
|0 ... (2<sup>64</sup> - 1)
 
|4
 
|4
 +
|big-endian<sup>[[#note1|[Note 1]]]</sup>
 +
|-
 +
 +
|ulongx
 +
|unsigned long integer
 +
|0 ... (2<sup>64</sup> - 1)
 +
|4
 +
|big-endian, low word first<sup>[[#note2|[Note 2]]]</sup>
 
|-
 
|-
  
Line 103: Line 145:
 
| -32768 ... +32767
 
| -32768 ... +32767
 
|1
 
|1
 +
|big-endian<sup>[[#note1|[Note 1]]]</sup>
 
|-
 
|-
  
Line 109: Line 152:
 
| -2<sup>31</sup> ... (2<sup>31</sup> - 1)
 
| -2<sup>31</sup> ... (2<sup>31</sup> - 1)
 
|2
 
|2
 +
|big-endian<sup>[[#note1|[Note 1]]]</sup>
 +
|-
 +
 +
|intx
 +
|signed integer
 +
| -2<sup>31</sup> ... (2<sup>31</sup> - 1)
 +
|2
 +
|big-endian, low word first<sup>[[#note2|[Note 2]]]</sup>
 
|-
 
|-
  
Line 115: Line 166:
 
| -2<sup>63</sup> ... (2<sup>63</sup> - 1)
 
| -2<sup>63</sup> ... (2<sup>63</sup> - 1)
 
|4
 
|4
 +
|big-endian<sup>[[#note1|[Note 1]]]</sup>
 +
|-
 +
 +
|long x
 +
|signed long integer
 +
| -2<sup>63</sup> ... (2<sup>63</sup> - 1)
 +
|4
 +
|big-endian, low word first<sup>[[#note2|[Note 2]]]</sup>
 
|-
 
|-
  
|float
+
|float
 
|signed IEEE 754 floating point
 
|signed IEEE 754 floating point
 
| +/- 2<sup>-149</sup> ... (2-2<sup>-23</sup>)x2<sup>127</sup>
 
| +/- 2<sup>-149</sup> ... (2-2<sup>-23</sup>)x2<sup>127</sup>
 
|2
 
|2
 +
|IEEE 754 floating-point "single format" bit layout
 +
|-
 +
 +
|floatx
 +
|signed IEEE 754 floating point
 +
| +/- 2<sup>-149</sup> ... (2-2<sup>-23</sup>)x2<sup>127</sup>
 +
|2
 +
|IEEE 754 floating-point "single format" bit layout, low word first<sup>[[#note2|[Note 2]]]</sup>
 
|-
 
|-
  
 
|}
 
|}
 +
 +
<span id="note1">
 +
;Note 1 : The big-endian representation means that when a numerical quantity larger than a single byte is transmitted, the most significant byte is sent first.
 +
</span>
 +
 +
<span id="note2">
 +
;Note 2 : This format transmits the least significant word first. The most significant byte of each word is sent first.
 +
</span>
 +
  
 
=== Read Coils ===
 
=== Read Coils ===
Line 135: Line 211:
 
|-
 
|-
  
|rowspan="3"|unitid.address.di
+
|unitid.address
 +
|readcoils:<n>
 +
|W
 +
|executes the Read Coils function, reading <n> coils
 +
|-
 +
 
 +
|rowspan="2"|unitid.address.1<br/> ... <br/>unitid.address.<n><br/> or<br/> unitid.address.01.<n><br/> if extendednames is set
 +
|0
 +
|R
 +
|the status of the <n>-th coil starting at address is off
 +
|-
 +
|1
 +
|R
 +
|the status of the <n>-th coil starting at address is on
 +
|-
 +
 
 +
|unitid.address.error<br/> or<br/> unitid.address.01.error<br/> if extendednames is set
 +
|0, null, >0
 +
|R
 +
| the error status. 0: no error; null: unknown error; >0: modbus exception code
 +
|-
 +
|}
 +
 
 +
The <n> value after readcoils: can also be a variable, that will be replaced with its numeric content, for example:
 +
IO modbus.27.400 = readcoils:$number
 +
 
 +
=== Read Discrete Inputs ===
 +
Reads from 1 to 2000 contiguous discrete inputs, using function code 02. Each input returns a binary value, 0 or 1, in distinct data points, one for each coil.
 +
 
 +
{| class="wikitable"
 +
!ID
 +
!Value
 +
!R/W
 +
!Description
 +
|-
 +
 
 +
|unitid.address
 +
|readdiscreteinputs:<n>
 +
|W
 +
|executes the Read Discrete Inputs function, reading <n> inputs
 +
|-
 +
 
 +
|rowspan="2"|unitid.address.1 <br/> ... <br/> unitid.address.<n> <br/> or <br/> unitid.address.02.<n> <br/> if extendednames is set
 +
|0
 +
|R
 +
|the status of the <n>-th input starting at address is off
 +
|-
 +
|1
 +
|R
 +
|the status of the <n>-th input starting at address is on
 +
|-
 +
 
 +
|unitid.address.error <br/> or <br/> unitid.address.02.error <br/> if extendednames is set
 +
|0, null, >0
 +
|R
 +
| the error status. 0: no error; null: unknown error; >0: modbus exception code
 +
|-
 +
 
 +
|}
 +
 
 +
The <n> value after readdiscreteinputs: can also be a variable, that will be replaced with its numeric content, for example:
 +
IO modbus.27.400 = readdiscreteinputs:$number
 +
 
 +
=== Read Holding Registers ===
 +
Reads one data point, using function code 03 to read the appropriate number of registers, based on the data type.
 +
 
 +
Each reading returns a single value, but you can also use an extended read function to read multiple consecutive values with one single Modbus read function, by adding :<n> at the end of “readholdingregisters:<format>”, where <n> is the number of values.
 +
 
 +
{| class="wikitable"
 +
!ID
 +
!Value
 +
!R/W
 +
!Description
 +
|-
 +
 
 +
|unitid.address
 +
|readholdingregisters:ushort
 +
|W
 +
|executes the Read Holding registers function, reading 1 register and interpreting the data as an unsigned short integer
 +
|-
 +
 
 +
|unitid.address
 +
|readholdingregisters:uint
 +
|W
 +
|executes the Read Holding registers function, reading 2 consecutive registers and interpreting the data as an unsigned integer
 +
|-
 +
 
 +
|unitid.address
 +
|readholdingregisters:ulong
 +
|W
 +
|executes the Read Holding registers function, reading 4 consecutive registers and interpreting the data as an unsigned long integer
 +
|-
 +
 
 +
|unitid.address
 +
|readholdingregisters:short
 +
|W
 +
|executes the Read Holding registers function, reading 1 register and interpreting the data as a signed short integer
 +
|-
 +
 
 +
|unitid.address
 +
|readholdingregisters:int
 +
|W
 +
|executes the Read Holding registers function, reading 2 consecutive registers and interpreting the data as a signed integer
 +
|-
 +
 
 +
|unitid.address
 +
|readholdingregisters:long
 +
|W
 +
|executes the Read Holding registers function, reading 4 consecutive registers and interpreting the data as a signed long integer
 +
|-
 +
 
 +
|unitid.address
 +
|readholdingregisters:bitshort
 +
|W
 +
|executes the Read Holding registers function, reading 1 register and interpreting the data as 16 individual bits
 +
|-
 +
 
 +
|unitid.address
 +
|readholdingregisters:bitint
 +
|W
 +
|executes the Read Holding registers function, reading 2 consecutive registers and interpreting the data as 32 individual bits
 +
|-
 +
 
 +
|unitid.address
 +
|readholdingregisters:bitlong
 +
|W
 +
|executes the Read Holding registers function, reading 4 consecutive registers and interpreting the data as 64 individual bits
 +
|-
 +
 
 +
|unitid.address
 +
|readholdingregisters:float
 +
|W
 +
|executes the Read Holding registers function, reading 2 consecutive registers and interpreting the data as a floating-point number using the IEEE Standard 754 representation
 +
|-
 +
 
 +
|style="white-space:nowrap"| unitid.address <br/> or <br/> unitid.address.03 <br/> if extendednames is set
 +
|the returned value, according to the requested data type
 +
|R
 +
|this is the value read using the Read Holding Registers function
 +
|-
 +
 
 +
|style="white-space:nowrap"| unitid.address.<n> <br/> or unitid.address.03.<n> <br/> if extendednames is set
 +
|the returned bit value, with <n> from 1 to 16, 32 or 64
 +
|R
 +
|this is the value read using the Read Holding Registers function with bitshort, bitint or bitlong format options
 +
|-
 +
 
 +
|style="white-space:nowrap"| unitid.address.error <br/> or <br/> unitid.address.03.error <br/> if extendednames is set
 +
|0, null, >0
 +
|R
 +
|the error status. 0: no error; null: unknown error; >0: modbus exception code
 +
|-
 +
 
 +
|}
 +
 
 +
=== Read Input Registers ===
 +
Reads one data point, using function code 04 to read the appropriate number of registers, based on the data type.
 +
 
 +
Each reading returns a single value, but you can also use an extended read function to read multiple consecutive values with one single Modbus read function, by adding :N at the end of “readinputregisters:<format>”, where <n> is the number of values.
 +
 
 +
{| class="wikitable"
 +
!ID
 +
!Value
 +
!R/W
 +
!Description
 +
|-
 +
 
 +
|unitid.address
 +
|readinputregisters:ushort
 +
|W
 +
|executes the Read Input registers function, reading 1 register and interpreting the data as an unsigned short integer
 +
|-
 +
 
 +
|unitid.address
 +
|readinputregisters:uint
 +
|W
 +
|executes the Read Input registers function, reading 2 consecutive registers and interpreting the data as an unsigned integer
 +
|-
 +
 
 +
|unitid.address
 +
|readinputregisters:ulong
 +
|W
 +
|executes the Read Input registers function, reading 4 consecutive registers and interpreting the data as an unsigned long integer
 +
|-
 +
 
 +
|unitid.address
 +
|readinputregisters:short
 +
|W
 +
|executes the Read Input registers function, reading 1 register and interpreting the data as a signed short integer
 +
|-
 +
 
 +
|unitid.address
 +
|readinputregisters:int
 +
|W
 +
|executes the Read Input registers function, reading 2 consecutive registers and interpreting the data as a signed integer
 +
|-
 +
 
 +
|unitid.address
 +
|readinputregisters:long
 +
|W
 +
|executes the Read Input registers function, reading 4 consecutive registers and interpreting the data as a signed long integer
 +
|-
 +
 
 +
|unitid.address
 +
|readinputregisters:bitshort
 +
|W
 +
|executes the Read Input registers function, reading 1 register and interpreting the data as 16 individual bits
 +
|-
 +
 
 +
|unitid.address
 +
|readinputregisters:bitint
 +
|W
 +
|executes the Read Input registers function, reading 2 consecutive registers and interpreting the data as 32 individual bits
 +
|-
 +
 
 +
|unitid.address
 +
|readinputregisters:bitlong
 +
|W
 +
|executes the Read Input registers function, reading 4 consecutive registers and interpreting the data as 64 individual bits
 +
|-
 +
 
 +
|unitid.address
 +
|readinputregisters:float
 +
|W
 +
|executes the Read Input registers function, reading 2 consecutive registers and interpreting the data as a floating-point number using the IEEE Standard 754 representation
 +
|-
 +
 
 +
|style="white-space:nowrap"| unitid.address <br/> or <br/> unitid.address.04 <br/> if extendednames is set
 +
|the returned value, according to the requested data type
 +
|R
 +
|this is the value read using the Read Input Registers function
 +
|-
 +
 
 +
|style="white-space:nowrap"| unitid.address.<n> <br/> or unitid.address.04.<n> <br/> if extendednames is set
 +
|the returned bit value, with <n> from 1 to 16, 32 or 64
 +
|R
 +
|this is the value read using the Read Input Registers function with bitshort, bitint or bitlong format options
 +
|-
 +
 
 +
|style="white-space:nowrap"| unitid.address.error <br/> or <br/> unitid.address.04.error <br/> if extendednames is set
 +
|0, null, >0
 +
|R
 +
|the error status. 0: no error; null: unknown error; >0: modbus exception code
 +
|-
 +
 
 +
|}
 +
 
 +
=== Write Coils ===
 +
Writes a single coil, using function code 05, or multiple coils, using function 15.
 +
 
 +
{| class="wikitable"
 +
!ID
 +
!Value
 +
!R/W
 +
!Description
 +
|-
 +
 
 +
|rowspan="3"|unitid.address
 
|bit:0<br/>bit:false<br/>bit:off<br/>
 
|bit:0<br/>bit:false<br/>bit:off<br/>
 
|W
 
|W
|sets a single bit in the discrete inputs data table to off
+
|executes the Write Single Coil function, setting the coil to off
 
|-  
 
|-  
 
|bit:1<br/>bit:true<br/>bit:on<br/>
 
|bit:1<br/>bit:true<br/>bit:on<br/>
 
|W
 
|W
|sets a single bit in the discrete inputs data table to on
+
|executes the Write Single Coil function, setting the coil to on
 
|-  
 
|-  
|bits:<nowiki><0|1|-...></nowiki> <br/> '-' characters in the sequence are skipped
+
|bits:<nowiki><0|1|-...</nowiki> <br/>
 
|W
 
|W
|sets multiple bits in the discrete inputs data table. <br/> The left-most bit in the sequence is the discrete input at <address>
+
|executes the Write Multiple Coils function, setting consecutive coils based on the binary sequence. The left-most bit in the sequence is the first coil
 
|-  
 
|-  
 +
 +
|rowspan="2" style="white-space:nowrap"| unitid.address.<n> <br/> or unitid.address.05.<n> <br/> if extendednames is set
 +
|0
 +
|R
 +
|the write function returned a correct response, and the new status of the coil is off
 +
|-
 +
|1
 +
|R
 +
|the write function returned a correct response, and the new status of the coil is on
 +
|-
 +
 +
|style="white-space:nowrap"| unitid.address.error <br/> or <br/> unitid.address.05.error <br/> if extendednames is set
 +
|0, null, >0
 +
|R
 +
|the error status. 0: no error; null: unknown error; >0: modbus exception code
 +
|-
 
|}
 
|}
  
=== Coils Data Table ===
+
The value after bit: and bits: can also be a variable, that will be replaced with its numeric content, for example:
The internal coils data table can be read or written by a Modbus client, and written locally to set the coils’ values.
+
IO modbus.27.1220 = bit:$value
 +
 
 +
=== Write Registers ===
 +
Writes registers, using function code 06 or 16, based on the data type.
  
 
{| class="wikitable"
 
{| class="wikitable"
Line 160: Line 512:
 
|-
 
|-
  
 +
|unitid.address
 +
|ushort:<value>
 +
|W
 +
|executes the Write Single Register function, writing an unsigned short number
 +
|-
 +
 +
|unitid.address
 +
|uint:<value>
 +
|W
 +
|executes the Write Multiple Registers function, writing an unsigned integer number to two adjacent registers
 +
|-
 +
 +
|unitid.address
 +
|ulong:<value>
 +
|W
 +
|executes the Write Multiple Registers function, writing an unsigned long number to four adjacent registers
 +
|-
 +
 +
|unitid.address
 +
|short:<value>
 +
|W
 +
|executes the Write Single Register function, writing a signed short number
 +
|-
 +
 +
|unitid.address
 +
|int:<value>
 +
|W
 +
|executes the Write Multiple Registers function, writing a signed integer number to two adjacent registers
 +
|-
 +
 +
|unitid.address
 +
|long:<value>
 +
|W
 +
|executes the Write Multiple Registers function, writing a signed long number to four adjacent registers
 +
|-
 +
 +
|unitid.address
 +
|float:<value>
 +
|W
 +
|executes the Write Multiple Registers function, writing a floating-point number using the IEEE Standard 754 representation to two adjacent registers
 +
|-
 +
 +
|style="white-space:nowrap"| unitid.address <br/> or <br/> unitid.address.06 <br/> unitid.address.16 <br/> if extendednames is set
 +
|the returned value, according to the requested data type
 +
|R
 +
|the write function returned a correct response
 +
|-
 +
 +
|style="white-space:nowrap"| unitid.address.error <br/> or <br/> unitid.address.06.error <br/> unitid.address.16.error <br/> if extendednames is set
 +
|0, null, >0
 +
|R
 +
|the error status. 0: no error; null: unknown error; >0: modbus exception code
 +
|-
 +
 +
|}
 +
 +
The value after short:, int: etc. can also be a variable, that will be replaced with its numeric content, for example:
 +
IO modbus.27.1220 = int:$value
  
 
== Examples ==
 
== Examples ==
Line 171: Line 581:
  
 
The second line sets a text object of the Web interface with the value read from the Modbus device.
 
The second line sets a text object of the Web interface with the value read from the Modbus device.
 +
 +
=== Reading several values at once ===
 +
TIME: IO modbus.27.220 Modbus.27.222 Modbus.27.224 = readinputregisters:float
 +
IO modbus.27.220 : UISET mydata1.text = IO modbus.27.220
 +
IO modbus.27.224 : UISET mydata1.text = IO modbus.27.224
 +
 +
The first line is a compact form for reading multiple data points.
 +
 +
=== Reading binary data ===
 +
 +
TIME: IO modbus.27.1000 = readdiscreteinputs:24
 +
IO modbus.27.1000.1 = 0 : UISET mylabel1.color = g
 +
IO modbus.27.1000.1 = 1 : UISET mylabel1.color = r
 +
...
 +
IO modbus.27.220.24 = 0 : UISET mylabel24.color = g
 +
IO modbus.27.220.24 = 1 : UISET mylabel24.color = r
 +
 +
The first line executes the read discrete inputs function on a Modbus device with address 27, reading two contiguous registers starting at address 1000 and interpreting the first 24 bits as binary values.
 +
 +
The following lines change the color of some objects of the Web interface to green if the bit status is 0 or red if the status is 1.
 +
 +
=== Reading in extended mode ===
 +
If the option extendednames was specified in System Settings:
 +
 +
TIME: IO modbus.27.1000 = readholdingregisters:ushort
 +
IO modbus.27.1000.03 : UISET mydata.text = IO modbus.27.1000.03
 +
 +
The first line executes the read holding register function for an ushort value on a Modbus device with address 27, reading the registers 1000.
 +
 +
The following line changes the attribute text of a text object setting it to the value just read.
 +
 +
=== Writing a single coil ===
 +
TIME : IO modbus.27.1220 = bit:1
 +
Set coil 1220 to on.
 +
 +
=== Writing multiple coils ===
 +
TIME : IO modbus.27.1220 = bits:100
 +
 +
Set three coils starting from 1220 to 1, 0, 0.
 +
 +
=== Writing registers ===
 +
TIME : IO modbus.27.210 = ushort:2048
 +
TIME : IO modbus.27.220 = float:-123.22
 +
TIME : IO modbus.27.230 modbus.27.240 = long:200000000
 +
 +
The first line executes the write single register on a Modbus device with address 27, writing the register at address 210 with an unsigned short number.
 +
 +
The second line executes the write multiple registers on a Modbus device with address 27, writing two consecutive registers starting at address 220 with an IEEE-754 floating-point number.
 +
 +
The third line executes the write multiple registers on a Modbus device with address 27, writing four consecutive registers starting at address 230 and four at 240 with a signed long number.
  
 
== Release Notes ==
 
== Release Notes ==
 +
 +
=== 3.5.0 ===
 +
*new data types "intx", "longx", "uintx", "ulongx", "floatx" to implement low word first data encoding
 +
 +
=== 3.4.0 ===
 +
*added support for function 23, Read/Write Multiple Registers:
 +
**modbusReadWriteMultipleRegisters() Java method and JavaScript function
 +
 
=== 3.3.0 ===
 
=== 3.3.0 ===
 
*added support for Write Multiple Coils function:
 
*added support for Write Multiple Coils function:
writeMultipleCoils(String name, int unit, int address, int coils, byte[] bytes) Java method
+
**writeMultipleCoils() Java method and JavaScript function
 +
**ID.unitid.address = bits:<0|1>... in EVENTS
  
ID.unitid.address = bits:<0|1>... in EVENTS
 
 
=== 3.2.1 ===
 
=== 3.2.1 ===
 
*EVENTS: minor fixes to the readcoils and readdiscreteinputs actions
 
*EVENTS: minor fixes to the readcoils and readdiscreteinputs actions
Line 192: Line 660:
 
=== 3.0.0 ===
 
=== 3.0.0 ===
 
*initial release
 
*initial release
 +
  
 
----
 
----
 
''Modbus is a registered trademark of Modbus Organization, Inc.''
 
''Modbus is a registered trademark of Modbus Organization, Inc.''

Latest revision as of 14:15, 24 April 2015

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 supports both Modbus TCP and Modbus RTU, acting as a Modbus master in both cases.

This support is implemented as a set of Java APIs that allow read/write functions as described in the HSYCO Programming Handbook, and an I/O Server with a data point representation that you can use in the EVENTS language to poll Modbus data and generate events.

General Configuration

Modbus TCP

If a Modbus TCP/IP device is used to interface HSYCO to a Modbus RTU or ASCII bus over RS485, the gateway should be configured as Modbus TCP/IP slave and Modbus RTU/ASCII master.

HSYCO can also directly access Modbus TCP slave devices.

Modbus RTU

HSYCO supports Modbus RTU over RS485, using the FTDI’s USB-RS485 cable, a USB to RS485 levels serial UART converter cable incorporating FTDI’s FT232RQ USB to serial UART interface IC device which handles all the USB signaling and protocols. The cable provides a fast, simple way to connect devices with a RS485 interface to USB.

IO Server Modbus Cable1.png

The USB-RS485 cable connects to any USB port of the HSYCO server, and doesn’t require external power supply. It offers an internal 120 Ohm resistor for line termination, and +5Vcc and ground level references that could become handy to connect pull-up and pull-down resistors for lines’ polarization (See the MODBUS over Serial Line Specification & Implementation guide for additional information about line polarization and termination).

IO Server Modbus Cable2.png
The USB-RS485 cable doesn’t provide opto-isolated RS485 lines. Transient currents and voltage levels exceeding the cable’s specification could damage the USB-RS485 cable, HSYCO server and any Modbus device attached to the RS485 line.


HSYCO Configuration

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

Communication

Modbus TCP

  • IP Address: IP address of the gateway
  • IP Port: TCP/IP port to use, leave blank to use default port 502.

Modbus RTU

  • Comm ID: select the comm port the device is connected to.

When creating the used comm port, its id depends on your HSYCO server configuration, and should be "ttyUSB0" if there are no other devices connected to the USB ports. You could also use the "ftdi-n-n.n" alias, that is unique for each physical USB port on the server. In this case, you should also list the full device name in the CommPortsList parameter.

The communication parameters should be set to the 9600,8,1,2,0 defaults, unless you want to use a different speed.

High Availability

  • Shutdown when inactive: defaults to true.

Options

ID Default Values Description
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
extendednames false true when this option is set, data point names used to return values will have the postfix .FF, where FF is the 2-digit decimal function code
false data points’ default naming convention is used

Modbus Integration

Because the Modbus protocol is not asynchronous, but is based on polling, you always have to request a read to tell the driver to actually read the data and eventually generate an event on the associated data point if the data has changed.

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: ID.unitid.address, where ID is the I/O server id, 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 (only available using the modbusWriteMaskRegister() JavaScript and Java function)
  • 23: Read/Write Multiple Registers (only available using the modbusReadWriteMultipleRegisters() JavaScript and Java function).

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

Type code Description Return range Number of registers Data format
ushort unsigned short integer 0 ... 65535 1 big-endian[Note 1]
uint unsigned integer 0 ... (232 - 1) 2 big-endian[Note 1]
uintx unsigned integer 0 ... (232 - 1) 2 big-endian, low word first[Note 2]
ulong unsigned long integer 0 ... (264 - 1) 4 big-endian[Note 1]
ulongx unsigned long integer 0 ... (264 - 1) 4 big-endian, low word first[Note 2]
short signed short integer -32768 ... +32767 1 big-endian[Note 1]
int signed integer -231 ... (231 - 1) 2 big-endian[Note 1]
intx signed integer -231 ... (231 - 1) 2 big-endian, low word first[Note 2]
long signed long integer -263 ... (263 - 1) 4 big-endian[Note 1]
long x signed long integer -263 ... (263 - 1) 4 big-endian, low word first[Note 2]
float signed IEEE 754 floating point +/- 2-149 ... (2-2-23)x2127 2 IEEE 754 floating-point "single format" bit layout
floatx signed IEEE 754 floating point +/- 2-149 ... (2-2-23)x2127 2 IEEE 754 floating-point "single format" bit layout, low word first[Note 2]

Note 1 
The big-endian representation means that when a numerical quantity larger than a single byte is transmitted, the most significant byte is sent first.

Note 2 
This format transmits the least significant word first. The most significant byte of each word is sent first.


Read Coils

Reads from 1 to 2000 contiguous coils, using function code 01. Each coil returns a binary value, 0 or 1, in distinct data points, one for each coil.

ID Value R/W Description
unitid.address readcoils:<n> W executes the Read Coils function, reading <n> coils
unitid.address.1
...
unitid.address.<n>
or
unitid.address.01.<n>
if extendednames is set
0 R the status of the <n>-th coil starting at address is off
1 R the status of the <n>-th coil starting at address is on
unitid.address.error
or
unitid.address.01.error
if extendednames is set
0, null, >0 R the error status. 0: no error; null: unknown error; >0: modbus exception code

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

IO modbus.27.400 = readcoils:$number

Read Discrete Inputs

Reads from 1 to 2000 contiguous discrete inputs, using function code 02. Each input returns a binary value, 0 or 1, in distinct data points, one for each coil.

ID Value R/W Description
unitid.address readdiscreteinputs:<n> W executes the Read Discrete Inputs function, reading <n> inputs
unitid.address.1
...
unitid.address.<n>
or
unitid.address.02.<n>
if extendednames is set
0 R the status of the <n>-th input starting at address is off
1 R the status of the <n>-th input starting at address is on
unitid.address.error
or
unitid.address.02.error
if extendednames is set
0, null, >0 R the error status. 0: no error; null: unknown error; >0: modbus exception code

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

IO modbus.27.400 = readdiscreteinputs:$number

Read Holding Registers

Reads one data point, using function code 03 to read the appropriate number of registers, based on the data type.

Each reading returns a single value, but you can also use an extended read function to read multiple consecutive values with one single Modbus read function, by adding :<n> at the end of “readholdingregisters:<format>”, where <n> is the number of values.

ID Value R/W Description
unitid.address readholdingregisters:ushort W executes the Read Holding registers function, reading 1 register and interpreting the data as an unsigned short integer
unitid.address readholdingregisters:uint W executes the Read Holding registers function, reading 2 consecutive registers and interpreting the data as an unsigned integer
unitid.address readholdingregisters:ulong W executes the Read Holding registers function, reading 4 consecutive registers and interpreting the data as an unsigned long integer
unitid.address readholdingregisters:short W executes the Read Holding registers function, reading 1 register and interpreting the data as a signed short integer
unitid.address readholdingregisters:int W executes the Read Holding registers function, reading 2 consecutive registers and interpreting the data as a signed integer
unitid.address readholdingregisters:long W executes the Read Holding registers function, reading 4 consecutive registers and interpreting the data as a signed long integer
unitid.address readholdingregisters:bitshort W executes the Read Holding registers function, reading 1 register and interpreting the data as 16 individual bits
unitid.address readholdingregisters:bitint W executes the Read Holding registers function, reading 2 consecutive registers and interpreting the data as 32 individual bits
unitid.address readholdingregisters:bitlong W executes the Read Holding registers function, reading 4 consecutive registers and interpreting the data as 64 individual bits
unitid.address readholdingregisters:float W executes the Read Holding registers function, reading 2 consecutive registers and interpreting the data as a floating-point number using the IEEE Standard 754 representation
unitid.address
or
unitid.address.03
if extendednames is set
the returned value, according to the requested data type R this is the value read using the Read Holding Registers function
unitid.address.<n>
or unitid.address.03.<n>
if extendednames is set
the returned bit value, with <n> from 1 to 16, 32 or 64 R this is the value read using the Read Holding Registers function with bitshort, bitint or bitlong format options
unitid.address.error
or
unitid.address.03.error
if extendednames is set
0, null, >0 R the error status. 0: no error; null: unknown error; >0: modbus exception code

Read Input Registers

Reads one data point, using function code 04 to read the appropriate number of registers, based on the data type.

Each reading returns a single value, but you can also use an extended read function to read multiple consecutive values with one single Modbus read function, by adding :N at the end of “readinputregisters:<format>”, where <n> is the number of values.

ID Value R/W Description
unitid.address readinputregisters:ushort W executes the Read Input registers function, reading 1 register and interpreting the data as an unsigned short integer
unitid.address readinputregisters:uint W executes the Read Input registers function, reading 2 consecutive registers and interpreting the data as an unsigned integer
unitid.address readinputregisters:ulong W executes the Read Input registers function, reading 4 consecutive registers and interpreting the data as an unsigned long integer
unitid.address readinputregisters:short W executes the Read Input registers function, reading 1 register and interpreting the data as a signed short integer
unitid.address readinputregisters:int W executes the Read Input registers function, reading 2 consecutive registers and interpreting the data as a signed integer
unitid.address readinputregisters:long W executes the Read Input registers function, reading 4 consecutive registers and interpreting the data as a signed long integer
unitid.address readinputregisters:bitshort W executes the Read Input registers function, reading 1 register and interpreting the data as 16 individual bits
unitid.address readinputregisters:bitint W executes the Read Input registers function, reading 2 consecutive registers and interpreting the data as 32 individual bits
unitid.address readinputregisters:bitlong W executes the Read Input registers function, reading 4 consecutive registers and interpreting the data as 64 individual bits
unitid.address readinputregisters:float W executes the Read Input registers function, reading 2 consecutive registers and interpreting the data as a floating-point number using the IEEE Standard 754 representation
unitid.address
or
unitid.address.04
if extendednames is set
the returned value, according to the requested data type R this is the value read using the Read Input Registers function
unitid.address.<n>
or unitid.address.04.<n>
if extendednames is set
the returned bit value, with <n> from 1 to 16, 32 or 64 R this is the value read using the Read Input Registers function with bitshort, bitint or bitlong format options
unitid.address.error
or
unitid.address.04.error
if extendednames is set
0, null, >0 R the error status. 0: no error; null: unknown error; >0: modbus exception code

Write Coils

Writes a single coil, using function code 05, or multiple coils, using function 15.

ID Value R/W Description
unitid.address bit:0
bit:false
bit:off
W executes the Write Single Coil function, setting the coil to off
bit:1
bit:true
bit:on
W executes the Write Single Coil function, setting the coil to on
bits:<0|1|-...
W executes the Write Multiple Coils function, setting consecutive coils based on the binary sequence. The left-most bit in the sequence is the first coil
unitid.address.<n>
or unitid.address.05.<n>
if extendednames is set
0 R the write function returned a correct response, and the new status of the coil is off
1 R the write function returned a correct response, and the new status of the coil is on
unitid.address.error
or
unitid.address.05.error
if extendednames is set
0, null, >0 R the error status. 0: no error; null: unknown error; >0: modbus exception code

The value after bit: and bits: can also be a variable, that will be replaced with its numeric content, for example:

IO modbus.27.1220 = bit:$value

Write Registers

Writes registers, using function code 06 or 16, based on the data type.

ID Value R/W Description
unitid.address ushort:<value> W executes the Write Single Register function, writing an unsigned short number
unitid.address uint:<value> W executes the Write Multiple Registers function, writing an unsigned integer number to two adjacent registers
unitid.address ulong:<value> W executes the Write Multiple Registers function, writing an unsigned long number to four adjacent registers
unitid.address short:<value> W executes the Write Single Register function, writing a signed short number
unitid.address int:<value> W executes the Write Multiple Registers function, writing a signed integer number to two adjacent registers
unitid.address long:<value> W executes the Write Multiple Registers function, writing a signed long number to four adjacent registers
unitid.address float:<value> W executes the Write Multiple Registers function, writing a floating-point number using the IEEE Standard 754 representation to two adjacent registers
unitid.address
or
unitid.address.06
unitid.address.16
if extendednames is set
the returned value, according to the requested data type R the write function returned a correct response
unitid.address.error
or
unitid.address.06.error
unitid.address.16.error
if extendednames is set
0, null, >0 R the error status. 0: no error; null: unknown error; >0: modbus exception code

The value after short:, int: etc. can also be a variable, that will be replaced with its numeric content, for example:

IO modbus.27.1220 = int:$value

Examples

Reading an integer value from two registers

TIME: IO modbus.27.400 = readholdingregisters:uint
IO modbus.27.400 : UISET mydata.text = IO modbus.27.400

The first line executes the read holding registers function on a Modbus device with address 27, reading two contiguous registers starting at address 400.

The second line sets a text object of the Web interface with the value read from the Modbus device.

Reading several values at once

TIME: IO modbus.27.220 Modbus.27.222 Modbus.27.224 = readinputregisters:float
IO modbus.27.220 : UISET mydata1.text = IO modbus.27.220
IO modbus.27.224 : UISET mydata1.text = IO modbus.27.224

The first line is a compact form for reading multiple data points.

Reading binary data

TIME: IO modbus.27.1000 = readdiscreteinputs:24
IO modbus.27.1000.1 = 0 : UISET mylabel1.color = g
IO modbus.27.1000.1 = 1 : UISET mylabel1.color = r
...
IO modbus.27.220.24 = 0 : UISET mylabel24.color = g
IO modbus.27.220.24 = 1 : UISET mylabel24.color = r

The first line executes the read discrete inputs function on a Modbus device with address 27, reading two contiguous registers starting at address 1000 and interpreting the first 24 bits as binary values.

The following lines change the color of some objects of the Web interface to green if the bit status is 0 or red if the status is 1.

Reading in extended mode

If the option extendednames was specified in System Settings:

TIME: IO modbus.27.1000 = readholdingregisters:ushort
IO modbus.27.1000.03 : UISET mydata.text = IO modbus.27.1000.03

The first line executes the read holding register function for an ushort value on a Modbus device with address 27, reading the registers 1000.

The following line changes the attribute text of a text object setting it to the value just read.

Writing a single coil

TIME : IO modbus.27.1220 = bit:1

Set coil 1220 to on.

Writing multiple coils

TIME : IO modbus.27.1220 = bits:100

Set three coils starting from 1220 to 1, 0, 0.

Writing registers

TIME : IO modbus.27.210 = ushort:2048 
TIME : IO modbus.27.220 = float:-123.22
TIME : IO modbus.27.230 modbus.27.240 = long:200000000

The first line executes the write single register on a Modbus device with address 27, writing the register at address 210 with an unsigned short number.

The second line executes the write multiple registers on a Modbus device with address 27, writing two consecutive registers starting at address 220 with an IEEE-754 floating-point number.

The third line executes the write multiple registers on a Modbus device with address 27, writing four consecutive registers starting at address 230 and four at 240 with a signed long number.

Release Notes

3.5.0

  • new data types "intx", "longx", "uintx", "ulongx", "floatx" to implement low word first data encoding

3.4.0

  • added support for function 23, Read/Write Multiple Registers:
    • modbusReadWriteMultipleRegisters() Java method and JavaScript function

3.3.0

  • added support for Write Multiple Coils function:
    • writeMultipleCoils() Java method and JavaScript function
    • ID.unitid.address = bits:<0|1>... in EVENTS

3.2.1

  • EVENTS: minor fixes to the readcoils and readdiscreteinputs actions

3.2.0

  • reading multiple consecutive values with a single Modbus read is now supported for readholdingregisters and readinputregisters

3.1.1

  • using format options bitshort, bitint and bitlong for readholdingregisters and readinputregisters, it is now possible to read 1, 2 or 4 registers and return the individual bit values

3.1.0

  • new error events on Modbus read or write errors
  • new option extendedNames. When this option is set, data point names used to return values will have the postfix .FF, where FF is the 2-digit decimal function code

3.0.3

  • fixes to the Modbus write functions
  • variables can now be used in value strings

3.0.0

  • initial release



Modbus is a registered trademark of Modbus Organization, Inc.