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.
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
DefaultLockso crate consumers can use it.
Changed
-
#365 (@Dirreke) Add
io-uringfeature soio_uringcan be disabled for Linux platforms that don't support it. ExampleCargo.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::Incompatibleon 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
xdpfeature. - #305 (@fpdotmonkey) Added
SubDevice::sdo_info_object_description_listandSubDevice::sdo_info_object_quantitiesto 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_apitraits 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
TimeoutErrortoError::Timeoutto give a reason why a timeout occurred. - #328 (breaking) (@theol0403) Change
MainDevice::initto require an empty groups structure to be passed in, instead of relying on aDefaulttrait 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_arrayto more cleanly write multiple SDO sub-indices and prevent mistakes. - #274 (@1cedsoda) Added
SubDeviceRef::sdo_read_arrayto 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_blockingto 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 onTxRxResponseto test for various state conditions. - #273 Added
SubDevice::set_alias_addressandSubDeviceRef::set_alias_addressto 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::NoWakervariant has been removed as it is no longer used or returned by any EtherCrab method. - #263 (breaking)
SubDeviceIteratoris removed. The returned iterator's type signature is nowimpl Iterator<Item = SubDeviceRef<'group, ...>> - #270 (breaking) Windows:
tx_rx_taskis removed and replaced withtx_rx_task_blockingwhich is no longerasync. It must be spawned into its own thread instead of an async task.
Changed
- #263
SubDeviceGroupnow 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::Borrowas 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_framenow returnsResult<ReceiveAction, Error>instead ofResult<(), Error>. - #269 (breaking)
SubDeviceGroup::tx_rx_*methods now return aTxRxResponsestruct instead of a tuple. - #278 Add
DerefandDerefMutimpls forSubDeviceRefinstead of copy/pasting methods fromSubDevice. - #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
NoWakererror.
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::NoWakervariant 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
NoWakererror.
0.5.1 - 2024-11-26
Deprecated
- #248 Windows:
tx_rx_taskis replaced withtx_rx_task_blockingwhich is no longerasync. It must be spawned into its own thread instead of an async task.tx_rx_taskwill be removed in a future release.
Added
- #248 Windows: Add
tx_rx_task_blockingto use in a separate thread to send/receive EitherCAT frames.
Changed
- #248 (breaking)
PduRx::receive_framenow returnsResult<ReceiveAction, Error>instead ofResult<(), 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
expectedandgotfields fromError::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
MainDeviceandSubDevicefor master/slave respectively.Type Old New enumSlaveStateSubDeviceStatefnClient::num_slaves()MainDevice::num_subdevices()fnDs402::slave()Ds402::subdevice()fnSlaveGroup::slave()SubDeviceGroup::subdevice()modethercrab::slave_groupethercrab::subdevice_groupstructClientMainDevicestructClientConfigMainDeviceConfigstructGroupSlaveIteratorGroupSubDeviceIteratorstructSlaveSubDevicestructSlaveGroupSubDeviceGroupstructSlaveGroupRefSubDeviceGroupRefstructSlaveIdentitySubDeviceIdentitystructSlavePdiSubDevicePdistructSlaveRefSubDeviceRefvariantAlStatusCode::SlaveNeedsColdStartAlStatusCode::SubDeviceNeedsColdStartvariantAlStatusCode::SlaveNeedsInitAlStatusCode::SubDeviceNeedsInitvariantAlStatusCode::SlaveNeedsPreopAlStatusCode::SubDeviceNeedsPreopvariantAlStatusCode::SlaveNeedsRestartedLocallyAlStatusCode::SubDeviceNeedsRestartedLocallyvariantAlStatusCode::SlaveNeedsSafeopAlStatusCode::SubDeviceNeedsSafeopvariantError::UnknownSlaveError::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_addressmethod toSlaveandSlaveRefto get a SubDevice's alias address.
0.4.1 - 2024-04-05
Fixed
- #208 Expose
DcSupportenum 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
PduReadandPduDatatraits. These are replaced withEtherCrabWireReadandEtherCrabWireReadWritetraits respectively, along withEtherCrabWireReadWritefor write-only items.Some pertinent trait bounds changes in the public API:
SlaveRef::sdo_readfromPduDatatoEtherCrabWireWriteSlaveRef::sdo_writefromPduDatatoEtherCrabWireReadSizedSlaveRef::register_readfromPduDatatoEtherCrabWireWriteSlaveRef::register_writefromPduDatatoEtherCrabWireReadWrite
-
#144 (breaking)
PduError::InvalidIndex(usize)is now aPduError::InvalidIndex(u8)as the EtherCAT index field is itself onl au8. -
#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-timefrom 0.2.0 to 0.3.0. -
#181
PduStoragenow 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 newPduStorage::element_sizemethod to calculate element sizes based on a given maximum PDU payload value.
Added
-
#141 Added the
ethercat-wireandethercat-wire-derivecrates.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-wirefor dealing with packing/unpacking data:EtherCrabWireReadEtherCrabWireReadSizedEtherCrabWireReadWriteEtherCrabWireSizedEtherCrabWireWrite
-
#151 Add
EepromError::ClearErrorsvariant. -
#152 Expose
error::CoeAbortCodefor 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::SubDeviceto get a subdevice status code on failure. -
#180 Add
PreOpPdistate, allowing access to a group's PDI whilst inPRE-OP. -
#180 Add
ethercrab::std::ethercat_nowfunction to get the current time in nanoseconds from the EtherCAT epoch of 2000-01-01. -
#194 Added
SlaveGroupmethods to facilitate graceful shutdown:SlaveGroup<Op>::into_safe_opSlaveGroup<SafeOp>::into_pre_opSlaveGroup<PreOp>::into_init
The
ek1100example shows these methods in use. -
#195 Add
Register::DcCyclicUnitControl(0x0980). -
#193 Add
SlaveGroup::<PreOp>::request_into_opto request all SubDevices in a group transition to OP, but does not wait for them to transition. Also addSlaveGroup::<Op>::all_opto check if all SubDevices in the group have reached OP state. -
#198 Add
Error::DistributedClock(_)andDistributedClockErrorerror variant and type to communicate DC errors. -
#198 Add
SlaveGroup::tx_rx_sync_system_time,SlaveGroup::tx_rx_dc,SlaveRef::set_dc_syncandSlaveGroup::configure_dc_syncto 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_readandsdo_write. - #194
SlaveGroup<PreOp>::into_opnow transitions through SAFE-OP instead of illegally transitioning straight into OP.
Removed
- #145 (breaking) Remove the
contextfield fromError::WorkingCounter. The output from EtherCrab's error logging should be used instead. - #181 (breaking) Remove async
SendableFrame::send. UseSendableFrame::send_blockinginstead. - #197 Remove
SlaveGroupStatetrait. 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
'staticbound fortx_rx_taskon Windows.
0.3.6 - 2024-02-14
Added
- #167 Add support for reading/writing
f32,f64andbool. Note thatf64cannot currently be written usingsdo_writeas only 4 byte expedited transfers are currently supported.
0.3.5 - 2023-12-22
Changed
-
#135 macOS only:
tx_rx_tasknow uses native networking (BPF) instead oflibpcapngto improve reliability. -
#136 (breaking) Fix unsoundness issue where
SlaveRef::io_rawcould be called multiple times, allowing multiple mutable references into the device's output data. -
#136 (breaking) Rename
SlaveRef::io_rawtoSlaveRef::io_raw_mut.SlaveRef::io_rawremains, but now only returns non-mutable references to both the device inputs and outputs.Also renames
SlaveRef::outputs_rawtoSlaveRef::outputs_raw_mut.SlaveRef::outputsnow 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)
Slaveno longer implementsCloneorPartialEq. Devices should instead be compared usingname(),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
PduReadandPduDatafor[u8; N].
Fixed
- #121 Linux only: Relax
'staticlifetime requirement onstd::tx_rx_taskto a named lifetime to allow non-'staticstorage 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()toPduTx::replace_waker(). Instead of calling e.g.pdu_tx.waker().replace(ctx.waker().clone()), now it should bepdu_tx.replace_waker(ctx.waker()). - (potentially breaking) #125 Package upgrades, notably
async_ioandfutures_litefrom 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
RetryBehaviourstruct and associatedClientConfig.retry_behaviouroption. -
#103 Added optional
serdefeature to enable ser/de of some EtherCrab items. -
#104 Implement
std::error::Errorforethercrab::error::Errorwhen thestdfeature is enabled. -
#107 Add watchdog fields to
Registerenum:WatchdogDivider,PdiWatchdogSyncManagerWatchdog,SyncManagerWatchdogStatusSyncManagerWatchdogCounter,PdiWatchdogCounter. -
#113
SlaveStatenow implementsPduReadso 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::initwill no longer exit with an error. - #101 (breaking)
SendableFrame::send_blockingandSendableFrame::sendmust now return the number of bytes sent over the network. - #101 (breaking)
SendableFrame::write_ethernet_packetis no longerpub. Instead, useSendableFrame::send_blockingorSendableFrame::send. - #103 Removed inner
smoltcp::error::ErrorfromPduError::EthernetandPduError::CreateFrameas these don't add much meaning to the variant. - #109 (breaking) Make all methods on
PduLoopprivate. - #113 (breaking)
Command::{code,address,parse}are no longerpub. - #119 (breaking) Changed
SlaveState::UnknowntoSlaveState::Other(u8)to better represent unknown or different states of multiple slaves (e.g. when sending aBRD).
Removed
- #99 (breaking) All PDU methods on
Client(Client::bwr,Client::fprd) have been removed. Instead, use the same methods onCommandlikeCommand::bwr,Command::fprdetc.
0.2.1 - 2023-07-31
Fixed
- #84
GroupSlave::iterwill now panic instead of completing early if a slave device is already borrowed. - #114 The
stdTX/RX future now consumes any queued packets, not just the first one. This fixes PDU timeout issues withzip/joined futures.
Added
- #83 Add
SlaveRef::identitymethod to get the vendor ID, hardware revision, etc of a slave device. - #86 Expose the
SlaveIdentitystruct.
Changed
- #84 The
SlaveGroupStatetrait is now not-doc-hidden so theGroupSlave::slavemethod 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_readandSlaveRef::sdo_write. - #30 Added
Copy,Clone,PartialEqandEqimplementations toErrorandPduError. - #1 Added
SlaveGroup::lenandSlaveGroup::is_emptymethods. - #29 Implement
DisplayforError,PduError,MailboxError,EepromError,VisibleStringErrorandPduValidationError - #31 (breaking) Added a
ClientConfigargument toClient::newto allow configuration of various EtherCrab behaviours. - #55 Added
Client::init_single_groupto reduce boilerplate when only using a single group of devices. - #55 Removed MSRV commitment (was 1.68)
- #59 Added
SendableFrame::send_blockingmethod.
Removed
- #75 (breaking)
Client::request_slave_stateis removed. Groups should be transitioned into the various states individually usinginto_oporinto_safe_op. - #75 (breaking)
SlaveGroup::newis removed. Slave groups can be created withSlaveGroup::default()instead - #45 (breaking) The
SlaveGroupContainertrait is no longer needed and has been removed.
Changed
-
#75 (breaking)
Client::initno longer takes agroupsargument and now requiresG: Default. -
#75 (breaking)
SlaveGroups no longer configure using a closure - instead useSlaveGroup::iterorSlaveGroup::slaveto configure slave devices inline. -
#75 (breaking)
SlaveGroups now have a type state. Useinto_safe_opandinto_opto transition from PRE-OP as provided byClient::initinto run mode. -
#47 Slave
sdo_readandsdo_writemethods no longer require the use ofSubIndex. For single accesses, a rawu8can be passed instead for cleaner configuration code. -
#47 (breaking)
SlaveGroup::slaveandSlaveGroup::iter(wasslaves) now requires the passing of aClientreference when called. -
#47 (breaking)
SlaveGroup::slavesis renamed toSlaveGroup::iter -
#47 (breaking) Grouped slaves that were previously represented as
GroupSlaves are now represented asSlaveRef<'_, SlavePdi<'_>>instead.GroupSlaveis removed. -
#47 (breaking) The
io(),inputs()andoutputs()methods on grouped slaves have been renamed toio_raw(),inputs_raw()andoutputs_raw()respecitively. -
#47 (breaking) The
Slave.nameandSlave.identityfields have been replaced with methods of the same name. -
#45 (breaking) The grouping closure passed to
Client::initnow requires a&dyn SlaveGroupHandleto be returned. This is a sealed trait only implemented forSlaveGroups and allows some internal refactors by erasing the const generics fromSlaveGroup. -
#32 (breaking) To mitigate some internal issues,
PduStoragenow requiresN(the number of storage elements) to be a power of two. -
#33 (breaking)
send_frames_blockingis removed. It is replaced withPduTx::next_sendable_framewhich can be used to send any available frames in a loop until it returnsNone. -
#30 (breaking) Removed
PduError::Encodevariant. -
#25 (breaking) Renamed
pdu_rxtoreceive_frameto mirrorsend_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/TIMEOUTgeneric parameter.stdenvironments will now use the timer provided bysmol(async-io).no_stdenvironments will useembassy-time. -
#9 (breaking) Rename the fields of some variants in
ethercrab::error::Errorto make them less confusing. -
#2 (breaking) Rename
slave_group::ConfiguratortoSlaveGroupRef. -
#1 (breaking)
SlaveGroup::slavesnow 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