Open source TIG welder logic controller - Page 1
Forum section: electronics / general

author: robby60259
author_rank: Newbie
timestamp: December 16, 2023, 03:15:20 am
content: 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.2KHz, 58 = 34 KHz, 62 = 32 KHzOCR2B = ((analogRead(pot2)-380)/2); // defines the duty cycle - Half the OCR2A valuefor 50%TCCR2A = _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);TCCR2B = _BV(WGM22) | _BV(CS21);}

author: ifrythings
author_rank: Contributor
timestamp: December 16, 2023, 07:58:25 am
content: 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:/* 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// Variablesvolatile 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 detentvolatile 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 detentvolatile 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,PolarityLiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);//Custom °C symbolbyte degtempC = {B11000, // HH...B11000, // HH...B00111, // ..HHHB01000, // .H...B01000, // .H...B01000, // .H...B00111, // ..HHHB00000, // .....};void PinA() {cli(); //stop interrupts happening before we read pin valuesreading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's valuesif (reading == B00001100 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edgeif (button == 1 && frequency > 20)frequency --; // If frequency is selected in menu and frequency is above 20Hz decrement frequencyif (button == 2 && ac_balance > 10)ac_balance --; // if AC Balance is selected in menu and AC Balance is above 10% decrement AC BalancebFlag = 0; //reset flags for the next turnaFlag = 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 rotationsei(); //restart interrupts}void PinB() {cli(); //stop interrupts happening before we read pin valuesreading = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's valuesif (reading == B00001100 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edgeif (button == 1 && frequency < 400)frequency ++; // If frequency is selected in menu and frequency is below 400Hz increment frequencyif (button == 2 && ac_balance < 90)ac_balance ++; // if AC Balance is selected in menu and AC Balance is below 90% increment AC BalancebFlag = 0; //reset flags for the next turnaFlag = 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 rotationsei(); //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 selectold_time = millis();}else button ++;if (button == 1) { // Frequency has been selectedlcd.setCursor(15, 0); // Goto the end of the linelcd.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 differentlcd.setCursor(15, 0); // Goto the end of the linelcd.print(" "); // Clear the arrow to signify that Frequency is unselected}if (button == 2) { // AC Balance has been selectedlcd.setCursor(15, 1); // Goto the end of the linelcd.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 differentlcd.setCursor(15, 1); // Goto the end of the linelcd.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 timeperiod = 1000000/frequency; // Convert frequency to micro seconds for timer 1 to useTimer1.setPeriod(period); // Update timer 1's period for realtime updatingTimer1.pwm(9, dutycycle); // Apply duty cycle for realtime updatingTimer1.pwm(10, dutycycle); // Apply duty cycle for realtime updatingoldfrequency = frequency; // Update old value to new value}}void AC_Balance(){if (oldac_balance != ac_balance) { // See if anything changed from last timedutycycle = map(ac_balance, 0, 100, 0, 1023); // Convert and scale from human readable to 10 bit numberdutycycle += 1; // Calibrationdutycycle = constrain(dutycycle, 102, 922); // Limit duty cycle to 10% - 90%Timer1.pwm(9, dutycycle); // Apply new duty cycle for realtime updatingTimer1.pwm(10, dutycycle); // Apply new duty cycle for realtime updatingoldac_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 Ampsweld_current_2 = ((analogRead(Weld_Current_2) + 1) * ADC_V - Vidle) * Ic; // Current transducer 2 transfer function to Ampsweld_current_total = weld_current_1 + weld_current_2; // Add parallel currents togetherweld_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 °Ctemp_hs_2 = ((analogRead(Temp_HS_2) + 1) * ADC_V - V0C) * Tc; // Heatsink 2 temperature sensor Transfer function to °Cif(temp_hs_1 > temp_hs_2)Ta = temp_hs_1; // If temperature of heatsink 1 is greater than 2, than temperature ambient = temperature heatsink 1else Ta = temp_hs_2; // If temperature of heatsink 2 is greater than 1, than temperature ambient = temperature heatsink 2Ta = constrain(Ta, -40, 125); // Keep readings within sensors readable rangeif(Ta >= 40 || weld_current_total > 20){ // If Heatsink temperatures get above 40°C or weld current is >20amps turn fan ondigitalWrite(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 weldergd_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 resolvedTimer1.pwm(9, 0); // Turn off PWMTimer1.pwm(10, 0); // Turn off PWMlcd.clear();lcd.setCursor(1, 0); // Go to first character on line 1lcd.print("LOW GATE DRIVE");lcd.setCursor(1, 1); // Go to first character on line 2lcd.print("VOLTAGE");while(gd_volts < 12){ // When Gate Drive Voltage exceeds 12V re-enable H-BridgeVoltage(); // Make sure to update the voltage so it can be displayed below in realtimeTemperature(); // Make sure to update Temperature so the fan can START/STOPif(millis() - last_time >= 100){ // Refresh rate (10hz)lcd.setCursor(9, 1); // Go to first character on line 2if(gd_volts < 10)lcd.print(" "); // Text alaginmentlcd.print(gd_volts,2); // Display updated Gate Drive Voltagelcd.print("V");last_time = millis(); // Part of refresh rate}}lcd.clear();Timer1.pwm(9, dutycycle); // Return PWM to previous stateTimer1.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 resolvedTimer1.pwm(9, 0); // Turn off PWMTimer1.pwm(10, 0); // Turn off PWMlcd.clear();lcd.setCursor(0, 0); // Go to first character on line 1lcd.print("OVER TEMPERATURE");while(Ta > 80){ // When Heatsinks drop below 80°C re-enable H-BridgeTemperature(); // Make sure to update Temperature so it can be displayed below and so the fan can START/STOPif(millis() - last_time >= 100){ // Refresh rate (10hz)lcd.setCursor(6, 1); // Go to first character on line 2if(Ta < 100)lcd.print(" ");lcd.print(Ta); // Display updated Heatsink Temperaturelcd.write(byte(0)); // Write °Clast_time = millis(); // Part of refresh rate}}lcd.clear();Timer1.pwm(9, dutycycle); // Return PWM to previous stateTimer1.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 currentTimer1.pwm(9, 0); // Turn off PWMTimer1.pwm(10, 0); // Turn off PWMlcd.clear();lcd.setCursor(2, 0); // Go to first character on line 1lcd.print("OVER CURRENT");lcd.setCursor(4, 1); // Go to first character on line 2lcd.print("MAX 200A");oldbutton = 0; // Required to prevent menu selection while acknoledging over current messagewhile(digitalRead(BUTTON) == HIGH){ // Wait till the operator acknoledges the over current condition and presses the button to re-enable H-BridgeTemperature(); // Make sure to update Temperature so the fan can START/STOP}lcd.clear();Timer1.pwm(9, dutycycle); // Return PWM to previous stateTimer1.pwm(10, dutycycle); // Return PWM to previous state}} // End of Protectionvoid setup() {pinMode(pinA, INPUT_PULLUP); // set pinA as an input with week pull uppinMode(pinB, INPUT_PULLUP); // set pinB as an input with weak pull uppinMode(BUTTON, INPUT_PULLUP); // set button as an input with weak pull uppinMode(10, OUTPUT); // set pin 10 as output for pwmpinMode(FAN, OUTPUT); // set pin 11 as output for fanattachInterrupt(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 microsecondsTimer1.initialize(period); // initialize timer 1 with the frequency we got from eepromTCCR1A |= 0x30; // enable pin 10 inverted pwm outputac_balance = EEPROM.read(2); // Read last AC balance value used from eeprom (single byte)dutycycle += 1; // Calibrationdutycycle = constrain(dutycycle, 102, 922);//Serial.begin(115200); // start the serial monitor linklcd.begin(16, 2); // Initiatize the LCD for 16 chars 2 lineslcd.setBacklight (HIGH); // Turn backlight onlcd.home(); // Go to first character on line 1 (0,0)lcd.createChar(0, degtempC); // Load custom °C character into LCDif(digitalRead(BUTTON) == HIGH){menu = 1; // Normal operating moderun_once = 1;}else{menu = 0; // Diagnositics operating modelcd.setCursor(2, 0); // Go to first character on line 2lcd.print("DIAGNOSITICS");lcd.setCursor(6, 1); // Go to first character on line 2lcd.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 °Clcd.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 °Clcd.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 °Clcd.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 displaylcd.setCursor(0, 1); // Go to first character on line 2lcd.print("AC BALANCE "); // Write static characters to the displaylcd.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 raterun_once = 0;}}}