A control system is a set of components that regulate the behavior of a plant to bring its variables to a desired state and keep them there, regardless of what happens externally.
Within this world, the first and most important classification we must make is how our controller relates to the plant. There are two opposing philosophies: open loop and closed loop.
Understanding this difference is the first real step to stop programming static sequences and start programming intelligent dynamic systems.
Open-loop control
An open-loop control system is one in which the control action is completely independent of the system’s output. That is, the controller issues a command based solely on the input or a prior calibration, but does not verify if the command was fulfilled.
I like to call this type of control “control by faith” or “blind trust”.
Real-world examples
The classic example is a toaster. You tell it to toast the bread for 3 minutes (that’s the input). The timer activates the heating elements for that time and then turns them off. The toaster has no idea whether the bread came out white, golden, or burnt. It simply executes its time and finishes.
In the maker world, a stepper motor operated without an encoder is open loop. You send it 200 pulses to make one full revolution. If the motor stalls halfway, the microcontroller doesn’t know; it keeps sending pulses, believing the motor is still turning.
How does it look in code?
Open-loop code is usually based on timers or direct mappings (prior calibration):
// Open-loop example: Automatic irrigation motor
void water_plants() {
turn_on_pump();
delay(60000); // We assume 2 liters fall in 1 minute. We trust.
turn_off_pump();
}
Open-loop control is not necessarily bad. It is very cheap, simple to implement, and never has stability problems (it cannot oscillate because it has no feedback). If the environment is very predictable, it is perfectly valid.
The big problem with open-loop is disturbances. If the mains water pressure drops by half, our previous code will water the plants with 1 liter instead of 2, and they will dry out. The system is blind.
Closed-loop control
In a closed-loop control system (or system with feedback), the control action depends on the system’s output.
To achieve this, we need to introduce an essential component: the sensor. We constantly measure reality (the output), compare it with what we wanted to obtain (the reference), and make decisions based on the difference between the two.
The anatomy of the closed loop
To speak properly, let’s define the terms we will use from now on throughout the course:
- Setpoint (SP) or reference
r(t): The target value we want to achieve (e.g., 200 °C on the 3D printer). - Process variable (PV) or output
y(t): The actual state of the system measured by our sensors (e.g., 195 °C measured by the thermistor). - Error
e(t): It is the mathematical difference between what we want and what we have. It is calculated ase(t) = r(t) - y(t).
The controller takes this error and calculates what signal to send to the actuator to reduce it to zero.
Real-world examples
The cruise control of a modern car. You ask it to go at 120 km/h (Setpoint). The speedometer measures you are going at 115 km/h (PV). The car’s computer calculates an error of +5 km/h and decides to inject more fuel. If suddenly the car starts going uphill (disturbance), the speed will drop, the error will increase again, and the system will inject even more fuel to compensate for the hill, all without you touching the pedal.
How does it look in code?
The structure of a closed loop is always executed within a continuous cycle of evaluation and action.
// Conceptual example of a closed loop (Very basic Proportional Control)
float temperature_setpoint = 200.0;
void loop() {
// 1. Measure reality (Feedback)
float current_temperature = read_thermistor();
// 2. Calculate the error
float error = temperature_setpoint - current_temperature;
// 3. Act on the plant to correct the error
float pwm_power = calculate_controller(error);
apply_heater_power(pwm_power);
delay(100); // Wait for the next control cycle
}
The big advantage of the closed loop is that it can compensate for external disturbances (someone opens the window and cools the room) and internal variations (the motor gears wear out over the years). The result will depend on the sensor, the controller, and the stability margins; feedback alone does not guarantee a robust response.
Comparison between both loops
To always have it on hand, here are the key differences between both approaches:
| Feature | Open Loop | Closed Loop |
|---|---|---|
| Architecture | Controller → Actuator → Plant | Controller ⇆ Plant ← Sensor |
| Complexity | Low. Easy to design and program. | Higher. Requires sensors and a well-tuned controller. |
| Disturbances | Totally vulnerable. Does not react to changes. | Robust. Detects and corrects them automatically. |
| Accuracy | Depends on calibration and on conditions not changing. | Can reduce error, but depends on the sensor and controller design. |
| Stability | Always stable (if the plant is). | Danger of instability. Poor design can make the system oscillate wildly. |
The last point in the table is the important one. By introducing feedback, if we react too late or with too much force, we can make the system unstable (imagine a car that corrects the steering wheel so abruptly that it ends up swerving until it leaves the road).