1. Overview of Omron PLC Communication Pathways
Omron PLCs expose their internal memory (DM, CIO, WR, HR, AR, EM, TIM/CNT) to a host PC through several protocol stacks. Choosing the correct pathway is the single most important decision when you build a VB or C++ integration; the wrong choice leads to unstable links, lost frames, or vendor lock-in.
The four main pathways are:
- Host Link (C-mode or C-series) — a half-duplex ASCII protocol that originated on the C200H and is still supported on CS/CJ/CP/CP2E/NX/NJ families as a compatibility layer.
- FINS (Factory Intelligent Network Service) — Omron's native command/response protocol, used on Ethernet, RS-232C, RS-422, Controller Link, and EtherNet/IP. Available on CS, CJ1, CJ2, CP1, CP2E, NX, and NJ controllers.
- NT Link — a high-speed proprietary protocol between Omron PT (Programmable Terminals) and PLCs, primarily designed for HMI traffic. See the official OMRON PT communication guide for the topology and 1:1 / 1:N rules.
- OPC / OPC UA — the vendor-neutral path, delivered through Omron's CX-Server Lite / Sysmac Studio OPC UA servers, or third-party KEPServerEX / Matrikon OPC servers.
2. Omron Memory Area Map and Addressing
Every integration has to address words or bits inside the PLC. The Data Memory (DM) area is the most common target for setpoint and recipe data. The address depends on the CPU series and the routing mode (binary vs. BCD). The following table lists the most common areas and the way they appear in Host Link / FINS frames.
| Area | Word prefix (FINS) | Word range (typical CJ2) | Bit prefix | Bit range |
|---|---|---|---|---|
| CIO (Core I/O) | 0x30 / "CIO" | 0–6143 | 0x30 | 0.00–6143.15 |
| WR (Work / Internal Relay) | 0x31 / "WR" | 0–511 | 0x31 | W0.00–W511.15 |
| HR (Holding Relay) | 0x32 / "HR" | 0–511 | 0x32 | H0.00–H511.15 |
| AR (Auxiliary Relay) | 0x33 / "AR" | 0–959 | 0x33 | A0.00–A959.15 |
| DM (Data Memory) | 0x82 / "DM" | 0–32767 | 0x02 | D0.00–D32767.15 |
| EM (Extended Memory) | 0xA0–0xBF (bank 0–F) | 0–32767 per bank | 0xA0–0xBF | E0_0.00 – E_F_32767.15 |
| TIM/CNT | 0x09 / "TIM" | 0–4095 | — | — |
To read DM0100 (word 100) on a CJ2, the FINS memory area code is 0x82, the address field is 0x00010000 (little-endian: 00 00 01 00), and the bit offset is 0x00. The same location in Host Link appears as the four-digit BCD string 0100 within the C-mode command frame.
3. Host Link Protocol (C-Mode) — Frame Structure
Host Link is a single-master, ASCII-only, polled protocol. The PC is always the master; the PLC is always the slave. The default serial parameters are 9600-7-E-2 (9600 bps, 7 data bits, even parity, 2 stop bits), but most modern Omron CPUs also accept 19200, 38400, 57600, and 115200 bps when configured in the PLC Setup (DM area, words DM25650–DM25699 on CJ2).
The C-mode command frame is built as:
@ | NodeNo(2B) | Header(2B) | Text | FCS(2B) | * \r
Where:
-
@— start character (0x40) -
NodeNo— 00–31 (BCD), 00 = broadcast (no response) -
Header— two-letter command, e.g.RD(read DM/CIO),WR(write DM/CIO),SC(status write),MS(mode set),KR,KS -
Text— arguments in BCD/ASCII -
FCS— 8-bit XOR of every byte from@through the last text character, encoded as two hex characters
3.1 Example: read DM0000–DM0009 (10 words)
@ 00 RD 00000000 0A FCS * \r
Broken down: @ start, 00 node 0, RD read command, 0000 beginning word, 0000 beginning bit, 0A number of words (10 decimal). The response from the PLC has the same header plus a 4-character end code, e.g. @00RD00xxxxxxxx….
3.2 Example: write decimal 1234 to DM0100
@ 00 WR 0100 00 1234 FCS * \r
To write multiple consecutive words, append additional 4-digit BCD words after the first value. Each value is the BCD (or signed-hex when the data type flag is changed) representation of the word.
00 = normal completion, 01 = parity error, 02 = framing error, 03 = overrun, 04 = FCS error, 13 = FCS error on receive, 14 = format error, 15 = entry number data error, 16 = command not supported, 18 = frame length error, 21 = non-executable during run, 23 = user memory write-protected.4. FINS Protocol — Ethernet and Serial
FINS is a binary command/response protocol that operates over TCP (port 9600 by default), UDP (port 9600), and serial Host Link physical layers. The CJ2/NJ/NX Ethernet/IP-capable CPUs implement the FINS/TCP server on port 9600 and the FINS/UDP server on the same port.
4.1 FINS/TCP header
Magic(4B) Length(4B) Command(4B) Error(4B) Parameters
12 34 56 78 00 00 00 1A 00 00 00 02 00 00 00 00 …
The first packet sent to the PLC must be the Node Address Send command (0x00000001) so that the PLC learns the client's IP and node number; the second packet is the actual FINS command.
4.2 Memory Area Read — command 0x0101
ICF RSV GCT DNA DA1 DA2 SNA SA1 SA2 SID MRC SRC | Area Address(4) Bit Count(2)
00 00 00 00 0A 00 00 01 00 00 01 01 | 82 00000100 00 000A
Reading DM0100..DM0109 (10 words) from a CJ2: ICF=0x80 (response requested), DNA=0x00 (default network), DA1=0x0A (node 10), SA1=0x01 (host node 1), Area=0x82 (DM), Address=00 00 01 00 = word 100, Bit=0x00, Count=0x000A = 10 words. The response carries end code 0x00000000 on success and 20 bytes of data.
4.3 FINS error codes (main response end codes)
| Code (hex) | Meaning |
|---|---|
| 0x0000 | Normal completion |
| 0x0101 | Local node not part of network |
| 0x0102 | Token timeout, node number too large |
| 0x0103 | Number of transmit retries exceeded |
| 0x0104 | Too many send frames |
| 0x0105 | Node address setting error |
| 0x0106 | Node address duplication |
| 0x0201 | Destination node not part of network |
| 0x0202 | Unit missing |
| 0x0203 | Third node missing |
| 0x0204 | Destination node busy |
| 0x0205 | Response timeout |
| 0x0301 | Communication controller error |
| 0x0302 | CPU Unit error |
| 0x0401 | Undefined command |
| 0x0402 | Cannot process (wrong CPU mode) |
| 0x0501 | Destination address setting error |
| 0x0502 | No routing tables |
| 0x0503 | Routing table error |
| 0x1001 | Command too long |
| 0x1002 | Command too short |
| 0x1003 | Elements/records not in data |
| 0x1004 | Data length too long |
| 0x1005 | Data length too short |
| 0x1101 | Area type mismatch |
| 0x1102 | Address out of range |
| 0x1103 | Address range exceeded |
| 0x2201 | Area read-protected |
| 0x2202 | Area write-protected |
| 0x2203 | UM read-protected (program) |
| 0x2204 | UM write-protected |
| 0x3001 | PLC is in PROGRAM mode |
5. NT Link — When and Why to Use It
According to Omron's Programmable Terminals communication guide, NT Link (1:1 or 1:N) is a high-speed proprietary protocol that runs between an Omron PT and an Omron PLC. It is not intended for PC-to-PLC traffic, and most HMI development tools (CX-Designer, NB-Designer, Sysmac Studio) consume the link directly.
If you intend to use a PT and a PC on the same CPU serial port, you must use a different physical port (CP1W-CIF01 / CIF11 / CIF12 option board on CP1, or the built-in RS-232C / RS-422A ports on CJ1W-SCU / CJ2M). The CPU cannot serve NT Link and Host Link on the same port at the same time.
6. CX-Server and CX-One as Middleware
CX-Server is the COM-based communication core that ships with CX-One, CX-Programmer, and Sysmac Studio. It exposes its objects through two interfaces:
- CX-Server DDE Manager — a Windows DDE server for legacy VB6 / VC++ applications. DDE is deprecated on modern Windows (10 / 11 / Server 2016+); use only for plant-floor migration of existing code.
- CX-Server Lite / CX-Server OPC — a COM automation layer that wraps the same I/O drivers and exposes them to .NET, VB, and C++ through an OLE Automation interface, and to OPC clients through a wrapper that registers itself when installed.
Inside VB, the classic calls are:
Dim plc As Object
Set plc = CreateObject("CXServerHMI.HMIPlc")
plc.Connect "MyCXProject"
Dim v As Variant
v = plc.GetData("D", 100) ' read DM0100
plc.SetData "D", 100, 1234 ' write 1234 to DM0100
plc.Disconnect
Inside C++ (MFC / ATL), you import the type library CXServerHMI.tlb and instantiate the same object through CoCreateInstance. The class IDs and interface GUIDs are registered automatically by the CX-One installer.
7. OPC UA — the Modern Replacement
For new VB.NET or C# code, OPC UA is the recommended path. Omron offers an OPC UA server built into Sysmac Studio for NJ/NX controllers (firmware ≥ 1.10 on the OPC UA option) and an OPC UA server for CJ2 through the SYSMAC Gateway add-on. The endpoint URL follows the pattern:
opc.tcp://<plc-ip>:4840
From a .NET 4.8 / .NET 6 application, reference OPCFoundation.NetStandard.Opc.Ua.Client and use:
var ep = new EndpointDescription("opc.tcp://192.168.1.50:4840");
var session = await Opc.Ua.Client.Session.Create(
configuration, ep, identity);
DataValue v = await session.ReadValueAsync("ns=4;s=DECOMP_DM0100");
For modern NJ/NX controllers, the NJ OPC UA Server Option exposes tag names configured inside Sysmac Studio under Configurations and Setup → OPC UA.
8. Direct Serial Communication in VB (MSComm)
For low-cost, low-volume integrations where CX-One is not available, the legacy MSComm control still works on Windows 10/11 for read-only traffic. MSComm is unsupported on 64-bit .NET processes; for a 64-bit application, fall back to the System.IO.Ports.SerialPort class.
' VB6 + MSComm
MSComm1.Settings = "9600,n,8,2" ' NB: Host Link = 7,E,2; change accordingly
MSComm1.InputLen = 0
MSComm1.CommPort = 1
MSComm1.PortOpen = True
Function Fcs(s As String) As String
Dim i As Integer, f As Long
f = 0
For i = 1 To Len(s)
f = f Xor Asc(Mid(s, i, 1))
Next
Fcs = Right("0" & Hex(f), 2)
End Function
Function HostLinkRead(node As Integer, area As String, _
addr As Long, count As Integer) As String
Dim body As String
body = Format(node, "00") & "RD" & Format(addr, "0000") & _
"00" & Format(count, "00")
Dim frame As String
frame = "@" & body & Fcs(body) & "*" & vbCr
MSComm1.Output = frame
' wait for <LF> response
Do While MSComm1.InBufferCount = 0: DoEvents: Loop
HostLinkRead = MSComm1.Input
End Function
9. Direct FINS/TCP Communication in C++ (Winsock)
A minimal C++17 example using Winsock2 to read DM0100..DM0109 from a CJ2 at IP 192.168.1.50:
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
// Memory Area Read: ICF=0x80, RSV=0x00, GCT=0x02,
// DNA=0x00, DA1=0x0A, DA2=0x00,
// SNA=0x00, SA1=0x01, SA2=0x00, SID=0x00,
// MRC=0x01, SRC=0x01, Area=0x82, Address=00 00 01 00, Bit=0x00, Count=0x000A
static const uint8_t FINS_READ_DM[18] = {
0x80,0x00,0x02,0x00,0x0A,0x00,0x00,0x01,
0x00,0x00,0x01,0x01,0x82,0x00,0x00,0x01,
0x00,0x00,0x0A
};
// FINS/TCP header = Magic(4) | Length(4 BE) | Command(4) | Error(4)
// Command 0x00000001 = Node Address Send
// Command 0x00000002 = Frame Send (FINS body)
Complete the handshake with the 8-byte Node Address Send header carrying the client node number, then issue a Frame Send header whose length field is the size of the FINS body (18 bytes in the example above). Parse the response's end code (bytes 12–15 of the FINS body) and the 20 bytes of DM data that follow.
10. Verification, Diagnostics and Troubleshooting
| Symptom | Likely cause | Diagnostic / fix |
|---|---|---|
| No response, <NAK> from PLC | Wrong node number, wrong parity | Check DIP switch / PLC Setup (DM25650 on CJ2), Host Link = 7-E-2 |
| FCS error (end code 04 or 13) | Bad XOR calculation | Verify the FCS is computed from @ through the last text character, not including the FCS bytes themselves |
| FINS end code 0x0103 | No Node Address Send sent | Always send command 0x00000001 first; some firmware versions are strict about it |
| FINS end code 0x0205 | Response timeout, routing table missing | Check routing table (CX-Integrator) when going through ETN21 or Ethernet/IP unit |
| DM read returns zeros | Wrong memory area code (0x82 vs 0xB2) | DM = 0x82; EM0 bank = 0xA0; EM10 bank = 0xAA |
| Write succeeds in VB but PLC ignores value | Word is being overwritten by ladder logic | Use a non-overlapping DM range (e.g. D30000+) and confirm with cross-reference in CX-Programmer |
| MSComm1.PortOpen fails on Windows 11 | COM port > 16 or USB-serial driver | Use a real COM number, install the OEM USB-serial driver (FTDI 2.12.36+) |
| OPC UA connection refused | Sysmac Gateway service not running | Start Omron SYSMAC Gateway Console service; verify firewall inbound 4840 |
10.1 Loopback self-test
- Pin 2–3 shorted on a DB9 break-out box: anything you send to MSComm1.Output should echo back to MSComm1.Input.
- With the PLC connected, send
@00MS01**\rand verify a00end code — this forces PROGRAM mode, useful when testing offline. - For FINS/TCP, use the free Omron CX-Protocol trace or Wireshark with the FINS/TCP dissector to inspect every frame end-to-end.
11. Selection Matrix — Pick the Right Path
| Use case | Recommended path | Why |
|---|---|---|
| Quick proof-of-concept, one PC, one CPU, RS-232C | Host Link via MSComm / System.IO.Ports | No middleware, single-port, ASCII readable |
| Multi-PLC Ethernet, sub-50 ms polling | FINS/TCP direct | Lowest overhead, full register access |
| SCADA / HMI from multiple vendors | OPC UA via Sysmac Gateway or third-party KEPServerEX | Vendor-neutral, encrypted, browseable |
| Existing VB6 code, no rewrite budget | CX-Server DDE / OPC | Code stays the same, but plan migration to OPC UA |
| PT + PC on the same CPU | Use two physical ports (RS-232C + RS-422A), NT Link on one, Host Link / FINS on the other | Per the OMRON PT communication guide, NT Link and Host Link cannot share a port |
12. Field-Proven Caveats
- Baud-rate auto-detect: many CJ2 firmware versions ignore the "auto-detect" bit in the PLC Setup and will revert to 9600 on power cycle. Always store the desired baud rate in DM25653 (CJ2 port 1) or DM25654 (port 2).
- NT Link collision: the Omron PT communication guide makes clear that an Omron PT in NT Link mode locks the port; do not attempt to multiplex Host Link on the same RS-422A bus.
- FINS over Ethernet/IP unit: if the CPU has no built-in Ethernet, an ETN21 / CJ1W-EIP21 / CS1W-EIP21 unit must be addressed by its unit number (DA1 = 0x10 + unit – 0x10). Mis-routing produces FINS end code 0x0501.
- DM area protection: words D20000–D29599 can be write-protected from the CPU side (PLC Setup bits). A successful transport that returns end code 0x0000 may still leave the value at 0 if the destination register is inside a protected range.
- VB6 / DDE on Windows 10/11: confirm the CX-Server version is ≥ 5.0; older versions do not register the DDE server in the 64-bit registry hive and VB6 clients will see "Topic not registered".
Do I need CX-One to talk to an Omron PLC from VB or C++?
No. CX-One is convenient (CX-Server, CX-Protocol trace, CX-Integrator routing tables) but is not required. You can implement Host Link or FINS frames directly with MSComm, System.IO.Ports, or Winsock. CX-One is required only if you want the DDE / OPC wrapper or ladder/program tooling.
How do I find the address of a DM word in FINS?
The FINS memory-area code for DM is 0x82. The four-byte address is a little-endian word offset. To read DM0100, set Address = 00 00 01 00 and Bit = 00. To read D32767, set Address = 00 00 7F FF.
What is the difference between Host Link and FINS?
Host Link is ASCII, half-duplex, single-port, slow (typical 100–500 ms round trip) and is the compatibility layer for the legacy C series. FINS is binary, supports routing, runs over Ethernet, RS-232C, RS-422, Controller Link, and EtherNet/IP, and is 3–5× faster on equivalent physical layers.
Can I run NT Link and Host Link on the same RS-422A port?
No. Per Omron's PT communication guide, NT Link monopolises the port. Use the second serial option board (CP1W-CIF12 / CJ1W-SCU) to keep PC traffic on a separate physical port.
Why does my FINS/TCP connection drop after a few minutes of idle time?
CJ2/NJ firmware closes idle FINS/TCP sockets after a configurable timeout (default 3 minutes). Either send a no-op FINS command (e.g. 0x00000002 with an empty body) on a heartbeat, or switch to FINS/UDP and manage retries in your application code.