Project-GlassKilnController-Code
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 )**********