An EDS file is a device’s résumé. It is plain text, it ships with the hardware, and your configuration software reads it so it does not have to know anything about your device in advance.
Two industrial worlds use the same three letters for completely different files. CANopen has one EDS format. EtherNet/IP has another. They share an INI-style layout and nothing else — different section names, different fields, different meaning. A CANopen tool handed an EtherNet/IP EDS will simply reject it.
This guide reads both files the way you would actually open them: top to bottom, one section header at a time. Open one of your own vendor files alongside it and follow along.
Drop it into the online EDS File Viewer and keep it open in a second tab. It detects CANopen or EtherNet/IP automatically and lays out the same sections this guide walks through, so you can match each header to your device as you read.
Table of Contents
Where the file comes from
Three places, in order of how much you should trust them:
| Source | Trust | Note |
|---|---|---|
| Vendor download page, matched to the device revision | High | Check the revision matches the label on the hardware |
| Installed by the configuration tool | Medium | Often one file covering a whole product family |
| Bundled in an old project archive | Low | May predate a firmware change that added objects |
The file is not stored in the device. That matters: an EDS describes what the vendor says the device does. The device itself is the authority. When the two disagree, believe the device.
Part 1 — Reading a CANopen EDS
The CANopen EDS format is defined in CiA 306. It is an INI file: square-bracket section headers, key=value lines, semicolon comments.
[FileInfo]
Housekeeping. The one field worth reading is EDSVersion — 4.0 is what you will see on almost everything current.
[FileInfo]
FileName=GenericIO.eds
FileVersion=1
FileRevision=2
EDSVersion=4.0
Description=Generic 16-channel digital I/O node
CreatedBy=Engineering
FileRevision is the revision of the file, not the device. Vendors bump it when they fix a typo in a parameter name. Do not use it to identify hardware.
[DeviceInfo]
This is the identity block, and the first place to look when a device will not come up.
[DeviceInfo]
VendorName=Example Automation
VendorNumber=0x000002F1
ProductName=DIO-16 Node
ProductNumber=0x00000101
RevisionNumber=0x00010002
BaudRate_125=1
BaudRate_250=1
BaudRate_500=1
BaudRate_1000=1
NrOfRXPDO=1
NrOfTXPDO=1
LSS_Supported=1
VendorNumber, ProductNumber, and RevisionNumber should match what the device reports in object 0x1018 over SDO. If they do not, you have the wrong file for that hardware.
RevisionNumber packs two values into 32 bits: the upper 16 are the major revision, the lower 16 are the minor. 0x00010002 is major 1, minor 2.
The BaudRate_xxx lines are flags, not settings. A 1 means the device can run at that bit rate. It says nothing about what the device is currently set to.
LSS_Supported=1 tells you the node ID and bit rate can be set over the bus instead of with DIP switches. Worth knowing before you take the cabinet door off.
[MandatoryObjects], [OptionalObjects], [ManufacturerObjects]
Three index lists. They are a table of contents, not the content itself.
[MandatoryObjects]
SupportedObjects=3
1=0x1000
2=0x1001
3=0x1018
Every CANopen device has those three: device type, error register, and identity. If any is missing from the mandatory list, the file is malformed.
The manufacturer list is the one to read carefully. Anything in it lives in the 0x2000–0x5FFF range, which is vendor territory. That is where the useful device-specific settings hide — filter times, scaling, channel enables — and where two devices from different vendors will never match.
The object sections
Each object gets its own section, named after its index in hex.
[1017]
ParameterName=Producer heartbeat time
ObjectType=0x7
DataType=0x0006
AccessType=rw
DefaultValue=1000
PDOMapping=0
Six fields do the work:
| Field | What it tells you |
|---|---|
ParameterName | Display name. Cosmetic, but it is what your tool shows you |
ObjectType | 0x07 VAR, 0x08 ARRAY, 0x09 RECORD — whether the object has sub-indexes |
DataType | Numeric code, see the table below |
AccessType | ro, wo, rw, const, plus rwr and rww for objects readable or writable only in specific PDO contexts |
DefaultValue | Power-on value. May be an expression — see the $NODEID section below |
PDOMapping | 1 means this object can be placed in a PDO. 0 means SDO access only |
Data type codes come from CiA 301 and are the same in every EDS you will open:
| Code | Type | Code | Type |
|---|---|---|---|
| 0x0001 | BOOLEAN | 0x0007 | UNSIGNED32 |
| 0x0002 | INTEGER8 | 0x0008 | REAL32 |
| 0x0003 | INTEGER16 | 0x0009 | VISIBLE_STRING |
| 0x0004 | INTEGER32 | 0x000A | OCTET_STRING |
| 0x0005 | UNSIGNED8 | 0x000F | DOMAIN |
| 0x0006 | UNSIGNED16 | 0x0015 | INTEGER64 |
LowLimit and HighLimit show up on writable objects. Your tool uses them to reject a bad value before it goes on the bus. They are advisory — the device enforces its own limits and will send an SDO abort regardless.
Sub-index sections
An ARRAY or RECORD splits across several sections. The naming is the parent index, then sub, then the sub-index in hex.
[1018]
ParameterName=Identity object
ObjectType=0x9
SubNumber=5
[1018sub0]
ParameterName=Highest sub-index supported
DataType=0x0005
AccessType=ro
DefaultValue=4
[1018sub1]
ParameterName=Vendor-ID
DataType=0x0007
AccessType=ro
DefaultValue=0x000002F1
Sub-index 0 is always the count. On a RECORD it holds the highest sub-index supported; on an ARRAY it holds the number of entries. Read it before you read anything else in that object.
SubNumber in the parent section says how many sub-index sections follow — including sub 0. A mismatch between SubNumber and the sections actually present is a broken file, and some tools will refuse to load it.
CompactSubObj
An array of 64 identical channels would mean 65 sections. Compact storage collapses them:
[6000]
ParameterName=Read input 8-bit
ObjectType=0x8
DataType=0x0005
AccessType=ro
CompactSubObj=64
Sub-indexes 1 through 64 are implied. They all share the data type and access type of the parent. If you are diffing two EDS files and one looks half the size, check whether it uses compact storage.
The index ranges
Where an object sits tells you who defined it. This is the single most useful thing to know when reading a dictionary you have never seen before.
| Range | Area | Who owns it |
|---|---|---|
| 0x0000–0x0FFF | Data types | CiA |
| 0x1000–0x1FFF | Communication profile | CiA 301 — identical on every device |
| 0x2000–0x5FFF | Manufacturer-specific | The vendor. No portability |
| 0x6000–0x9FFF | Device profile | CiA device profile, e.g. 401 for I/O, 402 for drives |
| 0xA000–0xBFFF | Interface profile | CiA |
| 0xC000–0xFFFF | Reserved | — |
An object in 0x6000 does the same thing on every CiA 401 I/O module ever made. An object in 0x2000 does whatever that vendor decided. Plan your engineering accordingly.
PDO communication and mapping objects
Four blocks in the communication area, and they are the reason most people open an EDS at all.
| Range | What it is |
|---|---|
| 0x1400–0x15FF | RPDO communication parameters |
| 0x1600–0x17FF | RPDO mapping parameters |
| 0x1800–0x19FF | TPDO communication parameters |
| 0x1A00–0x1BFF | TPDO mapping parameters |
The offset is the PDO number. RPDO1 is 0x1400 and 0x1600. RPDO2 is 0x1401 and 0x1601. Same pattern for transmit.
The communication object holds the identifier and the timing:
[1800sub1]
ParameterName=COB-ID used by TPDO
DefaultValue=$NODEID+0x180
[1800sub2]
ParameterName=Transmission type
DefaultValue=254
[1800sub3]
ParameterName=Inhibit time
DefaultValue=100
[1800sub5]
ParameterName=Event timer
DefaultValue=500
Transmission type is a single byte that changes the entire behavior of the PDO:
| Value | Behavior |
|---|---|
| 0 | Synchronous, acyclic — transmitted after SYNC, only if the data changed |
| 1–240 | Synchronous, cyclic — transmitted every nth SYNC |
| 252 | Synchronous, RTR only |
| 253 | Asynchronous, RTR only |
| 254 | Asynchronous, manufacturer-specific event |
| 255 | Asynchronous, device-profile event |
Inhibit time is in units of 100 µs. A value of 100 is 10 ms — the minimum gap between two transmissions of that PDO. Event timer is in milliseconds and forces a transmission even when nothing changes.
The mapping object says what goes in the eight data bytes:
[1A00sub0]
ParameterName=Number of mapped objects
DefaultValue=2
[1A00sub1]
DefaultValue=0x60000108
[1A00sub2]
DefaultValue=0x60000208
Each mapping entry is one 32-bit word, split three ways:
0x60000108
6000 → index of the mapped object
01 → sub-index
08 → length in bits
So that PDO carries object 0x6000 sub 1 in the first byte and 0x6000 sub 2 in the second. Add the lengths of all entries — if the total goes past 64 bits, the device will reject the mapping with an SDO abort. That is a five-minute check in the file versus an hour on the bus.
$NODEID expressions
COB-IDs in an EDS are almost never hardcoded. The same file has to work for node 1 and node 100, so identifiers are written as arithmetic:
DefaultValue=$NODEID+0x200
On node 5 that is 0x205. On node 12 it is 0x20C. The offsets are the CANopen predefined connection set:
| Service | Base | Node 5 |
|---|---|---|
| EMCY | 0x080 + node | 0x085 |
| TPDO1 | 0x180 + node | 0x185 |
| RPDO1 | 0x200 + node | 0x205 |
| TPDO2 | 0x280 + node | 0x285 |
| RPDO2 | 0x300 + node | 0x305 |
| SDO server → client | 0x580 + node | 0x585 |
| SDO client → server | 0x600 + node | 0x605 |
| Heartbeat | 0x700 + node | 0x705 |
The COB-ID value also carries flags in its top bits. Bit 31 set means the PDO is disabled — which is why a COB-ID that looks like 0xC0000205 on an analyzer is not a valid identifier at all, it is a switched-off PDO. Bit 30 means RTR is not allowed. Bit 29 selects the 29-bit extended frame format.
[DummyUsage] and [Comments]
[DummyUsage] lists which dummy data types the device accepts as PDO padding. It matters when you need to align a value to a byte boundary in a mapping.
[Comments] is free text, numbered by line. Vendors sometimes put real information in there — supported firmware versions, known limitations. Worth a read.
Part 2 — Reading an EtherNet/IP EDS
The ODVA EDS format looks similar at a glance and parses completely differently. Entries end with a semicolon, not a newline, so a single entry can span twenty lines. Comments start with $ and run to end of line. Strings are double-quoted and can contain commas.
That means you cannot read this format line by line. You read it entry by entry.
[File]
Header block. DescText and Revision are the ones people check.
[File]
DescText = "Example EtherNet/IP Adapter";
CreateDate = 03-11-2024;
Revision = 1.1;
HomeURL = "http://www.example.com";
[Device]
The identity block, and the source of the electronic key.
[Device]
VendCode = 1298;
VendName = "Example Automation";
ProdType = 12;
ProdTypeStr = "Communications Adapter";
ProdCode = 257;
MajRev = 1;
MinRev = 2;
ProdName = "EIP-ADPT-16";
Catalog = "EIP-ADPT-16";
When a scanner opens a connection it can check four of these against what the device reports: vendor code, device type, product code, and revision. That check is the electronic key. Set it to exact match in your scanner and swap in a device with a different minor revision, and the connection fails before any data moves. The error is a connection failure, not a network failure — the cable is fine, the key does not match.
ProdType is a CIP device type code. A few of the common ones:
| Code | Device type | Code | Device type |
|---|---|---|---|
| 0x00 | Generic device | 0x0E | Programmable logic controller |
| 0x02 | AC drive | 0x10 | Position controller |
| 0x07 | General purpose discrete I/O | 0x16 | Motor starter |
| 0x0C | Communications adapter | 0x22 | Encoder |
Vendor codes are assigned by ODVA. The file carries the name in VendName, so you rarely need the number for anything except the key.
[Device Classification]
One line per supported network.
[Device Classification]
Class1 = EtherNetIP;
A device that also speaks DeviceNet or ControlNet will list those too. Some multiprotocol devices ship one EDS per network instead.
[Params]
Every configurable parameter, one entry each. The fields are positional, comma-separated, and mostly unlabeled — which is why this section is unreadable without either a tool or a lot of counting.
Param1 =
0, $ reserved
6, "20 04 24 64 30 03", $ link path size, link path
0x0000, $ descriptor
0xC7, $ data type
2, $ data size in bytes
"Input Assembly Size", $ name
"bytes", $ units
"Number of input bytes produced", $ help
1, 496, 8, $ min, max, default
,,,, $ scaling
,,,, $ links
0; $ decimal places
The data type is a CIP type code, not a CANopen one:
| Code | Type | Code | Type |
|---|---|---|---|
| 0xC1 | BOOL | 0xC7 | UINT |
| 0xC2 | SINT | 0xC8 | UDINT |
| 0xC3 | INT | 0xCA | REAL |
| 0xC4 | DINT | 0xD0 | STRING |
| 0xC6 | USINT | 0xDA | SHORT_STRING |
The descriptor word carries flags — whether the parameter is read only, whether it supports scaling, whether it has enumerated strings. If it does have enumerated strings, they appear as a separate EnumN entry with the same number:
Enum2 = 0, "Disabled",
1, "Enabled";
The link path is a CIP path in hex: class, instance, attribute. 20 04 24 64 30 03 reads as class 0x04 (assembly), instance 0x64 (100), attribute 0x03. That is how the parameter maps onto something the device actually exposes.
[Assembly]
The section that decides what your input and output tags contain.
Assem100 =
"Input Assembly",
"20 04 24 64 30 03",
8,
0x0000,
,
,
8, Param1,
8, Param2;
Name, path, size in bytes, descriptor, two reserved fields, then pairs: bit size and the parameter it references. An empty reference means padding.
Two things to check here. First, the member bit sizes should add up to the declared byte size — if they do not, there is undocumented padding. Second, the instance number in the section name (Assem100 is instance 100) is what you type into the scanner’s connection configuration. Pick the wrong instance and you get data, just not the data you wanted. That failure mode looks like corrupted values rather than a broken connection, which is why it costs so much time.
[Connection Manager]
Each entry describes one connection the device supports: trigger and transport bits, connection parameters, RPI limits, sizes, format codes, a name, and a path.
Connection1 =
0x04010002,
0x44640405,
,,,
,,,
,,
,,
"Exclusive Owner",
"Bidirectional class 1 connection",
"20 04 24 05 2C 96 2C 64";
The names tell you most of what you need. “Exclusive Owner” is a bidirectional class 1 connection that only one scanner can hold. “Input Only” and “Listen Only” are the read-paths other scanners use for the same device. The path at the end names the configuration, output, and input assembly instances the connection uses — which cross-checks against the [Assembly] section.
Vendors leave optional fields blank, so the commas run together. Count them carefully or let a parser do it.
[Port]
The physical or logical ports and their CIP paths. Short, and rarely the problem.
[Capacity]
Connection and packet-rate limits — how many class 1 connections the device supports, and the total packets per second it can handle. If you are fanning out a lot of I/O connections to one adapter, this is the section that tells you when you have asked for too much.
EDS or DCF?
A DCF is a device configuration file. Same format as a CANopen EDS, same sections, but with actual configured values written in for one specific node — plus a [DeviceComissioning] section carrying the node ID and bit rate.
An EDS says what the device can do. A DCF says what one particular device is set to do. If you are handed a DCF, someone has already made configuration decisions and written them down. Read it before you overwrite them.
What goes wrong
| Symptom | Likely cause in the file |
|---|---|
| Device will not appear in the tool’s catalog | Wrong format — a GSD, GSDML, or IODD given to a tool expecting an EDS |
| Device appears but rejects the download | EDS revision does not match device firmware; objects listed that the device does not have |
| CANopen connection fails on key check | VendorNumber, ProductNumber, or RevisionNumber does not match object 0x1018 |
| EtherNet/IP connection fails immediately | Electronic key mismatch on vendor, device type, product code, or revision |
| PDO mapping rejected with SDO abort | Mapped entries total more than 64 bits |
| PDO configured but nothing on the wire | Bit 31 set in the COB-ID — the PDO is disabled |
| Data arrives but the values are wrong | Wrong assembly instance selected, or a run/idle header the scanner is not expecting |
| Tool refuses to load the file | SubNumber does not match the sub-index sections present |
Quick reference: section names
| CANopen (CiA 306) | EtherNet/IP (ODVA) | Holds |
|---|---|---|
[FileInfo] | [File] | File housekeeping |
[DeviceInfo] | [Device] | Vendor, product, revision |
| — | [Device Classification] | Supported networks |
[MandatoryObjects] etc. | — | Index lists |
[1000], [1018sub1] | [Params] | The configurable content |
[1600], [1A00] | [Assembly] | What goes in the cyclic data |
[1400], [1800] | [Connection Manager] | Connection identity and timing |
| — | [Port], [Capacity] | Ports and limits |
[Comments] | — | Free text |
Frequently asked questions
Can I edit an EDS file by hand? You can — it is text. Whether you should is another matter. Object counts, SubNumber fields, and sub-index-0 values all have to stay consistent with the content, and most vendor tools will refuse a file they did not write. If you need a device to behave differently, change the device, not the description of it.
Why does one EDS cover several part numbers? Vendors ship one file per product family to cut support load. The unused objects stay in the file. This is why an EDS can show objects your device returns an abort on — the file is describing a sibling.
Do I need the EDS to talk to the device? No. The EDS is for the engineering tool, not the protocol. A CANopen master can read any object by index over SDO with no file at all, and a CIP scanner can open a connection given the right instance numbers. The file saves you from having to know those numbers.
What is the difference between an EDS and a GSD or GSDML? Same job, different network. GSD is PROFIBUS, GSDML is PROFINET, IODD is IO-Link, EDS is CANopen and the CIP family. None of them are interchangeable.
The EDS says an object is read/write but the device rejects the write. Believe the device. AccessType in the file is documentation. The device enforces access in firmware, and some objects are only writable in specific NMT states — pre-operational, typically. Check the state before you check the file.
How do I know which assembly instance to use? The [Assembly] section names them, and the connection path in [Connection Manager] shows which instances each connection type uses. Exclusive Owner connections normally use both an input and an output instance; Input Only uses the input instance with a heartbeat instance for the output.
