Omron PLC Communication: VB and C++ Host Link and FINS Tutorial

James Nishida13 min read
OmronSerial CommunicationTutorial / How-to
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.
Engineering rule: Use Host Link or FINS for low-latency polling loops (≤ 100 ms) and small point counts. Use OPC UA for SCADA-style tag publishing, multi-client access, and cross-vendor architectures. DDE (Dynamic Data Exchange) is deprecated since Windows Vista and must not be used for new designs.

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.

End codes: 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.

Licensing: CX-Server is part of the CX-One DVD. The CX-Server Lite runtime is free to redistribute with your installer, provided the CX-One license is present on the development machine. OPC clients connecting to CX-Server OPC are limited to the number of tags the license tier supports.

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

  1. Pin 2–3 shorted on a DB9 break-out box: anything you send to MSComm1.Output should echo back to MSComm1.Input.
  2. With the PLC connected, send @00MS01**\r and verify a 00 end code — this forces PROGRAM mode, useful when testing offline.
  3. 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.

Back to blog