Retguy
Published © GPL3+

Complete PID Electric Smoker Control with Bluetooth

Take the work (and guesswork) out of smoking meat to perfection.

IntermediateShowcase (no instructions)12,197
Complete PID Electric Smoker Control with Bluetooth

Things used in this project

Story

Read more

Code

Arduino based electric smoker controller

Arduino
// Note: The format offered here does not allow sufficient space on one line
// to accomidate lengthy comments. Some changes may be necessary in the form
// of "//" if the code is to be cut and pasted but may be ok if pasted into 
// the Arduino compiler

//				Start of Arduino Sketch:

#include <LiquidCrystal_I2C.h> // Library for I2C LCD
#include <OnewireKeypad.h>     // OneWireKeypad Library

char KEYS[] = {                // Define keys values of Keypad
  '1', '2', '3',
  '4', '5', '6',
  '7', '8', '9',
  '*', '0', '#'};

#include <SPI.h>                //The two max6675 thermocouples use SPI communications
#include <Thermocouple.h>       //This is the max6675 library that works (could not use "include max6675.h" library)
#define csTC1 9                 //chip select for MAX6675 #1 (SPI)--Smoker temperature--TC1--Orange wire 
#define csTC2 10                //chip select for MAX6675 #2 (SPI)--Meat temperature--TC3--Blue wire
#include <PID_v1.h>             //Library for implementing PID control of the set Smoker temperature
#include <Wire.h>               // Allows the use of the I2C bus
Thermocouple tc1 = Thermocouple(csTC1);  //Identify Thermocouple (from library) as Smoker pin 9
Thermocouple tc2 = Thermocouple(csTC2);  //Identify Thermocouple (from library) as Smoker pin 10
OnewireKeypad <Print, 12 > Keypad(Serial, KEYS, 4, 3, A0, 4700, 1000 );  // Defines keypad as a 4 row, 3 column device //with pin A0 input along with the voltage dividing resistance value.
                                                             
LiquidCrystal_I2C lcd(0x3F, 16, 2); // Provides the I2C Bus address and defines the LCD as a 16 char, 2 line device
double Input;            // declare Input to be a double variable because the PID control library uses double (not interger) variables
double Input2;
double Input3;
double InputAve;    
int RelayPin = 8;        // declare pin8 for relay control
int alert = 7;           // declare pin7 for alert control
const int tempMax = 320; // declare tempMax to be a constant 320 (degreeF)as the maximum temperature the smoker can reach
double tempSmoke = 0;    // declare tempSmoke to be a double variable of the internal temp. of the smoker (DegF) read from thermocouple1
int tempMeat = 0;        // declare tempMeat to be an interger value of the internal temp. of the meat being smoked (DegF) read from thermocouple2
int tempMeat1 = 0;
int tempMeat2 = 0;
int tempMeatAve = 0;     // tempMeatAve is the average of three tc2 readings to increase the accuracy of the actual meat temp reading
int keypress = 0;        // Set to 0 to eliminate any residual values from previous cycles
int keypress1 = 0;
int keypress2 = 0;
double Setpoint;         // declare 'Setpoint' as the value entered from the keypad as the desired maximum internal smoker temp.
int SetMeatTemp;         // declare SetMeatTemp as the value entered from the kaypad as the desired maximum internal meat temp.
char Stop=0;             // Sets the variable 'Stop' to zero to enable use of the 'while' function
char Str1[15] = "CYCLE COMPLETE";  // declare a character string variable to be used at the end of the smoking cycle
char Str2[15] = "ENJOY DINNER!!";  // declare a character string variable to be used at the end of the smoking cycle
double  Output;                    //Define Variables we'll be connecting to for PID control
int Input1=Input;        // declare 'Input1' as an integer so the places past the decimal are dropped for the Android (BT) display
int Setpoint1=Setpoint;  // declare 'Setpoint1' as an integer so the places past the decimal are dropped for the Android (BT) display
PID myPID(&InputAve, &Output, &Setpoint,150,1,1, DIRECT); //Specify the links and initial tuning parameters for PID control
int WindowSize = 3500;
unsigned long windowStartTime;


void setup ()
{
  
  Serial.begin(9600);
  windowStartTime = millis();

  myPID.SetOutputLimits(0, WindowSize);  //tell the PID to range between 0 and the full window size
  
  myPID.SetMode(AUTOMATIC);              //activate the PID
  pinMode(RelayPin, OUTPUT);             //declare pin8 as an output to the 120v relay to turn power on or off to the heating coil
  lcd.init();
  lcd.backlight();
  Wire.begin();
  Keypad.SetHoldTime(100);
  Keypad.Getkey();
  lcd.begin(16, 2);
  lcd.clear();           
  pinMode(alert, OUTPUT);                // declare pin7 as output for the alert
  lcd.print("SMOKE TEMP?");              //Displays 'SMOKE TEMP?' on LCD
                       
  delay(100);                            //Allows everything to get set
}
void loop()
{ // The following section of the code reads the desired temperature for the smoker and the meat and sets up a 3 digit integer using
  //  (int number = 100 * int1 + 10 * int2 + int3) for the variables 'Setpoint' and 'SetMeatTemp'.

  // 1st digit of the desired smoker temperature:

  while (Stop == 0) {   // Allows void loop to be executed in segments to control when events happen. Without 'while' the ketpress is a random entry.
    if ((Keypad.Key_State() == 3))  // not pressed = 0, pressed = 1, released = 2,  held = 3
    {
      lcd.setCursor(13, 0);

      keypress = Keypad.Getkey();
      while ((Keypad.Key_State())) {} // Stay here until Key is held down
      keypress = keypress - '0';
      lcd.print(keypress);
      Stop = 1;
    }
  }

  //2nd digit smoke:
  while (Stop == 1) {
    if ((Keypad.Key_State() == 3))  // not pressed = 0, pressed = 1, released = 2,  held = 3
    {
      lcd.setCursor(14, 0);
      keypress1 = Keypad.Getkey();
      while ((Keypad.Key_State())) {} // Stay here until Key is held down
      keypress1 = keypress1 - '0';
      lcd.print(keypress1);
      Stop = 2;
    }
  }
  //3rd digit smoke:
  while (Stop == 2) {
    if ((Keypad.Key_State() == 3))  // not pressed = 0, pressed = 1, released = 2,  held = 3
    {
      lcd.setCursor(15, 0);
      keypress2 = Keypad.Getkey();
      while ((Keypad.Key_State())) {} // Stay here until Key is held down
      keypress2 = keypress2 - '0';
      lcd.print(keypress2);
      Stop = 3;
    }
  }

  while (Stop == 3) {
    Setpoint = 100 * keypress + 10 * keypress1 + keypress2; // Combines the three entered digits into one (3 digit) variable called 'SetSmokeTemp'.

    lcd.setCursor(0, 1);
    delay(10);
    lcd.print("MEAT TEMP?");                            //Displays 'MEAT TEMP' on LCD
    Stop = 4;
  }
  // 1st digit of the desired meat temperature:
  while (Stop == 4) { // Allows void loop to be executed in segments to control when events happen

    if ((Keypad.Key_State() == 3))  // not pressed = 0, pressed = 1, released = 2,  held = 3
    {
      lcd.setCursor(13, 1);
      keypress = Keypad.Getkey();
      while ((Keypad.Key_State())) {} // Stay here until Key is held down
      keypress = keypress - '0';
      lcd.print(keypress);
      Stop = 5;
    }
  }
  //2nd digit meat:
  while (Stop == 5) {
    if ((Keypad.Key_State() == 3))  // not pressed = 0, pressed = 1, released = 2,  held = 3
    {
      lcd.setCursor(14, 1);
      keypress1 = Keypad.Getkey();
      while ((Keypad.Key_State())) {} // Stay here until Key is held down
      keypress1 = keypress1 - '0';
      lcd.print(keypress1);
      Stop = 6;
    }
  }

  //3rd digit meat:
  while (Stop == 6) {
    if ((Keypad.Key_State() == 3))  // not pressed = 0, pressed = 1, released = 2,  held = 3
    {
      lcd.setCursor(15, 1);
      keypress2 = Keypad.Getkey();
      while ((Keypad.Key_State())) {} // Stay here until Key is held down
      keypress2 = keypress2 - '0';
      SetMeatTemp = 100 * keypress + 10 * keypress1 + keypress2; //Combines the 3 entered digits into one (3 digit) variable called 'SetMeatTemp'.
      lcd.print(keypress2);
      Stop = 7;
    }
  }
  
  delay(50);                  // Provides a delay before the cycle begins
 
  Input = (tc1.readF()-19);    //'Input' is the reading of the actual internal smoker temperature adjusted(-19deg)to match the Redi reading(at225deg)
  delay(350);
  
  Input2=(tc1.readF()-19);
  delay(350);
  
  Input3=(tc1.readF()-19);
  delay(350);
  
  InputAve=(Input+Input2+Input3)/3;  //The average of 3 readings is a bit more accurate
  
  if (InputAve>335){InputAve=225;}  //Prevents a 'flier' from shutting down the program (max temp 320)
  
  delay(350);                 //the Max 6675 board is slow and should not be read more than 3 times per second
  tempMeat = (tc2.readF()); //The reading from Thermocouple2 becomes the variable 'tempMeat' adjustment not needed to match the Redi reading(at165deg)
 
  delay(350);                 //the Max 6675 board is slow and should not be read more than 3 times per second
  tempMeat1 = (tc2.readF());
  
  delay(350);
  tempMeat2 = (tc2.readF());
 
  delay(350);
  if(tempMeat>tempMeat1+5||tempMeat<tempMeat1-5){tempMeat=100;tempMeat1=100;Serial.print("flier1 ");} //prevents 'fliers' from adversely affecting    //the program
  if(tempMeat>tempMeat2+5||tempMeat<tempMeat2-5){tempMeat=100;tempMeat2=100;Serial.print("flier2");}  //prevents' fliers' from adversely affecting //the program
  tempMeatAve = (tempMeat+tempMeat1+tempMeat2)/3;                          //Accuracy improved by averaging readings
  
  if(tempMeatAve>SetMeatTemp+10){tempMeatAve=150;Serial.print("flier3");}  //prevents 'fliers' from adversely affecting the program
 

                  // The following 7 lines turns the output pin on/off based on PID output

  myPID.Compute();  
  
  unsigned long now = millis();
  
  if(now - windowStartTime>WindowSize)
  { 
    windowStartTime += WindowSize;  //time to shift the Relay Window
  }
  if(Output > (now - windowStartTime)){ 
   digitalWrite(RelayPin, HIGH);Serial.println("ON"); 
    }
  else {digitalWrite(RelayPin,LOW);Serial.println("OFF");}//}
                       
    lcd.clear();                     // The following 17 lines set up the LCD display to indicate the Set & Actual temperatures
    lcd.setCursor(0, 0);
    lcd.print("SKset");
    lcd.setCursor(5, 0);
    lcd.print(Setpoint);
    lcd.setCursor(9, 0);
    lcd.print("ACT");
    lcd.setCursor(13, 0);
    lcd.print(InputAve);
    lcd.setCursor(0, 1);
    lcd.print("MTset");
    lcd.setCursor(5, 1);
    lcd.print(SetMeatTemp);
    lcd.setCursor(9, 1);
    lcd.print("ACT");
    lcd.setCursor(13, 1);
    lcd.print(tempMeatAve);
    
    Serial.print("SmokerSetTemp=");  // All "Serial.print" and "Serial.println" commands are to send status of smoker to Android BT device
    Setpoint1=Setpoint;
    Serial.print(Setpoint1);
    Serial.print("   ");
    Serial.print("ActualTemp=");
    Input1=InputAve;
    Serial.print(Input1);             
    Serial.println();
    Serial.println();
    Serial.print("MeatSetTemp  =  ");
    Serial.print(SetMeatTemp);
    Serial.print("   ");
    Serial.print("ActualMeatTemp=");
    Serial.print(tempMeatAve);
    Serial.println();
    Serial.println();
    Serial.println(); 
    Serial.println();
    Serial.println();
    Serial.println();
    Serial.println();
    Serial.println();
    Serial.println();       
    delay(50);
    
  if (InputAve>=tempMax)                       // The following 8 lines monitor the maximum temperature of 320deg, shuts down the cycle, 
    {digitalWrite(RelayPin, LOW);           //                                                          sends an alert to the display, and buzzer
                                           
    lcd.clear();                            
    lcd.setCursor(0,0); 
    lcd.print("TEMPERATURE OUT"); 
    lcd.setCursor(0,1);
    lcd.print(" OF CONTROL!!!");
    digitalWrite(alert, HIGH);}   
  
  if (tempMeatAve >= SetMeatTemp)            // When tempMeat(from MAX6675(2)(thermocouple in meat))equals the meat set temp turn smoker coil OFF,
                                             // indicate CYCLE COMPLETE on display, sound the Alert, and stop the Smoker cycle
  {
    digitalWrite(RelayPin, LOW);             //"Stops"the smoking cycle (until the meat temp drops below the meat set temp)
    Serial.println("                      DINNER IS READY");  //Sends message to Android BT device
    Serial.println("                     COME AND GET IT!");  //Sends message to Android BT device
    Serial.println();
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(Str1);                         // print "CYCLE COMPLETE" on first line of LCD display
    lcd.setCursor(0, 1);
    lcd.print(Str2);                         // print "ENJOY DINNER!!" on second line of LCD display
    digitalWrite(alert, HIGH);               // Sets off the alert buzzer 
    delay(150);
    digitalWrite(alert, LOW);                // Provides a short 'beep-beep' instead of an annoying constant single'beeeeeeeep'
    delay(150);
    digitalWrite(alert, HIGH);
    delay(150);
    digitalWrite(alert, LOW);
    delay(3000);}
    } 
            

Credits

Retguy

Retguy

0 projects • 6 followers

Comments