Crate ethercrab

Source
Expand description

A performant, async-first EtherCAT MainDevice written in pure Rust.

§Crate features

  • std (enabled by default) - exposes the std module, containing helpers to run the TX/RX loop on desktop operating systems.
  • defmt - enable logging with the defmt crate.
  • log - enable logging with the log crate. This is enabled by default when the std feature is enabled.
  • serde - enable serde impls for some public items.

For no_std targets, it is recommended to add this crate with

cargo add --no-default-features --features defmt

§Examples

This example increments the output bytes of all detected SubDevices every tick. It is tested on an EK1100 with output modules but may work on other basic SubDevices.

Run with e.g.

Linux

RUST_LOG=debug cargo run --example ek1100 --release -- eth0

Windows

$env:RUST_LOG="debug" ; cargo run --example ek1100 --release -- '\Device\NPF_{FF0ACEE6-E8CD-48D5-A399-619CD2340465}'
use env_logger::Env;
use ethercrab::{
    error::Error, std::{ethercat_now, tx_rx_task}, MainDevice, MainDeviceConfig, PduStorage, Timeouts
};
use std::{sync::Arc, time::Duration};
use tokio::time::MissedTickBehavior;

/// Maximum number of SubDevices that can be stored. This must be a power of 2 greater than 1.
const MAX_SUBDEVICES: usize = 16;
/// Maximum PDU data payload size - set this to the max PDI size or higher.
const MAX_PDU_DATA: usize = 1100;
/// Maximum number of EtherCAT frames that can be in flight at any one time.
const MAX_FRAMES: usize = 16;
/// Maximum total PDI length.
const PDI_LEN: usize = 64;

static PDU_STORAGE: PduStorage<MAX_FRAMES, MAX_PDU_DATA> = PduStorage::new();

#[tokio::main]
async fn main() -> Result<(), Error> {
    env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();

    let interface = std::env::args()
        .nth(1)
        .expect("Provide network interface as first argument.");

    log::info!("Starting EK1100 demo...");
    log::info!("Ensure an EK1100 is the first SubDevice, with any number of modules connected after");
    log::info!("Run with RUST_LOG=ethercrab=debug or =trace for debug information");

    let (tx, rx, pdu_loop) = PDU_STORAGE.try_split().expect("can only split once");

    let maindevice = Arc::new(MainDevice::new(
        pdu_loop,
        Timeouts {
            wait_loop_delay: Duration::from_millis(2),
            mailbox_response: Duration::from_millis(1000),
            ..Default::default()
        },
        MainDeviceConfig::default(),
    ));

    tokio::spawn(tx_rx_task(&interface, tx, rx).expect("spawn TX/RX task"));

    let mut group = maindevice
        .init_single_group::<MAX_SUBDEVICES, PDI_LEN>(ethercat_now)
        .await
        .expect("Init");

    log::info!("Discovered {} SubDevices", group.len());

    for subdevice in group.iter(&maindevice) {
        // Special case: if an EL3004 module is discovered, it needs some specific config during
        // init to function properly
        if subdevice.name() == "EL3004" {
            log::info!("Found EL3004. Configuring...");

            subdevice.sdo_write(0x1c12, 0, 0u8).await?;
            subdevice.sdo_write(0x1c13, 0, 0u8).await?;

            subdevice.sdo_write(0x1c13, 1, 0x1a00u16).await?;
            subdevice.sdo_write(0x1c13, 2, 0x1a02u16).await?;
            subdevice.sdo_write(0x1c13, 3, 0x1a04u16).await?;
            subdevice.sdo_write(0x1c13, 4, 0x1a06u16).await?;
            subdevice.sdo_write(0x1c13, 0, 4u8).await?;
        }
    }

    let mut group = group.into_op(&maindevice).await.expect("PRE-OP -> OP");

    for subdevice in group.iter(&maindevice) {
        let io = subdevice.io_raw();

        log::info!(
            "-> SubDevice {:#06x} {} inputs: {} bytes, outputs: {} bytes",
            subdevice.configured_address(),
            subdevice.name(),
            io.inputs().len(),
            io.outputs().len()
        );
    }

    let mut tick_interval = tokio::time::interval(Duration::from_millis(5));
    tick_interval.set_missed_tick_behavior(MissedTickBehavior::Skip);

    loop {
        group.tx_rx(&maindevice).await.expect("TX/RX");

        // Increment every output byte for every SubDevice by one
        for mut subdevice in group.iter(&maindevice) {
            let mut io = subdevice.io_raw_mut();

            for byte in io.outputs().iter_mut() {
                *byte = byte.wrapping_add(1);
            }
        }

        tick_interval.tick().await;
    }
}

Re-exports§

pub use subdevice_group::SubDeviceGroup;
pub use subdevice_group::TxRxResponse;

Modules§

error
EtherCrab error types.
std
Items required for running in std environments.
subdevice_group
A group of SubDevices.

Structs§

MainDevice
The main EtherCAT controller.
MainDeviceConfig
Configuration passed to MainDevice.
PduLoop
The core EtherCrab network communications driver.
PduRx
EtherCAT frame receive adapter.
PduStorage
Stores PDU frames that are currently being prepared to send, in flight, or being received and processed.
PduTx
EtherCAT frame transmit adapter.
SendableFrame
An EtherCAT frame that is ready to be sent over the network.
SubDevice
SubDevice device metadata. See SubDeviceRef for richer behaviour.
SubDeviceIdentity
SubDevice identity information (vendor ID, product ID, etc).
SubDevicePdi
Process Data Image (PDI) segments for a given SubDevice.
SubDeviceRef
A wrapper around a SubDevice and additional state for richer behaviour.
Timeouts
Timeout configuration for the EtherCrab master.
WrappedRead
A wrapped version of a Reads exposing a builder API used to send/receive data over the wire.
WrappedWrite
A wrapped version of a Writes exposing a builder API used to send/receive data over the wire.

Enums§

AlStatusCode
AL (Application Layer) Status Code.
Command
PDU command.
DcSupport
SubDevice DC support status.
DcSync
DC sync configuration for a SubDevice.
Reads
Read commands that send no data.
ReceiveAction
What happened to a received Ethernet frame.
RegisterAddress
SubDevice device register address abstraction.
RetryBehaviour
Network communication retry policy.
SubDeviceState
AL (application layer) state for a single SubDevice.
SubIndex
Subindex access.
Writes
Write commands.

Traits§

EtherCrabWireRead
A type to be received from the wire, according to EtherCAT spec rules (packed bits, little endian).
EtherCrabWireReadSized
A readable type that has a size known at compile time.
EtherCrabWireReadWrite
A type that can be both written to the wire and read back from it.
EtherCrabWireSized
Implemented for types with a known size at compile time.
EtherCrabWireWrite
A type to be sent/received on the wire, according to EtherCAT spec rules (packed bits, little endian).
EtherCrabWireWriteSized
Implemented for writeable types with a known size at compile time.

Derive Macros§

EtherCrabWireRead
Items that can only be read from the wire.
EtherCrabWireReadWrite
Items that can be written to and read from the wire.
EtherCrabWireWrite
Items that can only be written to the wire.