Omron CJ1M .NET FINS Communication: Protocol Reference

James Nishida18 min read
HMI ProgrammingOmronTechnical Reference
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: Why Implement FINS in .NET

Industrial gateways that bridge Omron CS/CJ-series PLCs to SCADA, MES, or cloud telemetry stacks often rely on third-party OPC servers or vendor-locked ActiveX controls. Both paths age poorly: classic Omron CX-Server COM components only work in 32-bit hosts, and many FINS toolkits on the market are abandoned. Building a native .NET FINS gateway against the CJ1M-CPU13 gives you a modern async, cross-platform, 64-bit-clean implementation that you control end-to-end.

The CJ1M-CPU13 is a compact, modular CPU that ships with one RS-232C port (peripheral or Host Link) and supports an Ethernet Communication Unit (ETN21) on the backplane. Both transports speak the same application layer — FINS (Factory Interface Network Service) — so a single .NET state machine can drive serial and TCP/UDP sessions with only the transport layer changing.

This reference documents the FINS frame format, the memory-area model, the command codes you need for CIO/HR/DM access, and a production-ready C# transport implementation with code samples, error handling, and verification steps.

Mandatory reading before writing code. The Omron FINS Commands Reference Manual W421-E1 and the CS/CJ-series Ethernet Units Operation Manual W342-E1 (also cited as W420 for the CPU Bus Unit setup) are the primary protocol specifications. The Host Link manual W227-E1 covers the serial gateway path. Download from your regional Omron support portal (e.g., industrial.omron.eu).

2. FINS Protocol Architecture

FINS is a request/response application-layer protocol. The CPU (or a Communication Unit) accepts a command frame, executes it on the specified memory area, and returns a response frame. Every frame has a fixed header followed by command-specific data.

2.1 Layer Model

Layer Function Notes
Application FINS command/response Defined in W421, identical across transports
Session FINS header (ICF, SID, DA/SA) Routes frames through FINS network / unit / node addresses
Transport (Ethernet) UDP or TCP Default UDP/TCP port 9600
Transport (Serial) Host Link (C-mode) or FINS-on-serial Peripheral / RS-232C port, default 9600/19200 bps
Physical 10/100Base-TX or RS-232C ETN21, CP1W-CIF01, or onboard port

2.2 Frame Topology

Every FINS frame is structured: [FINS Header (10 or 16 bytes)] [Command Code (2 bytes)] [Command Body] [Response Body]. For Ethernet, the header expands to 16 bytes to include the Gateway count and destination/source network addresses; serial frames use 10 bytes because the Host Link layer adds its own addressing wrapper.

3. FINS Header Layout (Ethernet / TCP-UDP)

Offset Field Size (B) Description
0 ICF 1 Information Control Field. 0x80 = response required, 0x81 = response not required. Set bit 6 (0x40) for command, bit 7 (0x80) for response. Typical request: 0x80.
1 RSC 1 Reserved, 0x00
2 GCT 1 Gateway count. Number of networks to traverse. 0x02 when going through an ETN21 to a CJ1M on the same Ethernet segment.
3 DNA 1 Destination network address (0–127). The ETN21's network number.
4 DA1 1 Destination node number (0–254). FINS node derived from the last octet of the unit's IP.
5 DA2 1 Destination unit address. 0x00=CPU, 0xFE=ETN21 unit.
6 SNA 1 Source network address (your gateway's network)
7 SA1 1 Source node (your PC's FINS node)
8 SA2 1 Source unit (0x00 for PC application)
9 SID 1 Service ID. Use a rolling counter 0x00–0xFF to correlate requests/responses.
10 DNA2 / SNA2 2 (Ethernet only) Relay network / node when GCT > 2
DA1n / SA1n varies Additional hop pairs, length 3×(GCT–1) bytes

The CPU responds with the same SID, swapped SA/DA, and bits 0–3 of ICF holding the completion code (MRES/SRES codes). The main response code is at offset 12 in the response payload, sub-code at 13.

4. Memory Areas and Address Encoding

Each FINS memory command encodes the target area as a 2-byte area code followed by a 3-byte address. Word addresses are little-endian; bit addresses use the lower 4 bits of byte 2 of the address (0x0–0xF).

Area Area Code (hex) Range (CJ1M) Notes
CIO 0x30 0–6143 Includes built-in I/O, CP-series link words, and CPU Bus Unit areas. CJ1M also exposes 0x00 as an alias for CIO in some firmware revisions.
WR (Work) 0x31 0–511 Internal work relays/words.
HR (Holding) 0x32 0–511 Retained on power loss.
AR (Auxiliary) 0x33 0–959 Read-only for most user code.
DM (Data Memory) 0x02 (D20000+) / 0x82 (legacy D00000–D19999) D0–D32767 Use 0x82 for the banked legacy region, 0x02 for the modern D20000+ region. CJ1M CPU13 supports EM banks 0–3 (0x180x20 for E0_0–E3_32767).
EM (Extended) 0x180x20 (banks 0–3), 0x300x38 (banks 4–7 if enabled) 32K words per bank Requires EM allocation in PLC Setup.
TK (Task Flag) 0x06 bit only Rarely used by SCADA gateways.
Timer PV 0x09 0–4095 Read only PV values.
Counter PV 0x05 0–4095 Read only PV values.
Address packing. The 3-byte word address must be little-endian. To read DM D00100, encode as bytes 0x64 0x00 0x00. To read CIO bit CIO 0.07, encode the word (0) and put 0x07 in the low nibble of the third address byte.

5. Core FINS Commands

W421 enumerates ~80 commands. The following subset covers 95% of SCADA/IIoT gateway use cases against a CJ1M-CPU13.

Command Code (hex) Function
Memory Area Read 01 01 Read up to 999 consecutive words from one area
Memory Area Write 01 02 Write up to 999 words to one area
Memory Area Fill 01 04 Fill a region with a constant (useful for DMs on commissioning)
Multiple Memory Area Read 01 05 Read up to 198 words from up to 4 disjoint areas in one round-trip (efficiency win for tag-list polling)
Memory Area Transfer 01 06 Copy a block within or between areas
Run/Stop/Read CPU 04 01 / 04 02 Mode change and status read
Controller Data Read 05 01 Controller model, version, area ranges
Controller Status Read 06 01 Operating mode, fatal/non-fatal flags
Time Read/Write 07 01 / 07 02 Read or set PLC clock (BCD encoded)
Message Read/Write/Log 21 0121 04 FINS message instructions (MSG-SND/MSG-RCV) used by the PLC program
File Memory Read/Write 22 01 / 22 02 EM file and Memory Card operations
Clock/Calendar 2nd Read 07 03 Sub-second precision (CJ2 and later; CJ1M may not support)

5.1 Command Body: Memory Area Read (01 01) Example

To read 10 words starting at DM D00100:

  1. Area code: 0x82 (DM legacy) or 0x02 (DM 20000+). Pick one based on PLC Setup → DM Area Setup.
  2. Address: 0x64 0x00 0x00 (=100 decimal, little-endian).
  3. Number of items: 0x0A 0x00 (little-endian word count).

Command payload (after the 2-byte command code): 82 64 00 00 0A 00. Full request frame on Ethernet: 10-byte FINS header + 01 01 + payload. Response payload carries the 10 words little-endian (20 bytes), and the trailing MRES/SRES end codes (0x0000 = success).

5.2 Command Body: Multiple Memory Area Read (01 05) Example

Read CIO 0, HR 0, and DM 0, each 5 words:

Body: 03 00         // number of area entries
      30 00 00 00 05 00   // CIO 0, 5 words
      32 00 00 00 05 00   // HR 0, 5 words
      02 64 00 00 05 00   // DM 100, 5 words

Response packs the three blocks in order. Maximum 198 words across all entries.

6. Transport: Ethernet (TCP / UDP)

The ETN21 supports both TCP and UDP FINS on port 9600. UDP gives the lowest latency for periodic polling; TCP gives a session model that survives PLC reboots and is recommended for write operations.

6.1 ETN21 Configuration (CX-Programmer)

  1. Set the unit's IP address, subnet, and gateway in the ETN21's I/O Table → Unit Setup dialog.
  2. Set FINS/UDP Port = 9600, FINS/TCP Port = 9600.
  3. Set FINS Node Number = last octet of the IP (e.g., 100 for 10.0.0.100).
  4. Set Network Number (1–127) for routing across multiple subnets.
  5. Set Routing Table entries if crossing ETN21 → CPU.
  6. Transfer settings, then power-cycle the rack.

Reference: CS/CJ-series Ethernet Units Operation Manual W342 and the ProFace CS/CJ Ethernet Driver notes (PDF) for cross-vendor confirmation of port and FINS node behavior.

6.2 Connection Parameters to Cache in Your .NET App

Parameter Typical Value Source
Remote IP 10.0.0.100 ETN21 setup
Local FINS Node 10.0.0.5 (your PC) Operating system
PLC FINS Network 1 ETN21 routing table
PLC FINS Node 100 ETN21 setup (last IP octet)
PLC Unit Number 0 (CPU) or 0xFE (ETN21) Rack/slot position
Transport TCP/UDP 9600 ETN21 setup
Connection Count TCP: 16 simultaneous, UDP: unlimited (unreliable) W342 §4-3

6.3 .NET UDP Transport Skeleton

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

public sealed class FinsUdpClient : IDisposable
{
    private readonly UdpClient _socket;
    private readonly IPEndPoint _remote;
    private readonly byte _sid;
    private int _sidCounter;

    public byte LocalNode   { get; }
    public byte LocalNetwork { get; }
    public byte RemoteNode  { get; }
    public byte RemoteNetwork { get; }
    public byte RemoteUnit  { get; }

    public FinsUdpClient(string remoteIp, int port,
                         byte localNet, byte localNode,
                         byte remoteNet, byte remoteNode, byte remoteUnit = 0)
    {
        _socket = new UdpClient(0); // bind ephemeral
        _socket.Client.ReceiveTimeout = 2000;
        _remote = new IPEndPoint(IPAddress.Parse(remoteIp), port);
        LocalNetwork = localNet; LocalNode = localNode;
        RemoteNetwork = remoteNet; RemoteNode = remoteNode;
        RemoteUnit = remoteUnit;
    }

    public async Task<byte[]> SendAsync(byte[] commandBody, CancellationToken ct = default)
    {
        byte sid = (byte)Interlocked.Increment(ref _sidCounter);
        var frame = BuildFrame(sid, commandBody);
        await _socket.SendAsync(frame, frame.Length, _remote).ConfigureAwait(false);

        var result = await _socket.ReceiveAsync().WaitAsync(ct).ConfigureAwait(false);
        ValidateResponse(frame, result.Buffer, sid);
        return result.Buffer;
    }

    private byte[] BuildFrame(byte sid, byte[] body)
    {
        // 10-byte FINS header + 2-byte command code + body
        var frame = new byte[10 + body.Length];
        frame[0] = 0x80;                       // ICF: command, response required
        frame[1] = 0x00;                       // RSC
        frame[2] = 0x02;                       // GCT: 2 hops (PC -> ETN21 -> CPU)
        frame[3] = RemoteNetwork;
        frame[4] = RemoteNode;
        frame[5] = RemoteUnit;
        frame[6] = LocalNetwork;
        frame[7] = LocalNode;
        frame[8] = 0x00;                       // SA2 (PC unit)
        frame[9] = sid;
        Buffer.BlockCopy(body, 0, frame, 10, body.Length);
        return frame;
    }

    private static void ValidateResponse(byte[] request, byte[] response, byte sid)
    {
        if (response.Length < 14)
            throw new FinsException("Truncated FINS response");
        if (response[9] != sid)
            throw new FinsException($"SID mismatch: sent 0x{sid:X2} got 0x{response[9]:X2}");
        int main = response[12];
        int sub  = response[13];
        if (main != 0 || sub != 0)
            throw new FinsException($"FINS error 0x{main:X2}{sub:X2}");
    }

    public void Dispose() => _socket?.Close();
}

public class FinsException : Exception { public FinsException(string m): base(m) {} }

6.4 .NET TCP Transport Notes

TCP FINS prepends a 4-byte length header to the FINS frame (big-endian, length in bytes of the FINS payload). The PLC closes the socket if the unit is reset, so wrap your read loop in a reconnect task. Use a single NetworkStream with a SemaphoreSlim(1,1) to serialize requests; FINS is half-duplex on a given socket.

7. Transport: Serial (Host Link / FINS-on-Serial)

The CJ1M's peripheral port and the optional CP1W-CIF01 RS-232C/485 Option Board can run Host Link (C-mode) or a serial FINS variant. FINS over serial is rare in practice; most legacy installs use Host Link because it is the path supported by older HMI panels.

7.1 Host Link (C-mode) Frame Format

@ [FA header (optional 2B)] [node (2B ASCII)] [header (2B)] [text] [FCS (2B)] [* CR]

Default baud is 9600, 7E2, node 00. The '@' sentinel indicates the start; '*' and CR mark the end. The Frame Check Sequence is the two-character ASCII hex of the XOR of every byte between '@' and the FCS. The Host Link Command Reference W227 details each text code (RR, WR, RD, WD, etc.).

FA header. When using serial FINS (gateway to Ethernet backplane), the FA field carries the FINS header (2 bytes: command/response + SID, or the full 10-byte header in the long format). Most modern .NET gateways skip this and use Host Link with reduced command set, or migrate to Ethernet.

7.2 Choosing the Transport

Criterion Use Ethernet (FINS/TCP or UDP) Use Serial (Host Link)
Latency 5–20 ms typical 50–200 ms at 9600 baud
Throughput Thousands of words/s ~50 words/s with 1 ms scan
Cable distance 100 m per hop, switches extend 15 m RS-232C, 1200 m RS-485
Modernity Recommended for new gateways Legacy / brownfield
PLC effort Low (ETN21 handles routing) CPU serial port cycles per char

8. Tag Mapping and Polling Patterns

A SCADA gateway rarely reads one word at a time. The recommended pattern is to build a tag list, then issue a single 01 05 Multiple Memory Area Read per scan for all tags that share the same FINS node, batching by maximum 198 words. A second 01 05 handles any overflow or tags in different EM banks.

8.1 Address Resolution Rules

  • DM100 (CX-Programmer notation) → area code 0x82, word 100.
  • D20100 → area code 0x02, word 100. The D20000 offset is implied.
  • CIO 0.07 → area code 0x30, word 0, bit 0x07.
  • HR10 → area code 0x32, word 10.
  • E0_0 → area code 0x18, word 0 (EM bank 0).

8.2 Polling Cadence

On a CJ1M-CPU13, the FINS response time for a 198-word read is typically < 30 ms over a 100 Mbit LAN. A 100 ms scan gives three full reads per second and is a sensible default. Avoid issuing a new FINS request before the previous response arrives on the same TCP socket; on UDP, allow a 100 ms timeout for lost frames before retrying with the same SID.

9. .NET Implementation Building Blocks

9.1 Frame Encoder

public static class FinsEncoder
{
    public static byte[] BuildReadWords(byte area, int address, ushort count)
    {
        if (count == 0 || count > 999)
            throw new ArgumentOutOfRangeException(nameof(count));
        return new byte[]
        {
            0x01, 0x01,                      // Memory Area Read
            area,
            (byte)(address & 0xFF),
            (byte)((address >> 8) & 0xFF),
            (byte)((address >> 16) & 0x0F),
            (byte)(count & 0xFF),
            (byte)((count >> 8) & 0xFF)
        };
    }

    public static byte[] BuildWriteWords(byte area, int address, ushort[] data)
    {
        if (data.Length == 0 || data.Length > 999)
            throw new ArgumentOutOfRangeException(nameof(data));
        var body = new byte[6 + data.Length * 2];
        body[0] = 0x01; body[1] = 0x02;       // Memory Area Write
        body[2] = area;
        body[3] = (byte)(address & 0xFF);
        body[4] = (byte)((address >> 8) & 0xFF);
        body[5] = (byte)((address >> 16) & 0x0F);
        body[6] = (byte)(data.Length & 0xFF);
        body[7] = (byte)((data.Length >> 8) & 0xFF);
        for (int i = 0; i < data.Length; i++)
        {
            body[8 + i*2]     = (byte)(data[i] & 0xFF);
            body[8 + i*2 + 1] = (byte)((data[i] >> 8) & 0xFF);
        }
        return body;
    }
}

9.2 Async Tag Poller

public sealed class FinsTagPoller
{
    private readonly FinsUdpClient _fins;
    private readonly CancellationTokenSource _cts = new();
    public event Action<TagSnapshot>? OnSnapshot;

    public async Task RunAsync(TimeSpan period, IEnumerable<FinsTag> tags)
    {
        var grouped = tags.GroupBy(t => t.FinsNode).ToList();
        while (!_cts.IsCancellationRequested)
        {
            var snap = new TagSnapshot(DateTime.UtcNow);
            foreach (var g in grouped)
            {
                var body = FinsEncoder.BuildMultiRead(g.Select(t => t.Descriptor).ToArray());
                var resp = await _fins.SendAsync(body, _cts.Token).ConfigureAwait(false);
                FinsDecoder.UnpackMultiRead(resp, g.ToList(), snap);
            }
            OnSnapshot?.Invoke(snap);
            await Task.Delay(period, _cts.Token).ConfigureAwait(false);
        }
    }
}

9.3 Graceful Shutdown and Reconnect (TCP)

while (!ct.IsCancellationRequested)
{
    try
    {
        using var tcp = new TcpClient();
        await tcp.ConnectAsync(_ip, _port, ct);
        await using var ns = tcp.GetStream();
        while (!ct.IsCancellationRequested)
        {
            var req = await _queue.Reader.ReadAsync(ct);
            var frame = BuildFrame(req.Sid, req.Body);
            var length = BitConverter.GetBytes(frame.Length).Reverse().ToArray();
            await ns.WriteAsync(length, ct);
            await ns.WriteAsync(frame, ct);
            var hdr = new byte[4];
            await ReadExactAsync(ns, hdr, ct);
            int n = (hdr[0] << 24) | (hdr[1] << 16) | (hdr[2] << 8) | hdr[3];
            var resp = new byte[n];
            await ReadExactAsync(ns, resp, ct);
            req.CompletionSource.TrySetResult(resp);
        }
    }
    catch (Exception ex) when (!ct.IsCancellationRequested)
    {
        await Task.Delay(TimeSpan.FromSeconds(2), ct);
        // log ex, loop back to reconnect
    }
}

10. Error Codes and Recovery

MRES (hex) SRES (hex) Meaning Recommended Action
00 00 Success None
02 01 Local node not in network Verify PLC FINS routing table; ensure ETN21 is online
02 02 Token timeout Increase timeout, check for network congestion
02 03 Retries exhausted Check cabling / switch, verify PLC in RUN/MONITOR
03 01 Destination node not in network Wrong DA1 / DA2 — confirm FINS node = last IP octet
03 02 No such unit Wrong DA2; use 0x00 for CPU, 0xFE for ETN21
04 01 Undefined command Check command code; CJ1M does not implement all of W421
05 01 Destination address error Address outside area range (e.g., DM 40000 on CPU13)
05 02 Wrong number of items Item count 0, > 999, or 0 when non-zero required
10 01 Area classification error Used the wrong area code (e.g., 0x00 vs 0x30 for CIO)
11 01 Read prohibited UM/protect bits set, or area is write-only
11 02 Write prohibited CPU is in PROGRAM mode and area is read-only then
22 01 CPU in PROGRAM mode and command not allowed Switch CPU to MONITOR or RUN before issuing writes
23 01 File device error EM file / memory card issue
26 01 Protection / password UM read protect is on; supply password via FINS

For a complete list, see W421-E1 §8 "Error Code Lists." The 2-byte end code appears at offsets 12–13 of the response frame.

11. Verification and Commissioning

  1. Layer-2 ping. From a Windows admin shell, run ping 10.0.0.100. The ETN21 should reply.
  2. Port probe. Use Test-NetConnection 10.0.0.100 -Port 9600 (PowerShell) to confirm TCP FINS is open.
  3. CX-Programmer online. Connect via Ethernet in CX-Programmer and read CIO 0.00 to confirm the rack and ETN21 are correctly configured.
  4. Minimal .NET test. Use the FinsUdpClient class above to read 5 words from DM 0. Expect a 0000 end code and 10 bytes of payload.
  5. Multi-area read smoke test. Combine CIO, HR, DM in a single 01 05 and verify each unpacked word matches the CX-Programmer value.
  6. Write / read-back. Write a known pattern (e.g., 0x1234) to DM 200, then read it back. This catches address/area-code mistakes that would otherwise silently corrupt data.
  7. Stress test. Poll 198 words at 100 ms for 10 minutes; verify no SID drops, no exception leaks, and no socket exhaustion.
  8. Fault injection. Disconnect the Ethernet cable; confirm reconnect logic restores communication without leaking sockets.

12. Common Pitfalls and Field Notes

  • Wrong GCT. Setting GCT=0 in a FINS header going PC → ETN21 → CPU yields 0x02-01 "Local node not in network." GCT counts the number of networks to traverse, not the number of nodes.
  • Confusing FINS node with IP last octet. A node number of 100 maps to a /24 address ending in .100 only when the network administrator has not manually remapped. Verify in ETN21 setup → FINS Node Number.
  • DM area code mismatch. Mixing 0x02 and 0x82 for the same D-word reads two different physical locations on some firmware revisions. Standardize on one and document it.
  • UDP receive race. A single UdpClient instance can interleave responses if multiple SIDs are in flight. Either serialize with a SemaphoreSlim or use TCP.
  • Endianness. FINS is little-endian for both address and data. Reading a D-word and showing it as the wrong value is almost always an endianness bug, not a PLC bug.
  • Time data is BCD. 07 01 returns time as packed BCD, not decimal. 0x21 = 21, not 33.
  • UM protection. Reading the program (UM) area without supplying the password returns 0x26-01. Most SCADA gateways should not need UM access; stay in CIO/HR/DM/AR/WR/EM.

13. Extending the Gateway

Once the CIO/HR/DM core is stable, layer in:

  • Tag-list import from CX-Programmer CSV exports or a JSON manifest. Validate every area code and address against a built-in table to catch typos at startup, not at 2 a.m.
  • OPC UA server on top of the poller. The Software Toolbox overview of Omron protocols highlights OPC UA bridges as the modern replacement for FINS-only SCADA.
  • Event-driven reads using MSG instructions inside the ladder. The PLC sends unsolicited FINS messages to a registered SA1; the .NET listener must maintain an open UDP/TCP socket and a SID table for matching.
  • Redundancy. Two ETN21 units on the same rack with different FINS networks and a watchdog in the gateway. Verify failover end-to-end by pulling an Ethernet cable.
  • Modern replacements. Newer Omron CPUs (NJ/NX) drop FINS-on-TCP in favor of EtherNet/IP (CIP) and OPC UA. If a refresh is on the roadmap, plan a parallel implementation against the NX-series FINS Function Manual W596 and the EtherNet/IP EDS for CIP-Forward-Open.
Industrial safety disclaimer. A SCADA gateway is not a safety controller. Never use FINS writes to drive a hard-wired E-Stop, safety relay, or any function governed by ISO 13849 / IEC 62061. Keep the safety chain on hardware that is rated and audited for that purpose.

14. Reference Summary

Document Number Coverage
FINS Commands Reference Manual W421-E1 All FINS command codes, request/response bodies, error codes
CS/CJ Ethernet Units Operation Manual W342 / W420 ETN21 setup, routing tables, FINS node / port configuration
Host Link Command Reference W227-E1 Serial C-mode protocol, FA header, FCS
NX-series CPU Unit FINS Function W596-E1 Modern FINS behavior on NJ/NX; useful for forward-compatibility
CS/CJ Series Programming Manual W394 / W474 Memory map, EM bank allocation, PLC Setup
ProFace CS/CJ Ethernet Driver notes n/a (PDF) Cross-vendor confirmation of port, FINS node, and routing assumptions

What is the default FINS port for an Omron ETN21?

Both TCP and UDP FINS use port 9600 on the ETN21. Some legacy bridges use 9600 plus an offset for parallel sessions, but a single TCP/UDP connection on 9600 is the modern default. Verify the value in CX-Programmer under ETN21 Setup → FINS/TCP Port.

How do I read DM D00100 from a CJ1M-CPU13 over Ethernet?

Send a FINS Memory Area Read (01 01) with area code 0x82 (legacy DM 0–19999), 3-byte little-endian address 64 00 00 (= 100), and item count 0A 00 for 10 words. The response carries the 10 words little-endian followed by end code 00 00. Use area code 0x02 for the D20000+ region.

Can I use one TCP connection for many concurrent reads?

Yes. FINS on a single TCP socket is half-duplex: issue one request, wait for the response, then issue the next. Serialize access with a SemaphoreSlim(1,1) or an async channel. For parallel polling, open up to 16 TCP connections to the ETN21 — that is the unit's documented maximum.

What causes FINS end code 0x05-01 "destination address error"?

The address you encoded is outside the area's valid range for the target CPU. Common offenders: writing DM 40000 on a CJ1M-CPU13 (max D32767), or reading EM bank 4 (0x20) when EM is not allocated in PLC Setup. Halve the address you are trying, then re-check the area map in W394.

Is FINS still recommended for new Omron projects?

For new designs on NJ/NX/CP-series CPUs, Omron recommends EtherNet/IP (CIP) and OPC UA. FINS remains the right choice for legacy CS/CJ installations like the CJ1M-CPU13 and for any gateway that must read existing programs without an OPC server. The W596 manual documents the modern FINS variants still supported on NX CPUs.

Back to blog