Hi Can someone help me change the thermocouple from max6675 to max31856 I did change it to max31855 and got it to work but will not with the 31856, this board is needed because I need to change from k type to s type due to the klin high temps damaging the k type thermocouple sketch below is the original sketch before any alterations, thanks.
// Libraries to include
#include <LiquidCrystal.h> // LCD display library
#include <max6675.h> // Thermocouple card library
#include <PID_v1.h> // PID temp control library
#include <SPI.h> // Serial Peripheral Interface library
#include <SD.h> // SD memory card library (SPI is required)
// Setup user variables (CHANGE THESE TO MATCH YOUR SETUP)
const int lcdRefresh = 2500; // Refresh rate to update screen when running (ms)
const int maxTemp = 1600; // Maximum temperature (degrees). If reached, will shut down.
const int numZones = 1; // Number of heating element + thermostat zones (max of 3)
const int pidCycle = 2500; // Time for a complete PID on/off cycle for the heating elements (ms)
double pidInput[numZones]; // Input array for PID loop (actual temp reading from thermocouple). Don't change.
double pidOutput[numZones]; // Output array for PID loop (relay for heater). Don't change.
double pidSetPoint[numZones]; // Setpoint array for PID loop (temp you are trying to reach). Don't change.
PID pidCont[numZones] = {PID(&pidInput[0], &pidOutput[0], &pidSetPoint[0], 800, 47.37, 4.93, DIRECT)}; // PID controller array for each zone. Set arguments 4/5/6 to the Kp, Ki, Kd values after tuning.
const long saveCycle = 15000; // How often to save current temp / setpoint (ms)
const int tempOffset[numZones] = {0}; // Array to add a temp offset for each zone (degrees). Use if you have a cold zone in your kiln or if your thermocouple reading is off. This gets added to the setpoint.
const int tempRange = 2; // This is how close the temp reading needs to be to the set point to shift to the hold phase (degrees). Set to zero or a positive integer.
const char tempScale = 'c'; // Temperature scale. F = Fahrenheit. C = Celsius
// Setup pin connections (CHANGE THESE TO MATCH YOUR SETUP)
// Pins 0 + 1 are used for serial port
const int upPin = 2; // Pin # connected to up arrow button
const int downPin = 3; // Pin # connected to down arrow button
const int selectPin = 4; // Pin # connected to select / start button
MAX6675 thermo[numZones] = {MAX6675(5, 6 ,7)}; // Pins connected to the thermocouple card. This is an array for each thermocouple (zone).
const int heaterPin[numZones] = {9}; // Pins connected to relays for heating elements. This is an array for each output pin (zone).
// Pins 10 thru 13 are for SD card. These are automatically setup.
LiquidCrystal lcd(19, 18, 17, 16, 15, 14); // LCD display (connected to analog inputs / reverse order so I don't have to twist ribbon cable)
// Setup other variables (DON'T CHANGE THESE)
double calcSetPoint; // Calculated set point (degrees)
unsigned long holdStart; // Exact time the hold phase of the segment started (ms). Based on millis().
int i = 0; // Simple loop counter
int lastSeg = 0; // Last segment number in firing schedule
int lastTemp; // Last setpoint temperature (degrees)
unsigned long lcdStart; // Exact time you refreshed the lcd screen (ms). Based on millis().
int optionNum = 1; // Option selected from screen #3
unsigned long pidStart; // Exact time you started the new PID cycle (ms). Based on millis().
double rampHours; // Time it has spent in ramp (hours)
unsigned long rampStart; // Exact time the ramp phase of the segment started (ms). Based on millis().
unsigned long saveStart; // Exact time you saved the temps (ms). Based on millis().
File saveFile; // File to save temps / set point.
char schedDesc1[21]; // Schedule description #1 (first line of text file)
int schedNum = 1; // Current firing schedule number. This ties to the file name (ex: 1.txt, 2.txt).
boolean schedOK = false; // Is the schedule you loaded OK?
unsigned long schedStart; // Exact time you started running the schedule (ms). Based on millis().
int screenNum = 1; // Screen number displayed during firing (1 = temps / 2 = schedule info / 3 = tools / 4 = done
int segHold[20]; // Hold time for each segment (min). This starts after it reaches target temp.
int segNum = 0; // Current segment number running in firing schedule. 0 means a schedule hasn't been selected yet.
boolean segPhase = 0; // Current segment phase. 0 = ramp. 1 = hold.
int segRamp[20]; // Rate of temp change for each segment (deg/hr).
int segTemp[20]; // Target temp for each segment (degrees).
//******************************************************************************************************************************
// SETUP: INITIAL SETUP (RUNS ONCE DURING START)
//******************************************************************************************************************************
void setup() {
// Setup all pin modes on board. Remove INPUT_PULLUP if you have resistors in your wiring.
pinMode(upPin, INPUT_PULLUP);
pinMode(downPin, INPUT_PULLUP);
pinMode(selectPin, INPUT_PULLUP);
for (i = 0; i < numZones; i++) {
pinMode(heaterPin[i], OUTPUT);
}
for (i = 14; i <= 19; i++) { // Change analog inputs to digital outputs for LCD screen
pinMode(i, OUTPUT);
}
// Setup lcd display (20 columns x 4 rows)
lcd.begin(20,4);
// Setup SD card
if (SD.begin() == false) {
lcd.print(F(" ERROR:"));
lcd.setCursor(0, 2);
lcd.print(F("Can't setup SD card"));
lcd.setCursor(0, 3);
lcd.print(F("System was shut down"));
shutDown();
}
// Delete old save file
SD.remove("temps.txt");
// Intro screens (i'm bored / delete if you want)
lcd.print(F(" Kiln Controller"));
lcd.setCursor(2, 1);
lcd.print(F("---------------"));
lcd.setCursor(2, 3);
lcd.print(F("by Steve Turner"));
delay(2000);
lcd.clear();
lcd.print(F("<----- On / Off"));
lcd.setCursor(0, 3);
lcd.print(F("<----- Heater On LED"));
delay(2000);
lcd.clear();
lcd.print(F(" ^ ^"));
lcd.setCursor(15, 1);
lcd.print(F("| |"));
lcd.setCursor(0, 2);
lcd.print(F("USB Connection-- |"));
lcd.setCursor(0, 3);
lcd.print(F("microSD Card--------"));
delay(2000);
lcd.clear();
lcd.print(F("Up ---------------->"));
lcd.setCursor(0, 1);
lcd.print(F("Down -------------->"));
lcd.setCursor(0, 2);
lcd.print(F("Select / Start ---->"));
delay(2000);
lcd.clear();
lcd.print(F(" LCD Brightness"));
lcd.setCursor(10, 1);
lcd.print(F("|"));
lcd.setCursor(10, 2);
lcd.print(F("|"));
lcd.setCursor(10, 3);
lcd.print(F("|"));
delay(2000);
// Open firing shedule # 1
openSched();
}
//******************************************************************************************************************************
// LOOP: MAIN LOOP (CONTINUOUS)
//******************************************************************************************************************************
void loop() {
//******************************
// Shutdown if too hot
for (i = 0; i < numZones; i++) {
if (pidInput[i] >= maxTemp) {
lcd.clear();
lcd.print(F(" ERROR:"));
lcd.setCursor(2, 2);
lcd.print(F("Max temp reached"));
lcd.setCursor(0, 3);
lcd.print(F("System was shut down"));
shutDown();
}
}
//******************************
// Select a firing schedule
if (segNum == 0) {
// Up arrow button
if (digitalRead(upPin) == LOW && schedNum > 1) {
schedNum = schedNum - 1;
openSched();
btnBounce(upPin);
}
// Down arrow button
if (digitalRead(downPin) == LOW) {
schedNum = schedNum + 1;
openSched();
btnBounce(downPin);
}
// Select / Start button
if (digitalRead(selectPin) == LOW && schedOK == true) {
setupPIDs();
segNum = 1;
lcdStart = millis();
pidStart = millis();
rampStart = millis();
schedStart = millis();
updateLCD();
btnBounce(selectPin);
}
}
//******************************
// Running the firing schedule
if (segNum >= 1) {
// Up arrow button
if (digitalRead(upPin) == LOW) {
if (screenNum == 2 || (screenNum == 3 && optionNum == 1)) {
screenNum = screenNum - 1;
}
else if (screenNum == 3 && optionNum >= 2) {
optionNum = optionNum - 1;
}
updateLCD();
btnBounce(upPin);
}
// Down arrow button
if (digitalRead(downPin) == LOW) {
if (screenNum <= 2) {
screenNum = screenNum + 1;
}
else if (screenNum == 3 && optionNum <= 2) {
optionNum = optionNum + 1;
}
updateLCD();
btnBounce(downPin);
}
// Select / Start button
if (digitalRead(selectPin) == LOW && screenNum == 3) {
if (optionNum == 1) { // Add 5 min
segHold[segNum - 1] = segHold[segNum - 1] + 5;
optionNum = 1;
screenNum = 2;
}
if (optionNum == 2) { // Add 5 deg
segTemp[segNum - 1] = segTemp[segNum - 1] + 5;
optionNum = 1;
screenNum = 1;
}
if (optionNum == 3) { // Goto next segment
segNum = segNum + 1;
optionNum = 1;
screenNum = 2;
}
updateLCD();
btnBounce(selectPin);
}
// Update PID's / turn on heaters / update segment info
if (screenNum < 4) {
if (millis() - pidStart >= pidCycle) {
pidStart = millis();
updatePIDs();
}
htrControl();
updateSeg();
}
// Refresh the LCD
if (millis() - lcdStart >= lcdRefresh) {
updateLCD();
lcdStart = millis();
}
// Save the temps to a file on SD card
if (millis() - saveStart >= saveCycle && pidInput[0] > 0 && screenNum < 4) {
saveFile = SD.open("temps.txt", FILE_WRITE);
saveFile.print((millis() - schedStart) / 60000.0); // Save in minutes
for (i = 0; i < numZones; i++) {
saveFile.print(",");
saveFile.print(pidInput[i]);
}
saveFile.print(",");
saveFile.println(pidSetPoint[0]);
saveFile.close();
saveStart = millis();
}
}
}
//******************************************************************************************************************************
// BTNBOUNCE: HOLD UNTIL BUTTON IS RELEASED. DELAY FOR ANY BOUNCE
//******************************************************************************************************************************
void btnBounce(int btnPin) {
while (digitalRead(btnPin) == LOW);
delay(40);
}
//******************************************************************************************************************************
// HTRCONTROL: TURN HEATERS ON OR OFF
//******************************************************************************************************************************
void htrControl() {
// Loop thru all zones
for (i = 0; i < numZones; i++) {
if (pidOutput[i] >= millis() - pidStart) {
digitalWrite(heaterPin[i], HIGH);
}
else {
digitalWrite(heaterPin[i], LOW);
}
}
}
//******************************************************************************************************************************
// INTLENGTH: GET THE LENGTH OF A INTEGER
//******************************************************************************************************************************
int intLength(int myInt) {
myInt = abs(myInt);
if (myInt >= 10000) {
return 5;
}
else if (myInt >= 1000) {
return 4;
}
else if (myInt >= 100) {
return 3;
}
else if (myInt >= 10) {
return 2;
}
else {
return 1;
}
}
//******************************************************************************************************************************
// OPENSCHED: OPEN AND LOAD A FIRING SCHEDULE FILE / DISPLAY ON SCREEN
//******************************************************************************************************************************
void openSched() {
// Setup all variables
int col = 1; // Column number (of text file). First column is one.
int row = 1; // Row number (of text file). First row is one.
char tempChar; // Temporary character holder (read one at a time from file)
char tempLine[21]; // Temporary character array holder
int tempLoc = 0; // Current location of next character to place in tempLine array
char schedDesc2[21]; // Schedule description #2 (second line of text file)
char schedDesc3[21]; // Schedule description #3 (third line of text file)
// Clear the arrays
memset(schedDesc1, 0, sizeof(schedDesc1));
memset(segRamp, 0, sizeof(segRamp));
memset(segTemp, 0, sizeof(segTemp));
memset(segHold, 0, sizeof(segHold));
// Make sure you can open the file
sprintf(tempLine, "%d.txt", schedNum);
File myFile = SD.open(tempLine, FILE_READ);
if (myFile == false) {
lcd.clear();
lcd.print(F("SELECT SCHEDULE: "));
lcd.print(schedNum);
lcd.setCursor(0, 2);
lcd.print(F("Can't find/open file"));
schedOK = false;
return;
}
// Load the data
while (myFile.available() > 0) {
// Read a single character
tempChar = myFile.read();
if (tempChar == 13) { // Carriage return: Read another char (it is always a line feed / 10). Add null to end.
myFile.read();
tempLine[tempLoc] = '\0';
}
else if (tempChar == 44) { // Comma: Add null to end.
tempLine[tempLoc] = '\0';
}
else if (tempLoc <= 19) { // Add it to the temp line array
tempLine[tempLoc] = tempChar;
tempLoc = tempLoc + 1;
}
if (row == 1 && tempChar == 13) {
memcpy(schedDesc1, tempLine, 21);
}
else if (row == 2 && tempChar == 13) {
memcpy(schedDesc2, tempLine, 21);
}
else if (row == 3 && tempChar == 13) {
memcpy(schedDesc3, tempLine, 21);
}
else if (row >= 4 && col == 1 && tempChar == 44) {
segRamp[row - 4] = atoi(tempLine);
}
else if (row >= 4 && col == 2 && tempChar == 44) {
segTemp[row - 4] = atoi(tempLine);
}
else if ((row >= 4 && col == 3 && tempChar == 13) || myFile.available() == 0) {
segHold[row - 4] = atoi(tempLine);
}
if (tempChar == 13) { // End of line. Reset everything and goto next line
memset(tempLine, 0, 21);
tempLoc = 0;
row = row + 1;
col = 1;
}
if (tempChar == 44) { // Comma. Reset everything and goto 1st column
memset(tempLine, 0, 21);
tempLoc = 0;
col = col + 1;
}
} // end of while(myFile.available ...
// Close the file
myFile.close();
// Set some variables
lastSeg = row - 3;
schedOK = true;
// Fix Ramp values so it will show the correct sign (+/-). This will help to determine when to start hold.
for (i = 0; i < lastSeg; i++) {
segRamp[i] = abs(segRamp[i]);
if (i >= 1) {
if (segTemp[i] < segTemp[i - 1]) {
segRamp[i] = -segRamp[i];
}
}
}
// Display on the screen
lcd.clear();
lcd.print(F("SELECT SCHEDULE: "));
lcd.print(schedNum);
lcd.setCursor(0, 1);
lcd.print(schedDesc1);
lcd.setCursor(0, 2);
lcd.print(schedDesc2);
lcd.setCursor(0, 3);
lcd.print(schedDesc3);
// Cut down schedule description so it shows better on other screen when running
schedDesc1[14 - intLength(schedNum)] = '\0';
}
//******************************************************************************************************************************
// READTEMPS: Read the temperatures
//******************************************************************************************************************************
void readTemps() {
// Loop thru all zones
for (i = 0; i < numZones; i++) {
if (tempScale == 'C') {
pidInput[i] = thermo[i].readCelsius();
}
if (tempScale == 'F') {
pidInput[i] = thermo[i].readFahrenheit();
}
}
}
//******************************************************************************************************************************
// SETUPPIDS: INITIALIZE THE PID LOOPS
//******************************************************************************************************************************
void setupPIDs() {
for (i = 0; i < numZones; i++) {
pidCont[i].SetSampleTime(pidCycle);
pidCont[i].SetOutputLimits(0, pidCycle);
pidCont[i].SetMode(AUTOMATIC);
}
}
//******************************************************************************************************************************
// SHUTDOWN: SHUT DOWN SYSTEM
//******************************************************************************************************************************
void shutDown() {
// Turn off all zones (heating element relays)
for (i = 0; i < numZones; i++) {
digitalWrite(heaterPin[i], LOW);
}
// Disable interrupts / Infinite loop
cli();
while (1);
}
//******************************************************************************************************************************
// UPDATELCD: UPDATE THE LCD SCREEN WHEN RUNNING
//******************************************************************************************************************************
void updateLCD() {
// Clear screen and set cursor to top left
lcd.clear();
// Temperatures
if (screenNum == 1) {
lcd.print(F("TEMPS "));
lcd.print((char)223);
lcd.print(tempScale);
lcd.print(F(" Rdg / SetPt"));
for (i = 0; i < numZones; i++) {
lcd.setCursor(1, i + 1);
lcd.print(F("Zone "));
lcd.print(i + 1);
lcd.setCursor(12 - intLength((int)pidInput[i]), i + 1);
lcd.print((int)pidInput[i]);
lcd.print(F(" / "));
lcd.print((int)pidSetPoint[i]);
}
}
// Schedule / segment info
if (screenNum == 2) {
lcd.print(F("SCH "));
lcd.print(schedNum);
lcd.print(F(": "));
lcd.print(schedDesc1);
lcd.setCursor(0, 1);
lcd.print(F("SEG: "));
lcd.print(segNum);
lcd.print(F(" / "));
lcd.print(lastSeg);
if (segPhase == 0) {
lcd.setCursor(2, 2);
lcd.print(F("Ramp to "));
lcd.print(segTemp[segNum - 1]);
lcd.print(F(" "));
lcd.print((char)223);
lcd.print(tempScale);
lcd.setCursor(2, 3);
lcd.print(F("at "));
lcd.print(segRamp[segNum - 1]);
lcd.print(F(" "));
lcd.print((char)223);
lcd.print(F("/hr"));
}
else {
lcd.setCursor(2, 2);
lcd.print(F("Hold at "));
lcd.print(segTemp[segNum - 1]);
lcd.print(F(" "));
lcd.print((char)223);
lcd.print(tempScale);
lcd.setCursor(2, 3);
lcd.print(F("for "));
lcd.print((millis() - holdStart) / 60000);
lcd.print(F(" / "));
lcd.print(segHold[segNum - 1]);
lcd.print(F(" min"));
}
}
// Tools
if (screenNum == 3) {
lcd.print(F(" TOOLS:"));
lcd.setCursor(2, 1);
lcd.print(F("Add 5 min"));
lcd.setCursor(2, 2);
lcd.print(F("Increase 5 deg"));
lcd.setCursor(2, 3);
lcd.print(F("Skip to next seg"));
lcd.setCursor(0, optionNum);
lcd.print(F(">"));
lcd.setCursor(19, optionNum);
lcd.print(F("<"));
}
// Schedule completed
if (screenNum == 4) {
readTemps();
lcd.print(F(" SCHEDULE COMPLETE"));
lcd.setCursor(2, 1);
lcd.print(F("Wait until cool"));
lcd.setCursor(2, 2);
lcd.print(F("before you open"));
lcd.setCursor(2, 3);
lcd.print(F("Zone 1: "));
lcd.print((int)pidInput[0]);
lcd.print(F(" "));
lcd.print((char)223);
lcd.print(tempScale);
}
}
//******************************************************************************************************************************
// UPDATEPIDS: UPDATE THE PID LOOPS
//******************************************************************************************************************************
void updatePIDs() {
// Get the last target temperature
if (segNum == 1) { // Set to room temperature for first segment
if (tempScale == 'C') {
lastTemp = 24;
}
if (tempScale == 'F') {
lastTemp = 75;
}
}
else {
lastTemp = segTemp[segNum - 2];
}
// Calculate the new setpoint value. Don't set above / below target temp
if (segPhase == 0) {
rampHours = (millis() - rampStart) / 3600000.0;
calcSetPoint = lastTemp + (segRamp[segNum - 1] * rampHours); // Ramp
if (segRamp[segNum - 1] >= 0 && calcSetPoint >= segTemp[segNum - 1]) {
calcSetPoint = segTemp[segNum - 1];
}
if (segRamp[segNum - 1] < 0 && calcSetPoint <= segTemp[segNum - 1]) {
calcSetPoint = segTemp[segNum - 1];
}
}
else {
calcSetPoint = segTemp[segNum - 1]; // Hold
}
// Read the temperatures
readTemps();
// Loop thru all PID controllers
for (i = 0; i < numZones; i++) {
// Set the target temp. Add any offset.
pidSetPoint[i] = calcSetPoint + tempOffset[i];
// Update the PID based on new variables
pidCont[i].Compute();
}
}
//******************************************************************************************************************************
// UPDATESEG: UPDATE THE PHASE AND SEGMENT
//******************************************************************************************************************************
void updateSeg() {
// Start the hold phase
if ((segPhase == 0 && segRamp[segNum - 1] < 0 && pidInput[0] <= (segTemp[segNum - 1] + tempRange)) ||
(segPhase == 0 && segRamp[segNum - 1] >= 0 && pidInput[0] >= (segTemp[segNum - 1] - tempRange))) {
segPhase = 1;
holdStart = millis();
}
// Go to the next segment
if (segPhase == 1 && millis() - holdStart >= segHold[segNum - 1] * 60000) {
segNum = segNum + 1;
segPhase = 0;
rampStart = millis();
}
// Check if complete / turn off all zones
if (segNum - 1 > lastSeg) {
for (i = 0; i < numZones; i++) {
digitalWrite(heaterPin[i], LOW);
}
screenNum = 4;
}
}