Robert Korn
Published © Apache-2.0

Smokerduino

An Arduino based smoker controller.

IntermediateShowcase (no instructions)10 hours12,464
Smokerduino

Things used in this project

Story

Read more

Schematics

Schematic

Check out learn.adafrui.com and follow their Sous Vide build. My only hardware changes are the massive SSR and the Thermocouple module

Code

SmokerDuino.ino

Arduino
//****************************************************************
//*  Name    : SmokerDuino PID Controller                        *
//*  Author  : Robert Joseph Korn                                *
//*  Notice  : Copyright (c) 2015 Open Valley Consulting Corp    *
//*  Date    : 10/18/15                                          *
//*  Version : 1.1                                               *
//*  Notes   :                                                   *
//*          :                                                   *
//****************************************************************
//-------------------------------------------------------------------
// Based on Sous Videno
// by Brady Doll
//
// Which was based on the Sous Vide Controller
// by Bill Earl - for Adafruit Industries
// https://github.com/adafruit/Sous_Viduino
// 
// which is in turn was
// Based on the Arduino PID and PID AutoTune Libraries
// by Brett Beauregard
// http://playground.arduino.cc/Code/PIDLibrary
// http://playground.arduino.cc//Code/PIDAutotuneLibrary
//
// Uses the Adafruit RGB LCD Shield library
// https://github.com/adafruit/Adafruit-RGB-LCD-Shield-Library
// 
//-----------
// Large LCD font based on Instructable:
// Custom Large Font for 16x2 LCDs by mpilchfamily
// http://www.instructables.com/id/Custom-Large-Font-For-16x2-LCDs/
//------------------------------------------------------------------
// ************************************************
// Compilation options
// 
// Comment/Uncomment to disable/enable options
// ************************************************

// LCD Options
#define CHARS 16		// Screen characters, Supports 16 or 20

// Support color LCD backlight
#define COLOR true		// Comment for monochrome backlight

// Enable serial logging
#define LOGGING true	// Comment to disable logging

// Default setpoint
#define SETPOINT_DEF 64.5

// Default Kp, Ki, & Kd values
#define KP_DEF 850
#define KI_DEF 0.5
#define KD_DEF 0.1


// ************************************************
// Libraries
// ************************************************

// Libraries for Adafruit LCD shield
#include <Wire.h>
//#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>

// Libraries for the max6675 Thermocouple
#include "max6675.h"

// PID Library
#include <PID_v1.h>
#include <PID_AutoTune_v0.h>

// So we can save and retrieve settings
#include <EEPROM.h>


// ************************************************
// PID Variables and constants
// ************************************************

//Define Variables we'll be connecting to
double Setpoint = SETPOINT_DEF;
double Input;
double Output;
// Output relay on time
volatile long onTime = 0;

// pid tuning parameters
double Kp = KP_DEF;
double Ki = KI_DEF;
double Kd = KD_DEF;

// EEPROM addresses for persisted data
const int SpAddress		= 0;
const int KpAddress		= 8;
const int KiAddress		= 16;
const int KdAddress		= 24;
const int configAddress	= 32;	// Bitmask, Unit settings, pump settings

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

// 10 second Time Proportional Output window
const int WindowSize = 10000; 
unsigned long windowStartTime;


// ************************************************
// Auto Tune Variables and constants
// ************************************************

byte ATuneModeRemember = 2;

const double aTuneStep = 500;
const double aTuneNoise = 1;
const unsigned int aTuneLookBack = 20;

// Tuning mode
boolean tuning = false;
// Auto Tuner
PID_ATune aTune(&Input, &Output);


// ************************************************
// Display variables and constants
// ************************************************

// Backlight colors
#if defined(COLOR)
#define RED 0x1
#define YELLOW 0x3
#define GREEN 0x2
#define TEAL 0x6
#define BLUE 0x4
#define VIOLET 0x5
#endif
#define WHITE 0x7

// Large font segments
const int TOP_LEFT       = 0;
const int TOP_BAR        = 1;
const int TOP_RIGHT      = 2;
const int LOWER_LEFT     = 3;
const int LOWER_BAR      = 4;
const int LOWER_RIGHT    = 5;
const int TOP_MIDDLE_BAR = 6;
const int DEGREE         = 7;
const int BLANK          = 254;
const int SOLID          = 255;

// Define custom characters that make up segmetns
// Top left
uint8_t tl[8] = { B00111, B01111, B11111, B11111, B11111, B11111, B11111, B11111, };
// Top bar
uint8_t ub[8] = { B11111, B11111, B11111, B00000, B00000, B00000, B00000, B00000, };
// Top right
uint8_t tr[8] = { B11100, B11110, B11111, B11111, B11111, B11111, B11111, B11111, };
// Bottom left
uint8_t ll[8] = { B11111, B11111, B11111, B11111, B11111, B11111, B01111, B00111, };
// Bottom bar
uint8_t lb[8] = { B00000, B00000, B00000, B00000, B00000, B11111, B11111, B11111 };
// Bottom right
uint8_t lr[8] = { B11111, B11111, B11111, B11111, B11111, B11111, B11110, B11100 };
// Upper middle bar
uint8_t umb[8] = { B11111, B11111, B11111, B00000, B00000, B00000, B11111, B11111 };
// Degree
uint8_t deg[8] = { B01110, B11111, B11011, B11111, B01110, B00000, B00000, B00000 };

// Define large characters
uint8_t zero[6]  = { TOP_LEFT,       TOP_BAR,        TOP_RIGHT,      LOWER_LEFT, LOWER_BAR,  LOWER_RIGHT };
uint8_t one[6]   = { TOP_BAR,        TOP_RIGHT,      BLANK,          LOWER_BAR,  SOLID,      LOWER_BAR   };
uint8_t two[6]   = { TOP_MIDDLE_BAR, TOP_MIDDLE_BAR, TOP_RIGHT,      LOWER_LEFT, LOWER_BAR,  LOWER_BAR   };
uint8_t three[6] = { TOP_MIDDLE_BAR, TOP_MIDDLE_BAR, TOP_RIGHT,      LOWER_BAR,  LOWER_BAR,  LOWER_RIGHT };
uint8_t four[6]  = { LOWER_LEFT,     LOWER_BAR,      SOLID,          BLANK,      BLANK,      SOLID       };
uint8_t five[6]  = { LOWER_LEFT,     TOP_MIDDLE_BAR, TOP_MIDDLE_BAR, LOWER_BAR,  LOWER_BAR,  LOWER_RIGHT };
uint8_t six[6]   = { TOP_LEFT,       TOP_MIDDLE_BAR, TOP_MIDDLE_BAR, LOWER_LEFT, LOWER_BAR,  LOWER_RIGHT };
uint8_t seven[6] = { TOP_BAR,        TOP_BAR,        TOP_RIGHT,      BLANK,      BLANK,      SOLID       };
uint8_t eight[6] = { TOP_LEFT,       TOP_MIDDLE_BAR, TOP_RIGHT,      LOWER_LEFT, LOWER_BAR,  LOWER_RIGHT };
uint8_t nine[6]  = { TOP_LEFT,       TOP_MIDDLE_BAR, TOP_RIGHT,      BLANK,      BLANK,      SOLID       };
uint8_t c[6]     = { TOP_LEFT,       TOP_BAR,        TOP_BAR,        LOWER_LEFT, LOWER_BAR,  LOWER_BAR   };
uint8_t f[6]     = { TOP_LEFT,       TOP_MIDDLE_BAR, TOP_MIDDLE_BAR, SOLID,      BLANK,      BLANK       };
uint8_t s[6]     = { TOP_LEFT,       TOP_MIDDLE_BAR, TOP_MIDDLE_BAR, LOWER_BAR,  LOWER_BAR,  LOWER_RIGHT };
uint8_t v[6]     = { LOWER_LEFT,     BLANK,          BLANK,          BLANK,      LOWER_LEFT, LOWER_RIGHT };
uint8_t blank[6] = { BLANK, BLANK, BLANK, BLANK, BLANK, BLANK };

// Initalize the Adafruit LCD shield
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();

// Flash state
unsigned long previousFlash = 0;
const int flashInterval = 500;
bool flashed = false;
int8_t prevFlash = -1;

// Current display offset
uint8_t x = 0;


// ************************************************
// Relay control
// ************************************************

// Output Relay
#define RelayPin 5



// ************************************************
// Logging
// ************************************************

// Optional logging support
#if defined(LOGGING)
const int logInterval = 10000; // log every 10 seconds
unsigned long lastLogTime = 0;
#endif


// ************************************************
// Mode variables and constants
// ************************************************

// Maximum number of menu items
const int MENU_MAX = 4;
const int SETTINGS_MENU_MAX = 6;
const int TUNE_MENU_MAX = 11;

// Menu timeout
const long menuTimeout = 20000; // 20s

enum mode {
	MENU = 0,
	RUN,			// --> begin Main menu
	SET_TEMP,
	DISPLAY_TEMP,
	SETTINGS,		// <-- end Main menu
	CHOOSE_UNITS,	// --> begin Settings menu
	TUNING,			// <-- end Settings menu
	TUNE_KP,		// --> begin Tuning menu
	TUNE_KI,
	TUNE_KD,
	AUTOTUNE		// <-- end Tuning menu
};
int8_t currentMode = MENU;
uint8_t previousMode = MENU;
unsigned long menuReturn = 0;

// Run PID
bool runPID = false;

// ************************************************
// Button variables and constants
// ************************************************

#define BUTTON_MENU BUTTON_SELECT

// Current selected button(s)
uint8_t buttons = 0;
// Delay interval for button registration
const unsigned long selectInterval = 90;
// Previous states of buttons
bool buttonStates[5] = {
	false,	// BUTTON_LEFT
	false,	// BUTTON_RIGHT
	false,	// BUTTON_UP
	false,	// BUTTON_DOWN
	false	// SELECT
};




// ************************************************
// Temperature interface constants

int tcDO = 6;
int tcCS = 7;
int tcCLK = 8;

// Load temperature sensor
MAX6675 tc(tcCLK, tcCS, tcDO);

// Temperature (for viewing)
float currentTemp;
// Change temperature
float changeTemp;
// Selected digit
int8_t selected = 0;

// Units
enum units { C = 0, F };
// Current unit
uint8_t currentUnits = C;
// Selected unit
uint8_t selectedUnits = currentUnits;

enum cycle {
	SHOW_TEMP = 0,
	SETP_TEMP,
	DUTY,
	DETAILED
};
uint8_t cycleDisplay = 0;
const int cycleInterval = 5000;


// Current time
unsigned long currentMillis = 0;
//// Misc Interval
unsigned long previousInterval = 0;
// Last selected item
int8_t lastSelect;
float changeValue = 0.0;
float prevValue = 0.0;




// ************************************************
// Setup
// ************************************************
void setup()
{
	// Initialize the LCD
	lcd.begin(CHARS, 2);

	lcd.clear();
#if defined(COLOR)
	lcd.setBacklight(GREEN);
#endif

	// Create custom characters segements
	lcd.createChar(TOP_LEFT, tl);
	lcd.createChar(TOP_BAR, ub);
	lcd.createChar(TOP_RIGHT, tr);
	lcd.createChar(LOWER_LEFT, ll);
	lcd.createChar(LOWER_BAR, lb);
	lcd.createChar(LOWER_RIGHT, lr);
	lcd.createChar(TOP_MIDDLE_BAR, umb);
	lcd.createChar(DEGREE, deg);

	// Display splash screen
	x = 1;
	customChar(s);
	lcd.print(F("moker"));
	x += 5;
        lcd.setCursor(x, 0); 
        lcd.write(255);  
        lcd.write(1); 
        lcd.write(2);
        lcd.setCursor(x, 1); 
        lcd.write(255);  
        lcd.write(4);  
        lcd.write(5);
 	lcd.print(F("uino"));
 
#if defined(LOGGING)
	Serial.begin(9600);
#endif

	// Initialize Output Relay Control
	pinMode(RelayPin, OUTPUT);    // Output mode to drive relay
	digitalWrite(RelayPin, LOW);  // Make sure it is off to start
	
	// Initialize the PID and related variables
	LoadParameters();
	myPID.SetTunings(Kp, Ki, Kd);

	myPID.SetSampleTime(1000);
	myPID.SetOutputLimits(0, WindowSize);

	// Run timer2 interrupt every 15 ms 
	TCCR2A = 0;
	TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20;

	//Timer2 Overflow Interrupt Enable
	TIMSK2 |= 1<<TOIE2;

	myPID.SetMode(MANUAL);

	// Delay for splash screen
	delay(3000);
	lcd.clear();
	// Load menu
	changeMode(MENU);
}

// ************************************************
// Main Loop
// ************************************************
void loop()
{
	currentMillis = millis();
	buttons = lcd.readButtons();

	// Determine the current mode
	switch (currentMode) {
		case MENU:
			displayMenu(MENU);
			break;
		case RUN:
			Run();
			break;
		case SET_TEMP:
			setValue(true, 1, true, MENU);
			break;
		case DISPLAY_TEMP:
			displayTemp();
			break;
		case SETTINGS:
			displayMenu(SETTINGS);
			break;
		case CHOOSE_UNITS:
			chooseUnits();
			break;
		case TUNING:
			displayMenu(TUNING);
			break;
		case TUNE_KP:
			setValue(false, 0, true, TUNING);
			break;
		case TUNE_KI:
		case TUNE_KD:
			setValue(false, 2, false, TUNING);
			break;
	}

	if (runPID) {
		doControl();
	}
}



// ************************************************
// Timer Interrupt Handler
// ************************************************
SIGNAL(TIMER2_OVF_vect) 
{
	if (runPID) {
		DriveOutput();
	} else {
		digitalWrite(RelayPin, LOW);  // make sure relay is off
	}
}



// ************************************************
// Mode Functions
// ************************************************

/// Handles mode changes
///
/// <param name="newMode"></param>
void changeMode( uint8_t newMode )
{

	// Reset the button states
	for (uint8_t i = 0; i < 5; i++) {
		buttonStates[i] = false;
	}

	bool celsius = (C == currentUnits);

	previousMode = currentMode;

	switch (previousMode) {
		case SET_TEMP:
			Setpoint = (celsius) ? changeValue : (changeValue - 32) * 0.555555555555556;
			SaveParameters();
			break;
		case TUNE_KP:
			Kp = changeValue;
			SaveParameters();
			myPID.SetTunings(Kp, Ki, Kd);
			break;
		case TUNE_KI:
			Ki = changeValue;
			SaveParameters();
			myPID.SetTunings(Kp, Ki, Kd);
			break;
		case TUNE_KD:
			Kd = changeValue;
			SaveParameters();
			myPID.SetTunings(Kp, Ki, Kd);
			break;
	}

	switch (newMode) {
		case MENU:
		case SETTINGS:
		case TUNING:
#if defined(COLOR)
			lcd.setBacklight(BLUE);
#endif
			lastSelect = -1;
			selected = currentMode;
			menuReturn = previousInterval = currentMillis;
			break;
		case RUN:
			if (!runPID) {
				myPID.SetMode(AUTOMATIC);
				windowStartTime = millis();
				runPID = true;
			}
			previousInterval = currentMillis;
			cycleDisplay = SHOW_TEMP;
			getTemp(celsius);
			break;
		case SET_TEMP:
#if defined(COLOR)
			lcd.setBacklight(TEAL);
#endif
			prevValue = changeValue = (celsius) ? Setpoint : (Setpoint * 1.8) + 32;
			selected = 2;
			lcd.clear();
			printTemp(changeValue, celsius, true, true, 1);
			prevFlash = -1;
			break;
		case DISPLAY_TEMP:
			if (runPID) {
				turnOff();
				changeMode(MENU);
				return;
			} else {
#if defined(COLOR)
				lcd.setBacklight(GREEN);
#endif
				getTemp(celsius);
			}
			break;
		case CHOOSE_UNITS:
#if defined(COLOR)
			lcd.setBacklight(TEAL);
#endif
			lcd.clear();
			
			lcd.setCursor(0, 0);
			lcd.print(F("Units"));

			selectedUnits = currentUnits;
			displayUnitChooser();
			break;
		case TUNE_KP:
#if defined(COLOR)
			lcd.setBacklight(TEAL);
#endif
			prevValue = changeValue = Kp;
			selected = 2;
			lcd.clear();
			x = (CHARS - 12) / 2;
			printLargeNumber(changeValue, 0, true);
			prevFlash = -1;
			break;
		case TUNE_KI:
#if defined(COLOR)
			lcd.setBacklight(TEAL);
#endif
			prevValue = changeValue = Ki;
			selected = 2;
			lcd.clear();
			x = (CHARS - 10) / 2;
			printLargeNumber(changeValue, 2, false);
			prevFlash = -1;
			break;
		case TUNE_KD:
#if defined(COLOR)
			lcd.setBacklight(TEAL);
#endif
			prevValue = changeValue= Kd;
			selected = 2;
			lcd.clear();
			x = (CHARS - 10) / 2;
			printLargeNumber(changeValue, 2, false);
			prevFlash = -1;
			break;
		case AUTOTUNE:
			if (abs(Input - Setpoint) < 0.5) {
				// Remember the mode we're in
				ATuneModeRemember = myPID.GetMode();

				// set up the auto-tune parameters
				aTune.SetNoiseBand(aTuneNoise);
				aTune.SetOutputStep(aTuneStep);
				aTune.SetLookbackSec(int(aTuneLookBack));
				tuning = true;

				// Switch to the RUN mode
				changeMode(RUN);
			} else {
				lcd.clear();
				lcd.setCursor(0, 0);
				lcd.print(F("The temperature must"));
				lcd.setCursor(0, 1);
				lcd.print(F("be within 0.5"));
				lcd.write(B11011111);
				lcd.print(F("C"));
				delay(cycleInterval);

				changeMode(SETTINGS);
			}
			return;
			break;
	}

	currentMode = newMode;
}

/// Displays the menu
/// 
///
void displayMenu( uint8_t menu )
{
	bool change = false;

	if (buttons) {
		if ((currentMillis - menuReturn) > flashInterval ) {
			//if (checkButton(BUTTON_MENU)) {
			//	if ((currentMillis - menuReturn) > flashInterval ) {
			//		// Change back to the previous mode
			//		changeMode(previousMode);
			//	}
			//} else if (checkButton(BUTTON_UP)) {
			if (checkButton(BUTTON_UP)) {
				selected--;
			} else if(checkButton(BUTTON_DOWN)) {
				selected++;
			} else if (checkButton(BUTTON_RIGHT)) {
				changeMode(selected);
				return;
			} else if (MENU != menu && (checkButton(BUTTON_LEFT) || checkButton(BUTTON_MENU))) {
				if (SETTINGS == menu) {
					changeMode(MENU);
					selected = SETTINGS;
				} else if (TUNING == menu) {
					changeMode(SETTINGS);
					selected = TUNING;
				}
			}

			previousInterval = currentMillis;
		}
	} else {
		// Reset the button states
		for (uint8_t i = 0; i < 5; i++) {
			buttonStates[i] = false;
		}

		
		// Check if we have reached our timout interval
		if ((currentMillis - previousInterval) > menuTimeout) {
			if (runPID) {
				changeMode(RUN);
			} else {
				turnOff();
				// Turn off the display backlight
				lcd.setBacklight(0);

				// Wait until a button is pressed
				while (!buttons) {
					buttons = lcd.readButtons();
				}

				menuReturn = previousInterval = millis();

				// Turn the backlight back on
#if defined(COLOR)
				lcd.setBacklight(BLUE);
#else
				lcd.setBacklight(WHITE);
#endif
			}
		}
	}

	x = 0;

	uint8_t min;
	uint8_t max;

	// Get the item ranges for the selected menu
	if (MENU == menu) {
		min = 1;
		max = MENU_MAX;
	} else if (SETTINGS == menu) {
		min = MENU_MAX + 1;
		max = SETTINGS_MENU_MAX;
	} else if (TUNING == menu) {
		min = SETTINGS_MENU_MAX + 1;
		max = TUNE_MENU_MAX;
	}

	// Make sure the currently selected item is within the range
	if (min > selected) {
		selected = min;
	} else if (max < selected) {
		selected = max;
	}

	// Check if the selected item has changed
	if (selected != lastSelect) {
		bool celsius = (C == currentUnits);

		lcd.clear();

		int selected_num = selected;
		if (SETTINGS == menu) {
			selected_num -= MENU_MAX;
		} else if (TUNING == menu) {
			selected_num -= SETTINGS_MENU_MAX;
		}

		printLargeDigit(selected_num);
		x++;
		lcd.setCursor(x, 0);

		switch ( selected ) {
			case RUN:
				if (runPID) {
					if (tuning) {
						lcd.print(F("Tuning"));
					} else {
						lcd.print(F("Running"));
					}
				} else {
					lcd.print(F("Run"));
				}

				lcd.setCursor(( (celsius) ? CHARS - 9 : CHARS - 10 ), 1);
				lcd.print(F("SP:"));
				lcd.print( (celsius) ? Setpoint : (Setpoint * 1.8) + 32 );
				lcd.setCursor(CHARS - 2, 1);
				lcd.write(B11011111); // Degree symbol
				lcd.print( (celsius) ? 'C' : 'F' );
				break;
			case SET_TEMP:
				lcd.print(F("Set"));
				lcd.setCursor(x, 1);
				lcd.print(F("Temperature"));
				break;
			case DISPLAY_TEMP:
				if (runPID) {
					lcd.print(F("Stop"));
					lcd.setCursor(x, 1);
					lcd.print(F("Running"));
				} else {
					lcd.print(F("Display"));
					lcd.setCursor(x, 1);
					lcd.print(F("Temperature"));
				}
				break;
			case SETTINGS:
				lcd.print(F("Settings"));
				lcd.setCursor(x,1);
				break;
		// Settings Menu
			case CHOOSE_UNITS:
				lcd.print(F("Set"));
				lcd.setCursor(x, 1);
				lcd.print(F("Units"));
				lcd.setCursor(CHARS - 1, 1);
				lcd.print( (celsius) ? 'C' : 'F' );
				break;
			case TUNING:
				lcd.print(F("Configure"));
				lcd.setCursor(x, 1);
				lcd.print(F("Tuning"));
				break;
		// Tuning Menu
			case TUNE_KP:
				lcd.print(F("Tune"));
				lcd.setCursor(x, 1);
				lcd.print(F("Kp"));
				if (Kp < 100) {
					lcd.setCursor(CHARS - 5, 1);
				} else if (Kp < 1000) {
					lcd.setCursor(CHARS - 6, 1);
				} else {
					lcd.setCursor(CHARS - 7, 1);
				}
				lcd.print(Kp);
				break;
			case TUNE_KI:
				lcd.print(F("Tune"));
				lcd.setCursor(x, 1);
				lcd.print(F("Ki"));
				lcd.setCursor(CHARS - 4, 1);
				lcd.print(Ki);
				break;
			case TUNE_KD:
				lcd.print(F("Tune"));
				lcd.setCursor(x, 1);
				lcd.print(F("Kd"));
				lcd.setCursor(CHARS - 4, 1);
				lcd.print(Kd);
				break;
			case AUTOTUNE:
				lcd.print(F("Auto"));
				lcd.setCursor(x,1);
				lcd.print(F("Tuning"));
				break;
		}

		lcd.setCursor(CHARS - 1, 0);
		lcd.write(B01111110);

		lastSelect = selected;
	}
}

// Handles the run state
//
//
void Run() {
	bool celsius = (C == currentUnits);

	if (buttons) {
		if (checkButton(BUTTON_MENU)) {
			changeMode(MENU);
			return;
		} else if (buttons & BUTTON_LEFT && buttons & BUTTON_RIGHT) {
			if (DETAILED != cycleDisplay) {
				cycleDisplay = DETAILED;
				lcd.clear();
				lcd.setCursor(0, 0);
				lcd.print(F("Sp: "));
				lcd.print((celsius) ? Setpoint : (Setpoint * 1.8) + 32);
				lcd.write(B11011111);	// degree symbol
				lcd.print((celsius) ? 'C' : 'F');

				lcd.setCursor(CHARS - 1, 0);
				if (tuning) {
					lcd.print(F("T"));
				} else {
					lcd.write(BLANK);
				}
			}
		}
	} else {
		for (uint8_t i = 0; i < 5; i++) {
			buttonStates[i] = false;
		}
	}

	if (DETAILED != cycleDisplay) {
		if ((SHOW_TEMP == cycleDisplay && (currentMillis - previousInterval) > menuTimeout) || ((SHOW_TEMP != cycleDisplay) && (currentMillis - previousInterval) > cycleInterval)) {
			cycleDisplay++;

			if (cycleDisplay > 2) {
				cycleDisplay = 0;
			}

			if (SETP_TEMP == cycleDisplay) {
				float setpTemp = (celsius) ? Setpoint : (Setpoint * 1.8) + 32;
				printTemp(setpTemp, celsius, true, true, 1);
				lcd.setCursor(0, 0);
				lcd.print(F("S"));
				lcd.setCursor(0, 1);
				lcd.print(F("P"));
				
				lcd.setCursor(CHARS - 1, 0);
				if (tuning) {
					lcd.print(F("T"));
				} else {
					lcd.write(BLANK);
				}
			} else if (DUTY == cycleDisplay) {
				lcd.clear();
				lcd.setCursor(CHARS - 2, 0);
				lcd.print(F("%"));

				lcd.setCursor(CHARS - 1, 0);
				if (tuning) {
					lcd.print(F("T"));
				} else {
					lcd.write(BLANK);
				}
			}

			previousInterval = currentMillis;
		}
	}

	float pct;

	switch (cycleDisplay) {
		case SHOW_TEMP:
			printTemp(currentTemp, celsius, false, true, 2);
			
			lcd.setCursor(CHARS - 1, 0);
			if (tuning) {
				lcd.print(F("T"));
			} else {
				lcd.write(BLANK);
			}
			break;
		case DUTY:
			pct = (map(Output, 0, WindowSize, 0, 1000) / 10);
			x = (CHARS - 14) / 2;
			printLargeNumber(pct, 2, false);
			break;
		case DETAILED:
			lcd.setCursor(0, 1);
			lcd.print(currentTemp);
			lcd.write(B11011111);	// degree symbol
			if (celsius) {
				lcd.print(F("C :"));
			} else {
				lcd.print(F("F :"));
			}
			pct = (map(Output, 0, WindowSize, 0, 1000) / 10);
			if (pct < 100) {
				lcd.write(BLANK);
			}
			lcd.print(pct);
			lcd.print("%");
			break;
	}

#if defined(COLOR)
	setBacklight();
#endif
}

///
///
///
void turnOff() {
	runPID = false;

	myPID.SetMode(MANUAL);
	// Turn off the relay
	digitalWrite(RelayPin, LOW); // make sure it is off
}

// ************************************************
// Execute the control loop
// ************************************************
void doControl() {
	getTemp(C == currentUnits);

	if (tuning) {
		if (aTune.Runtime()) {	// returns 'true' when done
			FinishAutoTune();
		}
	} else {
		// TODO: Add feed forward/quick heat mode
		myPID.Compute();
	}

	 // Time Proportional relay state is updated regularly via timer interrupt.
	onTime = Output;

#if defined(LOGGING)
	// Periodically log to the serial port in CSV format
	if (currentMillis - lastLogTime > logInterval) {
		Serial.print(Input);
		Serial.print(",");
		Serial.print(Output);
		Serial.print(",");
		Serial.print(Setpoint);

		lastLogTime = currentMillis;
	}
#endif
}

///
void displayTemp() {
	if (buttons) {
		if (checkButton(BUTTON_MENU)) {
			changeMode(MENU);
			return;
		}
	} else {
		for (int i = 0; i < 5; i++) {
			buttonStates[i] = false;
		}
	}

	bool celsius = (C == currentUnits);

	getTemp(celsius);

	printTemp(currentTemp, celsius, false, true, 2);

#if defined(LOGGING)
	// Periodically log to the serial port in CSV format
	if (currentMillis - lastLogTime > logInterval) {
		Serial.print(Input);
		Serial.println(",0,0");

		lastLogTime = currentMillis;
	}
#endif
}

///
///
///
...

This file has been truncated, please download it to see its full contents.

Credits

Robert Korn

Robert Korn

15 projects • 27 followers
I Made the Internet of Things before people had a name for it.
Thanks to BradyDoll.

Comments