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:
- Open the System → Profiles tab (
SystemSetting_Profiles.xaml.cs). - Click Load to set the file path in
Settings.Pedal_file_string[profile, pedal]. - Click Apply Profile to deserialize the JSON via
JsonConvert.DeserializeObject<DAP_config_st>()inSimHubPlugin/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 =
maxForce−preloadForce - stepperPosRange = mechanical travel derived from
pedalStartPositionandpedalEndPosition
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
relativeForceXXandrelativeTravelXXarrays in JSON configuration files. - Spring stiffness is automatically computed from
maxForce,preloadForce, and travel limits inupdateStiffness()—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 whencontrol_strategy_bis enabled. - The SimHub plugin handles JSON deserialization through
others.csand provides UI editors inSystemSetting_Profiles.xaml.csandCurveTab_PedalForceTravel.xaml.cs. - All configuration resides in the
payloadPedalConfigstruct defined inDiyActivePedal_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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →