Skip to content

Commit db6115a

Browse files
author
David R Forrest
committed
Add examples/PID_simulated_heater_disp with LCD
1 parent ee65466 commit db6115a

File tree

2 files changed

+191
-1
lines changed

2 files changed

+191
-1
lines changed
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/********************************************************
2+
PID Basic simulated heater Example
3+
Reading simulated analog input 0 to control analog PWM output 3
4+
********************************************************/
5+
// This simulates a 20W heater block driven by the PID
6+
// Vary the setpoint with the Pot, and watch the heater drive the temperature up
7+
//
8+
// Simulation at https://wokwi.com/projects/359088752027305985
9+
//
10+
// Based on
11+
// Wokwi https://wokwi.com/projects/357374218559137793
12+
// Wokwi https://wokwi.com/projects/356437164264235009
13+
14+
#include "PID_v1.h" // https://github.com/br3ttb/Arduino-PID-Library
15+
// local copy of .h and .cpp are tweaked to expose the integral per
16+
// https://github.com/br3ttb/Arduino-PID-Library/pull/133
17+
#define USE_HACK // access the PID.outputSum variable
18+
19+
//Define Variables we'll be connecting to
20+
double Setpoint, Input, Output;
21+
22+
//Specify the links and initial tuning parameters
23+
//double Kp = 20, Ki = .01, Kd = 10; // works reasonably with sim heater block fo 220deg
24+
double Kp = 25.5, Ki = 0.1, Kd = 0; // +/-10°proportional band
25+
//double Kp = 255, Ki = 0.05, Kd = 0; // works reasonably with sim heater block
26+
//double Kp = 255, Ki = .0, Kd = 0; // +/-1° proportional band works reasonably with sim heater block
27+
//double Kp = 10000, Ki = 0.0, Kd = 0.0; // bang-bang
28+
//double Kp = 2, Ki = 0.0, Kd = 0.0; // P-only
29+
//double Kp = 2, Ki = 5, Kd = 1; // commonly used defaults
30+
31+
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, P_ON_E, DIRECT);
32+
33+
const int PWM_PIN = 3; // UNO PWM pin for Output
34+
const int INPUT_PIN = -1; // Analog pin for Input (set <0 for simulation)
35+
const int SETPOINT_PIN = A1; // Analog pin for Setpoint Potentiometer
36+
const int AUTOMATIC_PIN = 8; // Pin for controlling manual/auto mode, NO
37+
const int OVERRIDE_PIN = 12; // Pin for integral override, NO
38+
const int PLUS_PIN = 4; // Pin for integral override, NO
39+
const int MINUS_PIN = 7; // Pin for integral override, NO
40+
const int LCD_SDA_PIN = A4; // Used by LiquidCrystal_I2C
41+
const int LCD_SCL_PIN = A5; // Used by LiquidCrystal_I2C
42+
43+
#include <LiquidCrystal_I2C.h> // https://github.com/johnrickman/LiquidCrystal_I2C
44+
45+
#define I2C_ADDR 0x27
46+
#define LCD_COLUMNS 20
47+
#define LCD_LINES 4
48+
49+
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES); // uses SDA=A4,SCL=A5 on Uno
50+
51+
void setup()
52+
{
53+
Serial.begin(115200);
54+
Serial.println(__FILE__);
55+
myPID.SetOutputLimits(0, 255); // -4 for
56+
pinMode(OVERRIDE_PIN, INPUT_PULLUP);
57+
pinMode(AUTOMATIC_PIN, INPUT_PULLUP);
58+
pinMode(MINUS_PIN, INPUT_PULLUP);
59+
pinMode(PLUS_PIN, INPUT_PULLUP);
60+
Setpoint = 0;
61+
//turn the PID on
62+
myPID.SetMode(AUTOMATIC);
63+
if(INPUT_PIN>0){
64+
Input = analogRead(INPUT_PIN);
65+
}else{
66+
Input = simPlant(0.0,1.0); // simulate heating
67+
}
68+
lcd.init();
69+
lcd.backlight();
70+
71+
Serial.println("Setpoint Input Output Integral");
72+
}
73+
74+
void loop()
75+
{
76+
// gather Input from INPUT_PIN or simulated block
77+
float heaterWatts = Output * 20.0 / 255; // 20W heater
78+
if (INPUT_PIN > 0 ) {
79+
Input = analogRead(INPUT_PIN);
80+
} else {
81+
float blockTemp = simPlant(heaterWatts,Output>0?1.0:1-Output); // simulate heating
82+
Input = blockTemp; // read input from simulated heater block
83+
}
84+
85+
if (myPID.Compute())
86+
{
87+
//Output = (int)Output; // Recognize that the output as used is integer
88+
analogWrite(PWM_PIN, Output);
89+
90+
}
91+
92+
Setpoint = analogRead(SETPOINT_PIN) / 4; // Read setpoint from potentiometer
93+
if(digitalRead(OVERRIDE_PIN)==LOW) mySetIntegral(&myPID,0); // integral override
94+
if(digitalRead(AUTOMATIC_PIN)==HIGH != myPID.GetMode()==AUTOMATIC){
95+
myPID.SetMode(digitalRead(AUTOMATIC_PIN)==HIGH ? AUTOMATIC :MANUAL);
96+
}
97+
static uint32_t lastButton = 0;
98+
if(myPID.GetMode()==MANUAL && millis() - lastButton > 250){
99+
if(digitalRead(PLUS_PIN)==LOW){
100+
Output += 1;
101+
lastButton = millis();
102+
}
103+
if(digitalRead(MINUS_PIN)==LOW){
104+
Output -= 1;
105+
lastButton = millis();
106+
}
107+
}
108+
109+
report();
110+
reportLCD();
111+
112+
}
113+
114+
void report(void)
115+
{
116+
static uint32_t last = 0;
117+
const int interval = 250;
118+
if (millis() - last > interval) {
119+
last += interval;
120+
// Serial.print(millis()/1000.0);
121+
Serial.print("SP:");Serial.print(Setpoint);
122+
Serial.print(" PV:");
123+
Serial.print(Input);
124+
Serial.print(" CV:");
125+
Serial.print(Output);
126+
Serial.print(" Int:");
127+
#if defined(USE_HACK)
128+
Serial.print(myPID.outputSum);
129+
#endif
130+
Serial.print(' ');
131+
Serial.println();
132+
}
133+
}
134+
135+
void reportLCD(void)
136+
{
137+
static uint32_t last = 0;
138+
const int interval = 250;
139+
if (millis() - last > interval) {
140+
last += interval;
141+
// Serial.print(millis()/1000.0);
142+
// lcd.clear();
143+
lcd.setCursor(0,0);
144+
lcd.print("PV:");
145+
lcd.print(Input,3);
146+
lcd.print(" CV:");
147+
lcd.print(Output,3);
148+
lcd.print(" ");
149+
lcd.setCursor(0,1);
150+
lcd.print("SP:");
151+
lcd.print(Setpoint,3);
152+
lcd.print(myPID.GetMode()==AUTOMATIC? " Automatic ":" Manual ");
153+
lcd.print(" ");
154+
lcd.setCursor(0,3);
155+
lcd.print("Int:");
156+
#if defined(USE_HACK)
157+
lcd.print(myPID.outputSum,4);
158+
#endif
159+
lcd.print(' ');
160+
lcd.println();
161+
}
162+
}
163+
164+
float simPlant(float Q,float hfactor) { // heat input in W (or J/s)
165+
// simulate a 1x1x2cm aluminum block with a heater and passive ambient cooling
166+
// float C = 237; // W/mK thermal conduction coefficient for Al
167+
float h = 5 *hfactor ; // W/m2K thermal convection coefficient for Al passive
168+
float Cps = 0.89; // J/g°C
169+
float area = 1e-4; // m2 area for convection
170+
float mass = 10 ; // g
171+
float Tamb = 25; // °C
172+
static float T = Tamb; // °C
173+
static uint32_t last = 0;
174+
uint32_t interval = 100; // ms
175+
176+
if (millis() - last >= interval) {
177+
last += interval;
178+
// 0-dimensional heat transfer
179+
T = T + Q * interval / 1000 / mass / Cps - (T - Tamb) * area * h;
180+
}
181+
return T;
182+
}
183+
184+
void mySetIntegral(PID * ptrPID,double value ){
185+
ptrPID->SetMode(MANUAL);
186+
Output = value;
187+
ptrPID->SetMode(AUTOMATIC);
188+
}
189+
190+

library.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name=PID_v1_bc
2-
version=1.2.3
2+
version=1.2.4
33
author=David Forrest
44
maintainer=David Forrest
55
sentence=PID controller based on PID_v1 with back-calculation anti-windup

0 commit comments

Comments
 (0)