# How to Implement Emergency Stop and Safety Features in a DIY Sim-Racing FFB Pedal

> Learn to implement emergency stop and safety features in your DIY sim-racing FFB pedal. Discover a robust hardware-software architecture for guaranteed zero torque during faults.

- Repository: [chrgri/diy-sim-racing-ffb-pedal](https://github.com/chrgri/diy-sim-racing-ffb-pedal)
- Tags: how-to-guide
- Published: 2026-02-27

---

**The DIY Sim-Racing FFB Pedal implements emergency stop and safety features through a three-layer hardware-software architecture that monitors a dedicated GPIO pin, triggers a force-stop state machine transition, and physically cuts servo power via `servoIdleAction()` to guarantee zero torque during fault conditions.**

The `chrgri/diy-sim-racing-ffb-pedal` firmware protects users and hardware through a robust safety system built around a stepper-driven force-feedback servo state machine. Implementing emergency stop and safety features requires configuring a hardware guard pin, handling the emergency logic in the main control loop, and ensuring fail-safe power-down procedures execute immediately when faults are detected. This guide covers the exact implementation found in both the ESP32 and V3 firmware variants.

## Safety Architecture Overview

The firmware implements **emergency stop and safety features** through three distinct layers working in concert:

- **Hardware Guard** – A pull-up configured push-button on `EMERGENCY_PIN` (GPIO 6 on V2 boards) provides immediate physical interruption capability
- **Emergency-Stop Handler** – The main control loop continuously monitors button state during `SERVO_CONNECTED` mode and transitions to `SERVO_FORCE_STOP` status when triggered
- **Fail-Safe Power-Down** – The `servoIdleAction()` routine disables the servo power supply or enters sleep mode while managing brake resistor states

## Configuring the Emergency Stop Pin

Define the emergency input pin in your board configuration header. In [`Firmware_for_V3/PedalFirmware/include/Main.h`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/Firmware_for_V3/PedalFirmware/include/Main.h) at line 362, the pin is declared as:

```c
#define EMERGENCY_PIN 6          // Connected to emergency-stop button

```

The ESP32 implementation uses an identical definition at line 369 in [`ESP32/include/Main.h`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/ESP32/include/Main.h). Both firmware variants initialize this pin during startup with internal pull-up activation to ensure the line remains high when the button is open.

Initialize the pin in your setup routine as shown in [`ESP32/src/Main.cpp`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/ESP32/src/Main.cpp) (lines 776-777):

```c
pinMode(EMERGENCY_PIN, INPUT_PULLUP);

```

The V3 firmware performs the same initialization at lines 354-355 in [`Firmware_for_V3/PedalFirmware/src/Main.cpp`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/Firmware_for_V3/PedalFirmware/src/Main.cpp).

## Implementing Emergency Detection Logic

The control loop checks the emergency button state every iteration while the servo remains active. When the button pulls `EMERGENCY_PIN` low, the firmware executes a synchronized safety sequence.

In [`ESP32/src/Main.cpp`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/ESP32/src/Main.cpp) (lines 1852-1869), the detection block appears as:

```c
if ((stepper->servoStatus == SERVO_CONNECTED) &&
    (stepper->servoStatus != SERVO_FORCE_STOP) &&
    (digitalRead(EMERGENCY_PIN) == LOW))
{
    stepper->servoIdleAction();          // Power-down
    stepper->servoStatus = SERVO_FORCE_STOP;
    // Audible & visual cue
    Buzzer.single_beep_tone(770, 100);
    delay(300);
    pixels.setPixelColor(0, 0xff, 0x00, 0x00); // red LED
    pixels.show();
    Serial.println("Servo force Stoped.");
}

```

The V3 firmware contains identical logic at lines 1215-1230. The `SERVO_FORCE_STOP` state prevents subsequent motion commands until system reset, while `servoIdleAction()` handles the actual power removal.

## Fail-Safe Power-Down Implementation

The `servoIdleAction()` method in [`Firmware_for_V3/PedalFirmware/src/StepperWithLimits.cpp`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/Firmware_for_V3/PedalFirmware/src/StepperWithLimits.cpp) (lines 29-46) provides hardware-specific power management:

```c
bool StepperWithLimits::servoIdleAction()
{
    bool returnValue_b = false;
    #ifdef SERVO_POWER_PIN
        // Turn off the servo's power
        gpio_set_level((gpio_num_t)SERVO_POWER_PIN, 0);
        delay(500);               // Let the driver fully discharge
        returnValue_b = true;
    #endif

    #ifndef SERVO_POWER_PIN
        setServoToSleep_b = true; // Driver-only sleep mode
        returnValue_b = true;
    #endif
    return returnValue_b;
}

```

This implementation exists in both firmware variants. When `SERVO_POWER_PIN` is defined, the code physically cuts the servo power rail, guaranteeing zero torque output. Boards without dedicated power control pins instead force the driver into low-power sleep mode via `setServoToSleep_b`.

## Additional Safety Mechanisms

Beyond the emergency stop button, the firmware implements several automatic protection systems:

**Servo Idle Timeout** – After `servoIdleTimeout` milliseconds of inactivity, `servoIdleAction()` automatically powers down the servo to prevent overheating.

**Brake Resistor Control** – When the servo enters idle or emergency states, the firmware disables the high-current braking resistor (controlled via `BRAKE_RESISTOR_PIN` in [`StepperWithLimits.cpp`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/StepperWithLimits.cpp) lines 1111-1114) to eliminate unnecessary heat generation.

**Watchdog-Style Lifeline** – The main loop monitors `stepper->getLifelineSignal()` (referenced at line 1640 in [`Main.cpp`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/Main.cpp)). If communication fails, the system automatically transitions to `SERVO_FORCE_STOP` status.

## Custom Implementation Examples

### Adding a Redundant Emergency Button

Extend the safety system with a secondary button on GPIO 10:

```c
#define EMERGENCY_PIN_2 10   // Second button, also pulled-up

pinMode(EMERGENCY_PIN_2, INPUT_PULLUP);

// Extend the check in the main loop
if ( (digitalRead(EMERGENCY_PIN) == LOW) ||
     (digitalRead(EMERGENCY_PIN_2) == LOW) )
{
    // Emergency-stop handling
    stepper->servoIdleAction();
    stepper->servoStatus = SERVO_FORCE_STOP;
}

```

### Enabling Visual and Audible Feedback

Ensure your build configuration includes the feedback peripherals:

```c
#define EMERGENCY_PIN 6
#define USING_LED                // Enable visual feedback
#define USING_BUZZER             // Enable audible feedback

```

These definitions activate the buzzer tone and red LED indicators shown in the emergency handler examples.

### Software-Controlled Reset

To clear a `SERVO_FORCE_STOP` condition without power cycling, trigger an ESP32 restart:

```c
ESP.restart();   // Clears stop state and reinitializes system

```

This approach mirrors the wake-up sequence implementation found at line 1828 in the ESP32 [`Main.cpp`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/Main.cpp).

## Summary

- **Hardware Configuration** – Define `EMERGENCY_PIN` (typically GPIO 6) and configure with `INPUT_PULLUP` to detect button presses via active-low signaling
- **State Management** – Monitor `servoStatus` for `SERVO_CONNECTED` before checking emergency inputs, then transition to `SERVO_FORCE_STOP` to lock out motion commands
- **Power Cutoff** – Implement `servoIdleAction()` to physically disable servo power via `SERVO_POWER_PIN` or enter sleep mode when hardware power control is unavailable
- **Feedback Systems** – Integrate buzzer tones and LED indicators to provide immediate user notification during emergency events
- **Automatic Protection** – Leverage built-in idle timeouts and lifeline monitoring to catch communication failures without manual intervention

## Frequently Asked Questions

### How does the emergency stop button wiring work?

The firmware expects a normally-open push-button connected between `EMERGENCY_PIN` (GPIO 6) and ground. The internal pull-up resistor keeps the pin high when the button is released; pressing the button pulls the line low, triggering the detection logic. This active-low configuration provides fail-safe behavior in case of wire disconnections.

### What happens when the emergency stop is triggered?

When triggered, the firmware immediately calls `servoIdleAction()` to cut power or sleep the servo driver, sets `servoStatus` to `SERVO_FORCE_STOP` to block further motion commands, activates the buzzer with a 770Hz tone, illuminates the status LED red, and prints a debug message. The system remains in this safe state until a hardware or software reset occurs.

### How do I reset the pedal after an emergency stop?

The `SERVO_FORCE_STOP` state persists until you power-cycle the controller or execute `ESP.restart()` for ESP32-based boards. The firmware does not provide automatic reset from this state to prevent accidental restarts while the emergency condition may still exist. Always verify the physical button has released before resetting.

### What is the difference between power-cut and sleep mode?

When `SERVO_POWER_PIN` is defined, `servoIdleAction()` performs a hard power cut by setting the control pin low and waiting 500ms for driver discharge, guaranteeing zero holding torque. Without this pin defined, the system sets `setServoToSleep_b = true`, placing the stepper driver into a low-power sleep state that may retain minimal holding current depending on the driver hardware configuration.