ethercat-devices (0.1.0)

Published 2026-06-12 06:18:29 +00:00 by madsludvig

Installation

[registry]
default = "forgejo"

[registries.forgejo]
index = "sparse+" # Sparse index
# index = "" # Git

[net]
git-fetch-with-cli = true
cargo add ethercat-devices@0.1.0

About this package

Typed catalog of EtherCAT terminals + declarative topology macro on top of ethercrab.

ethercat-devices

Typed EtherCAT bus access on top of ethercrab, plus a topology! proc-macro: declare the physical chain and name the bits you care about, get a typed struct back — no string lookups or channel numbers in application code.

On connect() every declared slot is validated against the hardware (vendor/product id, position by position), so a miswired bus fails loudly at startup instead of silently driving the wrong terminal.

Example

use ethercat_devices::{Ek1100, El1012, El2002, El7031, el7031, topology};

topology! {
    pub struct MyBus;

    Ek1100;                                          // declared + validated, no accessor
    El1012 { 0 => photocell, 1 => part_present };     // named input bits
    El2002 { 0 => gripper,   1 => lamp };             // named output bits
    El7031 => stepper;                                // typed handle: bus.stepper()
    skip(8);                                          // unknown terminal, 8 PDI bytes
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let bus = MyBus::connect("enp7s0").await?;

    // PRE-OP: per-device configuration through the slot field.
    bus.bus()
        .configure_stepper(bus.stepper, &el7031::Config::default())
        .await?;

    let bus = bus.into_op().await?;

    loop {
        bus.tx_rx().await?;                            // one process-data exchange

        if bus.photocell().read()? {                   // input bit
            bus.gripper().write(true)?;                // output bit
            bus.stepper()?.command(|c| {               // specialised handle
                c.set_enable(true);
                c.set_velocity(1800);
            });
        }
    }
}

Everything static is checked at compile time:

  • bus.photocell().write(true) — doesn't compile: the EL1012 is an input terminal (.write only exists for DigitalOutDevices).
  • El1012 { 5 => foo } — doesn't compile when used: bit 5 is out of range for a 2-channel terminal.
  • A typo'd signal name is a plain missing-method error.

Forms

Line Meaning
Device; On the chain, identity-validated, no accessor.
Device { N => name, ... }; One bus.name() per bit, returning a BitRef with .read() / .write(bool) per the device's direction.
Device => name; Typed per-cycle handle via the HasHandle trait (e.g. the EL7031 stepper).
skip(N); Unknown/unsupported terminal: no identity check, but its PDI must total N bytes (verified at OP transition).

Adding a device

No macro edits needed:

  • New digital terminal → marker type + impl Device (identity) + impl DigitalInDevice / impl DigitalOutDevice (channel count).
  • New specialised terminal → impl HasHandle mapping it to a per-cycle handle type.

Workspace

  • . — device catalog and bus (ethercat-devices)
  • macros/ — the topology! proc-macro (ethercat-devices-macros)

EtherCAT uses raw Ethernet frames, so binaries need sudo setcap cap_net_raw,cap_net_admin=eip <binary>.

Dependencies

ID Version
anyhow ^1
ethercat-devices-macros ^0.1.0
ethercrab ^0.7.1
log ^0.4
tokio ^1
Details
Cargo
2026-06-12 06:18:29 +00:00
0
MIT OR Apache-2.0
23 KiB
Assets (1)
Versions (5) View all
0.1.4 2026-06-12
0.1.3 2026-06-12
0.1.2 2026-06-12
0.1.1 2026-06-12
0.1.0 2026-06-12