The control device is an Arduino Uno, the IDE is V 1.8.19
The display device is an SSD1306 oled 0.96 inch
The SSD1306 has four pins:
GND - connect to (-);
VCC - connect to 3.3VDC or 5.0 VDC;
SCL (clock signal) - connect to A4 analog pin on Uno;
SDA (data) - connect to A5 pin on Uno
The temperature sensor is a MAX6675 Type-K thermocouple and amplifier
two thermocouple connections to the amplifier plus five amplifier pins:
GND - connect to (-)
VCC - connect to 3.3VDC
SCK - connect to Uno digital; choose D13
DO/SO - connect to Uno digital; choose D12
SC - connect to Uno digital; choose D11
There is a 10kohm potentiometer with one side grounded and the other side at 5VDC. The POTPIN variable reads the voltage at the potentiometer variable output.
There is an LED with a 220ohm series resistor between A1 and ground, parallel to the control relay signal, to indicate when the relay is being called to engage.
Control Sketch:
(this page editor treats some asterisks as format-italics, so some spaces have been inserted here which may render copied text into bad format for the Arduino IDE. Copying from the post-editor might resolve such an error if it is not otherwise obvious to the user.)
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <MAX6675.h>
#define MAXDO 12 //DO or SO pin on Max6675 to D12
#define MAXCLK 13 //SCK to D13
#define MAXCS 11 //CS to D11
#define POTPIN A0 // voltage on A0 will define setpoint
#define RELAY A1 // A1 calls relay to engage
#define OUTMIN 0 // the minimum PID output
#define OUTMAX 255 // the maximum PID output
#define KP 5 //PID proportionality constant
#define KD 200 //PID rate constant (millisec/degree)
#define KI .00001 //PID integral constant (1/deg-millisec)
float temp; //temp reading from thermocouple
float SetPoint; //calculated from 10k pot 0-5V
float SPMin = 250.; //minimum setpoint (250C for pyrolysis)
float SPMax = 550.; //max setpoint (550C)
float output; //PID output for relay control
float PinOut; //analog reading for determining setpoint
float error; // temperature - setpoint
float POut; // proportional control output contribution
float lastime; //processor clock time in prior loop
float lasterror; //setpoint error in prior loop
float tstep; // time step for PID control
float estep; // error change for PID control
float istep; // increment of integral error * time
float isum; // total integral error * time
float IOut; // integral control output contribution
float DOut; //derivative control output contribution
unsigned long ton; //relay ON time this relay cycle
unsigned long tonsum = 0; //cumulative ON time
unsigned long toff; //relay OFF time this relay cycle
int RELAYSTATE = 0; //relay is off initially
unsigned long now; //the current time to check relay timer
unsigned long before = 900; //the last time the relay status changed
// initialize to 900 = 3 x 300ms delays to first use of “before”.
#define OLED_RESET -1
#define DIS_WIDTH 128
#define DIS_HEIGHT 32
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(DIS_WIDTH, DIS_HEIGHT, &Wire, OLED_RESET);
// display call defines A4 and A5 connections for this device
MAX6675 thermoCouple;
void setup() {
Serial.begin(9600); //in case we need
// to communicate to the serial monitor
delay(500);
display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
thermoCouple.begin(MAXCLK, MAXCS, MAXDO);
thermoCouple.setSPIspeed(4000000);
delay(300); // 250ms min frequency of thermocouple reads
int status = thermoCouple.read();
if (status != STATUS_OK)
{
display.println(“ERROR”);
display.display();
}
temp = thermoCouple.getTemperature();
/* Serial prints for troubleshooting
Serial.print("temp ");
Serial.println(temp);//initial temp */
PinOut = analogRead(POTPIN); //0-1024 reading on POTPIN
SetPoint = SPMin + (PinOut / 1024)*(SPMax-SPMin);
/* Serial.print("SP ");
Serial.println(SetPoint);// */
error = temp - SetPoint; //initial error
lastime = millis(); //initialize lastime counter
lasterror = error; //initialize lasterror counter
isum = 0.; //initialize integral response
} //end of setup
void loop()
{
delay(700); // this is the only recurring delay 250 minimum
PinOut = analogRead(POTPIN); //0-1024 reading on POTPIN
SetPoint = SPMin + (PinOut/ 1024)*(SPMax - SPMin); // recheck SP
thermoCouple.begin(MAXCLK, MAXCS, MAXDO);
int status = thermoCouple.read();
// Serial.print(status);
temp = thermoCouple.getTemperature();
display.clearDisplay();
display.display();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.cp437(true);
display.print("T= “);
display.display();
display.print(temp, 0);
display.print(” SP= ");
display.println(SetPoint, 0); //temp and set-point displayed
display.display(); //temp and set-point displayed
now = millis();
error = temp - SetPoint; // this is usually negative
POut = -KP * error; //proportional response to error
if(POut > OUTMAX) {
POut = OUTMAX; //proportional response cap
} else {
if(POut < -OUTMAX) {
POut = -OUTMAX;
}
}
tstep = now-lastime; //time since last loop - a positive value
lastime = now; //record time in this loop pass
estep = error - lasterror; //error change
lasterror = error; //record error in this loop pass
istep = error * tstep; //recent error duration - usually negative
isum = isum + istep; //cumulative error X duration
if(isum < -12000000) { //2 minute of -100 degree error - arbitrary
isum = -12000000;
} else {
if(isum > 12000000) { //2 min. of +100 degree error
isum = 12000000;
}
}
IOut = -KI * isum; //integral response to cumulative error
if(IOut >= OUTMAX) {
IOut = OUTMAX;
}
DOut = -KD * (estep/tstep); //derivative response to error change
/* because tstep is less than one second, and estep
has “noise” due to relay cycling this parameter is
not employed nor has there been troubleshooting*/
output = POut + IOut + DOut;//cumulative PID response
// note there can be offsetting contributions (+/-) to output
// need different integral reset criterion. REVISIT
// is integral reset necessary? Yes, it remains maxxed long-term otherwise
if(output >= OUTMAX) {
output = OUTMAX; //total controller response cap
} else {
if(output <= OUTMIN) {
output = OUTMIN;
}
}
display.print("P-I-D: “);
display.print(POut,0);
display.print(” “);
display.print(IOut,0);
display.print(” ");
display.println(DOut,0);
display.display();
/*Serial.print("output = ");
Serial.println(output);
Serial.print(output/255); */
ton = ((output/255) * 48000) + 1000 ; // on time 49000 ms max
toff = 50000 - ton; //relay on/off cycle time is 50 seconds
/*Serial.print("time on (ms): ");
Serial.println(ton);
Serial.print("RELAYSTATE: ");
Serial.println(RELAYSTATE); */
if(RELAYSTATE == 255) {
tonsum = tonsum + tstep;
// add last cycle time to the on-time total
}
if(RELAYSTATE == 0) {
if((now - before) >= toff) {
RELAYSTATE = 255;
/if the relay was off and toff has passed
turn the relay on and reset “before”/
before = now; // before records last relay state change
}
} else {
if((now - before) >= ton) {
RELAYSTATE = 0;
/* else, if the relay was on and ton has passed,
turn the relay off and reset “before”*/
before = now;
}
/ * else, if state change is not due, loop again */
}
analogWrite(RELAY, RELAYSTATE); //5VDC to control AC Power relay and LED
float time = millis(); //compute the nominal hh:mm:ss
int hour = time/3600000;
int min = time/60000 - (hour60);
int sec = time/1000 - (min60) - (hour*3600);
display.print("run time: “);
display.print(hour);
display.print(”: “);
display.print(min);
display.print(”: ");
display.println(sec);
display.print("ON time (s): ");
display.print(tonsum/1000);
display.display();
}
End of Sketch
Data displays:
Current temperature and Setpoint temperature
PID response factors
Time since start (hh:mm:ss)
Heat on time since start (seconds) - for calculating cumulative energy input
LED light to show "heat ON’’