CIP Assembly Object Explained: How EtherNet/IP Devices Bundle I/O Data

By | June 19, 2026

When a ControlLogix PLC reads inputs from a 1734-AENT Point I/O block, it does not read each input one by one. It reads one big block of bytes that contains every input in a defined order. That block is an Assembly Object instance. The Assembly Object is the mechanism CIP uses to bundle scattered data — input bits, output bits, analog values, status flags — into one efficient packet that can be exchanged cyclically.

If Forward_Open is how a connection gets opened (see our CIP Forward_Open Service Explained article), the Assembly Object is what flows through that connection. Every I/O block, every drive, every smart sensor exposes its data through Assembly Object instances. The Connection Path in a Forward_Open request always references Assembly Object instances. Understanding Assembly Objects is non-negotiable for anyone troubleshooting CIP I/O.

This article explains the Assembly Object from ODVA’s CIP Networks Library Volume 1, Section 5-5. For broader CIP context, see the CIP Protocol Complete Guide and the CIP Object Model Explained articles.

What the Assembly Object is in one paragraph

The Assembly Object (Class Code 0x04) binds attributes from multiple other objects into a single block of data that can be sent or received over one CIP connection. Each Assembly Object instance is essentially an ordered list of references to data fields elsewhere in the device, packed together so they can move as one unit. There are static assemblies (defined by the device manufacturer) and dynamic assemblies (created by the user). Most I/O devices use static assemblies. Every cyclic I/O connection in CIP carries Assembly Object data.

The input/output terminology gotcha

This trips up almost everyone. In CIP, “input” and “output” are defined from the network’s point of view, not the device’s.

TermMeaningWhat the device does
Input AssemblyProduces data ON the networkDevice sends its inputs (sensor values, status) to the PLC
Output AssemblyConsumes data FROM the networkDevice receives commands (outputs to set, setpoints) from the PLC

So when a 1734-AENT has “Input Assembly Instance 101” — that contains data the device produces (the actual digital input states from the field). When the same device has “Output Assembly Instance 100” — that contains data the device consumes (the digital outputs the PLC wants to drive).

If you think about it from the PLC’s perspective, the names match too: the PLC reads inputs (which come from the Input Assembly) and writes outputs (which go to the Output Assembly). Either way, the convention is consistent — producer side or consumer side, network’s perspective.

How Assembly Objects connect to Forward_Open

Every Forward_Open Connection Path includes references to one or more Assembly Object instances. The standard pattern is three references:

  • Configuration Assembly — Data the PLC sends during connection establishment. Typically used to configure the device (channel ranges, alarm thresholds, etc.). Sent once at Forward_Open.
  • Consumed Assembly (Output) — Data the device consumes during cyclic exchange. Typically the outputs the PLC wants to drive.
  • Produced Assembly (Input) — Data the device produces during cyclic exchange. Typically the inputs the device sends to the PLC.

For a typical EtherNet/IP I/O block, the Connection Path might be:

Class 0x04 (Assembly)
Configuration Assembly: Instance 102
Output (Consumed) Assembly: Instance 100
Input (Produced) Assembly: Instance 101

The exact instance numbers vary by device. The device’s EDS file documents which instances to use for each role. See our future EDS Files Explained article (referenced in the CIP Protocol Complete Guide) for more on how EDS describes these.

After Forward_Open succeeds, the PLC produces output data into the Output Assembly every RPI, and the device produces input data from its Input Assembly every RPI. Both directions flow as UDP packets on port 2222 in EtherNet/IP.

Static vs Dynamic assemblies

The Assembly Object spec defines two types:

Static Assemblies

Defined by the device manufacturer or by the device profile. Fixed at design time. Cannot be modified by the user.

  • Instance number is fixed
  • Number of members is fixed
  • Member list is fixed
  • Can usually be implemented entirely in ROM

Static assemblies are how almost every commercial CIP device works. A PowerFlex drive has static Input and Output assemblies whose layouts are documented in its EDS file and user manual. You use them as they are.

Dynamic Assemblies

Created and managed by the user. The application can add or remove members at runtime.

  • Instance numbers must be in the vendor-specific range (0x64-0xC7 or 0x300-0x4FF)
  • Member list can be modified
  • Created via the Create service (0x08), modified via Insert_Member (0x1A) and Remove_Member (0x1B)

Dynamic assemblies are less common in field devices but appear in flexible systems where the user needs to customize which data points flow over a connection. Some PLC scanners and configuration tools create dynamic assemblies to access non-standard data combinations.

The Assembly Object specification supports additional services for Dynamic Assemblies (Create, Delete, Insert_Member, Remove_Member) that Static Assemblies do not need.

Assembly Instance ID ranges

The Assembly Object specification carves up the instance ID space (per Volume 1 Table 5-5.2):

RangeMeaningQuantity
0x01 – 0x63Open (static assemblies defined in device profile)99
0x64 – 0xC7Vendor-specific (static and dynamic assemblies)100
0xC8 – 0x2FFOpen (static assemblies defined in device profile)568
0x300 – 0x4FFVendor-specific (static and dynamic assemblies)512
0x500 – 0xFFFFFOpen (static assemblies defined in device profile)1,047,296
0x100000 – 0xFFFFFFFFReserved by CIP for future use4,293,918,720

The “open” ranges are reserved for ODVA-defined device profiles. The “vendor-specific” ranges are where individual manufacturers (and dynamic assemblies) live.

In practice, the most-seen instance numbers are in the 0x64-0xC7 vendor-specific range. Rockwell Point I/O typically uses instances 100, 101, 102. Allen-Bradley PowerFlex drives typically use 20, 21, 70, 71, and various others depending on the drive profile and configuration mode.

Assembly Object class attributes

The Assembly Object class itself has minimal attributes. Per Volume 1 Table 5-5.3:

Attribute IDNeedAccessNameTypeValue
1ConditionalGetRevisionUINTCurrent = 2
2-7OptionalVarious optional class attributesSee Chapter 4

Class attribute 1 (Revision) is required only if instance attribute 2 (Member List) is supported. Most devices return Revision = 2, reflecting the current Assembly Object Revision 02 (which obsoleted the 0x4B/0x4C Add_Member/Remove_Member services from Revision 01).

Assembly Object instance attributes

The instance attributes — the actual data the Assembly Object exposes — per Volume 1 Table 5-5.4:

Attribute IDNeedAccessNameTypePurpose
1ConditionalGetNumber of Members in ListUINTRequired only for Dynamic Assemblies
2ConditionalGet (Static) / Set (Dynamic)Member ListARRAY of STRUCTRequired only for Dynamic Assemblies
3RequiredSetDataARRAY of BYTEThe actual assembly data
4OptionalGetSizeUINTNumber of bytes in attribute 3

The single attribute that matters most for I/O is Attribute 3 (Data) — this is the actual byte array that gets exchanged cyclically. When the PLC reads inputs from an I/O block, it is reading Attribute 3 of the Input Assembly instance. When the PLC writes outputs, it is writing Attribute 3 of the Output Assembly instance.

The Member List (Attribute 2) is only present on Dynamic Assemblies (and on Static Assemblies that expose their structure). It describes which underlying data each part of the assembly references.

The Member List structure

When present, the Member List (Attribute 2) is an array of structures, each describing one member:

FieldTypePurpose
Member Data DescriptionUINTSize of this member’s data, in bits
Member Path SizeUINTSize of the member path that follows, in bytes
Member PathPacked EPATHCIP path to the data source (e.g., another object’s attribute)

So a single Member List entry might say: “16 bits of data, sourced from Class 0x0A (Analog Input Point), Instance 1, Attribute 3 (Analog Value).” The Assembly Object reads from that path when producing, or writes to it when consuming.

This is the mechanism that lets one Assembly bundle data from multiple objects. An Input Assembly might pull bits from a Discrete Input Object, words from an Analog Input Object, and status flags from the Identity Object — all packed into one byte array that gets sent as a unit.

For static assemblies, this structure is fixed in firmware and the device may not even expose Attribute 2 (since the layout cannot be changed). For dynamic assemblies, the application builds the member list explicitly.

Empty paths and bit-level padding

The Assembly Object spec defines a special case for member paths:

When an empty path (Member Path Size = 0) is used in an assembly member list, the assembly inserts/discards the number of bits as specified in the Member Data Size field when producing/consuming. The assembly shall use a value of zero for all produced data which has been inserted.

In plain English: you can put a “filler” entry in the member list with no path. When producing, the assembly inserts that many zero bits. When consuming, it discards that many bits.

This is useful for two things:

  1. Bit alignment — Forcing the next real member to start at a byte boundary
  2. Multicast filtering — Multiple devices on a multicast group can be configured to discard the parts not intended for them

Most field devices do not use empty paths heavily, but they appear in advanced multi-device sharing configurations.

Assembly Object services

The Assembly Object supports both common CIP services and (for dynamic assemblies) additional ones. Per Volume 1 Table 5-5.5:

ServiceCodeStatic ClassStatic InstanceDynamic ClassDynamic InstancePurpose
Get_Attribute_Single0x0EConditionalRequiredRequiredRequiredRead an assembly attribute
Set_Attribute_Single0x10n/aOptionaln/aConditionalWrite an assembly attribute
Get_Member0x18n/aOptionaln/aOptionalRead one member
Set_Member0x19n/aOptionaln/aOptionalWrite one member
Create0x08n/an/aRequiredn/aCreate new dynamic instance
Delete0x09n/an/aOptionalRequiredDelete dynamic instance
Insert_Member0x1An/an/an/aConditionalAdd member to dynamic
Remove_Member0x1Bn/an/an/aConditionalRemove member from dynamic

Obsolete services worth knowing about

Per Volume 1 Table 5-5.6, two services that used to be Assembly Object-specific were obsoleted in Revision 02:

  • 0x4B Add_Member — Obsoleted (replaced by common service Insert_Member 0x1A)
  • 0x4C Remove_Member — Obsoleted (replaced by common service Remove_Member 0x1B)

Modern devices should not use these codes. If you see 0x4B associated with an Assembly Object in older documentation, that is the obsolete Add_Member service. Note: in some legacy or vendor-specific references, the obsolete 0x4B has occasionally been mis-attributed to other services entirely — when in doubt, the current spec (Volume 1 Section 5-5.4 and Section 3-5.5.2) is the authoritative source.

How to actually read/write Assembly data

Two main patterns:

Cyclic (implicit) — via a CIP connection

This is the normal case. Forward_Open establishes a connection that exchanges Attribute 3 of the assembly automatically every RPI. No explicit Get/Set service calls are needed — the data just flows.

Acyclic (explicit) — via Get_Attribute_Single

For occasional reads (typically diagnostics or initial inspection), you can read Attribute 3 directly with a Get_Attribute_Single (service 0x0E) call:

Service: 0x0E (Get_Attribute_Single)
Path:    Class 0x04 (Assembly), Instance N, Attribute 3 (Data)

The response is the byte array. This is how you “peek” at an assembly’s value without setting up cyclic communication.

For configuration assemblies, you can also write the configuration data via Set_Attribute_Single (0x10) to Attribute 3 before connecting — or include it as a Data Segment in the Forward_Open’s Connection Path so it arrives during connection establishment.

Real-world example — a 1734-AENT Point I/O block

Imagine an Allen-Bradley 1734-AENT Point I/O adapter with two modules:

  • Slot 1: 1734-IB8 (8-channel digital input)
  • Slot 2: 1734-OB8 (8-channel digital output)

After auto-configuration, the device exposes:

InstanceTypeSizePurpose
100Output (Consumed)1 byte8 output bits — slot 2
101Input (Produced)1 byte8 input bits — slot 1
102Configurationseveral bytesConfiguration data, sent at Forward_Open
198Listen-only(multicast)Multicast subscription for other consumers
199Input-only(input only)Input data without owning the device

The PLC opens a Forward_Open with:

  • Configuration Assembly: Instance 102
  • Output Assembly: Instance 100
  • Input Assembly: Instance 101

Every RPI:

  • The PLC sends 1 byte to Assembly 100 (PLC’s view: “drive these 8 outputs”)
  • The device sends 1 byte from Assembly 101 (PLC’s view: “here are 8 inputs”)

For a more complex Point I/O configuration with multiple analog modules, the assembly sizes grow accordingly. The 1734-AENT documents the exact assembly layout in its user manual and its EDS file.

Assembly mistakes are a major source of Forward_Open failures. The most frequent:

SymptomLikely causeWhere to look
Extended Status 0x0109 (Invalid connection size)Your config has wrong assembly sizeCheck EDS file for actual assembly size
Extended Status 0x0117 (Invalid produced/consumed application path)Assembly instance ID doesn’t existVerify instance numbers in EDS file
Extended Status 0x0118 (Invalid configuration application path)Configuration assembly instance ID wrongVerify configuration assembly instance
Get_Attribute_Single returns General Status 0x05 (Path destination unknown)Assembly instance does not exist on this deviceList the device’s assemblies first
Data values look corrupt/swappedByte order or member layout mismatchCompare assembly definition against your client’s interpretation
Connection drops shortly after openingWrong configuration assembly contentsConfiguration data may be malformed for this device

For complete error code coverage, see our CIP General Status Codes Reference — especially the Extended Status section under General Status 0x01 for Forward_Open failures.

How EDS files describe assemblies

Every CIP device’s EDS file contains an [Assembly] section that documents its supported assemblies. A typical entry:

ini

[Assembly]

Assem1 =
    "Output Assembly 100",      $ Name
    ,                           $ Path (none — describes only)
    8,                          $ Size in bits (1 byte = 8 bits)
    0x0000,                     $ Descriptor (flags)
    ,                           $ Reserved
    ,                           $ Reserved
    8, 0xC1, 1, "Output Bit 0", $ Member: 1 bit, BOOL, descriptor 1
    8, 0xC1, 1, "Output Bit 1",
    ...

The EDS sections used most heavily by configuration tools are:

  • [Assembly] — Lists all assembly instances and their sizes
  • [Parameters] — Lists individual data points and their types
  • [Connection Manager] — Lists which connections can be opened and which assemblies they reference

When you import an EDS file into Studio 5000 or another engineering tool, the tool reads these sections to know which assemblies to use, what sizes to expect, and how to display the data. Choosing an assembly that does not match the device’s actual capability is the most common source of Forward_Open failures during commissioning.

Reading Assembly traffic in Wireshark

Once a connection is open, assembly data flows as UDP packets on port 2222 with a Sequenced Address Item carrying the Connection ID. Useful Wireshark filters:

cip                                  # All CIP traffic
cip.class == 0x04                    # Anything addressed to Assembly Object class
cip_class.iface                      # Connected I/O packets carrying assembly data
udp.port == 2222                     # All EtherNet/IP I/O messaging
enip.cpf.typeid == 0x8002            # Sequenced Address Item packets (I/O messaging)
cip.service == 0x0E                  # Get_Attribute_Single (used to read assembly Attribute 3 explicitly)

When debugging assembly contents:

  1. Capture during the issue
  2. Filter on udp.port == 2222
  3. Find the cyclic packets going to/from your device’s IP
  4. Expand a packet — the Data section is the assembly’s Attribute 3 contents
  5. Compare against your expected layout (from the device’s EDS file or manual)

If bits or bytes are not where you expect, your client code’s interpretation of the assembly does not match the device’s actual layout. This is one of the most common bugs in custom EtherNet/IP integrations.

For broader Wireshark workflows, see our Wireshark for EtherNet/IP guide.

Listen-only and input-only connections

In addition to standard exclusive-owner connections, the Assembly Object supports two special connection types that show up frequently in real systems:

Listen-only connections

A Listen-only connection lets a second consumer subscribe to an existing multicast Input Assembly being produced for another exclusive-owner connection. The listen-only consumer receives the data but does not own the device.

Listen-only requires that another exclusive-owner connection is already open. If no primary connection exists, the listen-only Forward_Open fails with Extended Status 0x0119 (Non-listen-only connection not opened).

Devices that support listen-only typically dedicate a specific Assembly Instance ID for it (often instance 198 or similar in the vendor-specific range).

Input-only connections

An Input-only connection consumes the Input Assembly without sending any Output Assembly data. The PLC reads inputs but does not drive outputs. Used when one PLC needs to monitor an I/O block whose outputs are driven by a different PLC.

These connection types let multi-PLC configurations share I/O cleanly without ownership conflicts. They are configured via the Network Connection Parameters in Forward_Open (the Redundant Owner and Connection Type bits) plus the appropriate assembly instance selection.

Frequently asked questions

What is the CIP Assembly Object?

The Assembly Object (Class Code 0x04) is a CIP object that bundles attributes from multiple other objects into a single block of data that can be exchanged over one CIP connection. Every EtherNet/IP I/O device exposes its data through Assembly Object instances. The cyclic I/O exchange that happens after Forward_Open transfers the Data attribute (attribute 3) of these assemblies.

What is the difference between input and output assemblies?

In CIP, the terms are defined from the network’s perspective. An Input Assembly produces data on the network (the device sends data to the PLC). An Output Assembly consumes data from the network (the device receives data from the PLC). From the PLC’s perspective this works out — the PLC reads inputs (from the device’s Input Assembly) and writes outputs (to the device’s Output Assembly).

What is a configuration assembly?

A configuration assembly is a CIP Assembly Object instance whose data is sent to a device during Forward_Open connection establishment. It contains configuration parameters (channel ranges, alarm thresholds, scaling, etc.) that the device should apply when the connection opens. Configuration data is transferred once at connection establishment, not cyclically like input/output assemblies.

What is the difference between static and dynamic assemblies?

Static assemblies are defined by the device manufacturer and cannot be modified by the user — the instance number, member count, and member list are fixed. Most commercial CIP I/O devices use only static assemblies. Dynamic assemblies are created and managed by the user at runtime using the Create, Insert_Member, Remove_Member, and Delete services. They allow flexible custom data combinations but are less common in field devices.

What is the class code for the Assembly Object?

The class code for the CIP Assembly Object is 0x04 (decimal 4). This is one of the most-referenced class codes in CIP because every cyclic I/O connection involves Assembly Object instances. In a Forward_Open Connection Path, you typically see “Class 0x04, Instance N” multiple times — once for each assembly the connection uses.

What does Extended Status 0x0117 mean in a Forward_Open failure?

Extended Status 0x0117 means “Invalid produced or consumed application path” — the Assembly Instance ID in your Forward_Open Connection Path does not exist on the target device. Verify the assembly instance number against the device’s EDS file or user manual. Wrong instance numbers are a common commissioning error, especially with third-party devices where the assembly numbering may not match Rockwell conventions.

How do I read assembly data without opening a connection?

Use Get_Attribute_Single (service 0x0E) directly on the assembly’s Data attribute (attribute 3). The path is Class 0x04, Instance N, Attribute 3. The response is the raw byte array of the assembly’s current value. This is useful for diagnostics or one-shot reads but is much less efficient than cyclic implicit messaging for ongoing exchange.

What is the maximum size of a CIP assembly?

A standard CIP connection (opened with Forward_Open service 0x54) supports assemblies up to 511 bytes per direction. For larger assemblies, Large_Forward_Open (service 0x5B) supports up to 65,535 bytes per direction. Modern PLCs that exchange large structured data over single I/O connections typically use Large_Forward_Open. The size limit is per direction — input and output have independent limits.

What is a listen-only connection?

A listen-only connection lets a consumer subscribe to an existing multicast Input Assembly without owning the device. A primary exclusive-owner connection must already exist; the listen-only consumer just receives the multicast data. This is useful when multiple PLCs need to monitor the same I/O block, but only one PLC drives the outputs. Listen-only connections fail with Extended Status 0x0119 if no primary connection exists.

What instance numbers do EtherNet/IP devices commonly use for assemblies?

There is no universal standard — each manufacturer chooses. Common Rockwell conventions: 100 for outputs, 101 for inputs, 102 for configuration on Point I/O blocks. Allen-Bradley drives use various numbers depending on the drive profile (20, 21, 70, 71, and others). Always check the EDS file or device manual for the actual instance numbers — guessing leads to Forward_Open failures with Extended Status 0x0117.

Were 0x4B and 0x4C ever CIP Assembly Object services?

Yes, historically. In Assembly Object Revision 01, service codes 0x4B (Add_Member) and 0x4C (Remove_Member) were object-specific services. They were obsoleted in Revision 02 and replaced by the Common Services Insert_Member (0x1A) and Remove_Member (0x1B). Modern devices use only the Common Service codes. If you see references to 0x4B or 0x4C for Assembly operations in old documentation, those services are obsolete.

Where can I find the official CIP Assembly Object specification?

The Assembly Object is defined in ODVA’s CIP Networks Library Volume 1: Common Industrial Protocol (CIP) Specification, Chapter 5 (Object Library), Section 5-5. The current edition is 3.3 (2007). ODVA members can access the full specification at odva.org. Volume 1 also documents how device profiles in Chapter 6 reference specific Assembly instances for common device categories like AC Drives, Discrete I/O, and Encoders.

Author: Zakaria El Intissar

I'm an automation and industrial computing engineer with 12 years of experience in power system automation, SCADA communication protocols, and electrical protection. I build tools and write guides for Modbus, DNP3, IEC 101/103/104, and IEC 61850 on ScadaProtocols.com to help engineers decode, analyze, and troubleshoot real industrial communication systems.