A performant, async
-first EtherCAT master written in pure Rust.
Are you looking to use Rust in your next EtherCAT deployment? Commercial support for EtherCrab is available! Send me a message at [james@wapl.es](mailto:james@wapl.es) to get started with a free consulting call.
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
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 (i, o) = subdevice.io_raw();
log::info!(
"-> SubDevice {:#06x} {} inputs: {} bytes, outputs: {} bytes",
subdevice.configured_address(),
subdevice.name(),
i.len(),
o.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 (_i, o) = subdevice.io_raw_mut();
for byte in o.iter_mut() {
*byte = byte.wrapping_add(1);
}
}
tick_interval.tick().await;
}
}
async
APIno_std
contexts with no allocator required, as long as an async
executor is available.
smol
, tokio
or std::thread
and
block_on
.io_uring
on Linux systems to improve performance and latencylinuxcnc-hal
crate.Thank you to everyone who has donated test equipment, time or money to the EtherCrab project! Would you like to be in this list? Then please consider becoming a Github sponsor!
Licensed under either of
at your option.