I am designing a new soldering station which will use a 24 V ac soldering iron.
I started to make some research and I designed a few steps of the soldering station. I will use a 24Vac/100VA toroidal transformer. I will use a 24V/50W soldering iron with K type thermocouple. For the thermocouple I will use a LM358 amplifier. Please have a look at the attached schematic and code. The code is not ready yet and I need some help in making the connection between the PID library and output in the code. The voltage on the soldering iron looks like: https://ibb.co/CMF1gQ0 If I rotate the pot, nothing happens on the screen of the scope.
I wrote a few lines of code but I don't know how to make the connection between the PID library functions and the interruption function and the other variables in my code.
Please have a look at the code and at the schematic and tell me what you think... I am waiting for your help.
#include <PID_v1.h>
int AC_LOAD = 1; // Output to Opto Triac pin
double set_value = 0;
double input = 0;
double output = 0;
PID aaa_PID(&input, &output, &set_value, 1, 0.05, 0.25, DIRECT);
void setup()
{
aaa_PID.SetOutputLimits(0, 128);
aaa_PID.SetMode(AUTOMATIC);
pinMode(AC_LOAD, OUTPUT);// Set AC Load pin as output
attachInterrupt(0, zero_crosss_int, RISING); // Choose the zero cross interrupt # from the table above
}
//the interrupt function must take no parameters and return nothing
void zero_crosss_int() //function to be fired at the zero crossing to dim the light
{
// Firing angle calculation : 1 full 50Hz wave =1/50=20ms
// Every zerocrossing thus: (50Hz)-> 10ms (1/2 Cycle)
// For 60Hz => 8.33ms (10.000/120)
// 10ms=10000us
// (10000us - 10us) / 128 = 75 (Approx) For 60Hz =>65
int dimtime = (75*output); // For 60Hz =>65
delayMicroseconds(dimtime); // Wait till firing the TRIAC
digitalWrite(AC_LOAD, HIGH); // Fire the TRIAC
delayMicroseconds(10); // triac On propogation delay (for 60Hz use 8.33)
digitalWrite(AC_LOAD, LOW); // No longer trigger the TRIAC (the next zero crossing will swith it off) TRIAC
}
void loop() {
int set_point = analogRead(A1);
set_point = map(set_point, 0, 1023, 150, 400);
int in = analogRead(A0);
input = map(in, 0, 550, 25, 400);
set_value = set_point;
aaa_PID.Compute();
}
2 Answers 2
I see several issues here.
The most serious is in the wiring of the Arduino. The first argument of
attachInterrupt()
is an interrupt number. On the Uno, "interrupt 0" is
wired to the pin "digital 2". This is where you should connect the
output of the zero-crossing detector (ZERO_OUT).
Then, as noticed by Juraj, there is the issue of the interrupt handler
taking too long to execute. This is generally considered bad practice,
and it has some negative consequences on the working of the Arduino core
like, e.g., the millis()
function will be completely off. I do not
think, however, that this is a serious issue for you. There is one
single time-critical bit in your program, which is the generation of the
triac trigger pulses. You really do not care about millis()
or any
other time-related things. Thus, at least for the initial version of the
program, I would not care too much, and just let the interrupt handler
delay as needed.
The third issue has been noted by chrisl in a comment: there is a race
condition with both the main program and the interrupt handler accessing
the same output
variable. Race conditions are a nasty kind of bug
because usually things work fine "most of the time" and then fail at
unexpected times. I would not recommend, however, to run
myPID.Compute()
with interrupts disabled: that may throw off the
timing of your triac pulses. Instead, compute the delay required by the
interrupt, save it to a temporary variable, then copy it to a variable
shared with the handler. Only the copy needs to be done with interrupts
disabled, and that takes only a handful of CPU cycles:
// Variable used by zero_crosss_int().
// Do not forget the `volatile' qualifier.
volatile int dimtime;
void loop() {
// PID and associated computations.
set_value = map(analogRead(A1), 0, 1023, 150, 400);
input = map(analogRead(A0), 0, 550, 25, 400);
aaa_PID.Compute();
int tmp_dim_time = 75*output;
// Critical section executed with interrupts disabled.
noInterrupts();
dimtime = tmp_dim_time;
interrupts();
}
Edit: If things do not work, you should try to debug piece by piece. Sending debug messages to the serial port should help, although serial communication is likely to be significantly slowed down by the long-running interrupt handler. As an alternative, you can use the triac control pulses as a debug aid, maybe with the heating element unplugged. For example:
In
loop()
, setdimtime
to a value proportional toset_value
. Turn the pot and look at the FIRING_PULSE signal. Does the pulse width vary proportionally to the pot position? Is it in sync with the Vin zero-crossings?Set
dimtime
proportional toinput
. Heat the thermocouple by some external means. Does FIRING_PULSE vary as expected?Set
dimtime
proportional tooutput
, but keep the heating element unplugged. How does the pulse change as you move the pot or heat the thermocouple?
-
I added the code into my program and I run it, but on the scope screen appears something like this: youtube.com/watch?v=rAK9ounINAQ , and the heating of the soldering iron does not stop. As the temperature increases, the voltage on the soldering iron heater also increases...beard999– beard9992020年04月29日 15:35:37 +00:00Commented Apr 29, 2020 at 15:35
-
@mike_mike: did you fix the wiring?Edgar Bonet– Edgar Bonet2020年04月29日 15:38:07 +00:00Commented Apr 29, 2020 at 15:38
-
Yes, the "interrupt 0" is wired to the pin "digital 2", as you said.beard999– beard9992020年04月29日 15:46:25 +00:00Commented Apr 29, 2020 at 15:46
-
@mike_mike: Are you sure the PID parameters are sensible for your system? How did you tune them?Edgar Bonet– Edgar Bonet2020年04月29日 16:01:03 +00:00Commented Apr 29, 2020 at 16:01
-
The PID parameters are from another soldering station, which used a mosfet instead of a triac.beard999– beard9992020年04月29日 16:03:09 +00:00Commented Apr 29, 2020 at 16:03
I need some help in making the connection between the PID library and output in the code
PID calculates the value of output
and it is used in int dimtime = (75*output);
.
The aaa_PID
object gets the reference to variable output
with &
so when the PID algorithm sets the output value it sets the value of this variable in your sketch.
output
should be volatile double output = 0;
because it is used in interrupt.
EDIT 2:
I hope you wired the zero crossing detector to pin 2, because that is the pin for external interrupt 0. And don't use pin 1 for Triac, it is wired to USB chip too.
EDIT: Your code stays too long in interrupt. You exit the interrupt only in the time after firing the TRIAC and the next zero crossing.
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 o AVR.
EDIT 3: mike's oscilloscope output of the sketch above: https://www.youtube.com/watch?v=dnfy_EsPlVI
EDIT 4: the PID outputs high values for high power and small values for lower power. but the Triac period is the off time. so the period should be something like period = 11000 - (75 * output)
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):
The PID control gets smoother over time as it finetunes its parameters, but I think a simple PID has some problems with the sine relation between the output (time) and the resulting heating power.
-
Comments are not for extended discussion; this conversation has been moved to chat.VE7JRO– VE7JRO2020年04月29日 19:01:07 +00:00Commented Apr 29, 2020 at 19:01
myPID.Compute()
, since you can get garbled data in your interrupt, when it happens while theOutput
variable gets assigned. What exactly is your problem with the code?Input
. The additional analogRead in the new code is meaningless (you don't use the value anywhere)