Hello everyone,
I am working on a PID temperature control that increases and decreases temperature rate slowly within the time frame. The code below is based on electronoobs pid temperature control that uses zero cross method to control temperature rate through a triac, that's not the problem here but here is the link http://electronoobs.com/eng_arduino_tut39.php so you can get the general it and see the schematic. Here is what the code below is about, the user enters setpoint of temperature with a keypad and then can change it with push buttons +1 or -1 degrees. The setpoint is what the heater should be, and with the thermocouple attached to it the arduino will get the feedback through max6675 and compare the two values and the PID will fix the error with it values if there is any.
I want to set time with keypad (30 sec , 1 min , 1.30 sec, 2min) and then set temperature rate (1 degree , 2 degrees, etc) and temperature will increase or decrease following the degree per time rate until it reaches it setpoint of temperature.
Can you help me in code form and not just some vague phrases it would be much appreciated. I looked all over the internet non stop for weeks without anything that resembles to what I am looking for.
#include <SPI.h>
#include <Keypad.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "max6675.h"
LiquidCrystal_I2C lcd(0x27,16,2);
//Inputs and outputs
int firing_pin = 3;
int increase_pin = 11;
int decrease_pin = 12;
int zero_cross = 8;
int thermoDO = 4; // so
int thermoCS = 5; //
int thermoCLK = 6; // sck
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
const byte rows = 4; /* four rows */
const byte columns = 4; /* four columns */
/* define the symbols on the buttons of the keypads */
char hexaKeys[rows][columns] = {
{'0','1','2','3'},
{'4','5','6','7'},
{'8','9','A','B'},
{'C','D','E','F'}
};
byte row_pins[rows] = {26, 27, 28, 29}; /* connect to the row pinouts of the keypad */
byte col_pins[columns] = {22, 23, 24, 25}; /* connect to the column pinouts of the keypad */
/* initialize an instance of class Keypad */
Keypad keypad_key = Keypad( makeKeymap(hexaKeys), row_pins, col_pins, rows, columns);
//Keypad variables
char degkey[3]; // degree we enter with key
int i=0;
char key_pressed=0;
//Variables
int last_CH1_state = 0;
bool zero_cross_detected = false;
int firing_delay = 7400;
int maximum_firing_delay = 7400;
unsigned long previousMillis = 0;
unsigned long currentMillis = 0;
int temp_read_Delay = 500;
int real_temperature = 0;
int setpoint = 0;
bool pressed_1 = false;
bool pressed_2 = false;
//PID variables
float PID_error = 0;
float previous_error = 0;
float elapsedTime, Time, timePrev;
int PID_value = 0;
//PID constants
int kp = 203; int ki= 7.2; int kd = 1.04;
int PID_p = 0; int PID_i = 0; int PID_d = 0;
void setup() {
lcd.init();
lcd.backlight();
pinMode (firing_pin,OUTPUT);
pinMode (zero_cross,INPUT);
pinMode (increase_pin,INPUT);
pinMode (decrease_pin,INPUT);
PCICR |= (1 << PCIE0); //enable PCMSK0 scan
PCMSK0 |= (1 << PCINT0); //Set pin D8 (zero cross input) trigger an interrupt on state change.
PCMSK0 |= (1 << PCINT3); //Set pin D11 (increase button) trigger an interrupt on state change.
PCMSK0 |= (1 << PCINT4); //Set pin D12 (decrease button) trigger an interrupt on state change.
}
void loop() {
key_pressed = keypad_key.getKey();
if(key_pressed=='#')
setpoint==setpoint; // # will setpoint to initial 0
if (key_pressed)
{
degkey[i++]=key_pressed;
lcd.print(key_pressed);
}
if(i==3)
{
delay(500);
for(int j=0;j<3;j++)
setpoint==degkey; // setpoint = the number we entered thats below 3 digits
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Set Temperature");
lcd.setCursor(0, 1);
lcd.print(setpoint);
delay(5000);
lcd.clear();
}
currentMillis = millis(); //Save the value of time before the loop
/* We create this if so we will read the temperature and change values each "temp_read_Delay"
* value. Change that value above iv you want. The MAX6675 read is slow. Tha will affect the
* PID control. I've tried reading the temp each 100ms but it didn't work. With 500ms worked ok.*/
if(currentMillis - previousMillis >= temp_read_Delay){
previousMillis += temp_read_Delay; //Increase the previous time for next loop
real_temperature = thermocouple.readCelsius(); //get the real temperature in Celsius degrees
PID_error = setpoint - real_temperature; //Calculate the pid ERROR
if(PID_error > 30) //integral constant will only affect errors below 30ºC
{PID_i = 0;}
PID_p = kp * PID_error; //Calculate the P value
PID_i = PID_i + (ki * PID_error); //Calculate the I value
timePrev = Time; // the previous time is stored before the actual time read
Time = millis(); // actual time read
elapsedTime = (Time - timePrev) / 1000;
PID_d = kd*((PID_error - previous_error)/elapsedTime); //Calculate the D value
PID_value = PID_p + PID_i + PID_d; //Calculate total PID value
//We define firing delay range between 0 and 7400. Read above why 7400!!!!!!!
if(PID_value < 0)
{ PID_value = 0; }
if(PID_value > 7400)
{ PID_value = 7400; }
//Printe the values on the LCD
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Set: ");
lcd.setCursor(5,0);
lcd.print(setpoint);
lcd.setCursor(0,1);
lcd.print("Real temp: ");
lcd.setCursor(11,1);
lcd.print(real_temperature);
previous_error = PID_error; //Remember to store the previous error.
}
//If the zero cross interruption was detected we create the 100us firing pulse
if (zero_cross_detected)
{
delayMicroseconds(maximum_firing_delay - PID_value); //This delay controls the power
digitalWrite(firing_pin,HIGH);
delayMicroseconds(100);
digitalWrite(firing_pin,LOW);
zero_cross_detected = false;
}
}
//End of void loop
// |
// |
// |
// v
//See the interruption vector
//This is the interruption routine (pind D8(zero cross), D11(increase) and D12(decrease))
//----------------------------------------------
ISR(PCINT0_vect){
///////////////////////////////////////Input from optocoupler
if(PINB & B00000001){ //We make an AND with the state register, We verify if pin D8 is HIGH???
if(last_CH1_state == 0){ //If the last state was 0, then we have a state change...
zero_cross_detected = true; //We have detected a state change! We need both falling and rising edges
}
}
else if(last_CH1_state == 1){ //If pin 8 is LOW and the last state was HIGH then we have a state change
zero_cross_detected = true; //We haev detected a state change! We need both falling and rising edges.
last_CH1_state = 0; //Store the current state into the last state for the next loop
}
if(PINB & B00001000){ //We make an AND with the state register, We verify if pin D11 is HIGH???
if (!pressed_1)
{
setpoint = setpoint + 1; //Increase the temperature by 5. Change this with your value if you want.
delay(20);
pressed_1 = true;
}
}
else if (pressed_1)
{
pressed_1 = false;
}
if(PINB & B00010000){ //We make an AND with the state register, We verify if pin D12 is HIGH???
if (!pressed_2)
{
setpoint = setpoint - 1; //Decrease the temperature by 5. Change this with your value if you want.
delay(20);
pressed_2 = true;
}
}
else if (pressed_2)
{
pressed_2 = false;
}
}