Modbus TCP

Manual for Modbus TCP devices and variables in the configuration area.

Updated 15 min read
On this page (8 sections)
  1. Overview
  2. Device Settings
  3. Variable Settings
  4. Supported Data Types
  5. Addressing
  6. Variable Arrays
  7. Bit Selector
  8. Notes

The Modbus TCP configuration pages define Modbus devices and Modbus variables. Start by creating the device connection settings, then create the variables that map to coils, discrete inputs, input registers, or holding registers.

Use Modbus device entries for endpoint information such as address, port, and communication behavior. Use Modbus variable entries for register-level mapping, datatype, scaling, and naming.

Keep register maps grouped logically and name variables according to plant function instead of raw register number only. This makes later troubleshooting easier.

Device Settings

Each Modbus TCP device defines a connection to a Modbus TCP slave (server). Configure the connection endpoint, timing, and data handling options.

Create a new device with the Add button in the toolbar. The Add device dialog contains all device settings organized in the sections General, Connection, Scanning, Byte order, and Diagnostics, with default values already filled in. Enter at least the Name and IP Address and press Create.

The device row shows Enable, Name, IP Address, Port, Unit ID, Scan Rate, Template, and Description directly and these values can be edited in place. Press the Settings icon on a device row to open the Edit device dialog, which contains the same sections and all settings of the device. Press Save to apply the changes.

18 rows
SettingDescriptionExampleUnits / Format
EnableEnables or disables communication with the device.trueBoolean
SimulationRuns the device in simulation mode. The device does not open its real connection; its variables stay available and become read and write in memory like Local variables, with a Good status and a Channel State of Simulation, so the project can be tested without hardware. Written values are held in memory only and reset when the application restarts.falseBoolean
NameUnique device name used as a prefix for all variables on this device.PLC1Text
IP AddressIP address of the Modbus TCP slave.192.168.1.10IPv4
Secondary IPSecondary IP address used for redundancy or failover.192.168.2.10IPv4
PortTCP port of the Modbus slave.502Integer
Unit IDModbus unit identifier (slave address) sent in the request frame.1Integer
Scan RatePolling interval for the device.1000ms
TemplateTemplate device name. When set, all variables from the template are cloned to this device at startup.PLC_TemplateText
DescriptionFree text description for documentation purposes.Main PLC line 1Text
Allow Scanning BlocksEnables grouped block reads to reduce the number of Modbus requests.trueBoolean
Max Block Size AllowedMaximum number of registers per block read when block scanning is enabled.100Integer
Allow Jumping RegistersAllows non-contiguous registers to be included in a single block read by filling gaps.trueBoolean
TimeoutCommunication timeout for a single request before the connection is marked as failed.3000ms
Byte SwapSwaps the byte order within each 16-bit register (low byte / high byte).falseBoolean
Word SwapSwaps the word order for 32-bit values (low word / high word).falseBoolean
Double SwapSwaps the double-word order for 64-bit values.falseBoolean
VerboseLogging level. 0 logs connection changes and errors. 1 also logs every read and write request. 2 also logs the request and response data of every request in hexadecimal, on the same line as the request.0Integer

Variable Settings

Each Modbus TCP variable maps a single register address to a named variable in the EnvisionSCADA namespace.

Create a new variable with the Add button in the toolbar. The Add variable dialog organizes all variable settings in the tabs General, Scaling, Historian, and Gateway, with default values already filled in. Enter at least the Name and Address on the General tab and press Create. When the variable is created on a template device, it is also created on all instance devices that use the template. On a template device, start the Name with the {Device} replacer, so that each instance device gets its own variable. The Name must resolve to a different variable on each device; this is checked for every instance device before anything is saved, when a variable is created and when it is renamed.

The variable row shows Enable, Name, Asset, Type, Address, and Description directly and these values can be edited in place. Press the Settings icon on a variable row to open the Edit variable dialog, which contains the same tabs and all settings of the variable. Press Save to apply the changes. Changes made on a template device are also applied to the matching variable on all instance devices, while each instance keeps its own Modbus Address value. The Gateway tab settings Modbus Address, OPC UA, Writeable, MQTT Publisher, Kafka Device, Kafka Remap Device, and Kafka Remap Variable are also available as table columns, which are shown with Columns as described in the Configuration Tables article.

34 rows
SettingDescriptionExampleUnits / Format
EnableEnables or disables polling of this variable.trueBoolean
NameFull variable name. The {Device}. replacer prefix is optional: {Device} resolves to the device name when the device loads, so the variable follows a device rename automatically, while a literal name is used exactly as written and keeps working across device renames. The resolved name must be unique across the whole project.Pump1_SpeedText
AssetAsset path for logical grouping.Line1/Pump1Text
TypeData type of the variable.REALEnum
AddressModbus register address in standard or IEC notation.%MW100See Addressing
DescriptionFree text description for documentation.Pump 1 speed setpointText
Recipe TargetsRecipe type name (target) this variable belongs to. Supports the {Device} placeholder, which resolves to the owning device name so one naming rule can spread a set of variables across several devices.Line1Text
Recipe Parameter NameExplicit name of the parameter this variable represents inside a recipe. Variables that fill the same role across different targets share the same Recipe Parameter Name, which is how values are matched on Apply and Snapshot. A variable takes part in recipes only when both Recipe Targets and Recipe Parameter Name are set.SetpointText
Trend GroupsComma-separated list of trend group names this variable belongs to. A variable can be in several groups. The groups are the filter the Trend Analyst offers when picking variables. The names accept the {Device}, {DeviceName} and {VariableName} placeholders, so one UDT definition gives every instance its own group.Pumps,{Device}Text
User GroupsComma-separated user groups allowed to write this variable from the runtime.Operators,EngineersText
Signature RequiredRequires an electronic signature for each write to this variable from a page object, the Live Manager, the Runtime API or MCP while Part 11 compliance mode is enabled and Write variables marked Signature Required is selected under Signature Required Actions on the Parameters page. The write rights of the user and the value range are checked first, so a refused write does not open the Electronic Signature dialog. A variable created while Part 11 compliance mode is enabled starts with Signature Required selected. Writes by alarm, event and schedule actions, Python scripts and the gateways run without a signature. See the Electronic Signatures article.trueBoolean
UnitsEngineering units displayed alongside the value.RPMText
FormatDisplay format of the value on screens and in the Trend Analyst: the value of a Label or Input (unless the object uses its own Floating Point and Decimals), the value text of a Knob and a Slider, the Trend tooltip, a Data Grid Variable column, the right-click menu, Quick Trend, the current value in the Recipe Manager, variable tokens in alarm and event messages, and the Trend Analyst tooltip, summary and pen Format. An Input shows the plain value while it is being edited. 0 shows no decimals, 0.00 always shows two decimals, 0.0## shows one to three decimals and #,##0.00 adds a thousands separator, with up to 15 decimals. Values are rounded half away from zero for display only; alarms and expressions keep the full value, and so does the historian unless Round to Format is on. ReadVariable in the runtime API and MCP returns the Format beside the full value. Leave empty to show the value as received.0.00Text
Min RawMinimum raw value for linear scaling.0Number
Max RawMaximum raw value for linear scaling.27648Number
Min ScaledMinimum scaled (engineering) value.0Number
Max ScaledMaximum scaled (engineering) value.100Number
ConverterConverter name applied to the variable value.Celsius_FahrenheitText
DeadbandMinimum value change required before the historian records a new sample.0.5Number
Minimum TimeMinimum time between historian samples regardless of value change.1000ms
Sample GroupSample group name for historian grouping.FastText
HistorianEnables historian logging for this variable.trueBoolean
Round to FormatStores the historian samples of this variable rounded to the decimals of its Format, as a plain number: with Format 0.00 the value 34.30487 is stored as 34.3. The deadband compares the rounded values, so with Deadband 0 a sample is written only when the rounded value changes. On a variable without scaling the Raw value is stored empty, since it equals the value. The live value, alarms, expressions and all interfaces keep the full value. Applies to new samples only; samples already stored keep their values. Requires a Format.falseBoolean
History SourceWhere the trends read the history of this variable: Project default (the History Source with Project default enabled on the History Sources page, or the EnvisionSCADA Historian when no source has it), EnvisionSCADA Historian (the samples recorded while Historian is on) or a History Source of the History Sources page (a query on a Database Connection or the fetch() function of a Python Module script). In the table, an empty History Source is Project default and Historian is the EnvisionSCADA Historian. Live values always come from the variable itself. Test reads the last 24 hours with the settings in the dialog. See the History Sources article.Project defaultEnum
History ReferenceWhat the History Source reads for this variable: the reference passed to fetch(), or @Variable of the History Query. When empty, the History Reference of the source is used, by default the full name of the variable; the field shows it in italics. {Name} is replaced by the full name of the variable. Browse lists the references that a Script source offers.CDT158Text
History ParametersShown for a Script source: optional key=value pairs separated by ;, added to the History Parameters of the source. A key set here replaces the same key of the source. The field shows the History Parameters of the source in italics.scale=0.1Text
Start new blockForces this variable to start a new read block instead of being merged into the previous one. Use it to split a register range that the device refuses to serve in one request.falseBoolean
Modbus AddressOptional address on the built-in Modbus TCP and Modbus RTU servers where this variable is exposed. The servers are enabled in the Gateways section of the Parameters page. Each instance device keeps its own value.%MW0See Addressing
OPC UAExposes this variable on the built-in OPC UA server. When Historize is also ticked, the Historian data of the variable is readable through OPC UA Historical Access.trueBoolean
WriteableAllows external systems to write this variable through the gateways: the OPC UA Server, the Modbus TCP and Modbus RTU servers, the MQTT publisher when Subscribe is enabled on the publisher, and Kafka when Consumer is enabled on the Kafka device. Writes from pages, actions, scripts and the API are independent of this setting.trueBoolean
MQTT PublisherName of the MQTT publisher that publishes this variable, typed or chosen with the ... button. A variable is published by one publisher, and the name must match a publisher on the MQTT Publishers page. Leave empty to keep the variable unpublished. Writes from the broker need Subscribe on the publisher and Writeable on the variable. See the MQTT Publishers article.CloudIoTText
Kafka DeviceName of the Kafka device this variable is linked to, typed or chosen with the ... button. Every value change of a linked variable is produced to the Producer Topic of the Kafka device while the device is enabled. Writes from the Consumer Topic need Consumer on the Kafka device and Writeable on the variable. Leave empty to keep the variable unlinked. See the Kafka Devices article.KafkaBroker1Text
Kafka Remap DeviceDevice name used for this variable in Kafka messages instead of the name of the device that owns the variable. Inbound messages from the Consumer Topic are matched on this name as well. Leave empty to use the device name.RemotePLC1Text
Kafka Remap VariableVariable name used for this variable in Kafka messages instead of its own name. Inbound messages from the Consumer Topic are matched on this name as well. Leave empty to use the variable name without the device name, for example Pump1_Speed for PLC1.Pump1_Speed on device PLC1.SpeedText

Supported Data Types

The Type field determines how the raw register bytes are interpreted.

13 rows
TypeDescriptionSize
BOOLSingle bit value (true/false).1 bit
BYTEUnsigned 8-bit integer.1 byte
INTSigned 16-bit integer.1 register
UINTUnsigned 16-bit integer.1 register
WORDUnsigned 16-bit word.1 register
DINTSigned 32-bit integer.2 registers
UDINTUnsigned 32-bit integer.2 registers
DWORDUnsigned 32-bit word.2 registers
REAL32-bit IEEE 754 floating point.2 registers
LINTSigned 64-bit integer.4 registers
ULINTUnsigned 64-bit integer.4 registers
LREAL64-bit IEEE 754 floating point.4 registers
STRINGCharacter string read from consecutive registers.Variable

Addressing

The Address field supports both IEC-style prefix notation and standard Modbus numeric notation. Holding and input register addresses support an optional bit selector using dot notation.

11 rows
FormatRegister TypeExample
%MW{n}Holding Register (zero-based)%MW0, %MW100
%IW{n}Input Register (zero-based)%IW0, %IW50
%I{n}Discrete Input (zero-based)%I0, %I15
%M{n}Coil (zero-based)%M0, %M7
400001-465535 (6-digit)Holding Register400001, 400100
40001-49999 (5-digit)Holding Register40001, 40100
300001-365535 (6-digit)Input Register300001, 300050
30001-39999 (5-digit)Input Register30001, 30050
100001-165535 (6-digit)Discrete Input100001, 100016
10001-19999 (5-digit)Discrete Input10001, 10016
1-9999Coil1, 8

Variable Arrays

A variable can expand into an array by setting the Array size field to a value between 2 and 512. The variable itself is the first element and its Name must end in the first index, for example PLC1.Temps[0]. The remaining elements are created automatically as Temps[1], Temps[2] and so on, with addresses advancing by the width of the data type: one register for INT, UINT and WORD, two registers for DINT, UDINT, DWORD and REAL, four registers for LINT, ULINT and LREAL, and one point per element for coils and discrete inputs. An address with a bit selector cannot form an array.

The element rows are managed by the first element: they follow its settings, appear locked in the variable list, and are edited by editing the first element. Changing the Array size adds or removes elements at the end. Renaming the first element or changing its address renames and re-addresses all elements. Setting Array size back to 0 while elements exist removes the elements.

To turn the elements into ordinary independent variables, use the Ungroup array action on the first element's row in the variable list, or the Configuration API (POST /API/Config/VariableArrays/Ungroup). All rows are kept; only the grouping is removed. This is one-way. The element count badge next to the first element's name folds and unfolds the element rows in the list.

The same mechanism is available on OPC UA (the elements share one array node), EtherNet/IP (the tag address must end in an array index such as MyArray[0]), S7, MELSEC, Omron FINS, DNP3, BACnet, IEC104, Beckhoff ADS and Local variables.

Bit Selector

Use the dot notation to select a single bit from a holding or input register. This is useful for reading packed status words as individual BOOL values.

FormatDescriptionExample
%MW{n}.{bit}Bit from holding register (zero-based bit index).%MW100.0, %MW100.15
%MW{n}.X{bit}Alternative notation with X prefix.%MW100.X0, %MW100.X7
{address}.{bit}Bit from holding register using numeric notation.400101.0, 400101.5
%IW{n}.{bit}Bit from input register.%IW50.3

A BOOL on a bit of a holding register can be written. The write sends that one register, with the selected bit set or cleared in the register value of the last scan. The value must be true, false, 1 or 0. Any other value is rejected and the register on the device keeps its value. The variable status then shows FormatError until the next successful read; ModbusWriteMany in a Python script skips the entry and reports it as not written.

Notes

The channel state indicator on each device row shows the current communication status. A connected device shows the execution time of the last scan cycle.

When Allow Scanning Blocks is enabled, the system groups contiguous register ranges into a single Modbus request. This reduces network overhead but requires that the slave supports reading a range that spans all registers in the block. Use Max Block Size Allowed to limit the block length if the slave has restrictions.

Byte Swap, Word Swap, and Double Swap affect the byte ordering of multi-byte values. The correct combination depends on the Modbus slave device. When communication succeeds but values appear incorrect, adjust the swap settings to match the byte order of the target device.

Variables are referenced at runtime using the format DeviceName.VariableName, for example PLC1.Pump1_Speed.

Simulation mode lets you exercise a project when the device is not connected. When Simulation is enabled on a device, the device does not open its connection and its scan thread does not run. Its variables stay present in the runtime with a Good status instead of Connection Lost, and each variable is read and written directly in memory, exactly like a Local variable. This makes graphics, alarms, events, logic, and trends respond to values written from the runtime without any hardware. The device Channel State reads Simulation. Written values live in memory only and reset to their default when the application restarts. Turning Simulation off and saving reloads the device and it attempts the real connection again.

Enable Verbose only during commissioning or diagnostics. It increases log volume significantly. With Verbose 2 the SystemLog Viewer shows every request as one line: the request text, then TX with the request and RX with the response of the device, both in hexadecimal. TX and RX start with the Modbus function code, followed by the data of that function code. The Unit ID is part of the request text.

SystemLog Viewer with Verbose 2
Reading Holding Registers at Unit:1 Start:100 Count:3 | TX [0300640003] | RX [030604D200000000]
Write Multiple Registers at Unit:1 Addr:100 Value:04D2 | TX [10006400010204D2] | RX [1000640001]
Write Single Coil at Unit:1 Addr:5 Value:True | TX [050005FF00] | RX [050005FF00]

An RX value that starts with the function code plus 80 hexadecimal is a Modbus exception response, followed by the exception code. For example, RX [8302] is exception code 02 on a Read Holding Registers request. RX (no valid response) is shown when the request ends without a valid response, for example after a timeout. In both cases the error is logged on the next line.

A value written to a variable must match its type: a number for the numeric types and true, false, 1 or 0 for a BOOL. A value that does not match, for example text written to a REAL, is not sent and the register on the device keeps its value. The System Log records the rejected write, and the variable status shows FormatError until the next successful read of the variable.

Something missing or wrong in this article? Contact us