When you read an attribute from a CIP device, the bytes you get back have a type. When you write a value, you must encode it in the right type or the device returns an error. This page is the complete reference for every CIP data type defined by ODVA’s CIP Networks Library Volume 1, Appendix C.
CIP’s type system is aligned with IEC 61131-3 (the international standard for PLC programming languages), with a handful of CIP-specific extensions for paths, engineering units, and internationalized strings. The same types work across every CIP network — EtherNet/IP, DeviceNet, ControlNet, and CompoNet — because data typing is part of the CIP common specification, not the network adaptations.
This article completes the reference-page trio alongside our CIP General Status Codes Reference and CIP Service Codes Reference. For broader CIP context, see the CIP Protocol Complete Guide.
Table of Contents
Quick jump — the types you’ll actually use
- BOOL, SINT, INT, DINT, LINT — signed integers
- USINT, UINT, UDINT, ULINT — unsigned integers
- REAL and LREAL — IEEE 754 floats
- STRING, SHORT_STRING, STRING2, STRINGN, STRINGI — character strings
- BYTE, WORD, DWORD, LWORD — bit strings
- DATE, TIME_OF_DAY, DATE_AND_TIME, TIME — time and date
- EPATH — encoded CIP path
- ENGUNIT — engineering units identifier
- STRUCT, ARRAY — composite types
What CIP data types are
A CIP attribute is just a sequence of bytes on the wire. The data type tells you how to interpret those bytes — whether they are a 16-bit signed integer, a 32-bit float, a string, or something else. Every attribute defined by an object class specifies its data type in the spec.
The data type system has three layers:
- Elementary types — primitive types like INT, REAL, BOOL. These map directly to memory.
- String types — character strings with various encodings and length conventions.
- Derived types — composites built from elementary types: STRUCT, ARRAY, and combinations.
CIP also defines a set of type codes — single-byte identifiers used in EDS files and CIP-encoded data structures to specify which type a value uses. These codes start at 0xC1 (BOOL) and run through the elementary type range.
For full alignment with IEC 61131-3, see Section C-3 of Volume 1 Appendix C. CIP supports almost the entire IEC 61131-3 data type set with several extensions added (SHORT_STRING, STRING2, STRINGN, STRINGI, EPATH, ENGUNIT, FTIME, LTIME, ITIME).
Elementary integer types
The integer types are the workhorses of CIP. They cover every signed and unsigned width from 8 to 64 bits.
| Type | Type Code | Size | Range | IEC 61131-3 equivalent |
|---|---|---|---|---|
| BOOL | 0xC1 | 1 byte (1 bit value) | 0 (FALSE) or 1 (TRUE) | BOOL |
| SINT | 0xC2 | 1 byte | -128 to 127 | SINT |
| INT | 0xC3 | 2 bytes | -32,768 to 32,767 | INT |
| DINT | 0xC4 | 4 bytes | -2,147,483,648 to 2,147,483,647 | DINT |
| LINT | 0xC5 | 8 bytes | -2^63 to 2^63-1 | LINT |
| USINT | 0xC6 | 1 byte | 0 to 255 | USINT |
| UINT | 0xC7 | 2 bytes | 0 to 65,535 | UINT |
| UDINT | 0xC8 | 4 bytes | 0 to 4,294,967,295 | UDINT |
| ULINT | 0xC9 | 8 bytes | 0 to 2^64-1 | ULINT |
Endianness
All elementary types are transmitted in little-endian byte order on the wire. The least significant byte comes first.
Example: the UINT value 0x1234 (4660 decimal) is transmitted as 34 12 on the wire. The DINT value 0x12345678 is transmitted as 78 56 34 12.
This is the same convention used by the EtherNet/IP encapsulation header and matches Intel x86 architecture. It is the opposite of standard “network byte order” used by TCP/IP, which is one of the reasons custom CIP parsers occasionally fail when developers default to big-endian. For more on this gotcha, see our EtherNet/IP Encapsulation Protocol Explained article.
BOOL — the special case
BOOL is logically 1 bit but transmitted as 1 byte. Value 0 = FALSE, any non-zero = TRUE. The spec says only 0 and 1 are defined values, but devices generally accept any non-zero byte as TRUE on read.
When BOOL values appear inside structures, they often pack into byte arrays — 8 BOOLs per byte. The Discrete Input/Output Point Objects use this packing for their assemblies. The bit numbering within a byte is little-endian: bit 0 is the least significant.
Floating-point types
CIP uses standard IEEE 754 floats — same as nearly every modern computer language.
| Type | Type Code | Size | Format | IEC 61131-3 equivalent |
|---|---|---|---|---|
| REAL | 0xCA | 4 bytes | IEEE 754 single-precision (32-bit) | REAL |
| LREAL | 0xCB | 8 bytes | IEEE 754 double-precision (64-bit) | LREAL |
REAL gives roughly 7 decimal digits of precision. LREAL gives roughly 15 decimal digits.
Both are transmitted little-endian. So a REAL value of 1.0 (IEEE 754 hex 0x3F800000) is transmitted as 00 00 80 3F on the wire.
REAL is by far the more common in field devices. LREAL appears in higher-precision motion controllers and analytical instruments where the extra precision matters.
Bit string types
Bit string types treat a value as a fixed-width collection of bits, with no integer interpretation. They are used for status registers, flag words, and bitmask attributes.
| Type | Type Code | Size | Use |
|---|---|---|---|
| BYTE | 0xD1 | 1 byte | 8-bit status flags |
| WORD | 0xD2 | 2 bytes | 16-bit status flags |
| DWORD | 0xD3 | 4 bytes | 32-bit status flags |
| LWORD | 0xD4 | 8 bytes | 64-bit status flags |
The bits within these types are accessed by position: bit 0 is the least significant bit, bit N-1 is the most significant. Many CIP status attributes are WORDs where each bit means something specific (e.g., the Identity Object’s Status attribute uses a WORD with bits for “Owned,” “Configured,” “Major Recoverable Fault,” “Major Unrecoverable Fault,” etc.).
Like all elementary types, bit strings are transmitted little-endian.
String types
CIP defines several string types because different applications need different character widths and length conventions. This is one of the areas where CIP extends IEC 61131-3.
| Type | Type Code | Length prefix | Character width | Encoding |
|---|---|---|---|---|
| STRING | 0xD0 | UINT (2 bytes) | 1 byte | ISO-8859-1 |
| STRING2 | 0xD5 | UINT (2 bytes) | 2 bytes | ISO 10646 (Unicode) |
| STRINGN | 0xD9 | UINT + UINT (4 bytes) | N bytes | ISO 10646 |
| SHORT_STRING | 0xDA | USINT (1 byte) | 1 byte | ISO-8859-1 |
| STRINGI | 0xDE | USINT (count) | Variable per language | Multilingual structure |
STRING
A UINT length prefix followed by that many bytes of ISO-8859-1 (Latin-1) characters. The most common general-purpose string type. Length can be 0 to 65,535 characters.
Example: the string “Hello” encoded as STRING is:

(0x0005 in little-endian = 5 characters)
SHORT_STRING
Same idea but with a 1-byte (USINT) length prefix. Maximum length 255 characters. Common in DeviceNet devices where every byte matters.
Example: the string “OK” encoded as SHORT_STRING is:

The Identity Object’s Product Name attribute (Class 0x01, Instance 1, Attribute 7) is a SHORT_STRING. Every CIP device’s name comes through this type.
STRING2
A UINT length prefix followed by that many 16-bit characters in ISO 10646 (basic Unicode plane). Each character takes 2 bytes, little-endian. Length prefix counts characters, not bytes.
Example: “Hi” as STRING2 takes 6 bytes (2-byte length + 2 × 2-byte characters):
02 00 48 00 69 00
STRINGN
For arbitrary character widths. Includes both a character count (UINT) and a character size (UINT, in bytes). Almost never seen in field devices but allowed by the spec for specialized internationalization needs.
STRINGI — the internationalized string
STRINGI is the most complex string type. It represents a string with multiple language variants. The structure:
- A USINT count of how many language variants follow
- For each variant:
- 3-byte ISO 639-2/T language code (e.g., “eng”, “fra”, “deu”)
- 1-byte EPATH segment (limited to 0xD0, 0xD5, 0xD9, or 0xDA — meaning STRING, STRING2, STRINGN, or SHORT_STRING)
- UINT character set identifier (IANA MIB Printer Codes — 4 = ISO-8859-1, 1000 = ISO-10646-UCS-2, etc.)
- The actual string in the indicated type
Common ISO 639-2/T language codes engineers see:
| Language | Code |
|---|---|
| English | eng |
| French | fra |
| Spanish | spa |
| Italian | ita |
| German | deu |
| Japanese | jpn |
| Portuguese | por |
| Chinese | zho |
| Russian | rus |
STRINGI is used by devices that need to expose multilingual product information. A drive sold worldwide might have its parameter descriptions in 8 languages, each stored as one variant within a STRINGI attribute.
Time and date types
CIP defines several time types with different resolutions and ranges.
| Type | Type Code | Size | Resolution | Range |
|---|---|---|---|---|
| TIME | 0xDB | 4 bytes | 1 millisecond | ±24 days |
| FTIME | 0xD6 | 4 bytes | 1 microsecond | ±35 minutes |
| LTIME | 0xD7 | 8 bytes | 1 microsecond | ±106,751,991 days |
| ITIME | 0xD8 | 2 bytes | 1 millisecond | ±32.768 seconds |
| DATE | 0xCD | 2 bytes | 1 day | 1972-01-01 to 2151-06-06 |
| TIME_OF_DAY | 0xCE | 4 bytes | 1 millisecond | 00:00:00.000 to 23:59:59.999 |
| DATE_AND_TIME | 0xCF | 8 bytes | 1 millisecond | DT#1972-01-01-00:00:00.000 to DT#2151-06-06-23:59:59.999 |
TIME and FTIME
TIME is a 32-bit signed value representing duration in milliseconds. Range is roughly ±24 days. Common for timeouts, watchdogs, and elapsed times.
FTIME is also 32-bit signed but in microseconds, so the range is only ±35 minutes. Used where sub-millisecond resolution matters but durations stay short — handshake timing, motion command intervals.
DATE
DATE counts days since 1972-01-01 (the start of the UTC era) as a 16-bit value. With 65,536 possible values, the range extends to 2151-06-06. Not as long-lived as a 32-bit Unix timestamp, but enough for most industrial applications.
TIME_OF_DAY
TIME_OF_DAY is a 32-bit value representing milliseconds since midnight. Range 0 to 86,399,999 covers 00:00:00.000 to 23:59:59.999.
DATE_AND_TIME
A 64-bit composite that combines DATE and TIME_OF_DAY. Lower 32 bits are TIME_OF_DAY (milliseconds since midnight). Upper 32 bits are DATE (days since 1972-01-01, zero-padded). Common in event logs and audit trails.
CIP-specific data types
These types are CIP extensions to IEC 61131-3 — they don’t exist outside the CIP world.
EPATH
Type code: 0xDC
EPATH is the encoded path type. It represents a CIP path (Class, Instance, Attribute, or routing path) in compact binary form. Almost every CIP message contains at least one EPATH.
An EPATH is a sequence of segments, each of which is one or more bytes describing one element of the path. Common segment types:
- Logical Segment (0x20-0x3F) — Class ID, Instance ID, Attribute ID, Connection Point ID, Member ID
- Port Segment (0x00-0x0F) — Routing through a network port to another network
- Symbolic Segment (0x60-0x6F) — Tag name reference (used heavily by Logix systems)
- Network Segment (0x40-0x5F) — Network-specific parameters
- Data Segment (0x80-0x9F) — Embedded data (used in Forward_Open for the connection’s application path)
Example: the EPATH for “Class 0x01, Instance 1, Attribute 7” encodes as:

Each segment is 2 bytes. 20 is the Class segment type (8-bit logical class), followed by the class ID. 24 is the Instance segment type, followed by the instance ID. 30 is the Attribute segment type, followed by the attribute ID.
For values above 255, 16-bit logical segment types (0x21, 0x25, 0x31) take 4 bytes per segment.
EPATH is what makes CIP routing possible. The path tells the receiver exactly where to send the message, including across network boundaries.
ENGUNIT
Type code: 0xDD
ENGUNIT is a 16-bit identifier (UINT) that references a specific engineering unit defined in Volume 1 Appendix D. It lets attributes self-document their physical units.
Examples (from Volume 1 Appendix D):
| ENGUNIT value | Unit |
|---|---|
| 0x0000 | (no units / dimensionless) |
| 0x1010 | meter |
| 0x101C | kilometer |
| 0x103E | foot |
| 0x1056 | meter per second |
| 0x2008 | volt |
| 0x2018 | ampere |
| 0x4080 | kelvin |
| 0x4081 | degree Celsius |
| 0x40C0 | newton |
| 0x40C8 | pascal |
| 0x4100 | hertz |
| 0x4810 | second |
| 0x4828 | hour |
When a configuration tool reads an attribute marked with an ENGUNIT companion attribute, it can display the value with the right units. A device exposing a temperature reading at attribute 5 might expose its ENGUNIT (0x4081 = degree Celsius) at attribute 6. The tool reads both and shows “23.5 °C” automatically.
Many devices skip ENGUNIT and just document units in their manual. When ENGUNIT is present, it makes the device self-describing.
Structured types
CIP supports composite types built from elementary and string types.
| Type | Type Code | Description |
|---|---|---|
| STRUCT | 0xA2 | A composite of multiple named members of different types |
| ABBREV_STRUCT | 0xA0 | Compact struct without member type tags (saves bytes when the structure is known) |
| ARRAY | 0xA3 | Fixed-size sequence of one type |
| ABBREV_ARRAY | 0xA1 | Compact array without element type tags |
STRUCT
A STRUCT has a type code (0xA2), then a definition listing each member’s type code and data. Used for attributes that bundle related fields.
Example: the Identity Object’s Revision attribute is a STRUCT of two USINTs (Major and Minor):

When the structure is known in advance (the spec defines it), ABBREV_STRUCT (0xA0) can be used to save the per-member type code bytes.
ARRAY
An ARRAY is a fixed-size sequence of one type. The type code (0xA3) is followed by the element count and the elements themselves.
Used for things like “list of registered protocols” or “device serial numbers of bridged devices.” Most field devices avoid ARRAY in favor of multiple instances of the same object class (which is generally cleaner), but it appears in network and routing objects.
User-Defined Data Types in Logix
Rockwell ControlLogix and CompactLogix expose tags as STRUCTs with named members. When an external client reads a structured tag through CIP, the data comes back as a CIP STRUCT. The Logix Symbol Object handles the mapping between the Logix tag’s user-defined structure and the wire-format STRUCT.
This is what makes the Logix tag-based addressing experience feel seamless even though CIP underneath is reading bytes.
Complete data type code table
The full list of CIP elementary and derived type codes, sorted by code:
| Code | Type | Size |
|---|---|---|
| 0xA0 | ABBREV_STRUCT | Variable |
| 0xA1 | ABBREV_ARRAY | Variable |
| 0xA2 | STRUCT | Variable |
| 0xA3 | ARRAY | Variable |
| 0xC1 | BOOL | 1 byte |
| 0xC2 | SINT | 1 byte |
| 0xC3 | INT | 2 bytes |
| 0xC4 | DINT | 4 bytes |
| 0xC5 | LINT | 8 bytes |
| 0xC6 | USINT | 1 byte |
| 0xC7 | UINT | 2 bytes |
| 0xC8 | UDINT | 4 bytes |
| 0xC9 | ULINT | 8 bytes |
| 0xCA | REAL | 4 bytes |
| 0xCB | LREAL | 8 bytes |
| 0xCC | STIME | 4 bytes (synchronous time) |
| 0xCD | DATE | 2 bytes |
| 0xCE | TIME_OF_DAY | 4 bytes |
| 0xCF | DATE_AND_TIME | 8 bytes |
| 0xD0 | STRING | Variable (UINT length) |
| 0xD1 | BYTE | 1 byte |
| 0xD2 | WORD | 2 bytes |
| 0xD3 | DWORD | 4 bytes |
| 0xD4 | LWORD | 8 bytes |
| 0xD5 | STRING2 | Variable (UINT length, 2 bytes/char) |
| 0xD6 | FTIME | 4 bytes |
| 0xD7 | LTIME | 8 bytes |
| 0xD8 | ITIME | 2 bytes |
| 0xD9 | STRINGN | Variable |
| 0xDA | SHORT_STRING | Variable (USINT length) |
| 0xDB | TIME | 4 bytes |
| 0xDC | EPATH | Variable |
| 0xDD | ENGUNIT | 2 bytes |
| 0xDE | STRINGI | Variable (multi-language) |
These codes are what you see in EDS files (parameter type fields) and in any wire-level encoding that includes type tags.
Mapping CIP types to common languages
A reference table for developers writing CIP code:
| CIP type | C / C++ | C# / .NET | Python (struct module) | Java |
|---|---|---|---|---|
| BOOL | bool / uint8_t | bool (1 byte) | ? or b | boolean (8-bit on wire) |
| SINT | int8_t | sbyte | b | byte |
| INT | int16_t | short | <h | short |
| DINT | int32_t | int | <i | int |
| LINT | int64_t | long | <q | long |
| USINT | uint8_t | byte | B | byte (unsigned via mask) |
| UINT | uint16_t | ushort | <H | short (unsigned via mask) |
| UDINT | uint32_t | uint | <I | int (unsigned via mask) |
| ULINT | uint64_t | ulong | <Q | long (unsigned via mask) |
| REAL | float | float | <f | float |
| LREAL | double | double | <d | double |
The < in Python’s struct format strings forces little-endian (CIP order). Without it, Python uses native byte order, which causes silent bugs on big-endian platforms.
Common gotchas
Endianness mismatch
The most common bug in custom CIP code. CIP is little-endian. TCP/IP, IP, and Ethernet headers are big-endian. Custom parsers that default to network byte order (big-endian) read CIP values wrong without obvious errors — just nonsense data.
STRING vs SHORT_STRING confusion
These look almost identical but have different length prefix sizes (UINT for STRING, USINT for SHORT_STRING). Reading a SHORT_STRING as a STRING gives you the first byte of the string mixed with the count. Some Identity Object attributes are SHORT_STRING; STRING is more common in general attributes.
BOOL in arrays
A single BOOL is 1 byte. But many BOOLs packed in an array of BOOLs (or in the bits of a BYTE/WORD/DWORD) use 1 bit each. The Discrete Input Object’s input data is a packed BOOL array — 8 inputs per byte. Misreading this as 8 separate bytes gives you wrong values.
Floating-point conversion
REAL is IEEE 754, but custom code that types float x = *(float*)bytes; only works on little-endian platforms with IEEE 754 native. On other platforms, swap bytes first then use memcpy to avoid alignment issues.
Time type ranges
TIME is in milliseconds with a 32-bit signed value — range ±24 days. A device using TIME for elapsed time wraps around 24 days. For longer durations, use LTIME (microseconds in a 64-bit value) or just store seconds in a DINT.
Sign extension
When reading a SINT or INT into a larger type, sign-extend it. A SINT value of -1 is byte 0xFF — read into a UDINT it becomes 255, not -1. Use proper signed-to-larger-signed conversion.
Reading data types in Wireshark
Wireshark’s CIP dissector handles common types automatically when the attribute being read is known. For unknown attributes, you see the raw bytes.
Filters that help:
cip # All CIP traffic
cip.service == 0x8E # Get_Attribute_Single replies (carry typed data)
cip.data_seg # Frames containing Data Segments (often typed data)
cip.epath # Frames containing EPATH content
When debugging type issues, capture the request and reply for an attribute you know the spec definition of, then verify the byte layout matches what the spec says. EDS files for the device often document each attribute’s exact data type.
Frequently asked questions
What is the difference between SINT, INT, DINT, and LINT in CIP?
These are signed integer types of increasing width. SINT is 8-bit (-128 to 127), INT is 16-bit (-32,768 to 32,767), DINT is 32-bit (about ±2.1 billion), and LINT is 64-bit (about ±9.2 quintillion). All are transmitted little-endian. The unsigned equivalents are USINT, UINT, UDINT, and ULINT.
What is the type code for a REAL in CIP?
The type code for REAL is 0xCA. REAL is a 4-byte IEEE 754 single-precision floating-point value. The 8-byte double-precision equivalent is LREAL with type code 0xCB. Both are transmitted little-endian on the wire.
What is the difference between STRING and SHORT_STRING?
STRING uses a 2-byte UINT length prefix and can hold up to 65,535 characters. SHORT_STRING uses a 1-byte USINT length prefix and can hold up to 255 characters. Both use ISO-8859-1 (Latin-1) encoding. SHORT_STRING is common in DeviceNet devices where bytes are at a premium. The Identity Object’s Product Name attribute is a SHORT_STRING.
Is CIP little-endian or big-endian?
CIP is little-endian. All multi-byte elementary types — INT, DINT, REAL, LREAL, UINT, UDINT, ULINT, etc. — are transmitted least-significant byte first. This is unusual because most Internet protocols use big-endian (network byte order). The EtherNet/IP encapsulation header is also little-endian to match CIP, but the surrounding TCP/IP, UDP/IP, and Ethernet headers are big-endian.
What is EPATH in CIP?
EPATH (Encoded Path, type code 0xDC) is the CIP data type that represents a path to an object — typically a sequence of Logical Segments for Class ID, Instance ID, and Attribute ID. EPATH is used in nearly every CIP message because every message must reference a target object. It also supports Port Segments for routing across networks and Symbolic Segments for tag-name references.
What is ENGUNIT in CIP?
ENGUNIT (type code 0xDD) is a 16-bit identifier that specifies an engineering unit — meters, volts, degrees Celsius, hertz, etc. The values are defined in Volume 1 Appendix D of the CIP specification. When a device attribute carries a measured value, a companion ENGUNIT attribute lets configuration tools display the value with its correct unit automatically.
How does CIP handle multilingual strings?
Through the STRINGI type (type code 0xDE). A STRINGI value contains one or more language variants, each tagged with an ISO 639-2/T language code (“eng”, “fra”, “deu”, etc.) and a character set identifier. Devices that need to expose multilingual product information — parameter descriptions, fault messages — use STRINGI to carry every supported language in one attribute.
What is the type code for STRUCT in CIP?
STRUCT has type code 0xA2. It is followed by the structure’s members, each with their own type code and value. The compact variant ABBREV_STRUCT (0xA0) omits the per-member type tags when the structure layout is already known from the object specification. ARRAY (0xA3) is the type code for fixed-size sequences of one type.
Why does CIP have so many time types?
Different resolutions and ranges. TIME (4 bytes, milliseconds, ±24 days) covers most general timeouts. FTIME (4 bytes, microseconds, ±35 minutes) provides higher resolution for short durations. LTIME (8 bytes, microseconds, ±106M days) provides both. ITIME (2 bytes, milliseconds, ±32.7 seconds) is compact for very short durations. DATE, TIME_OF_DAY, and DATE_AND_TIME are absolute time types rather than duration types.
How are CIP data types related to IEC 61131-3?
CIP data types align closely with the IEC 61131-3 PLC programming standard. BOOL, SINT, INT, DINT, LINT, USINT, UINT, UDINT, ULINT, REAL, LREAL, BYTE, WORD, DWORD, LWORD, TIME, DATE, TIME_OF_DAY, DATE_AND_TIME, and STRING all come directly from IEC 61131-3. CIP adds extensions: SHORT_STRING, STRING2, STRINGN, STRINGI (for various string formats), EPATH (for paths), ENGUNIT (for engineering units), FTIME, LTIME, ITIME (for additional time resolutions).
Where can I find the complete CIP engineering units list?
The complete list is in ODVA’s CIP Networks Library Volume 1, Appendix D. It defines hundreds of standardized unit identifiers for length, area, volume, time, mass, force, pressure, energy, power, electrical, magnetic, optical, thermal, and chemical units. The ENGUNIT data type (0xDD) carries the 16-bit identifier value. ODVA members can access the full specification at odva.org.
Are CIP data types the same on EtherNet/IP, DeviceNet, and ControlNet?
Yes. CIP data types are defined in the CIP common specification (Volume 1, Appendix C), not in any network-specific volume. A DINT on EtherNet/IP is the same as a DINT on DeviceNet — 4 bytes, signed, little-endian. The type system is one of the things that makes CIP truly media-independent.
