I made a PID controller that uses a SSR to drive a resistive load (heating coil), a type K thermocupple with the adafruit max31855 breakout board. I'm using the adafruit max31855 lib with the arduino PID and PID front end libraries. I have the set point at 100*C, when I turn it on at room temp the realy LED comes on, i had a signal on my output (as seen in pid frontend), after it overshoots (tuning issue?) it cuts off, once the temp falls below the setpoint the SSR never comes back on. I don't know much C/C++, just enough basics to combine these different libraries and get it to compile. Any help would be appreciated, I'm not sure where to begin troubleshooting but I though the PID frontend could help.
You can see the output=nan after the overshoot, during heating this it was at a real value.
`````/***************************************************
This is an example for the Adafruit Thermocouple Sensor w/MAX31855K
Designed specifically to work with the Adafruit Thermocouple Sensor
----> https://www.adafruit.com/products/269
These displays use SPI to communicate, 3 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/
#include "Adafruit_MAX31855.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <PID_v1.h>
// set replay pin for PID
#define RelayPin 6
double Setpoint, Input, Output;
int thermoCLK = 5;
int thermoCS = 4;
int thermoDO = 3;
//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint,53.2,10.5,2.12, DIRECT);
unsigned long serialTime; //this will help us know when to talk with processing
int WindowSize = 5000;
unsigned long windowStartTime;
// Initialize the Thermocouple
Adafruit_MAX31855 thermocouple(thermoCLK, thermoCS, thermoDO);
// initialize the library and set the LCD address to 0x27 for a 16x2 line display
LiquidCrystal_I2C lcd(0x27,16,2);
void setup() {
Serial.begin(9600);
windowStartTime = millis();
// inital setpoint before implementing control
Setpoint = 100;
pinMode(RelayPin, OUTPUT); // sets the digital pin as output
//tell the PID to range between 0 and the full window size
myPID.SetOutputLimits(0, WindowSize);
//turn the PID on
myPID.SetMode(AUTOMATIC);
// set up the LCD's number of columns and rows:
lcd.init(); // initialize the lcd
lcd.backlight();
lcd.print("MAX31855 test");
// wait for MAX chip to stabilize
delay(500);
}
void loop() {
Input = thermocouple.readCelsius();
myPID.Compute();
/************************************************
* turn the output pin on/off based on pid output
************************************************/
if(millis() - windowStartTime>WindowSize)
{ //time to shift the Relay Window
windowStartTime += WindowSize;
}
if(Output < millis() - windowStartTime) digitalWrite(RelayPin,HIGH);
else digitalWrite(RelayPin,LOW);
// basic readout test, just print the current temp
lcd.setCursor(0, 0);
lcd.print("Set Temp = ");
lcd.println(Setpoint);
lcd.print(" ");
lcd.setCursor(0, 1);
if (isnan(Input))
{
lcd.print("T/C Problem");
}
else
{
lcd.print("C = ");
lcd.print(Input);
lcd.print(" ");
}
//send-receive with processing if it's time
if(millis()>serialTime)
{
SerialReceive();
SerialSend();
serialTime+=500;
}
}
/********************************************
* Serial Communication functions / helpers
********************************************/
union { // This Data structure lets
byte asBytes[24]; // us take the byte array
float asFloat[6]; // sent from processing and
} // easily convert it to a
foo; // float array
// getting float values from processing into the arduino
// was no small task. the way this program does it is
// as follows:
// * a float takes up 4 bytes. in processing, convert
// the array of floats we want to send, into an array
// of bytes.
// * send the bytes to the arduino
// * use a data structure known as a union to convert
// the array of bytes back into an array of floats
// the bytes coming from the arduino follow the following
// format:
// 0: 0=Manual, 1=Auto, else = ? error ?
// 1: 0=Direct, 1=Reverse, else = ? error ?
// 2-5: float setpoint
// 6-9: float input
// 10-13: float output
// 14-17: float P_Param
// 18-21: float I_Param
// 22-245: float D_Param
void SerialReceive()
{
// read the bytes sent from Processing
int index=0;
byte Auto_Man = -1;
byte Direct_Reverse = -1;
while(Serial.available()&&index<26)
{
if(index==0) Auto_Man = Serial.read();
else if(index==1) Direct_Reverse = Serial.read();
else foo.asBytes[index-2] = Serial.read();
index++;
}
// if the information we got was in the correct format,
// read it into the system
if(index==26 && (Auto_Man==0 || Auto_Man==1)&& (Direct_Reverse==0 || Direct_Reverse==1))
{
Setpoint=double(foo.asFloat[0]);
//Input=double(foo.asFloat[1]); // * the user has the ability to send the
// value of "Input" in most cases (as
// in this one) this is not needed.
if(Auto_Man==0) // * only change the output if we are in
{ // manual mode. otherwise we'll get an
Output=double(foo.asFloat[2]); // output blip, then the controller will
} // overwrite.
double p, i, d; // * read in and set the controller tunings
p = double(foo.asFloat[3]); //
i = double(foo.asFloat[4]); //
d = double(foo.asFloat[5]); //
myPID.SetTunings(p, i, d); //
if(Auto_Man==0) myPID.SetMode(MANUAL);// * set the controller mode
else myPID.SetMode(AUTOMATIC); //
if(Direct_Reverse==0) myPID.SetControllerDirection(DIRECT);// * set the controller Direction
else myPID.SetControllerDirection(REVERSE); //
}
Serial.flush(); // * clear any random data from the serial buffer
}
// unlike our tiny microprocessor, the processing ap
// has no problem converting strings into floats, so
// we can just send strings. much easier than getting
// floats from processing to here no?
void SerialSend()
{
Serial.print("PID ");
Serial.print(Setpoint);
Serial.print(" ");
Serial.print(Input);
Serial.print(" ");
Serial.print(Output);
Serial.print(" ");
Serial.print(myPID.GetKp());
Serial.print(" ");
Serial.print(myPID.GetKi());
Serial.print(" ");
Serial.print(myPID.GetKd());
Serial.print(" ");
if(myPID.GetMode()==AUTOMATIC) Serial.print("Automatic");
else Serial.print("Manual");
Serial.print(" ");
if(myPID.GetDirection()==DIRECT) Serial.println("Direct");
else Serial.println("Reverse");
}