Soldering iron code

Is this soldering station code efficent and if something should be changed?

#include "Arduino.h"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <max6675.h>
#include <PID_v1.h>

// PID declaration
#define P_PID 40
// 40
#define I_PID 2
// 2
#define D_PID 25
// 25

// pin declaration
int buttonPin = 8;
int PIDPin = 5; // PWM?
int ktcSO = 10;
int ktcCS = 11;
int ktcCLK = 9;
int potPin = A0;
int powerPin = A1;  //??
int heaterLed = 6;
int buzzPin = 5;

// button service
boolean stat = false;
boolean buttonState;
boolean lastButtonState;
unsigned long time_passed;
unsigned long button_time;
unsigned long last_button_time;
int button_wait = 50;

// temp set and readout
int read_value = 0;
int set_value = 0;

int voltage = 0;
String voltage_lcd_read_value; //??
String voltage_lcd_set_value; //??
String voltage_lcd; //??

String pwr_lcd;
int temp_max = 500;

unsigned long voltageMillis = 0;
unsigned long previousMillis = 0;
unsigned long nowMillis = 0;

int pause_time = 300;

// voltage measurement mode
int voltage_measurement = 0; //??
float voltage_read_value = 0; //??
float voltanie = 0; //??

//LCD special characters
////////////////////////
byte energy[8] = //??
{ //??
  0b00010, 0b00100, 0b01000, 0b11111, 0b00010, 0b00100, 0b01000, 0b00000 //??
}; //??

byte temp[8] =
{
  0b00100, 0b01010, 0b01010, 0b01110, 0b01110, 0b11111, 0b11111, 0b01110
};

byte volt[8] = { //??
  B01110, B11011, B10001, B10001, B10001, B10001, B11111, B00000 //??
}; //??
///////////////////////

double Setpoint, Input, Output;
//double aggKp = 6.4, aggKi = 0.25, aggKd = 2;
//double aggKp = 4, aggKi = 0.10, aggKd = 1;

double aggKp = P_PID, aggKi = I_PID, aggKd = D_PID;
double consKp = aggKp, consKi = aggKi, consKd = aggKd;

PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, DIRECT);
MAX6675 ktc(ktcCLK, ktcCS, ktcSO);
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {

  String ver = "1.0.3";
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  myPID.SetMode(AUTOMATIC);
  myPID.SetOutputLimits(0, 255);
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(PIDPin, OUTPUT);
  pinMode(buzzPin, OUTPUT);
  pinMode(potPin, INPUT_PULLUP);
  pinMode(heaterLed, OUTPUT);
  delay(200);
  lcd.createChar(2, temp);
  lcd.createChar(3, energy); //??
  lcd.createChar(4, volt); //??

  // welcome screen
  lcd.setCursor(0, 0);
  lcd.print("   ArduSolder     ");
  lcd.setCursor(0, 1);
  lcd.print("   ver ");
  lcd.print(ver);
  delay(1000);
  lcd.clear();
}

void loop() {
  printDebug();
  checkLocalButton();
  time_passed = millis();
  int Output_led = Output;

  // readout from the soldering iron
  voltageMillis = millis();
  if (voltageMillis - previousMillis >= pause_time)
  {
    read_value = ktc.readCelsius();
    previousMillis = voltageMillis;
    //    Serial.print("Temp C = ");
    //    Serial.println(ktc.readCelsius());
  }

  // checking if soldering iron is connected
  //
  //  if   (read_value == 0) //
  //  {
  //    tone(buzzPin, 250, 20);
  //    lcd.setCursor(0, 0);
  //    lcd.print(" Podlacz kolbe  ");
  //    lcd.setCursor(0, 1);
  //    lcd.print("stacja wylaczona  ");
  //    stat = 0;
  //    analogWrite(PIDPin, 0);
  //    noTone(buzzPin);
  //  }
  //  // jest podlaczona
  //  else
  //  {
  // sprawdzanie czy temp. nie przekroczyła granicznych wartości
  if (read_value < temp_max)
  {
    if (voltage_measurement == 1)
    {
      voltage_read_value = analogRead(powerPin); //??
      voltanie = (voltage_read_value / 1024) * 25 * 0.9715; //??
    }

    // read_value data from the pot
    set_value = analogRead(potPin);

    // counting set up temperature
    voltage = (set_value / 2.58) + 60;  //////// 66 i 2,55?
    if (voltage > 450)    voltage = 450;
    if (voltage < 60)     voltage = 60;

    // set temperature
    Setpoint = voltage;
    if (stat == 0) // status OFF
    {
      Output = 0;
      analogWrite(PIDPin, Output);
    }
    else  // status ON
    {
      Input = read_value;
      double gap = abs(Setpoint - Input);
      if (gap < 30)
      {
        myPID.SetTunings(consKp, consKi, consKd);
      }
      else
      {
        myPID.SetTunings(aggKp, aggKi, aggKd);
      }
      myPID.Compute();
      analogWrite(PIDPin, Output);
    }

    // format set value to LCD readout
    voltage_lcd_set_value = String(voltage);
    int length1 = voltage_lcd_set_value.length();
    if (length1 == 2)
    {
      voltage_lcd_set_value = " " + voltage_lcd_set_value;
    }
    else if (length1 == 3)    {
      voltage_lcd_set_value = "" + voltage_lcd_set_value;
    }
    else    {    }

    // format voltage set value to LCD readout
    if (voltage_measurement == 1)
    {
      voltage_lcd = String(voltanie, 2);
      int length4 = voltage_lcd.length();
      if (length4 == 4)
      {
        voltage_lcd = " " + voltage_lcd;
      }
      else if (length4 == 5)
      {
        voltage_lcd = "" + voltage_lcd;
      }
      else
      {
      }
    }

    voltage_lcd_read_value = String(read_value);
    int length2 = voltage_lcd_read_value.length();
    if (length2 == 1)
    {
      voltage_lcd_read_value = "  " + voltage_lcd_read_value;
    }
    else if (length2 == 2)
    {
      voltage_lcd_read_value = " " + voltage_lcd_read_value;
    }
    else if (length2 == 3)
    {
      voltage_lcd_read_value = "" + voltage_lcd_read_value;
    }

    pwr_lcd = String(Output / 2.55, 0);
    int length13 = pwr_lcd.length();
    if (length13 == 1)
    {
      pwr_lcd = "  " + pwr_lcd;
    }
    else if (length13 == 2)
    {
      pwr_lcd = " " + pwr_lcd;
    }
    else if (length13 == 3)
    {
      pwr_lcd = "" + pwr_lcd;
    }
    else {}

    // readout voltage measurement
    if (voltage_measurement == 1)
    {
      lcd.setCursor(6, 0);
      lcd.write(4);
      lcd.setCursor(10, 0);
      lcd.print(voltage_lcd);
      lcd.setCursor(15, 0);
      lcd.print("V");
    }
    else
    {
      lcd.setCursor(0, 1);
      lcd.print(" ");
      lcd.write(2);
      lcd.print(" = ");
      lcd.setCursor(5, 1);
      lcd.print(voltage_lcd_read_value);
      lcd.print((char)223);
      lcd.print("C ");
      lcd.setCursor(12, 0);
      lcd.print(pwr_lcd);
      lcd.print("%");
      lcd.setCursor(0, 0);
      lcd.print("SET= ");
      lcd.setCursor(5, 0);
      lcd.print(voltage_lcd_set_value);
      lcd.print((char)223);
      lcd.print("C  ");

      if ( stat == 0)
      {
        lcd.setCursor(10, 0);
        lcd.print(" ");
        lcd.setCursor(11, 1);
        lcd.print("  OFF ");
        digitalWrite(heaterLed, LOW);

      }

      if ( stat == 1)
      {
        //    lcd.setCursor(11, 0);
        //   lcd.write(3);
        lcd.setCursor(11, 1);
        lcd.print("   ON ");
        analogWrite(heaterLed, Output_led / 1.5);
      }
    }
  }
  else
  {
    lcd.setCursor(0, 0);
    lcd.clear();
    lcd.print("  UWAGA !! ");
    lcd.setCursor(0, 1);
    lcd.print("  Temp. > ");
    lcd.print(temp_max);
    lcd.print((char)223);
    lcd.print("C");
    delay(700);
    lcd.setCursor(0, 1);
    lcd.print("stacja wylaczona");
    stat = 0;
    analogWrite(PIDPin, 0);

  }
}

void printDigits(byte digits)
{
  if (digits < 10)
  {
    lcd.print('0');
    lcd.print(digits, DEC);
  }
}

void checkLocalButton()
{
  buttonState = digitalRead(buttonPin);
  // compare the buttonState to its previous state
  if (buttonState != lastButtonState && last_button_time - button_time >= button_wait) // debouncing prevention
  {
    button_time = millis();
    if (buttonState == LOW)  // changed to pressed
    {
      last_button_time = millis();
      stat = !stat;
    }
  }
  lastButtonState = buttonState;
  last_button_time = millis();
}
void printDebug() // just for debugging
{
  //  int pin = digitalRead(buttonPin);
  //  int heaterled = digitalRead(heaterLed);
  //  Serial.print("button ");
  // / Serial.println(button);
  //  Serial.print("heaterLed ");
  //  Serial.println(heaterled);
  Serial.print("button ");
  Serial.println(buttonState);
  //    Serial.print("last_button_time ");
  //    Serial.println(last_button_time);
  //    Serial.print("button_time ");
  //    Serial.println(button_time);
  //  delay (200);
  //  Serial.println(voltage_lcd_read_value);
  //  Serial.println(P_PID);
}

do you expect someone to look at your code, reverse engineer it to figure out what 390 lines of does and then see if there are any bugs?

Ok, maybe I should be more clear. I kindly please someone to take a look at this code and tell me does it have any obvious bugs/mistakes or does the code can be written much better. Thank you for your comment.

whenever we did code reviews are work, we started with a description of the code and the various test cases and results.

Could you tell me more. Sound fair but I don't know what you exactly mean?

I stopped reading the code as soon as I saw "PID". Why would you implement the complexity of a PID for this rather than a simple Bang-Bang temperature controller with a degree or two of Hysteresis around the set point?

Isn't PID more precise?

szawus:
Is this soldering station code efficent and if something should be changed?

You need to tell us in detail what actually happens when you run the program and if that is not what you want to happen then you need to tell us what you want it to do that is different.

You also need to provide details of the soldering iron. Some of them are designed to heat up and cool down very rapidly, others have a much slower response.

...R

szawus:
Isn't PID more precise?

How precise do you need for a soldering iron?

you knew that was coming.

szawus:
Is this soldering station code efficent and if something should be changed?

Efficient? No.

  • It uses String objects.
  • It switches between two IDENTICAL sets of PID parameters.
  • It reads the temperature only periodically.

Some things that should be changed:

  • Variable names. They aren't very informative.
  • Variable names. Hard to tell locals from globals.
  • Variable names. Hard to tell pin numbers from variables.
  • A bunch of "// ??" comments that aren't helping anyone
  • An empty 'else' clause.
  • Using integers for PID parameters which are 'double'.
  • Using 'int' variables instead of 'const byte' variables to name pins.

Awesome! Thanks for informative advices! That's what I was asking for. I have a problem cause when I am reading temperature periodically it is "stucked". I dont know why. 1/250ms is the "fastest". I will change rest of stuff but for now mabe you can tell me why could be that.

And can you please tell me what PID parameters are you talking about. I can see double as Ki, Kp and Kd. Thanks!

EDIT: I have found something that can be related to my issue. Anyone knows anything about this?

Well after many hours of banging my head against what seemed to be a brick wall, I believe I have found the solution.

I read somewhere that the MAX6675 could not be expected to produce more than about 5 reads per second. My test program did 1 read every second, and so worked as expected.

The FiveD firmware, however, read the MAX6675 much more frequently (I had #define SLOW_CLOCK 5000 in configuration.h), resulting in the MAX6675 not updating it's temperature reading.

So, changing SLOW_CLOCK to 50000 solved the problem!

source: Temperature readings not updating

and I have found this:

Is it possible to make some "buffer" so readouts would be done 4x second but program and lcd will "get" those values more frequently? Cheers

Why would it be necessary to measure the temperature of a soldering iron more than 5 times per second?

...R

Actually good question. I wrote that in response to johnwasser. Don’t know is it an issue.

szawus:
Actually good question. I wrote that in response to johnwasser. Don’t know is it an issue.

If you answer the questions I asked in Reply #7 it will be much easier to help you.

...R

Ok. So let me summarize. I wont be attaching my code cause no point and it became too long. Please tell me few things so I can learn.

  1. Why using strings is inefficient?
  2. What PID values John reffered to?
  3. Is there a way to make PID autotune? I am familiar with PID Auto tune library but I don't know how to implement it to the real world applications. Is there any tutorial about that?

szawus:
Please tell me few things so I can learn.

For my money the first and most important thing is to learn to answer the questions YOU have been asked - before you start asking more questions

...R

Ok, fair enough. I didn't use bang bang hysteresis cause I thoght that PID i more accurate as has less overshoot during warming up.
I thought that measuring temperature more than 5 times a second is important cause John wrote that - at least I felt like he was saying that it is bad. I don't have personal oppion about this, that's why I am asking. If measuring more than 5 times a second isn't an issue - nice.

Answering Robin2 question (#7) - my soldring iron is working now quite well. I was more asking for some advices if anything can be done much better and more efficient. Sugestions like - just just a bang-bang controler gave me something to think about. Code IS running now so I don't have "more precise" questions like "why isn't it working. Cause it works. I just thought that it can work much better. Appreciate your time.

Have you characterized the performance under adverse conditions?

how stable is the temperature?

How well does it react when you try the solder something that can conduct away a lot of heat like really thick wire, or a ground pad on a groundplane board where there aren't thermals around the pad, or aluminum-core PCB - you should see it drift down some (indeed, it would have to be psychic otherwise, as how but the slightly lower temperature would it know that it should be trying harder to heat up the iron?), but not a huge amount, and it should start trying to recover as best it can quickly. What about when you do that stress test, let it do its best, hopefully soldering whatever it is, and then take the iron away - does it jump up? how far? You certainly don't want to risk it jumping up so high that if then did a normal easy joint immediately afterwards, it would make a poor joint, or even lift traces.

Quite frankly, you can make a pretty good iron with very crude algorithms - Weller used to do it with a magnet on a switch in the iron, while the end of the tip inserted into the iron was made of a material with a curie point at the target temperature. Below the curie point, the material was magnetic, so the magnetic switch turned on the heating element, once the temperature raised above the curie point, it would no longer be magnetic, and the switch would turn off the heat. That simple. Those irons still work very well, and I used one for a long time for board work with no issues - and I still have it for the day when the adjustable one I have (which is decades old, from the glory days of Weller) suffers some failure. I would never choose to use a curie point iron if I had an adjustable one (assuming both are aqua colored wellers) on hand, but if I could only own one iron, I would want it to be a curie point one, as there isn't really anything to fail in them, and an electronics guy without a soldering iron is up a creek without a paddle.

Actually it works quite nice. It stays at desired temp. It floats like 1-2 deg C from the desired temp. I am using big flat iron tip so it has some thermal capacity. everything looks stable. Soldering something, Large ground pads etc don't make any difference. Looks just ok.