0
\$\begingroup\$

I am running the below code on an Arduino Uno and it is partially working. I will try to explain the problem step by step:

  1. I start the soldering station from AC 230V
  2. The soldering station heats up to maximum (I measure 18mV on the TC at maximum)
  3. I reduce voluntary the temperature by rotating the pot to the minimum
  4. The temperature (and the voltage on the TC) starts to decrease
  5. When the voltage on the TC is about 16-17mV then the power to the soldering iron starts to increase suddenly. And I cannot do anything to make it stable or to decrease it.
  6. The voltage on the TC continues to rise, and when it reaches 22-23mV, I unplug the soldering station from the wall socket, to protect the heater of the soldering iron.

I tried the following solutions for my problem: I checked individually each part of the schematic and all of them were good. I found that this event happens, when in the code the PID error is equal (=) to set point. This makes the PID value to be 7400 and in the delayMicroseconds(maximum_firing_delay - PID_value); line of code the result is zero (0), making the entire sine wave to be present on the soldering iron heater. I measured those things in serial monitor.I have also monitored the voltage on the TC and on the output of the OpAmp and it seems to be ok, with no spikes or suddenly increases or decreases.

Schematic: https://ibb.co/hKn5kjL

//Inputs and outputs
int firing_pin = 5;
int zero_cross = 2;
//Variables
int last_CH1_state = 0;
bool zero_cross_detected = false;
int firing_delay = 7400;
//////////////////////////////////////////////////////
int maximum_firing_delay = 7400;
/*Later in the code you will se that the maximum delay after the zero detection
 * is 7400. Why? Well, we know that the 220V AC voltage has a frequency of around 50-60HZ so
 * the period is between 20ms and 16ms, depending on the country. We control the firing
 * delay each half period so each 10ms or 8 ms. To amke sure we wont pass thsoe 10ms, I've made tests
 * and the 7400us or 7.4ms was a good value. Measure your frequency and chande that value later */
//////////////////////////////////////////////////////
unsigned long previousMillis = 0; 
unsigned long currentMillis = 0;
int temp_read_Delay = 500;
int real_temperature = 0;
int setpoint = 100;
//PID variables
float PID_error = 0;
float previous_error = 0;
float elapsedTime, Time, timePrev;
int PID_value = 0;
//PID constants
int kp = 30; int ki= 10; int kd = 15;
int PID_p = 0; int PID_i = 0; int PID_d = 0;
void setup() {
 //Define the pins
 Serial.begin(9600);
 pinMode (firing_pin,OUTPUT); 
 pinMode (zero_cross,INPUT); 
 PCICR |= (1 << PCIE2); //enable scan 
 PCMSK2 |= (1 << PCINT18); //Set pin D2 (zero cross input) trigger an interrupt on state change.
}
void loop() { 
 currentMillis = millis(); //Save the value of time before the loop
 if(currentMillis - previousMillis >= temp_read_Delay){
 previousMillis += temp_read_Delay; //Increase the previous time for next loop
 //get the real temperature in Celsius degrees
 // added by Mike
 for(int i=0;i<50;i++)
 real_temperature += analogRead(A0); 
 real_temperature /= 50;
 real_temperature = map(real_temperature, 0, 550, 25, 400);
 // end added by Mike
 setpoint = analogRead(A1);
 setpoint = map(setpoint, 0, 1023, 150, 400);
 PID_error = setpoint - real_temperature; //Calculate the pid ERROR
 if(PID_error > 30) //integral constant will only affect errors below 30oC 
 {PID_i = 0;}
 PID_p = kp * PID_error; //Calculate the P value
 PID_i = PID_i + (ki * PID_error); //Calculate the I value
 timePrev = Time; // the previous time is stored before the actual time read
 Time = millis(); // actual time read
 elapsedTime = (Time - timePrev) / 1000; 
 PID_d = kd*((PID_error - previous_error)/elapsedTime); //Calculate the D value
 PID_value = PID_p + PID_i + PID_d; //Calculate total PID value
 //We define firing delay range between 0 and 7400. Read above why 7400!!!!!!!
 if(PID_value < 0) // initial it was <
 { PID_value = 0; }
 if(PID_value > 7400) // initial it was >
 { PID_value = 7400; }
 previous_error = PID_error; //Remember to store the previous error.
// Serial.println("PID_error=");
// Serial.println(PID_error);
// Serial.println("real_temperature=");
// Serial.println(real_temperature);
// Serial.println("setpoint=");
// Serial.println(setpoint);
// Serial.println("PID_value=");
// Serial.println(PID_value);
 }
 //If the zero cross interruption was detected we create the 100us firing pulse 
 if (zero_cross_detected) 
 {
 delayMicroseconds(maximum_firing_delay - PID_value); //This delay controls the power
 digitalWrite(firing_pin,HIGH);
 delayMicroseconds(100);
 digitalWrite(firing_pin,LOW);
 zero_cross_detected = false;
 } 
}
//This is the interruption routine (pind D8(zero cross), D11(increase) and D12(decrease))
//----------------------------------------------
ISR(PCINT2_vect){
 ///////////////////////////////////////Input from optocoupler
 if(PIND & B00000100){ //We make an AND with the state register, We verify if pin D2 is HIGH???
 if(last_CH1_state == 0){ //If the last state was 0, then we have a state change...
 zero_cross_detected = true; //We have detected a state change! We need both falling and rising edges
 }
 }
 else if(last_CH1_state == 1){ //If pin 2 is LOW and the last state was HIGH then we have a state change 
 zero_cross_detected = true; //We haev detected a state change! We need both falling and rising edges.
 last_CH1_state = 0; //Store the current state into the last state for the next loop
 }
}
asked Apr 28, 2020 at 14:43
\$\endgroup\$
29
  • \$\begingroup\$ You should map the output of the PID to your hardware, which is reversed: when you want high drive, you use low values. Something like "triac = 220-pid_output", given that pid_output goes from 0 to 218. \$\endgroup\$ Commented Apr 28, 2020 at 14:53
  • 1
    \$\begingroup\$ It sounds like you need first need to spend some time gaining an understanding of triac AC "dimming" and the way that chops cycles. It also sounds like you may need to learn to use the trigger of your scope (?) - try setting it to line. Hopefully this circuit is running from an isolated drop down transformer, otherwise it is potentially deadly. \$\endgroup\$ Commented Apr 28, 2020 at 20:45
  • 1
    \$\begingroup\$ Additionally the structure of your program is very unwise: you shouldn't do delays of up to an A/C cycle period in an ISR. And you shouldn't repeatedly disable interrupts to change the value and a main loop that is probably running many, many, many times faster than the ISR. And then you have both the main loop and the ISR trying to use the same A/D converter without any coordination... Is this original core, or are you perhaps following a tutorial written by someone who didn't have a very sound idea of what they were doing? \$\endgroup\$ Commented Apr 28, 2020 at 20:48
  • 2
    \$\begingroup\$ Trying to debug this is pointless given ADC conflicts, etc. You need to tear up the code and re-write something sane. Do all the measurements in the main loop. Get the delays out of the ISR by using a hardware timer for the phase chopping. No point in even thinking about PID until you have a sane program structure for the inputs and outputs. In fact, start by just making a "dimmer" where one of your potentiometers adjusts the A/C duty cycle and make sure that you have a nice range of adjustment with no "wrapping" at the end where minimum duty cycle suddenly becomes maximum or the reverse. \$\endgroup\$ Commented Apr 29, 2020 at 16:17
  • 1
    \$\begingroup\$ SImulate the whole process in your mind, and check instant by instant the various voltage levels or software variables. If something happens that you don't expect, you can see which input is unexpected, or which formula does bad calculations. \$\endgroup\$ Commented Apr 30, 2020 at 9:08

1 Answer 1

1
\$\begingroup\$

In this Arduino SE answer I offer two sketches which demonstrate Triac control. This is one of them:

#include <TimerOne.h>
const byte INTERRUPT_PIN = 2;
const byte TRIAC_PIN = 4;
const byte TRIAC_PULSE_MICROS = 30;
const int FADE_MAX = 9800;
const int FADE_MIN = 2000;
volatile bool triacOn;
volatile int period = FADE_MIN; // microseconds cut out from AC pulse
int fadeAmount = 10;
void zeroCrossing() {
 triacOn = false; // triac tuns off self at zero crossing
 Timer1.setPeriod(period); // to call triacPulse() after off period
}
void triacPulse() {
 if (triacOn) { // stop pulse
 digitalWrite(TRIAC_PIN, LOW);
 Timer1.stop();
 } else { // start pulse
 digitalWrite(TRIAC_PIN, HIGH);
 triacOn = true;
 Timer1.setPeriod(TRIAC_PULSE_MICROS);
 }
}
void setup() {
 pinMode(TRIAC_PIN, OUTPUT);
 attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), zeroCrossing, RISING);
 Timer1.initialize();
 Timer1.attachInterrupt(triacPulse);
}
void loop() {
 period = period + fadeAmount;
 if (period <= FADE_MIN || period >= FADE_MAX) {
 fadeAmount = -fadeAmount;
 }
 delay(25);
}

the other uses direct registers access of AVR.

EDIT:

https://arduino.stackexchange.com/questions/75194/arduino-pid-controller-for-triac

mike's oscilloscope output of the sketch above: https://www.youtube.com/watch?v=dnfy_EsPlVI

here are the input and output values of mike's code based on this answer (blue is temperature, red is output for the Triac period):

enter image description here

answered May 6, 2020 at 9:42
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.