# How to Create Custom Force Profiles and Set Spring/Damper Settings on the DIY Sim Racing FFB Pedal

> Learn to create custom force profiles and set spring damper settings for your DIY Sim Racing FFB Pedal by editing JSON arrays and configuring the PID controller. Optimize your setup today.

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

---

**You create custom force profiles by editing the `relativeForceXX` and `relativeTravelXX` arrays in a JSON file matching the `DAP_config_st` structure, while the firmware automatically calculates spring stiffness from your force range in `updateStiffness()` and applies damper effects through the PID controller configured in `MoveByPidStrategy`.**

The chrgri/diy-sim-racing-ffb-pedal project separates physical pedal behavior into two distinct systems: the **force profile** defining the force-versus-travel curve, and the **spring/damper model** governing how the stepper motor reacts to deviations from that curve. Both are controlled through JSON configuration files that the SimHub plugin serializes and pushes to the ESP32 firmware.

## Creating Custom Force Profiles

A **force profile** is a cubic spline curve defined by up to 11 control points stored in the `payloadPedalConfig` struct. The firmware interpolates between these points using `EvalForceCubicSpline()` in [`Firmware_for_V3/PedalFirmware/src/ForceCurve.cpp`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/Firmware_for_V3/PedalFirmware/src/ForceCurve.cpp) to determine target force at any pedal position.

### JSON Profile Structure

Create a JSON file containing a `payloadPedalConfig` object that follows the `DAP_config_st` layout defined in [`Firmware_for_V3/PedalFirmware/include/DiyActivePedal_types.h`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/Firmware_for_V3/PedalFirmware/include/DiyActivePedal_types.h) (lines 99‑126). The critical fields for curve shaping are:

```json
{
  "payloadPedalConfig": {
    "pedalStartPosition": 5,
    "pedalEndPosition": 95,
    "maxForce": 10.0,
    "preloadForce": 1.0,
    "quantityOfControl": 11,
    "relativeForce00": 0, "relativeForce01": 10, "relativeForce02": 20,
    "relativeForce03": 30, "relativeForce04": 40, "relativeForce05": 50,
    "relativeForce06": 60, "relativeForce07": 70, "relativeForce08": 80,
    "relativeForce09": 90, "relativeForce10": 100,
    "relativeTravel00": 0, "relativeTravel01": 10, "relativeTravel02": 20,
    "relativeTravel03": 30, "relativeTravel04": 40, "relativeTravel05": 50,
    "relativeTravel06": 60, "relativeTravel07": 70, "relativeTravel08": 80,
    "relativeTravel09": 90, "relativeTravel10": 100
  }
}

```

- **`relativeForceXX`** (0‑100): Defines the normalized force magnitude at each control point.
- **`relativeTravelXX`** (0‑100): Defines the normalized pedal position for each corresponding force value.
- **`quantityOfControl`**: Specifies how many points the firmware should read (maximum 11).

### Loading Profiles in SimHub

Place your JSON file in any accessible directory, then load it through the SimHub plugin interface:

1. Open the *System → Profiles* tab ([`SystemSetting_Profiles.xaml.cs`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/SystemSetting_Profiles.xaml.cs)).
2. Click **Load** to set the file path in `Settings.Pedal_file_string[profile, pedal]`.
3. Click **Apply Profile** to deserialize the JSON via `JsonConvert.DeserializeObject<DAP_config_st>()` in [`SimHubPlugin/UICallback/others.cs`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/SimHubPlugin/UICallback/others.cs) (line 990) and transmit it to the pedal.

The *Force‑Travel* tab ([`CurveTab_PedalForceTravel.xaml.cs`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/CurveTab_PedalForceTravel.xaml.cs)) provides a graphical editor where adjusting sliders updates `maxForce`, `preloadForce`, and the relative arrays in real time.

## Adjusting Spring and Damper Settings

The pedal’s physical response relies on a **spring constant** derived from your force range and travel limits, combined with a **damper** effect generated by the PID controller.

### Spring Stiffness Calculation

The firmware calculates spring stiffness automatically in `DAP_calculationVariables_st::updateStiffness()` ([`Firmware_for_V3/PedalFirmware/src/DiyActivePedal_types.cpp`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/Firmware_for_V3/PedalFirmware/src/DiyActivePedal_types.cpp), lines 75‑81) using the formula:

```

k = Force_Range / stepperPosRange

```

Where:
- **Force_Range** = `maxForce` − `preloadForce`
- **stepperPosRange** = mechanical travel derived from `pedalStartPosition` and `pedalEndPosition`

To stiffen the pedal, increase `maxForce` or decrease `pedalEndPosition` in your JSON. The firmware recalculates `springStiffnesss` and its inverse (`springStiffnesssInv`) immediately after loading the profile, which `MoveByInterpolatedStrategy` in [`StepperMovementStrategy.h`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/StepperMovementStrategy.h) (lines 32‑38) uses for position control.

### Damper Configuration via PID

**Damper** behavior—velocity‑dependent resistance—is implemented through the PID controller in `MoveByPidStrategy` ([`StepperMovementStrategy.h`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/StepperMovementStrategy.h), lines 69‑86). You can configure this via two modes:

| Mode | Configuration | Implementation |
|------|--------------|----------------|
| **Fixed PID** | Set `control_strategy_b = 0` and tune `PID_p_gain`, `PID_i_gain`, `PID_d_gain` in the JSON. | The controller applies constant gains regardless of pedal position. |
| **Dynamic PID** | Set `control_strategy_b = 1` to enable gradient scaling. | The firmware calls `EvalForceGradientCubicSpline()` to calculate the curve slope, then scales PID gains by `gain_modifier_fl32` (lines 120‑136 in [`StepperMovementStrategy.h`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/StepperMovementStrategy.h)). |

Example PID configuration in JSON:

```json
{
  "payloadPedalConfig": {
    "PID_p_gain": 0.3,
    "PID_i_gain": 50.0,
    "PID_d_gain": 0.0,
    "control_strategy_b": 1
  }
}

```

Higher **D‑gain** values increase velocity damping, making the pedal feel more viscous. The *Advanced* tab in the SimHub plugin exposes these parameters as sliders.

## Applying Changes Programmatically

When loading a profile via the SimHub C# plugin:

```csharp
// Load and apply a custom profile
var jsonPath = @"C:\PedalProfiles\MyCustomProfile.json";
string json = File.ReadAllText(jsonPath);
var cfg = JsonConvert.DeserializeObject<DAP_config_st>(json);
Plugin.Settings.Pedal_file_string[0, 0] = jsonPath;  // clutch slot, profile index
btn_apply_profile_Click_event?.Invoke(this, EventArgs.Empty); // pushes to pedal

```

On the firmware side ([`Main.cpp`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/Main.cpp)), the configuration triggers immediate updates:

```cpp
// Recalculate spring constant with new force range
dap_calculationVariables_st.updateStiffness();

// Dynamic PID scaling example from MoveByPidStrategy
if (control_strategy_u8 == 1) {
    float grad = forceCurve->EvalForceGradientCubicSpline(config_st, calc_st,
                                                          stepperPosFraction_constrained,
                                                          true);
    float gain = (grad > 1e-5f) ? 1.0f / pow(fabs(grad), 1.0f) : 10.0f;
    gain = constrain(gain, 0.1f, 10.0f);
    myPID.SetTunings(gain * Kp, gain * Ki, gain * Kd);
}

```

## Summary

- **Force profiles** are defined by 11‑point cubic splines using `relativeForceXX` and `relativeTravelXX` arrays in JSON configuration files.
- **Spring stiffness** is automatically computed from `maxForce`, `preloadForce`, and travel limits in `updateStiffness()`—no manual entry required.
- **Damper effects** are controlled via the **PID controller** (`PID_p_gain`, `PID_i_gain`, `PID_d_gain`), with optional gradient‑aware scaling when `control_strategy_b` is enabled.
- The SimHub plugin handles JSON deserialization through [`others.cs`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/others.cs) and provides UI editors in [`SystemSetting_Profiles.xaml.cs`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/SystemSetting_Profiles.xaml.cs) and [`CurveTab_PedalForceTravel.xaml.cs`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/CurveTab_PedalForceTravel.xaml.cs).
- All configuration resides in the `payloadPedalConfig` struct defined in [`DiyActivePedal_types.h`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/DiyActivePedal_types.h).

## Frequently Asked Questions

### How many control points can a custom force profile contain?

The `DAP_config_st` structure supports a maximum of 11 control points (indices 00 through 10) defined by the `quantityOfControl` field. These map to the `relativeForceXX` and `relativeTravelXX` arrays declared in [`Firmware_for_V3/PedalFirmware/include/DiyActivePedal_types.h`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/Firmware_for_V3/PedalFirmware/include/DiyActivePedal_types.h) around lines 99‑126. The firmware interpolates between these points using cubic spline evaluation in [`ForceCurve.cpp`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/ForceCurve.cpp).

### Why does the pedal feel softer when I increase the travel range?

Spring stiffness follows the formula `k = Force_Range / stepperPosRange` calculated in [`DiyActivePedal_types.cpp`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/DiyActivePedal_types.cpp) (lines 75‑81). Increasing `pedalEndPosition` or decreasing `preloadForce` expands the denominator or reduces the numerator, respectively, lowering the spring constant. The `MoveByInterpolatedStrategy` class uses this value to determine motor response, resulting in a softer mechanical feel.

### What is the difference between fixed PID and dynamic PID damper modes?

**Fixed PID** (`control_strategy_b = 0`) applies constant `PID_p_gain`, `PID_i_gain`, and `PID_d_gain` values regardless of pedal position, creating linear damping behavior. **Dynamic PID** (`control_strategy_b = 1`) continuously adjusts these gains based on the local gradient of the force curve calculated by `EvalForceGradientCubicSpline()` in [`StepperMovementStrategy.h`](https://github.com/chrgri/diy-sim-racing-ffb-pedal/blob/main/StepperMovementStrategy.h) (lines 120‑136), providing nonlinear damping that adapts to curve steepness.

### Where are the spring and damper parameters stored in the firmware?

All parameters reside in the `payloadPedalConfig` structure within `DAP_config_st`. Spring‑related fields include `maxForce`, `preloadForce`, `pedalStartPosition`, and `pedalEndPosition`. Damper parameters include `PID_p_gain`, `PID_i_gain`, `PID_d_gain`, and `control_strategy_b`. The firmware accesses these through the global `DAP_calculationVariables_st` instance which updates stiffness in real time via `updateStiffness()`.