Infernomat 1050 - a kiln control (Brennofensteuerung)
I had this tiny kiln on the shelv for some decades already, my parents gave it to me, when I was a teenager but it never was used due to the lacking temperature control. I only melted some metal with it but never did ceramics. It is an UHLIG U15, I think they are still sold. Max temperature 1050°C, draws 900W, so at 230V little less than 4A.
Now due to 3D printing I thought of some neat ideas that could be done in combination with the kiln. So I deceided to build a control unit for it.
At this point be warned, I take no responsibility for the things you are doing,
especially not when it comes to high voltage or high temperatures.
Do not play games if you don't trust in what you are doing.
The cheap kiln usually don 't have any control units, with some luck you can buy some random on/off control for them but there is no real temperature control.
My version got it and you can program different times and temperatures.
You need the following things:
- Thermocouple element, I got this one (not yet tested at high-temp) link
- An Arduino UNO or clone
- An Elegoo touch display shield link
- A relais which is able to switch with 5V and can handle the amps your kiln is drawing, such as the SLA-05VDC-SL-C
- One Adafruit MAX31855 board
- Plug, socket and wires for the high voltage stuff
- Some PSU for the Arduino (I looted the guts of an old Nokia charger.)
- An on/off switch (optional)
- Stuff for soldering, some screws, cable shoes, 3D printer for the housing, all depending on how you are realising it
In order to have some of the GPIOs (I/O pins of the Arduino) left, you need to reduce some functionality of the display, so I cut the pins used for the SD card slot on the display.
Yes I know I could have done better, had no side cutter at hand ;-) These pins are required for the connection of the relais (Arduino GPIO PIN 10) and the thermocouple board (Arduino GPIO PINS 11 to 13)
So the wiring goes as follows:
Arduino GPIO PIN 10 to SIG of the relais.
Arduino GPIO PIN 11 to CLK of the thermocouple board
Arduino GPIO PIN 12 to DO of the thermocouple board
Arduino GPIO PIN 13 to CS of the thermocouple board
Additionaly you need to get the 3V3 from the arduino to the thermocouple 3V3 and the 5V to the VCC of the relais. And of course GND to GND.
I soldered the power from the old Nokia PSU to the backside of the Arduino, of course you can also use the power socket or the USB socket of the Arduino. Just a matter of available room. The high voltage power L1 connected to the relais as seen on the above picture, source top, target bottom. If you choose the lowest connector, the switching of the relais is inverted.
Now the code doing the magic, I am sorry it is a total mess but I am too lazy to clean it up now. It just grew when I was testing the electronics. Of course it can be made way more elegant. Feel free to do so.
// libs for the display
#include <Elegoo_GFX.h>
#include <Elegoo_TFTLCD.h>
#include <TouchScreen.h>
#include <SPI.h>
#include <Wire.h>
// libs for the sensor
#include "Adafruit_MAX31855.h"
// display stuff, these settings are taken from Elegoo code
#define LCD_CS A3
#define LCD_CD A2
#define LCD_WR A1
#define LCD_RD A0
#define LCD_RESET A4
#define YP A3
#define XM A2
#define YM 9
#define XP 8
#define TS_MINX 120
#define TS_MAXX 900
#define TS_MINY 70
#define TS_MAXY 920
#define STATUS_X 10
#define STATUS_Y 65
// pin definition for the MAX31855 sensor board, I removed them from the display, where they are used for the SD card
#define MAXDO 12
#define MAXCS 13
#define MAXCLK 11
// define the relais at pin 10 (also removed from the display)
const int relaisIN1 = 10;
// a couple of used variables
unsigned long myTime, newTime, startTime, startTwomin, stopTwomin, newTwomin;
// maxtemp,interval temp, interval time, max time
int parameters[4] = {1050, 100, 60, 30};
int runs = 0;
float rohtemp = 650;
int timepos = 10;
float steptemp = 0;
int looptime = 0;
int oldpos = 0;
int tempcounter = 0;
int simfactor = 1;
int finished = 0;
unsigned long graphcolor = 0xF800;
float temp = 0;
float tempinc = 10;
float oldtemp = 0;
float deltemp = 0;
float loops = 0;
float numloops = 1;
// initialize the thermocouple sensor
Adafruit_MAX31855 thermocouple(MAXCLK, MAXCS, MAXDO);
// initialize the LCD display and touchscreen
Elegoo_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
// define array of 14 buttons .. yeah I know only 13 are on the screen, too lazy to fix that ;-)
Elegoo_GFX_Button buttons[14];
void setup(void) {
Serial.begin(9600);
pinMode(relaisIN1, OUTPUT);
delay(1000);
while (!Serial) delay(1); // wait for Serial on Leonardo/Zero, etc
delay(500);
Serial.print("Initializing sensor...");
if (!thermocouple.begin()) {
Serial.println("ERROR.");
while (1) delay(10);
}
Serial.println("DONE.");
Serial.println(F("TFT LCD test"));
Serial.println(graphcolor);
tft.reset();
temp = thermocouple.readCelsius();
uint16_t identifier = 0x9341;
tft.begin(identifier);
tft.setRotation(2);
tft.fillScreen(0x0000);
tft.setTextSize(2);
tft.setCursor(10, 2); tft.print("mx Temp:");
tft.setCursor(10, 20); tft.print("iv Temp:");
tft.setCursor(10, 38); tft.print("iv Zeit:");
tft.setCursor(10, 56); tft.print("mx Zeit:");
tft.setCursor(10, 153); tft.print("mx Temp:");
tft.setCursor(10, 187); tft.print("iv Temp:");
tft.setCursor(10, 222); tft.print("iv Zeit:");
tft.setCursor(10, 257); tft.print("mx Zeit:");
tft.setTextColor(0xB596);
tft.setCursor(110, 2); tft.print(parameters[0]); tft.print(" 'C");
tft.setCursor(110, 20); tft.print(parameters[1]); tft.print(" 'C");
tft.setCursor(110, 38); tft.print(parameters[2]); tft.print(" Min");
tft.setCursor(110, 56); tft.print(parameters[3]); tft.print(" Min");
tft.setCursor(0, 0);
buttons[1].initButton(&tft, 60, 90, 112, 30, 0xFFFF, 0xDDF2, 0xFFFF, "Keramik", 2);
buttons[2].initButton(&tft, 180, 90, 112, 30, 0xFFFF, 0x94B2, 0xFFFF, "Schmelze", 2);
buttons[3].initButton(&tft, 60, 125, 112, 30, 0xFFFF, 0xD340, 0xFFFF, "Glasur", 2);
buttons[4].initButton(&tft, 180, 125, 112, 30, 0xFFFF, 0x8AC8, 0xFFFF, " ", 2);
buttons[5].initButton(&tft, 150, 160, 53, 30, 0xFFFF, 0x2C5C, 0xFFFF, "-", 2);
buttons[6].initButton(&tft, 210, 160, 53, 30, 0xFFFF, 0x2C5C, 0xFFFF, "+", 2);
buttons[7].initButton(&tft, 150, 195, 53, 30, 0xFFFF, 0x2C5C, 0xFFFF, "-", 2);
buttons[8].initButton(&tft, 210, 195, 53, 30, 0xFFFF, 0x2C5C, 0xFFFF, "+", 2);
buttons[9].initButton(&tft, 150, 230, 53, 30, 0xFFFF, 0x2C5C, 0xFFFF, "-", 2);
buttons[10].initButton(&tft, 210, 230, 53, 30, 0xFFFF, 0x2C5C, 0xFFFF, "+", 2);
buttons[11].initButton(&tft, 150, 265, 53, 30, 0xFFFF, 0x2C5C, 0xFFFF, "-", 2);
buttons[12].initButton(&tft, 210, 265, 53, 30, 0xFFFF, 0x2C5C, 0xFFFF, "+", 2);
buttons[13].initButton(&tft, 60, 300, 112, 30, 0xFFFF, 0x0400, 0xFFFF, "START", 2);
for (uint8_t b = 1; b < 14; b++) {
buttons[b].drawButton();
}
steptemp = parameters[0];
}
#define MINPRESSURE 10
#define MAXPRESSURE 1000
// read temperature from sensor or do uncomment temp=temp+0.001*simfactor; for simulation
void gettemp() {
temp = thermocouple.readCelsius();
// temp = temp + 0.001 * simfactor;
}
void relais(int onoff) {
switch (onoff) {
case 0: if (digitalRead(relaisIN1) == HIGH) {
digitalWrite(relaisIN1, LOW); // relais1 off
// lower the temperature for simulation
Serial.println("off");
simfactor = -1;
} break;
case 1: if (digitalRead(relaisIN1) == LOW) {
digitalWrite(relaisIN1, HIGH); // relais1 on
// increase the temperature for simulation
simfactor = +1;
Serial.println("on");
} break;
}
}
void dostop() {
relais(0);
graphcolor = 0x001F;
tempcounter++;
if (tempcounter == 1000) {
tft.setCursor(150, 22); tft.setTextSize(2); tft.setTextColor(0x0000); tft.print(round(deltemp));
tft.setCursor(150, 22); tft.setTextSize(2); tft.setTextColor(0x07FF); tft.print(round(temp));
deltemp = temp;
tempcounter = 0;
}
if (newTwomin > (startTwomin + 120000)) {
drawtemp();
startTwomin = newTwomin;
}
if (temp < 30) {
while (1) {}
}
}
void drawtemp() {
Serial.println("draw");
tft.drawLine(timepos, 308 - (round(oldtemp / 5)), timepos + 1, 308 - (round(temp / 5)), graphcolor);
timepos = timepos + 1;
oldtemp = temp;
}
void intervall(long laufzeit) {
newTime = millis();
// set a start time
startTime = millis();
// start the two minute timer for the graph
// repeat as long as the running time is lower than the interval time laufzeit*60*1000 (minutes to seconds to milliseconds)
while ((millis() - startTime) < (laufzeit * 60 * 1000)) {
// Serial.print("L");
newTwomin = millis();
gettemp();
// draw the curve when the timer hits two minutes, then reset the timer
if (newTwomin > (startTwomin + 120000)) {
drawtemp();
startTwomin = newTwomin;
}
// if the temperature gets higher than the set temperature shut down the heating
if (temp > steptemp + 3) {
relais(0);
}
// if the temperature gets too low enable the heating again
if (temp < steptemp - 3) {
relais(1);
}
printtemp();
} // while end
loops = loops + 1;
// if there is only one heating intervall stop once it is done
if (parameters[1] == 0) {
finished = 1;
timepos = 10;
relais(0);
}
// once reached the biscuit firing go to the final temperature and hold it
if (steptemp == rohtemp) {
// heat up to the final temperature
steptemp = parameters[0];
// set the heating time for the final heating
parameters[2] = parameters[3];
} else {
// increase the temperature to the next step
steptemp = (steptemp + parameters[1]);
relais(1);
}
// if temperature is higher than biscuit them set the current target to biscuit temperature
if (steptemp > rohtemp && steptemp < parameters[0]) {
steptemp = rohtemp;
}
if (temp > parameters[0] - 5) {
relais(0);
timepos = 10;
dostop();
}
}
// this function sets the presets
void setparameters(int x, int y, int color, int mt, int it, int iz, int mz) {
tft.setTextColor(0x0000);
tft.setCursor(x, y); tft.print(parameters[0]); tft.print(" 'C");
tft.setCursor(x, y + 18); tft.print(parameters[1]); tft.print(" 'C");
tft.setCursor(x, y + 36); tft.print(parameters[2]); tft.print(" Min");
tft.setCursor(x, y + 54); tft.print(parameters[3]); tft.print(" Min");
parameters[0] = mt;
parameters[1] = it;
parameters[2] = iz;
parameters[3] = mz;
tft.setTextColor(color);
tft.setCursor(x, y); tft.print(mt); tft.print(" 'C");
tft.setCursor(x, y + 18); tft.print(it); tft.print(" 'C");
tft.setCursor(x, y + 36); tft.print(iz); tft.print(" Min");
tft.setCursor(x, y + 54); tft.print(mz); tft.print(" Min");
}
// this function updates the values time and temperature when pressing +/-
void incdec(int x, int y, int color, int orig, int factor) {
tft.setCursor(x, y); tft.setTextColor(0x0000); tft.print(parameters[orig]); tft.print(" 'C");
tft.setCursor(x, y); tft.setTextColor(0x0000); tft.print(parameters[orig]); tft.print(" Min");
newTime = millis();
// wenn touchscreen länger gedrückt beschleunige auf 10er Schritte, ansonsten +/- 1
if (newTime - myTime < 160) {
parameters[orig] = parameters[orig] + 10 * factor;
} else {
parameters[orig] = parameters[orig] + 1 * factor;
}
if (parameters[orig] < 0) parameters[orig] = 0;
tft.setCursor(x, y); tft.setTextColor(color); tft.print(parameters[orig]);
if (orig > 1) {
tft.print(" Min");
} else {
tft.print(" 'C");
}
myTime = millis();
}
void printtemp() {
tempcounter++;
if (tempcounter == 1000) {
// print the old temp with black
tft.setCursor(150, 22); tft.setTextSize(2); tft.setTextColor(0x0000); tft.print(round(deltemp));
// print the current temp
tft.setCursor(150, 22); tft.setTextSize(2); tft.setTextColor(0xB596); tft.print(round(temp));
deltemp = temp;
tempcounter = 0;
}
}
void loop(void) {
// some copied routines from elegoo for the touch control and buttons
digitalWrite(13, HIGH);
TSPoint p = ts.getPoint();
digitalWrite(13, LOW);
pinMode(XM, OUTPUT);
pinMode(YP, OUTPUT);
if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
p.x = map(p.x, TS_MINX, TS_MAXX, tft.width(), 0);
p.y = (tft.height() - map(p.y, TS_MINY, TS_MAXY, tft.height(), 0));
}
// go thru all the buttons, checking if they were pressed
for (uint8_t b = 0; b < 14; b++) {
if (buttons[b].contains(p.x, p.y)) {
buttons[b].press(true); // tell the button it is pressed
} else {
buttons[b].press(false); // tell the button it is NOT pressed
}
}
if (runs == 1) {
// clear screen
tft.fillScreen(0x0000);
// draw grid
tft.setTextColor(0xEEEE);
tft.setCursor(2, 22); tft.print("Temperatur:");
for (uint8_t i = 1; i < 27; i++) {
tft.drawLine(10, 38 + i * 10, 230, 38 + i * 10, 0x5AEB);
if ((i - 1) % 3 == 0) {
tft.drawLine(0 + i * 10, 48, 0 + i * 10, 309, 0x00EB);
} else {
tft.drawLine(0 + i * 10, 48, 0 + i * 10, 300, 0x00EB);
}
tft.setTextSize(1); tft.setTextColor(0xB596);
tft.setCursor(0, 30 + i * 10);
tft.print(1350 - i * 50);
}
// draw coords X axis, sadly no linear spacing so one by one
tft.setCursor(8, 310); tft.print("0");
tft.setCursor(35, 310); tft.print("60");
tft.setCursor(62, 310); tft.print("120");
tft.setCursor(92, 310); tft.print("180");
tft.setCursor(121, 310); tft.print("240");
tft.setCursor(152, 310); tft.print("300");
tft.setCursor(182, 310); tft.print("360");
tft.setCursor(212, 310); tft.print("420");
if (parameters[1] > 0) {
numloops = ceil(rohtemp / parameters[1]);
}
// if there are no more heating intervals go to the main temperature, otherwise go to next loop
if (parameters[1] == 0 && loops < 1) {
steptemp = parameters[0];
parameters[2] = parameters[3];
} else if (parameters[1] > 0 && loops < numloops) {
steptemp = parameters[1];
}
// relais on, start heating
relais(1);
// go to the heating loops
runs = 2;
}
if (runs == 2) {
// since there is no rtc, create a timer
if (startTime == 0) {
startTime = millis();
}
// zwei Minuten Timer starten
if (startTwomin == 0) {
startTwomin = millis();
}
// read temperature from sensor (or simulate)
gettemp();
// for how long is the program running already?
newTime = millis();
newTwomin = millis();
// draw graph all two minutes
if (newTwomin > (startTwomin + 120000)) {
drawtemp();
startTwomin = newTwomin;
}
// if destination temperature reached start the heating loop
if (temp > steptemp - 1 && parameters[1] > 0 && loops < numloops + 1) {
intervall(parameters[2]); // loop time
} else if (temp > parameters[0] - 1 && loops < 1) {
intervall(parameters[3]); // final time
}
// draw cooling graph in blue
if (numloops == loops) {
graphcolor = 0x001F;
}
printtemp();
} else {
for (uint8_t b = 1; b < 14; b++) {
if (buttons[b].justReleased()) {
buttons[b].drawButton(); // unpressed
}
if (buttons[b].justPressed()) {
buttons[b].drawButton(true); // pressed
switch (b) {
// presets
case 1: setparameters(110, 2, 0xB596, 1050, 100, 60, 30); break; // Keramik
case 2: setparameters(110, 2, 0xB596, 250, 0, 0, 30); break; // Schmelze
case 3: setparameters(110, 2, 0xB596, 1050, 0, 0, 30); break; // Glasur
case 5: incdec(110, 2, 0xB596, 0, -1);
break; // if (parameters[0]<30) parameters[0]=30; -
maxtemp
case 6: incdec(110, 2, 0xB596, 0,
1); break; // if (parameters[0]>1050)
parameters[0]=1050; + maxtemp
case 7: incdec(110, 20,
0xB596, 1, -1); break; // if
(parameters[1]<30) parameters[1]=30; - ivtemp
case
8: incdec(110, 20, 0xB596, 1, 1); break;
// if (parameters[1]>1050) parameters[1]=1050; + ivtemp
case 9: incdec(110, 38, 0xB596, 2, -1); break; // - iv Time
case 10: incdec(110, 38, 0xB596, 2, 1); break; // + iv Time
case 11: incdec(110, 56, 0xB596, 3, -1); break; // - max Time
case 12: incdec(110, 56, 0xB596, 3, 1); break; // + max Time
case 13: runs = 1; break; // start pressed
}
// little delay to prevent uncontrolled ui reactions
delay(100);
}
}
}
}
If you get some weird results from the thermocouple, make sure you have it wired correctly. Try switching the cables if the numbers look weird.