ethercat-linux/vendor/ethercrab/CHANGELOG.md
Tony Cao 21b2d3bf99 J1900 <-> TwinCAT 1 kHz verification over EL6695 bridge
Re-implementation of rustbootd's ecat_el6695_* examples as a single binary
with fixes found in review and on hardware:

- DC-follow PLL hardened against period-2 hunting: slew-limited anchor
  (+/-50us/cycle), bistable-trap snap re-anchor, re-prime on stale deadline
- drift-free absolute-grid ticker mode; probe mode for timestamp forensics
- bounded-memory online stats (histograms), graceful SIGINT/SIGTERM shutdown
  with full report, error-streak abort
- timestamp plausibility filter comparing against the previous raw sample
  (avoids the deadlock after a startup outlier)
- XFC scope waveform options: --el2202 (with --el2202-dual), --el2262,
  --el1252 latch timestamp readback with per-channel edge statistics
- register access unified in regs.rs: named bit constants everywhere,
  read-modify-write for enable/activation bytes
- vendored patched ethercrab 0.7.1 (sdo_write_complete, send_raw_coe)

Verified on J1900 (PREEMPT_RT 6.6.135): 600k cycles/600s exact 1 kHz,
tx/rx zero errors, phase_err p50=164us std=5us; EL2202<->EL1252 loopback
edge interval mean 2000.24us std=24.65us.
2026-07-22 08:55:41 +08:00

35 KiB

Changelog

A pure Rust EtherCAT MainDevice supporting std and no_std environments.

Unreleased - ReleaseDate

0.7.1 - 2026-03-23

Fixed

  • #366 Publicly expose DefaultLock so crate consumers can use it.

Changed

  • #365 (@Dirreke) Add io-uring feature so io_uring can be disabled for Linux platforms that don't support it. Example Cargo.toml:

    ethercrab = { version = "0.8.0", default-features = false, features = [ "std" ] }
    

0.7.0 - 2026-03-16

Fixed

  • #315 (@ClarkZaitun) Read SubDevice status code from correct register when state transition fails.
  • #313 (@fpdotmonkey) Read actual CoE abort code instead of returning CoeAbortCode::Incompatible on SDO transfer failures.
  • #322 Set all configured addresses before initialising SubDevices, fixing an error when adding an existing powered on segment to the EtherCAT network.
  • #322 Use the same DC reference time to set SYNC0 start time, fixing offsets in SubDevices with 32 bit clocks.

Added

  • #310 Add support for XDP on Linux systems using the xdp feature.
  • #305 (@fpdotmonkey) Added SubDevice::sdo_info_object_description_list and SubDevice::sdo_info_object_quantities to get information about a SubDevice's SDOs.
  • #351 Added basic oversampling support with SubDevice::set_oversampling.

Changed

  • #298 (breaking) Change MSRV from 1.81 to 1.85, migrate to edition 2024.
  • #331 (breaking) (@theol0403) Use lock_api traits to allow for different locking behaviour.
  • #301 No longer warn when mailbox counter is not what was sent by the MainDevice.
  • #320 (breaking) (@fpdotmonkey) Add TimeoutError to Error::Timeout to give a reason why a timeout occurred.
  • #328 (breaking) (@theol0403) Change MainDevice::init to require an empty groups structure to be passed in, instead of relying on a Default trait bound.
  • #330 (@theol0403) Stop re-reading potentially broken SyncManager types over CoE. Now the EEPROM is the source of truth for this information.
  • #354 (@prrn) Don't enable Sync Managers with a length of zero.
  • #347 (@fpdotmonkey) Stop writing to register 0x0980. The prior behaviour was copied from SOEM. The register has very little documentation about it and causes issues with some ABB drives, so any writes to it within EtherCrab are removed.

0.6.0 - 2025-03-29

Added

  • #234 Added SubDeviceRef::sdo_write_array to more cleanly write multiple SDO sub-indices and prevent mistakes.
  • #274 (@1cedsoda) Added SubDeviceRef::sdo_read_array to read all sub-indices from a given SDO.
  • #239 Add MailboxError::Emergency { error_code, error_register } variant to surface EMERGENCY responses from CoE transactions.
  • #246 Windows: Add tx_rx_task_blocking to use in a separate thread to send/receive EitherCAT frames.
  • #242 Add support for PDIs longer than a single PDU
  • #269 (breaking) SubDeviceGroup::tx_rx_* methods now read the state (PRE-OP, OP, etc) of each SubDevice in the group, along with methods on TxRxResponse to test for various state conditions.
  • #273 Added SubDevice::set_alias_address and SubDeviceRef::set_alias_address to write an alias address to a SubDevice's EEPROM.
  • #278 Add SubDevice::{eeprom_read, eeprom_read_raw, eeprom_size, eeprom_write_dangerously} methods for working with a SubDevice's EEPROM.

Removed

  • #260 (breaking) The PduError::NoWaker variant has been removed as it is no longer used or returned by any EtherCrab method.
  • #263 (breaking) SubDeviceIterator is removed. The returned iterator's type signature is now impl Iterator<Item = SubDeviceRef<'group, ...>>
  • #270 (breaking) Windows: tx_rx_task is removed and replaced with tx_rx_task_blocking which is no longer async. It must be spawned into its own thread instead of an async task.

Changed

  • #263 SubDeviceGroup now wraps its PDI storage in an internal spinlock to allow easier use across multiple tasks/threads, at the risk of introducing deadlocks.
  • #263 Remove Error::Borrow as it's no longer returned from any EtherCrab method.
  • #287 (breaking) Increase MSRV from 1.77 to 1.81.
  • #231 Enable reading of up to 64 PDO entries per PDO from EEPROM.
  • #232 Use string index from EEPROM to read device name instead of hard coding to the first string index.
  • #232 Add {SubDevice,SubDeviceRef}::description() method to get longer device name
  • #237 Read SubDevice group status in chunks of 16 devices to speed up group status reads.
  • #239 Mailbox emergency responses now return Error::Mailbox(MailboxError::Emergency) instead of being ignored.
  • #238 Group SubDevice status checks are now chunked into however many fit into a frame, instead of being sent separately.
  • #241 (@david-boles) During init, SMs and FMMUs are reset one-by-one instead of the entire block being written to.
  • #246 (breaking) PduRx::receive_frame now returns Result<ReceiveAction, Error> instead of Result<(), Error>.
  • #269 (breaking) SubDeviceGroup::tx_rx_* methods now return a TxRxResponse struct instead of a tuple.
  • #278 Add Deref and DerefMut impls for SubDeviceRef instead of copy/pasting methods from SubDevice.
  • #287 Change SM FMMU mapping fallback behaviour to use the current SM's index instead of looking for the first FMMU of the correct direction.

Fixed

  • #229 Fix overflowing subtraction panic when calculating DC cycle offset.
  • #260 Frames received before their future's waker is registered will no longer trigger a NoWaker error.

0.5.6 - 2025-03-28

Fixed

  • #294 Compute SM type using fallback data when EEPROM record is incomplete.

0.5.5 - 2025-03-21

Fixed

  • #291 Allow initialisation to progress further for devices with empty EEPROMs by falling back to defaults if config data cannot be found.

0.5.4 - 2025-03-20

Fixed

  • #288 Prevent overflow and infinite loops when reading empty or nearly-empty SubDevice EEPROMs.

0.5.3 - 2025-01-24

Added

  • #242 Add support for PDIs longer than a single PDU

0.5.2 - 2024-12-21

Removed

  • #260 (breaking) The PduError::NoWaker variant has been removed as it is no longer used or returned by any EtherCrab method.

Fixed

  • #260 Frames received before their future's waker is registered will no longer trigger a NoWaker error.

0.5.1 - 2024-11-26

Deprecated

  • #248 Windows: tx_rx_task is replaced with tx_rx_task_blocking which is no longer async. It must be spawned into its own thread instead of an async task. tx_rx_task will be removed in a future release.

Added

  • #248 Windows: Add tx_rx_task_blocking to use in a separate thread to send/receive EitherCAT frames.

Changed

  • #248 (breaking) PduRx::receive_frame now returns Result<ReceiveAction, Error> instead of Result<(), Error>.

0.5.0 - 2024-07-28

Changed

  • #216 PDU retries now use the same PDU index instead of allocating a new frame for every resend.
  • #217 EEPROM strings must now be valid ASCII as per the EtherCAT specification. UTF-8 strings will return an error when read.
  • #218 (breaking) Removed expected and got fields from Error::Wire(WireError::{Read,Write}BufferTooShort).
  • #218 (breaking) Increase MSRV from 1.75 to 1.77.
  • #227 (breaking) Renamed many public items to use the newer EtherCAT terminology MainDevice and SubDevice for master/slave respectively.
    Type Old New
    enum SlaveState SubDeviceState
    fn Client::num_slaves() MainDevice::num_subdevices()
    fn Ds402::slave() Ds402::subdevice()
    fn SlaveGroup::slave() SubDeviceGroup::subdevice()
    mod ethercrab::slave_group ethercrab::subdevice_group
    struct Client MainDevice
    struct ClientConfig MainDeviceConfig
    struct GroupSlaveIterator GroupSubDeviceIterator
    struct Slave SubDevice
    struct SlaveGroup SubDeviceGroup
    struct SlaveGroupRef SubDeviceGroupRef
    struct SlaveIdentity SubDeviceIdentity
    struct SlavePdi SubDevicePdi
    struct SlaveRef SubDeviceRef
    variant AlStatusCode::SlaveNeedsColdStart AlStatusCode::SubDeviceNeedsColdStart
    variant AlStatusCode::SlaveNeedsInit AlStatusCode::SubDeviceNeedsInit
    variant AlStatusCode::SlaveNeedsPreop AlStatusCode::SubDeviceNeedsPreop
    variant AlStatusCode::SlaveNeedsRestartedLocally AlStatusCode::SubDeviceNeedsRestartedLocally
    variant AlStatusCode::SlaveNeedsSafeop AlStatusCode::SubDeviceNeedsSafeop
    variant Error::UnknownSlave Error::UnknownSubDevice

Added

  • #162 Add support for FreeBSD and NetBSD using BPF.

Changed

  • #236 Change mailbox response validation to check index/subindex instead of returned counter.

0.4.3 - 2024-09-26

Changed

  • #236 Change mailbox response validation to check index/subindex instead of returned counter.

0.4.2 - 2024-05-27

Added

  • #213 (@jbbjarnason) Add alias_address method to Slave and SlaveRef to get a SubDevice's alias address.

0.4.1 - 2024-04-05

Fixed

  • #208 Expose DcSupport enum at the crate root so it can be used by everyone.

0.4.0 - 2024-03-31

Changed

  • #134 (breaking) Bump MSRV to 1.75.0

  • #134 Refactor sub device EEPROM reader to be more efficient when skipping sections of the device EEPROM map.

  • #142 (breaking) Remove PduRead and PduData traits. These are replaced with EtherCrabWireRead and EtherCrabWireReadWrite traits respectively, along with EtherCrabWireReadWrite for write-only items.

    Some pertinent trait bounds changes in the public API:

    • SlaveRef::sdo_read from PduData to EtherCrabWireWrite
    • SlaveRef::sdo_write from PduData to EtherCrabWireReadSized
    • SlaveRef::register_read from PduData to EtherCrabWireWrite
    • SlaveRef::register_write from PduData to EtherCrabWireReadWrite
  • #144 (breaking) PduError::InvalidIndex(usize) is now a PduError::InvalidIndex(u8) as the EtherCAT index field is itself onl a u8.

  • #151 Reduced overhead for EEPROM reads. Each chunk reader now only checks for and (attempt to) clear device errors once before reading a chunk of data, not for every chunk.

  • #156 Update embassy-time from 0.2.0 to 0.3.0.

  • #181 PduStorage now stores complete Ethernet frames instead of building them on the fly. This adds a little more overhead to each slot, so the reserved data const parameter must be larger to compensate. Use the new PduStorage::element_size method to calculate element sizes based on a given maximum PDU payload value.

Added

  • #141 Added the ethercat-wire and ethercat-wire-derive crates.

    These crates are EXPERIMENTAL. They may be improved for public use in the future but are currently designed around EtherCrab's internal needs and may be rough and/or buggy. Use with caution, and expect breaking changes.

  • #141 Re-export the following traits from ethercrab-wire for dealing with packing/unpacking data:

    • EtherCrabWireRead
    • EtherCrabWireReadSized
    • EtherCrabWireReadWrite
    • EtherCrabWireSized
    • EtherCrabWireWrite
  • #151 Add EepromError::ClearErrors variant.

  • #152 Expose error::CoeAbortCode for matching on CoE transfer errors.

  • #169 Linux only: add io_uring-based blocking TX/RX loop for better performance.

  • #173 Add MUSL libc support.

  • #178 Add Error::SubDevice to get a subdevice status code on failure.

  • #180 Add PreOpPdi state, allowing access to a group's PDI whilst in PRE-OP.

  • #180 Add ethercrab::std::ethercat_now function to get the current time in nanoseconds from the EtherCAT epoch of 2000-01-01.

  • #194 Added SlaveGroup methods to facilitate graceful shutdown:

    • SlaveGroup<Op>::into_safe_op
    • SlaveGroup<SafeOp>::into_pre_op
    • SlaveGroup<PreOp>::into_init

    The ek1100 example shows these methods in use.

  • #195 Add Register::DcCyclicUnitControl (0x0980).

  • #193 Add SlaveGroup::<PreOp>::request_into_op to request all SubDevices in a group transition to OP, but does not wait for them to transition. Also add SlaveGroup::<Op>::all_op to check if all SubDevices in the group have reached OP state.

  • #198 Add Error::DistributedClock(_) and DistributedClockError error variant and type to communicate DC errors.

  • #198 Add SlaveGroup::tx_rx_sync_system_time, SlaveGroup::tx_rx_dc, SlaveRef::set_dc_sync and SlaveGroup::configure_dc_sync to support EtherCAT Distributed Clocks.

Fixed

  • #143 (breaking) (technically) Fix typo in name AlStatusCode::ApplicationControllerAvailableI -> AlStatusCode::ApplicationControllerAvailable
  • #152 CoE errors are not reported correctly from sdo_read and sdo_write.
  • #194 SlaveGroup<PreOp>::into_op now transitions through SAFE-OP instead of illegally transitioning straight into OP.

Removed

  • #145 (breaking) Remove the context field from Error::WorkingCounter. The output from EtherCrab's error logging should be used instead.
  • #181 (breaking) Remove async SendableFrame::send. Use SendableFrame::send_blocking instead.
  • #197 Remove SlaveGroupState trait. It is no longer required, but the same methods are available so migration should be as simple as just removing the import.

0.3.7 - 2024-03-12

Fixed

  • #183 Relax 'static bound for tx_rx_task on Windows.

0.3.6 - 2024-02-14

Added

  • #167 Add support for reading/writing f32, f64 and bool. Note that f64 cannot currently be written using sdo_write as only 4 byte expedited transfers are currently supported.

0.3.5 - 2023-12-22

Changed

  • #135 macOS only: tx_rx_task now uses native networking (BPF) instead of libpcapng to improve reliability.

  • #136 (breaking) Fix unsoundness issue where SlaveRef::io_raw could be called multiple times, allowing multiple mutable references into the device's output data.

  • #136 (breaking) Rename SlaveRef::io_raw to SlaveRef::io_raw_mut. SlaveRef::io_raw remains, but now only returns non-mutable references to both the device inputs and outputs.

    Also renames SlaveRef::outputs_raw to SlaveRef::outputs_raw_mut. SlaveRef::outputs now returns a non-mutable reference to the device output data.

0.3.4 - 2023-11-20

Fixed

  • #132 The mailbox counter is now per-device instead of global, fixing issues with many devices communicating over CoE.

Changed

  • #132 Revert #130 "Counter in mailbox response is no longer checked." as this was masking the root cause, which is now fixed.
  • #132 (breaking) Slave no longer implements Clone or PartialEq. Devices should instead be compared using name(), identity(), configured_address(), etc.

0.3.3 - 2023-11-10

Changed

  • #130 Counter in mailbox response is no longer checked.

0.3.2 - 2023-11-02

Added

  • #122 Added Slave{Ref}::propagation_delay() to get the EtherCAT propagation delay for a specific device on the network.
  • #126 Implement PduRead and PduData for [u8; N].

Fixed

  • #121 Linux only: Relax 'static lifetime requirement on std::tx_rx_task to a named lifetime to allow non-'static storage to be used.
  • #124 Fixed some spurious panics from race conditions by using atomic wakers.
  • #127 Improve frame allocation reliability when contention is high.

Changed

  • #124 (breaking) Changed PduTx::waker() to PduTx::replace_waker(). Instead of calling e.g. pdu_tx.waker().replace(ctx.waker().clone()), now it should be pdu_tx.replace_waker(ctx.waker()).
  • (potentially breaking) #125 Package upgrades, notably async_io and futures_lite from 1.x to 2.0.

0.3.1 - 2023-10-16

0.3.0 - 2023-10-12

Added

  • #91 Add support for "cross" topologies, e.g. with EK1122.

  • #102 PDU retry behaviour is now configurable between no retries, a limited count, or retrying forever with the RetryBehaviour struct and associated ClientConfig.retry_behaviour option.

  • #103 Added optional serde feature to enable ser/de of some EtherCrab items.

  • #104 Implement std::error::Error for ethercrab::error::Error when the std feature is enabled.

  • #107 Add watchdog fields to Register enum: WatchdogDivider, PdiWatchdog SyncManagerWatchdog, SyncManagerWatchdogStatus SyncManagerWatchdogCounter, PdiWatchdogCounter.

  • #113 SlaveState now implements PduRead so can now be read directly, e.g.

    let (status, _wkc) =
        Command::fprd(slave.configured_address(), RegisterAddress::AlStatus.into())
            .receive::<SlaveState>(&client)
            .await?;
    

Changed

  • #92 If no slave devices are detected, Client::init will no longer exit with an error.
  • #101 (breaking) SendableFrame::send_blocking and SendableFrame::send must now return the number of bytes sent over the network.
  • #101 (breaking) SendableFrame::write_ethernet_packet is no longer pub. Instead, use SendableFrame::send_blocking or SendableFrame::send.
  • #103 Removed inner smoltcp::error::Error from PduError::Ethernet and PduError::CreateFrame as these don't add much meaning to the variant.
  • #109 (breaking) Make all methods on PduLoop private.
  • #113 (breaking) Command::{code,address,parse} are no longer pub.
  • #119 (breaking) Changed SlaveState::Unknown to SlaveState::Other(u8) to better represent unknown or different states of multiple slaves (e.g. when sending a BRD).

Removed

  • #99 (breaking) All PDU methods on Client (Client::bwr, Client::fprd) have been removed. Instead, use the same methods on Command like Command::bwr, Command::fprd etc.

0.2.1 - 2023-07-31

Fixed

  • #84 GroupSlave::iter will now panic instead of completing early if a slave device is already borrowed.
  • #114 The std TX/RX future now consumes any queued packets, not just the first one. This fixes PDU timeout issues with zip/joined futures.

Added

  • #83 Add SlaveRef::identity method to get the vendor ID, hardware revision, etc of a slave device.
  • #86 Expose the SlaveIdentity struct.

Changed

  • #84 The SlaveGroupState trait is now not-doc-hidden so the GroupSlave::slave method is more easily accessible.

0.2.0 - 2023-07-31

Added

  • #47 Add the ability to read/write registers/SDOs from grouped slave devices, with the methods SlaveRef::register_read, SlaveRef::register_write, SlaveRef::sdo_read and SlaveRef::sdo_write.
  • #30 Added Copy, Clone, PartialEq and Eq implementations to Error and PduError.
  • #1 Added SlaveGroup::len and SlaveGroup::is_empty methods.
  • #29 Implement Display for Error, PduError, MailboxError, EepromError, VisibleStringError and PduValidationError
  • #31 (breaking) Added a ClientConfig argument to Client::new to allow configuration of various EtherCrab behaviours.
  • #55 Added Client::init_single_group to reduce boilerplate when only using a single group of devices.
  • #55 Removed MSRV commitment (was 1.68)
  • #59 Added SendableFrame::send_blocking method.

Removed

  • #75 (breaking) Client::request_slave_state is removed. Groups should be transitioned into the various states individually using into_op or into_safe_op.
  • #75 (breaking) SlaveGroup::new is removed. Slave groups can be created with SlaveGroup::default() instead
  • #45 (breaking) The SlaveGroupContainer trait is no longer needed and has been removed.

Changed

  • #75 (breaking) Client::init no longer takes a groups argument and now requires G: Default.

  • #75 (breaking) SlaveGroups no longer configure using a closure - instead use SlaveGroup::iter or SlaveGroup::slave to configure slave devices inline.

  • #75 (breaking) SlaveGroups now have a type state. Use into_safe_op and into_op to transition from PRE-OP as provided by Client::init into run mode.

  • #47 Slave sdo_read and sdo_write methods no longer require the use of SubIndex. For single accesses, a raw u8 can be passed instead for cleaner configuration code.

  • #47 (breaking) SlaveGroup::slave and SlaveGroup::iter (was slaves) now requires the passing of a Client reference when called.

  • #47 (breaking) SlaveGroup::slaves is renamed to SlaveGroup::iter

  • #47 (breaking) Grouped slaves that were previously represented as GroupSlaves are now represented as SlaveRef<'_, SlavePdi<'_>> instead. GroupSlave is removed.

  • #47 (breaking) The io(), inputs() and outputs() methods on grouped slaves have been renamed to io_raw(), inputs_raw() and outputs_raw() respecitively.

  • #47 (breaking) The Slave.name and Slave.identity fields have been replaced with methods of the same name.

  • #45 (breaking) The grouping closure passed to Client::init now requires a &dyn SlaveGroupHandle to be returned. This is a sealed trait only implemented for SlaveGroups and allows some internal refactors by erasing the const generics from SlaveGroup.

  • #32 (breaking) To mitigate some internal issues, PduStorage now requires N (the number of storage elements) to be a power of two.

  • #33 (breaking) send_frames_blocking is removed. It is replaced with PduTx::next_sendable_frame which can be used to send any available frames in a loop until it returns None.

  • #30 (breaking) Removed PduError::Encode variant.

  • #25 (breaking) Renamed pdu_rx to receive_frame to mirror send_frames_blocking.

  • #20 (breaking) Changed the way the client, tx and rx instances are initialised to only allow one TX and RX to exist.

    Before

    static PDU_STORAGE: PduStorage<MAX_FRAMES, MAX_PDU_DATA> = PduStorage::new();
    static PDU_LOOP: PduLoop = PduLoop::new(PDU_STORAGE.as_ref());
    
    async fn main_app(ex: &LocalExecutor<'static>) -> Result<(), Error> {
        let client = Arc::new(Client::new(&PDU_LOOP, Timeouts::default()));
    
        ex.spawn(tx_rx_task(INTERFACE, &client).unwrap()).detach();
    }
    

    After

    static PDU_STORAGE: PduStorage<MAX_FRAMES, MAX_PDU_DATA> = PduStorage::new();
    
    async fn main_app(ex: &LocalExecutor<'static>) -> Result<(), Error> {
        let (tx, rx, pdu_loop) = PDU_STORAGE.try_split().expect("can only split once");
    
        let client = Arc::new(Client::new(pdu_loop, Timeouts::default()));
    
        ex.spawn(tx_rx_task(INTERFACE, tx, rx).unwrap()).detach();
    }
    
  • #16 (breaking) Remove TIMER/TIMEOUT generic parameter. std environments will now use the timer provided by smol (async-io). no_std environments will use embassy-time.

  • #9 (breaking) Rename the fields of some variants in ethercrab::error::Error to make them less confusing.

  • #2 (breaking) Rename slave_group::Configurator to SlaveGroupRef.

  • #1 (breaking) SlaveGroup::slaves now returns an iterator over each slave with IO in the group, instead of a plain slave.

Fixed

  • #28 Fix abort code parsing for expedited SDO responses.
  • #26 🎉 EtherCrab now works on stable Rust. The MSRV is 1.68.
  • #23 Strip trailing null bytes (\0) in strings read from SII
  • #14 Fixed various overflow/arithmetic bugs in distributed clock time calculations and PDI configuration
  • #6 Fixed topology detection not stopping at first upstream fork when there is a slave device before the fork.
  • #6 Internal bugfixes to topology discovery code.
  • #2 Fixed multiple group PDI mapping calculation during initialisation.
  • [#57] Fixed a buffer size calculation crash when reading SDOs.

0.1.0 - 2023-01-02

Added

  • Initial release