The Arduino PID library: hold a temperature, speed or position
PID_v1, by Brett Beauregard, is the PID controller behind most Arduino temperature, motor speed and self-balancing tutorials. You give it three variables by address (the measured input, the output it should drive, and the setpoint you want) and three gains, Kp, Ki and Kd. Every time Compute() runs it reads the input, works out the error from the setpoint, and writes a new output.
Built in on Mokxi: #include <PID_v1.h> and it builds, with nothing to install. Free, and no account needed.
What PID does
It was written to avoid the classic beginner mistakes. Compute() only acts once per sample time (100 ms by default) so the gains mean the same thing however fast loop() runs. The integral is clamped to the output limits, 0 to 255 unless you change them, so it cannot wind up while an output is saturated. The derivative uses the change in the input rather than the error, so moving the setpoint does not kick the output.
The controller starts in MANUAL and does nothing until SetMode(AUTOMATIC). Use DIRECT when a bigger output raises the input (a heater) and REVERSE when it lowers it (a cooling fan).
| Library Manager name | PID |
|---|---|
| Author | Brett Beauregard |
| Include | #include <PID_v1.h> |
| Boards | every board Mokxi compiles for but ATtiny85 |
| In the simulator | Simulated: it runs against the part the way it does on the bench. |
Install it
It builds on every board Mokxi compiles for but ATtiny85.
In Mokxi it is built in: #include <PID_v1.h> and the sketch compiles, with no download, no zip and no Library Manager.
On a real board, install "PID" by Brett Beauregard from the Arduino IDE鈥檚 Library Manager (Sketch, Include Library, Manage Libraries), or add PID to lib_deps in PlatformIO. Mokxi follows the calls of release 1.2.1, so a sketch written here builds there too.
ESP32-C3 路 Arduino Uno 路 Arduino Nano 路 Arduino Mega 2560 路 Arduino Leonardo 路 SparkFun Pro Micro 路 Arduino Micro 路 ESP32-C6 路 Raspberry Pi Pico 路 Raspberry Pi Pico W 路 Seeed XIAO SAMD21 路 STM32 路 STM32 Nucleo-F411RE 路 STM32 Blue Pill 路 micro:bit V2 路 Arduino Uno R4 路 ESP32 DevKit 路 ESP32-S3-DevKitC-1 路 ESP8266 NodeMCU 路 Arduino Pro Mini 3.3 V 路 Seeed XIAO ESP32-C3 路 ESP32-C3 SuperMini 路 Seeed XIAO ESP32-C6 路 ESP32-DevKitC V4 路 LOLIN32 Lite 路 Wemos D1 R32 路 Cheap Yellow Display 路 TTGO T-Display 路 Seeed XIAO ESP32-S3 路 Arduino Nano ESP32 路 Arduino Nano RP2040 Connect 路 Seeed XIAO RP2040 路 Waveshare RP2040-Zero 路 Arduino Uno R4 WiFi
The calls you will use
In the order a sketch meets them.
PID myPID(&Input, &Output, &Setpoint, 2, 5, 1, DIRECT);
A controller on three double variables, with Kp 2, Ki 5 and Kd 1. The variables are passed by address, so the library reads and writes them directly.
myPID.SetMode(AUTOMATIC)
Turns the controller on. MANUAL turns it off and leaves Output to the sketch; switching back is bumpless.
myPID.Compute()
Call it on every pass of loop(). It returns true when it recalculated the output and false when the sample time has not passed yet.
Input = analogRead(A0);
myPID.Compute();
analogWrite(3, Output);myPID.SetTunings(Kp, Ki, Kd)
Changes the gains while running, for tuning from Serial or a pot.
myPID.SetOutputLimits(0, 255)
The range Output is held to. Use -255 to 255 for a motor that can reverse, or 0 to 1 for a time-proportioned relay.
myPID.SetSampleTime(50)
How often Compute() acts, in milliseconds.
myPID.SetControllerDirection(REVERSE)
Flips the action, for a process where more output lowers the input.
myPID.GetKp()
Reads back a gain; GetKi(), GetKd() and GetMode() do the same for the others.
Example: A PID controller holds a simulated heater at 100
On the Arduino Uno R3. It runs in the browser exactly as printed.
#include <PID_v1.h>
double Setpoint, Input, Output;
PID myPID(&Input, &Output, &Setpoint, 2, 5, 1, DIRECT);
int computes = 0;
bool settled = false;
void setup() {
Serial.begin(9600);
Input = 20; // the heater starts at room temperature
Setpoint = 100;
myPID.SetMode(AUTOMATIC);
}
void loop() {
if (myPID.Compute()) {
computes++;
if (computes == 1) {
Serial.print("First output: ");
Serial.println(Output);
}
// The heater: Output heats it, and it loses heat to the room.
Input += 0.05 * Output - 0.02 * (Input - 20);
if (!settled && computes > 10 && abs(Input - Setpoint) < 1) {
settled = true;
Serial.println("settled near 100");
}
}
}Common errors and how to fix them
Output stays at 0 and never changes
The controller is still in MANUAL, or Compute() is not called in loop(). Call SetMode(AUTOMATIC) in setup() after giving Input and Setpoint their first values, and call Compute() every loop.
The output swings from one limit to the other and never settles
The gains are too high for the process. Start with Ki and Kd at 0, raise Kp until it just begins to oscillate, halve it, then add a little Ki to remove the offset and Kd only if it overshoots.
The output goes the wrong way, heating harder as it gets hotter
The controller direction does not match the process. Use REVERSE when increasing the output decreases the input, such as a fan cooling a sensor.
no matching function for call to 'PID::PID(int*, int*, int*, ...)'
The library works on doubles. Declare Input, Output and Setpoint as double, convert sensor readings into them, and pass their addresses with &.
Try PID without installing anything
Open the example, change the sketch, and keep your version in a free account.