[code]//This is software for an Arduino UNO + display board + protoboard stack to control Sarah's glass fusing kiln. // In every step the temperature increase from the current to Tmp in Ramp minutes,  after wait at this for Hld time. // Duration of every step is Ramp + Hold time, if you want only Ramp insert Hold to 0 //In this version I have fixed the following: //1. DONE - Allow the cooling Ramp to also be controlled. //2. DONE - Add an asterisk to the edit menu to indicate which program will be edited. //3. DONE - Add an asterisk to the run menu to indicate which program will be run. //4. DONE - In addition to the current actual temperature, show the current tgt temperature. //5. DONE - During hold stages, show how much time remains. //6. DONE, but not tested - Allow the user to increase or decrease the remaining time during holds. (This one will be the hardest of the bug fixes). //7. DONE - Sarah wants the temperatures to be in 5 degree increments instead of ten.  The useable range shoudl still fit in 8 bits, though //Richard Moore //January 2014 //are.kay.more@gmail.com //Not tested for safety.  Use or modify at your own risk.   //Feb 22, 2014 //Fixed a couple of bugs.  This one makes sure that the displayed numbers don't have an old uncleared extra place value.   //April 24, 2014 //Kiln stayed on even after program was complete.  Now I have it set the relay coil OFF once a minute during the "Program Complete" phase.   //It also shows the temperature after the program is complete now.   // Eugenio Baldi //November 2016 Change Adafruit with others libs  LiquidCrystal.h and Keyboad extension // Optimized EEPROM writings // Exiting from Step editing with Select Button to Main Manu // Exiting 10 minutes after end of cycle to Main Manu // Use of Celsius degree (the MAX31855.h have only this) // Extended the Temp range from 0 degree to 1275 // New step are filled with values of last step inserted // include the library code: #include #include #include #include #include //#include int thermoDO = 10; int thermoCS = 11; int thermoCLK = 12; Adafruit_MAX31855 thermocouple(thermoCLK, thermoCS, thermoDO); //MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO); LiquidCrystal lcd(8, 9, 4, 5, 6, 7); int state = 1; //The states are as follows: //  1: navigation - Main Menu       //  2: Navigation - Run Menu         //  3: Navigation - Edit Menu       //  4: editing an existing program   //  5: running                       //  6: Creating a new program       int currStep = 1;               //This is the program step currently being edited.   int currCharVal = 65;           //This integer will be used to cycle through characters when the user names a new program.   int statechange = 1; int cursorHoriz = 1;            //This is the horizontal cursor location. int cursorVert = 1;             //This is the certical cursor location. int currentProg = 1;            //This is the currrently selected program. int currentActualTemp = 20;     //The current actual temperature. int currentTargetTemp = 20;     //This is the current target temperature; int tempUpdateDelay = 3000;     //This is the time between relay actions in milliseconds. int eepromAddress = 0; byte numProgs;                  //This is the total number of stored programs, written to the first byte of the EEPROM memory. char name1[11] = "          ";  //This is the name of the currently selected program char name2[11] = "          ";  //This is the name of the program after the currently selected program char newName[11] = "          "; unsigned long progStartTime; unsigned long currTime; double elapsed = 0; double currTemp = 0; double planTemp = 0; byte Tmp = 0;                  //Temperature in 5x of degrees Celsius max:5x255=1275 byte Rte = 0;                  //Ramp in degrees per hour byte Hld = 0;                  //Hold time in minutes at Tmp after Rating increase byte Tmp_o = 0;                  //Temperature in 5x of degrees Celsius max:5x255=1275 byte Rte_o = 0;                  //Ramp in degrees per hour byte Hld_o = 0;                  //Hold time in minutes int prgStart = 0;              //This is the initial memory address of the currently selected program int heatIncreaseFlag = 1; int currStage = 0; double currInitTemp = 0; double currTgtTemp = 0; int currRamp = 0; unsigned long currStageStartTime = 0; unsigned long currRunTime = 0; int currStageState = 3;        //1 = heating, 2 = holding, 3 = stage finished unsigned long currHoldTime = 0; unsigned long holdStartTime = 0; int runflag = 0; // define some values used by the panel and buttons int buttons     = 0; int adc_key_in  = 0; #define BUTTON_RIGHT  0 #define BUTTON_UP     1 #define BUTTON_DOWN   2 #define BUTTON_LEFT   3 #define BUTTON_SELECT 4 #define BUTTON_NONE   5 // read the buttons int read_LCD_buttons() { adc_key_in = analogRead(0);      // read the value from the sensor // my buttons when read are centered at these valies: 0, 144, 329, 504, 741 // we add approx 50 to those values and check to see if we are close if (adc_key_in > 1000) return BUTTON_NONE; // We make this the 1st option for speed reasons since it will be the most likely result // For V1.1 us this threshold if (adc_key_in < 50)   return BUTTON_RIGHT;   if (adc_key_in < 170)  return BUTTON_UP; if (adc_key_in < 360)  return BUTTON_DOWN; if (adc_key_in < 550)  return BUTTON_LEFT; if (adc_key_in < 800)  return BUTTON_SELECT;   return BUTTON_NONE;  // when all others fail, return this... } //I'll use pin 13 for the relay controller, because it already has an indicator LED on the Arduino UNO board. #define WHITE 0x7 void setup() {  // Debugging output  Serial.begin(9600);  pinMode(13, OUTPUT);  // set up the LCD's number of columns and rows:  lcd.begin(16, 2);  //int time = millis();  lcd.print("MELTINATOR 9000 ");//("Initializing...");  lcd.setCursor(0,1);  lcd.print("LezBurnSumStuff!");  delay(2000); //  lcd.blink();  //time = millis() - time;  //Serial.print("Took "); Serial.print(time); Serial.println(" ms");  //lcd.setBacklight(WHITE); } uint8_t i=0; void loop() {  //Serial.begin(9600);  //Serial.print("Iteration...\n");  //Serial.print(state);  //Serial.print("\n");  //Serial.print(runflag);  //Serial.print("\n"); //  Serial.print(currStageState); //  Serial.print("\n"); //  Serial.print(currTgtTemp); //  Serial.print("\n"); //  Serial.print(currRamp); //  Serial.print("\n"); //  Serial.print(currRunTime); //  Serial.print("\n"); //  Serial.print(currInitTemp); //  Serial.print("\n"); //  Serial.print(planTemp); //  Serial.print("\n"); //  Serial.println("\n");  //This first "if" loop is to allow the loop to run freely if the program is in the "run" state (5).    //if (state!=5) {  buttons = read_LCD_buttons();  // read the buttons  //}  delay(350);  //else {  //  uint8_t buttons = 1;  //  statechange = 1;  //}  if ((buttons<5)||(state==5)) {   runflag = 1;  }  else {    runflag = 0;  }  if ((runflag)||(statechange)) {  //if ((buttons)||(statechange)) {    statechange = 0;  switch (state) {    case 1:      //State:Navigation - Main Menu      //Draw the Main Menu screen      lcd.clear();      lcd.setCursor(0,0);      //lcd.blink();      lcd.print("Main Menu       ");      lcd.setCursor(0,1);      switch (cursorHoriz) {        case 1:          lcd.print("*Run _Edit _New ");          break;        case 2:          lcd.print("_Run *Edit _New ");          break;        case 3:          lcd.print("_Run _Edit *New ");          break;      }      //Place the cursor in the appropriate location      //Allow the user to move the cursor location and press enter.      if (buttons<5) {        if (buttons == BUTTON_LEFT) {         cursorHoriz = cursorHoriz - 1;          statechange = 1;        }        if (buttons == BUTTON_RIGHT) {          cursorHoriz = cursorHoriz + 1;          statechange = 1;        }        //Keep the cursor location valid:        cursorHoriz = constrain(cursorHoriz,1,3);        //If the select button was pressed, change to the next state:        if (buttons == BUTTON_SELECT) {          statechange = 1;          switch (cursorHoriz) {            case 1:              state = 2;              //Set the vertical cursor to 1 to start at the beginning of the programs list:              cursorVert = 1;              break;            case 2:              state = 3;              break;            case 3:              //This one is for initializing a new program.              state = 6;              cursorHoriz = 0;              lcd.clear();              lcd.setCursor(0,0);              lcd.print("New Program Name");              break;          }        }      }      break;    case 2:      //State:Navigation - Run Menu      //Get the list of programs      numProgs = EEPROM.read(0);      for (int n = 0; n < 10; n++) {        name1[n] = EEPROM.read((cursorVert-1)*40+1+n);//Here the program length is 40: a ten character name with 10 3-byte envelope stages.      }      for (int n = 0; n < 10; n++) {        name2[n] = EEPROM.read((cursorVert)*40+1+n);//Here the program length is 40: a ten character name with 10 3-byte envelope stages.      }      //Draw the Run Menu screen      lcd.clear();      lcd.setCursor(0,0);      lcd.print("_");      lcd.print(name1);      lcd.setCursor(10,0);      lcd.print("  RUN");      lcd.setCursor(0,1);      lcd.print("*");      lcd.print(name2);      //Place the cursor in the appropriate location      //If up, cursorVert = cursorVert--      //If down, cursorVert = cursorVert++      //If enter, change state to Run      if (buttons<5) {        statechange = 1;        if (buttons == BUTTON_UP) {          cursorVert = cursorVert - 1;        }        if (buttons == BUTTON_DOWN) {          cursorVert = cursorVert + 1;        }        //Keep the cursor location valid:        cursorVert = constrain(cursorVert,1,int(numProgs));        //If the select button was pressed, start running the selected program:        if (buttons == BUTTON_SELECT) {          statechange = 1;          state = 5;          currentProg = cursorVert;          //Start run timer          progStartTime = millis();          lcd.clear();          lcd.setCursor(0,0);          lcd.print("Program Running");          prgStart = (currentProg-1)*40+1;        }      }      break;    case 3:      //State:Navigation - Edit Menu      //Get the list of programs      numProgs = EEPROM.read(0);      for (int n = 0; n < 10; n++) {        name1[n] = EEPROM.read((cursorVert-1)*40+1+n);//Here the program length is 40: a ten character name with 10 3-byte envelope stages.      }      for (int n = 0; n < 10; n++) {        name2[n] = EEPROM.read((cursorVert)*40+1+n);//Here the program length is 40: a ten character name with 10 3-byte envelope stages.      }      lcd.clear();      lcd.setCursor(0,0);      lcd.print("_");      lcd.print(name1);      lcd.setCursor(10,0);      lcd.print(" EDIT");      lcd.setCursor(0,1);      lcd.print("*");      lcd.print(name2);      //Place the cursor in the appropriate location      //If up, cursorVert = cursorVert--      //If down, cursorVert = cursorVert++      //If enter, change state to Edit      if (buttons<5) {        statechange = 1;        if (buttons == BUTTON_UP) {          cursorVert = cursorVert - 1;        }        if (buttons == BUTTON_DOWN) {          cursorVert = cursorVert + 1;        }        //Keep the cursor location valid:        cursorVert = constrain(cursorVert,1,int(numProgs));        //If the select button was pressed, start running the selected program:        if (buttons == BUTTON_SELECT) {          statechange = 1;          state = 4;          currentProg = cursorVert;          //Let's initialize the cursor position prior to the edit state:          cursorHoriz = 1;          cursorVert = 1;          prgStart = (currentProg-1)*40+1;          Tmp = EEPROM.read(prgStart+10+(currStep-1)*3);          Rte = EEPROM.read(prgStart+10+(currStep-1)*3+1);          Hld = EEPROM.read(prgStart+10+(currStep-1)*3+2);        }      }      break;    case 4:      //State:Programming      //Depending on the horizontal cursor position, the vertical buttons will 1: change the step, 2: change the target temp, 3: change the Ramp, or 4: change the hold time      if (buttons<5) {        statechange = 1;        if (buttons == BUTTON_UP) {          switch (cursorHoriz) {            case 1:         //Thee EEPROM is writed only when changed the Step      WriteEEPROM();              currStep = currStep + 1;              currStep = constrain(currStep,1,10);              Tmp = EEPROM.read(prgStart+10+(currStep-1)*3);              Rte = EEPROM.read(prgStart+10+(currStep-1)*3+1);              Hld = EEPROM.read(prgStart+10+(currStep-1)*3+2);              NewStep();              break;            case 2:              Tmp = constrain(Tmp,0,254);              Tmp = Tmp + 1;              break;            case 3:              Rte = constrain(Rte,0,254);              Rte = Rte + 1;              break;            case 4:              Hld = constrain(Hld,0,254);              Hld = Hld + 1;              break;          }          }        if (buttons == BUTTON_DOWN) {          switch (cursorHoriz) {            case 1:      WriteEEPROM();              currStep = currStep - 1;              currStep = constrain(currStep,1,10);              Tmp = EEPROM.read(prgStart+10+(currStep-1)*3);              Rte = EEPROM.read(prgStart+10+(currStep-1)*3+1);              Hld = EEPROM.read(prgStart+10+(currStep-1)*3+2);              break;            case 2:              Tmp = constrain(Tmp,1,255);              Tmp = Tmp - 1;              break;            case 3:              Rte = constrain(Rte,1,255);              Rte = Rte - 1;                            break;            case 4:              Hld = constrain(Hld,1,255);              Hld = Hld - 1;                            break;          }        }        if (buttons == BUTTON_LEFT) {          cursorHoriz = cursorHoriz - 1;        }        if (buttons == BUTTON_RIGHT) {          cursorHoriz = cursorHoriz + 1;        }        if (buttons == BUTTON_SELECT) {          state = 1;        }        cursorHoriz = constrain(cursorHoriz,1,4);      }        lcd.clear();      lcd.setCursor(0,0);      lcd.print("Stp Tmp Ramp Hld");      //Display the current step values      lcd.setCursor(0,1);      lcd.print(currStep);      lcd.setCursor(3,1);      //lcd.print(Tmp*10);      //Sarah requested temperature control in 5 degree increments.        lcd.print(Tmp*5);      lcd.setCursor(8,1);      lcd.print(Rte);      lcd.setCursor(13,1);      lcd.print(Hld);      break;    case 5:      //State:Running          //          //I need to add an lcd.clear and write all the necessary info to the screen each time to avoid uncleared place values.            lcd.clear();          lcd.setCursor(0,0);          lcd.print("Stg:");          lcd.print(currStage);          lcd.print("           ");      switch (currStageState) {        case 1:          //Heating          currRunTime = millis() - currStageStartTime;          //There are different expressions to find planTemp depending on if it's heating or cooling.          lcd.setCursor(5,0);          lcd.print("Time:");          lcd.print(currRamp-(int((currRunTime)/(double(60000)))));          if (heatIncreaseFlag)          { //            planTemp = double(currRamp)*(double(currRunTime)/(double(1000)*double(60)*double(60))) + double(currInitTemp);            planTemp = double(currInitTemp) + ((double(currTgtTemp) - double(currInitTemp)) * (double(currRunTime)/(double(1000)*double(60))))/ double(currRamp);          }          else          {            planTemp = double(currInitTemp) - ((double(currTgtTemp) - double(currInitTemp)) * (double(currRunTime)/(double(1000)*double(60))))/ double(currRamp);          }          ManageTemp(planTemp);          //Here the heatIncreaseFlag is used to determine if the currTgtTemp is going to be crossed from above or below before the stage is incremented.          if (heatIncreaseFlag) {            if (currTemp>currTgtTemp) {              //Change to the hold state.              currStageState = 2;              currHoldTime = currHoldTime + currRamp-(int((currRunTime)/(double(60000))));              holdStartTime = millis();            }          }          else          {            if (currTemp=currHoldTime) {            currStageState = 3;          }          break;        case 3:          //Stage finished          currStage = currStage + 1;          if (currStage<11)          {            currStageState = 1;            //currStage = currStage + 1;            currHoldTime = EEPROM.read(prgStart+10+(currStage-1)*3+2);            currRamp = EEPROM.read(prgStart+10+(currStage-1)*3+1);            currTgtTemp = 5*double(EEPROM.read(prgStart+10+(currStage-1)*3));            currStageStartTime = millis();            currInitTemp = thermocouple.readCelsius();    // temp = 0 End of cicle            if (currTgtTemp == 0) {              currStage = 15;            }            else             if (currTgtTemp>currInitTemp) {               heatIncreaseFlag = 1;             }             else             {               heatIncreaseFlag = 0;             }          }          if (currStage>=11)          {            lcd.clear();            lcd.setCursor(0,0);            lcd.print("Program Complete");            //Also, display the temperature:            currTemp = thermocouple.readCelsius();            lcd.setCursor(0,1);            lcd.print("Act:");            lcd.print(int(currTemp));            //Force the coil relay to be OFF:            digitalWrite(13, LOW);            //There's got to be a better way to exit the program, but for now I'll just stay in a delay loop.              delay(600000);//Right now this is set to 10 minutes.            state = 1;          }          break;      }      //Wait for the appropriate delay      delay(tempUpdateDelay);      break;    case 6:      //State: Create a new program      //Left and right change the cursor position.  Up and down cycle through characters.  Enter saves.        lcd.setCursor(cursorHoriz,1);      //Okay.  It looks like I can write characters this way.      lcd.print(char(currCharVal));      newName[cursorHoriz] = char(currCharVal);      if (buttons<5) {        statechange = 1;        if (buttons == BUTTON_UP) {          currCharVal = currCharVal + 1;        }        if (buttons == BUTTON_DOWN) {          currCharVal = currCharVal - 1;        }        if (buttons == BUTTON_RIGHT) {          cursorHoriz = cursorHoriz + 1;        }        if (buttons == BUTTON_LEFT) {          cursorHoriz = cursorHoriz - 1;        }        if (buttons == BUTTON_SELECT) {          //And read the number of programs          numProgs = EEPROM.read(0);          //Increment the number of programs and save to EEPROM          numProgs = numProgs + 1;          EEPROM.write(0,numProgs);          //Make the new program the current program in currentProg          currentProg = numProgs;          //Write the name from the display to the EEPROM          for (int n = 0; n<10; n++) {            EEPROM.write(numProgs*40+1+n,newName[n]);          }          //Clear EEPROM of new program          for (int n = 11; n<40; n++) {            EEPROM.write(numProgs*40+1+n,0);          }          //And pass control to the program editing state (4).          state = 4;          prgStart = (numProgs-1)*40+1;        }      }      //This delay will ensure that single button presses don't double trigger.      delay(500);        break;  }  } } void WriteEEPROM() {    Tmp_o = EEPROM.read(prgStart+10+(currStep-1)*3);    Rte_o = EEPROM.read(prgStart+10+(currStep-1)*3+1);    Hld_o = EEPROM.read(prgStart+10+(currStep-1)*3+2); if  (Tmp != Tmp_o)  {    EEPROM.write(prgStart+10+(currStep-1)*3, Tmp);  } if  (Rte != Rte_o) {              EEPROM.write(prgStart+10+(currStep-1)*3+1, Rte);  } if  (Hld != Hld_o) {              EEPROM.write(prgStart+10+(currStep-1)*3+2, Hld);  } } void NewStep() {  // insert in a new step the values of previous     if  (Tmp == 0 & Rte == 0 & Hld == 0)   {          Tmp = EEPROM.read(prgStart+10+(currStep-2)*3);          Rte = EEPROM.read(prgStart+10+(currStep-2)*3+1);          Hld = EEPROM.read(prgStart+10+(currStep-2)*3+2);  } } void ManageTemp(double TargetTemp) { // Get Temp from thermocouple              currTemp = thermocouple.readCelsius(); // Display Current e Target Temp                    lcd.setCursor(0,1);          lcd.print("Act:");          lcd.print(int(currTemp));          lcd.setCursor(8,1);          lcd.print("Tgt:");          lcd.print(int(TargetTemp)); // Out for Serial monitor           //          Serial.print("Tmp "); Serial.print(currTgtTemp); Serial.print(" Rte ");Serial.print(currRamp); Serial.print(" Hld ");Serial.print(currHoldTime);Serial.print(" Time ");Serial.println(currRunTime);          Serial.print("Current Temperature: "); Serial.print(currTemp); Serial.println(" degrees C");          Serial.print("Planned Temperature: "); Serial.print(TargetTemp); Serial.println(" degrees C");          Serial.print(" \n"); // Switch On/Off SSR control                    if (currTemp