0% found this document useful (0 votes)
144 views20 pages

PID Control (OLED Display - HDMI Output)

This document describes an Arduino PID control experiment using an Arduino Uno, potentiometers, LCD display, and OLED display. The experiment code implements PID control to regulate an output value based on an input sensor reading and PID parameters adjusted by potentiometers. The code displays the dynamic input, output, and PID parameter values on the LCD and OLED screens in real-time. Turning the potentiometers demonstrates how the PID parameters affect the system response and ability to reach the setpoint value.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
144 views20 pages

PID Control (OLED Display - HDMI Output)

This document describes an Arduino PID control experiment using an Arduino Uno, potentiometers, LCD display, and OLED display. The experiment code implements PID control to regulate an output value based on an input sensor reading and PID parameters adjusted by potentiometers. The code displays the dynamic input, output, and PID parameter values on the LCD and OLED screens in real-time. Turning the potentiometers demonstrates how the PID parameters affect the system response and ability to reach the setpoint value.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 20

Proportional-Integral-Derivative (PID) Control

OLED Display/HDMI Output) with


Microcontrollers
Code 1: Arduino Uno ................................................................................................................. 2
Code 2: ESP32 C++ ................................................................................................................. 10
Code 3: ESP32 MicroPython ................................................................................................... 13
Code 4: Raspberry Pi 4 Python (OLED) .................................................................................. 16
Code 5: Raspberry Pi 4 Python (HDMI- Tkinter) .................................................................... 19

1
Code 1: Arduino Uno

Experiment Name:Arduino PID Control Experiment (OLED Display Output)


Objective: To learn the basics of PID control using Arduino, potentiometer, and I2C LCD
display.
Materials:
1. Arduino UNO
2. 3 potentiometers
3. I2C LCD display
4. OLED display • Relay or LED (an additional power source may be required when
using a relay)
5. Breadboard and jumper wires
Structure:
1. Setup:
1. Connect the middle leg of a potentiometer to the A0 pin. (This represents our
sensor)
2. Connect the middle legs of the other potentiometers to the A1, A2, and A3 pins
respectively.
3. Connect pin 13 to the control pin of the relay (or the anode of the LED).
4. Connect the SDA and SCL pins of the I2C LCD and OLED displays to the
Arduino’s SDA (A4) and SCL (A5) pins respectively.
2. Experiment Code:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
#include <PID_v1.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
// I2C LCD definition
LiquidCrystal_I2C lcd(0x27, 16, 2); // Address, rows, columns
// OLED screen definition
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);
// PID Variables double setPoint = 100;
double input, output; double Kp, Ki, Kd;

2
PID myPID(&input, &output, &setPoint, Kp, Ki, Kd, DIRECT);
void setup() {
// Define pin for relay pinMode(13, OUTPUT);
// Initialize LCD lcd.init(); lcd.backlight();
// Initialize OLED screen
if (!display.begin(SSD1306_I2C_ADDRESS, OLED_RESET)) {
lcd.print(“OLED init failed”);
for (;;);
}
display.display();
delay(2000);
display.clearDisplay();
// Start PID myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(0, 255);
// Set a range of 0-255 as an example }
void loop() {
// Read PID constants from pots
Kp = analogRead(A1) / 1023.0 * 10;
// Scale to get a value between 0-10
Ki = analogRead(A2) / 1023.0 * 10; // Scale to get a value between 0-10
Kd = analogRead(A3) / 1023.0 * 10; // Scale to get a value between 0-10
myPID.SetTunings(Kp, Ki, Kd);
// Sensor reading (in this case a potentiometer)
input = analogRead(A0);
// PID calculation
myPID.Compute();
// Relay control
if (output > 127) {
digitalWrite(13, HIGH);
} else {
digitalWrite(13, LOW);
}
// Print values to LCD screen lcd.setCursor(0, 0);
lcd.print("Input: ");
lcd.print(input); lcd.setCursor(0, 1);

3
lcd.print("Output: ");
lcd.print(output);
// Print PID values to OLED screen
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.print("Kp: ");
display.println(Kp);
display.print("Ki: ");
display.println(Ki);
display.print("Kd: ");
display.println(Kd);
display.display();
delay(100); // Wait a bit and continue the loop.
}

3. Uploading the Code:


• Upload the previously provided Arduino code to the Arduino.
4. Conducting the Experiment:
1. Power the Arduino.
2. Observe the “Input” and “Output” values on the LCD screen.
3. Observe the Kp, Ki, and Kd values on the OLED screen.
4. Turn the “Sensor” potentiometer (connected to A0) to change the input value and
observe how the output value changes.
5. Turn the other potentiometers to change the Kp, Ki, and Kd values. Observe the
effects of these changes on the system.
6. Observe how the system responds as you adjust the PID parameters.

Results and Observations:

• The system successfully displays the dynamic changes in the PID control as the
potentiometers are adjusted.
• The relay, acting as a simulated actuator, responds to the PID output, indicating the
controller’s decisions.
• Both the LCD and OLED screens provide a real-time visualization of the system’s
performance, with the LCD showing the input-output relationship and the OLED
presenting the PID parameters.

4
Explanation of the Code
1. Library Imports:

• <Wire.h>: This library is used for I2C communication, which is required for devices
like the LCD and OLED screens.
• <LiquidCrystal_I2C.h>: Library for controlling I2C-based Liquid Crystal Displays
(LCDs).
• <Adafruit_SSD1306.h> and <Adafruit_GFX.h>: Libraries for controlling the
SSD1306-based OLED display.
• <PID_v1.h>: This is the library used for implementing the PID control logic.
2. Constant and Variable Definitions:
• SCREEN_WIDTH and SCREEN_HEIGHT: Define the dimensions of the OLED
display.
• LiquidCrystal_I2C lcd(0x27, 16, 2);: Initialize the LCD with I2C address 0x27 and
specify that it has 16 columns and 2 rows.
• Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);:
Initialize the OLED display with the given width and height, and specify the I2C
communication method.
• PID related variables:
o input, output: Variables to store the input reading and the output of the PID.
o Kp, Ki, Kd: PID constants.
o PID myPID(&input, &output, &setPoint, Kp, Ki, Kd, DIRECT);: Initialize
the PID controller.
3. Setup Function (setup()):

• Pin Definitions:
o pinMode(13, OUTPUT);: Define pin 13 as an OUTPUT (intended for relay
control).
• LCD Initialization:
o lcd.init();: Initialize the LCD.
o lcd.backlight();: Turn on the backlight of the LCD.
• OLED Initialization:
o if (!display.begin(SSD1306_I2C_ADDRESS, OLED_RESET)) {...}:
Attempt to initialize the OLED, and if unsuccessful, display an error message
on the LCD and halt the program.
o display.display(); and display.clearDisplay();: Show the OLED’s initial state
and then clear it.
o

5
• PID Configuration:

o myPID.SetMode(AUTOMATIC);: Set the PID mode to


AUTOMATIC.
o myPID.SetOutputLimits(0, 255);: Define the output range for
the PID, which is between 0 and 255 in this context.

4. Main Loop (loop()):

• Reading PID Constants:


o Kp = analogRead(A1) / 1023.0 * 10; and similar lines: Read the PID
constants Kp, Ki, and Kd from potentiometers connected to analog pins A1,
A2, and A3 respectively, scaling the values to range between 0 and 10.
o myPID.SetTunings(Kp, Ki, Kd);: Update the PID controller with the new
tunings.
• Sensor Reading:
o input = analogRead(A0);: Simulate a sensor reading using a potentiometer
connected to pin A0.
• PID Calculation:
o myPID.Compute();: Perform the PID calculations.
• Actuator (Relay) Control:
o if (output > 127) {...}: Control the relay using pin 13 based on the PID output.
• LCD Display:
o Display the input and output values on the LCD.
• OLED Display:
o Display the PID constants (Kp, Ki, Kd) on the OLED screen.
• delay(100);: Wait for 100 milliseconds before the next iteration of the loop.

6
Discussion Questions about the Experiment:
1. What is the effect on the system when you increase the Kp value?
2. Slowly increase the Ki value from zero. What changes do you observe in the system?
3. What are the effects of increasing or decreasing the Kd value?
4. Which PID value affects the system’s response time the most?
5. What are the advantages and disadvantages of PID control?
6. How would you explain the principles of PID control in your own words?
7. If you redo the experiment with Kp, Ki, and Kd values all set to zero, how does the
system respond? How would you explain this?
8. If you change the system’s setPoint value, how would you explain its impact on the
PID parameters and consequently on the system’s response?
9. If you use an LED instead of a relay, what differences might you observe in the
experiment results and what could be the reasons for these differences?
10. Which PID parameter most affects how quickly the system reaches the desired value?
How is this effect observed?
11. Which PID parameter most affects the oscillations (swings) in the system’s process of
reaching the desired value? How is this effect observed?
12. If a delay is introduced in the system, what would be the effect on the PID control and
the system’s response?
13. What differences do you think might exist between the PID control in this experiment
and PID control in real-world industrial applications?
14. If you set the Ki parameter to a very high value, what kind of behavior might you
observe, and why?
15. Despite the advantages of a PID controller, in what situations might other control
methods be more appropriate?
16. How would you explain the changes in the system’s response caused by increasing the
Kd parameter and the reasons for these changes?
17. What do you think is the importance of PID control for future technological
applications? Please explain with examples.
18. How might the sensitivity or quality of the potentiometers used in this experiment
affect the results?
19. If you increase the noise in the system, how would the performance of the PID control
be affected? How might you reduce this effect?
20. What methods or techniques do you think can be used to optimize PID control?

7
Conceptual Questions about the Experiment:
1. What is the processor architecture of Raspberry Pi? o A) x86 o B) ARM o C) MIPS o
D) SPARC
2. Which component do you need to directly read an analog signal on the Raspberry Pi?
o A) DAC o B) EEPROM o C) ADC o D) Comparator
3. Which Python library is used to control GPIO pins on Raspberry Pi? o A) RPi.GPIO o
B) PiGpio o C) GPIOZero o D) All of the above
4. What does the “P” letter in PID control stand for? o A) Proportional o B) Periodic o C)
Polar o D) Phase
5. In PID control, which component represents the sum of continuous errors? o A)
Proportional o B) Integral o C) Derivative o D) None of the above
6. What is the tkinter library used for? o A) Web development o B) Database
management o C) Creating a Graphic User Interface (GUI) o D) Game development
7. What change does the “D” letter in PID control monitor? o A) Error o B) Total error o
C) Change in error over time o D) Change in setpoint
8. Which option correctly states the voltage that the Raspberry Pi 4’s GPIO pins operate
on? o A) 5V o B) 3.3V o C) 12V o D) 9V
9. For which type of systems is PID control used? o A) Open-loop systems o B) Closed-
loop systems o C) Only mechanical systems o D) Only digital systems
10. Which command runs a script in Python 3 on the Raspberry Pi?
• A) python script.py
• B) py3 script.py
• C) run script.py
• D) python3 script.py
11. For which problem is the “I” letter in PID control added to cope?
• A) System’s late response
• B) System’s fast response
• C) Persistent errors
• D) Temporary errors
12. What is the name of the Linux distribution run on the Raspberry Pi?
• A) Ubuntu
• B) Fedora
• C) Raspbian
• D) CentOS
13. In PID control, which component reduces sudden changes in the system?

8
• A) Proportional
• B) Integral
• C) Derivative
• D) Setpoint
14. What type of USB does the Raspberry Pi have?
• A) USB Type-A
• B) USB Type-B
• C) USB Type-C
• D) USB Mini
15. Which file extension is used when running Python code on Raspberry Pi?
• A) .cpp
• B) .java
• C) .js
• D) .py Answer Key: 1-B, 2-C, 3-D, 4-A, 5-B, 6-C, 7-C, 8-B, 9-B, 10-D, 11-C, 12-C,
13-C, 14-C, 15-D.

9
Code 2: ESP32 C++

#include <Wire.h>

#include <LiquidCrystal_I2C.h>

#include <Adafruit_SSD1306.h>

#include <Adafruit_GFX.h>

#include <PID_v1.h>

#define SCREEN_WIDTH 128

#define SCREEN_HEIGHT 64

// I2C LCD definition

LiquidCrystal_I2C lcd(0x27, 16, 2); // Address, columns, rows

// OLED screen definition

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);

// PID Variables

double setPoint = 100;

double input, output;

double Kp, Ki, Kd;

PID myPID(&input, &output, &setPoint, Kp, Ki, Kd, DIRECT);

void setup() {

// Define the relay pin

pinMode(13, OUTPUT);

// Start the LCD

lcd.init();

lcd.backlight();

// Start the OLED screen

if (!display.begin(SSD1306_I2C_ADDRESS, OLED_RESET)) {

lcd.print("OLED init failed");

10
while (true);

display.display();

delay(2000);

display.clearDisplay();

// Start the PID

myPID.SetMode(AUTOMATIC);

myPID.SetOutputLimits(0, 255); // Example limit set to 0-255

void loop() {

// Read the PID constants from the pots

Kp = analogRead(32) / 4095.0 * 10; // Scaled to a range of 0-10

Ki = analogRead(33) / 4095.0 * 10; // Scaled to a range of 0-10

Kd = analogRead(34) / 4095.0 * 10; // Scaled to a range of 0-10

myPID.SetTunings(Kp, Ki, Kd);

// Read the "sensor" (here it's a potentiometer)

input = analogRead(35);

// Compute the PID

myPID.Compute();

// Control the relay

if (output > 127) {

digitalWrite(13, HIGH);

} else {

digitalWrite(13, LOW);

// Print the values to the LCD

lcd.setCursor(0, 0);

11
lcd.print("Input: ");

lcd.print(input);

lcd.setCursor(0, 1);

lcd.print("Output: ");

lcd.print(output);

// Print the PID values to the OLED screen

display.clearDisplay();

display.setTextSize(1);

display.setTextColor(SSD1306_WHITE);

display.setCursor(0, 0);

display.print("Kp: ");

display.println(Kp);

display.print("Ki: ");

display.println(Ki);

display.print("Kd: ");

display.println(Kd);

display.display();

delay(100);

12
Code 3: ESP32 MicroPython

from machine import Pin, I2C, ADC

import time

import ssd1306

# I2C setup

i2c = I2C(scl=Pin(22), sda=Pin(21), freq=400000)

# OLED setup

oled = ssd1306.SSD1306_I2C(128, 64, i2c)

# ADC setup for ESP32 (reading values between 0 and 4095)

sensor = ADC(Pin(35))

sensor.atten(ADC.ATTN_11DB) # 3.3V range

Kp_pot = ADC(Pin(32))

Ki_pot = ADC(Pin(33))

Kd_pot = ADC(Pin(34))

# Relay setup

relay = Pin(13, Pin.OUT)

# Simple PID control function

class PID:

def __init__(self, kp, ki, kd, set_point):

self.kp = kp

self.ki = ki

self.kd = kd

self.set_point = set_point

self.prev_error = 0

self.integral = 0

def compute(self, current_value):

13
error = self.set_point - current_value

self.integral += error

derivative = error - self.prev_error

output = self.kp*error + self.ki*self.integral + self.kd*derivative

self.prev_error = error

return output

set_point = 100

pid = PID(0, 0, 0, set_point)

while True:

# Read potentiometer values

Kp = Kp_pot.read() / 4095.0 * 10

Ki = Ki_pot.read() / 4095.0 * 10

Kd = Kd_pot.read() / 4095.0 * 10

input_val = sensor.read()

pid.kp = Kp

pid.ki = Ki

pid.kd = Kd

output = pid.compute(input_val)

if output > 0.5: # Arbitrary threshold

relay.on()

else:

relay.off()

# Display values on OLED

oled.fill(0)

oled.text("Kp: " + str(Kp), 0, 0)

oled.text("Ki: " + str(Ki), 0, 10)

oled.text("Kd: " + str(Kd), 0, 20)

oled.text("Input: " + str(input_val), 0, 30)

14
oled.text("Output: " + str(output), 0, 40)

oled.show()

time.sleep(0.1)

15
Code 4: Raspberry Pi 4 Python (OLED)

import time

import smbus

import RPi.GPIO as GPIO

from RPLCD.i2c import CharLCD

# LCD setup

lcd = CharLCD('PCF8574', 0x27)

# OLED setup (with a hypothetical library)

# oled = YourOledLibraryHere

# MCP3008 ADC setup (SPI interface)

import spidev

spi = spidev.SpiDev()

spi.open(0,0)

spi.max_speed_hz = 1350000

def readadc(adcnum):

if adcnum > 7 or adcnum < 0:

return -1

r = spi.xfer2([1, 8 + adcnum << 4, 0])

adcout = ((r[1] & 3) << 8) + r[2]

return adcout

# Relay setup

relay_pin = 17

GPIO.setmode(GPIO.BCM)

GPIO.setup(relay_pin, GPIO.OUT)

# Simple PID control function

class PID:

def __init__(self, kp, ki, kd, set_point):

self.kp = kp

16
self.ki = ki

self.kd = kd

self.set_point = set_point

self.prev_error = 0

self.integral = 0

def compute(self, current_value):

error = self.set_point - current_value

self.integral += error

derivative = error - self.prev_error

output = self.kp*error + self.ki*self.integral + self.kd*derivative

self.prev_error = error

return output

set_point = 100

pid = PID(0, 0, 0, set_point)

while True:

# Read potentiometer values

Kp = readadc(0) / 1023.0 * 10

Ki = readadc(1) / 1023.0 * 10

Kd = readadc(2) / 1023.0 * 10

input_val = readadc(3)

pid.kp = Kp

pid.ki = Ki

pid.kd = Kd

output = pid.compute(input_val)

if output > 0.5: # Arbitrary threshold

GPIO.output(relay_pin, GPIO.HIGH)

else:

GPIO.output(relay_pin, GPIO.LOW)

17
# Display values on LCD

lcd.clear()

lcd.write_string(f"Input: {input_val}\nOutput: {output}")

# Display values on OLED (needs correct OLED library)

# oled.write_text(f"Kp: {Kp}, Ki: {Ki}, Kd: {Kd}")

time.sleep(0.1)

# Cleanup GPIO before exit

GPIO.cleanup()

18
Code 5: Raspberry Pi 4 Python (HDMI- Tkinter)
import time
import smbus
import RPi.GPIO as GPIO
from RPLCD.i2c import CharLCD
import tkinter as tk

# LCD setup
lcd = CharLCD('PCF8574', 0x27)

# MCP3008 ADC setup (SPI interface)


import spidev
spi = spidev.SpiDev()
spi.open(0,0)
spi.max_speed_hz = 1350000

def readadc(adcnum):
if adcnum > 7 or adcnum < 0:
return -1
r = spi.xfer2([1, 8 + adcnum << 4, 0])
adcout = ((r[1] & 3) << 8) + r[2]
return adcout

# Relay setup
relay_pin = 17
GPIO.setmode(GPIO.BCM)
GPIO.setup(relay_pin, GPIO.OUT)

# Simple PID control function


class PID:
def __init__(self, kp, ki, kd, set_point):
self.kp = kp
self.ki = ki
self.kd = kd
self.set_point = set_point
self.prev_error = 0
self.integral = 0

def compute(self, current_value):


error = self.set_point - current_value
self.integral += error
derivative = error - self.prev_error
output = self.kp*error + self.ki*self.integral + self.kd*derivative
self.prev_error = error
return output

set_point = 100
pid = PID(0, 0, 0, set_point)

# Setting up the GUI


root = tk.Tk()
root.title("PID Controller")

frame = tk.Frame(root)
frame.pack(pady=20, padx=20)

lbl_kp = tk.Label(frame, text="Kp: ")

19
lbl_kp.grid(row=0, column=0)
lbl_ki = tk.Label(frame, text="Ki: ")
lbl_ki.grid(row=1, column=0)
lbl_kd = tk.Label(frame, text="Kd: ")
lbl_kd.grid(row=2, column=0)

lbl_input = tk.Label(frame, text="Input: ")


lbl_input.grid(row=3, column=0)
lbl_output = tk.Label(frame, text="Output: ")
lbl_output.grid(row=4, column=0)

def update_gui():
# Read potentiometer values
Kp = readadc(0) / 1023.0 * 10
Ki = readadc(1) / 1023.0 * 10
Kd = readadc(2) / 1023.0 * 10
input_val = readadc(3)

pid.kp = Kp
pid.ki = Ki
pid.kd = Kd

output = pid.compute(input_val)

if output > 0.5: # Arbitrary threshold


GPIO.output(relay_pin, GPIO.HIGH)
else:
GPIO.output(relay_pin, GPIO.LOW)

# Update GUI
lbl_kp.config(text=f"Kp: {Kp:.2f}")
lbl_ki.config(text=f"Ki: {Ki:.2f}")
lbl_kd.config(text=f"Kd: {Kd:.2f}")
lbl_input.config(text=f"Input: {input_val}")
lbl_output.config(text=f"Output: {output:.2f}")

# Update LCD
lcd.clear()
lcd.write_string(f"Input: {input_val}\nOutput: {output:.2f}")

# Schedule the function to run again after 100ms


root.after(100, update_gui)

# Start the loop


update_gui()
root.mainloop()

# Cleanup GPIO before exit


GPIO.cleanup()

20

You might also like