Introduction
The PID controlled Boost Converter with ESP32, presented in this article, represents an efficient and versatile solution for managing voltage variations in the power supply of electronic systems, allowing you to increase the input voltage to a desired level, while keeping the system stable and precise thanks to PID control (Proportional, Integral, Derivative). In this comprehensive guide, we will explore the fundamental steps to design and implement a boost converter, using an ESP32 microcontroller for control, which offers high computing power and the flexibility to manage the dynamic behavior of the system in real time.
Whether you’re a hobbyist looking for a challenging project or an engineer interested in implementing advanced power management solutions, this guide will provide you with all the tools you need. We’ll start by understanding what a boost converter is, how PID control works, and how these elements integrate within your design. We will then move on to the practical implementation, with a focus on how to configure the hardware components, such as the voltage and current sensors, and on how to program the ESP32 to obtain a stable and responsive system.
By the end of this guide, you will be able to implement a complete system to manage the Boost Converter, with extremely precise control of the PWM (signal amplitude modulation) signal and real-time monitoring of key parameters thanks to a complete web interface which allows you not only to view dynamic and real-time updated graphs of input and output voltages and currents, but also to check the status of the PID with ease, verifying its operation at a glance. Furthermore, you will be able to interact with the system via REST API to send and receive data in a flexible and intuitive way, making monitoring and management of the Boost Converter even more efficient. As always, to develop the software, we will use the excellent PlatformIO IDE. Discover how to optimize power control with the PID-controlled Boost Converter on ESP32, through a complete and interactive experience!
The Boost Converter: introduction and operating principles
What is a Boost Converter and what is it for?
The Boost Converter is a type of DC-DC converter that is intended to boost the input (lower) voltage to a higher output voltage value. This makes it essential in a wide range of electronic applications where a low-voltage source, such as a battery or supercapacitor, must provide a higher voltage to power more demanding devices.
For example, it is used in systems powered by solar cells, where the voltage generated by the panels can vary based on the available light, but must be regulated to power a circuit in a stable manner. Likewise, in portable systems, such as smartphones or embedded devices (such as the Arduino), it can convert a lower battery voltage (e.g. 1.5V from a single cell) into a higher output voltage, such as 3.3V, needed for the operation of other parts of the system.
How does a Boost Converter work?
Let’s first see the basic electrical diagram of a Boost Converter:
As you can see, the DC input source to raise on the Vout node is the 1.5V V1 battery. Another source is the PWM generator (in the scheme called PULSE) which drives the IRLZ44N MOSFET.
The operation of the Boost Converter is based on the energy accumulated in an inductor during the turn-on phase of the MOSFET, which is then transferred to the load in the turn-off phase. The basic circuit is made up of a few key elements:
- Inductor (L): it accumulates energy in the form of a magnetic field during the charging phase (the MOSFET in this phase is turned on and connects the Drain to the Source) and releases it when the current is interrupted, inducing a higher voltage.
- MOSFET: acts as a controlled switch that closes the circuit to charge the inductor and opens it to transfer the stored energy to the load.
- Diode : it allows the current to flow to the load but blocks the return of current, ensuring that the energy is transferred correctly to the load (in our case it is a Schottky diode).
- Capacitor: it is connected in parallel to the load (in this case the 33Ω resistor) acts as a filter to stabilize the output voltage and reduce ripple.
The functioning of some of these components is fully explained in the Understanding major electronic components: an essential guide to analog and digital electronics article which I invite you to read.
The operating principle of the Boost Converter is based on the inductor, which resists sudden changes in current. When the MOSFET closes, current flows through the inductor. When the MOSFET opens, the inductor tries to keep the current constant, causing the voltage across it to rise. This voltage added to the input voltage is transferred to the load via the diode.
From the following link you can download the Boost Converter project created using LTspice.
Boost Converter LTspice Simulation
While the following video shows a simulation created with the mentioned project (with subtitles):
The converter operates in an open loop, it is not subject to regulation. With a PWM control signal of 62kHz and Duty Cycle of 57.2% it regulates the output voltage to 3.3V on a 33Ω load. The current on the load is therefore equal to 100mA. But what would happen if, by chance, the load changed? If, for example, the load went from 33Ω to 330Ω what would happen? Most likely, since the Duty Cycle value is fixed, the output voltage value would be very different from 3.3V. For this reason, as we will see later, the Boost Converter will be inserted in a more complex system which will include a PID, i.e. a regulating element which, by continuously measuring the voltage output from the converter, will modify the Duty Cycle of the PWM signal so that , regardless of variations in the value of the load, the output voltage on it is always regulated at the established value (in our case 3.3V). To do this, the PID will be set with parameters (Kp, Ki and Kd) which we will see later and which determine its behavior. Obviously the PID will be able to regulate the output voltage for variations within a certain range of the load value. For extreme load values the PID will no longer be able to regulate the output voltage from the Boost Converter.
NOTE: in reality the MOSFET should be driven by an electronic component called gate driver. This device is used to process the signal to provide sufficient current to the gate of the MOSFET, allowing fast turn-on and turn-off and thus improving the efficiency of the converter. The gate driver is especially important when controlling a MOSFET with low-power signals, such as those generated by a microcontroller (for example, the ESP32) that cannot provide the current needed to drive the MOSFET efficiently. In our case it was omitted for reasons of simplification of the project and therefore we will not use it but know that it would be good practice to use it. An example of a gate driver is the TC4420.
Physics rules and key formulas
The behavior of the Boost Converter can be described by some key formulas that connect the input voltage Vin, the output voltage Vout, and the duty cycle of the PWM signal (D) that controls the switch (MOSFET):
Output voltage
where D is the ratio of the time the MOSFET is active to the period of the PWM signal.
Inductor
The size of the inductor depends on the current flowing through it, the voltage and the frequency of the PWM. A typical inductance value can be calculated as:
where ΔIL is the desired current ripple and f is the frequency of the PWM signal.
Capacitor
The capacitance needed to reduce the voltage ripple on the output is:
where Iout is the load current, ΔVout is the tolerable voltage ripple and f is the PWM frequency.
History and applications
The Boost Converter was created to respond to the need for efficient and compact solutions for powering electronic devices. Over time, it has proven to be a key solution in many sectors: from renewable energies (such as solar panels) to the automotive industry for battery management, up to portable devices.
The first version of the Boost Converter was conceived in the 1960s, and has seen tremendous evolution since then, largely due to improvements in semiconductors (such as low-resistance MOSFETs and low-voltage-drop Schottky diodes) and the increasing capacity of capacitors and inductors.
Why a Boost Converter and not a linear power supply?
The main difference between a Boost Converter and a linear power supply is efficiency. Linear power supplies, despite being simple, dissipate a lot of energy in the form of heat because they regulate the voltage by lowering it via internal resistance or via a transformer, while switching converters such as Boost optimize energy by transferring all the energy stored in the inductor to the load .
Switching converters, such as Boost, can easily achieve efficiencies above 85%, while linear regulators rarely exceed 50%, especially when there is a large delta between input and output voltage.
The Boost Converter is an essential element for many modern applications where flexibility in adapting the input voltage is critical for energy efficiency. Although more complex than a linear power supply, its advantages in efficiency, compactness and versatility make it the preferred choice in a wide range of modern electronic systems.
In this specific project, the use of the Boost Converter is crucial to regulate a stable output voltage of 3.3V, even with an input source that varies considerably (such as a battery or supercapacitor), ensuring the correct operation of a powered system battery operated.
In the next image we can see the 470µH inductor used in the project:
The MOSFET: operation and applications
MOSFETs (Metal-Oxide-Semiconductor Field-Effect Transistors) are semiconductor devices that operate as switches or amplifiers in electronic circuits. They stand out for their high efficiency and ability to switch large currents with low control voltages. Unlike BJTs (Bipolar Junction Transistors), which are controlled by current, MOSFETs are controlled by the voltage applied to the Gate.
Operating principle
The MOSFET consists of three main terminals: the Gate (G), the Drain (D) and the Source (S). The voltage applied between the Gate and the Source (VGS) controls the current flow between the Drain and the Source (IDS). MOSFETs are divided into two main categories:
- N-channel MOSFET: an N-channel MOSFET turns on when a positive voltage is applied between the Gate and the Source. In this state, electrons (which are negative charge carriers) form a channel between the Drain and the Source, allowing current to flow.
- P-channel MOSFET: a P-channel MOSFET works in a complementary way to an N-channel MOSFET. It activates with a negative voltage between the Gate and the Source, creating a channel that allows current to flow. The charge carriers in this case are holes (positive carriers).
MOSFET in depletion and enhancement mode
MOSFETs can also be classified based on their mode of operation:
- MOSFET in enhancement mode: this is the most common type. The device is normally turned off when the Gate-Source voltage is zero, and turns on only when sufficient voltage is applied to create a conduction channel between the Drain and the Source.
- MOSFET in depletion mode: in this type of MOSFET, the device is normally on (with a pre-existing channel between D and S), and turns off when a negative (for the N channel) or positive (for the P channel) voltage is applied to the Gate, which reduces or eliminate the conduction channel.
Key MOSFET parameters
When selecting a MOSFET for a specific application, it is important to consider some key parameters:
- Threshold voltage (Vth): is the minimum voltage between Gate and Source needed to start creating a conduction channel between Drain and Source. For N-channel MOSFETs, it is generally positive, while for P-channel MOSFETs it is negative.
- Conduction resistance (Rds(on)): is the resistance of the channel when the MOSFET is turned on. A low value of Rds(on) is desirable to minimize power losses and improve efficiency.
- Drain-Source voltage (Vds max): it is the maximum voltage that the MOSFET can withstand between the Drain and the Source without suffering damage.
- Drain current (Id max): it is the maximum current that can flow through the MOSFET when it is turned on.
- Gate capacitance (Cgs, Cgd): indicates the gate capacitance, which affects the switching speed of the MOSFET.
MOSFET operating regions
The MOSFET operates in different regions based on the voltage applied between the Gate, the Drain, and the Source:
- Region of Interdiction (Cutoff): in this region, the Gate-Source voltage is lower than the threshold voltage, which means that there is no conductive channel between Drain and Source, and the MOSFET is turned off.
- Triode region: here, the MOSFET works as a voltage-controlled resistor. The Drain-Source current is proportional to the Drain-Source voltage when the MOSFET is turned on, and is primarily used for switching applications.
- Saturation Region: in this region, the Drain-Source current no longer depends on the Drain-Source voltage, but only on the Gate-Source voltage. This region is mainly used in amplification applications.
MOSFET vs. BJT
MOSFETs offer several advantages over BJTs:
- Energy efficiency: MOSFETs are voltage controlled and require no current to stay in the active state, making them more efficient than BJTs, which require base current to operate.
- Switching speed: MOSFETs have faster switching times than BJTs due to their low gate capacitance and the absence of minority charges.
- Linearity and distortion: BJTs tend to have better linearity in the active region, which can be an advantage in precision amplification applications, while MOSFETs can introduce more distortion in these cases.
Operation as amplifier and switch
- Amplifier: when operating in the saturation region, the MOSFET can amplify small analog signals. Due to their high input impedance and ability to be voltage controlled, MOSFETs are often used in RF and audio amplification stages.
- Switch: when used as a switch, the MOSFET operates in the triode region. Here, the device is either off (off state) or fully on, behaving like a low-resistance resistor. This feature makes MOSFETs ideal for fast switching applications, such as power converters and digital circuits.
MOSFETs are critical components in modern electronics due to their efficiency, speed and versatility. Their ability to operate as switches and amplifiers makes them indispensable in a wide range of applications, from power converters to amplifier circuits.
This overview of MOSFETs gives you the foundation you need to better understand how these devices work and how to select the right MOSFET for your project, whether you’re building a boost converter, an amplifier, or any other circuit that requires effective power management.
In the following image we have, on the left, the IRLZ44N MOSFET and, on the right, the 1N5822 Schottky diode:
The Boost Converter parameters calculator
This calculator is designed to help you determine the key parameters of the boost converter in your project. The interface is intuitive and will guide you through data entry to get a quick answer on the most important values for sizing and analyzing your converter.
The input parameters
- Input voltage (Vin): this is the supply voltage of the converter. Enter the voltage value in volts that the system receives from the power supply or battery.
- Output voltage (Vout): is the desired voltage at the output of the boost converter, i.e. the voltage you expect to get after the conversion process.
- Load current (Iout): indicates the current required by the load connected to the converter, expressed in amperes. This value is essential for determining the sizing of the inductor and for calculating the current ripple.
- PWM frequency (F): is the switching frequency of the PWM signal used to regulate the converter. It is expressed in hertz and directly influences system losses and performance.
- Inductor current ripple (ΔI_L): this value represents the current variation (ripple) in the inductor, expressed in amperes. High ripple may indicate the need for a larger inductor or higher PWM frequency to reduce fluctuations.
- Output voltage ripple (ΔV_out): indicates the voltage variation (ripple) at the converter output, expressed in volts. This value depends on the characteristics of the load and the capacitance of the output capacitor.
After entering the values in the appropriate fields, press the “Calculate” button. The calculator will use the parameters provided to perform the calculations and return the values that are important for your project. The list of results:
- Duty Cycle: is the percentage of time the PWM signal is active. As an example, a duty cycle of 55% indicates that the signal is active for more than half of the switching period.
- Inductor (L): the inductor value needed for your boost converter, calculated in microhenry (µH) or millihenry (mH). This value is the one suggested for the correct operation of the converter, since it influences the stability and the current ripple.
- Output Capacitor (Cout): indicates the value of the output capacitor needed to keep the output voltage stable, generally calculated in microfarads (µF). The value provided suggests the size of the capacitor to reduce voltage variations (ripple) at the output.
- Peak Current Absorbed (I_in Peak): this value, expressed in amperes (A), represents the maximum current absorbed at the converter input during operation.
- RMS Current Absorbed (I_in RMS): indicates the effective current (RMS) absorbed at the converter input. This value is generally lower than the peak current, since it represents the effective current absorbed.
These results provide a complete overview of key circuit parameters, helping the user to correctly size components and evaluate system behavior.
Table of selection criteria for MOSFETs and Schottky diodes
Parameter | MOSFET Recommended | Schottky Diode Recommended |
---|---|---|
Vout ≤ 30V, IinPeak ≤ 30A | IRLZ44N (55V, 47A, Rds(on): 0.022Ω, TO-220) | 1N5822 (40V, 3A, Vf: 0.525V, DO-201) |
Vout ≤ 60V, IinPeak ≤ 15A | IRF540N (100V, 33A, Rds(on): 0.044Ω, TO-220) | 1N5822 (40V, 3A, Vf: 0.525V, DO-201) |
Vout > 60V o IinPeak > 15A | Test a MOSFET with higher voltage and current. | Test a Schottky diode with higher voltage and current. |
Vout ≤ 40V, IinPeak ≤ 3A | IRLZ44N (55V, 47A, Rds(on): 0.022Ω, TO-220) | 1N5822 (40V, 3A, Vf: 0.525V, DO-201) |
Vout ≤ 100V, IinPeak ≤ 20A | IRF540N (100V, 33A, Rds(on): 0.044Ω, TO-220) | MBR360 (60V, 3A, Vf: 0.525V, DO-201) |
Explanation of the parameters
- Vout: Boost Converter output voltage. It is important to choose a MOSFET and diode that can handle the maximum expected voltage.
- IinPeak: peak current absorbed by the circuit. This parameter determines the current handling capability of the MOSFET and diode.
- Rds(on): turn-on resistance of the MOSFET, which affects the efficiency of the converter. Lower values are preferable to reduce power losses.
The PWM signal
We have seen that the signal used to control the Boost Converter is PWM. Pulse Width Modulation (PWM) is a technique used to modulate the width of a pulse to control the average amount of power delivered to a load. The PWM signal is a square wave that alternates ON (where the signal is active, or high) and OFF (where the signal is inactive, or low) periods.
Main characteristics of the PWM signal
- Frequency: the frequency represents how many times the signal repeats in one second. A higher frequency means faster cycling of the PWM signal.
- Duty Cycle: is the percentage of time the signal is high (ON) compared to the total cycle length (period). For example, a 50% duty cycle means the signal is active half the cycle, while a 75% duty cycle means the signal is active 75% of the time. The duty cycle is essential to modulate the average power supplied: the higher the duty cycle, the greater the power delivered to the load.
Pulse Width Modulation (PWM) is a technique used to modulate the width of a pulse to control the average amount of power delivered to a load. The PWM signal is a square wave that alternates ON (where the signal is active, or high) and OFF (where the signal is inactive, or low) periods.
The Duty Cycle can be calculated using the following formula:
Uses of PWM
- Control of electric motors: PWM is commonly used to regulate the speed of DC motors, since modulating the duty cycle varies the power supplied to the motor.
- LED lighting: by changing the duty cycle of a PWM signal, it is possible to adjust the light intensity of an LED. A higher duty cycle means more ON time, and therefore higher brightness.
- Switching power supplies: in DC-DC converters, such as boost and buck converters, the PWM signal controls the switching transistors to adjust the output voltage according to the needs of the circuit.
- Audio Modulation: PWM is also used in audio systems to transmit analog signals to digital, such as in the coding of sounds or audio signals.
How to get it
A PWM signal can be easily generated through microcontrollers such as the ESP32, which have built-in timers to manage the signal. In general, you set a frequency and a duty cycle, and the microcontroller manages the alternation between the ON and OFF states of the signal based on these parameters.
In the context of the Boost Converter design, PWM is used to control the switching of the transistor that regulates the output voltage. By changing the duty cycle of the PWM signal, you can control how much energy is stored in the inductor and transferred to the load, thus influencing the output voltage and current.
The PID
Before illustrating the operation of the PID, let’s clarify the concept of a closed loop regulation system like the one shown in the following image:
A closed-loop control system, such as the one shown in the image, is used to control processes in which the output variable must be kept close to a reference value. Let’s see the main blocks of the system:
Reference signal X(t): this represents the desired value that we want to obtain at the output of the system. This can be, for example, a target voltage or speed.
Error signal e(t): it is the difference between the reference signal and the actual value of the regulated quantity (feedback). This difference is the error, and represents the signal that the PID uses to correct the system. Formula:
PID controller: the PID block takes the error e(t) and processes it with three fundamental components: Proportional (P), Integral (I), and Derivative (D), as described in the next paragraph. The output of the PID is then sent to the process that needs to be controlled.
Controlled process P(t): this is the physical system or component that is being regulated, such as a motor or boost converter in our project. The output Y(t) is the variable we want to control, such as the output voltage in a converter or the rotation speed of an electric motor.
Feedback H(t): a part of the output Y(t) is returned to the regulator in the form of feedback. This allows the system to “know” how close the current output value is to the reference.
Overall operation
The system adjusts itself to minimize the error e(t), continuously correcting the controlled variable. In a boost converter, for example, the output voltage is monitored and compared to the reference value (e.g. 3.3V). If the error increases, the PID intervenes by increasing or decreasing the PWM duty cycle to keep the output voltage stable.
The PID, as we will see, has three components:
- Proportional: it acts on the current error and tends to reduce it quickly.
- Integrative: deals with correcting past errors and stabilizes the system response in the long term.
- Derivative: predicts future errors based on the rate of change of the error and reduces oscillations or overshoots.
In control theory, it is important to distinguish between regulation systems and servo systems (or servo systems).
Regulation systems
A regulation system is a control system designed to maintain a constant output variable, despite possible variations in the system parameters or the surrounding environment. The main task of the regulator is to stabilize the output with respect to a fixed reference value, trying to minimize the error between the desired value and the measured value.
Classic examples of regulation systems include:
- Temperature control in an oven, where the system must maintain a constant temperature.
- Voltage regulators, such as in our Boost Converter project, where the output voltage is kept stable at 3.3V, regardless of input voltage fluctuations or load variations.
Servo systems
On the other hand, a servo system not only seeks to maintain a stable variable, but is designed to follow a time-varying reference signal. In this case, the reference signal is not static, but can change continuously or discretely, and the system must “track” this signal so that the output faithfully follows the variations in the reference.
Examples of servo systems include:
- Position control systems, such as in the motors that drive a robot arm, where the system must follow the programmed movements (the reference) as precisely as possible.
- Speed controls, as in a car’s cruise control systems, where the system tries to maintain the set speed but can react to dynamic changes in the environment (uphills, downhills, etc.).
Relationship between PID and servo/regulation systems
A PID can be used in both types of systems. In the case of a regulation system, the PID is responsible for stabilizing the output with respect to a fixed value (such as maintaining the Boost Converter voltage at 3.3V). In the case of a servo system, however, the PID regulates the output to follow the variable reference signal (like controlling the speed of a motor that varies as a function of time).
Our Boost Converter design is a regulating system, since we want to keep the output voltage constant at a specific value. However, in applications such as those in which the reference signal varies over time, we enter the field of servo systems, where the aim is for the output to faithfully follow these variations. The PID is versatile and can be configured to work in both contexts.
This insight allows you to better understand the distinction between maintaining a constant value and following a value that changes dynamically.
The PID controller (Proportional-Integral-Derivative) is one of the most widespread and used control algorithms in engineering, particularly suitable for automatic regulation applications in closed loop control systems. It was introduced in the first decades of the 20th century, and its development is largely due to the advent of modern control theory. The PID controls a process variable (PV) by comparing it to a reference value or set point (SP), and tries to minimize the error, i.e. the difference between the desired value and the actual value. The controller’s output acts on an actuator, such as a valve, motor, or other device, to regulate the process based on three main components: proportional (P), integral (I), and derivative (D).
Proportional Component (P): the proportional part is directly proportional to the current error. The proportional term Kp amplifies the error, providing an immediate response to the controller. If Kp is too low, the system will be slow to respond; if it is too high, the system may become unstable. The higher the Kp, the greater the corrective action for a given error.
Where e(t) is the error at time t and Kp is the proportional gain.
Integrative Component (I): the integral part accumulates past errors, helping to reduce the stationary error that the proportional part alone cannot eliminate. The integral “remembers” the past error and continues to add until the error is eliminated. The integral term Ki is especially useful when the system fails to reach the setpoint exactly.
Where Ki is the integrative gain.
Derivative Component (D): the derivative term predicts the future behavior of the system based on the rate of change of the error. It helps reduce overshoot and improves system stability by smoothing out too-rapid changes. This component acts on the “slope” of the error and tries to prevent future errors.
Where Kd is the derivative gain.
PID formula
Combining the three components we obtain the complete PID equation:
Where:
- u(t) is the control signal that regulates the actuator.
- e(t) is the error, i.e. the difference between the setpoint and the process variable.
Effects of P, I, D components
- Kp increases system response, but can cause instability if too high.
- Ki reduces the stationary error, but can introduce oscillations if overestimated.
- Kd attenuates oscillations and reduces overshoot, improving stability.
PID optimization and tuning
One of the crucial aspects for the effectiveness of the PID is the tuning of the three gains. Common tuning techniques include:
- Ziegler-Nichols method: an empirical procedure that helps find initial values for Kp, Ki, and Kd.
- Manual tuning: based on trial and error, where values are adjusted until the desired response is achieved.
- Automatic tuning: advanced software algorithms that dynamically calculate optimal Kp, Ki and Kd values based on system response.
Uses of the PID
PID is used in numerous industrial applications and beyond. Some examples include:
- Temperature control (as in ovens or HVAC systems).
- Motor speed control.
- Pressure control systems.
- Control of chemical processes and industrial production.
Advantages and limitations of PID
Advantages:
- Simple to implement and conceptually easy to understand.
- Applicable to a wide range of processes.
- Suitable for linear and non-linear systems.
Limitations:
- May require careful tuning for optimal performance.
- The derivative term is sensitive to noise in the signal, so it may degrade stability in the presence of high noise.
- It is not ideal for systems with long downtime.
PID in embedded systems
In the context of embedded systems, such as your project with ESP32, the PID library makes the control implementation much easier. For example, the PID in voltage and current control systems, such as in the case of the Boost Converter, dynamically adjusts the duty cycle of the PWM signal to keep the output voltage constant even when conditions vary, such as battery voltage drop or a change in load.
In conclusion, PID is a very powerful tool that allows you to achieve precise and reliable control in a wide range of applications. It is essential to have a good understanding of the dynamics of the system to correctly set the parameters and fully exploit the potential of this type of controller.
The PID_v1 library
The PID_v1 library for Arduino is one of the most widespread and well-structured implementations of PID (Proportional, Integral, Derivative) control in the Arduino and microcontroller environment. PID is a control algorithm used in various fields of automation to maintain an output variable (for example temperature, speed or voltage) close to a desired value, called a setpoint. This is done by calculating the error between the setpoint and the current value of the variable, and adjusting the system output to minimize this error over time.
Composition of the PID algorithm
Proportional Component(P): this component is proportional to the current error. If the error is large, the corrective action will be large, and vice versa. The proportionality factor is called Kp. If Kp is too high, the output may fluctuate; if it is too low, the system may respond too slowly.
Integrative Component (I): the integrative component considers the sum of past errors. If the error persists over time, the integral will accumulate and the corrective action will increase. The Ki factor controls the weight of this component. If Ki is too high, the system may overreact, causing instability.
Derivative Component (D): the derivative component is based on the rate of change of the error. If the error is changing rapidly, the derivative component will respond to dampen this change. The Kd factor controls the influence of this component. If Kd is too high, it can cause noise in the system.
Operation of the PID_v1 library
Arduino’s PID_v1 library offers a simple and structured way to implement PID control on microcontrollers, facilitating interaction with sensors and actuators. The library provides all the tools necessary for managing the PID in real-time, calculating the three components (P, I, D) and producing a regulated output that is applied to the control system.
Main components of the library:
- PID constructor: when you initialize a PID object, you pass three basic parameters:
- Input: the variable to control (for example, the temperature read by a sensor). Setpoint: the desired value of the variable to be controlled. Output: the variable on which the PID algorithm will act (for example, the speed of a motor or the power of a heater).
- Compute() function: this is the main function that performs PID calculations and updates the output based on the current input. It must be called repeatedly in the loop() loop to ensure that the algorithm continues to adjust the output. Here is an example statement:myPID.Compute();
- Control mode: the PID can operate in AUTOMATIC or MANUAL mode. In manual mode, the output is not adjusted by the PID, but can be set manually. This is useful if you want to test or temporarily disable the algorithm.
- Setting output limits: the library allows you to set output limits so that the value generated by the PID remains within an acceptable range (for example, 0-255 for PWM control): myPID.SetOutputLimits(0, 255);
- Updating PID parameters: the library allows you to update the values of Kp, Ki and Kd at any time during execution: myPID.SetTunings(newKp, newKi, newKd);
- Direction mode: the PID can operate in DIRECT or REVERSE mode, depending on how the output is to affect the system. For example, if increasing the output reduces the input, then REVERSE mode should be used.
Advantages of the PID_v1 library:
- Ease of use: although the PID algorithm can be complex, the library makes it accessible even to those with little experience in automatic control, providing a simple and intuitive interface.
- Efficiency: the library is optimized to run on microcontrollers such as ESP32 or Arduino, allowing fast and efficient computation.
- Flexibility: the ability to configure parameters, set limits and change the direction of control offers great flexibility in managing a wide range of physical systems.
Typical applications
The PID_v1 library is commonly used to control:
- DC or Servo motors: speed or position adjustment.
- Heating systems: temperature regulation in HVAC systems or 3D printers.
- Voltage or current control: as in boost or buck converters.
In summary, the PID_v1 library for Arduino is a powerful tool for implementing precision control systems, making automatic control accessible even for amateur or educational projects. The possibility of managing the three components of the PID in a modular way makes it a versatile solution for many automation applications.
The INA219 modules
In the project, INA219 modules are used to precisely monitor both the voltage and current at the input and output of the boost converter. These sensors, connected via the I2C bus, allow real-time measurements to be taken and data to be transmitted to the ESP32 microcontroller.
The INA219 sensor is a versatile module designed to measure current through a shunt resistor and voltage on the bus. It can detect currents up to 3.2A (depending on configuration) and voltages up to 32V. Its high precision and ability to configure different resolution modes make it ideal for applications such as monitoring power supplies, DC-DC converters, or any other system requiring accurate current and voltage control.
In the project, two INA219 modules are used with distinct I2C addresses:
- I2C_ADDRESS_in: to monitor the voltage and current input to the converter.
- I2C_ADDRESS_out: to monitor the voltage and current output from the converter.
The microcontroller reads this data through I2C communication and uses it to control the PID feedback loop, which adjusts the duty cycle of the PWM to maintain the desired output voltage (3.3V). When the input voltage drops below a user-defined threshold, the sketch automatically disables the PID, setting the PWM duty cycle to zero and turning off the converter.
One of the most useful features of these modules is the ability to configure their I2C address, allowing multiple sensors to be connected to the same bus without address conflicts.
Configuring I2C addresses on INA219 modules
The I2C addresses of the INA219 modules are configurable via address selection pads, called A0 and A1, present on the module. Based on the configuration of these pads, one of four possible addresses can be assigned to the module.
Here are the I2C address configurations based on the state of pins A0 and A1. In practice, you can solder the pads indicated with A0 together, or solder the pads indicated with A1 together, or solder the pads indicated with A0 together and, again between them, solder the pads indicated with A1:
- A0 pads not soldered, A1 pads not soldered: default I2C address is 0x40
- A0 pads soldered, A1 pads not soldered: I2C address is 0x41
- A0 pads not soldered, A1 pads soldered: I2C address is 0x44
- A0 pads soldered, A1 pads soldered: I2C address is 0x45.
In our project the output module has address 0x40 as the A0 and A1 pads are all NOT soldered while the input module has address 0x44 as the A0 pads are NOT soldered while the A1 pads are soldered together.
This configuration allows you to manage both sensors with a single I2C bus, without address conflicts, thus ensuring accurate and reliable current and voltage readings for the PID control system.
If you need to add more sensors or change their addresses, you can do so easily by changing the state of pins A0 and A1, allowing flexible management of the I2C bus.
In the following photo the pair of sensors, one with unsoldered connectors and the other with soldered connectors:
If you need to solder connectors but don’t know how to solder, I invite you to take a look at the article Yet another tutorial on how to solder on this same blog.
We said that the INA 219 sensors communicate via the I2C protocol. How does this protocol work?
I2C interface
The I2C protocol, also known as Two-Wire Interface (TWI), is a serial communication standard that allows the transmission of data between electronic devices. This protocol is widely used in many applications, including microcontrollers, to facilitate communication between different components of a system. The I2C protocol is characterized by some key characteristics that make it ideal for low power consumption applications with limited hardware resources.
- Two-way communication: I2C supports two-way communication, allowing one device to send data to another device and vice versa. This feature is particularly useful in systems that require continuous interaction between components.
- Use of two lines: as the name suggests, I2C uses only two lines for communication: one for clock (SCL) and one for data (SDA). This significantly reduces the number of pins required for communication, making the protocol ideal for devices with limited pin resources.
- Address management: each I2C device has a unique address, allowing multiple devices to communicate on the same clock and data line. This addressing system allows for efficient and organized communication between devices. It should be noted that devices with the same address cannot coexist on this bus.
- Standard speed and low speed communication: I2C supports standard communication speeds up to 100 kHz and low speed communication speeds up to 400 kHz. These speeds are sufficient for most applications, making the protocol suitable for devices that do not require high communication speeds.
- Compatibility and ease of use: the I2C protocol is widely supported by many platforms and devices, making it a popular choice for communication between components. Its simplicity and the presence of many libraries and documentation make it easy to implement and use.
The I2C protocol is a perfect example of how the design of communication protocols can be simple and powerful, offering an efficient and versatile solution for communication between devices in a wide range of applications.
But our project also involves the use of some REST APIs. Let’s see in more detail what it is.
REST APIs
REST (Representational State Transfer) APIs are a very widespread communication paradigm, especially in the context of the Internet of Things (IoT), which allows interaction between different systems through a standardized and easy-to-use interface. Introduced by Roy Fielding in the early 2000s, REST APIs are based on architectural principles that leverage the HTTP protocol to allow devices, services and applications to communicate with each other in a scalable, efficient and platform-independent way.
Purpose of REST APIs
REST APIs are used to expose functionality of a system or service to another application, be it a mobile app, a web application or an IoT device. This is done via CRUD (Create, Read, Update, Delete) operations that map directly to HTTP operations: POST, GET, PUT/PATCH, DELETE. In practice, these APIs allow you to read, create, modify and delete resources on a remote server, all using simple and easily understandable HTTP requests.
Types of REST APIs
REST APIs can be classified into different types depending on the type of resource they manage:
- JSON REST APIs: responses are provided in JSON (JavaScript Object Notation) format, the most common and readable format for transmitting structured data.
- XML REST APIs: although less common than JSON, some REST APIs use XML (eXtensible Markup Language) to represent resources.
- REST Streaming APIs: used to transmit large streams of real-time data, such as video or IoT sensor data.
Importance of REST APIs in IoT
In the context of IoT, REST APIs are critical for several reasons:
- Interoperability: they allow heterogeneous devices to communicate with each other, regardless of their operating system or programming language.
- Scalability: thanks to their lightweight architecture, REST APIs are easily scalable, making them ideal for IoT environments with an increasing number of connected devices.
- Ease of implementation: REST APIs are simple to implement and understand, making IoT application development faster.
- Accessibility: they allow devices to be accessible from anywhere, facilitating the remote management and control of IoT systems, through global networks such as the Internet.
Why REST APIs matter
REST APIs represent a bridge between the physical and digital worlds. In the IoT, where devices generate and consume large amounts of data, REST APIs provide a structured and standardized way to access this data and use it in different applications. They facilitate integration between new and legacy systems, improving interoperability and reducing development complexity. Furthermore, thanks to their flexibility, REST APIs can be used for a wide range of applications, from home control to the management of large industrial plants.
REST APIs are a key element for developing modern IoT solutions. They offer a universal interface that simplifies communication between devices and services, ensuring flexibility, scalability and ease of use. Thanks to their global adoption, they represent a de facto standard for creating interconnected and interoperable IoT ecosystems.
If you are particularly interested in the use of REST APIs, I invite you to take a look at the article How to build a REST API server with ESP32 which explains how to create a web server with the ESP32 so that it exposes a set of REST APIs to be used to interact with the device in a to receive or send data.
Only two REST APIs are foreseen in this project: the get-parameters which returns a Json document containing the input and output voltage and current values of the Boost Converter, the Duty Cycle of the control signal, the status of the PID and its parameters (Kp, Ki and Kd); the set-pid which allows us to modify the operating parameters of the PID (Kp, Ki and Kd).
To test the REST APIs in this project we will use a well-known software: Postman.
Postman
Postman is a powerful and versatile tool used to test and develop REST APIs, essential for anyone working with server-client communications, especially in the Internet of Things (IoT) space. Thanks to its intuitive interface, Postman allows you to send GET, POST, PUT, DELETE HTTP requests and view the responses in real time. This facilitates debugging and verifying the correct functioning of the API, making it an indispensable tool for developers and engineers.
One of the main features of Postman is its ability to simulate API requests precisely, allowing you to test interactions between different devices or between a device and a remote server. This is particularly useful when developing IoT applications, where devices often need to communicate with cloud services or exchange data in real time. Furthermore, Postman supports the creation of collections of requests, which can be saved and reused, making the testing process more efficient and structured.
In summary, Postman is not just a simple client for testing APIs, but a real development tool that supports the entire API lifecycle, from design to testing to monitoring. In the context of IoT, its ability to manage and test REST APIs efficiently makes it an indispensable ally to ensure that devices can communicate with each other seamlessly. By integrating Postman into your workflow, you can ensure that the APIs you develop are robust, reliable, and ready to be deployed in production environments.
What components do we need?
The list of components is not particularly long:
- a breadboard to connect the ESP32 NodeMCU to the other components
- some DuPont wires (male – male, male – female, female – female)
- two INA219 modules
- a 470µH inductor
- a 47Ω resistor
- a 10kΩ resistor
- a 330Ω resistor
- a 33Ω 1W resistor
- an IRLZ44N MOSFET
- a 1N5822 Schottky diode
- a 100µF 50V electrolytic capacitor
- 4 single AA battery holders
- 4 x 1.5V AA batteries
- and, of course, an ESP32 NodeMCU!
The ESP32 model chosen for this project is that of the AZ-Delivery company.
In the following photo you can see an example of a single AA battery holder:
We will need to connect four of these battery holders in parallel, thus connecting all the red wires together and the black wires together. This “battery pack” will provide us with the 1.5V voltage to input to the Boost Converter so that it raises it to 3.3V on the load. Why four batteries and not one or two? In reality we could connect up to ten of them in parallel. The point is that while the voltage always remains 1.5V, the current supplied adds up so the more batteries there are, the greater the current they can supply. The problem of high input current is not particularly felt when the load is equal to 330Ω as it, at 3.3V, will only absorb 10mA. But when we do the experiment with the 33Ω 1W resistor (which at 3.3V will absorb 100mA) this relatively high value of current supplied by the batteries in parallel will be necessary. A single battery would discharge in a very short time and might even not be able to maintain its voltage at 1.5V given the other absorption by the Boost Converter.
Project implementation
The electrical diagram of the PID controlled Boost Converter with ESP32
Before creating the actual circuit let’s take a look at the pinout of the board:
The electrical diagram, created with Fritzing, is not exactly immediate but not too difficult either. We can see it in the next image:
The sketch
Let’s create the PlatformIO project
We have already seen the procedure for creating a PlatformIO project in the article How to create a project for NodeMCU ESP8266 with PlatformIO.
Although it refers to the ESP8266 board, the procedure is similar.
Simply, when choosing the platform, you will have to choose the AZ-Delivery ESP-32 Dev Kit C V4.
Do not install any of the libraries mentioned in the article.
Now edit the platformio.ini file to add a few lines so the file looks like this:
[env:az-delivery-devkit-v4]
platform = espressif32
board = az-delivery-devkit-v4
framework = arduino
monitor_speed = 115200
upload_speed = 921600
lib_deps =
SPIFFS
wollewald/INA219_WE@^1.3.8
br3ttb/PID@^1.2.1
wnatth3/WiFiManager @ 2.0.16-rc.2
me-no-dev/ESP Async WebServer @ ^1.2.3
bblanchon/ArduinoJson @ ^6.18.5
The libraries used are numerous: we have the SPIFFS to manage the file system which will contain the application web page and which will be consultable both from the browser on the PC and on the mobile phone, the library for the management of the PID, the WiFiManager library for the management of the WiFi connection which we will talk about later, the ESP Async WebServer and ArduinoJson libraries which will manage the REST APIs and their responses in Json format.
You can download the project from the following link:
PID controlled Boost Converter with ESP32
unzip it, take the main.cpp file and replace it with the one you have in the previously created project.
Then, in the project you created, create a folder named data at the same level as the src folder and create an empty file inside it that you will call index.html.
Inside this file copy the following code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Boost Converter Monitoring</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
.container {
max-width: 800px;
margin: auto;
}
canvas {
margin: 20px 0;
}
.info-container {
margin-top: 40px;
padding: 20px;
border: 1px solid #ccc;
border-radius: 10px;
background-color: #f9f9f9;
}
.info-container p {
font-size: 18px;
}
#resetButton {
background-color: red;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
}
#resetButton:hover {
background-color: darkred;
}
#pidStatus {
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
<h2>Boost Converter Monitoring</h2>
<canvas id="voltageChart" width="400" height="200"></canvas>
<canvas id="currentChart" width="400" height="200"></canvas>
<div class="info-container">
<p>PWM Value: <span id="pwmValue">N/A</span></p>
<p>Duty Cycle: <span id="dutyCycle">N/A</span>%</p>
<p>PID Status: <span id="pidStatus">N/A</span></p>
<h3>PID Parameters (Read Only)</h3>
<p>Kp: <span id="kpValue">N/A</span></p>
<p>Ki: <span id="kiValue">N/A</span></p>
<p>Kd: <span id="kdValue">N/A</span></p>
</div>
<button id="resetButton" onclick="resetESP()">Reset ESP32</button>
</div>
<script>
const ESP32_IP = window.location.hostname;
let voltageChart, currentChart;
// loads parameters and refresh the page
function loadParameters() {
fetch(`http://${ESP32_IP}/api/get-parameters`)
.then(response => response.json())
.then(data => {
console.log(data); // verufy if JSON contains data
// Update PWM, Duty Cycle and PID parameters
document.getElementById('pwmValue').innerText = data.pwm_value.toFixed(1);
document.getElementById('dutyCycle').innerText = data.duty_cycle.toFixed(1);
// updates PID status with colors
const pidStatusElement = document.getElementById('pidStatus');
pidStatusElement.innerText = data.pid_status;
if (data.pid_status === "ON") {
pidStatusElement.style.color = "green";
} else {
pidStatusElement.style.color = "red";
}
// Updates PID parameters
document.getElementById('kpValue').innerText = data.Kp ? data.Kp.toFixed(2) : "N/A";
document.getElementById('kiValue').innerText = data.Ki ? data.Ki.toFixed(2) : "N/A";
document.getElementById('kdValue').innerText = data.Kd ? data.Kd.toFixed(2) : "N/A";
// updates charts
updateCharts(data.voltage_in, data.voltage_out, data.current_in, data.current_out);
})
.catch(error => console.error('Errore nel caricamento dei parametri:', error));
}
// updates charts
function updateCharts(voltageIn, voltageOut, currentIn, currentOut) {
const time = new Date().toLocaleTimeString();
// updates voltages chart
if (!voltageChart) {
const voltageCtx = document.getElementById('voltageChart').getContext('2d');
voltageChart = new Chart(voltageCtx, {
type: 'line',
data: {
labels: [time],
datasets: [{
label: 'Input Voltage (V)',
borderColor: 'rgb(75, 192, 192)',
data: [voltageIn],
fill: false
}, {
label: 'Output Voltage (V)',
borderColor: 'rgb(255, 99, 132)',
data: [voltageOut],
fill: false
}]
}
});
} else {
voltageChart.data.labels.push(time);
voltageChart.data.datasets[0].data.push(voltageIn);
voltageChart.data.datasets[1].data.push(voltageOut);
voltageChart.update();
}
// updates currents chart
if (!currentChart) {
const currentCtx = document.getElementById('currentChart').getContext('2d');
currentChart = new Chart(currentCtx, {
type: 'line',
data: {
labels: [time],
datasets: [{
label: 'Input Current (mA)',
borderColor: 'rgb(54, 162, 235)',
data: [currentIn],
fill: false
}, {
label: 'Output Current (mA)',
borderColor: 'rgb(153, 102, 255)',
data: [currentOut],
fill: false
}]
}
});
} else {
currentChart.data.labels.push(time);
currentChart.data.datasets[0].data.push(currentIn);
currentChart.data.datasets[1].data.push(currentOut);
currentChart.update();
}
}
// Fresets the ESP32
function resetESP() {
fetch(`http://${ESP32_IP}/api/reset`)
.then(response => response.text())
.then(data => {
console.log("ESP32 in reset:", data);
alert('ESP32 in reset...');
})
.catch(error => {
console.error('Error resetting ESP32:', error);
});
}
// loads initial parameters
window.onload = loadParameters;
// updates parameters every 5 seconds
setInterval(loadParameters, 5000);
</script>
</body>
</html>
This is the application control web page that you can consult via browser on both your computer and smartphone.
This file will be transferred (with a procedure that we will see shortly) into the SPIFFS file system internal to the board. However, you will find this file, together with the main.c and the platformio.ini, in the zip file that you can download from the previous link.
How to transfer the index.html file to the SPIFFS file system
The operation is quite simple. It is necessary to open a new terminal on PlatformIO with the button indicated in the figure:
write the following command:
pio run --target uploadfs
and press the ENTER key. If everything goes well the file will be transferred to the SPIFFS file system. If necessary, stop viewing via Serial Monitor as it may conflict with the upload operation as it uses (and sometimes monopolizes) the communications port.
NOTE: it is important that the index.html file is located, as already mentioned at the beginning, in a folder named data at the same level as the src folder.
Loading the sketch, however, follows the normal way.
How the sketch works
Now let’s see how the sketch works.
The sketch begins with including the necessary libraries:
#include <Arduino.h>
#include <Wire.h>
#include <INA219_WE.h>
#include <PID_v1.h>
#include <WiFiManager.h>
#include <ESPAsyncWebServer.h>
#include <ArduinoJson.h>
#include <SPIFFS.h>
Then follows a #define that establishes whether to print debug messages on the Serial Monitor:
#define DEBUGSERIAL
If this define is left as above, debug messages will be printed. If instead it is commented as below:
// #define DEBUGSERIAL
messages will not be printed.
The I2C addresses for the two INA219 sensors are then defined:
#define I2C_ADDRESS_in 0x44
#define I2C_ADDRESS_out 0x40
We define the variables that will contain the input and output voltage and current values measured by the INA219 sensors:
double busVoltage_in = 0.0;
double current_in = 0.0;
double busVoltage_out = 0.0;
double current_out = 0.0;
Followed by the PIDOffThreshold variable which contains the threshold value of the input voltage below which the PID is excluded and the Boost Converter is turned off (this value must be determined experimentally by checking whether below it the PID in operation is still able to regulate the output voltage), the definition of the Setpoint variable (which will be valorised in the setup function and will contain the desired output voltage value), the definition of the disabledPID flag which determines when the Boost is on or must be off, the initialisation of the server that manages the REST API and listens on port 80:
double PIDOffThreshold = 0.9; // below this battery voltage value the PID is excluded and the PWM is set to 0 (BOOST off)
double Setpoint;
bool disabledPID = false; // flag to turn off Boost if battery voltage is too low
// Initializing the server on port 80
AsyncWebServer server(80);
We instantiate the two INA219 input and output sensors by passing them the I2C addresses:
INA219_WE ina219_in = INA219_WE(I2C_ADDRESS_in);
INA219_WE ina219_out = INA219_WE(I2C_ADDRESS_out);
Then follows the definition of the PWM signal parameters:
// PWM parameters
const int pwmPin = 25; // PWM GPIO
const int pwmChannel = 0; // PWM channel
const int pwmFrequency = 62000; // PWM frequency: 62 kHz
const int pwmResolution = 8; // duty cycle resolution: 8 bit (0-255)
double dutyCycle = 128; // Initially sets the duty cycle to 50%
double dutyCycle_percent;
We therefore define the GPIO 25, the channel 0, the frequency of 62 kHz, the resolution of the Duty Cycle equal to 8 (which tells us that the Duty Cycle can only vary between the two extremes 0 and 255), the initial value of the Duty Cycle placed halfway (128) between 0 and 255 and the variable that expresses the Duty Cycle as a percentage.
Then follow the operating parameters of the PID and the creation of the instance that manages the PID:
// PID parameters
double Kp = 2.0, Ki = 5.0, Kd = 1.0;
PID myPID(&busVoltage_out, &dutyCycle, &Setpoint, Kp, Ki, Kd, DIRECT);
Then follows the setup function. First, the serial port is initialized, the SPIFFS file system is initialized (if the initialization fails the sketch stops on return;), the Setpoint is set to the desired value (3.3V), the I2C bus is initialized (Wire.begin ();) and then initialized the two INA219 sensors:
// put your setup code here, to run once:
Serial.begin(115200);
delay(2000);
if (!SPIFFS.begin(true)) {
Serial.println("An error has occurred while mounting SPIFFS");
return;
}
Serial.println("SPIFFS mounted successfully");
Setpoint = 3.3;
Wire.begin();
while(!ina219_in.init()){
Serial.println("INA219 in not connected!");
}
while(!ina219_out.init()){
Serial.println("INA219 out not connected!");
}
The setup function continues, leaving aside the commented explanatory parts, with the setting of the two sensors:
ina219_in.setADCMode(SAMPLE_MODE_128); // choose mode and uncomment for change of default
ina219_out.setADCMode(SAMPLE_MODE_128); // choose mode and uncomment for change of default
ina219_in.setMeasureMode(CONTINUOUS);
ina219_out.setMeasureMode(CONTINUOUS);
ina219_in.setPGain(PG_320);
ina219_out.setPGain(PG_320);
ina219_in.setBusRange(BRNG_16);
ina219_out.setBusRange(BRNG_16);
ina219_in.setShuntSizeInOhms(0.1);
ina219_out.setShuntSizeInOhms(0.1);
I refer the reader to the comments in the code for the precise explanation of the previous functions.
The setup function continues by immediately carrying out a reading of the sensors:
busVoltage_in = ina219_in.getBusVoltage_V();
current_in = ina219_in.getCurrent_mA();
busVoltage_out = ina219_out.getBusVoltage_V();
current_out = ina219_out.getCurrent_mA();
Then follows the section that sets the values for the PWM:
// PWM section
// Configures the PWM channel
ledcSetup(pwmChannel, pwmFrequency, pwmResolution);
// Assign the PWM channel to the pin
ledcAttachPin(pwmPin, pwmChannel);
// Sets the initial duty cycle
ledcWrite(pwmChannel, dutyCycle);
the section that sets the PID in automatic operating mode and that sets the Duty Cycle limits of the generated PWM signal:
myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(10, 250); // PWM range 0-255
Then follows the section that manages WiFi:
// WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
// it is a good practice to make sure your code sets wifi mode how you want it.
//WiFiManager, Local intialization. Once its business is done, there is no need to keep it around
WiFiManager wm;
// reset settings - wipe stored credentials for testing
// these are stored by the esp library
// wm.resetSettings();
// Automatically connect using saved credentials,
// if connection fails, it starts an access point with the specified name ( "AutoConnectAP"),
// if empty will auto generate SSID, if password is blank it will be anonymous AP (wm.autoConnect())
// then goes into a blocking loop awaiting configuration and will return success result
bool res;
// res = wm.autoConnect(); // auto generated AP name from chipid
res = wm.autoConnect("AutoConnectAP"); // anonymous ap
//res = wm.autoConnect("ESP32_AP","password"); // password protected ap
if(!res) {
Serial.println("Failed to connect");
// ESP.restart();
}
else {
//if you get here you have connected to the WiFi
Serial.println("Connected...yeey :)");
Serial.println("My WiFi IP is: ");
Serial.print(WiFi.localIP());
Serial.println();
}
We will see in a later paragraph how to connect the ESP32 to Wifi with this system.
The setup function ends with the definition of the REST API exposed by the ESP32:
The first, very simple, takes the index.html page from the SPIFFS file system and shows it on the browser at the IP assigned by the router to the ESP32:
// Serve the index.html file from SPIFFS
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/index.html", "text/html");
});
The second, api/get-parameters, returns all the operating parameters of the Boost Converter and the PID in the form of a Json document:
// API to get system parameters
server.on("/api/get-parameters", HTTP_GET, [](AsyncWebServerRequest *request){
String jsonResponse;
StaticJsonDocument<200> jsonDoc;
jsonDoc["voltage_in"] = busVoltage_in;
jsonDoc["current_in"] = current_in;
jsonDoc["voltage_out"] = busVoltage_out;
jsonDoc["current_out"] = current_out;
jsonDoc["pwm_value"] = dutyCycle;
jsonDoc["duty_cycle"] = dutyCycle_percent;
jsonDoc["pid_status"] = !disabledPID ? "ON" : "OFF";
jsonDoc["Ki"] = Ki;
jsonDoc["Kp"] = Kp;
jsonDoc["Kd"] = Kd;
serializeJson(jsonDoc, jsonResponse);
request->send(200, "application/json", jsonResponse);
});
The third, api/set-pid, updates the values of the PID operating parameters (Kp, Ki, Kd) with the values that we enter via the API:
// API for setting PID parameters
server.on("/api/set-pid", HTTP_GET, [](AsyncWebServerRequest *request){
if (request->hasParam("Kp") && request->hasParam("Ki") && request->hasParam("Kd")) {
Kp = request->getParam("Kp")->value().toDouble();
Ki = request->getParam("Ki")->value().toDouble();
Kd = request->getParam("Kd")->value().toDouble();
// Aggiorna i valori del PID
myPID.SetTunings(Kp, Ki, Kd);
String response = "PID updated: Kp=" + String(Kp) + ", Ki=" + String(Ki) + ", Kd=" + String(Kd);
request->send(200, "text/plain", response);
} else {
request->send(400, "text/plain", "Missing parameters");
}
});
We can vary the parameters of the PID by trial and error and see if we can improve its dynamics and precision.
Then follows the api/reset api which resets the ESP32 by pressing the appropriate button on the web interface and the function which starts the web server:
// API for resetting ESP32
server.on("/api/reset", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/plain", "Resetting ESP32...");
delay(1000); // wait before resetting to send response
ESP.restart(); // restart the microcontroller
});
// Starting the web server
server.begin();
Once the setup function is finished, the loop function begins.
Initially the disabledPID flag is checked. If it has been set to true, the PWM is turned off (and consequently the Boost Converter), otherwise the PID is called into play which will calculate the appropriate value of the PWM Duty Cycle:
if(disabledPID) {
dutyCycle = 0; // Boost turned off
} else {
// PWM controlled by the PID only if disabledPID is false
myPID.Compute();
}
Then there is the check on the input voltage to see if it is appropriate to turn off the Boost. If the input voltage is lower than the PIDOffThreshold threshold value (to be determined experimentally), the disabledPID flag is set to true causing the previous if block to turn off the Boost by setting the Duty Cycle of the PWM signal to 0 at the next loop
// here we decide if PID must be disabled and Boost turned off
if(busVoltage_in < PIDOffThreshold) {
disabledPID = true;
}
In the next block of instructions, the Duty Cycle of the PWM signal is updated and its value is calculated in percentage terms and the INA219 sensors are reread to update the input and output voltage and current values:
ledcWrite(pwmChannel, dutyCycle); // update the duty cycle
dutyCycle_percent = dutyCycle * 100 / 255;
busVoltage_in = ina219_in.getBusVoltage_V();
current_in = ina219_in.getCurrent_mA();
busVoltage_out = ina219_out.getBusVoltage_V();
current_out = ina219_out.getCurrent_mA();
Finally we find a block which, if activated via the #define DEBUGSERIAL as explained at the beginning of the sketch, prints the various system operating information on the Serial Monitor:
#ifdef DEBUGSERIAL
Serial.println();
Serial.print(busVoltage_in);
Serial.print(", ");
Serial.print(current_in);
Serial.print(", ");
Serial.print(busVoltage_out);
Serial.print(", ");
Serial.print(current_out);
Serial.print(", ");
Serial.print("DC: ");
Serial.print(dutyCycle);
Serial.print(", ");
Serial.print("DC % ");
Serial.print(dutyCycle_percent);
Serial.print("%");
Serial.print(", ");
Serial.print("disabledPID ");
Serial.print(disabledPID);
Serial.print(", ");
Serial.print("Kp ");
Serial.print(Kp);
Serial.print(", ");
Serial.print("Ki ");
Serial.print(Ki);
Serial.print(", ");
Serial.print("Kd ");
Serial.print(Kd);
#endif
delay(10);
How to connect the board to the Internet
After uploading the sketch to the board, open the Serial Monitor to see the messages coming from the device.
First the board goes into Access Point mode and will provide us with an IP address that we will use shortly. This operation is used to connect the board to the Internet without having to enter the WiFi network parameters (SSID and password) in the code.
In this case the IP address is 192.168.4.1.
At this point the ESP32 is in Access Point mode (with AutoConnectAP SSID) and we need to connect our computer to the AutoConnectAP network. If we go to the networks menu of our computer, we should also see the AutoConnectAP network in the list of wireless networks.
Connect your computer to the AutoConnectAP network. Then go to your browser and enter the IP previously provided by ESP32 (which in this example is 192.168.4.1)
You will see a screen like this:
Click the ConfigureWiFi button. It will show you the available networks:
Choose your network’s SSID:
Enter your network password and click the save button:
The ESP32 module keeps the access parameters stored even if you turn it off, it will remember them when restarting and will reconnect automatically without having to repeat this procedure. Only if you reset it by uncommenting this line
// wm.resetSettings();
it will lose the connection parameters.
Among the various messages that will be printed on the Serial Monitor, the WiFiManager library will also tell us which IP has been assigned to it by the WiFi modem.
Please Note: the device can only store one network. If you later connect it to another network, it will forget the settings of the previous network.
The monitoring web page and REST APIs
Once the ESP32 has been connected to the WiFi network it will provide us with its IP address via the PlatformIO Serial Monitor, as visible in the following figure:
In this case the IP assigned by the router to the board is 192.168.1.129. It is absolutely not certain that in your case the same IP will be assigned (in fact, it will most likely be different). This IP will be used to view the web page and to compose the REST API.
To view the web page, simply type the IP address assigned to the ESP32 in the browser bar of your PC or smartphone. Below is an example of the web page at the address 192.168.1.129 which is what my WiFi router has assigned to my ESP32:
As already mentioned above, to interact with the board’s REST API we need a specific software called Postman. After installing the program, we are ready to use it.
This is what its home screen looks like:
In the main window there is a bar where you will have to enter the API.
To the left of this bar there is a drop-down menu that allows you to choose the type of API (for example GET, POST, PUT…).
Suppose we want to update the PID parameters in this way: Kp=3.0, Ki=2.0 and Kd=5.0 (these are random values).
Choose the GET type and enter the set-pid API that will have the following format:
http://<ESP32_IP>/api/set-pid?Kp=3.0&Ki=2.0&Kd=5.0
For example, in this case, since the assigned IP is 192.168.1.129, the API URL will be:
http://192.168.1.129/api/set-pid?Kp=3.0&Ki=2.0&Kd=5.0
Obviously you will have to enter the IP address assigned to your ESP32.
Press the Send button on the right.
The API will return a message like this: PID updated: Kp=3.00, Ki=2.00, Kd=5.00
Now we want to test the other API, get-parameters, which returns the system operating parameters on a Json document.
Choose the GET type and enter the get-parameters API which will have the format:
http://<ESP32_IP>/api/get-parameters
In the case of my IP it would be of the form:
http://192.168.1.129/api/get-parameters
Press the Send button on the right.
If all goes well, Postman will show you the requested data in a form similar to this:
{
"voltage_in": 1.519999981,
"current_in": 20.00000038,
"voltage_out": 3.019999981,
"current_out": 10.10000038,
"pwm_value": 137.7280003,
"duty_cycle": 54.0198049,
"pid_status": "Attivo",
"Ki": 5,
"Kp": 2,
"Kd": 1
}
In any case, you will see in more detail the functioning of the system and the REST APIs used with Postman in the following video (with subtitles):
Oscillograms of two use cases
In the following image we see a real photograph of the oscilloscope which captures the operation of the Boost Converter in the 330Ω load case. In this case the time base is set to 5µs:
Operation is quite smooth. The violet trace is the PWM control signal generated by the ESP32, the light blue signal is the one present on the MOSFET Drain and the yellow signal is the output voltage on the load. The oscillations corresponding to the switching can be seen on the yellow trace.
In the following image we see a real photograph of the oscilloscope which captures the operation of the Boost Converter in the 33Ω load case. :
In this case the time base is set to 20µs. The thing to note is that the switching (the ripple) on the yellow trace (the output voltage) is much more noticeable as the load is now drawing much more current. Despite everything, the PID still manages to regulate the voltage to the required value of 3.3V.
Newsletter
If you want to be informed about the release of new articles, subscribe to the newsletter. Before subscribing to the newsletter read the page Privacy Policy (UE)
If you want to unsubscribe from the newsletter, click on the link that you will find in the newsletter email.