Skip to main content
Version: 1.1.1

Faults and safe shutdown

Goal: detect a drive fault from your application, decide how to respond, and shut the bus down cleanly on Ctrl-C or at the end of a run.

This tutorial is about device-level faults — a drive that trips (overcurrent, following error, over-temperature). A whole slave dropping off the bus (a pulled cable) is a bus fault, covered later in Detecting and handling bus faults.

EtherCAT background: the CiA402 fault state

A CiA402 drive runs an internal power state machine (DS402). Its condensed shape is: Switch-On Disabled → Ready → Switched On → Operation Enabled, with a Fault state reachable from anywhere. The drive reports its state in the statusword (0x6041); LXMASTER decodes the two bits you care about for you:

Facade queryStatusword meaning
isEnabled()Reached Operation Enabled — powered and holding.
isFaulted()Latched in Fault (statusword bit 3). Motion is inhibited until cleared.

A fault latches: the drive stays faulted until it receives an explicit fault reset, even if the underlying condition has cleared. That is a safety feature — it forces the application to acknowledge the fault.

Watching for a fault

Poll isFaulted() in your control loop and react:

while (net.isRunning() && !g_interrupted) {
if (drive->isFaulted()) {
std::cerr << "Drive faulted, statusword=0x"
<< std::hex << drive->statusword() << std::dec << "\n";
break; // leave the loop and shut down (see below)
}
drive->moveTo(target);
std::this_thread::sleep_for(period);
}

statusword() gives you the raw 0x6041 value when you need manufacturer-specific bits beyond enabled/faulted for logging or diagnostics.

Clearing a fault

If the fault is recoverable and you want to continue, clear it with a one-shot reset, then re-enable:

if (drive->isFaulted()) {
drive->resetFault(); // one-shot acknowledge
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (!drive->isFaulted()) {
drive->enable(); // power back up
drive->moveTo(drive->actualPosition()); // resume holding here
}
}

resetFault() is a single request, not a repeated poll — call it once and give the drive a few cycles to act before checking isFaulted() again.

Automatic fault handling

You can also ask the profile to handle faults for you when you configure the axis, rather than doing it by hand:

  • Startup fault auto-reset (on by default): a residual fault present at OP entry — before the drive has ever enabled this run — is cleared automatically within a bounded window instead of latching a stop. This is why a drive that was left faulted from a previous run usually just comes up.
  • Auto fault-reset-and-recover (off by default): mid-run faults are automatically reset and the drive re-enabled. Leave this off for anything that can move unexpectedly — an automatic re-enable after a following-error is rarely what you want on a real machine.

Mid-run faults are terminal by default precisely so your application stays in control of recovery.

Safe shutdown

net.stop() is a graceful, ordered shutdown — not a hard kill. After it joins the real-time thread it walks the bus back down:

OPERATIONAL -> (disable outputs, settle) -> SAFE-OP -> PRE-OP -> INIT -> close NIC

Because of that walk-down, the pattern for a clean exit is: put devices in a safe state, then stop.

drive->moveTo(home); // command a safe resting setpoint
drive->disable(); // power down to a de-energised rest
net.stop(); // ordered walk-down + NIC close

stop() is safe to call more than once and is a no-op if the bus is already down, so it is fine to call it on every exit path.

Handling Ctrl-C

A real-time process should not be killed with an un-handled signal — that skips the walk-down and leaves drives energised. Trap the signal, flip a flag, and let your loop exit normally:

static std::atomic<bool> g_interrupted{false};

int main() {
signal(SIGINT, [](int){ g_interrupted = true; });
signal(SIGTERM, [](int){ g_interrupted = true; });
// ...
while (net.isRunning() && !g_interrupted) {
// control...
}
// falls through to the safe-shutdown block above
}

Guarding the loop with net.isRunning() and !g_interrupted means the loop also exits on its own if the library stops the bus underneath you (for example after a bus fault).

Post-run diagnostics

After stop(), ask the network to print each device's exit status. It reports from a snapshot taken while the master was still open, so you see what the bus actually looked like at shutdown — including a drive's final statusword and CiA402 fault code (0x603F):

net.stop();
net.reportDeviceStatus(std::cout);

This is the first thing to check when a run ends unexpectedly. For the full set of error- and log-viewing tools — including the runtime log and how to filter it — see Errors, logging, and debugging.

Recap

  • A CiA402 fault latches; watch it with isFaulted() and read statusword() for detail.
  • Clear a recoverable fault with a one-shot resetFault(), then enable() and resume.
  • Leave mid-run auto-recovery off unless you are certain it is safe.
  • Exit by commanding a safe state, then net.stop() (an ordered walk-down); trap Ctrl-C so you always reach it.
  • Use reportDeviceStatus() after stop() to see how the run ended.

Next

So far you have controlled one device at a time. Next you will run several facades together in one loop: Multiple devices on one bus.