/*
=== This software is NOT open source. ===
=== Copyright 2017 ATM (Advanced Technical Marketing, Inc.) All Rights Reserved. ===
12/12/2013 by Denny Money
Arduino Mega 2560 BBQ Controller/Monitor
Modified to work with the Arduino Mega 2560 --- 1/22/14
and removed Arduino Yun dependant code.
1/25/14 made mods to allow control of P I D parameters live time without a need to recompile and download.
2/1/14 made mods to control min and max auger base timing live time.
2/18/14 made mod to change set temp with digital push buttons up/down 5 degrees
2/28/14 made mod to change cycle timing with push buttons from 10 seconds to 110 seconds
for smoke control.
3/26/14 added manual mode as to mimic traeger smoke mode
4/29/14 changed averaging based on 15 minutes instead of 30 . Thanks Hugh
5/3/14 added push buttons and code to change Done temp on fly.
5/4/14 creating structure for saving settings to EEprom.
// maybe make the first part the base for timing. This would help if there was a power falure during a long cook and when power was
restored it would continue on with the current settings.
5/6/14 need to add swith to turn off SSR board so we can watch cool off. Or maybe just write a routine to do this in software. Maybe
by placing a button on inside of controller box so it would not get accidentally switched. Future.
5/6 maybe add another button to go to a so called smoke mode where the PID settings are wider. Future Pro Version.
5/9 set up playingwithfusion.com max31885K Thermo couple type K breakout board.
Wiring is as follows.
pwf board - Arduino mega2560
gnd - gnd
3.3 V - Must be connected to 3.3 for Playingwithfusion. 5 volts for Whizoo.com quad 5 v version.
SCK - pin 3 on ICSP pins or pin 52
CS0 - pin 53 on arduino mega2560
CS1 - pin 49 on arduino mega2560
CS2 - pin 47 on arduino mega2560
CS3 - pin 45 on arduino mega2560
SDO/SO - Pin 50 "" "" " " or pin 1 of ICSP
Must change int8_t CS?_PIN = to above in code.
include SPI.h file
different Arduino boards:
Arduino Board MOSI MISO/SDO SCK SS (slave) SS (master)
Uno or Duemilanove 11 or ICSP-4 12 or ICSP-1 13 or ICSP-3 10 -
Mega1280 or Mega2560 51 or ICSP-4 50 or ICSP-1 52 or ICSP-3 53 -
Leonardo ICSP-4 ICSP-1 ICSP-3 - -
Due ICSP-4 ICSP-1 ICSP-3 - 4, 10, 52
Note that MISO, MOSI, and SCK are available in a consistent physical location on the ICSP header;
this is useful, for example, in designing a shield that works on every board.
5/28/14 started on cool down mode and cook mode
6/5/14 started on adapt mode using averaging
7/1/14 re-write max31855 interface. Show Failures on LCD
7/9/14 finished start up code. This allows you to start the board and then need to push a button to actually start the cooking process
7/16/14 create command set so we can send commands from the serial monitor to arduino so we can make changes to arduino variables on the fly.
8/30/14 make 20 second average pit temp average so we can determine if the temp is falling or raising based on last 20 seconds.
9/25/14 finished cool down shut down routine. 15 minutes for cool down and fan shuts off.
10/31/14 finished startup, cooldown and cook mode logic.
11/1/14 added Arduino software reset code.
11/5/14 added partial second code. now auger timing is exactly what PID asks for.
11/16/14 created routine for Smoking Brothers Traditional 30 and Traeger 075.
--- smoking brothers uses a real slow auger so timing is very different than Traeger 2 rpm auger. Select the correct grill.
12/3/14 Changed auger timing a little. Now when in automatic mode the auger timing up and down buttons still change the auger base timing
but when in manual mode those buttons change the amount of time the auger stays on. So you can change the auger timing rate then change to manual
mode and change the on time independently.
1/16/2015 Work on simplifying the code so others can read.
1/16/2015 Taking most of the code out of the loop and placing in seconds routine.
1/17/2015 Fixed Auger on and off bounce of 1 ten of a second.
2/15/2015 Create routines for 8 port mux type K board.
4/1/2015 Seperate the mcycletime from the clock.
5/19/2015 ran into a batch of type K thermocouple probes that are grounded to stainless sheathes
5/22/2015 tuning for smokin brothers sbt30
8/16/2015 tuning for sbp36
8/19/2015 added High temp limit shutdown/cooldown. Also added more software commands to control controller via serial.
11/3/2015 more routines on the fan activity.
11/21/2015 added code for the addition of the RTC clock hardware. Now Cook clock is based on the rtc clock.
1/12/2016 added code for the 4x3 keypad to replace the radio shack push buttons.
1/21/2016 rewrite keypad control code to sense holding key down.
1/24/2016 start the controller via the serial port via the hitgo command.
2/6/2016 turn off adapt mode while in the Tuned mode. (screen display)
Serial Done 2/14/16..Code Done 3/13/16.. Make temp_offset work for pit vs actual
Serial Done 2/14/16..Code Done 3/13/16.. make button active for fan only mode. This will allow user to use a smoking device to do cold smoking.
--- to do ........... make a button available for changing min and max auger rates on the fly. Also change the auger spread values.
2/19/2016 started on EEPROM programming.
3/13/16. save important values to EEPROM..
--- to do ................. make test routine more robust and show more on the lcd screen.
Code Done 3/13/16 ......... make button & serail command available to reset the cook clock.
3/15/2016 Auto detect RTC board routine/ Use arduino routine if no RTC detected.
3/22/2016 write batch files to upgrade mega boards via avrdude. No compiler needed on user end.
8/7/2016 need to fix food_done_mode . it is in several places
8/7/2016 need to fix rtc hours and minutes refresh.
8/9/2016 started on WiFi verison using esp8266 and blynk.
2/5/2017 modified pidos. which is full blown PID only routine.
6/5/2017 fixed hr minute cook clock on blynk when no rtc is installed
6/27/2017 fixed eeprom recall of PID/mnmx/ and offsets on start up.
9/11/2019 re-wright clock format for lcd
re-write pid header file and cpp file for pid on measurement and on error.
clean up some code.
9/14/19 tried different pid setting. the best tonight at 180 pit temp is
P=.15 I=.001 D = .03
Bottom setting at 1.8 and top at 4.0
Both ponE and ponM reacted the same.
*/
#include <EEWrap.h>
#include <EEPROM.h>
#include <Keypad.h>
#include <Wire.h>
#include <FileIO.h>
#include <LCD.h>
#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>
#include "PID_v1.h"
#include "SPI.h"
#include <PlayingWithFusion_MAX31855_1CH.h>
#include <PlayingWithFusion_MAX31855_STRUCT.h>
#include <MAX31855.h>
#include "Sodaq_DS3231.h"
// create structures for EEPROM on arduino 2560
struct Foo{float_e aa;float_e ab;float_e ac;float_e ad;float_e ae;float_e af;float_e ag;float_e ah;};
struct profile1{float_e aa;float_e ab;float_e ac;float_e ad;float_e ae;float_e af;float_e ag;float_e ah;};
struct profile2{float_e aa;float_e ab;float_e ac;float_e ad;float_e ae;float_e af;float_e ag;float_e ah;};
struct profile3{float_e aa;float_e ab;float_e ac;float_e ad;float_e ae;float_e af;float_e ag;float_e ah;};
struct profile4{float_e aa;float_e ab;float_e ac;float_e ad;float_e ae;float_e af;float_e ag;float_e ah;};
struct pidkid{float_e p;float_e i;float_e d;};
// mn is min_auger_base, mx is max_auger_base, sp is auger_spread, pt is the pit temperature.
struct minmax{float_e mn;float_e mx;float_e sp;float_e pt;};
struct rtcinst {bool_e a;};
struct serial {int32_e a;};
struct Version {float_e a;};
struct Offsets {int16_e ap; int16_e aa; int16_e af;}; // ap=pit offset aa = ambient offset af= food offset
struct Probes {bool_e b; bool_e c; bool_e d; }; // b is probe 2 - 2nd pit, c is probe3 - first food probe, d is 2 nd food probe
Foo foo EEMEM; //EEMEM tells the compiler that the object resides in the EEPROM
profile1 prof1 EEMEM;
profile2 prof2 EEMEM;
profile3 prof3 EEMEM;
profile4 prof4 EEMEM;
pidkid pid EEMEM;
minmax aminmax EEMEM;
Offsets offset EEMEM;
Probes probes EEMEM;
rtcinst rtcin EEMEM;
serial serial_num EEMEM;
Version version_num EEMEM;
const byte numRows= 4; //number of rows on the keypad
const byte numCols= 3; //number of columns on the keypad
//keymap defines the key pressed according to the row and columns just as appears on the keypad
char keys[numRows][numCols]=
{
{'#', '0', '*'},
{'9', '8', '7'},
{'6', '5', '4'},
{'3', '2', '1'}
};
//Code that shows the the keypad connections to the arduino terminals
byte rowPins[numRows] = {5,4,3,2}; //Rows 0 to 3
byte colPins[numCols] = {8,7,6}; //Columns 0 to 2
//initializes an instance of the Keypad class
Keypad myKeypad=Keypad(makeKeymap(keys), rowPins, colPins, numRows, numCols);
int rtc_hour = 0;
int rtc_minute = 0;
int rtc_second = 0;
int rtc_year = 0;
int o_rtc_day = 0;
int o_rtc_hour = 0; //save original clock settings
int o_rtc_minute = 0;
int o_rtc_second = 0;
int rtc_month = 0;
int rtc_day = 0;
int rtc_dow = 0;
int therm = 0;
int stage1 = 180; // first stage
int stage2 = 180; // second stage, set first stage and second stage the same temp to get 1 stage cook
int stage1_time_hours = 9; // hours to run stage 1
int stage1_time_minutes = 0; // minutes to run stage 1
int stage2_time_hours = 15; // hours to run stage 2
int stage2_time_minutes = 0; // minutes to run stage 2
int hold_temp = 175; // temp to hold after cook is thru
int igniter_temp = 160; // for igniter. turn off above this temp cause we have fire or re-ignite if temp falls below this.
int igniter_timer = 25; // 10 minute timer for igniter
int cycletime = 20; // cycle time is amount of time that the auger will be polled in seconds
int meat_menu = 0; // 0 = pork, 1 = brisket, 3 = Foul
char buffer[7]; // allowcate for calculating averages
unsigned long start_time;
unsigned long lasttime;
unsigned long alasttime;
unsigned int auger_mode = 0 ; // for automatic and manual modes 0=Auto 1=Manual
int mcycletime = cycletime;
/////// in this version cycletime can not be more than 120 seconds. /////////
unsigned int pidsetsampletime = 1000;
unsigned int auger_status = 0 ;
unsigned int m = 0;
double half_second = 0.00;
unsigned int seconds = 0.0;
unsigned int minutes = 0;
unsigned int hours = 0;
int minute_tick = 0;
int second_tick = 0;
String realseconds ;
String realminutes ;
String tseconds ;
String cook_start_time;
String what_profile;
double Realseconds = 0.0;
unsigned int Realminutes = 0;
double pit_temp = 0.00;
double pit_temp2 = 0.00;
double food_temp1 = 0.00;
double food_temp2 = 0.00;
double food_temp3 = 0.00;
double food_temp4 = 0.00;
double ambient_temp = 0.00;
double food_done_temp = 195; // Pork shoulder done between 195-205
double food_wrap_temp = 165.00; // future
unsigned int analogpit = 1;
unsigned int analogfood1 = 1;
unsigned int analogfood2 = 2;
//unsigned int ambienttemp = 8;
//Define the aggressive and conservative Tuning Parameters for PID
double Input, Output;
double aggKp = 4, aggKi = 0.2, aggKd = 1;
double consKp = .15, consKi = .001, consKd = .03; // 10/03/19
double tempKp = .00, tempKi = .000, tempKd = .00;
unsigned int current_millis_value = 0;
unsigned int previous_millis_value = 0;
//unsigned int maint_mode = 0;
double auger_run_time = 0.00;
double temp_error = 0.00;
double min_auger_base = .043; // was .11 for 15 sec cycle time....base minimum for calculating auger time
double max_auger_base = .144;
double min_ab = .060;
double max_ab = .250;
double mm_timer = 2.00; // manual mode auger on time
double max_auger_percent = .60; // % of total auger time.this is just to limit overshoot at 180 Setpoint
double avg_auger_time = 0 ; // save average auger time here..
double min_auger_percent = .18;
double min_best = .000;
double max_best = .000;
double min_best_last = .050;
double max_best_last = .211;
double auger_seconds = 0.00;
double auger_spread = .061;
double pid_bottom = 2.;
double pid_top = 19;
// unsigned int alarm1 = igniter_temp; // alarm1 not used now. / Hot Rod.
double Setpoint = stage1;
//const int pitUppin = 22; // use digital pin 22 for up push button raise pit temp
// 23
//const int pitDnpin = 24; // use digital pin 24 for down push button lower pit temp
// 25 26 27
//const int mm_timerUppin = 26;
//const int mm_timerDnpin = 27;
//const int cycleUppin = 28; // use digital pin 28 for changing auger/cycle timing up
// 29
//const int cycleDnpin = 30; // use digital pin 30 for changing auger/cycle timing down
#define auger_pin 31 // now digital 4/27/14
const int doorsensor = 32; // for detecting if lid has been opened. Future
#define igniter_pin 33 // now digital 4/27/14
// 34
#define fan_pin 35 // now digital 5/29/14
//const int modeDnpin = 36; // use digital pin 36 for changing auger mode from Auto to Manual
// 37
//const int minaugerbaseUpPin = 38; //minimum auger base up pin/ to raise min_auger_base
// 39
//const int minaugerbaseDnPin = 40; //minimum auger base down pin/ to lower min_auger_base
//const int cooldown_startDnpin = 41; // use digital pin 41 for cool down start mode
//const int maxaugerbaseUpPin = 42; //maximum auger base up pin/ to raise max_auger_base
// 43
//const int maxaugerbaseDnPin = 44; //maximum auger base down pin/ to lower max_auger_base
//const int foodUppin = 46; // use digital pin 46 for up push button raise food done temp
//const int foodDnpin = 48; // use digital pin 48 for down push button lower food done temp
// 50 51 52 // for max31855
// SDO = 50
// SCK = 52
int8_t CS3_PIN = 45;
int8_t CS2_PIN = 47;
int8_t CS1_PIN = 49;
int8_t CS0_PIN = 53;
PWFusion_MAX31855_TC thermocouple0(CS0_PIN);
PWFusion_MAX31855_TC thermocouple1(CS1_PIN);
PWFusion_MAX31855_TC thermocouple2(CS2_PIN);
PWFusion_MAX31855_TC thermocouple3(CS3_PIN);
// tempvals for storing temps to do averaging. 15 minutes worth
int tempvals[15] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int sec_tempvals[20] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// profiles are for predetermined food cooks. like pork. set pit temp, stage1 and stage2, hours on stage1 and hours on stage 2, done temp, hold temp.
// sample would be. stage1 temp, hours on S1, minutes on S1,stage2 temp, hours on S2, minutes on S2, Done temp, hold temp.
// 180,4,30,225,14,30,200,170
// start stage1 @ 180, cook for 4 hrs & 30 minutes, stage2 @ 225 for 14 hrs & 30 minutes, internal meat to 200 and hold @ 170.
int profile0[8] = {180, 4, 30, 180, 14, 30, 200, 170}; // single stage cook.
int profile1[8] = {180, 9, 30, 250, 14, 30, 195, 185}; //Pork
int profile2[8] = {180, 9, 30, 250, 14, 30, 198, 195}; //Brisket
int profile3[8] = {180, 3, 0, 325, 6, 0, 170, 170}; // Foul
int profile4[8] = {180, 0, 30, 250, 2, 30, 186, 170}; // Beans Pork and beans with real pork butt.
int temp_offset = -15;
int ambient_offset = 0;
int food_offset = 0;
/// for adaptive tuning
int pit_peak = 0;
int pit_valley = 500;
int one_minute = 0;
int one_second = 0;
int cooldown_minutes = 15;
int cooldown_count = 15;
int pitUpstate = 0;
int pitDnstate = 0;
int foodUpstate = 0;
int foodDnstate = 0;
int cycleUpstate = 0;
int cycleDnstate = 0;
int mm_timerUpstate = 0;
int mm_timerDnstate = 0;
int fall_cnt = 0;
int climb_cnt = 0;
int modechange = 0;
int cooldowncookchange = 0;
int minaugerbaseDnstate = 0; // future pro version.
int minaugerbaseUpstate = 0;
int maxaugerbaseDnstate = 0;
int maxaugerbaseUpstate = 0;
int nopellets_cnt = 30 ; // 1 minutes maximum without pellet feed.
double tmp;
int buff_full = 0; // buffer for averaging temperature. if 15 then all 15 minutes have been filled.
int sec_buff_full = 0;
int avg_temp = 0;
int sec_avg_temp = 0;
int valcnt = 0; // for loop counter on averaging temps buffer.
int sec_valcnt = 0;
int last_temp = 0; // last temp reading a minute ago
int last_temp_sec = 0; // last temp second ago
int max_safe_temp = 530; // pit will shut down if this temp is reached.
int autotune_count = 0; // count the number times we reach auto tune status.
int fan_speed = 150; // variable for changing fan speed
int auger_speed = 200;
boolean adapt_mode = false; // start off in dumb mode. Kick it on when temp gets within 15 degrees of set.
boolean food_done_mode = false; // logical for testing if food is done.
boolean hold_temp_mode = false;
boolean cook_mode = false; // used to tell if we went from cook mode to cool down mode or start cook.
boolean setup_mode = false; // used to go into setup mode on the lrtc.now()cd screen. Future.
boolean bump_auger_mode = false; // used to bump the auger on for 1 second
boolean shave_auger_mode = false; // used to shave the auger for 1 second
boolean climbing = false ; // Is pit climbing faster than the last 15 second average.
boolean falling = false ; // is pit falling faster than the last 15 second average.
boolean testmode = false ; // if testing show test data on screen
boolean twoprobes = false ; // make this true for 2 temp probes and false for 4 temp probes.
boolean cooldown_startmode = false ; // for start and cool down mode, 0=cool down 1=start cook mode
boolean skipacycle = false; // skip an auger cycle
boolean stage2_set = false;
boolean sbt30 = false; // smoking brothers traditional 30 or premier 36 smoker.
boolean fumee = true; // for traeger 075 Texas with 2 rpm auger motor.
boolean useprofiles = false; // use profiles to get temps
boolean autotune_complete = false ; // auto tune found best min and max setting
boolean use_8port_mux = false; // use the 8 port mux for Thermocouple input
boolean igniter_fail = false; // igniter failure
boolean graph_data = true; // must be true to use esp8266 wifi module
boolean pid_only = true; // Turn on to deactivate adapt mode and let pid controll with min max auger rate set.
boolean nopellets = false; // should be false unless we are over temp.
boolean rtc_installed = true; // if realtime clock installed use it.
boolean ms_setup = false; // how to determine if we are in Multi stage setup mode.
boolean fan_mode = false; // just run the fan so you can cold smoke cheese and things
boolean mod_mode = false; // for setting modification mode on profiles.
boolean smoke_mode1 = false; // different types of smoke modes == most smoke
boolean smoke_mode2 = false; //
boolean smoke_mode3 = false; // you can play with all of these subroutines.
boolean smoke_mode4 = false; // generally 1 thru 6 with 1 being most and 6 being least smoke
boolean smoke_mode5 = false; //
boolean smoke_mode6 = false; //
boolean smoke_mode7 = true; //
boolean start_cycle = true; // used to go thru start up cycle.
boolean last_PID_on = true; // used to recall the last PID setting
boolean temp_changed = false; // used to tell if the user changed pit temp.
boolean ponm = true; // false means P_ON_E - true means P_ON_M
boolean probe1 = true;
boolean probe2 = false;
boolean probe3 = false;
boolean probe4 = false;
bool pOnM = true, pOnE = false;
double pOnEKp, pOnMKp;
PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, P_ON_M, DIRECT);
char weekDay[][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
char mkeypress ;
// I have 2 seperate lcd screens. Change to the address that suits.
// LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address -- Fumee uses this one
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address
double hits = 0.0; // hits counter for the cycle timer. increased on seconds.
double thits = 0.00;
void setup()
{
//aminmax.mn = min_auger_base,3;aminmax.mx = max_auger_base,3;aminmax.sp = auger_spread;aminmax.pt=Setpoint;
// retrieve last settings from min max and auger spread from last save
// retry seting up last saved parameters.
last_PID();
delay(500);
last_offset();
delay(100);
// initially set up the profile in eeprom. Only do this once per board.
/*
prof1.aa = 180;prof1.ab = 9;prof1.ac = 30;prof1.ad = 250;prof1.ae = 14;prof1.af = 30;prof1.ag = 195;prof1.ah = 185;
prof2.aa = 180;prof2.ab = 9;prof2.ac = 30;prof2.ad = 250;prof2.ae = 14;prof2.af = 30;prof2.ag = 198;prof2.ah = 195;
prof3.aa = 180;prof3.ab = 3;prof3.ac = 0;prof3.ad = 325;prof3.ae = 6;prof3.af = 0;prof3.ag = 170;prof3.ah = 170;
prof4.aa = 180;prof4.ab = 0;prof4.ac = 30;prof4.ad = 250;prof4.ae = 2;prof4.af = 30;prof4.ag = 186;prof4.ah = 170;
*/
myKeypad.addEventListener(keypadEvent);
// setup for the the SPI library:
SPI.begin(); // begin SPI
SPI.setDataMode(SPI_MODE1); // MAX31855 is a Mode 1 device
// initialize the chip select pins for 4 port type K thermocouple board.
pinMode(CS0_PIN, OUTPUT);
pinMode(CS1_PIN, OUTPUT);
pinMode(CS2_PIN, OUTPUT);
pinMode(CS3_PIN, OUTPUT);
Serial.begin(19200);
Serial3.begin(9600);
Wire.begin();
if (rtc_installed) {rtc.begin();}
lcd.begin(20, 4);
lcd.backlight();
//before compile
serial_num.a = 202000000; // uncomment to write new serial # and Version #
version_num.a = .317,0;
DateTime dt(2017, 03, 01, 0, 0, 0, 5);
rtc.setDateTime(dt);
DateTime now = rtc.now();
rtc_year = (now.year());
if (rtc_year != 2017 ) {
rtc_installed = false;
lcd.clear();
lcdw(0,3,"year= "+String(rtc_year));
lcdw(2,0," No RTC Found");lcdw(0,1,"Using System Clock");
}
else {
lcd.clear();
lcdw(5,0,"RTC Found");lcdw(0,1," Using RTC Clock");
}
delay(1000);
lcdw(0,0,"ATM BBQ Controller");
lcdw(0,1," The Most Advanced ");
lcdw(0,2," In the World ");
lcdw(0,3,String(version_num.a,3));
lcdw(5,3," Pitts");
delay(2000); // wait 2 seconds
clear_lcd(); // clear the lcd screen
pinMode(auger_pin, OUTPUT);
pinMode(igniter_pin, OUTPUT);
pinMode(fan_pin, OUTPUT); //
digitalWrite(igniter_pin, LOW); // make sure we start off with the igniter off
digitalWrite(fan_pin, HIGH); // Turn fan on and leave on
calibrate_auger_percent(); // initialize PID minimum and maximum limits
myPID.SetMode(AUTOMATIC); // don't use MANUAL. I compensate in code.
/////SetMode(AUTOMATIC);
myPID.SetSampleTime(pidsetsampletime); // allow the PID routine to change Output every .20 second. 300 is even faster but not needed.
/////SetSampleTime(AUTOMATIC);
// it may help you to look at the PID_V1.h file
probe2 = probes.b; probe3 = probes.c; probe4 = probes.d;
consKp = pid.p; consKi = pid.i; consKd = pid.d;
}
void loop()
{
if (cook_mode) {
update_time();
// hits are increased at 1 hit per second. not as precise as an RTC but close enough for a pellet grill
set_PID();
keypadcontrol(); // get keypad response
readserial(); // check serial port for commands
}
// if cook mode is true
// Tell user to push the start GO button
if (!cook_mode) {
lcdw(0,0,"Press GO button for");
lcdw(0,1," Single Stage Cook ");
lcdw(0,2," Press MS Button ");
lcdw(0,3," for 3 Stage Setup ");
fan_off (); // make sure fan is off
igniter_off (); // make sure igniter is off
keypadcontrol();
readserial();
} //!cook_mode
}// loop
// Custom Routines //
void pre_PID() {
if (sbt30) { // smokin brothers standard 30 pit
if (Setpoint < 300) {
if (temp_error >= 2) {min_auger_base = .136;}
if (temp_error < 0) { max_auger_base = .139;}
if (temp_error < -5) {max_auger_base = .159;}
if (temp_error < -10) {max_auger_base = .225;}
if (temp_error < -20) {max_auger_base = .250;}
if (temp_error < -50) {max_auger_base = .299;}
}
if (Setpoint > 300) {
if (temp_error >= 0) {min_auger_base = .168;}
if (temp_error < 0 ) {max_auger_base = .239;}
if (temp_error < -5) {max_auger_base = .240;}
if (temp_error < -10){max_auger_base = .249;}
if (temp_error < -20){max_auger_base = .261;}
if (temp_error < -50){max_auger_base = .392;}
}
}
if (fumee && ! pid_only) { // Fumee 500 Gallon Texas pit
if (Setpoint < 300) {
if (temp_error >= 2) {min_auger_base = .079;}
if (temp_error < 0) {max_auger_base = .119;}
if (temp_error < -5) {max_auger_base = .125;}
if (temp_error < -10){max_auger_base = .135;}
if (temp_error < -20){max_auger_base = .159;}
if (temp_error < -50){max_auger_base = .219;}
}
if (Setpoint > 300) {
if (temp_error >= 0) {min_auger_base = .109;}
if (temp_error < 0 ) {max_auger_base = .149;}
if (temp_error < -5) {max_auger_base = .155;}
if (temp_error < -10) {max_auger_base = .169;}
if (temp_error < -20) {max_auger_base = .181;}
if (temp_error < -50) {max_auger_base = .299;}
}
}
if (fumee && pid_only)
{
lcdw(6,3,"prepid");
if (Setpoint < 200) {
pid_bottom = 1.8 ; // Setpoint * .010;
pid_top = 6.0 ; // Setpoint * .022;
}
if (Setpoint > 200 && Setpoint < 300) {
pid_bottom = Setpoint * .015;
pid_top = (Setpoint/500)*mcycletime;
}
myPID.SetOutputLimits(pid_bottom , pid_top);
}
//if (Setpoint > 170) {
if (temp_error >= -15 && ! pid_only) {
adapt_mode = true; // only go into adapt mode if we are close to set point.
temp_changed = false; // we made it to set temp so not worried about temp change
start_cycle = false; // we are finished in start up because we have reached or close to set point.
}
// 10/15/2016
if (! pid_only) {calibrate_auger_percent();}
} //pre_PID
void food_done() {
if (food_temp1 >= food_done_temp && !hold_temp_mode && probe3) // This logic will only allow the Setpoint to be changed 1 time. So the user can change the temp in hold mode.
{
// in other words once the system goes into hold mode It will not keep setting the Setpoint to predetermined hold temp allowing user to change hold temp.
// lcd.setCursor(4,3);lcd.print("Done ");
// put in here the routine to go to hold mode.
// like go to 170 degrees and hold there.
// also you can place an alarm on the system to alarm the chef that the food is done.
Setpoint = hold_temp;
hold_temp_mode = true;
food_done_mode = true;
rtc_installed = false; // turn off so the word DONE will show up on lcd.
testmode = false;
calibrate_auger_percent();
// if (food_done_mode ){lcd.setCursor(0,3);lcd.print("Hold");}
}
else { }
}
void igniter_control() {
// igniter control
//\\ changed 10/13/19
if (pit_temp <= igniter_temp && ! cooldown_startmode && igniter_timer > 0 && ! fan_mode )
{
igniter_on();
// digitalWrite(igniter_pin, HIGH);
// Put code here for alarm or restart igniter
// you could also do a variable like if igniter_temp-Setpoint-30 for 30 degree below set temp
// turn on igniter
lcdw(17,2,"I");
}
else
{
igniter_off();
// Just leave it off.
lcdw(17,2," ");
igniter_timer = 25;
start_cycle = false;
}
} // igniter control
void auger_manual() {
adapt_mode = false;
auger_run_time = mm_timer,2; // I changed this in manual mode to change value with auger timing buttons
if (hits > auger_run_time || cooldown_startmode)
{
auger_off(); // turn off auger
auger_status = 0;
} else
{
auger_on(); // turn on auger
auger_status = hits;
}
if (auger_status == 0) {auger_status = 1;}
} //auger_manual
void auger_auto() {
if (auger_run_time < hits || cooldown_startmode || skipacycle || nopellets) // if out of time or cool down mode. turn off auger
{
// if (!pid_only) {
// pulse_auger(300,0);
auger_off();
auger_status = 0;
// }
if (!cooldown_startmode) {lcdw(15,0," ");}
if (bump_auger_mode ) {
auger_on();
lcdw(14,0,"bAuger");
}
else {
auger_off();
if (!cooldown_startmode) {
lcdw(14,0," ");
}
}
if (hits >= mcycletime) {
skipacycle = false;
}
} else {
auger_on ();
auger_status = hits;
}
if (auger_status == 0) {
auger_status = 1;
}
} //auger_auto
void set_PID() {
Input = pit_temp;
if (tempKp !=consKp | tempKi != consKi | tempKd != consKp) { // only set new tunings if changed
if (ponm) {
myPID.SetTunings(consKp, consKi, consKd, P_ON_M);}
else {
myPID.SetTunings(consKp, consKi, consKd, P_ON_E);
}
tempKp = consKp, tempKi = consKi, tempKd = consKp;
}
myPID.Compute();
auger_run_time = Output;
///// this routine changed 4/16/17
if (temp_error > 0 && temp_error <= 5 && falling ) { // lets get the PID routine to start early.
double save_auger_run_time = auger_run_time;
}
else if (temp_error <= 0 && temp_error >= -3 && climbing ) { }
else if ( temp_error > 10 ){ }
else { }
}
void PID_onm() {
Input = pit_temp;
myPID.SetTunings(consKp, consKi, consKd, P_ON_M);
myPID.Compute();
}
void read_TC_K() {
static struct var_max31855 TC_CH0;
static struct var_max31855 TC_CH1;
static struct var_max31855 TC_CH2;
static struct var_max31855 TC_CH3;
// update TC0
struct var_max31855 *tc_ptr;
tc_ptr = &TC_CH0;
thermocouple0.MAX31855_update(tc_ptr); // Update MAX31855 readings
// update TC1
tc_ptr = &TC_CH1;
thermocouple1.MAX31855_update(tc_ptr); // Update MAX31855 readings
// update TC2
tc_ptr = &TC_CH2;
thermocouple2.MAX31855_update(tc_ptr); // Update MAX31855 readings
// update TC3
tc_ptr = &TC_CH3;
thermocouple3.MAX31855_update(tc_ptr); // Update MAX31855 readings
// TC0
tmp = (double)TC_CH0.ref_jcn_temp * .0625; // 0.0625 convert fixed pt # to double
tmp = (double)TC_CH0.value * 0.25; // .25 convert fixed pt # to double
if (0x00 == TC_CH0.status) {
tmp = ((tmp * 9 / 5) + 32);
if (tmp > -100 && tmp < 700 ) {pit_temp = tmp+temp_offset;}
} else {
if ( twoprobes) {
lcdw(3,1,"^");
}
if (!twoprobes) {
lcdw(3,0,"^");
}
}
//
// else if(0x01 == TC_CH0.status){Serial.println("OPEN");}
// else if(0x02 == TC_CH0.status){Serial.println("SHORT TO GND");}
// else if(0x04 == TC_CH0.status){Serial.println("SHORT TO Vcc");}
// else{Serial.println("unknown fault");}
// TC1
tmp = (double)TC_CH1.ref_jcn_temp * .0625; // 0.0625 convert fixed pt # to double
tmp = (double)TC_CH1.value * 0.25; // .25 convert fixed pt # to double
if (0x00 == TC_CH1.status) {
tmp = ((tmp * 9 / 5) + 32);
if (tmp > -100 && tmp < 700 ) {pit_temp2 = tmp+temp_offset;}
if (probe2) {
if ( twoprobes) {
lcdw(3,1," ");
}
} else {
if (!twoprobes) {
// lcdw(3,0,"^");
}
}
}
// TC2 first food probe
tmp = (double)TC_CH2.ref_jcn_temp * .0625; // 0.0625 convert fixed pt # to double
tmp = (double)TC_CH2.value * 0.25; // .25 convert fixed pt # to double
//Serial.print("TC2 Temp = ");
if (0x00 == TC_CH2.status) {
// tmp = ((tmp * 2.2) +32);
tmp = ((tmp * 9 / 5) + 32);
if (tmp > -100 && tmp < 700 ) {food_temp1 = tmp+food_offset;}
if (probe3) {
if (!twoprobes) {
lcdw(7,0," ");
}
} else {
if (!twoprobes) {
// lcdw(7,0,"^");
}
}
}
// TC3 second food probe
tmp = (double)TC_CH3.ref_jcn_temp * .0625; // 0.0625 convert fixed pt # to double
tmp = (double)TC_CH3.value * 0.25; // .25 convert fixed pt # to double
//Serial.print("TC3 Temp = ");
if (0x00 == TC_CH3.status) {
// tmp = ((tmp * 2.2) +32);
tmp = ((tmp * 9 / 5) + 32);
if (tmp > -100 && tmp < 700 ) {food_temp2 = tmp+food_offset;}
if (probe4) {
if ( twoprobes) {
lcdw(7,1," ");
}
} else {
if (!twoprobes) {
// lcdw(7,1,"^");
}
}
}
temp_error = pit_temp - Setpoint; // how far off are we from Setpoint
}
void get_temps() { // this is the Thermister code.
// pit_temp = thermister_temp(analogRead(analogpit));
// pit_temp2 = thermister_temp(analogRead(analogpit)); // call thermistor routine.
food_temp3 = thermister_temp(analogRead(analogfood1));
//food_temp1 = thermister_temp(analogRead(analogpit));
food_temp4 = thermister_temp(analogRead(analogfood2));
//ambient_temp = thermister_temp(analogRead(ambienttemp));
// temp_error = pit_temp - Setpoint; // how far off are we from Setpoint
}
// This function returns a string with the date time stamp
// This function not used in the mega2560 version. Only for Yun
String getTimeStamp() {
String result;
Process time;
// date is a command line utility to get the date and the time
// in different formats depending on the additional parameter
// lets figure out how long since last update or auger run
time.begin("date");
time.addParameter("+%D-%T"); // parameters: D for the complete date mm/dd/yy
// T for the time hh:mm:ss
time.run(); // run the command
// read the output of the command
while (time.available() > 0)
{
char c = time.read();
if (c != '\n')
result += c;
}
return result;
}
double thermister_temp(double aval)
{
double R, T;
// Do the log once so as not to do it 4 times in the equation
// R = log(((1024/(double)aval)-1)*(double)22200);
R = log((1 / ((1024 / (double) aval) - 1)) * (double) 21200);
// Compute degrees C
T = (1 / ((2.3067434E-4) + (2.3696596E-4) * R + (1.2636414E-7) * R * R * R)) - 273.25;
// return degrees F
return ((double) ((T * 9.00) / 5.00 + 32.00));
}
void hours_tick()
{ // Things that happen on hourly basis
}
void minutes_tick()
{
// changed 7/13/2019 Set to a fixed temp of 160. Anything past that turn off igniter
lcdw(13,2," "); // clear Write status on screen
fall_cnt = 0;
climb_cnt = 0;
if (pit_temp > max_safe_temp) { fan_only();}
// changed 9/17/19
if (minutes < 4 && hours == 0 && start_cycle){
pid_top = 3.0;consKp = .15; consKi = .001; consKd = .03;
}
if (between(minutes,5,10) && hours == 0 && start_cycle){pid_top = 8.0 ;
consKp = .15; consKi = .001; consKd = .03;
}
if (minutes % 30 == 0) {
clear_lcd(); // clear the screen every 30 minutes just in case there is clutter
}
if ( ! fan_mode ) {igniter_timer -= 1; }
if (igniter_timer == 0) {cooldown_startmode = true ; // igniter failed to light in 10 minutes. Shut down pit.
igniter_fail = true;
lcdw(17,2," ");
lcdw(0,3,"Ignite Fail");
}
if (cooldown_startmode) {
cooling_down(); // Start the cool down cycle..
}
if (minute_tick == 7) {minute_tick = 0;buff_full = 1;
}
one_minute = minute_tick;
tempvals[one_minute] = pit_temp; //place current pit temp in var
// make sure we got at lease 1 temp in var
if (tempvals[0] > 0) {avg_temp = 0;
for (int valcnt = 0; valcnt < 7; valcnt++) {avg_temp = avg_temp + tempvals[valcnt];
}
lcdw(0, 3," ");avg_temp = 0; // reset avg_temp to start fresh
// if all vars are full divide by 7 otherwise divide by current count
if (buff_full == 1) {
for (int valcnt = 0; valcnt < 7; valcnt++) {avg_temp = avg_temp + tempvals[valcnt];
}
avg_temp = avg_temp / 7;}
else {
for (int valcnt = 0; valcnt < 7; valcnt++) {
avg_temp = avg_temp + tempvals[valcnt];
}
avg_temp = avg_temp / (one_minute + 1);
if ( ! cooldown_startmode) {
if (avg_temp < 100) {
lcdw(0,3," "+String(avg_temp)+" ");
}
else {
lcdw(0,3,String(avg_temp)+" ");
}
}
}
if (!adapt_mode) {
// pre_PID();
if (last_PID_on) {
last_PID();
}
if (temp_error >= -15 && ! pid_only ) {
adapt_mode = true; // only go into adapt mode if we are close to set point.
start_cycle = false; // we are finished in start up because we have reached or close to set point.
last_PID_on=true; // turn it back on for next time thru.
}
}
} //buff_full
minute_tick += 1;
last_temp = pit_temp;
calibrate_auger_percent();
} // minutes tick
void half_second_tick()
{
if (half_second / 10 < 1 ) {
hits = auger_seconds + (half_second / 10);
}
Realseconds = hits;
if (!cooldown_startmode && ! fan_mode) {
if (smoke_mode1) {smoke1();}
if (smoke_mode2) {smoke2();}
if (smoke_mode3) {smoke3();}
if (smoke_mode4) {smoke4();}
if (smoke_mode5) {smoke5();}
if (smoke_mode6) {smoke6();}
if (smoke_mode7) {smoke7();}
if (!twoprobes ) {
if (hits < 10) {
lcdw(10,0," "+String(hits,1));
}
else {
lcdw(10,0,String(hits,1));
}
}
else {
if (hits < 10) {
lcdw(10,1," "+String(hits,1));
}
else {
lcdw(10,1,String(hits,1));
}
}
}
if (hits > mcycletime) {
skipacycle = false;
hits = 0.0;
//// fall_cnt = 0;
auger_seconds = 0.0;
}
if (auger_mode == 0) {
auger_auto();
}
if (auger_mode == 1 && ! fan_mode) {
auger_manual(); // 0 = Auto , 1 = Manual
lcdw(16,1,String(mm_timer,1));
}
// auger in automatic mode, using PID
// auger in manual timing mode
if (!cooldown_startmode && auger_mode == 0) {
lcdw(16,1,String(Output,1));
}
}
void seconds_tick()
{ // Things that need to happen on a second by second basis
if (rtc_installed) {
DateTime now = rtc.now();
rtc_second = now.second(), DEC;
rtc_minute = now.minute(), DEC;
rtc_hour = now.hour(), DEC;
}
if (seconds % 10 == 0) { // only update every 10 seconds
if (rtc_installed) {ambient_temp = rtc.getTemperature();
ambient_temp = ambient_temp * 9 / 5 + 32 + ambient_offset;}
}
read_TC_K();
// get_temps(); thermistor
if (useprofiles ) {
///... stage1 = foo.aa;
///... stage2 = foo.ad;
// stage2_time_hours = foo.ae;
}
///// if (seconds % 45 == 0) { // changed 3/27/17
if (seconds % 30 == 0) {
if (adapt_mode && ! pid_only) {
adapt();
}
}
if (seconds % 10 == 0) {
calibrate_auger_percent();
}
// check and see if we have matched all setpoint and averages. if so set best min max settings.
if (avg_temp == Setpoint && Setpoint == int(pit_temp) && !pid_only) {
min_best = min_auger_base, 3;
max_best = max_auger_base, 3;
autotune_complete = true;
//if (seconds <15) {autotune_count +=1;}
min_best_last = min_best;
max_best_last = max_best;
if (seconds % 10 ) {
autotune_count +=1;
if(autotune_count == 1) {
// aminmax.mn = min_best,3;aminmax.mx = max_best,3;aminmaxspread;aminmax.pt=Setpoint;
}
}
//if (autotune_count == 1 ) {aminmax.mn = min_best,3;aminmax.mx = max_best,3;aminmax.pt=Setpoint;}
}
if (temp_error > 15 || temp_error < -15 && !pid_only) {
reset_autotune();
autotune_count = 0;
}
if (seconds % 30 == 0) {
food_done(); //Check if food is done.... moved from the loop to steady.
}
if (seconds % 30 == 0 && food_done_mode) {
lcdw(4,3,"Done ");
lcdw(0,3,"Hold");
}
if (seconds % 10 == 0) {
igniter_control(); // Check if igniter needs to be on... moved from loop
}
if (autotune_complete == true && !pid_only) {
lcdw(16,2,"T");
} else {
//lcd.setCursor(13, 2);
//lcd.print(" ");
}
if (testmode && ! cooldown_startmode) {
if (seconds % 3 == 0) {
//// lcdw(4,3,String(min_auger_base, 3)); // Show PID range & movement on LCD Screen
String ps=String(consKp,3);
lcdw(4,3,"P"+ps.substring(1));
}
if (seconds % 7 == 0) {
//// lcdw(4,3,String(max_auger_base,3)); // you can do a check for anything you want at any second like this.
String is=String(consKi,3);
lcdw(4,3,"I"+is.substring(1));
lcdw(4,2,"T="+String(pid_top));
}
if (seconds % 12 == 0) {
String ds = String(consKd,3);
lcdw(4,3,"D"+ds.substring(1));
lcdw(4,2,"B="+String(pid_bottom));
}
if (seconds %10 == 0) {
// lcd.setCursor(10,3);lcd.print(" ");
// lcd.setCursor(10,3);lcd.print(probe3);
}
}
else if (! cooldown_startmode) {
if (rtc_installed) {
if (seconds < 4) {
lcd.setCursor(4,3);lcd.print(" AT");
}
else {
if (ambient_temp > 99) {lcd.setCursor(4,3); lcd.print(ambient_temp,0);}
else{
lcd.setCursor(4,3);lcd.print(" ");
lcd.setCursor(5,3); lcd.print(ambient_temp, 0);
}
}
}
}
if (adapt_mode || autotune_complete) { // setup mechanism to graph valley and peak values
if (pit_temp > pit_peak) {
pit_peak = pit_temp;
if (pit_peak > 500) {
pit_peak = pit_temp;
}
}
if (pit_temp < pit_valley) {
pit_valley = pit_temp;
if (pit_valley < 0) {
pit_valley = pit_temp;
}
}
}
show_stages();
// average temp over a 10 second span --- change to 10 second averaging on 4/23/16
if (second_tick == 10) {
second_tick = 0;
sec_buff_full = 1;
}
one_second = second_tick;
sec_tempvals[one_second] = round(pit_temp); //place current pit temp in var
// make sure we got at lease 1 temp in var
if (sec_tempvals[0] > 0) {
sec_avg_temp = 0;
for (int sec_valcnt = 0; sec_valcnt < 10; sec_valcnt++) {
sec_avg_temp = sec_avg_temp + sec_tempvals[sec_valcnt];
}
// lcd.setCursor(7, 2);
// lcd.print(" ");
lcd.setCursor(7, 2);
sec_avg_temp = 0; // reset avg_temp to start fresh
// if all vars are full divide by 15 otherwise divide by current count
if (sec_buff_full == 1) {
for (int sec_valcnt = 0; sec_valcnt < 10; sec_valcnt++) {
sec_avg_temp = sec_avg_temp + sec_tempvals[sec_valcnt];
}
sec_avg_temp = sec_avg_temp / 10;
if (testmode && !twoprobes)
{
// if (sec_avg_temp <100) {lcd.print(" ");lcd.print(sec_avg_temp);lcd.print("");}
// else {lcd.print(sec_avg_temp);lcd.print("");}
}
}
else {
for (int sec_valcnt = 0; sec_valcnt < 10; sec_valcnt++) {
sec_avg_temp = sec_avg_temp + sec_tempvals[sec_valcnt];
}
sec_avg_temp = sec_avg_temp / (one_second + 1);
if (testmode && !twoprobes) {
// if (sec_avg_temp <100) {lcd.print(" ");lcd.print(sec_avg_temp);lcd.print("");}
// else {lcd.print(sec_avg_temp);lcd.print(""); }
}
}
} //sec_buff_full
second_tick += 1;
auger_seconds += 1;
last_temp_sec = pit_temp;
//if (testmode) {
if (twoprobes) {
if (round(pit_temp) > sec_avg_temp ) {
lcdw(3,1,"+");
climbing = true;
climb_cnt += 1;
} else {
lcd.setCursor(3, 1);
climbing = false;
}
if (round(pit_temp) < sec_avg_temp) {
lcdw(3,1,"-");
falling = true;
fall_cnt += 1;
/// if (climb_cnt > 0) {climb_cnt -=1;}
} else {
lcd.setCursor(3, 1);
falling = false;
}
if (round(pit_temp) == sec_avg_temp) {
lcdw(3,1,".");
}
} else {
if (round(pit_temp) > sec_avg_temp ) {
lcdw(3,0,"+");
climbing = true;
climb_cnt += 1;
} else {
lcd.setCursor(3, 0);
climbing = false;
}
if (round(pit_temp) < sec_avg_temp) {
lcdw(3,0,"-");
falling = true;
fall_cnt += 1;
//// if (climb_cnt > 0) {climb_cnt -=1;}
} else {
lcd.setCursor(3, 0);
falling = false;
}
if (round(pit_temp) == sec_avg_temp) {
lcdw(3,0,".");
}
}
if (fan_mode) {lcdw(11,1,"Fan Only");igniter_off();testmode = false;}
//} //testmode
// if (seconds % 5 == 0 ) {
// This can now be imported into a spreadsheet.
if (!graph_data ) {
Serial.print(min_auger_base, 3);
Serial.print(","); Serial.print(max_auger_base, 3);
Serial.print(","); Serial.print(food_temp1);
Serial.print(","); Serial.print(Setpoint);
Serial.print(","); Serial.print(pit_temp);
Serial.print(","); Serial.print(avg_temp);
Serial.print(","); Serial.print(consKp);
Serial.print(","); Serial.print(consKi);
Serial.print(","); Serial.print(consKd);
Serial.print(","); Serial.print(sec_avg_temp);
Serial.print(","); Serial.print(hours);
Serial.print(","); Serial.print(minutes);
Serial.print(","); Serial.print(seconds);
Serial.print(","); Serial.print(hits);
Serial.print(","); Serial.print(temp_error);
Serial.print(","); Serial.print(Output);
// Serial.print(" ");Serial.print(mcycletime);
// Serial.print("-M");Serial.print(mm_timer);
Serial.print(","); if (climbing) {Serial.print("clim");Serial.print(",");Serial.print(climb_cnt);}
Serial.print(","); if (falling) {Serial.print("fall");Serial.print(",");Serial.print(fall_cnt);}
Serial.print(","); Serial.print(pit_peak);
Serial.print(","); Serial.print(pit_valley);
Serial.print(","); if (autotune_complete && !pid_only) {Serial.print("T");}
Serial.print(",");
Serial.print(min_best, 3); Serial.print(","); Serial.print(max_best, 3); Serial.print(",");
Serial.print(igniter_timer);
Serial.print(",");
Serial.print(Realseconds);
Serial.print(",");
Serial.print(fall_cnt);
Serial.print(",Spread=");
Serial.print(auger_spread, 3);
Serial.print(",");
if(smoke_mode1) {Serial.print("smok1");}
else if (smoke_mode2){Serial.print("smok2");}
else if (smoke_mode3) {Serial.print("smok3");}
else if (smoke_mode4) {Serial.print("smok4");}
else if (smoke_mode5) {Serial.print("smok5");}
else if (smoke_mode6) {Serial.print("smok6");}
else if (smoke_mode7) {Serial.print("smok7");}
if (rtc_installed) {
}
Serial.println();
// }
} else {
// Put items here that you want to send to esp8266
// make sure you change the esp8266 program to match this structure
String txtcmd = "";
char cntchar;
Serial3.print(Setpoint,0);
Serial3.print(",");
Serial3.print(pit_temp,1);
Serial3.print(",");
Serial3.print(hits,0);
Serial3.print(",");
if (digitalRead(auger_pin)== HIGH){Serial3.print("1");}else {Serial3.print("0");}
Serial3.print(",");
if (digitalRead(igniter_pin)== HIGH){Serial3.print("1");}else {Serial3.print("0");}
Serial3.print(",");
if (rtc_installed) {
Serial3.print(rtc_hour);
Serial3.print(",");
Serial3.print(rtc_minute);
Serial3.print(",");
}else{
Serial3.print(hours);
Serial3.print(",");
Serial3.print(minutes);
Serial3.print(",");
}
if (cooldown_startmode && cook_mode) {Serial3.print("Cool Down ");
if( cooldown_minutes >1) {Serial3.print(cooldown_minutes); }else {
Serial3.print("Ignite Failure");}}
else if (digitalRead(igniter_pin)== HIGH) {Serial3.print("Igniting "); }
else if (cook_mode && digitalRead(igniter_pin)== LOW) {Serial3.print("Cook ");
if(pid_only ) {Serial3.print("PID "); }
if(adapt_mode && ! autotune_complete && !pid_only ){Serial3.print("Adapt");}
else if (autotune_complete && adapt_mode && ! pid_only) {Serial3.print("TUNED"); }
else if (fan_only && digitalRead(igniter_pin)== LOW && !pid_only) {Serial3.print("Fan Only");}
else if (cooldown_minutes <= 1 && !cook_mode && ! cooldown_startmode){Serial3.print("Pit Off");}
}
Serial3.print(",");
Serial3.print(food_done_temp,0);
Serial3.print(",");
Serial3.print(food_temp1,1);
Serial3.print(",");
Serial3.print(min_auger_base,3);
Serial3.print(",");
Serial3.print(max_auger_base,3);
Serial3.print(",");
if (auger_mode == 0) {Serial3.print(Output,2);} else {Serial3.print(mm_timer,2);}
Serial3.print(",");
Serial3.print(auger_mode);
Serial3.print(",");
Serial3.print(avg_temp);
Serial3.print(",");
if (digitalRead(fan_pin)== HIGH){Serial3.print("1"); }else {Serial3.print("0");}
Serial3.print(",");
Serial3.print(pit_temp2,1);
Serial3.print(",");
Serial3.print(food_temp2,1);
Serial3.print(",");
// 18 & 19
if (stage1 != stage2) {if (stage2_set) {Serial3.print("2 of 3");}if (!stage2_set){Serial3.print("1 of 3");}
if (food_done_mode) {Serial3.print("3 of 3");}}
else {Serial3.print("1 of 1"); }
Serial3.print(",");
/* if (stage1 && stage2_set) Serial3.print("1 of 3");
if (stage2_set ) Serial3.print("2 of 3");
if (food_done_mode ) Serial3.print("3 of 3");
Serial3.print(",");
*/
// 19 & 20
Serial3.print(stage1);
Serial3.print(",");
// 20 & 21
Serial3.print(stage1_time_hours);
Serial3.print(",");
// 21 & 22
Serial3.print(stage1_time_minutes);
Serial3.print(",");
// 22 & 23
Serial3.print(stage2);
Serial3.print(",");
// 23 & 24
Serial3.print(stage2_time_hours);
Serial3.print(",");
// 24 & 25
Serial3.print(stage2_time_minutes);
Serial3.print(",");
// 25 & 26
Serial3.print(hold_temp);
Serial3.print(",");
// 26 & 27
if (useprofiles) {Serial3.print(what_profile); }else if (!useprofiles) {Serial3.print("1 Stage");}
Serial3.print(",");
// 27 & 28
if (rtc_installed) {Serial3.print(ambient_temp);}else {Serial3.print("N/A"); }
Serial3.print(",");
//28 & 29
Serial3.print(ambient_offset);
Serial3.print(",");
// 29 & 30
Serial3.print(temp_offset);
Serial3.print(",");
// 30 & 31
if (climbing){Serial3.print("1");}
if (falling) {Serial3.print("0");}
if (!falling && !climbing) {Serial3.print("-");}
Serial3.print(",");
// 31 & 32
Serial3.print(String(consKp)+" "+String(consKi,3)+" "+String(consKd));
Serial3.print(",");
// 32 & 33
Serial3.print(String(climb_cnt));
Serial3.print(",");
// 33 & 34
Serial3.print(String(fall_cnt));
Serial3.print(",");
// 34 & 35
Serial3.print(String(pit_peak));
Serial3.print(",");
// 35 & 36
Serial3.print(String(pit_valley));
Serial3.print(",");
// 36 & 37
Serial3.print(String(fan_speed));
Serial3.print(",");
// 37 & 38
Serial3.print(String(auger_speed));
Serial3.print(",");
// Serial3.print(">>>");
// Serial3.print('\n');
Serial3.println();
// int scount = Serial3.println();
// Serial3.println();
}
if (twoprobes) {
lcdw(0,0,"PIT Food Cycle ");
if (int(pit_temp < 100)) {
lcdw(0,1," "+String(int(pit_temp)));
}
else {
lcdw(0,1,String(int(pit_temp)+" "));
}
if (int(food_temp1 < 100)) {
lcdw(4,1," "+String(int(food_temp1)));
}
else {
lcdw(4,1,String(int(food_temp1)));
}
if (food_done_temp < 100) {
lcdw(4,2," ");
}
} else { // 4 probes
//\\ probe 1
if (int(pit_temp < 100)) {
lcdw(0,0," "+String(int(pit_temp)));
}
else {
lcdw(0,0,String(int(pit_temp)));
}
lcdw(0,1,String(int(pit_temp)));
//\\ probe 2
if (int(pit_temp2 < 100)) {
lcdw(0,1," "+String(int(pit_temp2)));
}
else {
lcdw(0,1,String(int(pit_temp2))+" ");
}
if (!probe2) {lcdw(0,1," ");}
//\\ probe 3
if (int(food_temp1 < 100)) {
lcdw(4,0," "+String(int(food_temp1)));
}
else {
lcdw(4,0,String(int(food_temp1)));
}
if (!probe3) {lcdw(4,0," ");}
//\\ probe 4
if (int(food_temp2 < 100)) {
lcdw(4,1," "+String(int(food_temp2)));
}
else {
lcdw(4,1,String(int(food_temp2)));
}
if (!probe4) {lcdw(4,1," ");}
lcd.setCursor(4, 2);
if (food_done_temp < 100) {
lcdw(4,2," ");
}
}// if 2probes else
// Display if we are in Auto or Manual auger mode on screen
if (auger_mode == 1) {
lcdw(19,2,"M");
} else {
lcdw(19,2,"A");
}
// Display if we are in adapt mode or not on screen
if (adapt_mode && !autotune_complete && ! pid_only) {
lcdw(16,2,"L");
} else {
// lcdw(16,2," ");
}
// 10/15/2016
if(pid_only) {lcdw(13,2,"P");} else {lcdw(13,2," ");}
lcd.setCursor(14, 1);
if (cooldown_startmode ) {
lcdw(14,0," "+String(cooldown_minutes)+" ");
lcdw(13,1,"Cool DN");
lcdw(10,0," ");
if (igniter_fail) {
lcdw(0,3,"Ignite Fail");
}
igniter_off(); // make sure its off
}
else {
if ( ! fan_mode) {lcd.print(" "); lcd.setCursor(10, 2); lcd.print(" ");}
if (auger_mode == 0) {
lcdw(16,1,String(Output,2));
}// else {lcd.setCursor(16,1);lcd.print(mm_timer,2);}
if (auger_mode == 1 && ! fan_mode) {
lcdw(16,1,String(mm_timer,2));
}
cooldown_minutes = cooldown_count;
}
// controlling the fan.
// modified 10/29/15
// if (temp_error > 4 && digitalRead(auger_pin)==LOW && nopellets_cnt > 0 && !cooldown_startmode )
if (temp_error > 4 && !cooldown_startmode )
{
/// if ( ! fan_mode ) {fan_pulse(200,0);}
if ( ! fan_mode ) {
// fan_pulse(200,0);
} // changed 4/22/17
if (!pid_only) {
// changed 3/4/17
// nopellets=true;
nopellets_cnt -=1; // count down to zero
min_ab=min_auger_base;
// auger_pulse(200,0);
}
}
else
{
fan_on ();
nopellets=false;
if (nopellets_cnt < 30) { nopellets_cnt = 30 ; } //
}
// show percent of auger time
if (!twoprobes && ! fan_mode) {
if (!cooldown_startmode) {
int s_output = 0;
if (auger_mode == 1) {
s_output = (mm_timer / mcycletime) * 100;
} else {
s_output = (Output / mcycletime) * 100;
}
lcd.setCursor(10, 2);
if (s_output < 10) {lcdw(9,2," "+String(s_output));}
if (s_output > 99) {lcdw(9,2,"100");}
if (s_output < 100 && s_output > 9) {lcdw(9,2," "+String(s_output));}
else
{lcd.print(s_output);}
lcdw(12,2,"%");
}
lcd.setCursor(10, 1);
if (!fan_mode) {
if (mcycletime < 10) {lcd.print(" ");}
lcd.print(mcycletime); // Cycle time
if (mcycletime < 99) {lcd.print(" ");}
}
}
if (twoprobes && ! fan_mode) {
lcd.setCursor(10, 2);
if (mcycletime < 10) {
lcd.print(" ");
} lcd.print(mcycletime); // Cycle time
if (mcycletime < 99) {
lcd.print(" ");
}
}
// Cook time clock display. This is the RTC time displayed.
if (rtc_installed) {
char Time[16];
sprintf(Time, "%02d:%02d:%02d", rtc_hour, rtc_minute, rtc_second);
lcdw(12,3,Time);
} else {
char Time[16];
sprintf(Time, "%02d:%02d:%02d", hours, minutes, seconds);
lcdw(12,3,Time);
// Cook time clock display. Its actually time since arduino started or reset.
}
if (bump_auger_mode) {
bump_auger_mode = false;
}
// don't want to be in adapt mode if way off.
if (temp_error < -50 && ! pid_only) {
adapt_mode = false;
lcdw(16,2," ");
// pre_PID();
last_PID();
}
if (temp_error > 50 && ! pid_only) {
adapt_mode = false;
lcdw(16,2," ");
// pre_PID();
last_PID();
}
// show average temp on screen
if ( ! cooldown_startmode ) {
if (seconds > 4 && seconds < 7 ){
lcdw(0,3,"AVG");
}else{
if (avg_temp < 100) {
lcdw(0,3," "+String(avg_temp)+" ");
}
else {
lcdw(0,3,String(avg_temp)+" ");
}
}
}
// hits+=1;
thits = 0.00;
//..// lcdw(5,2,String(int(food_temp3)));
} // seconds tick
void update_time()
{
// don't mess with this routine. It works
static unsigned char last_half_second = 0.00;
static unsigned char last_seconds = 0;
static unsigned char last_minutes = 0;
static unsigned char last_hours = 0;
current_millis_value = millis() - start_time;
m += current_millis_value - previous_millis_value;
half_second = m / 100;
seconds += m / 1000;
minutes += seconds / 60;
hours += minutes / 60;
if (seconds > last_seconds)
{
last_seconds = seconds % 60;
seconds_tick();
}
if (half_second > last_half_second)
{ last_half_second = seconds / 100;
half_second_tick();
}
if (minutes > last_minutes)
{
last_minutes = minutes % 60;
minutes_tick();
}
if (hours > last_hours)
{
last_hours = hours % 24;
hours_tick();
}
m = m % 1000;
half_second = m / 10;
seconds = seconds % 60;
minutes = minutes % 60;
hours = hours % 24;
previous_millis_value = current_millis_value;
}//update_time
void calibrate_auger_percent()
{
// Lets set up some basic #'s based on my studies with Traeger timing.
min_auger_percent = (Setpoint+temp_offset ) * min_auger_base / 100; // This will give me minimum auger percent
// which is multiplied by mcycletime
// and you end up with minimum seconds
max_auger_percent = (Setpoint+temp_offset ) * max_auger_base / 100; // This is maximum auger time %25 so overshoot is minimized
// Set the limits for the PID control
// 10/15/2016 changed
if (! pid_only) {
myPID.SetOutputLimits(min_auger_percent * mcycletime , max_auger_percent * mcycletime );
}
else {
myPID.SetOutputLimits(pid_bottom , pid_top); // make it externally adjustable.
}
}
void adapt() {
if (Setpoint > 25 && Setpoint < 501) {
if (!autotune_complete && !pid_only) {
// tighten swings by reducing spread between min and max auger timing /
if ( minutes % 7 == 0) {
// reset peak & valley
pit_peak = 0;
pit_valley = 500;
}
if ( minutes % 5 == 0) {
// Adjust temp swings
if (pit_peak > Setpoint + 10 && max_auger_base >= min_auger_base + auger_spread && min_auger_base > auger_spread)
{
// max_auger_base -= .005;calibrate_auger_percent();
// Lets not effect spread on Fumee.
}
if (pit_valley < Setpoint - 10 && min_auger_base + auger_spread <= max_auger_base )
{
// min_auger_base += .005;calibrate_auger_percent();
}
}
if ( minutes % 1 == 0) { // Widen Spread at this point to cushion temp drop
// avg temp low
if (temp_error < -5)
{
if (avg_temp < Setpoint )
{
min_auger_base += .001; //changed 1/25/15
max_auger_base += .001;
}
}
// avg temp is high
if (temp_error > 5)
{
if (avg_temp > Setpoint )
{
max_auger_base -= .001;
min_auger_base -= .001;
}
}
if ( minutes % 3 == 0) {
if (temp_error >= 5) {
// max_auger_base -= .003;
// min_auger_base -= .003;
}
if (temp_error <= -5) {
// max_auger_base += .003;
// min_auger_base += .003;
}
}
calibrate_auger_percent();
} //minutes
}//!autotune_complete
} //Setpoint
// min max is out of whack ... Reset to known #'s
if (min_auger_base < .040 || max_auger_base < min_auger_base || max_auger_base > .500 || min_auger_base > .500 ) {
// changed 5/16/17
if (min_auger_base < .040)
{
min_auger_base = .040;
max_auger_base = min_auger_base+auger_spread;
}
// min_auger_base = min_best_last;
// max_auger_base = max_best_last;
}
} //adapt
void adapt_adjust()
{
}// adapt adjust
// This routine not needed on the mega version. made it for the MakerPlot Version.
void flushBuffer()
{
Serial.flush(); // flush serial buffer (may not always flush?)
while (Serial.available()) // manually empty as well, read as long as bytes are there
Serial.read();
}
void buttonControl(){}
void cooling_down()
{
cooldown_minutes -= 1;
if (igniter_fail) {
lcdw(0,3,"Ignite Fail");
lcdw(10,0," ");
}
if (cooldown_minutes < 1) {
clear_lcd(); cook_mode = false; cooldown_startmode = false; fan_off();
lcdw(0,1,"Put Your Toys Away");
lcdw(0,2," Your Done ");
delay(30000);
software_Reset();
}
}
void bump_auger()
{
if (hits > auger_run_time) {
digitalWrite(auger_pin, HIGH); // turn on auger
lcdw(9,0,"B");
}
else {
digitalWrite(auger_pin, LOW);
lcdw(9,0,"b");
} // turn off auger}
}
void clear_lcd()
{
lcdw(0,0," ");
lcdw(0,1," ");
lcdw(0,2," ");
lcdw(0,3," ");
}
void readserial() { // incoming commands from wifi thru serial port on mega
// read typing from the send bar on the com port added 8/13/14
if (Serial3.available()) {
delay(10);
for (int n = 0; n < 5; n++) { //Get Data
buffer[n] = Serial3.read();
delay(10);
}
lcd.setCursor(12, 0);
for (int i = 0; i < 5; i++) {
}
String sbuffer = buffer;
sbuffer.trim();
// Available commands to receive from the serial port
if (sbuffer.substring(0, 5) == "pitup" ) {Setpoint += 5;}
if (sbuffer.substring(0, 5) == "pitdn" ) {Setpoint -= 5;}
if (sbuffer.substring(0, 5) == "pitu1" ) {Setpoint += 1;reset_autotune();}
if (sbuffer.substring(0, 5) == "pitd1" ) {Setpoint -= 1;reset_autotune();}
if (sbuffer.substring(0, 5) == "mxaup" ) {max_auger_base += .005;calibrate_auger_percent();}
if (sbuffer.substring(0, 5) == "rspv" ) {pit_peak = 0;pit_valley = 400;}
if (sbuffer.substring(0, 5) == "mxadn" ) {max_auger_base -= .005;calibrate_auger_percent();}
if (sbuffer.substring(0, 5) == "mnaup" ) {min_auger_base += .005;calibrate_auger_percent();}
if (sbuffer.substring(0, 5) == "mnadn" ) {min_auger_base -= .005;calibrate_auger_percent();}
if (sbuffer.substring(0, 5) == "cycup" ) {mcycletime += 5;calibrate_auger_percent();}
if (sbuffer.substring(0, 5) == "cycdn" ) {mcycletime -= 5;calibrate_auger_percent();}
if (sbuffer.substring(0, 5) == "cyc10" ) {mcycletime = 10;calibrate_auger_percent();}
if (sbuffer.substring(0, 5) == "cyc20" ) {mcycletime = 20;calibrate_auger_percent();}
if (sbuffer.substring(0, 5) == "cyc40" ) {mcycletime = 40;calibrate_auger_percent();}
if (sbuffer.substring(0, 5) == "cyc50" ) {mcycletime = 50;calibrate_auger_percent();}
if (sbuffer.substring(0, 5) == "cyc60" ) {mcycletime = 60;calibrate_auger_percent();}
if (sbuffer.substring(0, 5) == "coold" ) {cooldown_startmode = true;testmode = false;fan_mode=false;}
if (sbuffer.substring(0, 4) == "cook" ) {cooldown_startmode = false;cook_mode = true;clear_lcd();fan_mode=false;}
if (sbuffer.substring(0, 2) == "pt" ) {
if(sbuffer.substring(2,5).toInt()>149) {Setpoint = sbuffer.substring(2,5).toInt() ;
temp_changed = true;pid_bottom=Setpoint*1.10/100;pid_top=(Setpoint/450)*mcycletime;}}
if (sbuffer.substring(0, 1) == "P" ) {consKp = sbuffer.substring(1,5).toDouble(),2;}
if (sbuffer.substring(0, 1) == "I" ) {consKi = sbuffer.substring(1,5).toDouble(),2;}
if (sbuffer.substring(0, 1) == "D" ) {consKd = sbuffer.substring(1,5).toDouble(),2;}
if (sbuffer.substring(0, 1) == "B" ) {pid_bottom = sbuffer.substring(1,5).toDouble(),2;}
if (sbuffer.substring(0, 1) == "T" ) {pid_top = sbuffer.substring(1,5).toDouble(),2;}
if (sbuffer.substring(0, 1) == "pida" ){myPID.SetMode(AUTOMATIC);} // set pid mode to auto
if (sbuffer.substring(0, 1) == "pidm" ){myPID.SetMode(MANUAL);} // set pid mode to manual
if (sbuffer.substring(0, 1) == "pidc" ){myPID.Compute();}
if (sbuffer.substring(0, 1) == "<" ) {min_auger_base = sbuffer.substring(1,5).toDouble(),2;}
if (sbuffer.substring(0, 1) == ">" ) {max_auger_base = sbuffer.substring(1,5).toDouble(),2;}
if (sbuffer.substring(0, 5) == "kpup" ) {consKp += .05;}
if (sbuffer.substring(0, 5) == "kpup2" ) {consKp += 2;}
if (sbuffer.substring(0, 5) == "kpdn" ) {consKp -= .05;}
if (sbuffer.substring(0, 5) == "kpdn2" ) {consKp -= 2;}
if (sbuffer.substring(0, 5) == "kptwo") {consKp = 2;}
if (sbuffer.substring(0, 5) == "kiup" ) {consKi += .20;}
if (sbuffer.substring(0, 5) == "kidn" ) {consKi -= .20;}
if (sbuffer.substring(0, 5) == "kione") {consKi = 1;}
if (sbuffer.substring(0, 5) == "kitwo") {consKi = 2;}
if (sbuffer.substring(0, 5) == "kiten") {consKi = 10;}
if (sbuffer.substring(0, 5) == "kdup" ) {consKd += .20;}
if (sbuffer.substring(0, 5) == "kddn" ) {consKd -= .20;}
if (sbuffer.substring(0, 5) == "kdten") {consKd = 10;}
if (sbuffer.substring(0, 5) == "kdfiv") {consKd = 5;}
if (sbuffer.substring(0, 5) == "kdfou") {consKd = 4;}
if (sbuffer.substring(0, 5) == "kdthr") {consKd = 3;}
if (sbuffer.substring(0, 5) == "kdtwo") {consKd = 2;}
if (sbuffer.substring(0, 5) == "kdone") {consKi = 1;}
if (sbuffer.substring(0, 5) == "lrnof") {adapt_mode = false;}
if (sbuffer.substring(0, 5) == "lrnon") {adapt_mode = true;}
if (sbuffer.substring(0, 5) == "pidsu") {pidsetsampletime -= 100;}
if (sbuffer.substring(0, 5) == "pidsd") {pidsetsampletime += 100;}
if (sbuffer.substring(0, 5) == "manu" ) {auger_mode = 1;} // manual auger mode. No PID
if (sbuffer.substring(0, 5) == "auto" ) {auger_mode = 0;fan_mode=false;}
if (sbuffer.substring(0, 5) == "maxau") {max_ab += .01;}
if (sbuffer.substring(0, 5) == "maxad") {max_ab -= .01;}
if (sbuffer.substring(0, 5) == "minau") {min_ab += .01;}
if (sbuffer.substring(0, 5) == "minad") {min_ab -= .01;}
if (sbuffer.substring(0, 5) == "mmtup") {mm_timer += .1;}
if (sbuffer.substring(0, 5) == "mmtdn") {mm_timer -= .1;}
if (sbuffer.substring(0, 1) == "x" ) {
mm_timer = sbuffer.substring(1,5).toFloat(),2 ;Serial.println(sbuffer.substring(1,5).toFloat(),2);}
// else {mm_timer = sbuffer.substring(2,5).toFloat() ;adapt_mode = false;last_PID();Serial.println(mm_timer,2);}
if (sbuffer.substring(0, 4) == "lpro") {
//useprofiles=true;
load_profile(sbuffer.substring(4,5).toInt());
}
///// stage 2 stuff
if (sbuffer.substring(0,2) == "sd" || sbuffer.substring(0,2) == "fd") { // this is actually the done temp
food_done_temp = sbuffer.substring(2,5).toInt();
}
if (sbuffer.substring(0,2) == "fs" ) { // this is actually the done temp
fan_speed = sbuffer.substring(2,5).toInt();
}
if (sbuffer.substring(0,2) == "as" ) { // this is actually the done temp
auger_speed = sbuffer.substring(2,5).toInt();
}
if (sbuffer.substring(0,3) == "hs2") {
stage2_time_hours = sbuffer.substring(3,5).toInt();
}
if (sbuffer.substring(0,3) == "ms2") {
stage2_time_minutes = sbuffer.substring(3,5).toInt();
}
if (sbuffer.substring(0,2) == "s2") {
stage2 = sbuffer.substring(2,5).toInt();
if (stage2_set) {Setpoint = stage2;} // if we are in stage 2 and changes made via Blynk then change setpoint
}
if (sbuffer.substring(0,2) == "s1") {
stage1 = sbuffer.substring(2,5).toInt();
if (!stage2_set) {Setpoint = stage1;} // if we are in stage one set the pit to stage1 temp from Blynk routine
}
if (sbuffer.substring(0,3) == "hs1") {
stage1_time_hours = sbuffer.substring(3,5).toInt();
}
if (sbuffer.substring(0,3) == "ms1") {
stage1_time_minutes = sbuffer.substring(3,5).toInt();
}
if (sbuffer.substring(0,2) == "ao") {
ambient_offset = sbuffer.substring(2,5).toInt();
if (offset.aa != ambient_offset) {offset.aa = ambient_offset ;lcdw(13,2,"W");}
}
if (sbuffer.substring(0,2) == "fo") {
food_offset = sbuffer.substring(2,5).toInt();
if (offset.af != food_offset) {offset.af = food_offset ;lcdw(13,2,"W");}
}
if (sbuffer.substring(0,2) == "to") {
temp_offset = sbuffer.substring(2,5).toInt();
if (offset.ap != temp_offset) {offset.ap = temp_offset ;lcdw(13,2,"W");}
}
if (sbuffer.substring(0,2) == "ht") {
hold_temp = sbuffer.substring(2,5).toInt();
}
if (sbuffer.substring(0, 5) == "test" ) {testmode = !testmode;clear_lcd();}
if (sbuffer.substring(0, 5) == "twopb") {twoprobes = !twoprobes;clear_lcd();}
if (sbuffer.substring(0, 5) == "pidos") {pid_only = !pid_only;clear_lcd();} //switch pid_only on/off
if (sbuffer.substring(0, 5) == "piddr") {};
if (sbuffer.substring(0, 5) == "reset") {software_Reset();} // reset arduino
if (sbuffer.substring(0, 5) == "clear") {clear_lcd();}
if (sbuffer.substring(0, 5) == "nolit") {lcd.noBacklight();}
if (sbuffer.substring(0, 5) == "blite") {lcd.backlight();}
if (sbuffer.substring(0, 5) == "igoff") {igniter_off();}
if (sbuffer.substring(0, 5) == "igon") {igniter_on();}
if (sbuffer.substring(0, 5) == "hitgo") {sw_start();} // start the pit. Like hitting the GO button.
if (sbuffer.substring(0, 5) == "swoff") {sw_stop();} //
if (sbuffer.substring(0, 5) == "swcdn") {sw_cool();}
if (sbuffer.substring(0, 5) == "fcton") {auger_mode = 3;}
if (sbuffer.substring(0, 5) == "graph") {graph_data = !graph_data;} // go into graph mode for data
if (sbuffer.substring(0, 5) == "sprdu") {if (auger_spread < .075) {auger_spread += .002;}} //change min_max auger spread
if (sbuffer.substring(0, 5) == "sprdd") {if (auger_spread > .025) {auger_spread -= .002; }}
if (sbuffer.substring(0, 5) == "fanon") {fan_only();testmode = false;} // turn on fan only mode
if (sbuffer.substring(0, 5) == "fanof") {cooldown_startmode = false;cook_mode = true;clear_lcd();fan_mode=false;
auger_mode=0;auger_off ();} // turn fan only mode off
if (sbuffer.substring(0, 5) == "rtcon") {rtc_installed = true;} // trun on rtc routines
if (sbuffer.substring(0, 5) == "rtcof") {rtc_installed = false;} // trun off rtc routines. use arduino clock
if (sbuffer.substring(0, 5) == "zrtc" ) {zero_rtc_clock();} // zero the rtc clock
if (sbuffer.substring(0, 5) == "wmnmx") {write_mn_mx();
lcd.setCursor(13,2);
lcd.print("W");
} // write min and max auger and spread and current set pit temp to eeprom.
if (sbuffer.substring(0, 4) == "wpid") {write_pid();lcdw(13,2,"W");}
if (sbuffer.substring(0, 4) == "woff") {write_off();lcdw(13,2,"W");}
if (sbuffer.substring(0, 5) == "fixee") {fix_eeprom();}
if (sbuffer.substring(0, 5) == "msset") { multistage_setup();}
if (sbuffer.substring(0, 5) == "seria") {show_serial_num();} // shows serial # and Version # on lcd screen
// if (sbuffer.substring(0, 5) == "probe1") {probe1 = !probe1;} // probe 1 - do not allow to turn off
if (sbuffer.substring(0, 5) == "2prob") {probe2 = !probe2;probes.b = probe2;}
if (sbuffer.substring(0, 5) == "3prob") {probe3 = !probe3;probes.c = probe3;} // first food probe
if (sbuffer.substring(0, 5) == "4prob") {probe4 = !probe4;probes.d = probe4;}
if (sbuffer.substring(0, 5) == "smok1") {
set_smoke_false();
smoke_mode1 = true;
lcdw(8,3,"S1");}
if (sbuffer.substring(0, 5) == "smok2") {
set_smoke_false();
smoke_mode2 = true;
lcdw(8,3,"S2");}
if (sbuffer.substring(0, 5) == "smok3") {
set_smoke_false();
smoke_mode3 = true;
lcdw(8,3,"S3");}
if (sbuffer.substring(0, 5) == "smok4") {
set_smoke_false();
smoke_mode4 = true;
lcdw(8,3,"S4");}
if (sbuffer.substring(0, 5) == "smok5") {
set_smoke_false();
smoke_mode5=true;
lcdw(8,3,"S5");}
if (sbuffer.substring(0, 5) == "smok6") {
set_smoke_false();
smoke_mode6=true;
lcdw(8,3,"S6");
}
if (sbuffer.substring(0, 5) == "smok7") {
set_smoke_false();
smoke_mode7=true;
lcdw(8,3,"S7");
}
if (sbuffer.substring(0, 5) == "pidom") { // Set PID mode to Proportional on Measurement/normal have on onError
ponm = true;
myPID.SetTunings(consKp, consKi, consKd, P_ON_M);
//set_PID() ;
if (testmode) {lcdw(8,3,"POM");}
}
if (sbuffer.substring(0, 5) == "pidoe") { // Set PID mode to Proportional on Measurement/normal have on onError
ponm = false;
myPID.SetTunings(consKp, consKi, consKd, P_ON_E);
//set_PID();
if (testmode) {lcdw(8,3,"POE");}
}
} //Serial.available
} //readserial
void set_smoke_false() {
smoke_mode1 = false;
smoke_mode2 = false;
smoke_mode3 = false;
smoke_mode4 = false;
smoke_mode5 = false;
smoke_mode6 = false;
smoke_mode7 = false;
}
void show_stages() {
if (hits < mcycletime ) // we are within cycletime
{
if (stage1 != stage2)
{
if (int(hits) == 1) {
lcdw(0,2,"S1=");
}
if (int(hits) == 2) {
if (stage1 < 100) {lcdw(0,2," "+String(stage1));
} else {lcdw(0,2,String(stage1));}
}
if (int(hits) == 3) {
lcdw(0,2,"Hr=");
}
if (int(hits) == 4) {
if (stage1_time_hours <10 ) {lcdw(0,2," "+String(stage1_time_hours));}
if (stage1_time_hours >10 ) {lcdw(0,2," "+String(stage1_time_hours));}
}
//////////////////
if (int(hits) == 5) {
lcdw(0,2,"S2=");
}
if (int(hits) == 6) {
lcd.setCursor(0, 2);
if (stage2 < 100) {
lcd.print(" ");lcd.print(stage2);
} else {lcd.print(stage2);}
}
if (int(hits) > 7) {
if (int(Setpoint < 100 )) {
lcdw(0,2," "+String(int(Setpoint)));
} else {
lcdw(0,2,String(int(Setpoint)));
}
}
if (int(hits) == 1) {lcdw(4,2,"Hold ");}
if (int(hits) == 2) {lcdw(4,2," @ ");}
if (int(hits) == 3) {lcdw(4,2,String(hold_temp));}
if (int(hits) == 5) {lcdw(4,2,"Done ");}
if (int(hits) == 6) {lcdw(4,2," @ ");}
if (int(hits) >= 7 && int(hits) <=18) {lcdw(4,2,String(food_done_temp,0));}
if (int(hits) >18 ) {if(useprofiles) {lcdw(4,2,what_profile);}}
lcd.setCursor(14, 2); lcd.print("3s");
if ((rtc_minute >= stage1_time_minutes && rtc_hour >= stage1_time_hours )
|| ( minutes>= stage1_time_minutes && hours >= stage1_time_hours ))
{
// we are in stage 2 of cook so change to stage 2 temp
if (! stage2_set ) {
Setpoint = stage2; // only change the setpoint if it has not already been done
stage2_set = true;
}
if (hold_temp_mode) {
Setpoint = hold_temp;
food_done_mode = true;
lcdw(8,3,"Hold");
}
// 10/15/2016
if (! pid_only) {
calibrate_auger_percent();
} // don't really need this here.
//// calibrate_auger_percent(); // removed 7/21/2019
if (food_done_mode) {
lcdw(4,3,"Done ");
}
} else
{ if (food_done_mode) {
lcdw(4,3,"Done ");
}
}
} else // its only a single stage cook
{ lcdw(14,2,"1s"); // stage1
if (int(Setpoint < 100 )) {lcdw(0,2," "+String(int(Setpoint)));
}
else {
lcdw(0,2,String(int(Setpoint)));
}
} // else stage1
} // Realseconds<mcycletime
} //show_stages
void software_Reset() // Restarts program from beginning but does not reset the peripherals and registers
{
auger_off();
igniter_off();
fan_off();
asm volatile (" jmp 0");
}
void set_stage1_pit() {
buttonControl();
clear_lcd();
}
void set_stage2_pit() { }
void set_stage1_hr() { }
void set_stage1_min() { }
//void set_stage2_pit() { }
void set_stage2_hr() { }
void set_stage2_min() { }
void reset_autotune()
{
min_best = 0.00;
max_best = 0.00;
autotune_complete = false;
}
void sw_start () { //start system in single stage mode
if (!cook_mode && !cooldown_startmode ) {
cook_mode = !cook_mode; // switch cooking on
if (rtc_installed) {zero_rtc_clock();}
clear_lcd();
}
}
void sw_stop () { // stop system without cool down.
clear_lcd();
cooldown_startmode = false;
cook_mode = false;
}
void sw_cool () { // stop system with cool down.
clear_lcd();
cooldown_startmode = true;
}
void fan_off () {
digitalWrite(fan_pin, LOW); // erase F on screen not on
lcdw(18,2," ");
}
void fan_on () {
digitalWrite(fan_pin, HIGH); // erase F on screen not on
lcdw(18,2,"F");
}
void auger_off () {
digitalWrite(auger_pin, LOW); // turn off auger
if ( !cooldown_startmode ) {lcdw(14,0," ");}
}
void auger_on () {
digitalWrite(auger_pin, HIGH); // turn on auger
lcdw(14,0," Auger");
}
void igniter_off () {
digitalWrite(igniter_pin, LOW);
lcdw(17,2," ");
}
void igniter_on () {
digitalWrite(igniter_pin, HIGH);
lcdw(17,2,"I");
}
void keypadpress() {
// switch (myKeypad.getState())
char(switch1) (myKeypad.getState());
switch (char(mkeypress))
{
case '*': // this will be the mode switch
break;
} //switch
} // keypadpress
void keypadcontrol() {
char keypressed = myKeypad.getKey();
if (keypressed != NO_KEY)
{
// mkeypress = int(keypressed);
mkeypress = keypressed;
keypadpress();
}
}
void fan_only () {
igniter_off();
auger_off ();
fan_mode=true;
auger_mode = 1; // switch to manual mode and set manual mm_timer to 0 so zero auger time.
mm_timer=0.00;
clear_lcd();
}
void zero_start_time () {
start_time = 0;
}
void zero_rtc_clock () {
DateTime now = rtc.now();
rtc_year = (now.year());
rtc_month = now.month(), DEC;
rtc_day = now.date(), DEC;
o_rtc_day=now.date(),DEC;
o_rtc_hour=now.hour(), DEC;
o_rtc_minute=now.minute(), DEC;
o_rtc_second=now.second(), DEC;
rtc_hour = 0;
rtc_minute = 0;
rtc_second = 00;
rtc_dow = now.dayOfWeek();
hours=0;minutes=0; // also zero the system clock. NEVER ZERO Seconds
// DateTime dt(2011, 11, 20, 20, 14, 0, 5);
DateTime dt(2016, rtc_month, rtc_day, rtc_hour, rtc_minute, 0, rtc_dow);
rtc.setDateTime(dt);
}
void multistage_setup () {
/// set up 2 stage cooking
// Lets turn off the fan and auger during this setup.
if (meat_menu = 0) {}
fan_off (); // turn off fan
auger_off (); // turn off auger
igniter_off();
int stage_set = 1;
mkeypress=' ';
ms_setup=true;
clear_lcd();
if (cooldown_startmode) { // can't set multi stage cook in cool down mode.
lcdw(0,0," In cooldown mode. ");
lcdw(0,1,"Restart is required.");
lcdw(0,2," Wait for cooldown. ");
lcdw(0,3," Then Try Again ");
delay(4000);
ms_setup=false;
stage_set = 10;
clear_lcd();
}else {
if (!cooldown_startmode) { // can't set multi stage cook in cool down mode.
setup_mode = true;
lcdw(0,0," Adjust Pit Temp ");
lcdw(0,1," For 3 Stage Cook ");
lcdw(0,2," Then hit MS ");
lcdw(0,3," to Save each line ");
delay(6000); // wait 6 seconds
clear_lcd();
lcd.setCursor(0, 0);
//lcd.print("Pit Stage 1 = "); lcd.print(profile1[0]); lcd.print(" ");
lcd.print("Pit Stage 1 = "); lcd.print(foo.aa); lcd.print(" ");
lcdw(0,3,"Press MS to Save");
lcd.blink();
// stage 1 temp
long tsecs = millis()+20000; // you have 20 seconds to push a key or program continues.
//while (stage_set == 1 || millis()+20000> tsecs) {
while (millis()< tsecs && stage_set == 1) {
keypadcontrol();
if (mkeypress == '#') {stage_set = 10;mkeypress = ' ';}
if (mkeypress == '2') { // raise temp
tsecs = millis()+20000;
if (foo.aa > 150) {
foo.aa -= 5;
}
mkeypress=' ';
}
if (mkeypress == '3') { // lower temp
tsecs = millis()+20000;
if (foo.aa < 500) {
foo.aa += 5;
}
mkeypress=' ';
}
lcd.setCursor(14, 0);
lcd.blink();lcd.print(foo.aa); lcd.print(" ");
if (mkeypress == '1') { // ms button
tsecs = millis()+20000;
// Setpoint = foo.aa;
Setpoint = foo.aa;
//stage1 = profile1[0];
mkeypress=' ';
stage_set = 2;
}
mkeypress=' ';
}
// stage 1 Hours
while (stage_set == 2) {
keypadcontrol();
if (mkeypress == '#') {stage_set = 10;mkeypress = ' ';}
if (mkeypress == '2') {
if (foo.ab > 0) {
foo.ab -= 1;
}
mkeypress=' ';
}
if (mkeypress == '3') {
if (foo.ab < 24) {
foo.ab += 1;
}
mkeypress=' ';
}
lcdw(0,1,"Hours = "+String(foo.ab)+" ");
if (mkeypress == '1') {
stage1_time_hours = foo.ab;
stage_set = 3;
mkeypress=' ';
}
mkeypress=' ';
}
// stage 1 Minutes
while (stage_set == 3) {
keypadcontrol();
if (mkeypress == '#') {stage_set = 10;mkeypress = ' ';}
if (mkeypress == '2') {
if (foo.ac > 0) {
foo.ac -= 1;
mkeypress=' ';
}
}
if (mkeypress == '3') {
if (foo.ac < 60) {
foo.ac += 1;
mkeypress = ' ';
}
}
lcdw(0,2,"Minutes = "+String(foo.ac)+" ");
if (mkeypress == '1') {
stage1_time_minutes = foo.ac;
mkeypress = ' ';
stage_set = 4;
}
}
// stage 2 temp
clear_lcd();
while (stage_set == 4) {
keypadcontrol();
if (mkeypress == '#') {stage_set = 10;mkeypress = ' ';}
lcdw(0,0,"Pit Stage 2 = "+String(foo.ad)+" ");
lcdw(0,3,"Press MS to Save");
if (mkeypress == '2') {
if (foo.ad > 150) {
foo.ad -= 5;
mkeypress = ' ';
}
}
if (mkeypress == '3') {
if (foo.ad < 500) {
foo.ad += 5;
mkeypress = ' ';
}
}
lcdw(14,0,String(foo.ad)+" ");
if (mkeypress == '1') {
stage2 = foo.ad;
stage_set = 5;
mkeypress = ' ';
}
useprofiles = true;
}
// stage 2 Hours
while (stage_set == 5) {
keypadcontrol();
if (mkeypress == '#') {stage_set = 10;mkeypress = ' ';}
if (mkeypress == '2') {
if (foo.ae > -0) {
foo.ae -= 1;
mkeypress = ' ';
}
}
if (mkeypress == '3') {
if (foo.ae < 24) {
foo.ae += 1;
mkeypress = ' ';
}
}
lcdw(0,1,"Hours = "+String(foo.ae)+" ");
if (mkeypress == '1') {
stage2_time_hours = foo.ae;
stage_set = 6;
mkeypress = ' ';
}
}
// stage 2 Minutes
while (stage_set == 6) {
keypadcontrol();
if (mkeypress == '#') {stage_set = 10;mkeypress = ' ';}
if (mkeypress == '2') {
if (foo.af > -0) {
foo.af -= 1;
mkeypress = ' ';
}
}
if (mkeypress == '3') {
if (foo.ag < 60) {
foo.af += 1;
mkeypress = ' ';
}
}
lcdw(0,2,"Minutes = "+String(foo.af)+" ");
if (mkeypress == '1') {
stage2_time_minutes = foo.af;
stage_set = 7;
mkeypress = ' ';
}
}
// Food Done @
clear_lcd();
while (stage_set == 7) {
keypadcontrol();
if (mkeypress == '#') {stage_set = 10;mkeypress = ' ';}
if (mkeypress == '2') {
if (foo.ag > 100) {
foo.ag -= 1;
mkeypress = ' ';
}
}
if (mkeypress == '3') {
if (foo.ag < 350) {
foo.ag += 1;
mkeypress = ' ';
}
}
lcdw(0,0,"Food Done @ "+String(foo.ag)+" ");
lcdw(0,3,"Press MS to Save");
if (mkeypress == '1') {
food_done_temp = foo.ag;
stage_set = 8;
mkeypress = ' ';
}
}
// Hold Temp
lcd.setCursor(0, 1);
while (stage_set == 8) {
keypadcontrol();
if (mkeypress == '#') {stage_set = 10;mkeypress = ' ';}
if (mkeypress == '2') {
if (foo.ah > 100) {
foo.ah -= 1;
mkeypress = ' ';
}
}
if (mkeypress == '3') {
if (foo.ah < 250) {
foo.ah += 1;
mkeypress = ' ';
}
}
lcdw(0,1,"Food Hold @ "+String(foo.ah)+" ");
if (mkeypress == '1') {
hold_temp = foo.ah;
stage_set = 9;
mkeypress = ' ';
}
}
// tell user all settings
clear_lcd();
while (stage_set == 9) {
keypadcontrol();
lcdw(0,0,"Settings Are :");
lcdw(0,1,"S1 Pit="+String(foo.aa,0)+" H="+String(foo.ab,0)+" M="+String(foo.ac,0));
lcdw(0,2,"S2 Pit="+String(foo.ad,0)+" H="+String(foo.ae,0)+" M="+String(foo.af,0));
lcdw(0,3,"Done@ " +String(foo.ag,0)+" Hold@ "+String(foo.ah,0));
if (mkeypress == '#') { // GO button
if(rtc_installed) {zero_rtc_clock();}
stage_set = 10;
mkeypress = ' ';
}
}
if (rtc_installed) {zero_rtc_clock();}
clear_lcd();
Setpoint = foo.aa;
igniter_timer = 25;
setup_mode = false;
cook_mode = true;
ms_setup=false; // get out of setup mode.
} // not cooldown
}
}
// Taking care of some special events.
void keypadEvent(KeypadEvent key){
if (ms_setup) { return;}
switch (myKeypad.getState()){
case PRESSED:
if (key == '#') { // go button
if (!cook_mode && !cooldown_startmode ) {
cook_mode = !cook_mode; // switch cooking on
if (rtc_installed) {zero_rtc_clock();} else {start_time=millis();}
clear_lcd();
} else if ( cook_mode && !cooldown_startmode) {
cooldown_startmode = !cooldown_startmode; // switch cooldown on
testmode = false;
igniter_off();
cooldown_minutes = cooldown_count; // lets start at 15 and count down.
clear_lcd();
if (rtc_hour+o_rtc_hour >= 24) {}
else {}
} else if ( cook_mode && cooldown_startmode) {
software_Reset(); // reset arduino to start fresh again. like powerup
}
}
//===========================================
if(key == '4' && key == '1') { // dual key pressed
//testmode = !testmode;
clear_lcd();
lcd.print("key 4 & 1 Pressed");
delay(2000);
}
//===========================================
if(key == '4') { //
if (!cook_mode && !cooldown_startmode ) {
show_serial_num();
}else{
testmode = !testmode;
clear_lcd();
}
}
//===========================================
if (key == '3') { //up pit temp
if (Setpoint < 500) {
Setpoint += 5;
temp_changed = true;
hold_temp_mode = false;
}
}
//===========================================
// lower pit temp // down pit temp
if (key == '2') {
if (Setpoint > 150) {
Setpoint -= 5;
temp_changed = true;
hold_temp_mode = false;
}
}
//===========================================
if (key == '6') { // raise food done temp
if (food_done_temp < 250) {
food_done_temp += 1; // add 5 to food_done_temp
}
}
//===========================================
if (key == '5') { // lower food done temp
if (food_done_temp > 150) {
food_done_temp -= 1; // subtract 5 to food_done_temp
}
}
//===========================================
if (key == '7') { // mode change from Auto to Manual ... flip flop
if (auger_mode == 0) {
auger_mode = 1;
} else {
auger_mode = 0;
}
}
//===========================================
if (key == '1') { // set up multi stage cook
// multistage_setup();
select_meat ();
}
//break;
//===========================================
if (key =='9') { // increase cycle timer
{
// maximum cycletime is 120 seconds
if (auger_mode == 1) {
if (mm_timer < mcycletime) {mm_timer += .25;} // max 100%
} else {
if (mcycletime > 120) {
mcycletime = 120; // add 5 second to cycletime
} else {
mcycletime += 5;
}
}
}
}
///////////////////////////////////////////////////////
if (key == '8') { // decrease cycle timer
{ // Minimum cycle time 10 seconds
if (auger_mode == 1) {
if (mm_timer > 0) {mm_timer -= .25;}
} else {
if (mcycletime < 10) {
mcycletime = 10;
} else {
mcycletime -= 5; // subtract 5 second from cycletime
}
}
}
}
//////////////////////////////////////////////////////
break;
case RELEASED:
if (key == '*') {
// mod_mode = !mod_mode;
mod_mode = true;
select_meat();
}
break;
case HOLD:
if (key == '0') { // switch back and forth between 2 probe and 4 probe screen mode
twoprobes = !twoprobes;
clear_lcd();
}
///////////////////////////////////////
if (key == '4') { // go into fan only mode
if ( ! fan_mode) {fan_only();testmode = false; }
else if (fan_mode) {
cooldown_startmode = false;
cook_mode = true;clear_lcd();
fan_mode=false;
auger_mode=0;}
lcd.noBlink();
}
///////////////////////////////////////
if (key == '3') { // add 20 to pit temp
if (Setpoint < 500) {
Setpoint += 15; // if holding down the key add another 15
temp_changed = true;
hold_temp_mode = false;
}
lcd.noBlink();
}
///////////////////////////////////////
if (key == '2') { // subtract 20 from pit temp
if (Setpoint > 150) {
Setpoint -= 15; // if holding down the key add another 5
temp_changed = true;
hold_temp_mode = false;
}
lcd.noBlink();
}
///////////////////////////////////////////////
// maximum cycletime is 120 seconds
if (key == '9') {
if (auger_mode == 1) {
if (mm_timer < mcycletime) {mm_timer += .75;} // max 100%
} else {
if (mcycletime > 120) {
mcycletime = 120; // add 5 second to cycletime
} else {
mcycletime += 15;
}
}
}
////////////////////////////////////////////
// decrease cycle timer
// Minimum cycle time 10 seconds
if (key == '8'){
if (auger_mode == 1) {
if (mm_timer > 0) {mm_timer -= .75;}
} else {
if (mcycletime < 10) {
mcycletime = 10;
} else {
mcycletime -= 15; // subtract 5 second from cycletime
}
}
}
break;
} //switch
}//keypadevent
void write_mn_mx (){
aminmax.mn = min_auger_base,3;aminmax.mx = max_auger_base,3;aminmax.sp = auger_spread;aminmax.pt=Setpoint;
lcd.setCursor(13,2);
lcd.print("W");
}
void write_pid () { // write the pid values to the eeprom
pid.p = consKp; pid.i = consKi; pid.d = consKd;
lcdw(13,2,"W");
}
void write_off () { // write the offset values to the eeprom
offset.ap = temp_offset;
offset.aa = ambient_offset;
offset.af = food_offset;
lcdw(13,2,"W");
}
void select_meat () {
fan_off (); // turn off fan
auger_off (); // turn off auger
igniter_off();
int stage_set = 1;
mkeypress=' ';
lcd.clear();
lcdw(0,0," Pork ");if (mod_mode) {lcdw(8,0," Change Mode");}
lcdw(0,1,"Brisket ");
lcdw(0,2," Foul ");
lcdw(0,3,"Dn=Scroll,Up=Select");
lcd.setCursor(7,0);
lcd.blink();
int mrow = 0;
long tsecs = millis()+20000; // you have 20 seconds to push a key or program continues.
while (stage_set == 1 ) {
// while (millis()< tsecs && stage_set == 1) { // 1 or ms button
keypadcontrol();
if (mkeypress == '3') {// Up arrow pushed.
useprofiles = true;
mkeypress = ' ';
lcd.noBlink();
lcd.clear();
if ( mrow == 0 ) {what_profile = "Pork";
if( mod_mode ) {lcdw(0,0,"Edit Pork Profile");setup_profiles(0);}
else {lcdw(0,0," Pork Profile On");
load_profile (1);
}
// if (mod_mode) {setup_profiles(0);} // if in modify mode go into pork setup
//
delay(2000);
lcd.clear();
// stage2_set = true; // tell system you are running 3 stage prog
stage_set=2;
if (rtc_installed) {zero_rtc_clock();} else {hours=0;minutes=0;auger_off();} // restart cook clocks. Don't reset seconds please.
}
if ( mrow == 1 ) {what_profile = "Brisk";
if( mod_mode ) {lcdw(0,0,"Edit Brisket Profile");setup_profiles(1);}
else {lcdw(0,0,"Brisket Profile On");
load_profile (2);
}
delay(2000);
lcd.clear();
stage_set=2;
if (rtc_installed) {zero_rtc_clock();} else {hours=0;minutes=0;auger_off();} // restart cook clocks. Don't reset seconds please.
}
if ( mrow == 2 ) {what_profile = "Foul";
if( mod_mode ) {lcdw(0,0,"Edit Foul Profile");setup_profiles(2);}
else {lcdw(0,0," Foul Profile On");
load_profile (3);
}
delay(2000);
lcd.clear();
stage_set=2;
if (rtc_installed) {zero_rtc_clock();} else {hours=0;minutes=0;auger_off();} // restart cook clocks. Don't reset seconds please.
}
}
/////////////////////////////////
if ( mkeypress == '2') {
if(mrow>=2){mrow = 0;lcd.setCursor(7,mrow);}else{lcd.setCursor(7,mrow += 1);}
meat_menu=mrow;
if (mod_mode && mrow == 0 ) { lcdw(8,2," ");lcdw(9,mrow,"Change Mode");lcdw(7,mrow,"");}
if (mod_mode && mrow == 1 ) {lcdw(8,mrow-1," ");lcdw(9,mrow,"Change Mode");lcdw(7,mrow,"");}
if (mod_mode && mrow == 2 ) {lcdw(8,mrow-1," ");lcdw(9,mrow,"Change Mode");lcdw(7,mrow,"");}
mkeypress = ' ';
}
if (mkeypress == '#'){ // bail out
setup_mode = false;
cooldown_startmode = false;
cook_mode = true;
ms_setup=false; // get out of setup mode.
stage_set = 2;
mkeypress = ' ';
}
igniter_timer = 25;
setup_mode = false;
cook_mode = true;
ms_setup=false; // get out of setup mode.
} // stage_set 1
lcd.noBlink();
} //select meat
int setup_profiles (int meat_val) {
fan_off (); // turn off fan
auger_off (); // turn off auger
igniter_off();
int stage_set = 1;
mkeypress=' ';
ms_setup=true;
clear_lcd();
lcd.blink();
///////////
if (meat_val == 0) {lcd.print("Pork Settings:");lcd.print(meat_val);}
if (meat_val == 1) {lcd.print("Brisket Settings:");lcd.print(meat_val);}
if (meat_val == 2) {lcd.print(" Foul Settings:");lcd.print(meat_val);}
lcd.setCursor(0, 1);
if (meat_val == 0) {lcd.print("Stage 1 Pit = "); lcd.print(prof1.aa,0);}
if (meat_val == 1) {lcd.print("Stage 1 Pit = "); lcd.print(prof2.aa,0);}
if (meat_val == 2) {lcd.print("Stage 1 Pit = "); lcd.print(prof3.aa,0);}
lcd.setCursor(0,3);lcd.print("Up/Dn Change,MS=Save");
/////////// stage 1
while (stage_set == 1) {
keypadcontrol();
delay(20);
///////////////////////////////////////////////
lcd.setCursor(14,1);
//lcd.blink();
if (mkeypress == '3') { // Raise temp
if (meat_val == 0 ){
if (prof1.aa < 500) {prof1.aa += 5;lcdw(14,1,String(prof1.aa,0));}
}
if (meat_val == 1) {
if (prof2.aa < 500) {prof2.aa += 5;lcd.print(prof2.aa,0);}
}
if (meat_val == 2) {
if (prof3.aa < 500) {prof3.aa += 5;lcd.print(prof3.aa,0);}
}
mkeypress=' ';
delay(100);
}
if (mkeypress == '2') { // Lower temp
if (meat_val == 0) {
if (prof1.aa > 150) {prof1.aa -= 5;lcd.print(prof1.aa,0);}
}
if (meat_val == 1) {
if (prof2.aa > 150) {prof2.aa -= 5;lcd.print(prof2.aa,0);}
}
if (meat_val == 2) {
if (prof3.aa > 150) {prof3.aa -= 5;lcd.print(prof3.aa,0);}
}
mkeypress=' ';
delay(100);
}
//////////////////////////////////////////////
if (mkeypress == '1') { // ms button
//tsecs = millis()+20000;
///lcd.setCursor(7,1);
// offset fix
if (meat_val == 0) {Setpoint = prof1.aa;stage1 = prof1.aa;}
if (meat_val == 1) {Setpoint = prof2.aa;stage1 = prof2.aa;}
if (meat_val == 2) {Setpoint = prof3.aa;stage1 = prof3.aa;}
mkeypress=' ';
lcd.clear();
stage_set = 2;
}
//////////////////////////////////////////////
if (mkeypress == '#') { // GO button
stage_set = 10;
mkeypress = ' ';
}
} // stage_set 1
///////////// stage 2
if (meat_val == 0){lcd.print("Pork Settings :");}
if (meat_val == 1){lcd.print("Brisket Settings :");}
if (meat_val == 2){lcd.print(" Foul Settings :");}
lcd.setCursor(0, 1);
lcd.print("Stage 1 Hr = ");
if (meat_val == 0) {
if (prof1.ab < 10 ) {lcd.setCursor(14,1); lcd.print(prof1.ab,0);lcd.print(" ");}
else {lcd.setCursor(14,1); lcd.print(prof1.ab,0);}}
if (meat_val == 1) {
if (prof1.ab < 10 ) {lcd.setCursor(14,1); lcd.print(prof2.ab,0);lcd.print(" ");}
else {lcd.setCursor(14,1); lcd.print(prof2.ab,0);}}
if (meat_val == 2) {
if (prof1.ab < 10 ) {lcd.setCursor(14,1); lcd.print(prof3.ab,0);lcd.print(" ");}
else {lcd.setCursor(14,1); lcd.print(prof3.ab,0);}}
lcd.setCursor(0,3);lcd.print("Up/Dn Change,MS=Save");
while (stage_set == 2) {
keypadcontrol();
lcd.setCursor(14,1);lcd.print(" ");lcd.setCursor(14,1);
if (meat_val == 0){lcd.print(prof1.ab,0);}
if (meat_val == 1){lcd.print(prof2.ab,0);}
if (meat_val == 2){lcd.print(prof3.ab,0);}
delay(20);
if (mkeypress == '3') { // Raise hours
if (meat_val == 0){if (prof1.ab < 24) {prof1.ab += 1;}}
if (meat_val == 1){if (prof2.ab < 24) {prof2.ab += 1;}}
if (meat_val == 2){if (prof3.ab < 24) {prof3.ab += 1;}}
mkeypress=' ';
delay(100);
}
if (mkeypress == '2') { // Lower hours
if (meat_val == 0){if (prof1.ab > 0) {prof1.ab -= 1;}}
if (meat_val == 1){if (prof2.ab > 0) {prof2.ab -= 1;}}
if (meat_val == 2){if (prof3.ab > 0) {prof3.ab -= 1;}}
mkeypress=' ';
delay(100);
}
//////////////////////////////////////////////
if (mkeypress == '1') { // ms button
//tsecs = millis()+20000;
mkeypress=' ';
if (meat_val == 0) {stage1_time_hours = prof1.ab;}
if (meat_val == 1) {stage1_time_hours = prof2.ab;}
if (meat_val == 2) {stage1_time_hours = prof3.ab;}
lcd.clear();
stage_set = 3;
}
if (mkeypress == '#') { // GO button
// save all new settings
if (meat_val == 0) {foo.aa = prof1.aa;foo.ab = prof1.ab;}
if (meat_val == 1) {foo.aa = prof2.aa;foo.ab = prof2.ab;}
if (meat_val == 2) {foo.aa = prof3.aa;foo.ab = prof3.ab;}
stage_set = 10;
mkeypress = ' ';
}
} // stage 2
///////////// stage 3
lcd.clear();
if (meat_val == 0) {lcd.print("Pork Settings :");}
if (meat_val == 1) {lcd.print("Brisket Settings :");}
if (meat_val == 2) {lcd.print(" Foul Settings :");}
lcd.setCursor(0, 1);
lcd.print("Stage 1 Min = ");
if (meat_val == 0) {
if (prof1.ac < 10 ) {lcd.setCursor(14,1); lcd.print(prof1.ac,0);lcd.print(" ");}
else {lcd.setCursor(14,1); lcd.print(prof1.ac,0);}
}
if (meat_val == 1) {
if (prof2.ac < 10 ) {lcd.setCursor(14,1); lcd.print(prof2.ac,0);lcd.print(" ");}
else {lcd.setCursor(14,1); lcd.print(prof2.ac,0);}
}
if (meat_val == 2) {
if (prof3.ac < 10 ) {lcd.setCursor(14,1); lcd.print(prof3.ac,0);lcd.print(" ");}
else {lcd.setCursor(14,1); lcd.print(prof3.ac,0);}
}
lcd.setCursor(0,3);lcd.print("Up/Dn Change,MS=Save");
while (stage_set == 3) {
keypadcontrol();
lcd.setCursor(14,1);lcd.print(" ");lcd.setCursor(14,1);
if (meat_val == 0) {lcd.print(prof1.ac,0); }
if (meat_val == 1) {lcd.print(prof2.ac,0); }
if (meat_val == 2) {lcd.print(prof3.ac,0); }
delay(20);
if (mkeypress == '3') { // Raise minutes
if (meat_val == 0) {if (prof1.ac < 60) {lcd.noBlink();prof1.ac += 1;}}
if (meat_val == 1) {if (prof2.ac < 60) {lcd.noBlink();prof2.ac += 1;}}
if (meat_val == 2) {if (prof3.ac < 60) {lcd.noBlink();prof3.ac += 1;}}
mkeypress=' ';
delay(100);
}
if (mkeypress == '2') { // Lower minutes
if (meat_val == 0) {if (prof1.ac > 0) {lcd.noBlink();prof1.ac -= 1;}}
if (meat_val == 1) {if (prof2.ac > 0) {lcd.noBlink();prof2.ac -= 1;}}
if (meat_val == 2) {if (prof3.ac > 0) {lcd.noBlink();prof3.ac -= 1;}}
mkeypress=' ';
delay(100);
}
//////////////////////////////////////////////
if (mkeypress == '1') { // ms button
//tsecs = millis()+20000;
if (meat_val == 0) {stage1_time_minutes = prof1.ac;}
if (meat_val == 1) {stage1_time_minutes = prof2.ac;}
if (meat_val == 2) {stage1_time_minutes = prof3.ac;}
mkeypress=' ';
lcd.clear();
stage_set = 4;
}
if (mkeypress == '#') { // GO button
// save all new settings
if (meat_val == 0) {foo.aa = prof1.aa;foo.ab = prof1.ab;foo.ac = prof1.ac;foo.ad = prof1.ad;}
if (meat_val == 1) {foo.aa = prof2.aa;foo.ab = prof2.ab;foo.ac = prof2.ac;foo.ad = prof2.ad;}
if (meat_val == 2) {foo.aa = prof3.aa;foo.ab = prof3.ab;foo.ac = prof3.ac;foo.ad = prof3.ad;}
stage_set = 10;
mkeypress = ' ';
}
} // stage 3
///////////// stage 4
lcd.clear();
if (meat_val == 0) {lcd.print("Pork Settings :");}
if (meat_val == 1) {lcd.print("Brisket Settings :");}
if (meat_val == 2) {lcd.print(" Foul Settings :");}
lcd.setCursor(0, 1);
lcd.print("Stage 2 Pit = ");
if (meat_val == 0) {lcd.print(prof1.ad,0);}
if (meat_val == 1) {lcd.print(prof2.ad,0);}
if (meat_val == 2) {lcd.print(prof3.ad,0);}
lcd.setCursor(0,3);lcd.print("Up/Dn Change,MS=Save");
while (stage_set == 4) {
keypadcontrol();
lcd.setCursor(14,1);lcd.print(" ");lcd.setCursor(14,1);
if (meat_val == 0) {lcd.print(prof1.ad,0);}
if (meat_val == 1) {lcd.print(prof2.ad,0);}
if (meat_val == 2) {lcd.print(prof3.ad,0);}
delay(100);
if (mkeypress == '3') { // Raise minutes
if(meat_val == 0) {if (prof1.ad < 500) {lcd.noBlink();prof1.ad += 5;}}
if(meat_val == 1) {if (prof2.ad < 500) {lcd.noBlink();prof2.ad += 5;}}
if(meat_val == 2) {if (prof3.ad < 500) {lcd.noBlink();prof3.ad += 5;}}
mkeypress=' ';
delay(100);
}
if (mkeypress == '2') { // Lower minutes
if(meat_val == 0) {if (prof1.ad > 0) {lcd.noBlink();prof1.ad -= 5;}}
if(meat_val == 1) {if (prof2.ad > 0) {lcd.noBlink();prof2.ad -= 5;}}
if(meat_val == 2) {if (prof3.ad > 0) {lcd.noBlink();prof3.ad -= 5;}}
mkeypress=' ';
delay(100);
}
//////////////////////////////////////////////
if (mkeypress == '1') { // ms button
//tsecs = millis()+20000;
if(meat_val == 0) {stage2 = prof1.ad;}
if(meat_val == 1) {stage2 = prof2.ad;}
if(meat_val == 2) {stage2 = prof3.ad;}
mkeypress=' ';
stage_set = 5;
}
if (mkeypress == '#') { // GO button
stage_set = 10;
mkeypress = ' ';
}
} // stage 4
///////////// stage 5
lcd.clear();
if (meat_val == 0 ) {lcd.print("Pork Settings :");}
if (meat_val == 1 ) {lcd.print("Brisket Settings :");}
if (meat_val == 2 ) {lcd.print(" Foul Settings :");}
lcd.setCursor(0, 1);
lcd.print("Stage 2 Hr = ");
if(meat_val == 0) {
if (prof1.ac < 10 ) {lcd.setCursor(14,1); lcd.print(prof1.ae,0);lcd.print(" ");}
else {lcd.setCursor(14,1); lcd.print(prof1.ae,0);}
}
if(meat_val == 1) {
if (prof2.ac < 10 ) {lcd.setCursor(14,1); lcd.print(prof2.ae,0);lcd.print(" ");}
else {lcd.setCursor(14,1); lcd.print(prof2.ae,0);}
}
if(meat_val == 2) {
if (prof3.ac < 10 ) {lcd.setCursor(14,1); lcd.print(prof3.ae,0);lcd.print(" ");}
else {lcd.setCursor(14,1); lcd.print(prof3.ae,0);}
}
lcd.setCursor(0,3);lcd.print("Up/Dn Change,MS=Save");
while (stage_set == 5) {
keypadcontrol();
lcd.setCursor(14,1);lcd.print(" ");lcd.setCursor(14,1);
if (meat_val == 0) {lcd.print(prof1.ae,0);}
if (meat_val == 1) {lcd.print(prof2.ae,0);}
if (meat_val == 2) {lcd.print(prof3.ae,0);}
delay(20);
if (mkeypress == '3') { // Raise minutes
if (meat_val == 0) {if (prof1.ae < 60) {lcd.noBlink();prof1.ae += 1;}}
if (meat_val == 1) {if (prof2.ae < 60) {lcd.noBlink();prof2.ae += 1;}}
if (meat_val == 2) {if (prof3.ae < 60) {lcd.noBlink();prof3.ae += 1;}}
mkeypress=' ';
delay(100);
}
if (mkeypress == '2') { // Lower minutes
if (meat_val == 0) {if (prof1.ae > 0) {lcd.noBlink();prof1.ae -= 1;}}
if (meat_val == 1) {if (prof2.ae > 0) {lcd.noBlink();prof2.ae -= 1;}}
if (meat_val == 2) {if (prof3.ae > 0) {lcd.noBlink();prof3.ae -= 1;}}
mkeypress=' ';
delay(100);
}
//////////////////////////////////////////////
if (mkeypress == '1') { // ms button
//tsecs = millis()+20000;
if (meat_val == 0 ) {stage2_time_hours = prof1.ae;}
if (meat_val == 1 ) {stage2_time_hours = prof2.ae;}
if (meat_val == 3 ) {stage2_time_hours = prof3.ae;}
mkeypress=' ';
lcd.clear();
stage_set = 6;
}
if (mkeypress == '#') { // GO button
// save all new settings
stage_set = 10;
mkeypress = ' ';
}
} // stage 5
///////////// stage 6
lcd.clear();
if (meat_val == 0 ) {lcd.print("Pork Settings :");}
if (meat_val == 1 ) {lcd.print("Brisket Settings :");}
if (meat_val == 2 ) {lcd.print(" Foul Settings :");}
lcd.setCursor(0, 1);
lcd.print("Stage 2 Min = ");
if (meat_val == 0 ){
if (prof1.ac < 10 ) {lcd.setCursor(14,1); lcd.print(prof1.af,0);lcd.print(" ");}
else {lcd.setCursor(14,1); lcd.print(prof1.af,0);}
}
if (meat_val == 1 ){
if (prof2.ac < 10 ) {lcd.setCursor(14,1); lcd.print(prof2.af,0);lcd.print(" ");}
else {lcd.setCursor(14,1); lcd.print(prof2.af,0);}
}
if (meat_val == 2 ){
if (prof3.ac < 10 ) {lcd.setCursor(14,1); lcd.print(prof3.af,0);lcd.print(" ");}
else {lcd.setCursor(14,1); lcd.print(prof3.af,0);}
}
lcd.setCursor(0,3);lcd.print("Up/Dn Change,MS=Save");
while (stage_set == 6) {
keypadcontrol();
lcd.setCursor(14,1);lcd.print(" ");lcd.setCursor(14,1);
if (meat_val == 0 ) {lcd.print(prof1.af,0);}
if (meat_val == 1 ) {lcd.print(prof2.af,0);}
if (meat_val == 2 ) {lcd.print(prof3.af,0);}
delay(20);
if (mkeypress == '3') { // Raise minutes
if (meat_val == 0 ) {if (prof1.af < 60) {lcd.noBlink();prof1.af += 1;}}
if (meat_val == 1 ) {if (prof2.af < 60) {lcd.noBlink();prof2.af += 1;}}
if (meat_val == 2 ) {if (prof3.af < 60) {lcd.noBlink();prof3.af += 1;}}
mkeypress=' ';
delay(100);
}
if (mkeypress == '2') { // Lower minutes
if (meat_val == 0 ) {if (prof1.af > 0) {lcd.noBlink();prof1.af -= 1;}}
if (meat_val == 1 ) {if (prof2.af > 0) {lcd.noBlink();prof2.af -= 1;}}
if (meat_val == 2 ) {if (prof3.af > 0) {lcd.noBlink();prof3.af -= 1;}}
mkeypress=' ';
delay(100);
}
//////////////////////////////////////////////
if (mkeypress == '1') { // ms button
//tsecs = millis()+20000;
///lcd.setCursor(7,1);
// Setpoint = prof1.aa;
// foo.af = prof1.af;
if (meat_val == 0 ) {stage2_time_minutes = prof1.af;}
if (meat_val == 1 ) {stage2_time_minutes = prof2.af;}
if (meat_val == 2 ) {stage2_time_minutes = prof3.af;}
mkeypress=' ';
lcd.clear();
stage_set = 7;
}
if (mkeypress == '#') { // GO button
// save all new settings
stage_set = 10;
mkeypress = ' ';
}
} // stage 6
///////////// stage 7
lcd.clear();
if (meat_val == 0 ) {lcd.print("Pork Settings :");}
if (meat_val == 1 ) {lcd.print("Brisket Settings :");}
if (meat_val == 2 ) {lcd.print(" Foul Settings :");}
lcd.setCursor(0, 1);
if (meat_val == 0 ) {lcd.print("Done Temp = "); lcd.print(prof1.ag,0);}
if (meat_val == 1 ) {lcd.print("Done Temp = "); lcd.print(prof2.ag,0);}
if (meat_val == 2 ) {lcd.print("Done Temp = "); lcd.print(prof3.ag,0);}
lcd.setCursor(0,3);lcd.print("Up/Dn Change,MS=Save");
while (stage_set == 7) {
keypadcontrol();
lcd.setCursor(14,1);lcd.print(" ");lcd.setCursor(14,1);
if (meat_val == 0) {lcd.print(prof1.ag,0);}
if (meat_val == 1) {lcd.print(prof2.ag,0);}
if (meat_val == 2) {lcd.print(prof3.ag,0);}
delay(20);
if (mkeypress == '3') { // Raise temp
if (meat_val == 0 ) {if (prof1.ag < 500) {lcd.noBlink();prof1.ag += 5;}}
if (meat_val == 1 ) {if (prof2.ag < 500) {lcd.noBlink();prof2.ag += 5;}}
if (meat_val == 2 ) {if (prof3.ag < 500) {lcd.noBlink();prof3.ag += 5;}}
mkeypress=' ';
delay(100);
}
if (mkeypress == '2') { // Lower temp
if (meat_val == 0 ) {if (prof1.ag > 0) {lcd.noBlink();prof1.ag -= 5;}}
if (meat_val == 1 ) {if (prof2.ag > 0) {lcd.noBlink();prof2.ag -= 5;}}
if (meat_val == 2 ) {if (prof3.ag > 0) {lcd.noBlink();prof3.ag -= 5;}}
mkeypress=' ';
delay(100);
}
//////////////////////////////////////////////
if (mkeypress == '1') { // ms button
//foo.ag = prof1.ag;
//show_profiles();
if (meat_val == 0 ) {food_done_temp = prof1.ag;}
if (meat_val == 1 ) {food_done_temp = prof2.ag;}
if (meat_val == 2 ) {food_done_temp = prof3.ag;}
mkeypress=' ';
lcd.clear();
stage_set = 8;
}
if (mkeypress == '#') { // GO button
// save all new settings
mkeypress = ' ';
stage_set = 10;
}
} // stage 7
///////////// stage 8
lcd.clear();
if (meat_val == 0 ) {lcd.print("Pork Settings :");}
if (meat_val == 1 ) {lcd.print("Brisket Settings :");}
if (meat_val == 2 ) {lcd.print(" Foul Settings :");}
lcd.setCursor(0, 1);
if (meat_val == 0 ) {lcd.print("Hold Temp = "); lcd.print(prof1.ah,0);}
if (meat_val == 1 ) {lcd.print("Hold Temp = "); lcd.print(prof2.ah,0);}
if (meat_val == 2 ) {lcd.print("Hold Temp = "); lcd.print(prof3.ah,0);}
lcd.setCursor(0,3);lcd.print("Up/Dn Change,MS=Save");
while (stage_set == 8) {
keypadcontrol();
lcd.setCursor(14,1);lcd.print(" ");lcd.setCursor(14,1);
if (meat_val == 0 ) {lcd.print(prof1.ah,0);}
if (meat_val == 1 ) {lcd.print(prof2.ah,0);}
if (meat_val == 2 ) {lcd.print(prof3.ah,0);}
delay(20);
if (mkeypress == '3') { // Raise temp
if (meat_val == 0 ) {if (prof1.ah < 500) {lcd.noBlink();prof1.ah += 5;}}
if (meat_val == 1 ) {if (prof2.ah < 500) {lcd.noBlink();prof2.ah += 5;}}
if (meat_val == 2 ) {if (prof3.ah < 500) {lcd.noBlink();prof3.ah += 5;}}
mkeypress=' ';
delay(100);
}
if (mkeypress == '2') { // Lower temp
if (meat_val == 0 ) {if (prof1.ah > 0) {lcd.noBlink();prof1.ah -= 5;}}
if (meat_val == 1 ) {if (prof2.ah > 0) {lcd.noBlink();prof2.ah -= 5;}}
if (meat_val == 2 ) {if (prof3.ah > 0) {lcd.noBlink();prof3.ah -= 5;}}
mkeypress=' ';
delay(100);
}
//////////////////////////////////////////////
if (mkeypress == '1') { // ms button
//tsecs = millis()+20000;
if (meat_val == 0 ) {hold_temp = prof1.ah;}
if (meat_val == 1 ) {hold_temp = prof2.ah;}
if (meat_val == 2 ) {hold_temp = prof3.ah;}
mkeypress=' ';
show_profiles(meat_val);
stage_set = 9;
}
if (mkeypress == '#') { // GO button
// save all new settings
mkeypress = ' ';
stage_set = 10;
}
} // stage 8
} // setup_profiles
int show_profiles (int prof_num) {
lcd.clear();
lcd.setCursor(0, 0);
if (prof_num == 0 ) {lcd.print("New Pork Settings:");}
if (prof_num == 1 ) {lcd.print("New Brisket Settings:");}
if (prof_num == 2 ) {lcd.print("New Foul Settings:");}
lcd.setCursor(0, 1);
lcd.print("S1 Pit=");
if (prof_num == 0 ) {lcd.print(prof1.aa,0);}
if (prof_num == 1 ) {lcd.print(prof2.aa,0);}
if (prof_num == 2 ) {lcd.print(prof3.aa,0);}
lcd.print(" H=");
if (prof_num == 0 ) {lcd.print(prof1.ab,0);}
if (prof_num == 1 ) {lcd.print(prof2.ab,0);}
if (prof_num == 2 ) {lcd.print(prof3.ab,0);}
lcd.print(" M=");
if (prof_num == 0 ) {lcd.print(prof1.ac,0);}
if (prof_num == 1 ) {lcd.print(prof2.ac,0);}
if (prof_num == 2 ) {lcd.print(prof3.ac,0);}
lcd.setCursor(0, 2);
lcd.print("S2 Pit=");
if (prof_num == 0 ) {lcd.print(prof1.ad,0);}
if (prof_num == 1 ) {lcd.print(prof2.ad,0);}
if (prof_num == 2 ) {lcd.print(prof3.ad,0);}
lcd.print(" H=");
if (prof_num == 0 ) {lcd.print(prof1.ae,0);}
if (prof_num == 1 ) {lcd.print(prof2.ae,0);}
if (prof_num == 2 ) {lcd.print(prof3.ae,0);}
lcd.print(" M=");
if (prof_num == 0 ) {lcd.print(prof1.af,0);}
if (prof_num == 1 ) {lcd.print(prof2.af,0);}
if (prof_num == 2 ) {lcd.print(prof3.af,0);}
lcd.setCursor(0, 3);
lcd.print("Done@ ");
if (prof_num == 0 ) {lcd.print(prof1.ag,0);}
if (prof_num == 1 ) {lcd.print(prof2.ag,0);}
if (prof_num == 2 ) {lcd.print(prof3.ag,0);}
lcd.print(" Hold@ ");
if (prof_num == 0 ) {lcd.print(prof1.ah,0);}
if (prof_num == 1 ) {lcd.print(prof2.ah,0);}
if (prof_num == 2 ) {lcd.print(prof3.ah,0);}
mod_mode = false;
delay(1000);
}
int lcdw (int col,int row, String txt) {
lcd.setCursor( col, row);
lcd.print(txt);
}
void show_serial_num () {
lcd.clear();
lcdw(0,0," Serial # ");lcdw(10,0,String(serial_num.a));
lcdw(0,2,"Version # ");lcdw(10,2,String(version_num.a,3));
delay(3000);
lcd.clear();
}
void smoke1 () { // used for testing on sbp36 == produces the most smoke
if (temp_error > -5 && temp_error < 1 && climbing ) {fan_pulse(100,0);}
if (temp_error > 0 && temp_error < 3 && climbing ) {fan_pulse(75,0);}
if (temp_error > 2 && temp_error < 5 && climbing ) {fan_pulse(50,0);}
if (temp_error > 1 && temp_error < 6 && !falling && ! climbing) { fan_pulse(500,0);}
if (temp_error > 0 && falling ) {fan_on();}
}
void smoke2 () { // used for testing on ok joe == produces less smoke than smoke1
if (temp_error > -6 && temp_error < 1 && climbing ) {fan_pulse(900,0);}
if (temp_error > -5 && temp_error < 1 && climbing ) {fan_pulse(800,0);}
if (temp_error > -4 && temp_error < 1 && climbing ) {fan_pulse(600,0);}
if (temp_error > -3 && temp_error < 1 && climbing ) {fan_pulse(400,0);}
if (temp_error > -2 && temp_error < 1 && climbing ) {fan_pulse(300,0);}
if (temp_error > -1 && temp_error < 1 && climbing ) {fan_pulse(100,0);}
if (temp_error > 0 && temp_error < 3 && climbing ) {fan_pulse(250,0);}
if (temp_error > 2 && temp_error < 5 && climbing ) {fan_pulse(150,0);}
if (temp_error > 1 && temp_error < 5 && falling ) {fan_pulse(500,0);}
if (temp_error > 2 && temp_error < 5 && falling ) {fan_pulse(300,0);}
if (temp_error > 3 && temp_error < 5 && falling ) {fan_pulse(200,0);}
if (temp_error > 4 && temp_error < 5 && falling ) {fan_pulse(100,0);}
}
void smoke3 () { // used for testing on sbp36 == produces the least smoke
if (temp_error > 0 && climbing ) {auger_pulse(auger_speed/2,0);fan_pulse(fan_speed / 2,0);}
if (temp_error > 4 && falling ) {auger_pulse(auger_speed*2,0);fan_on();}
if ( temp_error > 0 && !climbing && !falling) {fan_on();}
// if (temp_error > 10 && climbing ) {fan_pulse(fan_speed / 2,0);}
}
void smoke4 () { // no fan pulsing at all.
if (temp_error > 1 && temp_error < 3 && climbing )
{
auger_pulse(500,0);
}
fan_on();
}
void smoke5 () {
///// if (temp_error > -5 && temp_error <= 5 && climbing )
if (temp_error > -5 && climbing )
{
fan_pulse(fan_speed / 2,0);
auger_pulse(auger_speed / 2,0);
}
if ( temp_error > 0 && !climbing && !falling)
{
fan_pulse(fan_speed,0);
auger_pulse(auger_speed,0);
}
if (temp_error > -4 && temp_error <= 5 && falling )
{
fan_pulse(fan_speed*2,0);
auger_pulse(auger_speed*2,0);
}
if (temp_error > 5 && avg_temp > Setpoint +4)
{
fan_pulse(fan_speed ,0); // changed because of sooting. / 2 was too slow
auger_pulse(auger_speed ,0);
}
if (temp_error > 15 && avg_temp > Setpoint +4)
{
adapt_mode = false;
// pre_PID();
if (last_PID_on) {
last_PID();
}
// fan_pulse(fan_speed*4 ,0); // changed because of sooting. / 2 was too slow
// auger_pulse(auger_speed*2 ,0);
}
}
void smoke6 () { //
if (temp_error > -4 && temp_error < 15 && climbing )
{
fan_pulse(200,0);
auger_pulse(auger_speed / 2,0);
}
if (temp_error > 0 && ! climbing )
{
fan_on();
}
if (temp_error > 0 && falling )
{
fan_on();
}
}
void smoke7 () {
//// if (temp_error > -4 && climbing )
if (temp_error > 0 && climbing )
{
fan_pulse(fan_speed,0);
}
if (temp_error > 5 && avg_temp > Setpoint +4 && climbing )
{
fan_pulse(fan_speed ,0);
// auger_pulse(auger_speed/2,0);
}
}
long fan_pulse (long on, long off) {
// unsigned long nowsecs = seconds;
// unsigned long timechange = (nowsecs - lasttime);
//if (off < 11 ) { off = 12;} // 12 is a minimum or fan will not run long enough to move air. causing soot
unsigned long nowmillis = millis();
unsigned long timechange = (nowmillis - lasttime);
// if(timechange>=off) {fan_off();lasttime = nowsecs;} else {fan_on();}
if(timechange<=on) {fan_on();lcdw(18,2,"F");} else {fan_off();lcdw(18,2," ");}
lasttime = nowmillis;
}
long auger_pulse ( long on, long off) {
unsigned long anowmillis = millis();
unsigned long atimechange = (anowmillis - alasttime);
if (hits < auger_run_time && !cooldown_startmode){
if(atimechange<=on) {auger_on();} else {auger_off();}
alasttime = anowmillis;
}
}
void fix_eeprom () {
// delay is needed because eeproms are slow at writting
pid.p = .15; pid.i = .001 ; pid.d = .03;
consKp = .15, consKi = .001, consKd = .03;
delay(500);
offset.ap = 0,offset.aa = 0, offset.af = 0 ;
delay(500);
aminmax.mn = .109;aminmax.mx = .241;aminmax.sp = .061;
delay(500);
foo.aa = 180; foo.ab = 2; foo.ac = 30; foo.ad = 250; foo.ae = 14; foo.af = 30; foo.ag = 200; foo.ah = 170;
delay(500);
prof1.aa = 180; prof1.ab = 9; prof1.ac = 30; prof1.ad = 250; prof1.ae = 14; prof1.af = 30; prof1.ag = 195; prof1.ah = 185;
delay(500);
prof2.aa = 180; prof2.ab = 9; prof2.ac = 30; prof2.ad = 250; prof2.ae = 14; prof2.af = 30; prof2.ag = 198; prof2.ah = 195;
delay(500);
prof3.aa = 180; prof3.ab = 3; prof3.ac = 0; prof3.ad = 325; prof3.ae = 6; prof3.af = 0; prof3.ag = 170; prof3.ah = 170;
delay(500);
prof4.aa = 180; prof4.ab = 0; prof4.ac = 30; prof4.ad = 250; prof4.ae = 2; prof4.af = 30; prof4.ag = 186; prof4.ah = 170;
delay(500);
}
int load_profile (int profile) {
if (profile == 1) {
what_profile = "Pork";
Setpoint = prof1.aa;
stage1 = prof1.aa;
stage1_time_hours = prof1.ab;
stage1_time_minutes = prof1.ac;
stage2 = prof1.ad;
stage2_time_hours = prof1.ae;
stage2_time_minutes = prof1.af;
food_done_temp = prof1.ag;
hold_temp = prof1.ah;
} else if (profile == 2) {
what_profile = "Brisk";
Setpoint = prof2.aa;
stage1 = prof2.aa;
stage1_time_hours = prof2.ab;
stage1_time_minutes = prof2.ac;
stage2 = prof2.ad;
stage2_time_hours = prof2.ae;
stage2_time_minutes = prof2.af;
food_done_temp = prof2.ag;
hold_temp = prof2.ah;
} else if (profile == 3) {
what_profile = "Foul";
Setpoint = prof3.aa;
stage1 = prof3.aa;
stage1_time_hours = prof3.ab;
stage1_time_minutes = prof3.ac;
stage2 = prof3.ad;
stage2_time_hours = prof3.ae;
stage2_time_minutes = prof3.af;
food_done_temp = prof3.ag;
hold_temp = prof3.ah;
} else if (profile == 4) { // this is the single stage mode. for wifi
what_profile = "1 Stage";
useprofiles=false;
stage2_set = false;
hold_temp_mode = false;
stage1 = Setpoint;
//Setpoint = 180;
stage2 = Setpoint;
clear_lcd();
}
}
void last_PID () {
min_auger_base=aminmax.mn;
max_auger_base=aminmax.mx;
auger_spread=aminmax.sp;
// delay(100);
consKp = pid.p, consKi = pid.i, consKd = pid.d; // get saved pid info from eeprom.
last_PID_on=false;
}
void last_offset() {
temp_offset = offset.ap , ambient_offset = offset.aa, food_offset = offset.af ; // get saved offsets from eeprom
}
int between(int test, int lowend, int hiend) {
if (test >= lowend && test <= hiend) {return true;} else {return false;}
}
void write_probes () {
probes.b = probe2;
probes.c = probe3;
probes.d = probe4;
lcdw(13,2,"W");
}