Marcio Cunha

How to implement PID control on ESP32 for automation

Learn how to develop closed-loop PID controllers on ESP32 microcontrollers to stabilize temperature, motors, and industrial or residential processes.

Marcio Cunha•4 min
Also available in:PortuguêsEspañol
Summary
  • The PID algorithm combines proportional, integral, and derivative terms to keep physical variables stable despite external disturbances.
  • The ESP32 features sufficient processing power and hardware timers to execute control loops at strictly constant intervals.
  • Adjusting the integral term eliminates steady-state error, but requires careful limits to prevent windup saturation.
  • Before continuous operation, verifying power supply integrity prevents microcontroller reboots caused by actuator electrical noise.
  • Combining precise analog readings and robust actuators turns the ESP32 into a reliable controller for benches and industrial workflows.

What is PID control and why it matters

When automating a physical process—whether maintaining the temperature of a soldering enclosure or regulating a stepper motor's speed—we quickly realize that turning a digital pin on and off is not enough. If the logic is simply 'it is cold, turn on the heater', the system overshoots and oscillates endlessly. This is where PID (Proportional, Integral, and Derivative) control comes in, a mathematical algorithm that calculates the exact correction intensity based on current error, historical error, and change velocity.

In practical terms, the controller acts like an experienced driver: it looks at the distance to the destination (proportional), considers accumulated hills and valleys along the route (integral), and anticipates upcoming curves (derivative). This combination ensures a smooth, fast response free from unwanted oscillations. Before connecting heavy loads, it is wise to ensure hardware stability by reviewing the concepts in our guide on how to diagnose voltage drops in switched-mode power supplies to prevent unexpected micro-reboots.

How the mathematics behind PID work

To understand the algorithm without headaches, think of each letter as a reaction strategy. The Proportional (P) part generates a correction force directly proportional to the distance between the target value (setpoint) and the current sensor reading. If the gap is large, the response is strong; if the gap is minimal, the correction is gentle. However, the P term alone rarely zeroes out the error completely, as friction or thermal dissipation eventually matches the weak remaining force.

This is where the Integral (I) term steps in, summing errors over time. If the system stalls slightly below target, the integral part accumulates that frustration and gradually ramps up force until the target is reached. Finally, the Derivative (D) term evaluates the rate of error change, acting like a hydraulic brake that prevents the system from overshooting abruptly. For those looking to connect this intelligence to broader ecosystems, check out the guidelines on how to integrate ESP32 with Home Assistant using native ESPHome.

Structuring PID code on the ESP32

The ESP32 is ideal for this task thanks to its independent processing cores and high-precision hardware timers. For a PID loop to run smoothly, the time interval between each reading and calculation (known as the loop time or delta t) must remain strictly constant. If timing fluctuates, the integral and derivative parts lose their mathematical calibration. Therefore, we use hardware timer interrupts instead of simple delay commands.

Below is a practical C++ structure example to execute the core logic of a PID controller within the ESP32 development environment:

struct PIDController {
  float kp, ki, kd;
  float integral, previous_error;
  unsigned long last_time;
};

float compute_pid(PIDController *pid, float setpoint, float current, unsigned long current_time) {
  float delta_t = (current_time - pid->last_time) / 1000.0;
  if (delta_t <= 0) return 0;

  float error = setpoint - current;
  pid->integral += error * delta_t;
  float derivative = (error - pid->previous_error) / delta_t;

  float output = (pid->kp * error) + (pid->ki * pid->integral) + (pid->kd * derivative);

  pid->previous_error = error;
  pid->last_time = current_time;
  return output;
}

Practical challenges: saturation and noise

Implementing theory in the real world requires attention to automation pitfalls. The first is integral saturation, commonly known as windup. When a system responds slowly—like a large oven heating up—the error accumulated by the integral term grows out of control. When the oven finally heats up, the accumulated mathematical energy is so high that temperature shoots way past the target, taking minutes to settle back down.

To solve this, we impose limits (clamping) on the maximum and minimum values the integral term can reach. Another critical point is electrical noise picked up by sensors. Oscillating temperature or pressure readings cause violent jumps in the derivative term, creating jitter in the actuator output. Applying a simple digital low-pass filter to the sensor reading resolves this without sacrificing system response speed.

Conclusion and next steps in automation

Developing a PID control system on the ESP32 opens doors to projects that once required expensive, closed industrial controllers. By understanding the combined behavior of proportional, integral, and derivative terms, you gain the ability to stabilize complex physical variables with surgical precision on your own bench or home. Iterative gain tuning consolidates learning and reveals the beauty of control engineering applied to modern hardware.

Maintaining project robustness also involves paying attention to physical assembly details, power supply isolation, and selecting proper solid-state actuators. With a solid code foundation and properly dimensioned hardware, your microcontroller will be ready to operate autonomously for months, delivering unmatched efficiency and stability to your automated systems.