Skip to content

eylloztek/stm32-pid-controller

Repository files navigation

STM32 PID Controller with Python GUI

This project implements a basic PID controller on an STM32 microcontroller and provides a Python-based desktop GUI for real-time monitoring, parameter tuning, and response analysis.

The STM32 side reads an analog feedback signal through ADC, calculates the PID output, writes the control output to DAC, and sends live telemetry data to the PC over UART. The Python GUI communicates with the STM32 board through a serial connection, allows the user to update PID parameters, plots the system response in real time, and calculates common control response metrics such as rise time, settling time, overshoot, and steady-state error.


Features

  • STM32-based PID controller implementation
  • Adjustable Set Point, Kp, Ki, and Kd values from the GUI
  • UART communication between STM32 and Python
  • Real-time plotting of:
    • Measured voltage
    • Set point
    • PID output
  • DAC output generation based on PID output percentage
  • ADC-based feedback reading
  • Response metric calculation:
    • Rise time
    • Settling time
    • Overshoot
    • Steady-state error
  • START / STOP control from the GUI
  • Dynamic graph scaling
  • Serial port selection from the GUI

Project Overview

The system is built around a closed-loop PID control structure.

+----------------+        UART Commands        +----------------+
|                | --------------------------> |                |
|   Python GUI   |                             |     STM32      |
|                | <-------------------------- |                |
+----------------+        UART Telemetry       +----------------+
                                                        |
                                                        | DAC Output
                                                        v
                                                  Controlled System
                                                        |
                                                        | Analog Feedback
                                                        v
                                                      ADC Input

The STM32 continuously performs the following operations:

  1. Reads the feedback voltage from ADC.
  2. Calculates the control error:
error = setPoint - actualValue
  1. Computes the PID output.
  2. Limits the output between the configured minimum and maximum output values.
  3. Converts the PID output percentage into a 12-bit DAC value.
  4. Sends setpoint, voltage, and PID output values to the Python GUI over UART.

The GUI receives this data, parses it, plots the values, and calculates response metrics.


Repository Structure

Recommended repository structure:

STM32-PID-Controller/
├── STM32/
│   ├── Core/
│   │   ├── Inc/
│   │   │   └── pid.h
│   │   └── Src/
│   │       ├── pid.c
│   │       └── main.c
│   └── STM32CubeIDE project files
│
├── GUI/
│   └── pid_controller_gui.py
│
├── README.md
└── LICENSE

If this repository is intended to focus only on the PID library and GUI logic, the structure can be simplified:

STM32-PID-Controller/
├── pid.h
├── pid.c
├── main.c
├── pid_controller_gui.py
├── README.md
└── LICENSE

Hardware Requirements

  • STM32 development board with:
    • ADC input
    • DAC output
    • UART interface
  • PC with USB connection to STM32
  • Analog feedback signal source
  • Optional controlled plant or test circuit
  • Jumper wires
  • Breadboard or external circuit, depending on the application

This project was written for an STM32 HAL-based environment. The code structure is suitable for STM32CubeIDE projects.


STM32 Peripheral Configuration

The current firmware uses the following peripherals:

Peripheral Purpose
ADC1 Reads the feedback voltage
DAC Channel 1 Outputs the PID control signal
USART2 Communicates with the Python GUI
GPIO Board LED/button configuration generated by CubeMX
SysTick / HAL_GetTick Provides timing for the 100 ms control loop

Pin Configuration

The exact pins may vary depending on the STM32 board and CubeMX configuration. For an STM32 Nucleo-F446RE-style setup, the typical configuration is:

Function STM32 Peripheral Typical Pin Description
Feedback Input ADC1_IN0 PA0 Analog feedback voltage input
Control Output DAC_OUT1 PA4 Analog PID output
UART TX USART2_TX PA2 STM32 transmit line to PC / ST-LINK VCP
UART RX USART2_RX PA3 STM32 receive line from PC / ST-LINK VCP
User LED GPIO Output Board-dependent Optional status LED
User Button GPIO Input Pull-up Board-dependent Optional button input

For Nucleo boards, USART2 is commonly routed through the onboard ST-LINK Virtual COM Port. In that case, no external USB-to-UART converter is needed.

Important Notes

  • ADC input voltage must stay within the valid MCU ADC input range.
  • For a 3.3 V STM32 board, do not apply more than 3.3 V to the ADC pin.
  • DAC output range is typically 0 V to VDDA.
  • If the DAC output controls an external circuit, make sure the external circuit input impedance and voltage requirements are compatible with the STM32 DAC output.
  • If an external UART adapter is used, connect GND between STM32 and the adapter.

STM32 Configuration Details

ADC Configuration

The ADC is configured as:

Setting Value
ADC Instance ADC1
Channel ADC_CHANNEL_0
Resolution 12-bit
Conversion Mode Continuous conversion enabled
Trigger Software start
Data Alignment Right aligned
Sampling Time 480 cycles

The ADC value is converted to voltage using:

#define VSTEP 3.3/4096

voltage = (float)(adcValue * VSTEP);

For a 12-bit ADC, the raw ADC range is 0-4095. With a 3.3 V reference, the approximate voltage step is:

3.3 / 4096 ≈ 0.000805 V

DAC Configuration

The DAC is configured as:

Setting Value
DAC Instance DAC
DAC Channel DAC_CHANNEL_1
Trigger None
Output Buffer Enabled
Alignment 12-bit right aligned

The PID output is treated as a percentage value between 0.0 and 100.0. It is mapped to the DAC range using:

return (uint16_t)((percent / 100.0f) * 4095.0f + 0.5f);

So:

PID Output DAC Value Approximate Output
0% 0 0 V
50% ~2048 ~1.65 V
100% 4095 ~3.3 V

UART Configuration

The UART is configured as:

Setting Value
UART Instance USART2
Baudrate 115200
Word Length 8 bits
Stop Bits 1
Parity None
Hardware Flow Control None
Oversampling 16

The STM32 sends telemetry data to the Python GUI using this format:

SetPoint: 2.500 Voltage: 2.4812 PIDOutput: 75.400

The actual firmware format is:

"SetPoint: %.3f Voltage: %.4f PIDOutput: %.3f\r\n"

The GUI parses this format using a regular expression, so small variations such as : or = can also be supported.


UART Commands Sent by the GUI

The GUI sends text-based commands to the STM32.

Command Example Description
SETPOINT: SETPOINT:2.5 Sets the PID setpoint
SET_SP: SET_SP:2.5 Alternative setpoint command
KP: KP:20.0 Sets proportional gain
SET_KP: SET_KP:20.0 Alternative Kp command
KI: KI:15.0 Sets integral gain
SET_KI: SET_KI:15.0 Alternative Ki command
KD: KD:0.2 Sets derivative gain
SET_KD: SET_KD:0.2 Alternative Kd command
START START Enables PID calculation
STOP STOP Disables PID calculation

When the GUI START button is pressed, it sends all PID parameters first and then sends the START command.


PID Controller Implementation

The PID controller is implemented in pid.c and pid.h.

PID Structure

The PID controller stores:

  • Proportional gain: Kp
  • Integral gain: Ki
  • Derivative gain: Kd
  • Setpoint
  • Integral accumulator
  • Previous error
  • Output value
  • Minimum output limit
  • Maximum output limit
  • Sampling time

PID Calculation

The controller calculates:

error = setPoint - actualValue

Proportional term:

P = Kp * error

Integral accumulation:

integral_sum += error * sampleTime

Integral term:

I = Ki * integral_sum

Derivative term:

D = Kd * (error - previousError) / sampleTime

Final output:

output = P + I + D

The output is limited between minOutput and maxOutput.

Default PID Values

The default firmware initializes the PID controller as:

PID_Init(&pid, 20.0f, 15.0f, 0.2f, 0.1f, 0.0f, 100.0f);
PID_SetPoint(&pid, 2.5f);
Parameter Default Value
Kp 20.0
Ki 15.0
Kd 0.2
Sample Time 0.1 s
Minimum Output 0.0
Maximum Output 100.0
Initial Setpoint 2.5

Control Loop Timing

The main control loop executes every 100 ms:

if (currentTime - lastTime >= 100) {
    ...
}

This means the effective PID sample time is:

100 ms = 0.1 s

The PID initialization also uses 0.1f as the sample time. This should remain consistent unless the control loop period is changed.


Python GUI Overview

The GUI is implemented with:

  • tkinter for the interface
  • pyserial for UART communication
  • matplotlib for real-time plotting
  • deque buffers for efficient live graph data storage
  • threading and queue for safe serial reading

The GUI provides:

  • COM port selection
  • Baudrate selection
  • Stop bit selection
  • Parity selection
  • Connect / Disconnect buttons
  • Setpoint input
  • Kp input
  • Ki input
  • Kd input
  • START / STOP buttons
  • Clear Graph button
  • Real-time PID response graph
  • Response metric display panel

Python Requirements

Install the required Python packages:

pip install pyserial matplotlib opentelemetry-api

tkinter is usually included with standard Python installations. If it is missing on Linux, install it using your package manager. For example:

sudo apt install python3-tk

Note About opentelemetry

The current GUI code imports:

from opentelemetry import metrics

If this import remains in the code, opentelemetry-api must be installed. If the import is removed later, this package is no longer required.


Running the STM32 Firmware

  1. Open the STM32 project in STM32CubeIDE.
  2. Verify the peripheral configuration:
    • ADC1 enabled on the selected feedback input pin.
    • DAC channel 1 enabled.
    • USART2 configured at 115200 baud, 8N1.
  3. Build the project.
  4. Flash the firmware to the STM32 board.
  5. Open the Python GUI on the PC.
  6. Select the correct COM port.
  7. Click Connect.
  8. Enter the desired PID parameters.
  9. Click START.

Running the Python GUI

Navigate to the GUI folder and run:

python pid_controller_gui.py

The GUI window should open with:

  • Connection settings on the left side
  • PID parameter inputs below the connection section
  • START / STOP buttons
  • Response metrics panel
  • Real-time graph on the right side

GUI Usage

1. Connect to STM32

Select the correct COM port and use these default serial settings:

Setting Value
Baudrate 115200
Stop Bits One
Parity None

Click Connect. The status label should change from DISCONNECTED to CONNECTED.

2. Set PID Parameters

The GUI includes input fields for:

  • Set Point
  • Kp
  • Ki
  • Kd

You can update each parameter individually using the corresponding SET button.

When START is clicked, the GUI sends all parameter values to STM32 before starting the PID controller.

3. Start PID Control

Click START.

Expected behavior:

  • The graph is cleared.
  • Plotting is enabled.
  • STM32 receives the latest setpoint and PID gain values.
  • STM32 enables PID calculation.
  • Voltage, setpoint and PID output values begin appearing on the graph.

4. Stop PID Control

Click STOP.

Expected behavior:

  • GUI plotting stops.
  • STM32 receives the STOP command.
  • PID output is forced to zero on the STM32 side.

5. Clear Graph

Click CLEAR GRAPH to remove existing graph data and reset response metrics.


Expected GUI Output

During operation, the graph should show three curves:

Curve Meaning
Voltage Measured ADC feedback voltage
Set Point Desired target value
PID Output PID output percentage

The voltage curve should move toward the setpoint. The PID output curve should change according to the controller response.

A typical stable response may look like this:

Voltage starts below the setpoint
Voltage rises toward the setpoint
PID output changes rapidly at first
PID output stabilizes as voltage approaches the target
Steady-state error becomes small

The response metrics panel should display values such as:

Rise Time:          5.90 s
Settling Time:      11.90 s
Overshoot:          0.00 % (0.000 V)
Steady State Error: 0.0036 V

The exact values depend on the physical system, PID gains, setpoint, ADC noise, and DAC output behavior.

Example Output:

image

Response Metrics

Rise Time

Rise time is calculated as the time required for the voltage response to move from 10% to 90% of the step amplitude.

rise time = t90 - t10

If the system starts very close to the setpoint, rise time may not be meaningful.

Settling Time

Settling time is the first time at which the voltage enters the settling band and stays there.

The GUI uses a tolerance based on approximately ±2% of the setpoint, with a minimum threshold to avoid overly small bands.

Overshoot

Overshoot indicates how much the voltage exceeds the setpoint.

For an upward step:

overhoot = peak voltage - setpoint

If the voltage never exceeds the setpoint, overshoot is shown as 0.00% after the response settles.

Steady-State Error

Steady-state error is calculated from the average of recent voltage samples:

steady-state error = |setpoint - average recent voltage|

This value is updated continuously while data is being received.


UART Data Format

The STM32 sends telemetry in this format:

SetPoint: 2.500 Voltage: 2.4812 PIDOutput: 75.400

The GUI extracts the following fields:

  • SetPoint
  • Voltage
  • PIDOutput

The parser accepts both : and = separators and normalizes field names by removing spaces, underscores and hyphens.

Supported examples:

SetPoint:3.00 Voltage:2.85 PIDOutput:1.50
SetPoint: 3.00 Voltage: 2.85
Setpoint:3.00, Voltage:2.85
SETPOINT=3.00 VOLTAGE=2.85

Practical Test Setup

A simple test setup can be created by feeding the DAC output into an external circuit and reading the resulting feedback voltage with the ADC.

For a basic electrical loop test:

STM32 DAC_OUT1  --->  External circuit / filter / plant input
External output --->  STM32 ADC1_IN0
GND             --->  Common ground

For simple debugging only, a direct DAC-to-ADC connection may be used carefully, but this does not represent a real physical plant. A real control system should include a plant or circuit whose behavior can be controlled and measured.


Debugging Tips

GUI Connects but No Data Appears

Check:

  • Correct COM port selected
  • Baudrate is 115200
  • STM32 firmware is running
  • USART2 is correctly configured
  • ST-LINK Virtual COM Port driver is installed
  • START button has been pressed

Data Appears in Serial Terminal but Not in GUI

Check whether the UART format includes at least:

SetPoint
Voltage

The GUI requires both fields to plot data.

Graph Looks Too Noisy

Possible causes:

  • ADC noise
  • Very narrow y-axis scaling
  • Low UART numeric precision
  • Unfiltered feedback signal
  • Unstable PID gains

The GUI uses dynamic y-axis scaling, so small ADC variations may look visually large when the voltage is already close to the setpoint.

PID Output Does Not Change

Check:

  • START command was sent
  • pidEnabled is set to 1
  • Setpoint differs from actual voltage
  • PID gains are not all zero
  • ADC value changes correctly

Setpoint in GUI Does Not Match STM32 Behavior

The value displayed in the GUI entry field is not automatically active on the STM32 until it is sent. Press the corresponding SET button or press START, which sends all parameters before enabling PID control.


Common Improvements

Possible future improvements:

  • Add PID anti-windup based on output saturation behavior
  • Add derivative filtering
  • Add ADC moving average or low-pass filtering
  • Add CSV logging from the GUI
  • Add automatic COM port detection
  • Add selectable graph time window
  • Add save/load PID parameter presets
  • Add serial command acknowledgment from STM32
  • Add error messages for invalid UART commands
  • Add a real-time numeric dashboard for voltage and PID output

Safety Notes

  • Do not apply voltages higher than the STM32 ADC reference voltage to ADC pins.
  • Always connect common ground when using external circuits.
  • DAC output current capability is limited; do not drive heavy loads directly from the DAC pin.
  • Use buffer circuits or drivers when controlling motors, heaters, valves, or other power devices.
  • Test PID parameters carefully before connecting the output to a real actuator.

License

This project is licensed under the MIT License. See the LICENSE file for details.

About

STM32 Nucleo F446RE PID controller with Python GUI

Topics

Resources

License

Stars

2 stars

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors