Project-GlassKilnController-Code

From ArduinoInfo
Jump to navigation Jump to search

Project-GlassKilnController-Code


This will be the code under development for the project Shown HERE



/* YourDuinoStarter Example: Kiln Controller
  - WHAT IT DOES: Controls a 5000 Watt Kiln used for "Warm Glass"
    - The kiln has 3 sets of heating elements; they are controlled separately.
  - SEE the comments after "//" on each line below
  - V0.917 03/02/2016
  - Trying MeguntoLink PLotting
   Questions: terry@yourduino.com

  CREDITS:
  ArduinoMenu  Sep.2014 Rui Azevedo - ruihfazevedo(@rrob@)gmail.com

  creative commons license 3.0: Attribution-ShareAlike CC BY-SA
  This software is furnished "as is", without technical support, and with no
  warranty, express or implied, as to its usefulness for any purpose.
*/

/*-----( Import needed libraries )-----*/
#include "max6675.h"            // For Thermocouple interfaces
// Get the library HERE: https://github.com/adafruit/MAX6675-library
#include <Wire.h>               // Part of Arduino library
#include <LiquidCrystal_I2C.h>  // https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads
#include "RTClib.h"
// ArduinoMenu by Rui Azevedo from: https://github.com/neu-rah/ArduinoMenu
#include <menu.h>               // menu macros and objects
#include <genericKeyboard.h>    // define our 5-button Analog keyboard
#include <menuLCDs.h>           // Use our type LCD display
#include <menuFields.h>         // Allow entering data into fields
#include <keyStream.h>          // keyboard driver and stream 
#include <chainStream.h>        // concatenate multiple input streams 

#include <SD.h>                 // Part of Arduino library 
#include <SPI.h>                // Part of Arduino library
#include <Ethernet.h>           // Part of Arduino library
#include <MegunoLink.h>         // Data Plotting 

/*-----( Declare Constants and Pin Numbers )-----*/

#define keyboard_AnalogInput 0   // Analog input 0 used for 5-button Keyboard

#define btnRIGHT  menu::enterCode  // Constants recognized by the ArduinoMenu library
#define btnUP     menu::downCode
#define btnDOWN   menu::upCode
#define btnLEFT   menu::escCode
#define btnENTER  menu::enterCode
#define btnNONE   -1

/*----( Define Constants for Element Control  )-----*/
#define LidElementStartPeriod  0  // 3 Start periods, 1 second apart
#define MidElementStartPeriod  1
#define BotElementStartPeriod  2

#define ElementPeriodMs        3000 // The elements will run on a 3 second cycle, one starting each second

/*-----( Declare Thermocouple Constants and Pin Numbers )-----*/
// NOTE: On the Arduino Mega, SDA is digital pin 20 and SCL is pin 21. // (For LCD Display, RTC)
#define SO_PIN      2  // 6675 Serial Output for both modules
#define CK_PIN      3  // 6675 Clock Pin for both modules

#define SD_CARD_SS  4  // Select for SPI SD Card Module 
#define CS_PIN_A    5  // 6675 Chip Select for Module A
#define CS_PIN_B    6  // 6675 Chip Select for Module B
#define CS_PIN_C    7  // 6675 Chip Select for Module C

/*-----( Declare SPI Ethernet Constants and Pin Numbers )-----*/
#define SPI_SS_NET  10 // SPI Slave Select (for Ethernet Module)
#define SPI_MISO    50 // These for any SPI device; used for Ethernet
#define SPI_MOSI    51
#define SPI_SCK     52

#define LEDPIN      13                // Available for LED

#define MAIN_RELAY_240V_PIN          22   // 2-channel Relay board. This channel controls 12V 25A relay which turns on 240V power to SSRs, front panel etc.
#define RELAY2_PIN                   23   // Channel 2 (Unused) May be Vent control

#define LID_SOLID_STATE_RELAY_PIN    24   // Three SSR's control heating elements at 3 levels
#define MID_SOLID_STATE_RELAY_PIN    25
#define BOT_SOLID_STATE_RELAY_PIN    26

#define OFFSET_LID_THERMOCOUPLE       -0.0   // Offsets to calibrate Thermocouples 
#define OFFSET_MID_THERMOCOUPLE       -0.0
#define OFFSET_BOT_THERMOCOUPLE       -0.0

/*-----------( Read analog values from 4-button keyboard, decode voltages to Buttons )---------------*/
int getButton()
{
  int i, v, button;
  int sum = 0;

  for (i = 0; i < 4; i++) {
    sum += analogRead(keyboard_AnalogInput);
  }
  v = sum / 4;
  if (v > 1000) button = 0;
  else if (v >= 0  && v < 20)  button = 1; //LEFT
  else if (v > 135 && v < 155) button = 2; //UP
  else if (v > 315 && v < 335) button = 3; //DOWN
  else if (v > 495 && v < 515) button = 4; //RIGHT
  else if (v > 725 && v < 745) button = 5; //ENTER
  else button = 0;
  //  Serial.println(button);  // For test
  return button;
}// END getButton

/*-------------------( Debounce keystrokes, translate to ArduinoMenu buttons )-------------------*/
int old_button = 0;
int read_keyboard()
{
  int button, button2, pressed_button;
  button = getButton();         // Above
  if (button != old_button)     // A new button pressed
  {
    delay(50);                // debounce
    button2 = getButton();
    if (button == button2)
    {
      old_button = button;
      pressed_button = button;
      if (button != 0)
      {
        //Serial.println(button);
        if (button == 1) return btnLEFT;
        if (button == 2) return btnUP;
        if (button == 3) return btnDOWN;
        if (button == 4) return btnRIGHT;
        if (button == 5) return btnENTER;
      }
    }
  }
  else
  {
    return btnNONE;
  }
}// End read_keyboard

/*-----( Declare objects )-----*/
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 1, 177);

MAX6675 thermocoupleA(CK_PIN, CS_PIN_A, SO_PIN);  // Create three Thermocouple objects with different chip selects
MAX6675 thermocoupleB(CK_PIN, CS_PIN_B, SO_PIN);
MAX6675 thermocoupleC(CK_PIN, CS_PIN_C, SO_PIN);

genericKeyboard mykb(read_keyboard);  // Create the generic Keyboard object named mykb, output button as a stream
Stream* in2[] = {&mykb};              // Put the button in a stream
chainStream<1> allInputs(in2);

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address and pinout
RTC_DS1307 rtc;                                                 // Create a RealTimeClock object

// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
EthernetServer server(80);

/*-----( Declare Variables for Menu )-----*/
int    ledCtrl = 0;
bool   runMenu = false;
bool   scrSaverEnter = true;
int    percent;//just testing changing this var
int    F_Temp   ; // SetPoint Temperature

/*-----( Declare Variables for Thermocouples )-----*/
float DegreesC_A;  // For thermocouples A and B and C
float DegreesF_A;

float DegreesC_B;
float DegreesF_B;

float DegreesC_C;
float DegreesF_C;

/*-----( Variables for Element Control )------*/
int LidElement_SSR_DutyCycle = 0;   // 0..100
int MidElement_SSR_DutyCycle = 0;
int BotElement_SSR_DutyCycle = 0;

unsigned long ElementDutyCycleMsLast = millis();
unsigned long ElementDutyCycleMsNow ;

unsigned long LidElementOnMs;  // Time at start of duty cycle
unsigned long MidElementOnMs;
unsigned long BotElementOnMs;
unsigned long LidElementOffMs;  // Time at end of duty cycle
unsigned long MidElementOffMs;
unsigned long BotElementOffMs;

bool          LidElementIsOn = false;
bool          MidElementIsOn = false;
bool          BotElementIsOn = false;

int  ElementStartPeriodNow;  // 0..2 to determine start time of each element
int  ElementStartPeriodLast = 0;

int ResultKilnControlUpdate;     // Return codes
int ResultElementControl;


// functions for menu actions
bool pauseMenu()
{
  runMenu = false;
  scrSaverEnter = true;
}
bool ledOn()
{
  Serial.println("Main Power on!");
  digitalWrite(MAIN_RELAY_240V_PIN, ledCtrl = 0);
  return false;
}

bool ledOff()
{
  Serial.println("Main Power off!");
  digitalWrite(MAIN_RELAY_240V_PIN, ledCtrl = 1);
  return false;
}

bool quit()
{
  Serial.println("Quiting after action call");
  return true;
}


/*----------------------( MENU DEFINITIONS )---------------------------*/
// Define the menu structure and link action functions to it

MENU(KilnOps, "KilnOps"    // Main Menu line 1

     , FIELD(DegreesF_A, "LID", "F", 0, 1600, 100, 10)
     , FIELD(DegreesF_B, "MID", "F", 0, 1600, 100, 10)
     , FIELD(DegreesF_C, "BOT", "F", 0, 1600, 100, 10)
     , FIELD(F_Temp, "SetPoint", "F", 0, 1600, 100, 10) // Range 0..1600  big step=100 small step=10

    );

MENU(DownLoad, "DownLoad"  // Main Menu line 2

     , OP("A", quit)
     , OP("B", quit)
    );


MENU(Dcycle, "Duty Cycles"      // Main Menu line 3

     , FIELD(LidElement_SSR_DutyCycle, "LidDutyCycle", "%", 0, 100, 10, 1) // Range
     , FIELD(MidElement_SSR_DutyCycle, "MidDutyCycle", "%", 0, 100, 10, 1) // Range
     , FIELD(BotElement_SSR_DutyCycle, "BotDutyCycle", "%", 0, 100, 10, 1) // Range

    );


MENU(Diags, "Diagnostics"   // Main Menu line 4

     , OP("KilnPowerON" , ledOn)
     , OP("KilnPowerOFF", ledOff)
     , FIELD(F_Temp, "SetPoint", "F", 0, 1600, 100, 10) // Range 0..1600  big step=100 small step=10
    );

MENU(mainMenu, "Main menu",

     SUBMENU(KilnOps),
     SUBMENU(DownLoad),
     SUBMENU(Dcycle),
     SUBMENU(Diags),
     OP("Exit", pauseMenu)
    );

void scrSaver()     //  Startup Screen
{
  if (scrSaverEnter)
  {
    lcd.clear();
    lcd.print("   YourDuino.com ");
    lcd.setCursor(0, 1);
    lcd.print(" KILN CONTROL V0.917 ");
    lcd.setCursor(0, 3);
    lcd.print("  -click to START-");
    scrSaverEnter = false;
  }
}

menuLCD menu_lcd(lcd, 20, 4); //set the menu output device

TimePlot MyPlot;

void setup()   /****** SETUP: RUNS ONCE ******/
{
  pinMode(LEDPIN, OUTPUT);
  digitalWrite(MAIN_RELAY_240V_PIN, HIGH);  // set INactive at PowerOn
  digitalWrite(RELAY2_PIN, HIGH);           // set INactive
  pinMode(MAIN_RELAY_240V_PIN, OUTPUT);
  pinMode(RELAY2_PIN, OUTPUT);
  pinMode(LID_SOLID_STATE_RELAY_PIN, OUTPUT);
  pinMode(MID_SOLID_STATE_RELAY_PIN, OUTPUT);
  pinMode(BOT_SOLID_STATE_RELAY_PIN, OUTPUT);

  Serial.begin(115200);

  MyPlot.SetTitle("Kiln Temperature Ramp");
  MyPlot.SetXlabel("Time");
  MyPlot.SetYlabel("TEMP (F)");
  MyPlot.SetSeriesProperties("TempF", Plot::Magenta, Plot::Solid, 2, Plot::Square);
//  MyPlot.SetSeriesProperties("PowerLevel", Plot::Cyan, Plot::Solid, 2, Plot::Circle);

  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());

  Wire.begin();        // Start I2C communications for LCD and RTC use

  rtc.begin();         // Start the RTC library

  lcd.begin(20, 4);    // Initialize the LCD and Set LCD Size
  lcd.home();
  lcd.print("System Setup");
  delay(1000);    //TEMP
  DateTime now = rtc.now();  // Read data from the RTC Chip. Use now.year,month,day,hour,minute,second
  lcd.setCursor(0, 1); //Start at character 0 on line 0
  lcd.print("RealTime Clock: ");
  lcd.setCursor(0, 2); //Start at character 0 on line 0
  lcd.print(now.year(), DEC); //
  lcd.print('/');
  lcd.print(now.month(), DEC);
  lcd.print('/');
  lcd.print(now.day(), DEC);
  lcd.print(' ');
  lcd.print(now.hour(), DEC);
  lcd.print(':');
  lcd.print(now.minute(), DEC);
  lcd.print(':');
  lcd.print(now.second(), DEC);
  //  lcd.println();

  delay(2000);
  lcd.begin(20, 4);    // Initialize the LCD and Set LCD Size //TEMP
  lcd.println("System Starting");
  menu::wrapMenus = true;
  // For Tests
  LidElement_SSR_DutyCycle = 0;
  MidElement_SSR_DutyCycle = 0;
  BotElement_SSR_DutyCycle = 0;
}//--(end setup )---

void loop()   /****** LOOP: RUNS CONSTANTLY ******/
{
  if (runMenu) mainMenu.poll(menu_lcd, allInputs);    // Check for LCD actions every loop
  else if (allInputs.read() == menu::enterCode) runMenu = true;
  else scrSaver();

  ResultKilnControlUpdate = KilnControlUpdate();

  DateTime now = rtc.now();  // Read data from the RTC Chip. Use now.year,month,day,hour,minute,second  ?? Not every loop??
  /*---( ETHERNET SERVER )------*/
  // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
    Serial.println("new client");
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // the connection will be closed after completion of the response
          client.println("Refresh: 5");  // refresh the page automatically every 5 sec
          client.println();

          client.println("<!DOCTYPE HTML>");
          client.println("<html>");
          // output the value of each analog input pin
          client.println("YOURDUINO GLASS KILN ");
          client.println("
"
); client.print("Thermocouple A: F = "); client.print(DegreesF_A); client.println("
"
); client.print(" Thermocouple B: F = "); client.print(DegreesF_B); client.println("
"
); for (int analogChannel = 0; analogChannel < 3; analogChannel++) { int sensorReading = analogRead(analogChannel); client.print("analog input "); client.print(analogChannel); client.print(" is "); client.print(sensorReading); client.println("
"
); } client.println("</html>"); // F_Temp = F_Temp +1; // Testing Menu field update break; } if (c == '\n') { // you're starting a new line currentLineIsBlank = true; } else if (c != '\r') { // you've gotten a character on the current line currentLineIsBlank = false; } } } // give the web browser time to receive the data delay(1); // close the connection: client.stop(); Serial.println("client disconnected"); } /*---( END ETHERNET SERVER )------*/ }//--(end main loop )--- /*-----( Declare User-written Functions )-----*/ /*---( User defined functions for Element Control )-----*/ int KilnControlUpdate() // To be put in the loop { ResultElementControl = ElementControl( LidElement_SSR_DutyCycle, MidElement_SSR_DutyCycle, BotElement_SSR_DutyCycle); } /*------( Element Control: Duty Cycle (0..100 %) set for each of 3 elements )------------------*/ int ElementControl(int LidElement_SSR_DutyCycle, int MidElement_SSR_DutyCycle, int BotElement_SSR_DutyCycle) { ElementDutyCycleMsNow = millis(); // Save the current time // Serial.print("Ms NOW = "); // Serial.println(ElementDutyCycleMsNow); ElementStartPeriodNow = millis() / 1000 % 3; // This returns 0,1,2,0,1,2.. at 1 second period //----------( LID Element Control )-------------------------------- if ( (ElementStartPeriodNow == LidElementStartPeriod) // Each element starts at a different one of 3 periods && (ElementStartPeriodNow != ElementStartPeriodLast) // The Start period just changed ) { DegreesF_A = thermocoupleA.readFahrenheit(); //Read temperatures at beginning of cycle DegreesF_B = thermocoupleB.readFahrenheit(); DegreesF_C = thermocoupleC.readFahrenheit(); MyPlot.SendData("TempF", DegreesF_B); Serial.println("-----( Temperature Readings )-------"); Serial.print("Thermocouple A: F = "); Serial.println(DegreesF_A); Serial.print("Thermocouple B: F = "); Serial.println(DegreesF_B); Serial.print("Thermocouple C: F = "); Serial.println(DegreesF_C); Serial.println("------------------------------------"); Serial.println(); Serial.println(" START Lid Element Duty Cycle"); ElementStartPeriodLast = ElementStartPeriodNow ; // Do it only once this cycle //---- ( Calculate the start and stop time of this element in this 3-second cycle )-------- LidElementOnMs = ElementDutyCycleMsNow; LidElementOffMs = LidElementOnMs + ((LidElement_SSR_DutyCycle * ElementPeriodMs) / 100); /* Serial.print(" LidElementOnMs = "); Serial.println(LidElementOnMs); Serial.print(" LidElementOffMs = "); Serial.println(LidElementOffMs); */ LidElementOn(); } //--------------( Wait until the right time to turn element off )------------------- if ( (ElementDutyCycleMsNow > LidElementOffMs) && (LidElementIsOn == true) ) { LidElementOff(); } //-----------( MID Element Control )------------------------------- if ( (ElementStartPeriodNow == MidElementStartPeriod) // it's our part of the 3-second cycle && (ElementStartPeriodNow != ElementStartPeriodLast) ) { Serial.println(); Serial.println(" START Mid Element Duty Cycle"); ElementStartPeriodLast = ElementStartPeriodNow ; MidElementOnMs = ElementDutyCycleMsNow; MidElementOffMs = MidElementOnMs + ((MidElement_SSR_DutyCycle * ElementPeriodMs) / 100); /* Serial.print(" MidElementOnMs = "); Serial.println(MidElementOnMs); Serial.print(" MidElementOffMs = "); Serial.println(MidElementOffMs); */ MidElementOn(); } if ( (ElementDutyCycleMsNow > MidElementOffMs) && (MidElementIsOn == true) ) { MidElementOff(); } //------------( BOTTOM Element Control )------------------------------ if ( (ElementStartPeriodNow == BotElementStartPeriod) && (ElementStartPeriodNow != ElementStartPeriodLast) ) { Serial.println(); Serial.println(" START Bottom Element Duty Cycle"); ElementStartPeriodLast = ElementStartPeriodNow ; BotElementOnMs = ElementDutyCycleMsNow; BotElementOffMs = BotElementOnMs + ((BotElement_SSR_DutyCycle * ElementPeriodMs) / 100); // Serial.print(" BotElementOnMs = "); // Serial.println(BotElementOnMs); // Serial.print(" BotElementOffMs = "); // Serial.println(BotElementOffMs); BotElementOn(); } if ( (ElementDutyCycleMsNow > BotElementOffMs) && (BotElementIsOn == true) ) { BotElementOff(); } }// END ElemmentControl //---------( Functions that support ElementControl )------------------------------------ void LidElementOn() { if (LidElement_SSR_DutyCycle != 0) { digitalWrite(LID_SOLID_STATE_RELAY_PIN, HIGH); // Turn the element on Serial.println("LID Element ON"); LidElementIsOn = true; digitalWrite(LEDPIN, HIGH); } } void LidElementOff() { if (LidElementIsOn == true) { digitalWrite(LID_SOLID_STATE_RELAY_PIN, LOW); // Turn the element off LidElementIsOn = false; Serial.println("LID Element OFF"); digitalWrite(LEDPIN, LOW); } } //------------------- void MidElementOn() { if (MidElement_SSR_DutyCycle != 0) { digitalWrite(MID_SOLID_STATE_RELAY_PIN, HIGH); Serial.println("Mid Element ON"); MidElementIsOn = true; } } void MidElementOff() { if (MidElementIsOn == true) { digitalWrite(MID_SOLID_STATE_RELAY_PIN, LOW); MidElementIsOn = false; Serial.println("Mid Element OFF"); } } //------------------- void BotElementOn() { if (BotElement_SSR_DutyCycle != 0) { digitalWrite(BOT_SOLID_STATE_RELAY_PIN, HIGH); Serial.println("Bot Element ON"); BotElementIsOn = true; } } void BotElementOff() { if (BotElementIsOn == true) { digitalWrite(BOT_SOLID_STATE_RELAY_PIN, LOW); BotElementIsOn = false; Serial.println("Bot Element OFF"); } } //---( END OF ElementControl Functions )------------------------ //*********( THE END )**********