Author Topic: Arduino and Interrupts - trying to understand code and to add new function  (Read 816 times)

0 Members and 1 Guest are viewing this topic.

Offline mike_mikeTopic starter

  • Frequent Contributor
  • **
  • Posts: 944
  • Country: ro
I am thinking about building the project from this web site: https://www.instructables.com/id/DIY-Arduino-Soldering-Station/
I will use a Nokia 5110 display connected at the digital outputs of an Arduino Nano. You can find the schematic attached. The AC1 and AC2 is a 24Vac/100W transformer. The soldering iron will be a Gordak 24V/50W, with K Type Thermocouple.
The relay is used to cut power when the thermocouple wire gets disconnected or when the temperature is above 432 C.

I will also add a protection - which will cut the power to the iron - using the relay.
I wrote a code modification, which will cut off the power to the iron and will display a error message on the lcd.

Is the checkForErrors() function correct ?
Where, in the code, should I call the above mentioned function ?



Code: [Select]
#include <PCD8544.h>
#include <SPI.h>
#include <Wire.h>
#include <max6675.h>

#define thermoDO 11
#define thermoCS 12
#define thermoCLK 13
#define potentiometer A0
#define zerocrossing 2
#define triac 7
#define relay 9
float temperature, realTemperature;
int pottemperature;
int counter;

MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);

PCD8544 lcd(3, 4, 5, 6, 8);

void setup() {
  lcd.begin(84, 48);
  pinMode(potentiometer, INPUT);
  pinMode(zerocrossing, INPUT_PULLUP);
  pinMode(triac, OUTPUT);
  digitalWrite(triac, LOW);
  counter = 0;
  realTemperature = thermocouple.readCelsius();
  temperature = 0.779828 * realTemperature - 10.3427;
  updatedisplay();
  attachInterrupt(0, zero, RISING);
}

void loop() {
}

void zero() {
  counter++;
  if (counter < 40) {
    if (temperature <= pottemperature) {
      digitalWrite(triac, HIGH);
    }
    else {
      digitalWrite(triac, LOW);
    }
  }

  if (counter == 40) {
    digitalWrite(triac, LOW);
    detachInterrupt(0);
    realTemperature = thermocouple.readCelsius();
    temperature = 0.779828 * realTemperature - 10.3427;
    updatedisplay();
    counter = 0;
    attachInterrupt(0, zero, RISING);
  }
}

void updatedisplay() {
  pottemperature = analogRead(potentiometer);
  pottemperature = map(pottemperature, 0, 1023, 0, 400);
  checkForErrors();
  lcd.setCursor(0, 0);
  lcd.print("SET= ");
  lcd.print(5, 0);
  lcd.print(pottemperature);
  lcd.setCursor(0, 1);
  lcd.print("READ= ");
  lcd.setCursor(7, 1);
  lcd.print((int)temperature);
  delay(200);

}

void checkForErrors() {
  realTemperature = thermocouple.readCelsius();
  if (isnan(realTemperature) or realTemperature >= 432) {
    while (true) {
      detachInterrupt(0);
      digitalWrite(triac, LOW);
      digitalWrite(relay, LOW); // the relay will disconnect the power to the soldering iron heating element
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Error !");
      delay(500);
    }
  }
}
« Last Edit: July 14, 2020, 07:42:41 pm by mike_mike »
 

Offline Manul

  • Super Contributor
  • ***
  • Posts: 1158
  • Country: lt
I think you need in Setup()

Code: [Select]
pinMode(relay OUTPUT);
digitalWrite(relay, HIGH);

And here you already have temperature, so no need to check it again, just pass it to your function (more optimized):

Code: [Select]
void zero() {
  counter++;
  if (counter < 40) {
    if (temperature <= pottemperature) {
      digitalWrite(triac, HIGH);
    }
    else {
      digitalWrite(triac, LOW);
    }
  }

  if (counter == 40) {
    digitalWrite(triac, LOW);
    detachInterrupt(0);
    realTemperature = thermocouple.readCelsius();
    temperature = 0.779828 * realTemperature - 10.3427;

    checkForErrors(temperature); Pass temperature to your function

    updatedisplay();
    counter = 0;
    attachInterrupt(0, zero, RISING);
  }
}

Your function may be like that:

Code: [Select]
void checkForErrors(float Temp) {
  if (thermocouple.readError() || Temp >= 432) {
    digitalWrite(relay, LOW); // the relay will disconnect the power to the soldering iron heating element
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Error !");
    while(true);  //Loop forever until reboot
  }
}

From looking at a library I think thermocouple.readError() is a proper way to see if some error happened, so I changed.

P.S. I think "||" is correct logical operator, not "or". Code which I provided is not guaranteed. I'm not Arduino programmer.

EDIT: I just saw that temperature is a global variable here, so no need to pass it as an argument. You should add some freewheeling diode on your relay.
« Last Edit: July 14, 2020, 08:57:17 pm by Manul »
 
The following users thanked this post: mike_mike

Offline mike_mikeTopic starter

  • Frequent Contributor
  • **
  • Posts: 944
  • Country: ro
Hello, I built the attached schematic, but I have some problems in writing the code and understanding it, could you please help me with a check of the code ?
My questions are:
1. I don't know how to include the PID calculations and the lcd display code in the loop() function. Could you help me with some advice/code/comments ?
2. How to correctly write the zero() ISR function ?

Code: [Select]
#include <LiquidCrystal.h>
#include <SPI.h>
#include <Wire.h>
#include <max6675.h>

#define thermoDO 12
#define thermoCS 10
#define thermoCLK 13
#define potentiometer A0
#define zerocrossing 2
#define triac 7
#define relay A1

volatile float temperature, realTemperature;  // I have declared those 2 variables as VOLATILE, because of using them inside and outside ISR
int pottemperature;
int counter;
int duty = 0; // variable for duty cycle

byte thermometer[8] = //icon for termometer
{
  B00100,
  B01010,
  B01010,
  B01110,
  B01110,
  B11111,
  B11111,
  B01110
};

byte arrow[8] = //icon for arrow
{
  B11000,
  B01100,
  B00110,
  B00011,
  B00011,
  B00110,
  B01100,
  B11000
};

MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);

/*  The circuit:
   LCD RS pin to digital pin 12
   LCD Enable pin to digital pin 11
   LCD D4 pin to digital pin 5
   LCD D5 pin to digital pin 4
   LCD D6 pin to digital pin 3
   LCD D7 pin to digital pin 2
   LCD R/W pin to ground
   LCD VSS pin to ground
   LCD VCC pin to 5V
   10K resistor:
   ends to +5V and ground
   wiper to LCD VO pin (pin 3)
*/

#define PRINTRATE 100
#define DISPLAYRATE 250

char textbuf[100]; //buffer for data to send
unsigned long serialTime = millis(); //sending interval for data
unsigned long displayTime = serialTime;  //display interval for LCD
int pt; //local store for pot and iron temperatures;
int tmp;
double err, cErr, rErr, op;
int dty;

volatile int pidOut = 0;

double sensed_output, control_signal;
double setpoint;
double Kp; //proportional gain
double Ki; //integral gain
double Kd; //derivative gain
int T = 100; //sample time in milliseconds (ms)
unsigned long last_time;
double total_error, last_error;
int max_control = 0;
int min_control = 200;

LiquidCrystal lcd(3, 4, 5, 6, 8, 9);

void setup() {
  lcd.createChar(0, thermometer);
  lcd.createChar(1, arrow);
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print("STATIE DE LIPIT");
  delay(1200);
  lcd.clear();
  pinMode(relay, OUTPUT);
  pinMode(potentiometer, INPUT);
  pinMode(zerocrossing, INPUT_PULLUP);
  pinMode(triac, OUTPUT);
  digitalWrite(triac, LOW);
  digitalWrite(relay, HIGH);
  realTemperature = thermocouple.readCelsius();
  temperature = 0.779828 * realTemperature - 10.3427;
  //updateDisplay();
  attachInterrupt(digitalPinToInterrupt(2), zero, RISING);
}

void loop() {  //moved the PID computations outside the ISR
  unsigned long current_time = millis(); //returns the number of milliseconds passed since the Arduino started running the program
  int delta_time = current_time - last_time; //delta time interval
  pottemperature = analogRead(potentiometer);
  pottemperature = map(pottemperature, 0, 1023, 150, 400);
  setpoint = pottemperature;
  realTemperature = thermocouple.readCelsius();
  temperature = int(0.779828 * realTemperature - 10.3427); // make temperature an integer
  sensed_output = temperature;
  if (isnan(realTemperature) || sensed_output >= 432) {
    while (true) {
      digitalWrite(relay, LOW);
      displayErrors();
    }
  } else {
    updateDisplay();
  }
  if (delta_time >= T) {
    double error = setpoint - sensed_output;
    if (error <= 10) {
      Kp = 4, Ki = 0.2, Kd = 1;
    }
    if (error > 10) {
      Kp = 1, Ki = 0.05, Kd = 0.25;
    }
    total_error += error; //accumalates the error - integral term
    if (total_error >= max_control) total_error = max_control;
    else if (total_error <= min_control) total_error = min_control;

    double delta_error = error - last_error; //difference of error for derivative term

    control_signal = Kp * error + (Ki * T) * total_error + (Kd / T) * delta_error; //PID control compute
    if (control_signal >= max_control) control_signal = max_control;
    else if (control_signal <= min_control) control_signal = min_control;

    last_error = error;

  }
  noInterrupts();
  pidOut = int(control_signal);
  interrupts();
  last_time = current_time;
  delay(250);
}

void zero() {
  counter++;
  //*** change this line below
  if (counter > duty) { //reach duty cycle limit, unless duty was 25 in which case leave on until next duty calculated later
    digitalWrite(triac, LOW);
  }
  if (counter >= 25) {
    counter = 0;
  }
  else { //reading valid
    duty = map(pidOut, max_control, min_control, 0, 25);
    duty = constrain(duty, 0, 25); // ***keep duty between 0 and 25 (25 = 100%)
    if (duty > 0) {
      digitalWrite(triac, HIGH);
    } else {
      digitalWrite(triac, LOW);
    }
  }//if (tempError
} //if(counter >= 25

void updateDisplay() {
  pottemperature = analogRead(potentiometer);
  pottemperature = map(pottemperature, 0, 1023, 150, 400);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.write((byte)0);
  lcd.setCursor(2, 0);
  lcd.print((int)pottemperature);
  lcd.setCursor(6, 0);
  lcd.print((char)223); //degree sign
  lcd.setCursor(7, 0);
  lcd.print("C");
  lcd.setCursor(0, 1);
  lcd.write((byte)1);
  if (temperature <= 45) {
    lcd.setCursor(2, 1);
    lcd.print("Lo");
  } else {
    lcd.setCursor(2, 1);
    lcd.print((int)temperature);
  }
  lcd.setCursor(6, 1);
  lcd.print("[");
  lcd.setCursor(7, 1);
  lcd.print((int)realTemperature);
  lcd.setCursor(10, 1);
  lcd.print("]");
  lcd.setCursor(12, 1);
  lcd.print((char)223);
  lcd.setCursor(13, 1);
  lcd.print("C");
}

void displayErrors() {
  digitalWrite(relay, LOW); // the relay will disconnect the power to the soldering iron heating element
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.write((byte)0);
  lcd.setCursor(1, 0);
  lcd.write((byte)0);
  lcd.setCursor(5, 0);
  lcd.print("ERROR!");
  lcd.setCursor(14, 0);
  lcd.write((byte)0);
  lcd.setCursor(15, 0);
  lcd.write((byte)0);
  delay(500);
}

« Last Edit: July 31, 2020, 07:50:27 am by mike_mike »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf