Replies: 1 comment 8 replies
-
|
@wzj7531 thx for sharing your approach and your willingness to contribute this to Eclipse 4diac. I must say: wow this is amazing. I like the approach a lot. Especially the idea of a generic implementation for the device FBs in 4diac FORTE and an automatic generation of the required blocks in 4diac IDE is a brilliant idea. I'm currently not sure if the type attribute is the best place as others may have used this for descriptions. We have since 3.0 a very nice infrastructure for attributes which could handle this a bit cleaner. But this is something we can discuss in a later stage. Having this in Eclipse 4diac would be a major step forward. As you have seen our contribution guides we are handling contributions according to the Eclipse Project Handbook. For a contribution like yours there are two additional things that we should consider:
I hope this is not overwhelming. I'm happy to support you on all of the stages to make your contribution a smooth one. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
1. Preface
First of all, I would like to thank the founding organizations, members, and all contributors of the 4diac project! 4diac is an excellent open-source platform. I have learned a great deal from it, which has been very helpful for my career development.
I am a C# programmer from China who writes host applications for manufacturing companies. In 2024, my manager asked me to learn the 4diac platform because he had been following it for a long time and was very interested in the IEC 61499 standard. Later, we received a project that required building a hardware-in-the-loop simulation platform. We chose 4diac as the foundation for two reasons: it already implements major industrial data acquisition protocols, and its graphical function-block programming feels similar to using Simulink. We implemented it as follows:
After struggling for a while, we got the above working, but simulation turned out to be harder than we expected…
To explore 4diac more deeply, my manager gave me another task: build the main control program and implement Modbus RTU for data acquisition from I/O cards. At first I had no clear direction and could only read the source code, hoping to find answers. After consulting many resources, I found a community Q&A about how forte’s master–slave architecture uses Modbus TCP. I pulled and read that code, and the door to 4diac opened for me:
I eventually completed that task, but I became even more curious about 4diac. Although today’s topic is EtherCAT, those two stories already describe the basic approach for implementing EtherCAT:
Of course, I ran into many problems during implementation. That is what this article is mainly about.
2. EtherCAT
EtherCAT is inherently master–slave: the master and slaves are connected over Ethernet, as shown below:
In practice, slave devices can also host plug-in modules. A slave with mounted modules is called a coupler:
The coupler and its mounted modules still form a single EtherCAT slave as a whole.
I do not want to go too deep into the EtherCAT protocol itself, because I am not an EtherCAT expert. However, we need to know the following:
So we keep EtherCAT complexity inside the EtherCAT Master driver. Do not unleash that “demon” yourself—focus only on how to use the API it provides.
From here on, we move into concrete implementation work. Before that, a brief note on my development environment: I use a laptop running Ubuntu 22.04. Most 4diac-forte coding is done in VS Code; 4diac-ide development seems to require Eclipse Modeling Tools.
2.1 Building the EtherCAT Master
To use the EtherCAT library, you must build it from the [EtherCAT Master source](https://gitlab.com/etherlab.org/ethercat). For building the library, see the [IgH EtherCAT Master Installation Guide](https://github.com/veysiadn/IgHEtherCATImplementation). Please note:
Assuming the build succeeded, run:
to start the EtherCAT driver. To check whether the driver started successfully, run:
To inspect master information, run:
Below is sample output from the commands above. Your output should look similar:
Now check how many slaves are connected to the master:
(base) hyperion@Jarvis:~$ ethercat slave 0 0:0 PREOP + CN-8033 EtherCAT AdaptorThe output shows one slave named "CN-8033 EtherCAT Adapter". The first
0is the slave position;0:0is alias and position offset;PREOPis the slave state.Next, inspect the slave’s PDOs:
From the PDO listing you can see that CN-8033 hosts four modules: CT-222F, CT-4234, CT-121F, and CT-3238.
2.2 EtherCAT Master API
You can learn how to call the API by reading the examples shipped with the EtherCAT Master. The main points in [examples/user/main.c](https://gitlab.com/etherlab.org/ethercat/-/blob/stable-1.6/examples/user/main.c?ref_type=heads) are:
ecrt.h; the userspace API and data structures are defined theremain:ecrt_request_masterto obtain a master, thenecrt_master_create_domainto create a domainecrt_master_slave_configandecrt_slave_config_pdosfor slave and PDO configuration. The example configures four slaves: EL3102, EL4102, EL2032, and EK1100. EK1100 is only a coupler and registers no PDOsecrt_domain_reg_pdo_entry_listto obtain each PDO entry’s offset in the domainAlthough simple, the code in
main.cis the core of our EtherCAT implementation on 4diac.2.3 EtherCAT Master as a forte Dependency
You can of course build the EtherCAT Master with the same toolchain used for forte and link the resulting static library and headers via CMakeLists.txt. I strongly recommend adding the EtherCAT Master as a forte dependency instead. That greatly simplifies cross-compilation and reduces configuration work—provided you build forte with
4diac-fbe. Add an ethercat recipe under4diac-fbe/dependencies/recipes:ethercatfolder underrecipesbuild.cmakeandpackage.txtinside that folderThen, whenever EtherCAT IO is enabled, forte’s build will first build ethercat with the same toolchain and use it as a dependency.
3. Adding a Plugin to 4diac-ide
EtherCAT masters and slaves can be wired in several ways. This article covers only the simplest topology, shown below:
ECMaster— master function blockECSlave— slave function blockECCoupler— coupler function blockECModule— module function blockECBusAdapter— adapter between master and slaves, and between slavesECSlaveConfig— slave configuration structECMaster,ECBusAdapter, andECSlaveConfighave fixed interfaces.ECCouplerandECSlavedepend on the specific device (from ESI).(1) ECMaster — represents the master. Its interface is shown below:
MasterIndex: master index, default 0UpdateInterval: polling interval, default 40 µsINIT: triggers master initializationREQ: whenEnableis true, starts polling; otherwise stops polling(2) ECBusAdapter — adapter linking master to slaves and slaves to each other. Its interface is shown below:
(3) ECSlaveConfig.dtp — slave configuration struct:
This information is later used by the EtherCAT driver to configure slaves.
3.1 ESI Files
Slave vendors typically ship an ESI (EtherCAT Slave Information) file that describes:
Basic device identification
Process Data Object (PDO) mapping
Object Dictionary & SDO
Mailbox & Sync Manager
Mailbox protocols
Distributed Clocks (DC)
On the IDE side we mainly care about device identification and PDO mapping.
I split the
ECSlaveandECModuleinterfaces into a fixed part:MAPMAPOQI,Config, bus/module adaptersQO,STATUS,BusAdapterOut,ModuleAdapterOutThe dynamic part is mostly extra DIs: the plugin reads the ESI and maps RxPDO entries to
OUT_xand TxPDO entries toIN_x(x = index). The 4diac-ide plugin imports ESI and generates function block types.(1) Right-click the project Type Library → Import → ESI File Import wizard → Next:
(2) Browse and select the ESI file:
(3) Click Finish. Slave function blocks appear under Type Library:
Inovanceis the vendor name. The folderGL_RTU_ECTis a slave category containing theGL20_RTU_ECTcoupler and compatible modules.You can now build the application configuration:
3.2 Generic Function Blocks
Because slave function blocks have a variable number of DIs, we use 4diac’s generic FB mechanism. For example, when forte receives a request to create
CLIENT_2_1, it automatically gets two inputsSD_1,SD_2and one outputRD_1. We do not name generated slavesECSlave_x_y/ECCoupler_x_y/ECModule_x_y; the FB type name should come from the ESITypeattribute so different slaves remain distinguishable. OpenGL20_RTU_ECTas text:I changed some 4diac-ide deployment code: if the Identification node’s
Typeattribute is non-empty, that value is used as the base class for the generic FB. When the IDE deploys an instance, forte does not createGL20_RTU_ECTdirectly; it creates something likeeclipse::io::ethercat::ECCoupler_0_0.Here
ECCoupler_0_0meansGL20_RTU_ECTis anECCouplerwith no dynamic PDO ports (0 inputs, 0 outputs in the generic suffix). UnlikeCLIENT_x_y, for EtherCAT slave generics the generatedIN_xandOUT_yare all DIs, because they only carry IO mapping names (WSTRING), not real process I/O pins.4. Adding an EtherCAT Module to 4diac-forte
We can now add an
ethercatmodule to4diac-forte. I designed it mainly by following the existingembrickmodule.Like
embrick, theethercatmodule follows forte’s IO layering:Source code lives under
forte/modules/ethercat/:ethercat/
├── handler/ # ECBusHandler — bus controller
├── slave/ # ECSlaveHandler, ECDeviceHandler, ECModuleHandler, ECSlaveHandle
│ └── model/ # ECDeviceModel — IgH PDO/Sync/EntryReg structures
├── types/ # ECMaster, ECSlave, ECCoupler, ECModule, ECBusAdapter FBs
├── structs/ # ECSlaveConfig, ECModuleConfig struct types
└── utils/ # EsiFileParser — runtime ESI parsing
4.1 Enabling the ethercat Module Build
In
4diac-fbe, enable EtherCAT IO via a configuration file. Exampleconfigurations/ethercat.txt:IO=ETHERCATenables CMake optionFORTE_MODULE_ETHERCAT.DEPS=ethercatbuilds the IgH EtherCAT Master userspace library via the cget recipe (dependencies/recipes/ethercat/,--disable-kernel, no kernel tree required).tinyxmlparses ESI at runtime.4.2 ECBusHandler: Bus Controller
ECBusHandlerextendsIODeviceMultiControllerand is the core of EtherCAT communication. Its lifecycle has three phases:(1) init() — request master and domain
Only basic IgH resources are allocated; no slaves are configured yet.
(2) prepareLoop() — configure slaves and activate master
After all SlaveConfigFBs finish initialization,
runLoop()callsprepareLoop(). It iteratesmDevicesand for eachECDeviceHandler(skippingECModule) performs:ecrt_master_slave_config()— match slave by Alias, Position, VendorId, ProductCodeecrt_slave_config_pdos()— apply Sync/PDO fromECDeviceModelecrt_domain_reg_pdo_entry_list()— register PDO entries; IgH fills domain byte offsetsecrt_master_activate()— activate master (OP state)ecrt_domain_data()— obtain domain process data pointermECDomainPdModules on a coupler (
ECModuleHandler) do not callecrt_master_slave_configseparately. Their PDO entries are merged into the parentECDeviceHandler’sECDeviceModeland registered as one EtherCAT slave.(3) runLoop() — cyclic exchange
The main loop uses
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, ...). The period isECMaster.UpdateIntervalin microseconds. Each cycle:The
Enableinput controls real exchange viaenableECCycle(). WhenEnable=false, the loop still advances wake-up time but skips receive/send, pausing polling without tearing down the master.4.3 ECSlaveHandler Hierarchy
Each
ECSlaveHandlermaintains two process image buffers:mUpdateSendImage: master → slave (RxPDO / output direction)mUpdateRecvImage: slave → master (TxPDO / input direction)update()runs every cycle fromECBusHandler: sync domain data to handles, then detect changes on input handles with observers and fireonChange().4.4 ECSlaveHandle and IOMapper
ECSlaveHandleextendsIOHandleand bridges application FBs (e.g.E_PERMIT,E_SWITCH) to EtherCAT process data.ECBusHandler::createIOHandle()picks the IEC type fromHandleDescriptor::mByteLength:Besides
mId,mDirection, andmSlaveIndex, EtherCAT addsmOffset(byte offset in the slave image) andmByteLength.Data path:
On the
MAPevent, SlaveConfigFB reads mapping names fromIN_x/OUT_x(WSTRING).EsiFileParserparses ESI and callsinitHandle(). Empty mapping IDs are skipped so you can map only some PDO entries.4.5 ECDeviceModel: PDO Configuration Bridge
ECDeviceModelsits between ESI and IgH API structures:mSyncList: sync managers (SM2 = Outputs, SM3 = Inputs)mPdoList: PDO index and directionmPdoEntryList: entry Index, SubIndex, BitLengthmEntryRegList: domain registration entries pointing toECSlaveHandle::mECDomainDataOffsetmSlotIndexInc/mSlotPdoInc: coupler slot increments from ESI<Slots>getSyncs()andgetDomainRegs()buildec_sync_info_t[]andec_pdo_entry_reg_t[]duringprepareLoop().getSyncs()uses fixed SM2 (output) and SM3 (input), matching common vendor layouts (e.g. Beckhoff).4.6 EsiFileParser: Runtime ESI Parsing
The IDE import produces FB type definitions; forte still needs ESI at runtime for PDO Index, SubIndex, BitLen, etc.
EsiFileParseris a singleton that scansdevices/next to the executable at startup and indexes all.xmlESI files by:ProductCodefor devicesModuleIdentfor modulesOn SlaveConfigFB initialization:
FORTE_ECSlave::init()callsloadDevice(ProductCode)to verify ESI existsinitHandles()callsinitDeviceIOHandles()to parse TxPdo/RxPdo, create handles, fillECDeviceModelFORTE_ECModule::initHandles()callsinitModuleIOHandles()to merge module PDO into the parentECDeviceHandlerModule PDO indices are offset by slot:
Some vendors’ ESI entry indices do not match firmware.
EsiFileParserincludes corrections (e.g. Inovance −1, Odot +1). If a new vendor misbehaves, check whether a similar correction is needed.Deploying ESI files: place ESI XML next to the forte executable under
devices/. Example:4.7 Initialization Chain and Slave Indexing
Initialization follows forte’s adapter chain:
Each SlaveConfigFB runs
handleInitEvent()on adapterINIT:ECMasterviaMasterIdmIndexfromBusAdapterIn().var_IndexcreateSlaveHandler()registers withECBusHandlerinit()loads ESIQI=true,initHandles()binds IOINITto the next slave viaBusAdapterOutECModule::initHandles()finds the parent viabus.getSlave(mIndex / 100 - 1)and registers module PDO on the parent model. The coupler also forwards INIT onModuleAdapterOutwith index(parent index + 1) × 100.4.8 Function Blocks and Handlers
4.9 End-to-End Data Exchange Sequence
Linking IDE configuration, forte runtime, and the physical bus:
a. IgH receive → domain process data updated
b. ECSlaveHandle copies domain data into image buffers
c. On input change, notify IOMapper observers
d. Application FBs read/write via IOMapper
e. ECSlaveHandle writes output image to domain
f. IgH send → data reaches slaves
5. Current Limitations and Future Work
CMakeLists.txtreturns on non-Linux; depends on IgH master andclock_nanosleepECSlaveprepareLoop()callsecrt_master_activate()per device; multi-slave setups may need a single activate after all slaves are configuredPossible next steps:
GL20_AI_Ch0)WORK_COUNTERdiagnostic FBs when real-time scheduling is enabled6. Summary
Integrating EtherCAT on 4diac boils down to:
IN_x/OUT_xECBusHandlerfor IgH cyclic exchange;EsiFileParserturns ESI PDO data into IgH config and IO mappings at runtime4diac-fbecget recipe bundles the EtherCAT userspace library as a forte dependency for easier cross-compilationEtherCAT complexity stays inside the IgH driver and
ECBusHandler. Application developers wire FBs and mapping names like Modbus or embrick. For hardware-in-the-loop platforms that must integrate many industrial IO protocols quickly, this protocol-agnostic IO mapping approach is highly reusable.Slave index rules:
Beta Was this translation helpful? Give feedback.
All reactions