Regulace teploty vyhřívané fólie

12.05.2020 Arduino #arduino #regulace #pid #sd #rtc

Navazující projekt na regulaci teploty vyhřívané desky pro 3D tisk. Zde je použita vyhřívaná podložka, časový modul a modul pro zápis na SD kartu.


Jak je uvedeno tento projekt vychází z projektu pro regulaci vyhřívané desky pro 3D tisk, ale využívá tepelné fólie, která je z polyesteru. Teplota povrchu fólie odpovídá průměrné hodnotě při okolní teplotě 20 °C (relativní vlhkost vzduchu 45 %) a bezvětří! Dosažení "teploty vyhřívání" je velmi závislé na různých podmínkách, jako okolní teplota, přítomnost a typ podkladu, tepelná izolace, proudění vzduchu, vlhkost vzduchu a provozní napětí. K vyhřívání fólie se využívá na pětí 12 V. Vyrábí se v různých velikostech a tvarech a její využití je poměrně široké.

IMG_3366

Regulace teploty vyhřívané fólie ve spojení například s deskou Arduino Mega je poměrně jednoduchá.

Zapojení

V níže uvedeném zapojení je pro regulaci použit modul MOSFET Anet8. U tohoto modulu je zapotřebí si dát pozor, aby nedošlo v obvodu ke zkratu, protože okamžitě se Mosfet zničí a regulace nefunguje. Problém je v tom, že je modul neustále sepnutý a tím teplota stoupá a může dojít ke zničení fólie (v horším případě k požáru).

Ve schématu zapojení je pouze jeden termistor. Lze, ale použít termistorů více. Je zajímavé, že při měření teploty desky pomocí tří termistorů je teplota fólie homogenní ve všech místech. To například u vyhřívané desky pro 3D tisk není.

Do obvodu jsou přidány dva moduly pro záznam teploty: RTC - hodiny reálného času a SD Card pro záznam teploty. 

Princip celého obvodu je stejný jako u projektu regulace vyhřívané desky pro 3D tisk

navrh-regulace-heat-folie_bb  

RTC modul

DS3231 je modul RTC (Real Time Clock). Používá se k udržení data a času pro většinu projektů elektroniky. Tento modul má vlastní napájení z baterie, pomocí kterého udržuje datum a čas, i když je odpojen hlavní zdroj energie. Komunikace s celým modulem probíhá po sběrnici I2C a napájecí napětí je možné použít 3,3 nebo 5 Voltu.

Pro úspěšné propojení modulu DS3231 a Arduino desky stačí zapojit celkem 4 vodiče. Propojíme SCL s pinem A5, SDA s pinem A4, VCC s 5V Arduina a GND se zemí Arduina. Pro piny SDA a SCL musíme vybrat vždy vyhrazené I2C piny na vybrané desce:

SCL SDA
Arduino Uno A5 A4
Arduino Nano A5 A4
Arduino Mega 21 20
Leonardo/Micro 3 2

ds3231-rtc-module-chip  ds3231-rtc-module-pinout  

Díky knihovně  Arduino je používání tohoto modulu velmi snadné. Knihovna ds3231 ke stažení. Instalaci do Arduino lze provést pomocí návodu: Správa knihoven v Arduino.

DS3231  rtc(SDA, SCL);
void Initialize_RTC()
{
   // Initialize the rtc object
  rtc.begin();

//#### the following lines can be uncommented to set the date and time for the first time### 
/*
rtc.setDOW(FRIDAY);     // Set Day-of-Week to SUNDAY
rtc.setTime(18, 06, 00);     // Set the time to 12:00:00 (24hr format)
rtc.setDate(5, 6, 2020);   // Set the date to January 1st, 2014
*/
}

Při prvním použití tohoto modulu se musí nastavit datum a čas. To lze provést jednoduše odstraněním výše uvedených komentářů a zapsáním data a času. Po nastavení nezapomeňte přidat komentáře a nahrát, jinak se při každém spuštění desky nastaví datum a čas znovu. 

Modul pro SD kartu

Modul pracuje se standardními kartami MicroSD, jejichž provozní napětí je 3,3 V. Proto má modul regulátor napětí, takže můžeme použít 5V pin desky Arduino. Modul SD Card má šest pinů, dva pro napájení modulu, piny VCC a GND a čtyři další piny pro komunikaci SPI.

Zapojení modulu SD je pro každou desku jiné:

MOSI MISO SCK CS
Arduino Uno 11 12 13 10
Arduino Nano 11 12 13 10
Arduino Mega 51 50 52 53

micro-sd-tf-card-module-module-contains-level-shifter-and-regulatormicro-sd-tf-card-module-pinout-spi 

Základní kód pro práci s SD modulem se skládá ze dvou částí. Z inicializace a zápisu. V  níže uvedeném kódu je jméno souboru pro zápis dat uloženo v proměnné filename.

void Initialize_SDcard()
{

  delay(3000);
  Serial.println("Initializing SD card...");
  if (!SD.begin(chipSelect)) {
    Serial.println("Initialization failed!");
    return;
  }
  Serial.println("Initialization done.");
  /*
  delay(1000);
  if (SD.exists(fileName.c_str())) {
    Serial.println("Old file deleted ...");
    SD.remove(fileName.c_str());
  }
  */
  delay(1000);
  Serial.println("Create new file...");
  File dataFile = SD.open(fileName.c_str(), FILE_WRITE);
  dataFile.println("Date;Time;Temp;SetPoint");
  dataFile.close();
 
  delay(5000);
  Serial.println("========START========");

}

void Write_SDcard()
{

  File dataFile = SD.open(fileName.c_str(), FILE_WRITE);

  if (dataFile) {
    dataFile.print(rtc.getDateStr()); 
    dataFile.print(";"); 

    dataFile.print(rtc.getTimeStr()); 
    dataFile.print(";"); 

    dataFile.print(input); 
    dataFile.print(";"); 

    dataFile.print(setPoint); 
    dataFile.print(";"); 

    dataFile.println(); 
    dataFile.close(); //Close the file
  }else{
    Serial.println("OOPS!! SD card writing failed");
  }
}

Celé řešení

Zde je uvedeno celé řešení pro regulaci teploty vyhřívané fólie se záznamem teplot na SD kartu.

#include <PID_v1.h>
#include <Nokia5110.h>
//#include <EEPROM.h>
#include <EEPROMex.h>
#include <PID_AutoTune_v0.h>
#include <DS3231.h>
#include <SPI.h>
#include <SD.h>

// Pins for display
#define RST 8
#define CE 7
#define DC 6
#define DIN 5
#define CLK 4

// Pin for buttons
int buttonPinI = 10; 
int buttonPinD = 11;
double readingBtnI=0.1;
double readingBtnD=0.1;
int maxTempBed = 42;                  // Maximum temperature on heat bed

#define VCC 5                         //Supply voltage
#define SAMPLETIME 1000               // how long between pid/sampling
#define outputPin 9                   // Analog output pin for MOSFET
#define TEMPERATURENOMINAL 25         // nominal teperature from datasheet thermistor

int numSamples=3;                     // how many samples to take and average
  
      #define RT0 10000                    // Ω for MOUSER thermistor
      #define B 3950                       // K for MOUSER
      #define R 10450                      // R=10KΩ for MOUSER
      int numSensors=3;
      byte sensorPins[] = {A0,A1,A2};
  
  //
  /*
      #define RT0 100000                  // Ω for HEAT BED thermistor
      #define B 3950                      // K for HEAT BED
      #define R 100200                    // R=100KΩ for HEAD BED 
      int numSensors=3;  
      byte sensorPins[] = {A5, A4, A3};  // define pins for thermistors
  */
    

// SD card
const int chipSelect = 53;
String fileName="logger.csv";

// Define the aggressive and conservative Tuning Parameters
double consKp=75, consKi=5,consKd=4.3;

// Variables
float RT, VR, ln, TX, T0, VRT, average;
double input, Output, tempAvgIn, setPoint;
double setPointStart = 30.00;

// For tunning
byte ATuneModeRemember=2;
boolean tuning = false; // if want tunning PID
unsigned long  modelTime, serialTime;
double kpmodel=1.5, taup=100, theta[50];
double outputStart=5;
double aTuneStep=20, aTuneNoise=1, aTuneStartValue=40;
unsigned int aTuneLookBack=20;

//----------------------------------------------

PID myPID(&input, &Output, &setPoint, consKp, consKi, consKd, P_ON_M, DIRECT);
PID_ATune aTune(&input, &Output);

// initialization LCD display
LCDnokia5110 lcd(RST, CE, DC, DIN, CLK);

void setup() {
  
  if(tuning){
    tuning=false;
    changeAutoTune();
    tuning=true;
  }
  serialTime = 0;
  Serial.begin(9600);
  
  pinMode(buttonPinI, INPUT);
  pinMode(buttonPinD, INPUT);

  setPoint=EEPROM.readDouble(1);
  if(setPoint==0 || isnan(setPoint)){
    setPoint = setPointStart;
    myPID.Compute();
  }

  myPID.SetMode(AUTOMATIC);
  myPID.SetSampleTime(SAMPLETIME);
  
  T0 = 25 + 273.15;                   // Temperature T0 from datasheet, conversion from Celsius to kelvin

  lcd.LcdInitialise();                // start communication with display
  lcd.LcdClear();                     // erase display
  lcd.CharSpace = 1;                  // set space between chars - 0, 1, 2 points
  lcd.GotoXY(0,0);                    // set cursor on 1 char and 1 row
  lcd.LcdString("AVG Temp:");        // print message
  lcd.GotoXY(24,1);
  lcd.LcdString("---");
  /*
  lcd.GotoXY(0,2);
  lcd.LcdString("Tmp Avg:");
  lcd.GotoXY(24,3);
  lcd.LcdString("---");
  */
  lcd.GotoXY(0,2);
  lcd.LcdString("Tmp Set:");
  lcd.GotoXY(24,3);
  lcd.LcdString("---");

  pinMode(chipSelect, OUTPUT);
  Initialize_SDcard();
  Initialize_RTC();
  
}

double readTemp(byte tempPin, int numSamples) {
  uint8_t i;
  average = 0;
  // take N samples in a row, with a slight delay
  for (i = 0; i < numSamples; i++) {
    average += analogRead(tempPin);           //Acquisition analog value of VRT
    delay(50);
  }
  average /= numSamples;

  //-----------------------------
  
  VRT = (5.00 / 1023.00) * average;                 //Conversion to voltage
  VR = VCC - VRT;
  RT = VRT / (VR / R);                              //Resistance of RT
  ln = log(RT / RT0);
  TX = (1 / ((ln / B) + (1 / T0)));                 //Temperature from thermistor
  double inputT = TX - 273.15;                      //Conversion to Celsius
  
  //-----------------------------
/*
  average = 1023 / average - 1;
  average = R / average;
 
  float steinhart;
  steinhart = average / RT0;                          // (R/Ro)
  steinhart = log(steinhart);                         // ln(R/Ro)
  steinhart /= B;                                     // 1/B * ln(R/Ro)
  steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15);   // + (1/To)
  steinhart = 1.0 / steinhart;                        // Invert
  steinhart -= 273.15;                                // convert to C
  double inputT=steinhart;
*/
 Serial.print("Therm:"); Serial.print(tempPin);Serial.print("-");Serial.print(inputT);Serial.print(" / ");

  return inputT;
}

void loop() {

  readingBtnI = digitalRead(buttonPinI);
  readingBtnD = digitalRead(buttonPinD);

  if(readingBtnI == HIGH){
    setPoint=setPoint+0.1;
    EEPROM.writeDouble(1, setPoint);
  }
  if(readingBtnD == HIGH){
    setPoint=setPoint-0.1;
    EEPROM.writeDouble(1, setPoint);
  }


  uint8_t j;
  double inputS=0;
  for(j=0; j<numSensors; j++){ 
    inputS+=readTemp(sensorPins[j], numSamples);
  }
  input=inputS/numSensors;
  
/*
  double gap = abs(setPoint-input); //distance away from setpoint
  if(gap<0.4){  //we're close to setpoint, use conservative tuning parameters
     //myPID.SetTunings(consKp, consKi, consKd);
  }else{        //we're far from setpoint, use aggressive tuning parameters
     //myPID.SetTunings(aggKp, aggKi, aggKd);
  }
*/

   if(tuning){
    byte val = (aTune.Runtime());
    if (val!=0){
      tuning = false;
    }
    
    if(!tuning){ //we're done, set the tuning parameters
      consKp = aTune.GetKp();
      consKi = aTune.GetKi();
      consKd = aTune.GetKd();
      myPID.SetTunings(consKp,consKi,consKd);
      AutoTuneHelper(false);
    }
    
  }else{
    myPID.Compute();
  }

  if(input<maxTempBed){
    analogWrite(outputPin, Output); 
  }else{
    Output=0;
    analogWrite(outputPin, Output);
  }
  
  // Actual temperature on Heat Bed
  String tempC=String(input);
  char printTemp[10];                 // count of char in message
  tempC.toCharArray(printTemp, 10);   // convert variable to print format
  lcd.GotoXY(50,0);                    // first char and second row
  lcd.LcdString(printTemp);
  
  // Average temperature from in house
  /*
  tempAvgIn=0.00;
  String tempAvgC=String(tempAvgIn);
  char printAvgTemp[10];                 // count of char in message
  tempAvgC.toCharArray(printAvgTemp, 10);   // convert variable to print format
  lcd.GotoXY(50,2);                    // first char and second row
  lcd.LcdString(printAvgTemp);
  */
  // Setting temperature
  String tempSetC=String(setPoint);
  char printSetTemp[10];                 // count of char in message
  tempSetC.toCharArray(printSetTemp, 10);   // convert variable to print format
  lcd.GotoXY(50,2);                    // first char and second row
  lcd.LcdString(printSetTemp);
 
  /*
  Serial.print("Temperature:");
  Serial.print("t");
  Serial.print(input);
  Serial.print("t");
  Serial.print("setPoint:");
  Serial.print("t");
  Serial.print(setPoint);
  Serial.println("");
  Serial.print("t");
  Serial.print("Ctt");
  Serial.print(input + 273.15);          //Conversion to Kelvin
  Serial.print("Ktt");
  Serial.print((input * 1.8) + 32);      //Conversion to Fahrenheit
  Serial.print("F");
  Serial.print("tt A0 ");
  Serial.print(VRTa);
  Serial.print("tt");
  Serial.print(Output);
  Serial.println("tt");
  */

  //send-receive with processing if it's time
  if(millis()>serialTime){
    SerialReceive();
    SerialSend();
    serialTime+=500;
  }

  Write_SDcard();
  delay(SAMPLETIME);

}

void changeAutoTune(){
 if(!tuning){
    //Set the output to the desired starting frequency.
    Output=aTuneStartValue;
    aTune.SetNoiseBand(aTuneNoise);
    aTune.SetOutputStep(aTuneStep);
    aTune.SetLookbackSec((int)aTuneLookBack);
    AutoTuneHelper(true);
    tuning = true;
  }else{ //cancel autotune
    aTune.Cancel();
    tuning = false;
    AutoTuneHelper(false);
  }
}

void AutoTuneHelper(boolean start){
  if(start){
    ATuneModeRemember = myPID.GetMode();
  }else{
    myPID.SetMode(ATuneModeRemember);
  }
}

void SerialSend(){
  Serial.print("setpoint: ");Serial.print(setPoint); Serial.print(" ");
  Serial.print("input: ");Serial.print(input); Serial.print(" ");
  Serial.print("output: ");Serial.print(Output); Serial.print(" ");Serial.println();
  if(tuning){
    Serial.println("tuning mode");
  }else{
    /*
    Serial.print("kp: ");Serial.print(myPID.GetKp());Serial.print(" ");
    Serial.print("ki: ");Serial.print(myPID.GetKi());Serial.print(" ");
    Serial.print("kd: ");Serial.print(myPID.GetKd());Serial.println();
    */
  }
}

void SerialReceive(){
  if(Serial.available()){
   char b = Serial.read(); 
   Serial.flush(); 
   if((b=='1' && !tuning) || (b!='1' && tuning)){
    changeAutoTune();
   }
  }
}


// ************** HW ***************
// Init the DS3231 using the hardware interface
DS3231  rtc(SDA, SCL);
void Initialize_RTC()
{
   // Initialize the rtc object
  rtc.begin();

//#### the following lines can be uncommented to set the date and time for the first time### 
/*
rtc.setDOW(FRIDAY);     // Set Day-of-Week to SUNDAY
rtc.setTime(18, 06, 00);     // Set the time to 12:00:00 (24hr format)
rtc.setDate(5, 6, 2020);   // Set the date to January 1st, 2014
*/
}

void Initialize_SDcard()
{

  delay(3000);
  Serial.println("Initializing SD card...");
  if (!SD.begin(chipSelect)) {
    Serial.println("Initialization failed!");
    return;
  }
  Serial.println("Initialization done.");
  /*
  delay(1000);
  if (SD.exists(fileName.c_str())) {
    Serial.println("Old file deleted ...");
    SD.remove(fileName.c_str());
  }
  */
  delay(1000);
  Serial.println("Create new file...");
  File dataFile = SD.open(fileName.c_str(), FILE_WRITE);
  dataFile.println("Date;Time;Temp;SetPoint");
  dataFile.close();
 
  delay(5000);
  Serial.println("========START========");

}

void Write_SDcard()
{

  File dataFile = SD.open(fileName.c_str(), FILE_WRITE);

  if (dataFile) {
    dataFile.print(rtc.getDateStr()); 
    dataFile.print(";"); 

    dataFile.print(rtc.getTimeStr()); 
    dataFile.print(";"); 

    dataFile.print(input); 
    dataFile.print(";"); 

    dataFile.print(setPoint); 
    dataFile.print(";"); 

    dataFile.println(); 
    dataFile.close(); //Close the file
  }else{
    Serial.println("OOPS!! SD card writing failed");
  }
}

Ke stažení

Všechny potřebné soubory včetně knihoven jsou ke stažení: STÁHNOUT

Literatura:
https://www.arduino.cc/en/reference/SD
https://navody.arduino-shop.cz/navody-k-produktum/rtc-hodiny-realneho-casu-ds3231-at24c32-pametovy-modul.html

IMG_3419

IMG_3418