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

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 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 (lines 99‑126). The critical fields for curve shaping are:

{
  "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).
  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 (line 990) and transmit it to the pedal.

The Force‑Travel tab (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, lines 75‑81) using the formula:


k = Force_Range / stepperPosRange

Where:

  • Force_Range = maxForcepreloadForce
  • 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 (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, 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).

Example PID configuration in 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:

// 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), the configuration triggers immediate updates:

// 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 and provides UI editors in SystemSetting_Profiles.xaml.cs and CurveTab_PedalForceTravel.xaml.cs.
  • All configuration resides in the payloadPedalConfig struct defined in 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 around lines 99‑126. The firmware interpolates between these points using cubic spline evaluation in 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 (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 (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().

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →