Omron PLC Host Link C Serial Communication Protocol

James Nishida13 min read
OmronSerial CommunicationTechnical 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

Omron PLC Host Link Protocol: C Implementation Reference

This reference documents how to communicate with an Omron CP1, CJ1, CS1, or C-series PLC from a C program using the Host Link (SYSWAY) command-response protocol over a serial port. The material is written for automation engineers who need a vendor-neutral, non-Microsoft-specific communication path and who are evaluating the differences between Host Link over RS-232/422, FINS/TCP, EtherNet/IP, and tool-suite-based options such as CX-Programmer and the CX-One suite.

1. Protocol Overview and Positioning

Host Link is Omron's legacy command-response protocol that exchanges ASCII frames over an RS-232, RS-422, or RS-485 point-to-point link between a host computer and a single PLC. The PLC is configured for Host Link mode on its peripheral port, RS-232C port, or RS-422/485 port. The serial defaults are 9600 bit/s, 7 data bits, even parity, 2 stop bits (7E2), with unit number 00 by default.

Table 1 - Host Link vs. Modern Omron Protocols
Attribute Host Link (SYSWAY) FINS/TCP EtherNet/IP
Physical layer RS-232 / RS-422 / RS-485 Ethernet (TCP/UDP) Ethernet (TCP/UDP)
Framing ASCII text with FCS Binary FINS header + payload CIP encapsulation
Default port N/A (serial) 9600 44818 / 2222
PLC CPU load Low Moderate Higher
Native support on CP1L/CP1H Yes (option board on CP1L) Yes (CP1L-EM/CP1H with Ethernet option) Limited (CJ2/CJ1M-ETN)
Recommended use Legacy systems, simple poll loops, embedded hosts Modern SCADA bridges, CP1L-EM Logix-class integration, tag-based

For new applications, Omron recommends CX-Programmer for programming and CX-One for configuration. For non-Microsoft C hosts, the legacy Host Link protocol is often the most portable choice because it requires nothing more than a POSIX termios interface, a 7E2 UART, and a straight-through or null-modem cable.

2. Host Link Frame Structure

Every Host Link command and response begins with the '@' character (0x40) and ends with the terminator pair \r\n in many firmware revisions or simply \r in the CP1-series default. Between these delimiters, the host and the PLC exchange a fixed-position ASCII frame.

2.1 Command Frame (Host → PLC)

@ [Unit] [Header] [Text] [FCS] * [Terminator]
Table 2 - Host Link Command Frame Fields
Field Width Description
@ 1 Start character, 0x40
Unit 2 Unit number 00-31 (BCD ASCII), right-justified, leading zero
Header 2 Two-letter command, e.g. RR (read word), RW (write word), RL (read CIO bit)
Text n Address and optional data, ASCII hex
FCS 2 Frame Check Sequence, 2 hex characters
* 1 Delimiter, 0x2A
Terminator 1-2 CR (0x0D) on CP1, CR+LF (0x0D 0x0A) on legacy

2.2 Response Frame (PLC → Host)

For a read operation the PLC returns an end code in addition to the data. A successful read of one word looks like:

@ 01 RR 00 1A2B FCS * CR

For a write operation the PLC returns only the echoed command header and the end code:

@ 01 RW 00 FCS * CR

End code 00 indicates normal completion. Any other value is an error; the most common values are listed in the table below.

Table 3 - Selected Host Link End (Completion) Codes
Code (hex) Meaning
00 Normal completion
01 Not executable in RUN mode
02 Not executable in MONITOR mode
03 Not executable with PROM mounted
04 Address out of range
0B Not executable in PROGRAM mode
0C Not executable with I/O table error
0D FCS error (frame corruption)
0E Format error (length, content)
0F Entry number data error
10 Command not supported
11 Not executable due to CPU unit error
12 Not executable due to cycle time overrun

3. Frame Check Sequence (FCS) Calculation

The FCS is the 8-bit XOR of every character between @ and the last text character, written as two uppercase hex characters. The reference implementation in the source listing computes it with a forward loop:

void get_fcs(const char *TT, char fcs_out[3]) {
    unsigned char c = 0;
    for (size_t i = 0; TT[i] != '\0'; i++) {
        c ^= (unsigned char)TT[i];
    }
    sprintf(fcs_out, "%02X", c);
}
Critical: The FCS is computed over the string from the leading '@' up to and including the last text character. The '*' and terminator are NOT included in the XOR. Many first-time implementations mistakenly include the terminator and end up with a frame the PLC silently discards with end code 0D.

For the read command @01RR10000001 the XOR is computed as follows:

0x40 ^ 0x30 ^ 0x31 ^ 0x52 ^ 0x52 ^ 0x31 ^ 0x30 ^ 0x30 ^ 0x30 ^ 0x30 ^ 0x30 ^ 0x31 = 0x42
FCS = "42"

The complete transmitted frame is therefore @01RR1000000142*\r.

4. C Reference Implementation

The legacy C source uses a state machine with four steps: read, read-completion, write, write-completion. A state machine is the recommended architecture for Host Link clients because the protocol is half-duplex and the response window is non-deterministic on slower PLCs (CPM1, SRM1). The complete portable translation is shown below.

4.1 Serial Port Open (POSIX / Linux / Embedded)

#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

int hostlink_open(const char *dev, int baud) {
    int fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (fd < 0) return -1;

    struct termios tio;
    memset(&tio, 0, sizeof tio);
    cfmakeraw(&tio);  // disable canonical mode, echo, etc.
    tio.c_cflag |= (CLOCAL | CREAD);
    tio.c_cflag &= ~PARENB;            // clear then set even parity
    tio.c_cflag |= PARENB;
    tio.c_cflag &= ~CSTOPB;            // 2 stop bits
    tio.c_cflag |= CSTOPB;
    tio.c_cflag &= ~CSIZE;
    tio.c_cflag |= CS7;                // 7 data bits
    tio.c_cc[VMIN]  = 0;
    tio.c_cc[VTIME] = 1;               // 100 ms read timeout

    speed_t sp = B9600;
    switch (baud) {
        case 1200:  sp = B1200;  break;
        case 2400:  sp = B2400;  break;
        case 4800:  sp = B4800;  break;
        case 9600:  sp = B9600;  break;
        case 19200: sp = B19200; break;
        case 38400: sp = B38400; break;
        case 57600: sp = B57600; break;
        case 115200: sp = B115200; break;
    }
    cfsetispeed(&tio, sp);
    cfsetospeed(&tio, sp);

    if (tcsetattr(fd, TCSANOW, &tio) < 0) { close(fd); return -1; }
    tcflush(fd, TCIFLUSH);
    return fd;
}

4.2 Frame Builders for RR and RW

int hostlink_read_words(int fd, int unit, int area, int word_addr,
                        int count, uint16_t *out) {
    char head[64], body[80], fcs[3];
    /* area: 0=DM, 1=CIO, 2=HR, 3=AR, 4=LR, 5=WR, 6=DM (force-set) */
    static const char *area_pfx = "DCWARL";
    snprintf(head, sizeof head, "@%02dRR%c%04X%04X",
             unit, area_pfx[area], word_addr, count);
    get_fcs(head, fcs);
    snprintf(body, sizeof body, "%s%s*\r", head, fcs);

    if (write(fd, body, strlen(body)) < 0) return -1;

    char resp[256];
    int n = read_response(fd, resp, sizeof resp, 1000);
    if (n < 11) return -2;            // short frame
    if (strncmp(resp, head, 4) != 0)  return -3;  // echo header mismatch
    int end = (resp[5] - '0') << 4 | (resp[6] - '0');
    if (end != 0) return end;

    for (int i = 0; i < count; i++) {
        char tmp[5] = { resp[7 + 4*i], resp[8 + 4*i],
                        resp[9 + 4*i], resp[10 + 4*i], 0 };
        out[i] = (uint16_t)strtoul(tmp, NULL, 16);
    }
    return 0;
}

4.3 Read Response Helper

Read with a deadline instead of a fixed character count. The PLC's Host Link turnaround is typically 5-30 ms on CP1L and up to 100 ms on C200H with large DM reads.

#include <sys/time.h>

int read_response(int fd, char *buf, int bufsz, int timeout_ms) {
    int total = 0;
    struct timeval start, now;
    gettimeofday(&start, NULL);

    while (total < bufsz - 1) {
        gettimeofday(&now, NULL);
        long elapsed = (now.tv_sec - start.tv_sec) * 1000L
                     + (now.tv_usec - start.tv_usec) / 1000L;
        if (elapsed >= timeout_ms) break;

        int n = read(fd, buf + total, 1);
        if (n < 0) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                usleep(2000);
                continue;
            }
            return -1;
        }
        if (n == 0) { usleep(2000); continue; }
        total += n;
        if (buf[total - 1] == '\r') break;
    }
    buf[total] = '\0';
    return total;
}

5. State Machine Polling Loop

The four-step state machine in the source code is a clean, deterministic pattern that scales from a DOS controller to a Linux user-space daemon. The recommended time constants are:

Table 4 - Polling Loop Time Constants
Parameter Legacy DOS value Recommended modern value
Read scan period 55 ms 50-100 ms
Display scan period 110 ms 100-250 ms
Per-cycle timeout (read step) 20 ticks (1.1 s) 1.0-2.0 s
Inter-cycle pause None 0-20 ms to avoid flooding

The increment used in the source for the timeout counter is TimeOut++ inside the same scan tick. A safer pattern increments a monotonic timestamp and compares to a deadline; this avoids drift if the scan itself is delayed.

6. Cross-Platform Porting Notes

6.1 Windows (Win32 / Visual C)

Replace the termios block with CreateFile, GetCommState, SetCommState, SetCommTimeouts, and overlap I/O or ReadFile/WriteFile. The serial parameters must be set as fParity = TRUE, Parity = EVENPARITY, ByteSize = 7, StopBits = TWOSTOPBITS to match the Host Link default.

6.2 Embedded / RTOS

On STM32, NXP i.MX RT, or ESP32 (using the hardware UART), drive the Host Link state machine from a 1 ms SysTick. Drive a circular DMA buffer of at least 32 bytes for receive, and a state variable that tracks the FCS computation and the response deadline.

6.3 Linux systemd Service

Wrap the state machine in a daemon with the following unit template:

[Unit]
Description=Omron Host Link Gateway
After=network.target

[Service]
ExecStart=/usr/local/bin/hostlinkd -d /dev/ttyUSB0 -u 1 -a 0
Restart=on-failure
RestartSec=3

[Install]
WantedBy=multi-user.target

Pin the USB-serial converter with a udev rule so that re-enumeration does not rename the device:

SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SYMLINK+="omron0"

7. Wiring and Serial Parameters on the PLC Side

The PLC peripheral or RS-232C port must be set to Host Link mode. On CP1L this is configured from the CX-Programmer PLC Settings or from the CX-One configuration tool. The relevant PLC Setup bits are:

Table 5 - PLC Setup Bits Relevant to Host Link
Word Bit Setting
DM 6650 (port 1) 0-3 4 (Host Link) or 5 (Host Link, 1:N)
DM 6650 4-7 0 (9600), 1 (300), 2 (600), 3 (1200), 4 (2400), 5 (4800), 6 (9600), 7 (19200)
DM 6650 8-11 0 (Even, 7, 2), 2 (None, 8, 1), 3 (Even, 7, 1)
DM 6650 12-15 Unit number 0-31

Confirm with a small terminal program (minicom, PuTTY) that the PLC echoes @00RR00010001F7*\r in response to a manual read of DM 0001, where F7 is the correct FCS for the test command string. This is the single most useful first-line diagnostic.

8. Modern Alternatives to Direct C Serial

For new projects where portability, encryption, or higher data rates are required, three official paths are available:

  1. FINS/TCP over Ethernet - Reuses the same C-series memory model and command codes but transports them over TCP/9600. CP1L-EM and CP1H-EMU provide this natively. Sample code and reference documentation ship with CX-Programmer.
  2. EtherNet/IP (CIP) - Tag-based access on CJ2, NJ, and NX controllers; configured with the Sysmac Studio / CX-One integration.
  3. CX-One Suite - The CX-One automation suite bundles CX-Programmer, CX-Server OPC, and switch utilities. For SCADA or OPC clients this is the lowest-effort path.

For .NET / C# shops, the NF-Software-Inc/PLC-Omron-Standard library implements read and write primitives against Omron memory areas and is suitable for bridging to a Linux/.NET host. For a non-Microsoft C path, the Host Link reference in this document remains the most portable.

The Omron Sample Code Library contains C, C#, and Structured Text examples for CP, CJ, and NX families; this is the first stop for engineers starting new code.

9. Verification Procedure

After implementing the Host Link client, validate with the following checklist:

  1. Open the serial port at 9600 7E2; verify tcsetattr returns 0 and cfmakeraw disabled canonical mode.
  2. Send the canned read @00RR00010001F7*\r and confirm the PLC returns @00RR00[DATA]??*\r with end code 00. Use a logic analyzer on TX/RX to capture both directions if there is no response.
  3. Verify the FCS by computing it on a known string and comparing the on-wire bytes.
  4. Force a deliberate bad FCS and confirm the PLC returns end code 0D.
  5. Switch the PLC to PROGRAM mode and confirm a write attempt returns end code 0B.
  6. Run a 24-hour soak test at the planned poll rate; verify that TimeOutErr counter remains zero in the DOS/console output.

10. Troubleshooting Matrix

Table 6 - Common Host Link Faults and Remedies
Symptom Likely Cause Remedy
No response, port times out Cable not null-modem; PLC port in peripheral (toolbus) mode, not Host Link Use a null-modem cable; set DM 6650 = 0400 hex (Host Link, 9600 7E2)
End code 0D (FCS error) FCS computed over wrong range, including terminator Compute FCS only up to and including the last text character
End code 0E (format) Wrong command width (e.g. RL for bit but trying to read 16 bits) Match command code to data type; use RR/RW for words, RL/WL for bits
End code 04 (address out of range) Word address 0000-FFFF is invalid for the chosen area Verify area letter and address limits; e.g. CIO is 0-6143
Intermittent failures on long cable runs RS-422 not used, RS-232 ground differential Switch to RS-422 with proper termination (120 ohm)
Linux open() returns ENOENT USB-serial adapter renumbered after replug Pin the device with a udev SYMLINK rule
Garbled text in response Bit rate, parity, or stop bits mismatch Re-check termios settings; CP1 default is 7E2
Bytes received but FCS error Start character '@' missing or echoed as 0x40 followed by junk Disable any serial echo on the terminal emulator; flush RX before sending

11. Performance and Safety Considerations

  • Each Host Link round-trip is roughly 30-50 ms at 9600 bit/s, so 16 reads per second is a realistic upper bound before queueing.
  • Forced-set / forced-reset commands (KS, KR) should be used with care; they change the PLC I/O state without program logic.
  • The Host Link protocol does not authenticate the host; on a multi-drop RS-422 link with unit number 0, any device on the bus can issue commands. Lock down the unit number to a non-default value and avoid using unit 0 in production.
  • For functional safety, the Host Link channel is treated as a non-interlocking communication path; it must not be the sole path for an E-stop signal.

What serial parameters does an Omron PLC expect for Host Link?

Default Host Link parameters are 9600 bit/s, 7 data bits, even parity, and 2 stop bits (7E2). The unit number is set in PLC Setup (DM 6650 on CP1L) and defaults to 00. Any mismatch in parity or stop bits is the most common cause of garbled responses and FCS errors.

How is the Host Link FCS calculated in C?

Compute the 8-bit XOR of every byte from the leading '@' through the last text character, then format the result as two uppercase hex characters. The '*' delimiter and the CR/LF terminator are not included. Endianness does not matter because the checksum is bytewise.

Why does my C Host Link client time out on a CP1L?

The most frequent causes are: (1) the PLC serial port is in peripheral (CX-Programmer toolbus) mode rather than Host Link mode, (2) the wrong area letter was used (D for DM, C for CIO, H for HR, A for AR, L for LR, W for WR), or (3) the unit number in the command does not match the PLC Setup. Connect a PC terminal first and manually send @00RR00010001F7*\r to isolate the port configuration.

Can I use Host Link from a non-Windows host without Kepware?

Yes. Host Link is a plain ASCII protocol on a 7E2 UART, and a POSIX termios-based C client runs unmodified on Linux, embedded RTOS, and most industrial single-board computers. For higher-level languages, the NF-Software-Inc/PLC-Omron-Standard .NET library is one option, but for plain C the implementation in this reference is the most portable path.

What is the difference between Host Link and FINS/TCP?

Host Link is an ASCII command-response protocol that runs over serial or TCP, intended for direct word/bit access. FINS/TCP wraps the same memory model in a binary header transported over TCP/9600 and is the recommended path for modern Ethernet-equipped CP1L-EM and CJ2 systems because it is faster and supports 1:N host connections.

Back to blog