Furnace temperature controller - Arduino with Solid State Relay

Ứng dụng Arduino trong điều khiển thiết bị điện, điện tử.
Xêm thêm tại: http://kythuatvatlieu.org/magazine/arduino.html
Post Reply
User avatar
support
Posts: 243
Joined: 05 Jan 2017, 09:34

Furnace temperature controller - Arduino with Solid State Relay

Post by support » 22 Sep 2017, 16:20

Hardware
  • Arduino Nano
  • Solid State Relay 40A / 3-32VDC 24-380VAC DC SSR
  • Stainless Steel High Temperature -100 To 1250 Degree Thermocouple K type 100mm Probe
  • 1602 LCD screen (blue screen) with backlighting
  • Adafruit Thermocouple Amplifier MAX31855 Breakout Board
  • 2AMP 5v DC, Box, 2 buttons to start-stop programm, wires

Code: Select all

#include <LiquidCrystal.h>
#include <SPI.h>
#include <EEPROM.h>
#include <Average.h>
#include "Adafruit_MAX31855.h"
#include <math.h>
#include <PID_v1.h>

class TempInterval
{
public:
  TempInterval();
  TempInterval(int _from, int _to, int _tempFrom, int _tempTo)
  {
    from = _from;
    to = _to;
    tempFrom = _tempFrom;
    tempTo = _tempTo;
  }

  int from;
  int to;
  int tempFrom;
  int tempTo;
};


/* parameters to change */
const int temperatureSamplesCount = 5;

/*   
 *    PID
http://playground.arduino.cc/Code/PIDLibraryRelayOutputExample
 *    
 *    TempInterval(from  minute, to  minute, from temp, to temp)
 *    from minute 0 to 7 raise  temperature from 200 to 300
 *     TempInterval(0, 7, 200, 300),
 *    
 *    from minute 7 to 17 hold  temperature 300
 *     TempInterval(7, 17, 300, 300),
 *    
*/

const int atTempDiffStartAdjustTimeOnOff = 20;
//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint,2,1,1, DIRECT);

const int WindowSize = 30000; //sec



const int mininumMeaningfullTemperatureC = 5;
const int minAlowedTemperatureChange = 10; //per cycle (loop)
const bool exitOnError = true;

const int TempIntervalCnt = 6;

TempInterval tempIntervals[TempIntervalCnt] =
{
        TempInterval(0, 7, 50, 150),
        TempInterval(7, 15, 150, 200),
        TempInterval(15, 30, 200, 200),
        TempInterval(30, 45, 200, 300),
        TempInterval(45, 60, 300, 150),
        TempInterval(60, 90, 150, 150)
};

Average<double> temperatures(temperatureSamplesCount);

enum States
{
  none,
  finished,
  started,
  notfinished,
  askedToStop

};

States state;

unsigned long windowStartTime;
int currentTemp = 0;
int targetTemp;
int currentMinute;
long startTime;
int lastExecutionMinute;

volatile long lastOnTime;
volatile long lastOffTime;

bool IsOn = false;
bool switchOn = false;


LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
const int thermoCLK = 3, thermoCS = 4, thermoDO = 5;
Adafruit_MAX31855 thermocouple(thermoCLK, thermoCS, thermoDO);

void setup()
{

  windowStartTime = millis();

  //tell the PID to range between 0 and the full window size
  myPID.SetOutputLimits(0, WindowSize);

  //turn the PID on
  myPID.SetMode(AUTOMATIC);
  
  startTime = millis();
  Serial.begin(9600);

  lcd.begin(16, 2);
  lcd.print("Hello");

  delay(2500);

  pinMode(2, OUTPUT);

  lastExecutionMinute = EEPROMReadInt(0);
  if (lastExecutionMinute == -1) //finished
  {
    lastExecutionMinute = 0;
    state = none;
  }
  else
    state = notfinished;
}

void loop()
{

  delay(700);

  for (int i = 0; i < temperatureSamplesCount; i++)
  {

    float currentTemperature = thermocouple.readCelsius();

    if (isnan(currentTemperature))
      Serial.println("Wrong T reading ");
    else
      temperatures.push(currentTemperature);

    if (currentTemperature < mininumMeaningfullTemperatureC)
    {
      Serial.print("Wrong T reading - low");
      Serial.println(currentTemperature);
    }
    else
      temperatures.push(currentTemperature);
  }

  int avgTemp = round(temperatures.mode());

  Serial.print("T avg : ");
  Serial.println(avgTemp);

  if (!isTempChangeOk(avgTemp))
  {
   //exit and switch off
   switchOn = false; setHeater();
   return;
  }
    
  currentTemp = avgTemp;

  currentMinute = ((millis() - startTime) / 60000) + lastExecutionMinute;
  targetTemp = getCurrentInterval(currentMinute);

  int input = readInput();

  if (targetTemp == -1) //finished
  {
    setFinished();
  }

  switch (state)
  {
  case notfinished:

    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("P.not done");
    lcd.setCursor(11, 0);
    lcd.print(lastExecutionMinute);

    lcd.setCursor(0, 1);
    lcd.print("N");
    lcd.setCursor(5, 1);
    lcd.print("Y");
    lcd.setCursor(8, 1);
    lcd.print("Contin?");

    if (input == 1)
      state = started;
    if (input == 2)
      state = finished;

    break;

  case none:
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Start P1?  ");
    lcd.setCursor(0, 1);
    lcd.print("N");
    lcd.setCursor(5, 1);
    lcd.print("Y");

    if (input == 1)
      state = started;
    if (input == 2)
      state = none;

    break;

  case askedToStop:

    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Stop? ");
    lcd.setCursor(0, 1);
    lcd.print("N");
    lcd.setCursor(5, 1);
    lcd.print("Y");

    if (input == 1)
      state = finished;
    if (input == 2)
      state = started;

    break;

  case started:

    EEPROMWriteInt(0, currentMinute);

    setHeater();

    lcd.clear();
    lcd.setCursor(0, 0);

    lcd.print("Min");
    lcd.print(" Cur.T");
    lcd.print(" Tar.T");

    lcd.setCursor(0, 1);

    lcd.print(currentMinute);

    lcd.setCursor(5, 1);
    lcd.print(currentTemp);
    lcd.setCursor(10, 1);

    lcd.print(targetTemp);

    lcd.setCursor(14, 1);

    if (IsOn)

      lcd.print("On");
    else
      lcd.print("  ");

    if (input > 0)
      state = askedToStop;

    break;

  case finished:

    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Finished");
    digitalWrite(2, LOW);

    EEPROMWriteInt(0, -1);
    lastExecutionMinute = -1;

    if (input > 0)
    {
      state = none;
      setup();
    }
    break;
  }
}

long pressed;

bool isTempChangeOk(int newTemp)
{
  
  if (currentTemp != 0) {
      if (abs(newTemp) - abs(currentTemp) > minAlowedTemperatureChange)
      {
  
        Serial.print("T changed too quickly from ");
        Serial.println(currentTemp);
        Serial.print("to ");
        Serial.println(newTemp);
  
        lcd.clear();
        lcd.print("T too quickly");
  
        if (exitOnError)
        {
          setFinished();
          
          lcd.clear();
          lcd.print("T too quickly - Exit");
          Serial.println("exiting app");
        }
  
        return false;
      }  

     if (currentTemp < mininumMeaningfullTemperatureC)
     {
        lcd.clear();
        lcd.print("T is too low");
        return false;
     }
  }
  return true;

}

void setFinished()
{
    state = finished;
    EEPROMWriteInt(0, -1);
}


int readInput()
{

  int inputGreen = analogRead(3);
  int inputBlack = analogRead(1);

  if (inputGreen == 0 || inputBlack == 0)
    pressed++;
  else
    pressed = 0;

  if (pressed > 2) //long btn hold
  {

    pressed = 0;

    if (inputGreen == 0)
      return 1;

    if (inputBlack == 0)
      return 2;
  }

  return 0;
}

void setHeater()
{

  int tempDiff = (targetTemp - currentTemp);

  if (tempDiff > atTempDiffStartAdjustTimeOnOff)
  {

    if (currentTemp > targetTemp)
      switchOn = false;
    else
      switchOn = true;
  }
  else //use PID
  {

    Setpoint = targetTemp;
    Input = currentTemp;
    myPID.Compute();
  
    unsigned long now = millis();
    
    if(now - windowStartTime > WindowSize)
    { //time to shift the Relay Window
      windowStartTime += WindowSize;
    }
    if(Output > now - windowStartTime) 
      switchOn = true;
    else 
      switchOn = false;

    Serial.println("PID OUTPUT");
    Serial.print(Output);
  

  }

  Serial.println(tempDiff);
  Serial.print("Is on : ");
  Serial.println(switchOn);


  if (switchOn)
  {
    digitalWrite(2, HIGH);
    IsOn = true;
  }

  else
  {
    digitalWrite(2, LOW);
    IsOn = false;
  }
}

int getCurrentInterval(int minute)
{

  for (int i = 0; i < TempIntervalCnt; i++)
  {

    if (tempIntervals[i].from <= minute && minute <= tempIntervals[i].to)

      return map(minute, tempIntervals[i].from, tempIntervals[i].to, 
                                    tempIntervals[i].tempFrom, tempIntervals[i].tempTo);
  }
  return -1; //not found
}

void EEPROMWriteInt(int p_address, int p_value)
{
  byte lowByte = ((p_value >> 0) & 0xFF);
  byte highByte = ((p_value >> 8) & 0xFF);

  EEPROM.write(p_address, lowByte);
  EEPROM.write(p_address + 1, highByte);
}

//This function will read a 2 byte integer from the eeprom at the specified address and address + 1
unsigned int EEPROMReadInt(int p_address)
{
  byte lowByte = EEPROM.read(p_address);
  byte highByte = EEPROM.read(p_address + 1);

  return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
}
Link gốc: https://github.com/antonsn/furnace-temp ... controller

Tham khảo lệnh Arduino:
  • https://www.arduino.cc/en/Reference/Map

    Code: Select all

    /* Map an analog value to 8 bits (0 to 255) */
    void setup() {}
    
    void loop()
    {
      int val = analogRead(0);
      val = map(val, 0, 1023, 0, 255);
      analogWrite(9, val);
    }
  • https://www.arduino.cc/en/Reference/EEPROM
  • https://playground.arduino.cc/Code/PIDLibrary

    Code: Select all

    /********************************************************
     * PID RelayOutput Example
     * Same as basic example, except that this time, the output
     * is going to a digital pin which (we presume) is controlling
     * a relay.  The pid is designed to output an analog value,
     * but the relay can only be On/Off.
     *
     *   To connect them together we use "time proportioning
     * control"  Tt's essentially a really slow version of PWM.
     * First we decide on a window size (5000mS say.) We then 
     * set the pid to adjust its output between 0 and that window
     * size.  Lastly, we add some logic that translates the PID
     * output into "Relay On Time" with the remainder of the 
     * window being "Relay Off Time"
     ********************************************************/
    
    #include <PID_v1.h>
    #define RelayPin 6
    
    //Define Variables we'll be connecting to
    double Setpoint, Input, Output;
    
    //Specify the links and initial tuning parameters
    PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT);
    
    int WindowSize = 5000;
    unsigned long windowStartTime;
    void setup()
    {
      windowStartTime = millis();
    
      //initialize the variables we're linked to
      Setpoint = 100;
    
      //tell the PID to range between 0 and the full window size
      myPID.SetOutputLimits(0, WindowSize);
    
      //turn the PID on
      myPID.SetMode(AUTOMATIC);
    }
    
    void loop()
    {
      Input = analogRead(0);
      myPID.Compute();
    
      /************************************************
       * turn the output pin on/off based on pid output
       ************************************************/
      unsigned long now = millis();
      if(now - windowStartTime>WindowSize)
      { //time to shift the Relay Window
        windowStartTime += WindowSize;
      }
      if(Output > now - windowStartTime) digitalWrite(RelayPin,HIGH);
      else digitalWrite(RelayPin,LOW);
    
    }
  • Goup thảo luận về PID:
    https://groups.google.com/forum/#!forum/diy-pid-control


Post Reply