Temperature control with Arduino NANO

Hello guys, This is my first project with Arduino and I would need some advise to solve an issue with my code.

As you can see I’m using a MAX6675 to measure temperature of an iron, and a couple of OLED displays to show Process_Value (temp read from MAX6675) and Set_Value, the control is made using an abbreviated PID loop.
The output is a 1Hz PWM signal on Pin 12.
The Set_Value is increased or decreed using interruptions (Up_button in pin 2 and down_button in pin 3)

Everything works as expected as long as the “set_value_F” variable don’t go above 127(binary 1111111), but when this value reach 128 the code stop working.

Probably this is a silly mistake, but I can’t see it…

2_display_u8g2_temp_PID.ino (6.04 KB)

Please read the first topics like "How to use Forum", "How to attache code".
My guess is that You use a byte integer having a max of 127. Use a "normal 16 bit integer", reaching a max of 32767.

I’m using a long integer defined as: “volatile long set_value_F”

#include <Arduino.h>
#include <U8g2lib.h>
#include <max6675.h>
#include <Wire.h>


U8G2_SSD1306_128X64_NONAME_1_HW_I2C display1(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ 16, /* data=*/ 17);   // ESP32 Thing, HW I2C with pin remapping
U8G2_SSD1306_128X64_NONAME_1_HW_I2C display2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ 16, /* data=*/ 17);   // ESP32 Thing, HW I2C with pin remapping

int thermoDO = 4;
int thermoCS = 5;
int thermoCLK = 6;
int unitselection = 0;
int process_value = 0;

int PID_error = 0;
int previous_error = 0;
int PID_value = 0;
int PWM_HIGH = 0;
int PWM_LOW = 0;
 

//PID constants and values
int kp = 9.1;   int ki = 0.3;   int kd = 1.8;
int PID_p = 0;    int PID_i = 0;    int PID_d = 0;


volatile long set_value_F=32;// counter for the number of button presses
int set_value_C=0;
int set_value = 32;

//**** detección boton incremental de set value - definición de variables****

const byte interruptPinUp = 2;
const byte interruptPinDown = 3;

static unsigned long last_interrupt_time = 0;
unsigned long interrupt_time = millis();

void PWM() {
  digitalWrite(12, HIGH);
  delay(PWM_HIGH);
  digitalWrite(12, LOW);
  delay(PWM_LOW - PWM_HIGH);
}



MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);


void setup() {

  Serial.begin(9600);
  Serial.println("MAX6675 test");
  // wait for MAX chip to stabilize
  delay(500);
  display1.setI2CAddress(0x3C*2);       
  display2.setI2CAddress(0x3D*2); 

  display1.begin(); 
  display2.begin(); 
  pinMode(interruptPinUp, INPUT);
  attachInterrupt(digitalPinToInterrupt(interruptPinUp), shortKeyPressUp, FALLING);
  pinMode(interruptPinDown, INPUT);
  attachInterrupt(digitalPinToInterrupt(interruptPinDown), shortKeyPressDown, FALLING);
  pinMode(12,OUTPUT);
  
}

void loop() {

  
  display1.firstPage();
  unitselection = digitalRead(7);


//***********************************PID START************************************************

// First we read the real value of temperature
 
  
     process_value = thermocouple.readFahrenheit();
      
  //Next we calculate the error between the setpoint and the real value

    
  
  PID_error = set_value_F - process_value;
  //Calculate the P value
  PID_p = kp * PID_error;
  //Calculate the I value in a range on +-10
  if(-10 < PID_error <10)
  {
    PID_i = PID_i + (ki * PID_error);
  }

  //For derivative we need real time to calculate speed change rate, but as this PWM is 1Hz freq, there is no need to measure the elapsed time between changes.
 
  //Now we can calculate the D calue
  PID_d = kd*((PID_error - previous_error)); 
  //Final total PID value is the sum of P + I + D
  PID_value = PID_p + PID_i + PID_d;

  //We define PWM range between 0 and 200ms
  if(PID_value < 0)
  {    PID_value = 0;   }
  if(PID_value > 1000)  
  {    PID_value = 1000;  }
  
  PWM_HIGH = PID_value; 
  PWM_LOW = 1000-PWM_HIGH;
   
  //Now we can write the PWM signal to pin D12
  PWM();
  previous_error = PID_error;     //Remember to store the previous error for next loop.

Serial.print("PID_p: "); Serial.println(PID_p);Serial.print("PID_i: ");Serial.println(PID_i);Serial.print("PID_d: ");Serial.println(PID_d); Serial.print("PID_value: ");Serial.println(PID_value); //Serial.print(set_value_F); Serial.println(process_value); Serial.println(PWM_HIGH); Serial.println(PWM_LOW); 
  
  //****************************PID END*********************************************************
  
    do{ 
    display1.setFont(u8g2_font_helvR10_tf);
    display1.setCursor(4,14);
    display1.print("PROCESS VALUE");
    display1.drawRFrame(0,22,128,42,7);
    display1.setFont(u8g2_font_helvR18_tf);
    if(unitselection == 1){
      if(thermocouple.readCelsius()<100){  
         display1.setCursor(30,52);}
      else {
        display1.setCursor(23,52);}
      display1.print(thermocouple.readCelsius(),1); display1.print((char)176); display1.print("C");
    }
    else {
      if(thermocouple.readFahrenheit()<100){  
         display1.setCursor(30,52);}
      else {
        display1.setCursor(23,52);}
      display1.print(thermocouple.readFahrenheit(),1); display1.print((char)176); display1.print("F");
    }
    }
    while ( display1.nextPage());
 
display2.firstPage();   
  do{
 display2.setFont(u8g2_font_helvR10_tf);
    display2.setCursor(24,14);
    display2.print("SET VALUE");
    display2.drawRFrame(0,22,128,42,7);
    if(unitselection == 1){ 
          set_value_C=(set_value_F-32)*5/9;
          if(set_value_C<100){
             display2.setFont(u8g2_font_helvR18_tf);
             display2.setCursor(40,52); }
             else {
              display2.setFont(u8g2_font_helvR18_tf);
             display2.setCursor(35,52); }
             display2.print(set_value_C); display2.print((char)176); display2.print("C");
             }
      else{
              if(set_value_F<100){
             display2.setFont(u8g2_font_helvR18_tf);
             display2.setCursor(40,52); }
             else {
              display2.setFont(u8g2_font_helvR18_tf);
             display2.setCursor(35,52); }
             display2.print(set_value_F); display2.print((char)176); display2.print("F");
        }
          
        
         
    
}while(display2.nextPage());
    

  //delay(700);


  
}

void shortKeyPressUp() {
  
 interrupt_time = millis();
 if (interrupt_time - last_interrupt_time > 100) //lock out period
 {
   if(unitselection == 1){ set_value_F=((((set_value_F-32)*5/9)+10)*9/5)+32;}
   else{set_value_F=set_value_F+10;}
 }
 last_interrupt_time = interrupt_time;
 
}
void shortKeyPressDown() {
 
 interrupt_time = millis();
 if (interrupt_time - last_interrupt_time > 100) //lock out period
 {
   if(unitselection == 1){ set_value_F=((((set_value_F-32)*5/9)+10)*9/5)-32;}
   else{set_value_F=set_value_F-10;}
 }
 last_interrupt_time = interrupt_time;

    
}

Then check the chain of calculations, if there is any way that byte is used. Characters are bytes but not likely used in math. Is there any function involved that corrupts the value in the long int?

Everything works as expected…

int kp = 9.1;   int ki = 0.3;   int kd = 1.8;

Really?

That’s probably not the source of your problem. But it is possible your problem has something to do with the use of a volatile long. Read about it here: volatile - Arduino Reference

This is my first project with Arduino

As Dave has pointed out you are assigning INTEGER variables to floating point values (ie non-integer decimal values such as 1.3 )

Two alternatives:

1: the easy way - use float type variables (but then you WILL need to think about how you convert a value of say 0.7936 to an integer to send it out)

2: the clever way -

Your inputs and outputs are integers. Change your calculations so that you can use integer arithmetic throughout.