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);
  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;

  • Seems legit to me, though you might want to disable the interrupt while executing myPID.Compute(), since you can get garbled data in your interrupt, when it happens while the Output variable gets assigned. What exactly is your problem with the code?
    – chrisl
    Commented Apr 27, 2020 at 9:52
  • the problem is that I don't know if I correctly wrote the zero_cross_int() function and I don't know where is the relationship between the output of the PID calculations and the output waveform of the Arduino. I meant in the zero_cross_int() function the read value should be the one from the zero crossing detector or the one from the potentiometer ?
    – beard999
    Commented Apr 27, 2020 at 10:00
  • It is not clear to me, what you mean. You changed your code again. Now you are reading a second analog input in the interrupt. Why? With your old code the logic was the following: analogread the potentiometer for the setpoint, calculate output of PID, use PID for driving the triac signal. Only missing was the reading of the input for the PID (since you are creating a soldering station, here you would have to measure the temperature) and assigning it to Input. The additional analogRead in the new code is meaningless (you don't use the value anywhere)
    – chrisl
    Commented Apr 27, 2020 at 10:40
  • I modified the code again: A1 input is for the potentiometer and A0 is for the temperature sensor.
    – beard999
    Commented Apr 27, 2020 at 10:46
  • I have tried the code above and I am stuck at the potentiometer. I checked it in particular and it is working. But when I am rotating the knob, then nothing happens. I only see on the oscilloscope this waveforms: ibb.co/mBBnmk3
    – beard999
    Commented Apr 27, 2020 at 15:55

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);
    int tmp_dim_time = 75*output;

    // Critical section executed with interrupts disabled.
    dimtime = tmp_dim_time;

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(), set dimtime to a value proportional to set_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 to input. Heat the thermocouple by some external means. Does FIRING_PULSE vary as expected?

  • Set dimtime proportional to output, 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
    Commented Apr 29, 2020 at 15:35
  • @mike_mike: did you fix the wiring? Commented Apr 29, 2020 at 15:38
  • Yes, the “interrupt 0” is wired to the pin “digital 2”, as you said.
    – beard999
    Commented Apr 29, 2020 at 15:46
  • @mike_mike: Are you sure the PID parameters are sensible for your system? How did you tune them? Commented Apr 29, 2020 at 16:01
  • The PID parameters are from another soldering station, which used a mosfet instead of a triac.
    – beard999
    Commented 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.


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);
  } else { // start pulse
    digitalWrite(TRIAC_PIN, HIGH);
    triacOn = true;

void setup() {
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), zeroCrossing, RISING);

void loop() {
  period = period + fadeAmount;
  if (period <= FADE_MIN || period >= FADE_MAX) {
    fadeAmount = -fadeAmount;

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):

enter image description here

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
    Commented Apr 29, 2020 at 19:01

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.