{"id":13423319,"url":"https://github.com/ethercrab-rs/ethercrab","last_synced_at":"2025-03-15T17:31:42.726Z","repository":{"id":41745773,"uuid":"500086737","full_name":"ethercrab-rs/ethercrab","owner":"ethercrab-rs","description":"EtherCAT master written in pure Rust","archived":false,"fork":false,"pushed_at":"2024-04-06T18:53:28.000Z","size":25707,"stargazers_count":191,"open_issues_count":12,"forks_count":14,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-04-14T09:16:06.876Z","etag":null,"topics":["ethercat","ethercat-master","igh-ethercat-master","industrial-automation","rust","rust-embedded","soem"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ethercrab-rs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null},"funding":{"github":["jamwaffles"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":"jamwaffles","issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2022-06-05T12:07:17.000Z","updated_at":"2024-07-28T11:20:12.808Z","dependencies_parsed_at":"2023-12-10T13:26:27.866Z","dependency_job_id":"945f19ed-53cb-41c2-80f8-9ed14a942dc7","html_url":"https://github.com/ethercrab-rs/ethercrab","commit_stats":{"total_commits":521,"total_committers":1,"mean_commits":521.0,"dds":0.0,"last_synced_commit":"fcf0dc0c7ba906dc62b5c0445f57e3bbddae8941"},"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethercrab-rs%2Fethercrab","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethercrab-rs%2Fethercrab/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethercrab-rs%2Fethercrab/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethercrab-rs%2Fethercrab/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ethercrab-rs","download_url":"https://codeload.github.com/ethercrab-rs/ethercrab/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243766801,"owners_count":20344817,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ethercat","ethercat-master","igh-ethercat-master","industrial-automation","rust","rust-embedded","soem"],"created_at":"2024-07-31T00:00:30.574Z","updated_at":"2025-03-15T17:31:42.682Z","avatar_url":"https://github.com/ethercrab-rs.png","language":"Rust","readme":"# EtherCrab\n\n[![Build Status](https://circleci.com/gh/ethercrab-rs/ethercrab/tree/master.svg?style=shield)](https://circleci.com/gh/ethercrab-rs/ethercrab/tree/master)\n[![Crates.io](https://img.shields.io/crates/v/ethercrab.svg)](https://crates.io/crates/ethercrab)\n[![Docs.rs](https://docs.rs/ethercrab/badge.svg)](https://docs.rs/ethercrab)\n[![Matrix chat](https://img.shields.io/matrix/ethercrab:matrix.org)](https://matrix.to/#/#ethercrab:matrix.org)\n\nA performant, `async`-first EtherCAT MainDevice written in pure Rust.\n\n## Crate features\n\n- `std` (enabled by default) - exposes the `std` module, containing helpers to run the TX/RX\n  loop on desktop operating systems.\n- `defmt` - enable logging with the [`defmt`](https://docs.rs/defmt) crate.\n- `log` - enable logging with the [`log`](https://docs.rs/log) crate. This is enabled by default\n  when the `std` feature is enabled.\n- `serde` - enable `serde` impls for some public items.\n\nFor `no_std` targets, it is recommended to add this crate with\n\n```bash\ncargo add --no-default-features --features defmt\n```\n\n## Examples\n\nThis example increments the output bytes of all detected SubDevices every tick. It is tested on an\nEK1100 with output modules but may work on other basic SubDevices.\n\nRun with e.g.\n\nLinux\n\n```bash\nRUST_LOG=debug cargo run --example ek1100 --release -- eth0\n```\n\nWindows\n\n```ps\n$env:RUST_LOG=\"debug\" ; cargo run --example ek1100 --release -- '\\Device\\NPF_{FF0ACEE6-E8CD-48D5-A399-619CD2340465}'\n```\n\n```rust\nuse env_logger::Env;\nuse ethercrab::{\n    error::Error, std::{ethercat_now, tx_rx_task}, MainDevice, MainDeviceConfig, PduStorage, Timeouts\n};\nuse std::{sync::Arc, time::Duration};\nuse tokio::time::MissedTickBehavior;\n\n/// Maximum number of SubDevices that can be stored. This must be a power of 2 greater than 1.\nconst MAX_SUBDEVICES: usize = 16;\n/// Maximum PDU data payload size - set this to the max PDI size or higher.\nconst MAX_PDU_DATA: usize = 1100;\n/// Maximum number of EtherCAT frames that can be in flight at any one time.\nconst MAX_FRAMES: usize = 16;\n/// Maximum total PDI length.\nconst PDI_LEN: usize = 64;\n\nstatic PDU_STORAGE: PduStorage\u003cMAX_FRAMES, MAX_PDU_DATA\u003e = PduStorage::new();\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Error\u003e {\n    env_logger::Builder::from_env(Env::default().default_filter_or(\"info\")).init();\n\n    let interface = std::env::args()\n        .nth(1)\n        .expect(\"Provide network interface as first argument.\");\n\n    log::info!(\"Starting EK1100 demo...\");\n    log::info!(\"Ensure an EK1100 is the first SubDevice, with any number of modules connected after\");\n    log::info!(\"Run with RUST_LOG=ethercrab=debug or =trace for debug information\");\n\n    let (tx, rx, pdu_loop) = PDU_STORAGE.try_split().expect(\"can only split once\");\n\n    let maindevice = Arc::new(MainDevice::new(\n        pdu_loop,\n        Timeouts {\n            wait_loop_delay: Duration::from_millis(2),\n            mailbox_response: Duration::from_millis(1000),\n            ..Default::default()\n        },\n        MainDeviceConfig::default(),\n    ));\n\n    tokio::spawn(tx_rx_task(\u0026interface, tx, rx).expect(\"spawn TX/RX task\"));\n\n    let mut group = maindevice\n        .init_single_group::\u003cMAX_SUBDEVICES, PDI_LEN\u003e(ethercat_now)\n        .await\n        .expect(\"Init\");\n\n    log::info!(\"Discovered {} SubDevices\", group.len());\n\n    for subdevice in group.iter(\u0026maindevice) {\n        // Special case: if an EL3004 module is discovered, it needs some specific config during\n        // init to function properly\n        if subdevice.name() == \"EL3004\" {\n            log::info!(\"Found EL3004. Configuring...\");\n\n            subdevice.sdo_write(0x1c12, 0, 0u8).await?;\n            subdevice.sdo_write(0x1c13, 0, 0u8).await?;\n\n            subdevice.sdo_write(0x1c13, 1, 0x1a00u16).await?;\n            subdevice.sdo_write(0x1c13, 2, 0x1a02u16).await?;\n            subdevice.sdo_write(0x1c13, 3, 0x1a04u16).await?;\n            subdevice.sdo_write(0x1c13, 4, 0x1a06u16).await?;\n            subdevice.sdo_write(0x1c13, 0, 4u8).await?;\n        }\n    }\n\n    let mut group = group.into_op(\u0026maindevice).await.expect(\"PRE-OP -\u003e OP\");\n\n    for subdevice in group.iter(\u0026maindevice) {\n        let io = subdevice.io_raw();\n\n        log::info!(\n            \"-\u003e SubDevice {:#06x} {} inputs: {} bytes, outputs: {} bytes\",\n            subdevice.configured_address(),\n            subdevice.name(),\n            io.inputs().len(),\n            io.outputs().len()\n        );\n    }\n\n    let mut tick_interval = tokio::time::interval(Duration::from_millis(5));\n    tick_interval.set_missed_tick_behavior(MissedTickBehavior::Skip);\n\n    loop {\n        group.tx_rx(\u0026maindevice).await.expect(\"TX/RX\");\n\n        // Increment every output byte for every SubDevice by one\n        for mut subdevice in group.iter(\u0026maindevice) {\n            let mut io = subdevice.io_raw_mut();\n\n            for byte in io.outputs().iter_mut() {\n                *byte = byte.wrapping_add(1);\n            }\n        }\n\n        tick_interval.tick().await;\n    }\n}\n```\n\n## Community\n\n[We're on Matrix!](https://matrix.to/#/#ethercrab:matrix.org)\n\n## Current and future features\n\n- [x] `async` API\n- [x] Usable in `no_std` contexts with no allocator required, as long as an `async` executor is available.\n  - [x] Tested with [Embassy](https://embassy.dev)\n  - [ ] Tested with [RTIC](https://rtic.rs/2/book/en/)\n- [x] Autoconfigure SubDevices from their EEPROM (SII) data during startup\n  - [x] Supports configuration using CoE data\n- [x] Safely usable in multi-threaded Linux systems with e.g. `smol`, `tokio` or `std::thread` and\n      `block_on`.\n- [x] Support for `io_uring` on Linux systems to improve performance and latency\n- [x] Support for SDO read/writes to configure SubDevices\n- [x] Distributed clocks\n  - [x] Detection of delays between SubDevices in topology\n  - [x] Static drift compensation on startup\n  - [x] Cyclic synchronisation during OP\n- [x] Basic support for [CiA402](https://www.can-cia.org/can-knowledge/canopen/cia402/)/DS402 drives\n  - [ ] A higher level DS402 API for torque, position and velocity control of common servo drives in\n        a more abstract way.\n- [ ] Integration with LinuxCNC as a HAL component using\n      [the `linuxcnc-hal` crate](https://github.com/jamwaffles/linuxcnc-hal-rs).\n- [ ] Load SubDevice configurations from ESI XML files\n\n## Sponsors\n\n![GitHub Sponsors](https://img.shields.io/github/sponsors/jamwaffles)\n\nThank you to everyone who has donated test equipment, time or money to the EtherCrab project! Would\nyou like to be in this list? Then please consider\n[becoming a Github sponsor](https://github.com/sponsors/jamwaffles)!\n\n- [@nealsjoe](https://twitter.com/nealsjoe) generously donated an EK1100 with several IO modules for\n  testing with.\n- [Trisk Bio](https://triskbio.com/) generously donated some additional Beckhoff modules and some\n  optical ethernet gear.\n- Smark sent a $200 one time donation. Thank you!\n\n## License\n\nLicensed under either of\n\n- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or\n  http://www.apache.org/licenses/LICENSE-2.0)\n- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)\n\nat your option.\n","funding_links":["https://github.com/sponsors/jamwaffles","https://liberapay.com/jamwaffles","https://github.com/sponsors/jamwaffles)!"],"categories":["no-std crates"],"sub_categories":["WIP"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fethercrab-rs%2Fethercrab","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fethercrab-rs%2Fethercrab","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fethercrab-rs%2Fethercrab/lists"}