Test Station Setup

Add an E-Stop and Interlock to a Test Bench

Learn how to wire a hardwired e-stop and guard interlock on a motor test bench, and make the test procedure abort cleanly and record why it stopped.

JJulien Buteau
intermediate10 min readOctober 27, 2026

A motor test bench with rotating parts needs two protective functions that do not depend on the test software: an emergency stop that removes power, and a guard interlock that prevents the motor from running while the guard is open. Learn how to wire both in hardware, why a software stop does not replace them, and how to feed their state to a TofuPilot procedure so it refuses to start with the guard open and records a clean abort when the e-stop is pressed.

Why a Software Stop Is Not a Safety Function

A test PC can stop a motor by commanding zero throttle. That command passes through the operating system, the Python interpreter, the plug process, a USB or serial link, and the microcontroller firmware. Any of them can hang, crash or be busy when the stop is needed. None of them was designed and validated as a safety-related part of a control system.

Software stopSafety function
PathPC, OS, Python, USB, firmwareContacts, safety relay, contactors
Failure behaviourCan fail silently (hung process, lost link)Designed to fail to the safe state, faults detected
StandardNoneISO 13849-1 (Performance Level), ISO 13850, ISO 14119
Role on the benchClean data, controlled ramp-downRemove hazardous energy

Keep the software stop: it ramps the motor down cleanly and records what happened. Do not count it as the protective measure. ISO 13850 covers the emergency stop function, ISO 14119 covers interlocking devices associated with guards, and ISO 13849-1 sets how reliable the safety-related control parts must be, expressed as a Performance Level (PL a to e). The required level (PLr) comes from the risk assessment of your bench.

Wire the Hardware Chain

The protective chain opens the power path to the ESC without involving the test PC. A common architecture uses a safety relay that monitors two channels from each input device and drives two contactors in series:

safety-chain.txt
E-stop (2 NC channels) -------+Guard switch (2 NC channels) -+--> Safety relay --> K1 + K2 (DC contactors in series) --> ESC busReset button (NO) ------------+          ^                                         |           K1/K2 mirror contacts (NC) ---+   feedback loop: relay will not reset if a contactor is weldedAuxiliary contacts (non-safety) --> digital inputs --> test PC (read only)

Each element has a specific role:

  • E-stop device (ISO 13850). Red actuator on a yellow background, latching when pressed, contacts with direct opening action, reachable from the operator position. Releasing it does not restart anything: it only allows a reset.
  • Safety relay. Monitors both channels of each device, detects a cross-fault or a channel that does not open, and refuses to reset until the inputs are back to the safe state and the reset button is pressed.
  • Contactors K1 and K2. Rated for breaking DC at the bus voltage and current. DC arcs do not self-extinguish at a zero crossing, so an AC rating is not a DC rating. Two in series give redundancy; their mirror contacts (mechanically linked to the main contacts) are wired back to the relay so a welded contactor is detected at the next reset.
  • Reset button. Placed where the operator can see the hazard zone. A reset re-enables power; it must not start the motor.

Whether one channel or two, and which relay and contactors, follows from the PLr. Dual-channel inputs with monitored contactors are a common way to reach the higher levels; verify the achieved PL with the ISO 13849-1 calculation (the free SISTEMA tool from IFA implements it).

Choose the Stop Category

IEC 60204-1 (EN 60204-1) defines stop categories, and ISO 13850 requires the emergency stop to be category 0 or 1:

CategoryBehaviourOn a motor bench
0Immediate removal of powerContactors open at once; the rotor coasts down
1Controlled stop with power available, then removal of powerThrottle ramps to zero with braking, a timed relay output opens the contactors after a fixed delay

Category 0 is simpler. Two consequences need handling:

  • The rotor keeps turning. With a propeller, coast-down takes seconds. The guard must contain the rotor during that time, and if the guard can be opened before the rotor stops, the interlock needs guard locking (ISO 14119): the guard stays locked until the run-down time has elapsed or standstill is confirmed.
  • Regeneration on the ESC side. Once the contactors open, the energy the decelerating rotor returns has nowhere to go but the ESC input capacitors. Place the brake resistor or clamp on the ESC side of the contactors. See how to handle regenerative braking on a test bench.

Category 1 brakes the rotor faster but depends on the ESC still being powered during the delay; its control part must meet the same PLr.

Add a Guard Interlock

ISO 14119 classifies interlocking devices by how the actuator is coded:

TypeActuatorNotes
Type 1Uncoded mechanical (hinge cam, plunger)Simple, easier to defeat
Type 2Coded mechanical tongueHarder to defeat with a tool
Type 4Coded non-contact (RFID, magnetic)Tolerant of guard misalignment, high or individual coding available

Mount the switch so it cannot be defeated with a simple object, and so the guard cannot be removed without tools while the switch stays closed. The guard itself (ISO 14120) must contain parts that can be ejected: a failed propeller blade is a projectile. Wire the interlock into the same safety relay as the e-stop, not into the test PC.

Read the Safety State from the Procedure

The test software reads the state of the safety chain through auxiliary contacts: the auxiliary outputs of the safety relay or the auxiliary contacts of the e-stop, the guard switch and the contactors. These signals are not part of the safety function. The PC reads them; it never writes into the safety circuit.

Three inputs are enough for most benches:

InputMeaning when high
estop_releasedE-stop is not pressed
guard_closedGuard switch is closed
power_enabledSafety relay is reset and the contactors are closed

The plug below reads them from a microcontroller that reports its digital inputs as a bitmask. The protocol is an example; replace it with your I/O module's driver:

plugs/safety_inputs.py
19 lines
import serialBITS = {"estop_released": 0, "guard_closed": 1, "power_enabled": 2}class SafetyInputs:    def __init__(self, port):        self._ser = serial.Serial(port, 115200, timeout=0.5)    def read_inputs(self):        # Example protocol: "IO?" returns the input bitmask as an integer        self._ser.write(b"IO?")        mask = int(self._ser.readline().decode().strip())        return {name: bool(mask >> bit & 1) for name, bit in BITS.items()}    def __del__(self):        self._ser.close()

The procedure checks the chain in a setup phase, so the main phases never start with the guard open, and keeps watching it while the motor runs:

procedure.yaml
41 lines
name: Motor Endurance Runversion: 0.1.0config:  SAFETY_IO_PORT:    required: true  DSHOT_PORT:    required: trueexecution:  workers: 1plugs:  - name: Safety Inputs    key: safety    python: plugs.safety_inputs:SafetyInputs    scope: station    with:      port: "{config.SAFETY_IO_PORT}"  - name: DShot Driver    key: motor    python: plugs.dshot_driver:DshotDriver    with:      port: "{config.DSHOT_PORT}"      motor_index: 0setup:  - name: Check Safety Chain    python: phases.safety:check_safety_chain    timeout: 10smain:  - name: Endurance Run    python: phases.endurance:endurance_run    with:      throttle: 50.0      duration_s: 60teardown:  - name: Zero Throttle    python: phases.endurance:zero_throttle

The setup phase stops the run when any input is in the unsafe state. phase.stop() ends the run as aborted, not failed, so a bench condition is not counted as a defect of the unit; the CLI exits with code 3 for a run the procedure stopped itself:

phases/safety.py
21 lines
MESSAGES = {    "estop_released": "E-stop is pressed. Release it, reset the safety relay, then start again.",    "guard_closed": "Guard is open. Close it, then start again.",    "power_enabled": "Safety relay is not reset. Press reset, then start again.",}def first_open_input(state):    for name in ("estop_released", "guard_closed", "power_enabled"):        if not state[name]:            return name    return Nonedef check_safety_chain(phase, log, run, safety):    state = safety.read_inputs()    log.info(f"Safety inputs: {state}")    reason = first_open_input(state)    if reason:        run.metadata["stop_reason"] = f"{reason}_false_at_start"        phase.stop(MESSAGES[reason])

The main phase polls the inputs while the motor runs. When the chain opens, the hardware has already removed power; the phase sets the throttle to zero so the motor does not restart at speed after a reset, records why and when the run stopped, and stops:

phases/endurance.py
22 lines
import timefrom phases.safety import first_open_inputdef endurance_run(phase, log, run, motor, safety, throttle, duration_s):    start = time.monotonic()    motor.set_throttle(throttle)    while (elapsed := time.monotonic() - start) < duration_s:        reason = first_open_input(safety.read_inputs())        if reason:            motor.set_throttle(0.0)            run.metadata["stop_reason"] = f"{reason}_false"            run.metadata["stop_at_s"] = round(elapsed, 2)            log.warning(f"Safety chain opened after {elapsed:.2f} s: {reason} is false")            phase.stop(f"Safety chain opened: {reason} is false")        time.sleep(0.05)    motor.set_throttle(0.0)def zero_throttle(motor):    motor.set_throttle(0.0)

The throttle step to 50% is kept short for readability; ramp it on a real bench to avoid current spikes. The teardown phase always runs, even after a stop, so the motor command returns to zero in every case. The DshotDriver plug is the one from the guide on sizing the power source for a motor test bench.

On the dashboard, aborted runs carry the stop_reason and stop_at_s metadata, so you can filter them and see how often the e-stop or the guard interrupts a test, on which station and with which operator. A rising count on one station points at a guard switch out of adjustment or a procedure step that requires opening the guard.

Common Pitfalls

Wiring the e-stop to a PC digital input only. The PC reads the button but nothing removes power if the software hangs. The e-stop must open the power path through the safety relay and contactors.

Using AC contactors on the DC bus. DC arcs do not extinguish at a zero crossing. Use contactors rated for DC breaking at your bus voltage and current.

Leaving contactor feedback unwired. A welded contactor goes unnoticed and the next e-stop removes power through one channel only. Wire the mirror contacts into the relay feedback loop.

Opening the power path upstream of the brake resistor. Regenerated energy then lifts the ESC-side bus. Put the brake path on the ESC side of the contactors.

Recording a failing measurement before phase.stop(). A failed measurement takes precedence and the run reads as fail, counted against the unit. Stop first or skip the measurement.

Restarting the motor on reset. A reset must only re-enable power. The procedure sets the throttle to zero on every stop, and an operator starts a new run.

Key Points

  • The e-stop (ISO 13850) and guard interlock (ISO 14119) remove power in hardware through a safety relay and DC contactors; the test PC is not part of the safety function.
  • The required Performance Level comes from the risk assessment; verify the achieved PL with ISO 13849-1.
  • Choose stop category 0 or 1 deliberately, and handle coast-down and regeneration on the ESC side.
  • Feed auxiliary contacts to digital inputs so the procedure can read, never drive, the safety state.
  • A setup phase that calls phase.stop() refuses to start with the guard open; a monitored main phase records an aborted run with the reason instead of a false unit failure.

More Guides

Put this guide into practice