Resolving CX-Server Lite OCX Redistribution Issues with FINS UDP

James Nishida14 min read
Industrial NetworkingOmronTroubleshooting
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

Problem Overview: CX-Server Lite OCX Redistribution Failures

Custom Visual Basic (VB) applications that target Omron CS1/CJ1 series PLCs are typically built on top of the CX-Server Lite communications stack, exposed through ActiveX controls such as CX-Comms OCX, CX-Cmms OCX, and CX-Drive OCX. While these controls are documented in the CX-Server Lite User Manual and accelerate development on a single engineering workstation, they introduce a recurring deployment problem: the OCX runtime cannot be silently redistributed, and missing-dependency errors appear on target machines that never had CX-Server Lite installed.

Typical runtime failures observed when copy-deploying a compiled VB executable that references CX-Server Lite OCX controls:

  • 429 - ActiveX component can't create object at startup when the OCX is registered but license keys are absent.
  • Component 'CXCMMSO.OCX' or one of its dependencies not correctly registered when the OCX is missing or 32/64-bit mismatch occurs.
  • Class not licensed for use on systems where the OCX is present but the design-time license token (.LIC) cannot be located.
  • Silent failure of inline help files when the deployment language differs from the development language (a documented myOMRON Europe behaviour, see myOMRON KB #230).
Root cause. CX-Server Lite OCX controls are licensed per developer seat, not per runtime. A licensed install on the build machine embeds design-time license keys in the binary, and those keys are bound to the workstation. Pasting the OCX into a setup package without the corresponding vendor runtime (CX-Server Lite install media) does not satisfy COM activation. Each target PC requires a registered, licensed install - which is impractical for a free-distributed SCADA/HMI application.

CX-Server Lite Licensing Model and Distribution Reality

CX-Server Lite is offered as a single-seat developer licence. According to the Omron Europe software registration portal, the same site that lists CX-Compolet and CX-Position downloads (Software Registration & Downloads) also distributes CX-Server Lite v2.2 update packages. The licence terms cover:

  • One (1) developer installation per licence serial number.
  • Use of the OCX controls from a single engineering workstation.
  • No royalty-free runtime redistribution clause.

For multi-end-user deployment, the documented commercial paths are:

  1. Purchase a CX-Server Lite licence per end-user PC (cost-prohibitive at fleet scale).
  2. Upgrade to CX-Compolet ActiveX controls (SYSMAC Compolet), which ship as a separate component set and follow their own licence model.
  3. Replace the OCX layer entirely with direct FINS UDP communication from VB using Winsock - one licence, no runtime distribution dependency.

Option 3 is the only path that produces a single-instance-per-licence deployment model with no per-target runtime install, no OCX registration, and no design-time licence token to embed.

Why FINS UDP on Ethernet Is the Correct Replacement

CS1 and CJ1 PLCs equipped with an ETN21 Ethernet module, a built-in CS1W-ETN01/ETN11, CS1D-ETN21D, or any CJ1W-ETN21 variant, expose a UDP port for the Omron proprietary FINS (Factory Interface Network Service) protocol. By default this is UDP/9600, configurable in the Ethernet Unit's Routing Table.

FINS is a request/response protocol with deterministic completion codes. Because it sits directly on UDP, no OCX, no COM runtime, no third-party OPC server, and no IFR (Instruction Frame Routine) is needed - the application speaks FINS through a standard Windows Sockets control. The same VB6 Winsock control (MSWINSCK.OCX, freely redistributable with most VB6 runtimes) handles all transport work.

Key advantages over the OCX path:

Attribute CX-Server Lite OCX FINS UDP + Winsock
Runtime distribution Per-target install required None - bundled in EXE
Licence footprint Developer + runtime licence Developer licence only
OS dependency COM registration, MDAC Winsock 2 (all Win32+)
Network path CX-Server routing table Direct UDP to PLC IP
Latency overhead COM marshalling Single datagram
Troubleshooting Vendor trace tools Wireshark dissector (omron-fins)

FINS UDP Frame Architecture

A FINS/UDP datagram consists of two concatenated structures: the FINS/UDP header (10 bytes) and the FINS command frame (variable length). No payload-level integrity check is performed by FINS itself; reliability is delegated to the application's request/timeout/retries discipline.

FINS/UDP Datagram Layout (10 + N bytes) ICF RSV GCT DNA DA1 DA2 SNA SA1 SA2 SID MRC SRC Command Parameters (0..N bytes) FINS/UDP Header (10 bytes) FINS Command Frame (N bytes) ICF = 0x80 (command) / 0x81 (response) GCT = 0x02 (max gateways) DA1 = destination node (1..254) SA1 = source node (client) MRC = Main Request Code SRC = Sub-Request Code SID = Service ID (0x00..0xFF, unique per request) Transport: UDP/IP destination port 9600 (default for CS1 ETN modules)

FINS Header Field Definitions

Offset Field Width Typical Value Meaning
0 ICF 1 0x80 Information Control Field - command frame; bit 0 = response required
1 RSV 1 0x00 Reserved - always 0x00
2 GCT 1 0x02 Permitted gateway count (0..7); 2 supports a single router hop
3 DNA 1 0x00 Destination Network Address
4 DA1 1 0x01..0x7F Destination Node Number (CS1 default = 1)
5 DA2 1 0x00 Destination Unit Address (0 = CPU unit)
6 SNA 1 0x00 Source Network Address
7 SA1 1 client IP last octet Source Node - must be unique within the network
8 SA2 1 0x00 Source Unit Address
9 SID 1 0x00..0xFF Service ID - incremented for each new request

CS1 PLC Ethernet Unit Pre-Configuration

Before any VB client can talk FINS/UDP, the PLC side must be set up. Connect with CX-Programmer and configure the ETN module parameters:

  1. Set the Ethernet module's IP address, subnet mask, and default gateway (e.g. 192.168.250.1 / 255.255.255.0).
  2. In the TCP/IP tab, enable FINS/UDP port. Default is 9600; record the value if changed.
  3. In the FINS Network tab, assign a Network Number (e.g. 0) and the Ethernet module's Node Number (must match the FINS header's DA1 in the client request).
  4. In the CPU Bus Unit Settings, set Node Address Setting (NA!) to a value matching DA1.
  5. Add a FINS Routing Table entry: local network = 0, relay node = PC IP last octet, relay agent = CPU.
  6. Transfer the settings to the PLC and cycle power or restart the ETN unit (CX-Programmer can do this hot).
Critical: If the ETN module's IP and the Windows PC's IP are on different subnets without a gateway, FINS will silently time out because the routing table on the ETN side has no return path. Use ping to verify L3 reachability before debugging FINS itself.

VB6 Winsock Skeleton for FINS/UDP

Drop a Winsock control (MSWINSCK.OCX) onto the form as udpPLC, set Protocol = sckUDPProtocol, and bind to a local port (any free port > 1024). The minimal send/receive skeleton:

' --- Module-level constants
Private Const PLC_IP       As String = "192.168.250.1"
Private Const PLC_FINS_PORT As Long = 9600
Private Const LOCAL_NODE    As Byte = 100      ' must be unique on the network
Private Const PLC_NODE      As Byte = 1        ' DA1 value for CS1

' --- Build a 10-byte FINS/UDP header
Private Function BuildFinsHeader(ByVal sid As Byte) As Byte()
    Dim h(0 To 9) As Byte
    h(0) = &H80             ' ICF - command
    h(1) = &H00             ' RSV
    h(2) = &H02             ' GCT
    h(3) = &H00             ' DNA - destination network
    h(4) = PLC_NODE         ' DA1 - destination node
    h(5) = &H00             ' DA2 - destination unit (CPU)
    h(6) = &H00             ' SNA - source network
    h(7) = LOCAL_NODE       ' SA1 - source node
    h(8) = &H00             ' SA2 - source unit
    h(9) = sid              ' SID
    BuildFinsHeader = h
End Function

' --- Memory Area Read (MRC=0101, SRC=0101)
' Reads 'count' words starting at 'address' from 'areaCode'
Private Function FinsReadWords(ByVal areaCode As Byte, _
                               ByVal address As Long, _
                               ByVal count As Integer) As Byte()
    Dim sid As Byte: sid = (sidCounter + 1) And &HFF
    sidCounter = sid

    Dim pkt() As Byte
    ReDim pkt(0 To 17)        ' 10 header + 2 cmd + 4 params + 2 count

    Dim h() As Byte: h = BuildFinsHeader(sid)
    Dim i As Integer
    For i = 0 To 9: pkt(i) = h(i): Next i

    pkt(10) = &H01           ' MRC - memory area
    pkt(11) = &H01           ' SRC - read
    pkt(12) = areaCode       ' area
    pkt(13) = (address \ 256) And &HFF   ' address high
    pkt(14) = address And &HFF           ' address low
    pkt(15) = &H00           ' bit position = word access
    pkt(16) = (count ") And &HFF          ' items high
    pkt(17) = count And &HFF              ' items low

    FinsReadWords = pkt
End Function

Async Receive Handler

Private Sub udpPLC_DataArrival(ByVal bytesTotal As Long)
    Dim buf() As Byte
    udpPLC.GetData buf, vbArray + vbByte, bytesTotal

    ' Validate FINS echo: SID at offset 9 must match the last SID sent
    If buf(9) <> lastSid Then
        Debug.Print "SID mismatch - dropped frame"
        Exit Sub
    End If

    ' Completion code is the first two bytes of the FINS response body
    Dim mainCode As Byte: mainCode = buf(10)
    Dim subCode  As Byte: subCode  = buf(11)
    If mainCode <> &H0 Or subCode <> &H0 Then
        Debug.Print "FINS error "; Hex(mainCode); " "; Hex(subCode)
        RaiseEvent FinsError(mainCode, subCode)
        Exit Sub
    End If

    ' Parse word data starting at offset 12
    Dim wordCount As Integer: wordCount = (bytesTotal - 12) \ 2
    ReDim wordData(0 To wordCount - 1) As Integer
    Dim i As Integer
    For i = 0 To wordCount - 1
        wordData(i) = buf(12 + i * 2) * 256 + buf(13 + i * 2)
    Next i

    RaiseEvent FinsDataReady(wordData)
End Sub

FINS Area Codes and Common Commands for CS1

The CS1 supports a wide set of memory areas through FINS. Word-access codes (used when bit position = 0x00):

Code (hex) Area Symbol Range (words)
0x80 CIO Area CIO 0..6143
0x81 Work Area WR 0..511
0x82 Holding Area HR 0..511
0x83 Auxiliary Area AR 0..959 (read-only above 447)
0x84 Data Memory D 0..32767
0x90..0x9F Extended Data Memory E0..E15 0..32767 each bank
0x98 EM Current Bank E$ 0..32767

Bit-access codes (bit position 0x00..0x0F) use the same area base plus 0x80: e.g. 0xB0 = CIO bit, 0xB4 = D bit. 0xFF as bit position explicitly requests word access regardless of the area code.

Most-Used FINS Commands (CS1/CJ1)

MRC SRC Command Typical Use
0x01 0x01 Memory Area Read Read words/bits from any area
0x01 0x02 Memory Area Write Write words/bits
0x01 0x04 Memory Area Fill Bulk-fill DM/EM with a constant
0x03 0x04 Memory Area Transfer Server-side copy between areas
0x05 0x01 CPU Unit Information Read Read model code, version
0x06 0x01 CPU Unit Status Read Operating mode, fatal flag
0x21 0x01 Bit Set / Bit Reset Force bits (debug only)
0x23 0x01 Forced Set/Reset Cancel Release forced bits
0x26 0x01 Name Read Resolve symbolic tag to address
0x28 0x01 Message Read/Clear FINS message log

FINS Completion Codes (Error Mapping)

Every FINS response begins with a 2-byte completion code. The main code categorises the failure; the sub code gives the precise cause. Treat any non-zero pair as an exception in the client.

Main (hex) Category Sub Codes (hex) Likely Cause / Remedy
0x00 Normal completion 0x00 Success
0x01 Source / destination address error 0x03, 0x05, 0x06 Area code or address out of range; verify D range <= 32767
0x02 Command length error 0x01 FINS frame too short/long; recompute byte count
0x04 Address range error 0x01, 0x02 Start + count exceeds area end
0x05 Data length error 0x01, 0x02 Word count byte boundary; bit reads must specify bit position
0x10 PLC parity / checksum 0x01..0x05 Hardware issue; cycle power
0x20 CPU Unit error 0x02, 0x03, 0x04 PLC in PROGRAM/HALT mode or fatal error
0x40 Service aborted 0x01..0x04 Process interrupted; retry
0x50 Routing table error 0x01, 0x02, 0x03 ETN module has no route to destination network
0x60 Command format error 0x01, 0x02 Unknown MRC/SRC for CS1 firmware

Verification and Commissioning Procedure

After building the Winsock-based FINS client, run the following four-step verification on every deployment target before declaring the application production-ready:

  1. L3 reachability. ping 192.168.250.1 from the target PC. A successful ICMP echo confirms that any later FINS failure is a protocol/routing issue, not a network issue.
  2. L4 reachability. Open a command prompt and run netstat -an | findstr :9600 after the VB app has sent its first datagram, or use nc -u 192.168.250.1 9600 with Wireshark filtering on udp.port == 9600 to confirm packets leave the host.
  3. FINS round-trip. Send a CPU Unit Information Read (MRC=0x05, SRC=0x01). A non-zero-length reply with completion code 0x0000 confirms the entire stack - Winsock, UDP, ETN module routing table, CS1 CPU - is healthy.
  4. Application poll. Issue a Memory Area Read against a known D register (e.g. D0) and confirm the value matches what CX-Programmer online view shows. This catches swapped byte-order bugs (FINS is big-endian) and bit/word addressing mistakes.

Troubleshooting Matrix

Symptom Capture Point Likely Cause Corrective Action
No UDP traffic from PC Wireshark on switch SPAN port Windows Firewall blocking outbound 9600 Add inbound/outbound rule for UDP 9600; disable firewall temporarily to confirm
UDP sent, no response Wireshark shows outgoing only ETN routing table has no return entry; SNA/SA1 mismatch Set SA1 to PC IP last octet; verify FINS node address in CX-Programmer
Response received, completion 0x5002 FINS body decode Destination network unreachable Re-check ETN routing table network numbers match DNA field
Response received, completion 0x0103 FINS body decode Area code/address out of range Validate D address < 32768 and area code matches table above
Response received, completion 0x2002 FINS body decode PLC in PROGRAM mode Switch to MONITOR/RUN; verify CPU LED
Winsock error 10054 on remote reset VB error trap Previous ICMP destination unreachable Wrap send in retry/backoff; rebind local port
OCX 429 on legacy code path Application log License token missing Switch code path to FINS/Winsock - this article
Spanish-language install, blank help CX-Server Lite UI myOMRON KB #230 known issue Install English pack or upgrade to v2.2 from Omron registration portal

Edge Cases and Field-Proven Caveats

  • Multi-homed PC. If the Windows host has more than one active NIC, Winsock.LocalIP may bind to the wrong interface. Use udpPLC.Bind localPort, "192.168.250.100" explicitly to force the FINS network interface.
  • ETN21 firmware < v2.0. Older ETN21 modules reject FINS frames whose SID is 0x00 on retransmission. Always increment SID per request.
  • NAT in path. FINS UDP traverses NAT only if a static UDP mapping is configured. Carrier-grade NAT between PC and PLC will break the protocol because the SID/SA1 reflection becomes impossible to predict.
  • Polling rate. A single CS1 CPU can service roughly 50-100 FINS exchanges per second before ladder scan is impacted. Avoid polling loops > 50 ms period per tag; instead, use one multi-word FINS read for an entire region.
  • CX-Compolet interop. If a future project adopts CX-Compolet SYSMAC controls (listed on the Omron registration portal), the same FINS framing logic can stay as a fallback path; CX-Compolet can be configured to talk to a SYSMAC Gateway, allowing a graceful migration.
  • Firewalls. Windows Firewall on Windows 7+ blocks unsolicited inbound UDP by default, but FINS responses are allowed if the PC initiated the request. Still, explicitly allow udpPLC.LocalPort inbound.
  • Endianness. FINS commands always use big-endian for multi-byte integers. The byte-order swap in the receive handler above (buf(12 + i*2) * 256 + buf(13 + i*2)) converts to little-endian VB Integer.

Migrating an Existing OCX Application

For teams that already shipped a VB6 application referencing CX-Comms OCX, the migration to FINS/UDP does not require a rewrite. Wrap each OCX method call in an adapter class that translates ReadDevice into a FINS read request and back:

  1. Audit the current OCX call surface - typically ReadDevice(area, address, count), WriteDevice(...), Connect, Disconnect.
  2. Replace Connect with a single Winsock.Bind call.
  3. Implement a synchronous request/response state machine on a 500 ms timer with a configurable retry count (default 3).
  4. Map the OCX area enumerators (e.g. AR_DM, AR_CIO) to the FINS area codes in the table above.
  5. Keep the public method signatures of the adapter identical to the OCX so downstream forms do not change.

This preserves the application's UI layer and isolates the protocol change to a single class, dramatically reducing regression risk.

FAQ

Do I really need a CX-Server Lite licence per end-user PC to redistribute an OCX-based VB application?

Yes. The CX-Server Lite OCX controls are licensed per developer workstation and require a registered, licensed install on every runtime target. They cannot be silently redistributed as part of a setup package - copy-deploying the OCX yields "class not licensed" or "missing dependency" errors. The redistribution-free path is to talk FINS/UDP directly through Winsock.

What is the default UDP port for FINS on a CS1 ETN module?

9600. It is configurable in the Ethernet Unit's TCP/IP tab via CX-Programmer. If changed, both the client (Winsock RemotePort) and the PLC must be updated together.

Is the FINS/UDP frame big-endian or little-endian?

Big-endian (network byte order). All multi-byte fields - addresses, counts, and data values - must be byte-swapped to little-endian when interpreting responses in VB's native Integer/Long types.

How many FINS exchanges per second can a CS1 sustain?

Approximately 50-100 exchanges per second before ladder scan time is noticeably affected. Prefer a single multi-word read over many single-word reads, and use a polling period of at least 50 ms per tag.

What is the correct area code for DM (D) memory on CS1?

0x84 for word access. For bit access, use 0xB4 and set the bit position (0x00..0x0F). For EM banks, use 0x90 (E0) through 0x9F (E15), or 0x98 for the current EM bank selected in the CPU.

Can CX-Compolet replace CX-Server Lite for redistribution?

CX-Compolet (SYSMAC Compolet) is a separate ActiveX component set that follows its own licence model and is available from the Omron Europe software registration portal. It is the recommended upgrade path when an OCX-based architecture must be retained; for fully licence-free distribution, the direct FINS/UDP + Winsock approach remains the lowest-friction option.

Back to blog