Skip to content

EtherCAT HAL

Robin Krämer edited this page Sep 29, 2026 · 4 revisions

ethercat_hal is a hardware abstraction layer for EtherCAT, built on ethercrab (the QiTech fork, pinned by git revision). It gives you three things:

  • A master that runs in its own thread. It brings the bus up (Init → PreOp → Op), exchanges the process image every cycle, keeps distributed clocks (DC) in sync, and records every state transition for diagnostics.
  • Lock-free cyclic data exchange between that thread and your application code.
  • Device drivers that turn raw process-image bits into typed values for specific terminals (Beckhoff, WAGO and Panasonic). Machine code talks to them through hardware-agnostic io traits.

Minimal example

This reads the two inputs of an EL1002. The full version is in examples/el1002_minimal.rs.

use bitvec::{order::Lsb0, slice::BitSlice};
use std::time::Duration;
use ethercat_hal::{
    BECKHOFF_VENDOR_ID, EtherCATState, init_ethercat,
    devices::{EthercatDevice, NewEthercatDevice, beckhoff_modules::el1002::{EL1002, EL1002_PRODUCT_ID}},
    io::digital_input::DigitalInputDevice,
};

let control = init_ethercat("eth0", None);          // spawns the master thread
let mut handle = control.app_handle;

control.channel.request_state_change(EtherCATState::PreOp)?;
while handle.get_state() != EtherCATState::PreOp { std::thread::sleep(Duration::from_millis(10)); }

control.channel.request_state_change(EtherCATState::Op)?;
while handle.get_state() != EtherCATState::Op { std::thread::sleep(Duration::from_millis(10)); }

let subdevices = handle.try_get_subdevices_vec_sync()?;
let mut el1002 = EL1002::new();

loop {
    if let Some(inputs) = handle.get_inputs() {
        for sd in &subdevices {
            if sd.vendor == BECKHOFF_VENDOR_ID && sd.product_id == EL1002_PRODUCT_ID {
                el1002.input(BitSlice::<u8, Lsb0>::from_slice(&inputs[sd.start_tx..sd.end_tx]))?;
            }
        }
    }
    println!("DI1={} DI2={}", el1002.get_input(0)?, el1002.get_input(1)?);
}

To run it, pass the name of the network interface that the EtherCAT bus is connected to. Run this from the repository root:

cargo run --example el1002_minimal -- eth0

The configured cargo runner (bin/run-linux) uses sudo setcap to grant the binary raw-socket and real-time capabilities. Expect a sudo prompt.

Documentation

Document Read it when you want to…
Architecture understand the threads, the state machine, and how data moves between the master and your code
Application guide write a program that brings up a bus, configures terminals, and runs a control loop
Writing device drivers understand how a driver works (using the EL1002 and EL2002 as examples) or add support for a new terminal
Derive macros (ethercat_hal_derive) look up exactly what PdoObject, TxPdo, RxPdo and EthercatDevice require and generate

Device reference

Only some terminals have their own page so far. For the rest, the driver source is the reference.

Terminal Page
EL6021 (serial RS422/RS485) EL6021
EL4732 (2 × analog output ±10 V, oversampling) EL4732
EL7031, EL7031-0030, EL7041-0052 (stepper motor) EL70x1 steppers
WAGO 750-354 coupler + 750 slot modules (750-455, 750-531, 750-554 as examples) WAGO 750

Module map

Module Contents
lib.rs init_ethercat, EtherCATAppHandle, EtherCATThreadChannel, MasterConfiguration, MetaSubdevice, EtherCATState
controller.rs the master's state machine and cyclic loop (runs on the EthercatStateMachine thread)
ethercat_helpers.rs the blocking request API on EtherCATThreadChannel (sdo_read, sdo_write, enable_dc_sync0, …)
al_diagnostics.rs TransitionReport, TransitionLog, SubDeviceAlStatus
pdo/ the PDO traits (RxPdo, TxPdo, PdoObject) and reusable PDO object types
devices/ one driver per terminal, plus the identity → driver lookup (device_from_subdevice_identity)
io/ hardware-agnostic traits such as DigitalInputDevice and DigitalOutputDevice
coe.rs, shared_config/ CoE (SDO) configuration traits and shared config structs
interface_discovery.rs lists network interfaces and tests which ones have an EtherCAT bus attached
machine_ident_read.rs reads and writes QiTech machine identification stored in terminal EEPROM

Tests

cd ethercat_hal && cargo test

ethercat_hal is not part of a Cargo workspace, so cargo test -p ethercat_hal from the repository root does not work. The tests cover PDO encoding and decoding and the helper converters. None of them need hardware.

Clone this wiki locally