We continue with the series of posts dedicated to control theory by seeing how to implement a PID control in a microprocessor like Arduino.
In previous posts, we have seen an introduction to control theory, presented the on/off controller with hysteresis, the powerful PID controller, and seen how to adjust the parameters of a PID controller.
Now it’s time to leave theory behind, dust off the keyboard, and see how a PID controller is implemented in a microprocessor like Arduino. Fortunately, it’s quite simple to make a basic PID and, as we will see in the second part, we have a very complete library available to make it even easier.
A PID is an essential element when tackling a large number of interesting projects, such as automatically regulating light levels, maintaining temperature, making a motor spin at constant speed, stabilizing platforms, making a robot move straight, or even robots that balance on two wheels.
Manual PID Controller
First, let’s see how to implement a simple controller ourselves. Actually, the code is not overly complex. Let’s assume our controller takes the input of the controlled variable from A0, and outputs a PWM signal on pin 3.
The code for a basic PID could be as follows:
// Pin assignments
const int PIN_INPUT = A0;
const int PIN_OUTPUT = 3;
// Controller constants
double Kp=2, Ki=5, Kd=1;
// External controller variables
double Input, Output, Setpoint;
// Internal controller variables
unsigned long currentTime, previousTime;
double elapsedTime;
double error, lastError, cumError, rateError;
void setup()
{
Input = analogRead(PIN_INPUT);
Setpoint = 100;
}
void loop(){
pidController.Compute();
Input = analogRead(PIN_INPUT); // read a controller input
Output = computePID(Input); // calculate the controller
delay(100);
analogWrite(PIN_OUTPUT, Output); // write the controller output
}
double computePID(double inp){
currentTime = millis(); // get current time
elapsedTime = (double)(currentTime - previousTime); // calculate elapsed time
error = Setpoint - Input; // determine the error between setpoint and measurement
cumError += error * elapsedTime; // calculate the integral of the error
rateError = (error - lastError) / elapsedTime; // calculate the derivative of the error
double output = kp*error + ki*cumError + kd*rateError; // calculate the PID output
lastError = error; // store previous error
previousTime = currentTime; // store previous time
return output;
}
As we can see, it’s not too difficult. We have a ‘computePID()’ function that does all the work. In this function, we calculate the elapsed time between calls, which we need to calculate both the derivative and the integral of the error.
Next, we compare the controller input with the setpoint to determine the error and perform the ‘pseudo’ integrals and derivatives (their discrete equivalent). Finally, we calculate the system response using the PID formula, and save the values for the next cycle.
That wasn’t so hard, was it? However, although it is completely functional, our PID controller is quite simple. Therefore, it has certain shortcomings when faced with situations that frequently occur in reality.
We could improve our code, but it would no longer be so simple or quick to do. And the best part is, we don’t need to! To our delight, someone has done all the work for us, as we will see in the next section.
PID Controller with Library
Fortunately, we have the PIDController library available, based on the ArduinoPID library developed by Brett Beauregard.
ArduinoPID is a little gem that contains many improvements over a basic PID, which are detailed in this link from the author. I recommend you read the library documentation because it’s a whole class on PID controllers.
With this, creating a PID controller in Arduino is as simple as the following code.
#include <PIDController.hpp>
const int PIN_INPUT = 0;
const int PIN_OUTPUT = 3;
PID::PIDParameters<double> parameters(4.0, 0.2, 1);
PID::PIDController<double> pidController(parameters);
void setup()
{
pidController.Input = analogRead(PIN_INPUT);
pidController.Setpoint = 100;
pidController.TurnOn();
}
void loop()
{
pidController.Input = analogRead(PIN_INPUT);
pidController.Update();
analogWrite(PIN_OUTPUT, pidController.Output);
}
We have seen that it is quite simple to implement a PID controller in a microprocessor like Arduino. And it’s even simpler with the little gem that is the Arduino PID library. So there are no excuses or fears when it comes to implementing a PID controller in our projects.
In the next posts of the series, we will start to see practical examples of projects where we can implement PID control in Arduino. See you soon!

