Expand description
Non-blocking SDO read/write client for EtherCAT devices. Non-blocking SDO read/write client for EtherCAT devices.
SdoClient wraps CommandClient to provide an
ergonomic, handle-based interface for runtime SDO (Service Data Object)
operations over CoE (CANopen over EtherCAT). Create one per device, issue
reads/writes from your control loop, and check results by handle on
subsequent ticks.
§When to use
Use SdoClient for runtime SDO access — reading diagnostic registers,
changing operating parameters on the fly, or any CoE transfer that happens
after the cyclic loop is running. For SDOs that must be applied before
the cyclic loop starts (e.g. setting modes_of_operation), use the
startup_sdo array in project.json instead.
§Topic format
Requests are sent as IPC commands through the existing WebSocket channel.
Topics are scoped to the device name configured in project.json:
| Operation | Topic | Payload |
|---|---|---|
| Write | ethercat.{device}.sdo_write | {"index": "0x6060", "sub": 0, "value": "0x01"} |
| Read | ethercat.{device}.sdo_read | {"index": "0x6060", "sub": 0} |
§Usage with a state machine
A typical pattern pairs SdoClient with StateMachine
to fire an SDO write in one state, then advance on success:
use autocore_std::{ControlProgram, TickContext};
use autocore_std::ethercat::{SdoClient, SdoResult};
use autocore_std::fb::StateMachine;
use serde_json::json;
use std::time::Duration;
pub struct MyProgram {
sm: StateMachine,
sdo: SdoClient,
write_tid: Option<u32>,
}
impl MyProgram {
pub fn new() -> Self {
Self {
sm: StateMachine::new(),
sdo: SdoClient::new("ClearPath_0"),
write_tid: None,
}
}
}
impl ControlProgram for MyProgram {
type Memory = GlobalMemory;
fn process_tick(&mut self, ctx: &mut TickContext<Self::Memory>) {
self.sm.call();
match self.sm.index {
// State 10: Send SDO write for modes_of_operation = PP
10 => {
self.write_tid = Some(
self.sdo.write(ctx.client, 0x6060, 0, json!(1))
);
self.sm.timeout_preset = Duration::from_secs(3);
self.sm.index = 20;
}
// State 20: Wait for response
20 => {
let tid = self.write_tid.unwrap();
match self.sdo.result(ctx.client, tid, Duration::from_secs(3)) {
SdoResult::Pending => { /* keep waiting */ }
SdoResult::Ok(_) => {
log::info!("modes_of_operation set to PP");
self.sm.index = 30;
}
SdoResult::Err(e) => {
log::error!("SDO write failed: {}", e);
self.sm.set_error(1);
}
SdoResult::Timeout => {
log::error!("SDO write timed out");
self.sm.set_error(2);
}
}
}
// State 30: Done — continue with normal operation
30 => { /* ... */ }
_ => {}
}
}
}§Reading an SDO
// Fire a read request
let tid = sdo.read(ctx.client, 0x6064, 0); // Position Actual Value
// On a later tick, check the result
match sdo.result(ctx.client, tid, Duration::from_secs(3)) {
SdoResult::Ok(data) => {
let position: i32 = serde_json::from_value(data).unwrap();
log::info!("Current position: {}", position);
}
SdoResult::Pending => { /* still waiting */ }
SdoResult::Err(e) => { log::error!("SDO read failed: {}", e); }
SdoResult::Timeout => { log::error!("SDO read timed out"); }
}Structs§
- SdoClient
- Non-blocking SDO client scoped to a single EtherCAT device.
- SdoRequest
- Metadata for an in-flight SDO request.
Enums§
- SdoRequest
Kind - Discriminates SDO reads from writes.
- SdoResult
- Result of checking an in-flight SDO request.