I wrote a post a few months back detailing my Arduino PID controller approach. Based on some feedback, I realized the code in that post was overly complicated as well as had some errors in it. I am now going to detail a much simpler approach.
Previously, I used a PID controller to schedule temperature over a fixed amount of time, such as to have precise ramp up time, hold time, and cool down time. It is worth noting that a PID controller is also useful for indefinite time control, like a thermostat. By varying the coefficients, you can customize how fast the system reacts and not damage any equipment (this is a consideration when using mechanical relays that don’t like fast switching).
Below is my Arduino C code for maintaining a set temperature like a thermostat with a PID controller. The MAX31855 and the PID_v1 libraries can be found on my Github page.
#include "SPI.h" #include "MAX31855.h" #include "PID_v1.h" // //Arduino Pins // //CSB of MAX31855 thermocouple. By using separate CSBs, multiple devices can use the same SPI bus. byte thermo1_csb = 7; //Pin to control relay that switches the heating element byte temp_relay0_Pin = 0; // //PID calculation parameters // //Value read by thermocouple sensor double temp_read1; //Temperature that system is currently maintaining double temp_set1 = 95; //Control value resulting from PID calculation double Output_T1; // //Library Objects // MAX31855 thermo; PID PID_temp1(&temp_read1, &Output_T1, &temp_set1, 1, 5, 0.5, DIRECT); void setup() { //Initiate thermocouple SPI.begin(); thermo.setup(thermo1_csb); //Initiate heating element relay pinMode(temp_relay0_Pin, OUTPUT); digitalWrite(temp_relay0_Pin, LOW); } //This function reads the thermocouple sensor and translates the value into temp_read1 void read_sensors() { thermo.read_temp(); temp_read1 = thermo.thermocouple_temp*1.8+32; } //This function computes the output control value from the thermocouple reading and current set point //and then turns the heating element on and off accordingly void run_PID() { uint16_t low_WindowSize = 0; uint16_t high_WindowSize = 1000; uint32_t windowStartTime = millis(); //Specify the links and initial tuning parameters PID_temp1.SetOutputLimits(low_WindowSize, high_WindowSize); //PID_temp1.SetTunings(Kp, Ki, Kd); PID_temp1.SetMode(AUTOMATIC); PID_temp1.Compute(); //This section controls the heating element while(millis() < (high_WindowSize-low_WindowSize) + windowStartTime) { if(Output_T1 > (millis() - windowStartTime)) { digitalWrite(temp_relay0_Pin, HIGH); } else { digitalWrite(temp_relay0_Pin, LOW); } } } void loop() { read_sensors(); run_PID(); }