Rockwell/Allen-Bradley CIP DataTable services for grouped tag reads and writes over EtherNet/IP.
- Overview
- Lifecycle
- EPath Encoding
- Service 0x08 — Create DataTable Buffer
- Service 0x09 — Delete DataTable Buffer
- Service 0x4C — DataTable Read
- Service 0x4D — DataTable Write
- Service 0x4E — Add Tag to Buffer / ReadModifyWrite
- Service 0x4F — Remove Tag from Buffer
- Service 0x03 — Query Buffer Attributes
- Transport Wrapping
- Constants Reference
The CIP DataTable mechanism allows batched/grouped access to PLC tag data through a class 0xB2 CIP object. Instead of reading tags one at a time, a client:
- Creates a datatable buffer instance on the PLC
- Associates tags with the buffer (Add Tag)
- Reads all tag values from the buffer in a single request
- Deletes the buffer when done
The buffer is read-only from the client side — there is no "write to buffer" operation. The PLC maintains current tag values; the client reads them all at once. To write tag values, you must address tags directly via service 0x4D (Write) or 0x4E (ReadModifyWrite), not through the buffer.
All datatable CIP messages use AppDataType 0xB2 in the EIP encapsulation layer and target CIP class 0xB2 in the request path.
Symbolic addressing — tags identified by ASCII name:
- Uses ANSI Extended Symbol segments (0x91) in the IOI path
- Example: tag "MyTag" →
91 05 4D 79 54 61 67 00
Instance addressing — tags identified by numeric class/instance:
- Uses class segment 0x6B with 16-bit instance IDs
- Optional routing prefix with class 0x68
- Example: class 0x6B instance 0x4644 →
20 6B 25 00 44 46
┌─────────────────────────────────────────────────────────┐
│ 1. CREATE (service 0x08) │
│ → Allocates datatable buffer on PLC │
│ → Returns instance ID (e.g., 0x0021) │
├─────────────────────────────────────────────────────────┤
│ 2. ADD TAGS (service 0x4E on class 0xB2 instance) │
│ → Associates tag EPaths with the buffer │
│ → Returns batch index (1-based, per 0x4E call) │
│ → Can batch multiple tags per call (mixed sizes OK) │
├─────────────────────────────────────────────────────────┤
│ 3. READ BUFFER (service 0x4C, repeated as needed) │
│ → Returns all tag values in one response │
│ → Buffer is READ-ONLY from client side │
├─────────────────────────────────────────────────────────┤
│ 4. REMOVE TAGS (service 0x4F, optional) │
│ → Removes a tag from the buffer by index │
├─────────────────────────────────────────────────────────┤
│ 5. DELETE (service 0x09) │
│ → Frees the datatable buffer │
└─────────────────────────────────────────────────────────┘
NOTE: Writing tag values (0x4D) and atomic bit manipulation (0x4E on tag EPath) are done DIRECTLY to tags, NOT through the buffer. The buffer exists solely for grouped reads.
All CIP messages use an IOI (Internal Object Identifier) path to address target objects. The path is composed of segments:
| Header Byte | Name | Format | Size |
|---|---|---|---|
0x20 |
8-bit Class ID | 20 cc |
2 bytes |
0x21 |
16-bit Class ID | 21 00 cc cc |
4 bytes |
0x24 |
8-bit Instance ID | 24 ii |
2 bytes |
0x25 |
16-bit Instance ID | 25 00 ii ii |
4 bytes |
0x28 |
8-bit Element (Member) | 28 vv |
2 bytes |
0x29 |
16-bit Element | 29 00 vv vv |
4 bytes |
0x2A |
32-bit Element | 2A 00 vv vv vv vv |
6 bytes |
0x91 |
ANSI Extended Symbol | 91 LL ss ss .. [00] |
2 + len + pad |
0xA0 |
Extended Segment | A0 02 vv vv |
4 bytes |
Byte 0: 0x91 (segment header)
Byte 1: String length (number of ASCII characters)
Byte 2+: ASCII string (NOT null-terminated)
Pad: 0x00 appended if string length is odd (align to 16-bit boundary)
Example: tag name "MyDINT" (6 chars, even → no pad):
91 06 4D 79 44 49 4E 54
Example: tag name "Count" (5 chars, odd → pad with 0x00):
91 05 43 6F 75 6E 74 00
Used for array indices and sub-elements. Encoding depends on value magnitude:
| Value Range | Header | Format |
|---|---|---|
| 0–255 | 0x28 |
28 vv (2 bytes) |
| 256–65534 | 0x29 |
29 00 lo hi (4 bytes, LE) |
| 65535–4294967294 | 0x2A |
2A 00 b0 b1 b2 b3 (6 bytes, LE) |
For instance-based tag addressing, the IOI contains hardcoded class/instance segments:
[Optional routing prefix]:
20 68 Class 0x68 (8-bit class segment)
25 00 ii ii 16-bit instance ID (LE) — routing instance
[Required tag identifier]:
20 6B Class 0x6B (8-bit class segment)
25 00 ii ii 16-bit instance ID (LE) — tag instance
[Optional additional elements]:
28/29/2A ... Numeric element segments for sub-addressing
The path size field (byte 1 of every CIP message) is the total path length in 16-bit words (not bytes). Each segment is padded to a 16-bit boundary, so:
0x28 vv= 1 word0x29 00 vv vv= 2 words0x20 cc= 1 word0x25 00 ii ii= 2 words0x91 LL string [pad]= (2 + len + pad) / 2 words
Creates a new datatable buffer instance on class 0xB2.
CIP Message Router Request:
Service: 0x08 (CIP Create)
Request Path: 20 B2 25 00 00 00
├─ 20 B2 = Class 0xB2 (8-bit class segment)
└─ 25 00 00 00 = 16-bit Instance 0x0000 (create new)
Path size: 0x03 (3 words = 6 bytes)
Request Data: 01 00 03 00 03 00
├─ 01 00 = Hardcoded constant (LE16 = 1)
├─ 03 00 = Hardcoded constant (LE16 = 3)
└─ 03 00 = Hardcoded constant (LE16 = 3)
These 6 creation bytes are hardcoded constants. Every buffer creation uses the same 01 00 03 00 03 00 payload.
General Status: 0x00 (Success)
Response Data: 21 00 f8 d4 0c 00
├─ 21 00 = Instance ID (0x0021) — use in subsequent operations
└─ f8 d4 0c 00 = ? (additional creation response data)
The 16-bit instance ID from the response is used as the target instance for all subsequent Add Tag, Read, and Remove Tag operations on the buffer.
Deletes a previously created datatable buffer instance.
CIP Message Router Request:
Service: 0x09 (CIP Delete)
Request Path: 20 B2 25 00 <instID_lo> <instID_hi>
├─ 20 B2 = Class 0xB2
└─ 25 00 ii ii = 16-bit Instance ID (from Create response)
Request Data: (none)
General Status: 0x00 (Success)
Reads values for one or more tags from the datatable buffer. Supports batching multiple tags in a single request.
Byte 0: 0x4C (service code)
Byte 1: IOI path size (16-bit words)
Byte 2+: IOI path — concatenated tag segments:
For each tag:
Symbolic: 91 LL <ascii> [pad]
— OR —
Numeric: 28/29/2A <value>
After IOI: Offset (2 bytes LE) — starting offset for read
Read tag "MyDINT" starting at offset 0:
4C Service code: Read
04 IOI path size: 4 words (8 bytes)
91 06 4D 79 44 49 Symbolic segment: "MyDINT" (6 chars)
4E 54
00 00 Offset: 0x0000
Read all tags from buffer instance 0x0021:
4C Service code: Read
03 Path size: 3 words (6 bytes)
20 B2 Class 0xB2
25 00 21 00 Instance 0x0021
(no request data — reads all tags in buffer)
The response is grouped by 0x4E add-tag calls (batches). Each batch starts with a 2-byte LE batch/entry index (the value returned by that 0x4E call), followed by the concatenated tag values for all tags in that batch. There is one entry header per 0x4E call, not per tag.
For each 0x4E add-tag call (in order of calls):
[2 bytes LE: batch index (1, 2, ...)]
For each tag in that batch (in order they were added):
[DataTypeSize bytes: tag value data]
Example response — 1 DINT tag added via a single AddTag call (batch 1):
01 00 EE EE EE EE
├─ 01 00 = batch index 1
└─ EE EE EE EE = DINT value (4 bytes LE)
Example response — all tags added in one AddTags call (batch 1): 1 STRING + 3 DINTs, all in one 0x4E call:
01 00 batch index 1
[88 bytes: STRING value] tag 1 value
[4 bytes: DINT 1 value] tag 2 value
[4 bytes: DINT 2 value] tag 3 value
[4 bytes: DINT 3 value] tag 4 value
Example response — tags added across two 0x4E calls: Call 1 added 2 DINTs, call 2 added 1 INT:
01 00 batch index 1
[4 bytes: DINT 1 value] tag 1 value
[4 bytes: DINT 2 value] tag 2 value
02 00 batch index 2
[2 bytes: INT value] tag 3 value
Writes values directly to one or more tags by EPath. This service addresses tags directly — it does not operate through the datatable buffer.
Byte 0: 0x4D (service code)
Byte 1: IOI path size (16-bit words)
Byte 2+: IOI path — concatenated tag segments
After IOI: [Optional: A0 02 <extended_value_LE16> — extended segment]
Count (2 bytes LE) — number of data elements
Write data (raw bytes, memcpy'd)
Byte 0: 0x4D (service code)
Byte 1: IOI path size (16-bit words)
Byte 2+: IOI path:
[Optional: 20 68 25 00 <routingID_LE16>] Class 0x68 prefix
20 6B 25 00 <tagID_LE16> Class 0x6B tag
[28/29/2A <element>]* Additional elements
After IOI: [Optional: A0 02 <extended_value_LE16>]
Count (2 bytes LE)
Write data (raw bytes)
When present (determined by a flag parameter), a 4-byte extended segment is inserted before the count field:
A0 02 vv vv
├─ A0 = Extended segment type
├─ 02 = Segment size (2 bytes)
└─ vv vv = Extended value (LE16)
The extended value is a CIP data type code (e.g., 0xC1=BOOL, 0xC2=SINT, 0xC3=INT, 0xC4=DINT, 0xCA=LREAL, 0xD0=STRING). When the extended flag parameter is set, the type code is wrapped in this 0xA0 0x02 segment; otherwise the code is written as a single raw byte before the count field. When neither form is present, the count field follows the IOI directly.
4D Service code: Write
03 IOI path size: 3 words (6 bytes)
20 6B Class 0x6B
25 00 44 46 Instance 0x4644
01 00 Count: 1 element
78 56 34 12 Write data: DINT 0x12345678 (LE)
Service 0x4E has two distinct uses depending on the request path target:
| Target | Operation | Description |
|---|---|---|
| Class 0xB2 instance (buffer) | Add Tag | Associates a tag EPath with the buffer for grouped reads |
| Tag EPath directly | ReadModifyWrite | Atomic bit manipulation with OR/AND masks |
When addressed to a class 0xB2 datatable buffer instance, service 0x4E adds tag(s) to the buffer for grouped reads.
Multi-tag batching: Multiple tags can be added in a single 0x4E call. Each tag carries its own dataTypeSize and IOI length, so mixed type sizes are fully supported in a single call (confirmed via packet capture: typeSize=1, 2, 4, and 16 all interleaved in one request).
The dataTypeSize field is a raw byte count (LE16), not a CIP type code. Pass the total structure size for the tag's data type:
| CIP Type | dataTypeSize |
Hex | Notes |
|---|---|---|---|
| BOOL | 1 | 01 00 |
Stored as 1 byte |
| SINT | 1 | 01 00 |
1-byte signed integer |
| INT | 2 | 02 00 |
2-byte signed integer |
| DINT | 4 | 04 00 |
4-byte signed integer |
| REAL | 4 | 04 00 |
4-byte IEEE float |
| LINT | 8 | 08 00 |
8-byte signed integer |
| LREAL | 8 | 08 00 |
8-byte IEEE double |
| STRING | 88 | 58 00 |
Standard AB: 4-byte .LEN + 82-byte .DATA + 2 pad |
For custom-length string types (e.g., STRING[20]), use the actual structure size as reported by the PLC. This value is written directly into the message as a LE16.
When parsing the Read (0x4C) response, a 2-byte LE batch index header precedes each group of tags from a single 0x4E call. Within each group, tag values are concatenated at exactly dataTypeSize bytes each. The client must track batch boundaries (one header per 0x4E call) and each tag's size to correctly extract values.
Byte 0: 0x4E (service code)
Byte 1: 0x03 (path size: 3 words = 6 bytes)
Byte 2-7: Request path to buffer instance:
20 B2 Class 0xB2
25 00 ii ii 16-bit instance ID (from Create response)
Byte 8-11: Hardcoded header (4 bytes):
02 00 Hardcoded constant (LE16 = 2)
01 Hardcoded constant
01 Hardcoded constant
Byte 12: Tag count (1 byte) — number of tags in this call
Repeated [tagCount] times:
[2 bytes LE: dataTypeSize] This tag's slot size in bytes
[1 byte: ioiLenWords] This tag's IOI length in 16-bit words
[N bytes: IOI EPath] This tag's path bytes (N = ioiLenWords × 2)
The 4-byte header 02 00 01 01 is hardcoded. The 5th byte is the tag count (not a hardcoded constant). Each tag then carries its own dataTypeSize and IOI, allowing mixed type sizes in a single call.
4E Service code: Add Tag / ReadModifyWrite
03 Path size: 3 words (6 bytes)
20 B2 Class 0xB2
25 00 21 00 Instance 0x0021 (from Create response)
02 00 Hardcoded: 0x0002
01 Hardcoded: 0x01
01 Hardcoded: 0x01
01 Tag count: 1
04 00 dataTypeSize: 4 bytes (DINT)
03 ioiLenWords: 3 words (6 bytes)
20 6B Class 0x6B
25 00 44 46 Instance 0x4644
4E Service code: Add Tag
03 Path size: 3 words (6 bytes)
20 B2 Class 0xB2
25 00 28 01 Instance 0x0128
02 00 01 01 Hardcoded header
03 Tag count: 3
— Tag 1 (SINT, typeSize=1):
01 00 dataTypeSize: 1 byte
03 ioiLenWords: 3 words (6 bytes)
20 6B 25 00 0A 01 Class 0x6B, Instance 0x010A
— Tag 2 (INT, typeSize=2):
02 00 dataTypeSize: 2 bytes
04 ioiLenWords: 4 words (8 bytes)
20 6B 25 00 07 01 Class 0x6B, Instance 0x0107
28 05 Element 5
— Tag 3 (DINT, typeSize=4):
04 00 dataTypeSize: 4 bytes
03 ioiLenWords: 3 words (6 bytes)
20 6B 25 00 3F 01 Class 0x6B, Instance 0x013F
General Status: 0x00 (Success)
Response Data: ii ii
└─ Batch index (LE16, 1-based, incrementing per 0x4E call:
1st call → 01 00, 2nd call → 02 00, ...)
The batch index identifies this group of tags in the Read (0x4C) response. It increments per 0x4E call, NOT per tag. For single-tag-per-call usage, the batch index coincidentally equals the tag's sequential position.
When addressed to a tag EPath directly (not a class 0xB2 buffer instance), service 0x4E performs an atomic read-modify-write. Applies OR and AND masks to existing tag values, allowing individual bits to be set or cleared without race conditions.
The operation is: new_value = (old_value OR or_mask) AND and_mask
Byte 0: 0x4E (service code)
Byte 1: IOI path size (16-bit words)
Byte 2+: IOI path — concatenated tag segments
After IOI: Data size (2 bytes LE) — 1, 2, or 4 (byte width of masks)
OR mask (1, 2, or 4 bytes — same width as data size)
AND mask (1, 2, or 4 bytes — same width as data size)
Byte 0: 0x4E (service code)
Byte 1: IOI path size (16-bit words)
Byte 2+: IOI path:
[Optional: 20 68 25 00 <routingID_LE16>]
20 6B 25 00 <tagID_LE16>
[28/29/2A <element>]*
After IOI: Data size (2 bytes LE)
OR mask (data_size bytes)
AND mask (data_size bytes)
| Data Size | OR Mask Bytes | AND Mask Bytes | Total After IOI |
|---|---|---|---|
| 1 (0x01 0x00) | 1 | 1 | 4 bytes |
| 2 (0x02 0x00) | 2 | 2 | 6 bytes |
| 4 (0x04 0x00) | 4 | 4 | 10 bytes |
Set bit 3 (OR with 0x00000008, AND with 0xFFFFFFFF to preserve all other bits):
4E Service code: ReadModifyWrite
03 IOI path size: 3 words
20 6B Class 0x6B
25 00 44 46 Instance 0x4644
04 00 Data size: 4 bytes (DINT)
08 00 00 00 OR mask: 0x00000008 (set bit 3)
FF FF FF FF AND mask: 0xFFFFFFFF (keep all bits)
Clear bit 0 (OR with 0x00, AND with 0xFE):
4E Service code: ReadModifyWrite
04 IOI path size: 4 words (8 bytes)
91 05 46 6C 61 67 Symbolic: "Flags" (5 chars + pad)
73 00
01 00 Data size: 1 byte (SINT)
00 OR mask: 0x00 (don't set any bits)
FE AND mask: 0xFE (clear bit 0)
Removes a previously added tag from a datatable buffer instance by its tag index.
Byte 0: 0x4F (service code)
Byte 1: 0x03 (path size: 3 words = 6 bytes)
Byte 2-7: Request path to buffer instance:
20 B2 Class 0xB2
25 00 ii ii 16-bit instance ID (from Create response)
Byte 8-9: Tag index to remove (2 bytes LE)
The 1-based index returned by the Add Tag response
4F Service code: Remove Tag
03 Path size: 3 words (6 bytes)
20 B2 Class 0xB2
25 00 21 00 Instance 0x0021
02 00 Tag index: 2 (remove the second tag added)
General Status: 0x00 (Success)
CIP Get_Attribute_List (service 0x03) can query class 0xB2 for size limits and buffer status. Three query patterns are supported.
Asks the PLC how many buffer instances exist and the maximum allowed.
Service 0x03 on class 0xB2, instance 0x00 (class level):
03 Service: Get Attribute List
02 Path size: 2 words
20 B2 Class 0xB2
24 00 Instance 0x00 (class-level query)
02 00 Attribute count: 2
02 00 Attribute 2 (Max Instance — max buffers allowed)
03 00 Attribute 3 (Number of Instances — current buffer count)
Use this to check if the PLC can accept more buffer creations before calling Create (0x08).
Asks a specific buffer instance for its tag count.
Service 0x03 on class 0xB2, instance N (specific buffer):
03 Service: Get Attribute List
02 Path size: 2 words
20 B2 Class 0xB2
24/25 ii [ii] Instance ID (8-bit or 16-bit depending on value)
01 00 Attribute count: 1
06 00 Attribute 6 (tag count / driver count in buffer)
Use this to verify how many tags are currently associated with a buffer.
Reads the class 0xB2 revision.
Service 0x03 on class 0xB2, instance 0x00 (class level):
03 Service: Get Attribute List
02 Path size: 2 words
20 B2 Class 0xB2
24 00 Instance 0x00 (class-level query)
01 00 Attribute count: 1
01 00 Attribute 1 (Revision)
| Scope | Attribute | Standard CIP Name | Purpose |
|---|---|---|---|
| Class (inst 0) | 1 | Revision | Protocol version |
| Class (inst 0) | 2 | Max Instance | Max simultaneous buffers |
| Class (inst 0) | 3 | Number of Instances | Current buffer count |
| Instance | 6 | (Vendor-specific) | Tag count in buffer |
Buffer limits are PLC-side, not client-side:
- Max buffers: Determined by PLC firmware (queryable via attribute 2)
- Tags per buffer: Queryable per instance via attribute 6; max is PLC-dependent
- Client-side limit: A single Add Tag message's IOI payload is limited to ~980 bytes, but multiple Add calls can be made to the same buffer
All datatable CIP messages are sent inside EtherNet/IP (EIP) frames with AppDataType 0xB2.
Connected messaging (EIP command 0x70 SendUnitData):
- Used when a CIP connection is already established
- Route contains connection identifier:
81 00 04 00 <connID_LE32>
Unconnected messaging (EIP command 0x6F SendRRData):
- Used for one-shot requests without a pre-established connection
- CIP payload is wrapped in UCMM (Unconnected Message Manager)
For unconnected messaging, the CIP message is wrapped in a UCMM frame:
Service: 0x52 (Unconnected Send)
Path: 20 06 24 01 (class 0x06 = Connection Manager, instance 0x01)
Data:
Byte 0: Priority/Timeout (scaled 12-bit)
Byte 1: Timeout value
Byte 2-3: Embedded message length (2 bytes LE)
Byte 4+: Embedded CIP message (service 0x4C/0x4D/0x4E + IOI + data)
[Pad]: 0x00 if embedded message length is odd
After: Route path size (1 byte, in words) + 0x00 pad
Route path data (e.g., backplane port + slot)
Generic CIP services like Create (0x08) and Delete (0x09) use the CIP Message Router. A 6-byte header is prepended:
0A 02 20 02 24 01 | <CIP service + path + data>
This routes the inner message through class 0x02 (Message Router) instance 0x01.
| Code | Name | Usage |
|---|---|---|
0x03 |
Get Attribute List | Query class/instance attributes (count, revision, limits) |
0x08 |
Create | Create datatable buffer (class 0xB2 instance) |
0x09 |
Delete | Delete datatable buffer |
0x0A |
Multiple Service Packet / Forward | Message Router wrapper service |
0x4C |
DataTable Read | Read tag values from datatable |
0x4D |
DataTable Write | Write tag values to datatable |
0x4E |
Add Tag / ReadModifyWrite | Dual use: Add Tag to Buffer (on 0xB2 instance) OR atomic bit manipulation (on tag EPath) |
0x4F |
Remove Tag from Buffer | Remove a tag from datatable buffer by index |
0x52 |
Unconnected Send | UCMM wrapper for unconnected messaging |
| Class | Name | Usage |
|---|---|---|
0x02 |
Message Router | Generic CIP message routing |
0x06 |
Connection Manager | UCMM / connection services |
0x68 |
(Routing) | Optional routing prefix in instance-addressed datatable operations |
0x6B |
(Tag Object) | Tag instance identifier in datatable operations |
0xB2 |
DataTable | Datatable buffer object; also used as EIP AppDataType |
| Header | Type | Format |
|---|---|---|
0x20 |
8-bit Class Segment | 20 cc |
0x24 |
8-bit Instance Segment | 24 ii |
0x25 |
16-bit Instance Segment | 25 00 lo hi |
0x28 |
8-bit Element Segment | 28 vv |
0x29 |
16-bit Element Segment | 29 00 lo hi |
0x2A |
32-bit Element Segment | 2A 00 b0 b1 b2 b3 |
0x91 |
ANSI Extended Symbol | 91 len string [pad] |
0xA0 |
Extended Segment | A0 size data... |
| Code | Name | Usage |
|---|---|---|
0x6F |
SendRRData | Unconnected EIP messaging |
0x70 |
SendUnitData | Connected EIP messaging |
| Code | Name | Usage |
|---|---|---|
0x91 |
ASA/CIP Unconnected | Standard CIP unconnected messages |
0xB1 |
Connected Data | Connected transport data |
0xB2 |
DataTable | DataTable protocol messages |
4C 04 91 06 4D 79 44 49 4E 54 00 00
│ │ └──────── "MyDINT" ────────┘ │
│ │ IOI: symbolic segment │
│ └── path size: 4 words │
└──── service: Read └── offset: 0x0000
4D 03 20 6B 25 00 44 46 01 00 78 56 34 12
│ │ └──── class 0x6B ──┘│ │ └── DINT data ──┘
│ │ └── inst 0x4644 ────┘ └── count: 1
│ └── path size: 3 words
└──── service: Write
4E 03 20 6B 25 00 44 46 04 00 80 00 00 00 FF FF FF FF
│ │ └──── class 0x6B ──┘│ │ └─ OR mask ──┘└─ AND mask─┘
│ │ └── inst 0x4644 ────┘ └── data size: 4
│ └── path size: 3 words
└──── service: ReadModifyWrite
1. CREATE: 08 03 20 B2 25 00 00 00 01 00 03 00 03 00
├── Service: 0x08 (Create)
├── Path Size: 0x03 (3 words, 6 bytes)
├── Request Path
│ ├── 20 B2 = Class 0xB2
│ └── 25 00 00 00 = Instance 0x0000 (create new)
└── Request Data: 01 00 03 00 03 00 (hardcoded creation params)
→ Response: 21 00 f8 d4 0c 00 (instance ID = 0x0021, +4 bytes extra)
2. ADD TAG: 4E 03 20 B2 25 00 21 00 02 00 01 01 01 04 00 03 20 6B 25 00 44 46
├── Service: 0x4E (Add Tag)
├── Path Size: 0x03 (3 words, 6 bytes)
├── Request Path (buffer target)
│ ├── 20 B2 = Class 0xB2
│ └── 25 00 21 00 = Instance 0x0021 (from step 1)
├── Header: 02 00 01 01 (hardcoded)
├── Tag Count: 01 (1 tag)
├── Tag 1:
│ ├── dataTypeSize: 04 00 (4 bytes = DINT)
│ ├── ioiLenWords: 03 (3 words, 6 bytes)
│ └── IOI EPath: 20 6B 25 00 44 46 (Class 0x6B, Instance 0x4644)
→ Response: 01 00 (batch index = 1)
3. READ ALL: 4C 03 20 B2 25 00 21 00
├── Service: 0x4C (Read)
├── Path Size: 0x03 (3 words, 6 bytes)
└── Request Path
├── 20 B2 = Class 0xB2
└── 25 00 21 00 = Instance 0x0021 (buffer)
→ Response: 01 00 EE EE EE EE
├── 01 00 = batch index 1
└── EE EE EE EE = DINT value (tag 1 from batch 1)
4. REMOVE: 4F 03 20 B2 25 00 21 00 01 00 (optional step)
├── Service: 0x4F (Remove Tag)
├── Path Size: 0x03 (3 words, 6 bytes)
├── Request Path
│ ├── 20 B2 = Class 0xB2
│ └── 25 00 21 00 = Instance 0x0021 (buffer)
└── Tag Index: 01 00 (tag 1)
5. DELETE: 09 03 20 B2 25 00 21 00
├── Service: 0x09 (Delete)
├── Path Size: 0x03 (3 words, 6 bytes)
└── Request Path
├── 20 B2 = Class 0xB2
└── 25 00 21 00 = Instance 0x0021 (buffer)
→ Response: success