Author Topic: Open source TIG welder logic controller  (Read 641 times)

0 Members and 1 Guest are viewing this topic.

Offline robby60259Topic starter

  • Newbie
  • Posts: 4
  • Country: us
Open source TIG welder logic controller
« on: December 16, 2023, 03:15:20 am »
Hello, I am building a welder and I need a circuit that can provide a control signal to adjust the frequency and balance (duty cycle) of the output. I would like to use an Arduino Uno if possible to control these two functions via two potentiometers. I need help with the code. I would prefer the frequency to be adjustable from approximately 50Hz to maybe 200Hz and the duty cycle around 20% to 80%. The values only need to be an approximation, the higher frequency helps with arc control, and the variable duty cycle helps with cleaning and penetration when welding aluminum. This sketch is as far as I have got. Am I going in the right direction? Any help would be greatly appreciated.

byte oscOut = 3 ;
int pot1 = A0;
int pot2 = A1;

void setup()
{
  pinMode(oscOut, OUTPUT);
  pinMode(pot1, INPUT);
  pinMode(pot2,INPUT);
}
void loop()
{
analogRead(pot1);
analogRead(pot2);

  OCR2A = (analogRead(pot1)-380);     // defines the frequency 51 = 38.4 KHz, 54 = 36.2
                                                                                KHz, 58 = 34 KHz, 62 = 32 KHz
  OCR2B = ((analogRead(pot2)-380)/2);     // defines the duty cycle - Half the OCR2A value
                                                                                                                    for 50%
  TCCR2A = _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
  TCCR2B = _BV(WGM22)  | _BV(CS21);             
}
 

Offline ifrythings

  • Contributor
  • Posts: 14
  • Country: ca
Re: Open source TIG welder logic controller
« Reply #1 on: December 16, 2023, 07:58:25 am »
Here's what I made a few years ago, I never did build the H-bridge part yet but the arduino worked.

This uses a rotary encoder and a 16x2 lcd with IIC to parallel converter, temp sensor, current sensor and a few voltage dividers for protection.
Maybe you can use some of the code out of this, it does go 20hz-400hz and a duty cycle of 10%-90%

Code: [Select]
/*                                         ARDUINO NANO
 *                                       ------|USB|------
 *                                      <|FAN  |___|  D12|>
 *                                      <|            D11|>
 *                                      <|           PWM2|>
 *                                      <|IWI1       PWM1|>
 *                                      <|IWI2         D8|>
 *                                      <|THS1         D7|>
 *                                      <|THS2         D6|>
 *                                      <|IIC SDA      D5|>
 *                                      <|IIC SCL     ENS|>
 *                                      <|VWI         ENB|>
 *                                      <|GDV         ENA|>
 *                                      <|5V          GND|>
 *                                      <|               |>
 *                                      <|GND         RX |>
 *                                      <|VIN         TX |>
 *                                       -----------------
 */
//Libraries
#include <Wire.h>
#include <EEPROM.h>
#include <TimerOne.h>
#include <LiquidCrystal_I2C.h>

// ADC
#define VREF 5.003 // Measued value of refference voltage
#define ADC_V VREF/1024.0

// Temperature Sensor:
// MCP9701A Tc=19.5mV/°C, V0°C=400mV
//          (VADC - V0°C)/Tc = Ta
// Ex. (1.5V - 0.4V)/0.0195V/°C = 56°C
#define Tc 1/0.0195 // Reciprocal to use quicker multiplication, Temperature coefficient from datasheet of sensor
#define V0C 0.4 // Voltage at 0°C from datasheet of sensor

// Current Sensor:
// ACS770-ECB-200U Ic=20mV/A Vidle=0.5V
//                 (VADC - Vidle)/Ic = Iweld
// Ex.   (1.5V - 0.5V)/0.02V/A = 50A Welder output current
#define Ic 1/0.02 // Reciprocal to use quicker multiplication, from datasheet of sensor
#define Vidle 0.5 // Idle Output Voltage from datasheet of sensor

// Resistor divider network for welder voltage
// Vmax = Rtop/Rbottom*Vref
// Vweld = Rtop/Rbottom * ADCV
// Ex. 247K/10K*1.5V = 37V Welder output voltage
#define WRtop 247.0 // 247K resistor
#define WRbottom 10.0 // 10K resistor


// Resistor divider network for Gate Driver voltage
// Vmax = Rtop/Rbottom*Vref
// VGD = Rtop/Rbottom * ADCV
// Ex. 40K/10K*3.75V = 15V Gate Drive voltage
#define GDRtop 40.0 // 40K resistor
#define GDRbottom 10.0 // 10K resistor

// Input Declarations
#define pinA 2 // Encoder phase A on hardware digital interrupt pin 2
#define pinB 3 // Encoder phase B on hardware digital interrupt pin 3
#define BUTTON 4 // Encoder Button on digital pin 4
#define FAN 13 // Fan ON/OFF on digital pin 13
#define Weld_Current_1 A0 // Welder input current 1 on analog pin 0
#define Weld_Current_2 A1 // Welder input current 2 on analog pin 1
#define Temp_HS_1 A2 // Temperature of heatsink 1 on analog pin 2
#define Temp_HS_2 A3 // Temperature of heatsink 2 on analog pin 3
#define Weld_Volts A6 // Welder input voltage on analog pin 6
#define GD_Volts A7 // Gate Driver voltage on anlog pin 7

// Variables
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile byte reading = 0; // somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent
volatile int frequency;
volatile int oldfrequency = 0;
volatile byte ac_balance;
volatile byte oldac_balance = 0;
volatile byte button = 0;
volatile byte oldbutton = 0;
unsigned int period = 0;
unsigned int dutycycle = 0;
int weld_current_1 = 0;
int weld_current_2 = 0;
int weld_current_total = 0;
int temp_hs_1 = 0;
int temp_hs_2 = 0;
int Ta = 0;
int weld_volts = 0;
float gd_volts = 0;
byte menu = 0;
unsigned long last_time = millis();
unsigned long old_time = 0;
unsigned long fan_time = 0;
byte run_once = 0;

// Set the LCD I2C address and pinout
//                 address,EN,RW,RS,D4,D5,D6,D6,BL,Polarity
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

//Custom °C symbol
byte degtempC[8] = {
  B11000, // HH...
  B11000, // HH...
  B00111, // ..HHH
  B01000, // .H...
  B01000, // .H...
  B01000, // .H...
  B00111, // ..HHH
  B00000, // .....
};

void PinA() {
  cli(); //stop interrupts happening before we read pin values
  reading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values
  if (reading == B00001100 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
    if (button == 1 && frequency > 20)frequency --; // If frequency is selected in menu and frequency is above 20Hz decrement frequency
    if (button == 2 && ac_balance > 10)ac_balance --; // if AC Balance is selected in menu and AC Balance is above 10% decrement AC Balance
    bFlag = 0; //reset flags for the next turn
    aFlag = 0; //reset flags for the next turn
  }
  else if (reading == B00000100) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
  sei(); //restart interrupts
}

void PinB() {
  cli(); //stop interrupts happening before we read pin values
  reading = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values
  if (reading == B00001100 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
    if (button == 1 && frequency < 400)frequency ++; // If frequency is selected in menu and frequency is below 400Hz increment frequency
    if (button == 2 && ac_balance < 90)ac_balance ++; // if AC Balance is selected in menu and AC Balance is below 90% increment AC Balance
    bFlag = 0; //reset flags for the next turn
    aFlag = 0; //reset flags for the next turn
  }
  else if (reading == B00001000) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
  sei(); //restart interrupts
}

void Button(){
  if (digitalRead(BUTTON) == LOW && oldbutton == 1) { // Make sure the button is only read once when its low (button debounce)
    if (button >= 2){
      button = 0; // 3 level menu loop, level 0 is main, level 1 is Frequancy select, level 2 is AC Balance select
      old_time = millis();
    }
    else button ++;
   
    if (button == 1) { // Frequency has been selected
      lcd.setCursor(15, 0); // Goto the end of the line
      lcd.write(0x7F); // Put an arrow to signify that Frequency is selected
    }
    else {
      EEPROM.put(0, frequency); // Save the new Frequency to eeprom only if its different
      lcd.setCursor(15, 0); // Goto the end of the line
      lcd.print(" "); // Clear the arrow to signify that Frequency is unselected
    }
    if (button == 2) { // AC Balance has been selected
      lcd.setCursor(15, 1); // Goto the end of the line
      lcd.write(0x7F); // Put an arrow to signify that AC Balance is selected
    }
    else {
      EEPROM.update(2, ac_balance); // Save the new AC Balance to eeprom only if its different
      lcd.setCursor(15, 1); // Goto the end of the line
      lcd.print(" "); // lear the arrow to signify that AC Balance is unselected
    }
    oldbutton = 0; // Reset so we can watch for another button press
  }
  if (digitalRead(BUTTON) == HIGH) oldbutton = 1; // Reset the button so it can be read again
}

void Frequency(){
  if (oldfrequency != frequency) { // See if anything changed from last time
    period = 1000000/frequency; // Convert frequency to micro seconds for timer 1 to use
    Timer1.setPeriod(period); // Update timer 1's period for realtime updating
    Timer1.pwm(9, dutycycle); // Apply duty cycle for realtime updating
    Timer1.pwm(10, dutycycle); // Apply duty cycle for realtime updating
    oldfrequency = frequency; // Update old value to new value
  }
}

void AC_Balance(){
    if (oldac_balance != ac_balance) { // See if anything changed from last time
    dutycycle = map(ac_balance, 0, 100, 0, 1023); // Convert and scale from human readable to 10 bit number
    dutycycle += 1; // Calibration
    dutycycle = constrain(dutycycle, 102, 922); // Limit duty cycle to 10% - 90%
    Timer1.pwm(9, dutycycle); // Apply new duty cycle for realtime updating
    Timer1.pwm(10, dutycycle); // Apply new duty cycle for realtime updating
    oldac_balance = ac_balance; // Update old value to new value
  }
}

void Current(){ // Parallel connection of current transducers, Reading, Converting, Summing.
  weld_current_1 = ((analogRead(Weld_Current_1) + 1) * ADC_V - Vidle) * Ic; // Current transducer 1 transfer function to Amps
  weld_current_2 = ((analogRead(Weld_Current_2) + 1) * ADC_V - Vidle) * Ic; // Current transducer 2 transfer function to Amps
  weld_current_total = weld_current_1 + weld_current_2; // Add parallel currents together
  weld_current_total = constrain(weld_current_total, 0, 400); // Keep readings within sensors readable range
}

void Temperature(){
  temp_hs_1 = ((analogRead(Temp_HS_1) + 1) * ADC_V - V0C) * Tc; // Heatsink 1 temperature sensor Transfer function to °C
  temp_hs_2 = ((analogRead(Temp_HS_2) + 1) * ADC_V - V0C) * Tc; // Heatsink 2 temperature sensor Transfer function to °C
  if(temp_hs_1 > temp_hs_2)Ta = temp_hs_1; // If temperature of heatsink 1 is greater than 2, than temperature ambient = temperature heatsink 1
  else Ta = temp_hs_2; // If temperature of heatsink 2 is greater than 1, than temperature ambient = temperature heatsink 2
  Ta = constrain(Ta, -40, 125); // Keep readings within sensors readable range
  if(Ta >= 40 || weld_current_total > 20){ // If Heatsink temperatures get above 40°C or weld current is  >20amps turn fan on
    digitalWrite(FAN, HIGH);
    fan_time = millis();
  }
  else if(Ta < 35 && millis() - fan_time > 5000)digitalWrite(FAN, LOW); // 5°C hysteresis before turning fan off to prevent the fan from oscillating ON/OFF at the set point
}

void Voltage() {
  weld_volts = (analogRead(Weld_Volts) + 1)* ADC_V * (WRtop/WRbottom); // Get output voltage from welder
  gd_volts = (analogRead(GD_Volts) + 2) * ADC_V * (GDRtop/GDRbottom); // Get Gate Drive power supply voltage
}

void Protection(){
  if(gd_volts < 10){ // If Gate Drive Voltage falls below 10V turn off the H-Bridge and notify the operator till resolved
    Timer1.pwm(9, 0); // Turn off PWM
    Timer1.pwm(10, 0); // Turn off PWM
    lcd.clear();
    lcd.setCursor(1, 0); // Go to first character on line 1
    lcd.print("LOW GATE DRIVE");
    lcd.setCursor(1, 1); // Go to first character on line 2
    lcd.print("VOLTAGE");
    while(gd_volts < 12){ // When Gate Drive Voltage exceeds 12V re-enable H-Bridge
      Voltage(); // Make sure to update the voltage so it can be displayed below in realtime
      Temperature(); // Make sure to update Temperature so the fan can START/STOP
      if(millis() - last_time >= 100){ // Refresh rate (10hz)
        lcd.setCursor(9, 1); // Go to first character on line 2
        if(gd_volts < 10)lcd.print(" "); // Text alaginment
        lcd.print(gd_volts,2); // Display updated Gate Drive Voltage
        lcd.print("V");
        last_time = millis(); // Part of refresh rate
      }
    }
    lcd.clear();
    Timer1.pwm(9, dutycycle); // Return PWM to previous state
    Timer1.pwm(10, dutycycle); // Return PWM to previous state
  }
   
  if(Ta >= 90){ // If Heatsinks get above 90°C turn off the H-Bridge and notify the operator till resolved
    Timer1.pwm(9, 0); // Turn off PWM
    Timer1.pwm(10, 0); // Turn off PWM
    lcd.clear();
    lcd.setCursor(0, 0); // Go to first character on line 1
    lcd.print("OVER TEMPERATURE");
    while(Ta > 80){ // When Heatsinks drop below 80°C re-enable H-Bridge
      Temperature(); // Make sure to update Temperature so it can be displayed below and so the fan can START/STOP
      if(millis() - last_time >= 100){ // Refresh rate (10hz)
        lcd.setCursor(6, 1); // Go to first character on line 2
        if(Ta < 100)lcd.print(" ");
        lcd.print(Ta); // Display updated Heatsink Temperature
        lcd.write(byte(0));  // Write °C
        last_time = millis(); // Part of refresh rate
      }
    }
    lcd.clear();
    Timer1.pwm(9, dutycycle); // Return PWM to previous state
    Timer1.pwm(10, dutycycle); // Return PWM to previous state
  }
   
  if(weld_current_total > 210){ // If Welder output current is set too high turn off the H-Bridge and notify the operator to reduce the output current
    Timer1.pwm(9, 0); // Turn off PWM
    Timer1.pwm(10, 0); // Turn off PWM
    lcd.clear();
    lcd.setCursor(2, 0); // Go to first character on line 1
    lcd.print("OVER CURRENT");
    lcd.setCursor(4, 1); // Go to first character on line 2
    lcd.print("MAX 200A");
    oldbutton = 0; // Required to prevent menu selection while acknoledging over current message
    while(digitalRead(BUTTON) == HIGH){ // Wait till the operator acknoledges the over current condition and presses the button to re-enable H-Bridge
      Temperature(); // Make sure to update Temperature so the fan can START/STOP
    }
    lcd.clear();
    Timer1.pwm(9, dutycycle); // Return PWM to previous state
    Timer1.pwm(10, dutycycle); // Return PWM to previous state
  }
} // End of Protection

void setup() {
  pinMode(pinA, INPUT_PULLUP); // set pinA as an input with week pull up
  pinMode(pinB, INPUT_PULLUP); // set pinB as an input with weak pull up
  pinMode(BUTTON, INPUT_PULLUP); // set button as an input with weak pull up
  pinMode(10, OUTPUT); // set pin 10 as output for pwm
  pinMode(FAN, OUTPUT); // set pin 11 as output for fan
  attachInterrupt(0, PinA, RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
  attachInterrupt(1, PinB, RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
  EEPROM.get(0, frequency); // Get last Frequancy value used from eeprom (two bytes)
  period = 1000000/frequency; // convert frequency from Hz to microseconds
  Timer1.initialize(period); // initialize timer 1 with the frequency we got from eeprom
  TCCR1A |= 0x30; // enable pin 10 inverted pwm output
  ac_balance = EEPROM.read(2); // Read last AC balance value used from eeprom (single byte)
  dutycycle += 1; // Calibration
  dutycycle = constrain(dutycycle, 102, 922);
  //Serial.begin(115200); // start the serial monitor link
  lcd.begin(16, 2); // Initiatize the LCD for 16 chars 2 lines
  lcd.setBacklight (HIGH); // Turn backlight on
  lcd.home(); // Go to first character on line 1 (0,0)
  lcd.createChar(0, degtempC); // Load custom °C character into LCD
  if(digitalRead(BUTTON) == HIGH){
    menu = 1; // Normal operating mode
    run_once = 1;
  }
  else{
    menu = 0; // Diagnositics operating mode
    lcd.setCursor(2, 0); // Go to first character on line 2
    lcd.print("DIAGNOSITICS");
    lcd.setCursor(6, 1); // Go to first character on line 2
    lcd.print("MODE");
    delay(2000);
    lcd.clear();
    Timer1.setPeriod(period);
    Timer1.pwm(9, dutycycle);
    Timer1.pwm(10, dutycycle);
  }
 
}

void loop() {
  Temperature();
  Current();
  Voltage();
  Protection();

  if(menu == 0 && millis() - last_time >= 100){ // Refresh rate (10hz)
    lcd.setCursor(0, 0);
    if(weld_volts < 10)lcd.print("  ");
    else if(weld_volts < 100)lcd.print(" ");
    lcd.print(weld_volts);
    lcd.print("V ");
    lcd.setCursor(5, 0);
    if(weld_current_1 < -9);
    else if(weld_current_1 < 0)lcd.print(" ");
    else if(weld_current_1 < 10)lcd.print("  ");
    else if(weld_current_1 < 100)lcd.print(" ");
    lcd.print(weld_current_1);
    lcd.print("A");
    lcd.setCursor(10, 0);
    if(weld_current_2 < -9);
    else if(weld_current_2 < 0)lcd.print(" ");
    else if(weld_current_2 < 10)lcd.print("  ");
    else if(weld_current_2 < 100)lcd.print(" ");
    lcd.print(weld_current_2);
    lcd.print("A");
    lcd.setCursor(0, 1);
    if(temp_hs_1 < -9);
    else if(temp_hs_1 < 0)lcd.print(" ");
    else if(temp_hs_1 < 10)lcd.print("  ");
    else if(temp_hs_2 < 100)lcd.print(" ");
    lcd.print(temp_hs_1);
    lcd.write(byte(0));  // Write °C
    lcd.setCursor(5, 1);
    if(temp_hs_2 < -9);
    else if(temp_hs_2 < 0)lcd.print(" ");
    else if(temp_hs_2 < 10)lcd.print("  ");
    else if(temp_hs_2 < 100)lcd.print(" ");
    lcd.print(temp_hs_2);
    lcd.write(byte(0));  // Write °C
    lcd.setCursor(10, 1);
    if(gd_volts < 10)lcd.print(" ");
    lcd.print(gd_volts,2);
    lcd.print("V");
    last_time = millis(); // Part of refresh rate
  }

  if(millis() - old_time >= 10000 && button == 0 && menu == 1){
    if(millis() - last_time >= 100){
      lcd.setCursor(0, 0);
      if (frequency < 100)lcd.print(" ");
      lcd.print(frequency);
      lcd.print("Hz ");
      lcd.print(ac_balance);
      lcd.print("% ");
      if(Ta < 0)lcd.print(" ");
      else if(Ta < 10)lcd.print("  ");
      else if(Ta < 100)lcd.print(" ");
      lcd.print(Ta);
      lcd.write(byte(0));  // Write °C
      lcd.print("  ");
      lcd.setCursor(0, 1);
      lcd.print("WELDER ");
      if(weld_volts < 10)lcd.print("  ");
      else if(weld_volts < 100)lcd.print(" ");
      lcd.print(weld_volts);
      lcd.print("V ");
      lcd.setCursor(12, 1);
      if(weld_current_total < 10)lcd.print("  ");
      else if(weld_current_total < 100)lcd.print(" ");
      lcd.print(weld_current_total);
      lcd.print("A");
    }
  }
 
  if(menu == 1 || run_once == 1){
    Button();
    Frequency();
    AC_Balance();
    if(millis() - last_time >= 100 && button >= 1 || run_once == 1){ // Refresh rate (10hz)
      lcd.setCursor(0, 0);
      lcd.print("FREQUENCY "); // Write static characters to the display
      lcd.setCursor(0, 1); // Go to first character on line 2
      lcd.print("AC BALANCE "); // Write static characters to the display
      lcd.setCursor(10, 0);
      if (frequency < 100)lcd.print(" ");
      lcd.print(frequency);
      lcd.print("Hz");
      lcd.setCursor(11, 1);
      lcd.print(ac_balance);
      lcd.print("% ");
      last_time = millis(); // Part of refresh rate
      run_once = 0;
    }
  }
}
« Last Edit: December 16, 2023, 09:10:23 am by ifrythings »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf