#include <LiquidCrystal.h>
#include <SPI.h>
#include <Wire.h>
#include <max6675.h>
#define thermoDO 12
#define thermoCS 10
#define thermoCLK 13
#define potentiometer A0
#define zerocrossing 2
#define triac 7
#define relay A1
#define test A2
#define test1 A3
int lowError = 0;
int highError = 20;
float temperature, realTemperature;
int pottemperature;
int counter;
int tempError = false; // global error flag
int shownError = false; //flag to say error shown
int duty = 0; // variable for duty cycle
//PID constants
double Kp = 2.5;
double Ki = 0;
double Kd = 0;
//PID variables
unsigned long currentTime, previousTime;
double elapsedTime;
double error;
double lastError;
double input, output, setPoint;
double cumError, rateError;
byte thermometer[8] = //icon for termometer
{
B00100,
B01010,
B01010,
B01110,
B01110,
B11111,
B11111,
B01110
};
byte arrow[8] = //icon for arrow
{
B11000,
B01100,
B00110,
B00011,
B00011,
B00110,
B01100,
B11000
};
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
/* The circuit:
LCD RS pin to digital pin 12
LCD Enable pin to digital pin 11
LCD D4 pin to digital pin 5
LCD D5 pin to digital pin 4
LCD D6 pin to digital pin 3
LCD D7 pin to digital pin 2
LCD R/W pin to ground
LCD VSS pin to ground
LCD VCC pin to 5V
10K resistor:
ends to +5V and ground
wiper to LCD VO pin (pin 3)
*/
LiquidCrystal lcd(3, 4, 5, 6, 8, 9);
void setup() {
pinMode(test, OUTPUT);
pinMode(test1, OUTPUT);
lcd.createChar(0, thermometer);
lcd.createChar(1, arrow);
lcd.begin(16, 2);
lcd.setCursor(0, 0);
lcd.print("STATIE DE LIPIT");
output = 0;
setPoint = 0;
delay(1200);
lcd.clear();
pinMode(relay, OUTPUT);
pinMode(potentiometer, INPUT);
pinMode(zerocrossing, INPUT_PULLUP);
pinMode(triac, OUTPUT);
digitalWrite(triac, LOW);
digitalWrite(relay, HIGH);
realTemperature = thermocouple.readCelsius();
temperature = 0.779828 * realTemperature - 10.3427;
//updateDisplay();
attachInterrupt(digitalPinToInterrupt(2), zero, RISING);
}
void loop() {
if (!tempError) { // if no error
updateDisplay();
} else // do something on error
{
// eg show the word error on the display
if (!shownError) { // we've not shown error yet, so show it
displayErrors();
shownError = true; //set flag so don't show it again
}
}
delay(250); //controls loop timing
}
void zero() {
counter++;
if ((counter == 24) || (counter>duty)) { //reach max count or duty cycle limit.
digitalWrite(triac, LOW);
}
// else *** remove this
if (counter >= 25) {
counter = 0;
digitalWrite(test, HIGH); // *** added, this will generate a pulse on test pin (5) every 250mS to prove counter incrementing...
pottemperature = analogRead(potentiometer);
pottemperature = map(pottemperature, 0, 1023, 150, 400);
digitalWrite(test, LOW); // *** put test pin low
realTemperature = thermocouple.readCelsius();
temperature = int(0.779828 * realTemperature - 10.3427); // make temperature an integer
if (tempError || isnan(realTemperature) || temperature >= 432) { // on error kill power & set global error flag
digitalWrite(relay, LOW); // turn off power to iron
tempError = true; //set error flag. can only be unset outside ISR. Once set no further action taken till unset in main loop.
}
else { //reading valid
if (temperature < pottemperature) { //*** change, remove =
digitalWrite(test1, HIGH); // *** added, generate a pulse on test1 (D6) when pottemp >= temp
error = pottemperature - temperature; // don't think you need abs() here as t cannot be > pt and get here...
cumError += error * 250.0; //*** change - something got lost in one of my earlier edits
rateError = (error - lastError) / 250.0; //*** change
output = Kp * error + Ki * cumError + Kd * rateError; //output error needs to be mapped to a number between 0 and 24
duty = map(output, lowError, highError, 0, 24); // lowError is const >= 0, highError is const for fully on.
duty = constrain(duty, 0, 24); // keep duty between 0 and 24 (24 = 96%)
if(duty>0) { digitalWrite(triac, HIGH); } //*** change, added '>0'
lastError = error;
digitalWrite(test1, LOW); // *** added
}
else {
duty = 0;
}//if (temperature
}//if (tempError
}//if(counter == 25...
}//zero
void updateDisplay() {
pottemperature = analogRead(potentiometer);
pottemperature = map(pottemperature, 0, 1023, 150, 400);
lcd.clear();
lcd.setCursor(0, 0);
lcd.write((byte)0);
lcd.setCursor(2, 0);
lcd.print((int)pottemperature);
lcd.setCursor(6, 0);
lcd.print((char)223); //degree sign
lcd.setCursor(7, 0);
lcd.print("C");
lcd.setCursor(0, 1);
lcd.write((byte)1);
if (temperature <= 45) {
lcd.setCursor(2, 1);
lcd.print("Lo");
} else {
lcd.setCursor(2, 1);
lcd.print((int)temperature);
}
lcd.setCursor(6, 1);
lcd.print("[");
lcd.setCursor(7, 1);
lcd.print((int)realTemperature);
lcd.setCursor(10, 1);
lcd.print("]");
lcd.setCursor(12, 1);
lcd.print((char)223);
lcd.setCursor(13, 1);
lcd.print("C");
}
void displayErrors() {
digitalWrite(relay, LOW); // the relay will disconnect the power to the soldering iron heating element
lcd.clear();
lcd.setCursor(0, 0);
lcd.write((byte)0);
lcd.setCursor(1, 0);
lcd.write((byte)0);
lcd.setCursor(5, 0);
lcd.print("ERROR!");
lcd.setCursor(14, 0);
lcd.write((byte)0);
lcd.setCursor(15, 0);
lcd.write((byte)0);
}
#include <LiquidCrystal.h>
#include <SPI.h>
#include <Wire.h>
#include <max6675.h>
#define thermoDO 12
#define thermoCS 10
#define thermoCLK 13
#define potentiometer A0
#define zerocrossing 2
#define triac 7
#define relay A1
#define test A2
#define test1 A3
int lowError = 0;
int highError = 20;
float temperature, realTemperature;
int pottemperature;
int counter;
int tempError = false; // global error flag
int shownError = false; //flag to say error shown
int duty = 0; // variable for duty cycle
//PID constants
double Kp = 2.5;
double Ki = 0;
double Kd = 0;
//PID variables
unsigned long currentTime, previousTime;
double elapsedTime;
double error;
double lastError;
double input, output, setPoint;
double cumError, rateError;
byte thermometer[8] = //icon for termometer
{
B00100,
B01010,
B01010,
B01110,
B01110,
B11111,
B11111,
B01110
};
byte arrow[8] = //icon for arrow
{
B11000,
B01100,
B00110,
B00011,
B00011,
B00110,
B01100,
B11000
};
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
/* The circuit:
LCD RS pin to digital pin 12
LCD Enable pin to digital pin 11
LCD D4 pin to digital pin 5
LCD D5 pin to digital pin 4
LCD D6 pin to digital pin 3
LCD D7 pin to digital pin 2
LCD R/W pin to ground
LCD VSS pin to ground
LCD VCC pin to 5V
10K resistor:
ends to +5V and ground
wiper to LCD VO pin (pin 3)
*/
LiquidCrystal lcd(3, 4, 5, 6, 8, 9);
void setup() {
Serial.begin(115200); // or faster if your Arduino/PC can handle it...
pinMode(test, OUTPUT);
pinMode(test1, OUTPUT);
lcd.createChar(0, thermometer);
lcd.createChar(1, arrow);
lcd.begin(16, 2);
lcd.setCursor(0, 0);
lcd.print("STATIE DE LIPIT");
output = 0;
setPoint = 0;
delay(1200);
lcd.clear();
pinMode(relay, OUTPUT);
pinMode(potentiometer, INPUT);
pinMode(zerocrossing, INPUT_PULLUP);
pinMode(triac, OUTPUT);
digitalWrite(triac, LOW);
digitalWrite(relay, HIGH);
realTemperature = thermocouple.readCelsius();
temperature = 0.779828 * realTemperature - 10.3427;
//updateDisplay();
attachInterrupt(digitalPinToInterrupt(2), zero, RISING);
}
// added stuff to log temperatures on serial monitor
// change loop time management from simple delay
#define PRINTRATE 100
#define DISPLAYRATE 250
char textbuf[50]; //buffer for data to send
ulong serialTime = millis(); //sending interval for data
ulong displayTime = serialTime; //display interval for LCD
int pt; //local store for pot and iron temperatures;
int tmp;
void loop() {
if(millis() >serialTime+PRINTRATE){ //send serial data every PRINTRATE mS
noInterrupts(); // make sure our local copies are not corrupted while copying them over from ISR
tmp = temperature;
pt = pottemperature;
interrupts();
sprintf(textbuf, "Time: %lu, Set: %4u, Temp: %4u", millis()/100, pt, tmp); //format the print string
Serial.println(textbuf); //send to serial monitor, about 3mS @ 115200
serialTime += PRINTRATE;
}
if(millis()>displayTime+DISPLAYRATE) {//update display every DISPLAYRATE mS
if (!tempError) { // if no error
updateDisplay();
} else // do something on error
{
// eg show the word error on the display
if (!shownError) { // we've not shown error yet, so show it
displayErrors();
shownError = true; //set flag so don't show it again
}
}
displayTime += DISPLAYRATE;
}
}
void zero() {
counter++;
if ((counter == 24) || (counter>duty)) { //reach max count or duty cycle limit.
digitalWrite(triac, LOW);
}
// else *** remove this
if (counter >= 25) {
counter = 0;
digitalWrite(test, HIGH); // *** added, this will generate a pulse on test pin (5) every 250mS to prove counter incrementing...
pottemperature = analogRead(potentiometer);
pottemperature = map(pottemperature, 0, 1023, 150, 400);
digitalWrite(test, LOW); // *** put test pin low
realTemperature = thermocouple.readCelsius();
temperature = int(0.779828 * realTemperature - 10.3427); // make temperature an integer
if (tempError || isnan(realTemperature) || temperature >= 432) { // on error kill power & set global error flag
digitalWrite(relay, LOW); // turn off power to iron
tempError = true; //set error flag. can only be unset outside ISR. Once set no further action taken till unset in main loop.
}
else { //reading valid
if (temperature < pottemperature) { //*** change, remove =
digitalWrite(test1, HIGH); // *** added, generate a pulse on test1 (D6) when pottemp >= temp
error = pottemperature - temperature; // don't think you need abs() here as t cannot be > pt and get here...
cumError += error * 250.0; //*** change - something got lost in one of my earlier edits
rateError = (error - lastError) / 250.0; //*** change
output = Kp * error + Ki * cumError + Kd * rateError; //output error needs to be mapped to a number between 0 and 24
duty = map(output, lowError, highError, 0, 24); // lowError is const >= 0, highError is const for fully on.
duty = constrain(duty, 0, 24); // keep duty between 0 and 24 (24 = 96%)
if(duty>0) { digitalWrite(triac, HIGH); } //*** change, added '>0'
lastError = error;
digitalWrite(test1, LOW); // *** added
}
else {
duty = 0;
}//if (temperature
}//if (tempError
}//if(counter == 25...
}//zero
void updateDisplay() {
pottemperature = analogRead(potentiometer);
pottemperature = map(pottemperature, 0, 1023, 150, 400);
lcd.clear();
lcd.setCursor(0, 0);
lcd.write((byte)0);
lcd.setCursor(2, 0);
lcd.print((int)pottemperature);
lcd.setCursor(6, 0);
lcd.print((char)223); //degree sign
lcd.setCursor(7, 0);
lcd.print("C");
lcd.setCursor(0, 1);
lcd.write((byte)1);
if (temperature <= 45) {
lcd.setCursor(2, 1);
lcd.print("Lo");
} else {
lcd.setCursor(2, 1);
lcd.print((int)temperature);
}
lcd.setCursor(6, 1);
lcd.print("[");
lcd.setCursor(7, 1);
lcd.print((int)realTemperature);
lcd.setCursor(10, 1);
lcd.print("]");
lcd.setCursor(12, 1);
lcd.print((char)223);
lcd.setCursor(13, 1);
lcd.print("C");
}
void displayErrors() {
digitalWrite(relay, LOW); // the relay will disconnect the power to the soldering iron heating element
lcd.clear();
lcd.setCursor(0, 0);
lcd.write((byte)0);
lcd.setCursor(1, 0);
lcd.write((byte)0);
lcd.setCursor(5, 0);
lcd.print("ERROR!");
lcd.setCursor(14, 0);
lcd.write((byte)0);
lcd.setCursor(15, 0);
lcd.write((byte)0);
}

By Physicsch - Own work, CC0If this should be better then why not ...It could be that the current approach, using PWM for temperature control may not be effective, and a better option is simply to use PID to anticipate & inform when to turn iron on and off and just do a more 'informed' version of the original bang-bang control.
Yes, but it does give a clue...I tested for Kp = 5 and 300 C, but it doesn't overshoot. I think that the same thing happens.
I'll do the tests for other settings, if you need them...
If this should be better then why not ...
And if it will work correctly ...
//***in declarations
int lowError = -15; //guessing for now. The bigger these values the smaller the deadband either side of 0 error that constitues a 50% duty cycle
int highError = +15;
void zero() {
counter++;
//*** change this line below
if (counter>duty) { //reach duty cycle limit, unless duty was 25 in which case leave on until next duty calculated later
digitalWrite(triac, LOW);
}
if (counter >= 25) {
counter = 0;
digitalWrite(test, HIGH); //this will generate a pulse on test pin (5) every 250mS to prove counter incrementing...
pottemperature = analogRead(potentiometer);
pottemperature = map(pottemperature, 0, 1023, 150, 400);
digitalWrite(test, LOW); // put test pin low
realTemperature = thermocouple.readCelsius();
temperature = int(0.779828 * realTemperature - 10.3427); // make temperature an integer
if (tempError || isnan(realTemperature) || temperature >= 432) { // on error kill power & set global error flag
digitalWrite(relay, LOW); // turn off power to iron
//*** add this line below just in case
digitalWrite(triac, LOW);
tempError = true; //set error flag. can only be unset outside ISR. Once set no further action taken till unset in main loop.
}
else { //reading valid
//*** if (temperature < pottemperature) { //*** remove this line and allow errors to be both + and -
digitalWrite(test1, HIGH); // *** changed, generate a pulse on test1 (D6) when reading valid
error = pottemperature - temperature; // *** +ve error when low = increase duty cycle, -ve error when high = decrease it
cumError += error * 250.0; //
rateError = (error - lastError) / 250.0; //
output = Kp * error + Ki * cumError + Kd * rateError; //output error needs to be mapped to a number between 0 and 24
duty = map(output, lowError, highError, 0, 25); // *** lowError is const for fully off, highError is const for fully on. zero error maps to 50%
duty = constrain(duty, 0, 25); // ***keep duty between 0 and 25 (25 = 100%)
//*** re-arrange & add 3 lines
if(duty>0) {
digitalWrite(triac, HIGH);
} else {
digitalWrite(triac, LOW);
}
lastError = error;
digitalWrite(test1, LOW);
//*** remove 3 lines
// }
// else {
// duty = 0;
// }//if (temperature
}//if (tempError
} //if(counter >= 25
}// zero()
| DeltaTemperature | Time |
| 50 - 100 | 3.2sec |
| 100-150 | 4.5sec |
| 150-200 | 6sec |
| 200-250 | 8.5sec |
| 250-300 | 11.2sec |

I am sorry, but I don't really understand this. Should it be something like a "compare" between the new set point and minimum temperature ?Yes, but for consistency we need to make the set point a step-change in software from cold, rather than a gradual increase, to get a true measure of thermal rate of change...