Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork50
ethercrab-rs/ethercrab
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
A performant,async-first EtherCAT MainDevice written in pure Rust.
std(enabled by default) - exposes thestdmodule, containing helpers to run the TX/RXloop on desktop operating systems.defmt- enable logging with thedefmtcrate.log- enable logging with thelogcrate. This is enabled by defaultwhen thestdfeature is enabled.serde- enableserdeimpls for some public items.xdp- enable support for XDP on some (currently only Linux) systems.
Forno_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 anEK1100 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";cargorun--exampleek1100--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.constMAX_SUBDEVICES:usize =16;/// Maximum PDU data payload size - set this to the max PDI size or higher.constMAX_PDU_DATA:usize =1100;/// Maximum number of EtherCAT frames that can be in flight at any one time.constMAX_FRAMES:usize =16;/// Maximum total PDI length.constPDI_LEN:usize =64;staticPDU_STORAGE:PduStorage<MAX_FRAMES,MAX_PDU_DATA> =PduStorage::new();#[tokio::main]asyncfnmain() ->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"));letmut group = maindevice.init_single_group::<MAX_SUBDEVICES,PDI_LEN>(ethercat_now).await.expect("Init"); log::info!("Discovered {} SubDevices", group.len());for subdevicein group.iter(&maindevice){// Special case: if an EL3004 module is discovered, it needs some specific config during// init to function properlyif 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?;}}letmut group = group.into_op(&maindevice).await.expect("PRE-OP -> OP");for subdevicein 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());}letmut 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 oneformut subdevicein group.iter(&maindevice){letmut io = subdevice.io_raw_mut();for bytein io.outputs().iter_mut(){*byte = byte.wrapping_add(1);}} tick_interval.tick().await;}}
asyncAPI- Usable in
no_stdcontexts with no allocator required, as long as anasyncexecutor is available. - Autoconfigure SubDevices from their EEPROM (SII) data during startup
- Supports configuration using CoE data
- Safely usable in multi-threaded Linux systems with e.g.
smol,tokioorstd::threadandblock_on. - Support for
io_uringon Linux systems to improve performance and latency - Support for SDO read/writes to configure SubDevices
- Distributed clocks
- Detection of delays between SubDevices in topology
- Static drift compensation on startup
- Cyclic synchronisation during OP
- Basic support forCiA402/DS402 drives
- A higher level DS402 API for torque, position and velocity control of common servo drives ina more abstract way.
- Integration with LinuxCNC as a HAL component usingthe
linuxcnc-halcrate. - Load SubDevice configurations from ESI XML files
Thank you to everyone who has donated test equipment, time or money to the EtherCrab project! Wouldyou like to be in this list? Then please considerbecoming a Github sponsor!
- @nealsjoe generously donated an EK1100 with several IO modules fortesting with.
- Trisk Bio generously donated some additional Beckhoff modules and someoptical ethernet gear.
- Smark sent a $200 one time donation. Thank you!
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE orhttp://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT orhttp://opensource.org/licenses/MIT)
at your option.
About
A pure Rust EtherCAT MainDevice supporting std and no_std environments
Topics
Resources
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.