#define AUTOMATIC 1
#define MANUAL 0
#define DIRECT 0
#define REVERSE 1
#define P_ON_M 0
#define P_ON_E 1
class PIDv3 {
public:
PIDv3();
void setMode(int Mode);
bool compute(float mySetpoint, float myInput);
void setOutputLimits(float Min, float Max);
void setTunings(float Kp, float Ki, float Kd, int POn = P_ON_E);
void setControllerDirection(int Direction);
void setSampleTime(float NewSampleTime);
float getKp();
float getKi();
float getKd();
float getOutput();
int getMode();
int getDirection();
private:
void initialize();
float dispKp;
float dispKi;
float dispKd;
float kp;
float ki;
float kd;
float myOutput;
int controllerDirection;
int pOn;
unsigned long lastTime;
float outputSum, lastInput;
float SampleTime;
float outMin, outMax;
bool inAuto, pOnE;
};
PIDv3::PIDv3() {
pOn = P_ON_E;
setMode(AUTOMATIC);
inAuto = false;
SampleTime = 100;
lastTime = millis() - SampleTime;
}
bool PIDv3::compute(float mySetpoint, float myInput) {
if (!inAuto) return false;
unsigned long now = millis();
unsigned long timeChange = (now - lastTime);
if (timeChange >= SampleTime) {
/*Compute all the working error variables*/
float input = myInput;
float error = mySetpoint - input;
float dInput = (input - lastInput);
outputSum += (ki * error);
/*Add Proportional on Measurement, if P_ON_M is specified*/
if (!pOnE) outputSum -= kp * dInput;
if (outputSum > outMax) outputSum = outMax;
else if (outputSum < outMin) outputSum = outMin;
/*Add Proportional on Error, if P_ON_E is specified*/
float output;
if (pOnE) output = kp * error;
else output = 0;
/*Compute Rest of PIDv3 Output*/
output += outputSum - kd * dInput;
if (output > outMax) output = outMax;
else if (output < outMin) output = outMin;
myOutput = output;
/*Remember some variables for next time*/
lastInput = input;
lastTime = now;
return true;
} else return false;
}
void PIDv3::setTunings(float Kp, float Ki, float Kd, int POn) {
if (Kp < 0 || Ki < 0 || Kd < 0) return;
pOn = POn;
pOnE = POn == P_ON_E;
dispKp = Kp;
dispKi = Ki;
dispKd = Kd;
float SampleTimeInSec = ((float) SampleTime) / 1000;
kp = Kp;
ki = Ki * SampleTimeInSec;
kd = Kd / SampleTimeInSec;
if (controllerDirection == REVERSE) {
kp = (0 - kp);
ki = (0 - ki);
kd = (0 - kd);
}
}
void PIDv3::setSampleTime(float NewSampleTime) {
if (NewSampleTime > 0) {
float ratio = (float) NewSampleTime / (float) SampleTime;
ki *= ratio;
kd /= ratio;
SampleTime = NewSampleTime;
}
}
void PIDv3::setOutputLimits(float Min, float Max) {
if (Min >= Max) return;
outMin = Min;
outMax = Max;
if (inAuto) {
if (myOutput > outMax) myOutput = outMax;
else if (myOutput < outMin) myOutput = outMin;
if (outputSum > outMax) outputSum = outMax;
else if (outputSum < outMin) outputSum = outMin;
}
}
void PIDv3::setMode(int Mode) {
bool newAuto = (Mode == AUTOMATIC);
if (newAuto && !inAuto) {
PIDv3::initialize();
}
inAuto = newAuto;
}
void PIDv3::initialize() {
outputSum = myOutput;
lastInput = 0; //
if (outputSum > outMax) outputSum = outMax;
else if (outputSum < outMin) outputSum = outMin;
}
void PIDv3::setControllerDirection(int Direction) {
if (inAuto && Direction != controllerDirection) {
kp = (0 - kp);
ki = (0 - ki);
kd = (0 - kd);
}
controllerDirection = Direction;
}
float PIDv3::getKp() {
return dispKp;
}
float PIDv3::getKi() {
return dispKi;
}
float PIDv3::getKd() {
return dispKd;
}
float PIDv3::getOutput() {
return myOutput;
}
int PIDv3::getMode() {
return inAuto ? AUTOMATIC : MANUAL;
}
int PIDv3::getDirection() {
return controllerDirection;
}
//////////////////////////////////////////////////////
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#define ONE_WIRE_BUS 2
#define relay 4
#define ssr 10
#define button 5
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature DS18B20(&oneWire);
LiquidCrystal_I2C lcd(0x27, 20, 4);
PIDv3 pid;
unsigned long prevMillis = 0;
unsigned long interval = 1000;
boolean systemState = false;
float temperature = 0.0;
float sp = 50.0;
float outputPID;
void setup() {
Serial.begin(9600);
DS18B20.begin();
lcd.init();
lcd.backlight();
pid.setTunings(5.0, 2.7, 1.5);
pid.setOutputLimits(0, 255);
pid.setMode(AUTOMATIC);
pid.setControllerDirection(DIRECT);
pid.setSampleTime(0.1);
pinMode(button, INPUT_PULLUP);
pinMode(relay, OUTPUT);
digitalWrite(relay, LOW);
}
void loop() {
int state = !digitalRead(button);
if (state) {
systemState = !systemState;
delay(500);
}
if (systemState) {
digitalWrite(relay, HIGH);
Serial.println(outputPID);
pid.compute(sp, temperature);
outputPID = pid.getOutput();
unsigned long currentMillis = millis();
if (currentMillis - prevMillis >= interval) {
prevMillis = currentMillis;
DS18B20.requestTemperatures();
temperature = DS18B20.getTempCByIndex(0);
analogWrite(ssr, outputPID);
lcd.setCursor(0, 0);
lcd.print("PV = ");
lcd.setCursor(5, 0);
lcd.print(temperature);
lcd.print("C");
lcd.setCursor(0, 1);
lcd.print("SP = ");
lcd.setCursor(5, 1);
lcd.print(sp);
lcd.print("C");
lcd.setCursor(0, 2);
lcd.print("PID = ");
lcd.setCursor(5, 2);
lcd.print(outputPID);
}
} else {
digitalWrite(relay, LOW);
analogWrite(ssr, 0);
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(" SISTEM KENDALI");
lcd.setCursor(0, 2);
lcd.print(" SUHU KEDELAI");
}
}