PID (with BEM280-fan) questions...

Hi all!
I have been working for some time on the development of a control system, to control the temperature inside a box.
I would do this with a blower fan by means of a control system of the PID type (PID_V1 of Brett Beauregard) in which sucking air from the outside, if the internal temperature of the box exceeds 35 °C, I would maintain the desired temperature (about 35 °C).
This would be a basic scheme of the system:


A detailed outline of the components:

The components are the following:

  • Arduino Uno R3 (1)
  • Logic level converter 4 bi-directional channels 5-3.3 V (1)
  • Temp sensor BME280 (i2C) (2)
  • Mosfet IRF540 channel N
  • Blower fan, Servo DC12V 1.25A (1)

Now, what is the problem?...
I can not get that the fan turn on when the threshold is exceeded. The compiler does not present any errors.

The code:

//*****************Serial******************

#include <SPI.h>                           //Serial Peripheral Interface


//*****************BME280***********************

#include "cactus_io_BME280_I2C.h"

//*****************PID***********************

#include "PID_v1.h"

//**************BME280 Definitions **********************

BME280_I2C bme_Int3(0x76);                        // i2C address: 0x77 and 0x76 (unmodificed BME)

BME280_I2C bme_Ext(0x77);

float temperatura_Int;                          

float temperatura_Ext;                         


//*********PID Definitions ********

#define PIN_OUTPUT 3                            

double Setpoint_T, Input_T, Output_T;           

double Kp_T=(-1), Ki_T=(-0.05), Kd_T=(-0.25);                   


double commandMin = 100;                              //Min y Max fan parameters
double commandMax = 250;

PID myPID_T(&Input_T, &Output_T, &Setpoint_T, Kp_T, Ki_T, Kd_T, DIRECT);


//*********millis********

long intervaloSPrint = 3000;                       
long previousMillis_SPrint = 0;                   


void setup(){
 
//********PIN 3 frecuency********

  TCCR2B = TCCR2B & B11111000 | B00000001;          //actual frecuency: 31372.55 Hz

//*********Serial*************
   
  Serial.begin(9600);                           

  delay(100);                                       

//**********Start BME280**********
 
  bme_Int3.begin();
    if (!bme_Int3.begin()) { 
      Serial.println("BME280 Box3 not found");         

    }
   
  bme_Ext.begin();
    if (!bme_Ext.begin()) { 
      Serial.println("BME280 Exterior not found");         

    }
   
//**********variables**********

  Input_T = temperatura_Int;
  Setpoint_T = 35;                                          
 
  myPID_T.SetMode(AUTOMATIC);                             

  myPID_T.SetOutputLimits(commandMin, commandMax);

}
 
void loop(){

  //**************BME*************
 
  bme_Int3.readSensor();
    temperatura_Int = bme_Int3.getTemperature_C();


  bme_Ext.readSensor();
    temperatura_Ext = bme_Ext.getTemperature_C();

   
   
//******************PID Code*****************************


  Input_T = temperatura_Int;


  if (temperatura_Ext >= 20){
    myPID_T.Compute();                          
    analogWrite(PIN_OUTPUT, Output_T);         
  }
  else analogWrite(PIN_OUTPUT, 0);

 
       
  if(millis() - previousMillis_SPrint >= intervaloSPrint){
    previousMillis_SPrint = millis();
   
    Serial.print(" TInt_Box3(BME): ");
    Serial.print(temperatura_Int);
    Serial.print(" C");


    Serial.print(" Text(BME): ");
    Serial.print(temperatura_Ext);
    Serial.println(" C");

    }
}

I hope to had been clear with this explanation...
If someone can contribute something, I will be very grateful!

Why should the interior of the box ever heat up above the temperature of the outside air? If anything, the slight partial vacuum inside would lower the interior temperature.

Since it is likely that there is something wrong with the wiring, please edit your post to remove the confusing, misleading, incomplete Fritzing diagram and instead, post a clear hand-drawn wiring diagram, with properly labeled components.

This is wrong:
double Kp_T=(-1), Ki_T=(-0.05), Kd_T=(-0.25); .
The K values must be non-negative. Please study the user guide to the PID library and use DIRECT or REVERSE for the controller direction parameter.

Mosfet IRF540 channel N

This is a very poor choice and probably won't work. Choose a logic level MOSFET instead, such as the IRL540.

We strongly advise beginners to get each part of the project working before throwing everything together.

  • First learn to turn a fan ON and OFF using a logic level MOSFET.
  • Then learn to read the sensors.
  • Implement a simple controller that turns the fan ON when the temperature is too high, and OFF when too low.
  • Finally, add PID and properly tune the K values.

jremington:
Why should the interior of the box ever heat up above the temperature of the outside air?

Absorbing solar radiation? Something in the box dissipates power?

jremington:
Why should the interior of the box ever heat up above the temperature of the outside air? If anything, the slight partial vacuum inside would lower the interior temperature.

Hi Jremington!
Thanks for you reply!
In the interior box, I have a tiny heat source and I need to controlate it (similar to a server).

Since it is likely that there is something wrong with the wiring, please edit your post to remove the confusing, misleading, incomplete Fritzing diagram and instead, post a clear hand-drawn wiring diagram, with properly labeled components.

Sorry, I thought the diagram was enough to understand the problem.

This is wrong:

double Kp_T=(-1), Ki_T=(-0.05), Kd_T=(-0.25);

The K values must be non-negative. Please study the user guide to the PID library and use DIRECT or REVERSE for the controller direction parameter.

Ok, I will to try it!

This is a very poor choice and probably won't work. Choose a logic level MOSFET instead, such as the IRL540.

Ok!

We strongly advise beginners to get each part of the project working before throwing everything together.

  • First learn to turn a fan ON and OFF using a logic level MOSFET.
  • Then learn to read the sensors.
  • Implement a simple controller that turns the fan ON when the temperature is too high, and OFF when too low.
  • Finally, add PID and properly tune the K values.

I will apply your recommendations. I am very grateful with your help!
Greetings
[/quote]

gfvalvo:
Absorbing solar radiation? Something in the box dissipates power?

Hi gfvalvo!
I forgot draw an heat source inside the box :confused: ... The heat dissipates mainly through the air.
Greetings

Sorry, I thought the diagram was enough to understand the problem.

Please avoid use of Fritzing. They are useful only for the very simplest of circuits and only for beginners.

Thanks jremington!
I applied all his advices and it works!
The final code is:

//*****************Serial******************

#include <SPI.h>                           //Serial Peripheral Interface


//*****************BME280***********************

#include "cactus_io_BME280_I2C.h"

//*****************PID***********************

#include "PID_v1.h"

//**************BME280 Definitions **********************

BME280_I2C bme_Int3(0x76);                        // i2C address: 0x77 and 0x76 (unmodificed BME)

BME280_I2C bme_Ext(0x77);

float temperatura_Int;                         

float temperatura_Ext;                         


//*********PID Definitions ********

#define PIN_OUTPUT 3                           

double Setpoint_T, Input_T, Output_T;           

double Kp_T=(10), Ki_T=(1), Kd_T=(1);                   



PID myPID_T(&Input_T, &Output_T, &Setpoint_T, Kp_T, Ki_T, Kd_T, REVERSE);


//*********millis********

long intervaloSPrint = 3000;                       
long previousMillis_SPrint = 0;                   


void setup(){
 
//********PIN 3 frecuency********

  TCCR2B = TCCR2B & B11111000 | B00000001;          //actual frecuency: 31372.55 Hz

//*********Serial*************
   
  Serial.begin(9600);                           

  delay(100);                                       

//**********Start BME280**********
 
  bme_Int3.begin();
    if (!bme_Int3.begin()) {
      Serial.println("BME280 Box3 not found");         

   }
   
  bme_Ext.begin();
    if (!bme_Ext.begin()) {
      Serial.println("BME280 Exterior not found");         

   }
   
//**********variables**********

  Input_T = temperatura_Int;
  Setpoint_T = 35;                                         
 
  myPID_T.SetMode(AUTOMATIC);                             


}
 
void loop(){

  //**************BME*************
 
  bme_Int3.readSensor();
    temperatura_Int = bme_Int3.getTemperature_C();


  bme_Ext.readSensor();
    temperatura_Ext = bme_Ext.getTemperature_C();

   
   
//******************PID Code*****************************


  Input_T = temperatura_Int;


  if (temperatura_Ext >= 20){
    myPID_T.Compute();                         
    analogWrite(PIN_OUTPUT, Output_T);         
  }
  else analogWrite(PIN_OUTPUT, 0);

 
       
  if(millis() - previousMillis_SPrint >= intervaloSPrint){
    previousMillis_SPrint = millis();
   
    Serial.print(" TInt_Box3(BME): ");
    Serial.print(temperatura_Int);
    Serial.print(" C");


    Serial.print(" Text(BME): ");
    Serial.print(temperatura_Ext);
    Serial.println(" C");

    }
}

:slight_smile:

Hi everybody!
I worked on this project this month and I added some new features.
I added a bluetooth module (HM-10) for send data to my smartphone and a sdcard shield for data sensor storage.
Sound good because I left the display for a new blt module!
But I have a problem... When I send the "V" character I don't receive any data from arduino! everything else works fine!
The character doesn't enter to arduino and I don't know why?? :confused:
If I use this code without sd card it's work fine! for another hand, if I use this code without blt module works fine too! But together doesn't works
This is the code:

//*****************Serial port******************

#include <SPI.h>                           //Serial Peripheral Interface
#include <SoftwareSerial.h>                

//***************** BME280***********************

#include "cactus_io_BME280_I2C.h"

//***************** RTC-SDCARD***********************

#include <SD.h>                            //Interface SD
#include "RTClib.h"                        //Interface RTC


File myFile;
const int chipSelect = 10;                 // SDCARD

DS1307 rtc;                                

//***************** PID***********************

#include "PID_v1.h"

//**************Instances************

//**************Def Bluetooth HM10**********************

SoftwareSerial HM10(10, 11);           // 10--Rx y 11--Tx
char appData;
char rxData;                         // Var for get data from HM10
String inData = "";

//**************Def BME280**********************

BME280_I2C bme_Int(0x76);                       // i2C address:  0x77 BME and 0x76 for original BME 
BME280_I2C bme_Ext(0x77);

float Ti;                           // Internal temp (C)
float Hi;                               // Internal Rel. hum

float Te;                           // Ext temp (C)
float He;                               // Ext Rel. hum

int K=273;

//*********Def PID********

#define PIN_OUTPUT 3                             // pwm

double Setpoint_T, Input_T, Output_T;            
double Setpoint_H, Input_H, Output_H;           

double Kp_T=10, Ki_T=1, Kd_T=1;                  
double Kp_H=2, Ki_H=5, Kd_H=1;                   

PID myPID_T(&Input_T, &Output_T, &Setpoint_T, Kp_T, Ki_T, Kd_T, REVERSE);
PID myPID_H(&Input_H, &Output_H, &Setpoint_H, Kp_H, Ki_H, Kd_H, REVERSE); 

//*********Def millis********

long previousMillis_SD = 0;                      
long intervaloSD = 60000;                       



void setup() {
  
//********Set frec PIN 3********

  TCCR2B = TCCR2B & B11111000 | B00000001;       //change frec on PIN3 to 31372.55 Hz 

//**********Init Serial port*************
    
  Serial.begin(9600);                           
  
  
//**********Init  BLT*************  

  HM10.begin(9600);                               // set HM10 serial at 9600 baud rate

//**********Init BME280**********
  
  bme_Int.begin();
    if (!bme_Int.begin()) {  
      Serial.println("TI?");          
    }
    
  bme_Ext.begin();
    if (!bme_Ext.begin()) {  
      Serial.println("TE?");       
    }
    
 //*************Init SDCARD**********************
   
  if (! rtc.begin()) {
    Serial.println("RTCX");                       //RTCX
  }
  
  if (! rtc.isrunning()) {
    Serial.println("RTC?");                           //RTC?
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));//  sets the RTC to the date & time this sketch was compiled
  }
  
  if (!SD.begin(chipSelect)) {                // Is sdcard present?
    Serial.println("SDX");                      //SDX
  }
  
    
//**********Init var**********

  Input_T = Ti;
  Setpoint_T = 30;                               //Set point Temp

  Input_H = Hi;
  Setpoint_H = 60;                             //Set point hum

  
  myPID_T.SetMode(AUTOMATIC);                  //Temp PID
  myPID_H.SetMode(AUTOMATIC);                  //Hum PID

}

void loop() {
  
//*******RTC update time**********

  DateTime now = rtc.now();

//**************read data BME*************
  
  bme_Int.readSensor();
    Ti = bme_Int.getTemperature_C()+K;
    Hi = bme_Int.getHumidity(); 

  bme_Ext.readSensor();
    Te = bme_Ext.getTemperature_C()+K;
    He = bme_Ext.getHumidity();


//****************HM10********************
  HM10.listen();  // listen the HM10 port
  if (HM10.available() > 0){
    appData = HM10.read();
    inData = String(appData);
    Serial.write(appData);
  }

  if (inData == "V" ){
    HM10.println("T" + String(Te-K) + " " + String(He)+ " T" + String(Ti-K) + " " + String(Hi));
  }

//******************PID code*****************************

  if (Te >= 283 && Te < 310 ){                      
    myPID_H.Compute();                             //PID Hum
    analogWrite(PIN_OUTPUT, Output_H);             
  }
  
  if (Te >= 310 ){                      // 37°C 
    myPID_T.Compute();                             //PID Temp
    analogWrite(PIN_OUTPUT, Output_T);
  } 
  else analogWrite(PIN_OUTPUT, 0);

//**************Send data to SDCARD**************
  if (millis() - previousMillis_SD >= intervaloSD){
    previousMillis_SD = millis();
    myFile = SD.open("Log.txt", FILE_WRITE);
    if (myFile) {
      myFile.print(now.year(), DEC);
      myFile.print('/');
      myFile.print(now.month(), DEC);
      myFile.print('/');
      myFile.print(now.day(), DEC);
      myFile.print(' ');
      myFile.print(now.hour(), DEC);
      myFile.print(':');
      myFile.print(now.minute(), DEC);
      myFile.print(':');
      myFile.print(now.second(), DEC);
      myFile.print(';');
      myFile.print(" Ti ");
      myFile.print(Ti-K);
      myFile.print(" H ");
      myFile.print(Hi);
      myFile.print(" Te ");
      myFile.print(Te-K);
      myFile.print(" H ");
      myFile.println(He);
      
      myFile.flush();                           
      myFile.close();                           
      Serial.print("D");
    }
    else {
      Serial.println("Log?");     
    }
 }
}

What did I do wrong?
Thanks in advance!

You are trying to use pin 10 for two different purposes (SD card SS and Software Serial RX) at the same time.

jremington you are right!! Now the program works fine!
I will make some more modifications...
Thanks so much jremington !!

Hi everybody!
Yesterday I changed and reorganized the code to make it more readable...
I need to know how long the pwm cooler is ON and then send this elapsed time to in a SD Card. The code than I write for this purpose is the line 184 to 197, and 252 to 255. But it doesn't work...doesn't write any time data in Sd Card or BLT.
I am not sure how to use the PIN_OUTPUT states... maybe this is the problem.

//*****************Serial port******************

#include <SPI.h>                           //Serial Peripheral Interface
#include <SoftwareSerial.h>               

//***************** BME280***********************

#include "cactus_io_BME280_I2C.h"

//***************** RTC-SDCARD***********************

#include <SD.h>                            //Interface SD
#include "RTClib.h"                        //Interface RTC


File myFile;
const int chipSelect = 10;                 // SDCARD

DS1307 rtc;                               

//***************** PID***********************

#include "PID_v1.h"

//**************Instances************

//**************Def Bluetooth HM10**********************

SoftwareSerial HM10(10, 11);           // 10--Rx y 11--Tx
char appData;
char rxData;                         // Var for get data from HM10
String inData = "";

//**************Def BME280**********************

BME280_I2C bme_Int(0x76);                       // i2C address:  0x77 BME and 0x76 for original BME
BME280_I2C bme_Ext(0x77);

float Ti;                           // Internal temp (C)
float Hi;                               // Internal Rel. hum

float Te;                           // Ext temp (C)
float He;                               // Ext Rel. hum

int K=273;

//*********Def PID********

#define PIN_OUTPUT 3                             // pwm

double Setpoint_T, Input_T, Output_T;           
double Setpoint_H, Input_H, Output_H;           

double Kp_T=10, Ki_T=1, Kd_T=1;                 
double Kp_H=2, Ki_H=5, Kd_H=1;                   

PID myPID_T(&Input_T, &Output_T, &Setpoint_T, Kp_T, Ki_T, Kd_T, REVERSE);
PID myPID_H(&Input_H, &Output_H, &Setpoint_H, Kp_H, Ki_H, Kd_H, REVERSE);

//*********Def millis********

long previousMillis_SD = 0;                     
long intervaloSD = 60000;                       

unsigned long event_start_time=0;               //
unsigned long event_actual_time=0;
unsigned long event_elapsed_time=0;
volatile boolean flag_state = false;               //flag for pwm state.



void setup() {
 
//********Set frec PIN 3********

  TCCR2B = TCCR2B & B11111000 | B00000001;       //change frec on PIN3 to 31372.55 Hz

//**********Init Serial port*************
   
  Serial.begin(9600);                           
 
 
//**********Init  BLT************* 

  HM10.begin(9600);                               // set HM10 serial at 9600 baud rate

//**********Init BME280**********
 
  bme_Int.begin();
    if (!bme_Int.begin()) { 
      Serial.println("TI?");         
    }
   
  bme_Ext.begin();
    if (!bme_Ext.begin()) { 
      Serial.println("TE?");       
    }
   
 //*************Init SDCARD**********************
   
  if (! rtc.begin()) {
    Serial.println("RTCX");                       //RTCX
  }
 
  if (! rtc.isrunning()) {
    Serial.println("RTC?");                           //RTC?
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));//  sets the RTC to the date & time this sketch was compiled
  }
 
  if (!SD.begin(chipSelect)) {                // Is sdcard present?
    Serial.println("SDX");                      //SDX
  }
 
   
//**********Init var**********

  Input_T = Ti;
  Setpoint_T = 30;                               //Set point Temp

  Input_H = Hi;
  Setpoint_H = 60;                             //Set point hum

 
  myPID_T.SetMode(AUTOMATIC);                  //Temp PID
  myPID_H.SetMode(AUTOMATIC);                  //Hum PID

}

void W_SdCard();                //Function for write in SD_Card
void Codes(String inData);    //Function for manager codes

void loop() {
 
//*******RTC update time**********

  DateTime now = rtc.now();

//**************read data BME*************
 
  bme_Int.readSensor();
    Ti = bme_Int.getTemperature_C()+K;
    Hi = bme_Int.getHumidity();

  bme_Ext.readSensor();
    Te = bme_Ext.getTemperature_C()+K;
    He = bme_Ext.getHumidity();


//****************HM10********************
  HM10.listen();  // listen the HM10 port
  if (HM10.available() > 0){
    appData = HM10.read();
    inData = String(appData);
    Codes(inData);
  }

//******************PID code*****************************

  if (Te >= 288 && Te < 310 ){                     
    if (Hi < Setpoint_H && Setpoint_H < He){
      myPID_H.SetControllerDirection(DIRECT);  
      myPID_H.Compute();
    }

    else {
      myPID_H.SetControllerDirection(REVERSE);  
      myPID_H.Compute();
    }

    analogWrite(PIN_OUTPUT, Output_H);             
  }

  if (Te >= 310 ){                      
    myPID_T.Compute();                             //PID Temp
    analogWrite(PIN_OUTPUT, Output_T);
  } 
  
//**************Send data to SDCARD**************
  //Two criteria apply. First: time criteria... Second: cooler state criteria
  //First:
  if (millis() - previousMillis_SD >= intervaloSD){
    previousMillis_SD = millis();
    W_SdCard();
  }

  //Second: I need to know how long the cooler is ON:
  
  if (( PIN_OUTPUT==HIGH) && (flag_state==false)){    
    event_start_time=millis();                        //start time count
    HM10.println(String(event_start_time));           
    flag_state==true;
  }

  if ((PIN_OUTPUT==LOW) && (flag_state==true)){      
    flag_state==false;    							//turn flag
    event_actual_time=millis();
    event_elapsed_time = event_actual_time - event_start_time;
    HM10.println(String(event_elapsed_time));       
    W_SdCard();									//send to sdcard
  }
}

void Codes(String inData){

  if (inData == "A" ){
    HM10.println(String(Te-K) +" "+ String(He)+" "+ String(Ti-K) +" "+ String(Hi));
  }

  if (inData == "C" ){   //Read ctes values PID Temp Hum
    HM10.println(String(Kp_T) +" "+ String(Ki_T) +" "+ String(Kd_T));
    HM10.println(String(Kp_H) +" "+ String(Ki_H) +" "+ String(Kd_H));
  }

  if (inData == "X"){   //cooler pwm OFF
    analogWrite(PIN_OUTPUT, LOW);     
    HM10.println("OFF");;
  }
  
  if (inData == "Y"){   //cooler pwm ON
    analogWrite(PIN_OUTPUT, HIGH);     
    HM10.println("ON");;
  }

}

void W_SdCard(){
  
  //*******RTC update**********

    DateTime now = rtc.now();
    
    myFile = SD.open("Log.txt", FILE_WRITE);
    if (myFile) {
      myFile.print(now.year(), DEC);
      myFile.print('/');
      myFile.print(now.month(), DEC);
      myFile.print('/');
      myFile.print(now.day(), DEC);
      myFile.print(' ');
      myFile.print(now.hour(), DEC);
      myFile.print(':');
      myFile.print(now.minute(), DEC);
      myFile.print(':');
      myFile.print(now.second(), DEC);
      myFile.print(';');
      myFile.print(" ");
      myFile.print(Te-K);
      myFile.print(" ");
      myFile.print(He);
      myFile.print(" ");
      myFile.print(Ti-K);
      myFile.print(" ");
      myFile.print(Hi);
      myFile.print(" ");
      if ((flag_state==true) && (event_elapsed_time > 0)){    //I use event_elapsed_time to. 
        myFile.print(event_elapsed_time/1000);            //Print sec.
        event_elapsed_time=0;                             
      }
      myFile.println("");      
      myFile.flush();                           
      myFile.close();                        
      HM10.println("SD");
    }
    else {
      Serial.println("4");     
    }
}

What is wrong? :confused:
Thanks in advance