#ethercat #automation #fieldbus #beckhoff #soem

无std ethercrab

纯Rust编写的、与no_std兼容的EtherCAT主站

17个不稳定版本 (4个破坏性版本)

0.5.0 2024年7月28日
0.4.1 2024年4月5日
0.4.0 2024年3月31日
0.3.5 2023年12月22日
0.1.0 2023年1月2日

网络编程中排名第114

Download history 7/week @ 2024-04-29 8/week @ 2024-05-06 3/week @ 2024-05-13 21/week @ 2024-05-20 202/week @ 2024-05-27 55/week @ 2024-06-03 25/week @ 2024-06-10 37/week @ 2024-06-17 22/week @ 2024-06-24 156/week @ 2024-07-01 62/week @ 2024-07-08 63/week @ 2024-07-15 126/week @ 2024-07-22 255/week @ 2024-07-29 47/week @ 2024-08-05 55/week @ 2024-08-12

每月下载量:492次

MIT/Apache

745KB
15K SLoC

EtherCrab

Build Status Crates.io Docs.rs Matrix chat

使用纯Rust编写的性能高、以异步优先的EtherCAT主站。

您是否想在下一个EtherCAT部署中使用Rust?EtherCrab提供商业支持!发送邮件至[email protected]开始免费咨询。

库功能

  • std(默认启用)- 导出std模块,包含在桌面操作系统上运行TX/RX循环的辅助工具。
  • defmt - 使用defmt crate启用日志记录。
  • log - 使用log crate启用日志记录。在启用std功能时默认启用。
  • serde - 为一些公共项启用serde实现。

对于no_std目标,建议使用以下方式添加此crate:

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

示例

此示例在每个滴答中增加所有检测到的子设备的输出字节数。它在带有输出模块的EK1100上进行了测试,但可能在其他基本子设备上也适用。

使用例如:

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;
    }
}

社区

我们在Matrix上!

当前和未来功能

  • async API
  • 在没有分配器的no_std环境中使用,只要有一个可用的async执行器即可。
  • 启动时从其EEPROM(SII)数据自动配置子设备
    • 支持使用CoE数据进行配置
  • 在多线程Linux系统(例如使用smoltokiostd::threadblock_on)中安全使用。
  • 在Linux系统上支持io_uring以提高性能和降低延迟
  • 支持对子设备进行SDO读写以配置
  • 分布式时钟
    • 检测拓扑中子设备之间的延迟
    • 启动时进行静态漂移补偿
    • 在OP期间进行循环同步
  • CiA402/DS402驱动的基本支持
    • 为常见伺服电机的扭矩、位置和速度控制提供更抽象的DS402 API。
  • 使用linuxcnc-halcrate将LinuxCNC集成为一个HAL组件。
  • 从ESI XML文件中加载子设备配置

赞助商

GitHub Sponsors

感谢所有向EtherCrab项目捐赠测试设备、时间和金钱的人!您想加入这个名单吗?那么请考虑成为Github赞助商

  • @nealsjoe慷慨捐赠了EK1100和一些IO模块进行测试。
  • Trisk Bio慷慨捐赠了一些额外的Beckhoff模块和一些光纤以太网设备。
  • Smark捐赠了一次性200美元。谢谢!

许可证

根据您的选择,许可如下:

依赖项

~9–21MB
~305K SLoC