Allen-Bradley CompactLogix and ControlLogix PLCs do not support Modbus TCP natively. Their primary protocol is EtherNet/IP. But in many industrial systems, you need to communicate with Modbus TCP devices — energy meters, VFDs, third-party sensors, or legacy equipment.
Rockwell Automation provides a free solution: Modbus TCP Add-On Instructions (AOIs). These are pre-built function blocks you import into Studio 5000. They use the controller’s built-in socket services to communicate over Modbus TCP without any extra hardware modules.
The AOIs come in two versions:
- Modbus TCP Client AOI — the PLC acts as master, polling external Modbus devices
- Modbus TCP Server AOI — the PLC acts as slave, letting SCADA and HMI systems read its data
This guide covers both configurations step by step — from downloading the AOI to testing communication with real devices.
In This Guide
1. Requirements and Compatibility
| Item | Requirement |
|---|---|
| Controller | CompactLogix 5370/5380/5480 or ControlLogix 5570/5580 |
| Firmware | V20+ (V31+ recommended for best socket performance) |
| Software | Studio 5000 Logix Designer V20+ |
| Ethernet module | Built-in Ethernet port (CompactLogix) or 1756-EN2T/EN3TR (ControlLogix) |
| AOI version | V2.04.00 or later (free from Rockwell) |
| Additional hardware | None — the AOI uses built-in socket services |
⚠️ Important: The older 1756-ENBT module does NOT support socket communication. You must use the 1756-EN2T (Series B or later) or the built-in Ethernet port on CompactLogix for Modbus TCP to work.
2. How Modbus TCP Works on CompactLogix
Since CompactLogix does not have a native Modbus TCP stack, the AOI builds Modbus TCP frames in PLC memory and sends them using CIP socket services — the same TCP/IP socket layer used by EtherNet/IP.
| Mode | AOI | PLC Role | How It Works |
|---|---|---|---|
| Client | MOD_Client | Master — sends requests | PLC opens a TCP socket to the target device on port 502, builds Modbus TCP frames, sends them, and parses responses |
| Server | MOD_Server | Slave — receives requests | PLC listens on port 502 for incoming TCP connections, parses Modbus requests, and responds from a holding register array |
The AOI handles all the protocol details — MBAP header, function codes, transaction IDs, and error handling. You just configure the parameters and map the data.
Performance Limitations
Because the AOI uses socket services (not dedicated hardware), it is slower than a native protocol implementation:
- Minimum reliable poll rate: 100–500 ms per transaction
- Faster rates increase CPU load significantly
- Suitable for monitoring and slow control — not for high-speed real-time applications
- For high-performance Modbus TCP, use a dedicated hardware gateway
3. Downloading the Modbus TCP AOI
- Go to the Rockwell Automation Sample Code Library (search for “Modbus TCP AOI” on rockwellautomation.com)
- Download the file: “Modbus TCP Add-On Instructions for ControlLogix and CompactLogix Controllers” (AOI Version 2.04.00 or later)
- The download contains:
.L5Xfiles for the Client and Server AOIs- A User Manual (PDF) with parameter descriptions
- Example programs
Save these files to your project folder.
4. Importing the AOI into Studio 5000
Step 1. Open Your Project
Open your existing Studio 5000 project or create a new one with your CompactLogix/ControlLogix controller.
Step 2. Import the AOI
Right-click on Add-On Instructions in the Controller Organizer → Import Add-On Instruction → browse to the downloaded .L5X file.
Import both:
MOD_Client(for client/master mode)MOD_Server(for server/slave mode)
Or import only the one you need.
Step 3. Create a Periodic Task
Rockwell recommends running the Modbus AOI in a Periodic Task, not in the Continuous Task. This ensures consistent execution timing.
Right-click Tasks → New Task:
| Setting | Value |
|---|---|
| Name | ModbusTask |
| Type | Periodic |
| Period | 20 ms (minimum 10 ms, but 20 ms is recommended to reduce CPU load) |
Create a new Program and Routine inside this task for your Modbus logic.
5. Step-by-Step: Configure as Modbus TCP Client
The client AOI makes the PLC act as a Modbus master — it polls external devices.
Step 1. Create Tags
Create the following controller-scoped tags:
| Tag Name | Data Type | Purpose |
|---|---|---|
| MB_Client | MOD_Client | Instance of the client AOI |
| MB_ClientData | SINT[256] | Buffer for received data |
| ClientConfig | User-defined | Configuration structure (IP, port, registers) |
Step 2. Add the AOI to Your Routine
In your Modbus routine, add a rung with the MOD_Client AOI instruction.
Step 3. Configure the Client Parameters
| Parameter | Value | Meaning |
|---|---|---|
| CnxnEnable | 1 (TRUE) | Enable the connection |
| IP_Addr | Target device IP (e.g., “192.168.1.100”) | IP address of the Modbus server |
| IP_Port | 502 | TCP port |
| UnitID | 1 | Modbus Unit Identifier (slave address) |
| MsgType | 0 | 0 = Read Holding Registers (FC 03) |
| RegStart | 0 | Starting register address (0-based) |
| RegCount | 10 | Number of registers to read |
| DataPtr | MB_ClientData | Pointer to the data buffer |
Step 4. Trigger the Request
Set CnxnEnable = TRUE. The AOI automatically opens the TCP connection, sends the read request, and stores the response in the DataPtr buffer.
Monitor CnxnStatus for connection state and MsgStatus for transaction results.
6. Client AOI Parameters Explained
| Parameter | Direction | Type | Description |
|---|---|---|---|
| CnxnEnable | Input | BOOL | TRUE = open/maintain connection. FALSE = close. |
| IP_Addr | Input | STRING | Target device IP address |
| IP_Port | Input | INT | TCP port (default 502) |
| UnitID | Input | INT | Modbus Unit ID (1–247) |
| MsgType | Input | INT | 0 = Read Holding Reg (FC 03), 1 = Read Input Reg (FC 04), 2 = Write Single Reg (FC 06), 3 = Write Multiple Reg (FC 16), 4 = Read Coils (FC 01), 5 = Read Discrete Inputs (FC 02), 6 = Write Single Coil (FC 05), 7 = Write Multiple Coils (FC 15) |
| RegStart | Input | DINT | Starting register/coil address (0-based) |
| RegCount | Input | INT | Number of registers/coils |
| DataPtr | InOut | SINT[] | Data buffer for read/write values |
| CnxnStatus | Output | DINT | Connection status (0 = idle, 1 = connecting, 2 = connected, 3 = error) |
| MsgStatus | Output | DINT | Message status (0 = idle, 1 = sending, 2 = done, 3 = error) |
| MsgError | Output | DINT | Error code from the last failed transaction |
MsgType Reference
| MsgType | Modbus Function | Action |
|---|---|---|
| 0 | FC 03 | Read Holding Registers |
| 1 | FC 04 | Read Input Registers |
| 2 | FC 06 | Write Single Register |
| 3 | FC 16 | Write Multiple Registers |
| 4 | FC 01 | Read Coils |
| 5 | FC 02 | Read Discrete Inputs |
| 6 | FC 05 | Write Single Coil |
| 7 | FC 15 | Write Multiple Coils |
7. Polling Multiple Devices
The AOI can handle one transaction at a time per instance. To poll multiple devices or multiple register ranges, you have two options:
Option A: Multiple AOI Instances
Create a separate MOD_Client instance for each device. Each runs independently with its own TCP connection.
| Instance | Target Device | Registers |
|---|---|---|
| MB_Client_Meter1 | 192.168.1.100 | Read 40001–40020 |
| MB_Client_Meter2 | 192.168.1.101 | Read 40001–40020 |
| MB_Client_VFD1 | 192.168.1.200 | Read 40001–40010 |
This is simple but uses more CPU and socket resources.
Option B: Sequencer with One Instance
Use a step counter to cycle through different read/write operations:
// Step 0: Read voltage from Meter 1
IF Step = 0 AND MB_Client.MsgStatus = 2 THEN
// Copy data from buffer
Meter1_Voltage := MB_ClientData[0];
// Reconfigure for next read
MB_Client.IP_Addr := '192.168.1.101';
MB_Client.RegStart := 0;
MB_Client.RegCount := 10;
Step := 1;
END_IF;
// Step 1: Read voltage from Meter 2
IF Step = 1 AND MB_Client.MsgStatus = 2 THEN
Meter2_Voltage := MB_ClientData[0];
// Reconfigure for next read
MB_Client.IP_Addr := '192.168.1.100';
MB_Client.RegStart := 0;
MB_Client.RegCount := 10;
Step := 0;
END_IF;
This uses fewer resources but is slower (each device is polled every N × cycle time).
8. Step-by-Step: Configure as Modbus TCP Server
The server AOI makes the PLC act as a Modbus slave — external systems read and write its data.
Step 1. Create Tags
| Tag Name | Data Type | Purpose |
|---|---|---|
| MB_Server | MOD_Server | Instance of the server AOI |
| MB_HoldRegs | INT[100] | Holding register array (exposed to Modbus clients) |
Step 2. Add the AOI to Your Routine
Add the MOD_Server instruction to a rung in your Modbus routine.
Step 3. Configure Parameters
| Parameter | Value | Meaning |
|---|---|---|
| SrvrEnable | 1 (TRUE) | Enable the server |
| IP_Port | 502 | TCP port to listen on |
| UnitID | 1 | Unit ID this server responds to |
| HoldRegPtr | MB_HoldRegs | Pointer to the holding register array |
| HoldRegCount | 100 | Number of holding registers |
Step 4. Populate Holding Registers
In your PLC program, copy process values into the holding register array:
MB_HoldRegs[0] := Motor_Speed;
MB_HoldRegs[1] := Temperature;
MB_HoldRegs[2] := Pressure;
MB_HoldRegs[10] := Alarm_Status;
Step 5. Test
Connect a Modbus TCP client (ModbusPoll, SCADA, or HMI) to the PLC’s IP on port 502. Read holding registers starting at address 0. The values should match what your PLC program writes to MB_HoldRegs.
9. Server AOI Parameters Explained
| Parameter | Direction | Type | Description |
|---|---|---|---|
| SrvrEnable | Input | BOOL | TRUE = start listening. FALSE = stop. |
| IP_Port | Input | INT | TCP port to listen on (default 502) |
| UnitID | Input | INT | Unit ID to respond to (0 = respond to all) |
| HoldRegPtr | InOut | INT[] | Pointer to holding register array |
| HoldRegCount | Input | INT | Size of holding register array |
| InputRegPtr | InOut | INT[] | Pointer to input register array (optional) |
| CoilPtr | InOut | DINT[] | Pointer to coil data (optional) |
| SrvrStatus | Output | DINT | Server status |
| ClientConnected | Output | BOOL | TRUE when a client is connected |
| WriteEvent | Output | BOOL | TRUE for one scan when a client writes data |
10. Data Type Mapping Between Modbus and Logix
Allen-Bradley PLCs use different data types than the Modbus 16-bit register model.
| Modbus Data | Logix Data Type | Notes |
|---|---|---|
| 1 register (16-bit unsigned) | INT | Direct mapping |
| 1 register (16-bit signed) | INT | Same — Logix INT is signed by default |
| 2 registers (32-bit integer) | DINT | Combine two consecutive INT values |
| 2 registers (32-bit float) | REAL | Combine two INT into DINT, then copy to REAL |
| Coil (1 bit) | BOOL | Map to individual bits |
| Bitmask register | DINT | Use bit-level addressing (e.g., Tag.0, Tag.1) |
Converting 32-Bit Float
Modbus registers 0 and 1 contain a FLOAT32 value (big-endian AB CD):
// Combine two 16-bit registers into a 32-bit float
TempDINT := (MB_ClientData[0] * 65536) + MB_ClientData[1];
COP(TempDINT, ActivePower, 1); // Copy bits from DINT to REAL
⚠️ Warning: Byte order varies by device. If the float value looks wrong, swap registers 0 and 1. See: Modbus Register Map Explained
11. Alternative: Using a Hardware Gateway
If the AOI approach is too slow or consumes too much CPU, use a dedicated Modbus TCP to EtherNet/IP gateway.
| Gateway | Manufacturer | Max Devices | Communication |
|---|---|---|---|
| Anybus AB7072 | HMS Networks | 32 Modbus slaves | EtherNet/IP adapter to PLC |
| Red Lion DA30D | Red Lion | 32 Modbus slaves | EtherNet/IP adapter |
| ProSoft MVI56E-MNET | ProSoft (Belden) | 30 connections | ControlLogix backplane module |
| ICC ETH-1000 | ICC | 20+ devices | EtherNet/IP adapter |
How It Works
- The gateway connects to Modbus TCP devices on port 502.
- The gateway maps Modbus registers to EtherNet/IP produced/consumed tags.
- The PLC reads the gateway as a standard EtherNet/IP device — no AOI needed.
- Poll rates of 10–50 ms are achievable (much faster than the AOI method).
When to Use a Gateway vs AOI
| Criteria | Use AOI | Use Gateway |
|---|---|---|
| Number of Modbus devices | 1–5 | 5–30+ |
| Required poll rate | > 500 ms | < 500 ms |
| CPU load concern | Low (small program) | High (many tasks) |
| Budget | Free (AOI) | $200–$1,000 per gateway |
| Reliability requirement | Monitoring only | Control and protection |
12. Common Errors and How to Fix Them
| Error | Symptom | Cause | Fix |
|---|---|---|---|
| CnxnStatus = 3 | Connection failed | Wrong IP, device offline, or firewall blocking port 502 | Ping the device. Check port 502 with nc -zv IP 502. |
| MsgStatus = 3 | Transaction error | Wrong register address, wrong MsgType, or device rejected the request | Check the device register map. Verify 0-based vs 1-based addressing. |
| Timeout | No response | Device too slow or poll rate too fast | Increase timeout. Slow down the periodic task. |
| Socket error | AOI fails to open socket | 1756-ENBT used instead of EN2T, or max sockets exceeded | Use 1756-EN2T or built-in CompactLogix Ethernet port. |
| Data corruption | Wrong values | Byte order mismatch for 32-bit values | Try swapping the two 16-bit words. |
| CPU overload | PLC faults or slows down | Too many AOI instances or task period too fast | Increase periodic task period to 50–100 ms. Reduce number of AOI instances. |
| Server not responding | SCADA shows timeout | SrvrEnable not set, or wrong UnitID | Set SrvrEnable = TRUE. Match UnitID in PLC to SCADA configuration. |
13. Testing Your Configuration
Test Client Mode
- Run a Modbus Slave simulator on your PC (e.g., ModRSsim2 or Modbus Slave).
- Set the simulator to listen on port 502 with known register values.
- Configure the MB_Client AOI to connect to your PC’s IP.
- Trigger a read and verify received data matches the simulator.
Test Server Mode
- Enable the MB_Server AOI on the PLC.
- Open ModbusPoll or QModMaster on your PC.
- Connect to the PLC’s IP on port 502.
- Read holding registers and verify values match what your PLC program writes.
Verify with Wireshark
Capture traffic on port 502:
tcp.port == 502
You should see Modbus TCP request/response pairs with correct function codes, register addresses, and data values.
💡 Tip: Use the free Modbus Frame Decoder Tool to decode captured frames byte by byte.
Summary
Allen-Bradley CompactLogix and ControlLogix PLCs support Modbus TCP through Rockwell’s free Add-On Instructions. No extra hardware is needed for basic communication.
Key points:
- Download the AOI from Rockwell’s Sample Code Library (free)
- Run the AOI in a Periodic Task (20 ms recommended)
- The AOI uses socket services — requires 1756-EN2T or built-in CompactLogix Ethernet port (not ENBT)
- Client mode (MOD_Client): PLC polls external Modbus devices
- Server mode (MOD_Server): SCADA/HMI reads PLC data over Modbus TCP
- One transaction at a time per AOI instance — use a sequencer for multiple reads
- Poll rates of 100–500 ms are realistic with the AOI approach
- For faster polling or many devices, use a hardware gateway instead
The AOI approach works well for connecting a few meters or sensors. For large-scale Modbus integration, invest in a dedicated gateway.
