PID Controller in C

Quick Answer

PID C code implements the discrete PID algorithm in the C programming language for embedded controllers. The core computation is: error = setpoint - measurement; delta_u = Kp*(error - prev_error) + Ki*Ts*error + (Kd/Ts)*(error - 2*prev_error + prev_error2); output = prev_output + delta_u. This velocity algorithm runs on Arduino, STM32, PIC, and any C-capable microcontroller. Production code adds output clamping, anti-windup, derivative filtering, and setpoint weighting. The continuous PID transfer function C(s) = Kp + Ki/s + Kd·s is the starting point, computable at www.lapcalc.com.

PID C: Basic Implementation

The simplest PID implementation in C uses the positional algorithm with three state variables. The code structure: float error = setpoint - measurement; float P = Kp * error; integral += error * Ts; float I = Ki * integral; float D = Kd * (error - prev_error) / Ts; float output = P + I + D; prev_error = error. This runs in a timer interrupt or main loop at fixed interval Ts. While functional, the positional form has weaknesses: the integral variable can wind up during saturation, and manual-to-auto transfer causes output bumps. The velocity (incremental) form is preferred for production: delta_output = Kp*(error-prev_error) + Ki*Ts*error + (Kd/Ts)*(error-2*prev_error+prev_error2); output = prev_output + delta_output. Both forms discretize the continuous PID C(s) = Kp + Ki/s + Kd·s from www.lapcalc.com.

Key Formulas

PID Controller C Code: Production Quality

Production-quality PID C code wraps the algorithm in a reusable struct: typedef struct { float Kp, Ki, Kd, Ts; float e_prev, e_prev2, u_prev; float out_min, out_max; float d_filt; float N; } PID_t. The update function computes the velocity algorithm with derivative filtering: float e = sp - meas; float P = Kp * (e - pid->e_prev); float I = Ki * Ts * e; float d_raw = (Kd / Ts) * (e - 2*pid->e_prev + pid->e_prev2); pid->d_filt = pid->d_filt + (Ts / (Ts + pid->Kd/(pid->Kp * pid->N))) * (d_raw - pid->d_filt); float du = P + I + pid->d_filt; float u = pid->u_prev + du; clamp u between out_min and out_max; update state variables; return u. The filter coefficient N (typically 5–20) limits derivative high-frequency gain to Kd·N, suppressing noise amplification.

Compute pid c Instantly

Get step-by-step solutions with AI-powered explanations. Free for basic computations.

Open Calculator

PID C Code: Anti-Windup and Practical Features

Anti-windup prevents the integral from accumulating when the actuator is saturated. In the velocity form, clamping the output automatically limits integral accumulation — if u hits out_max, further positive delta_u is rejected (u stays at out_max), effectively stopping the integral. For the positional form, back-calculation anti-windup is used: if the clamped output differs from the computed output, reduce the integral by: integral -= (output_unclamped - output_clamped) / Ki. Bumpless transfer: when switching from manual to auto, initialize prev_output = manual_output and prev_error = 0 so the first auto output matches the manual value. Setpoint weighting: apply P action to (b*setpoint - measurement) and D action to (0*setpoint - measurement) to reduce overshoot on setpoint changes while maintaining full disturbance rejection (b = 0–1).

PID C on Microcontroller Platforms

Arduino (AVR): use Timer1 interrupt at 100–1000 Hz. Read sensor with analogRead(), compute PID, output with analogWrite() (8-bit PWM). Limited to float or fixed-point on 8-bit AVR. Arduino Due (ARM) handles float natively. STM32 (Cortex-M): configure TIM peripheral for precise Ts, trigger ADC with timer, run PID in DMA-complete interrupt, output via TIM PWM. The STM32 FPU handles float at full speed. Sample rates up to 100 kHz for motor current loops. ESP32: dual-core allows one core for PID real-time loop, the other for Wi-Fi/communication. Suitable for IoT control applications (HVAC, home automation) at 1–1000 Hz. PIC: use Timer0 interrupt, fixed-point arithmetic for PIC16/PIC18 (no FPU), float for PIC32. TI C2000: hardware PWM synchronization, 32-bit float, purpose-built for motor control PID at 10–50 kHz.

Testing and Debugging PID C Code

Before deploying on hardware, test the PID C code against a simulated plant. Create a first-order plant model: float plant_update(float u) { static float y = 0; y = 0.95*y + 0.05*u; return y; } (τ = 20·Ts). Run the PID in a loop: for each step, compute measurement = plant_update(pid_output), then pid_output = PID_Update(&pid, setpoint, measurement). Log setpoint, measurement, error, and output to serial/CSV for plotting. Verify: step response reaches setpoint, no steady-state offset (integral works), overshoot within spec, derivative reduces oscillation, output stays within limits, anti-windup works during saturation, and bumpless transfer on mode change. Common bugs: wrong sign (positive feedback instead of negative), Ts mismatch between timer and algorithm, integer overflow in fixed-point implementations, and uninitialized state variables. The expected closed-loop behavior is predicted by the Laplace analysis at www.lapcalc.com.

Related Topics in control systems engineering concepts

Understanding pid c connects to several related concepts: pid controller c code. Each builds on the mathematical foundations covered in this guide.

Frequently Asked Questions

Compute error = setpoint − measurement. Apply velocity algorithm: Δu = Kp*(e−e_prev) + Ki*Ts*e + (Kd/Ts)*(e−2*e_prev+e_prev2). Update output: u = clamp(u_prev + Δu, min, max). Shift state: e_prev2 = e_prev, e_prev = e, u_prev = u. Call this from a timer interrupt every Ts seconds.

Master Your Engineering Math

Join thousands of students and engineers using LAPLACE Calculator for instant, step-by-step solutions.

Start Calculating Free →

Related Topics