I am building a reflow toaster oven using the Adafruit MAX31855 and the PID Library version 1.1.1 referenced in the Playground. I got some anomalous operation so I went looking for the problem and found something that I do not understand.
In short, the value "input" in the Compute() function is always zero. This makes the "error" in the function always equal to the Setpoint". The PID is always wrong. Here is the code for the oven so far...
*********************************************
Reflow_PID
uses the Adafruit thermocouple MAX31855 and an SSR to
control the oven elements.
The code is adapted from the Adafruit library and the PID RelayOutput
Example.
****************************************************/
#include <SPI.h>
#include <PID_v1.h>
#include <Adafruit_MAX31855.h>
// Default connection is using software SPI
// Software SPI declarations for MAX31855
#define MAXDO 2
#define MAXCS 3
#define MAXCLK 4
#define RELAY_PIN 5 // solid state relay pin
// initialize the Thermocouple
Adafruit_MAX31855 thermocouple(MAXCLK, MAXCS, MAXDO);
// PID declarations
double Setpoint, Input, Output;
double Kp = 2, Ki = 0, Kd = 0;
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
int WindowSize = 5000;
unsigned long windowStartTime;
void setup() {
pinMode(RELAY_PIN, OUTPUT); // pin for solid state relay
while (!Serial); // wait for Serial Port to open
Serial.begin(9600);
delay(500); // wait for MAX31855 to settle down
// PID setup
windowStartTime = millis();
Setpoint = 70;
myPID.SetOutputLimits(0, WindowSize);
//turn the PID on
myPID.SetMode(AUTOMATIC);
}
void loop() {
double Input = thermocouple.readCelsius(); // get oven temp
if (isnan(Input)){ // if we do not get a number from the '855
Serial.println("Something wrong with thermocouple!");
// Here is where we abort the cycle if the
// Will need code to kill cycle here.
}
//*****************************************************************************
Input = 20; // here I set the Input value for troubleshooting purposes
// do the PID calculations here
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(RELAY_PIN, HIGH);
// Debug information to serial port
/*Serial.print("Set= ");
Serial.print(Setpoint);
Serial.print(" ");
Serial.print("Input= ");
Serial.print(Input);
Serial.print(" ");
Serial.print("Output= ");
Serial.print(Output);
Serial.print(" ");
Serial.print("millis - windowStartTime= ");
Serial.print(millis() - windowStartTime);
Serial.print(" ");
Serial.println(" Pin= High ");
*/
}
else {
digitalWrite(RELAY_PIN, LOW);
// Debug information to serial port
/*Serial.print("Set= ");
Serial.print(Setpoint);
Serial.print(" ");
Serial.print("Input= ");
Serial.print(Input);
Serial.print(" ");
Serial.print("Output= ");
Serial.print(Output);
Serial.print(" ");
Serial.print("millis - windowStartTime= ");
Serial.print(millis() - windowStartTime);
Serial.print(" ");
Serial.println(" Pin= Low");*/
}
}
I set the Setpoint and Input value for troubleshooting before calling the Compute() function.
In PID_v1.cpp below, I inserted some Serial.print lines in order to monitor the values being used inside the Compute() function. The line "double input = *myInput" is not passing the value of 20 along to the variable "input". I do not understand pointers well enough to interpret what is going on. I need advice on how to get the "Input" value in Reflow_PID.ino into the "input" value of the Compute() function.
/**********************************************************************************************
* Arduino PID Library - Version 1.1.1
* by Brett Beauregard <br3ttb@gmail.com> brettbeauregard.com
*
* This Library is licensed under a GPLv3 License
**********************************************************************************************/
#if ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include <PID_v1.h>
/*Constructor (...)*********************************************************
* The parameters specified here are those for for which we can't set up
* reliable defaults, so we need to have the user set them.
***************************************************************************/
PID::PID(double *Input, double* Output, double* Setpoint,
double Kp, double Ki, double Kd, int ControllerDirection)
{
myOutput = Output;
myInput = Input;
mySetpoint = Setpoint;
inAuto = false;
PID::SetOutputLimits(0, 255); //default output limit corresponds to
//the arduino pwm limits
SampleTime = 100; //default Controller Sample Time is 0.1 seconds
PID::SetControllerDirection(ControllerDirection);
PID::SetTunings(Kp, Ki, Kd);
lastTime = millis()-SampleTime;
}
/* Compute() **********************************************************************
* This, as they say, is where the magic happens. this function should be called
* every time "void loop()" executes. the function will decide for itself whether a new
* pid Output needs to be computed. returns true when the output is computed,
* false when nothing has been done.
**********************************************************************************/
bool PID::Compute()
{
if(!inAuto) return false;
unsigned long now = millis();
unsigned long timeChange = (now - lastTime);
if(timeChange>=SampleTime)
{
/*Compute all the working error variables*/
double input = *myInput;
double error = *mySetpoint - input;
/* Here I insert some lines to see what values I have
- my "Setpoint" is 70, which I set in the .ino file
- my "Input" is 20, set just before the myPID.Compute() call
but the variable "input" is always zero
- this means that the "error" always equals the "Setpoint"
- the PID is always in error
*/
Serial.print("Setpoint= ");
Serial.print(*mySetpoint);
Serial.print(" ");
Serial.print("input= ");
Serial.print(input);
Serial.print(" ");
Serial.print("error= ");
Serial.println(error);
ITerm+= (ki * error);
if(ITerm > outMax) ITerm= outMax;
else if(ITerm < outMin) ITerm= outMin;
double dInput = (input - lastInput);
/*Compute PID Output*/
double output = kp * error + ITerm- 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;
}
Has anyone else had this problem? I have spent the last few days reading and watching videos about PIDs and how they work. It all seems straightforward except that I do not understand pointers well enough. I need some advice badly.