Programmable Temperature Controller + Hot Plate
Programmable Temperature Controller + Hot Plate
Heating things up is one of the most performed tasks in a lab. Quite a lot of times it is not enough to simply hold something at a certain temperature, but the rate at which something is heated and for how long is just as important. Especially when you try to develop catalysts for chemical processes, the temperature program and exact temperature control is crucial and you probably do not want to stay in the lab for 16 hours to manually adapt your temperature program. Unfortunately, programmable temperature controllers that can automate processes are really expensive. So I decided to build a highly customizable controller that is able to run temperature ramps and read multiple different temperature programs from a SD card. It also provides a logging function on the SD card that allows you to evaluate the resulting temperature profile after running a program.

It is a great hack for your heating devices, since it can be easily connected to almost any heating apparatus you can think of, as long as it allows you to also connect a thermocouple. So If you have ever thought about building the perfect electric kiln (there are multiple really good explanations online) or hot plate (take a look at the steps 6 and 7), now is your time.

Overall the controller should cost you about $45 and the hot plate about $55. You should easily be able to build this as a weekend project.

Step 1: Things you need

Programmable temperature controller:

Electronics:
  • Solid state relays (5V control voltage, 16 A load current)
  • LCD (e.g. on amazon.com
    Programmable Temperature Controller + Hot Plate
    )
  • SD card board
  • MAX 6675 controller board (e.g. on olimex.com)
  • Atmega 328 chip & socket
  • 5 x 10 k? Resistors
  • 4 x 1 k? Resistors
  • 4 x 560 ? Resistors
  • 1 µF Capacitor
  • 100 pF Capacitor
  • 2 x 22pF Capacitor
  • 16 MHz quartz oscillator
  • LM7805 5V linear voltage regulator
  • Rotary encoder
  • Mechanical 110 V switches
  • 10 A fuse and fuse holder For the casing:
  • 4 mm ply wood
  • Wood glue
  • Laser cutter
  • Primer
  • Paint

    Hot plate:

  • Metal case as support (e.g. an old computer power supply)
  • Small plastic case
  • Aluminum plate 20x20x1 cm
  • 2 Cartridge Heaters (1/4 or 6 mm) (e.g. on amazon.com
    Programmable Temperature Controller + Hot Plate
    or from China on ebay.com)
  • Type K thermocouple (1/4 or 6 mm)
  • Steel thread rod (¼ or 3/8) and nuts
  • Locking screws
  • Lead
  • Copper paste Tools
  • Long (ca. 30 cm) drill for the heating cartridge and thermocouple
  • Tap & die for Locking screw and steel thread rod
  • Drill press

    Step 2: Building the case part 1

    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    You can of course use any casing you want, but I decided to laser cut a custom one. I uploaded the files to this step (the three different file types all include the same design. I just wanted to offer you options depending on which file type works best for you). You can see in the first image where every piece goes. Start by glueing the LCD distance holder to the back of the front-top panel. While the wood glue sets, attach the side panels to the back and the top panel. A corner clamp is certainly helpful to do so. Than add the front-bottom and front-top piece.

    In case you are wondering whether wood is a good material choice for the casing, let me reduce your fears. Its autoignition temperature is about 572F [1]. Since I have added a thermal switch to the hot plate that shuts it off at 338F, you can be very sure that it is safe. Even after more than a thousand days at a constant temperature of 300F, studies showed that wood would not self ignite [2]. Furthermore air is know to be an extremely good thermal insulator. If you are planning on running the temperature controller with a apparatus that heats up the surrounding air to more than 500F: DON'T! Even if you are building a kiln the outside shouldn't be getting that hot.
    Programmable Temperature Controller + Hot Plate
    case.ai18 KB
    Programmable Temperature Controller + Hot Plate
    case.cdr1 MB
    Programmable Temperature Controller + Hot Plate
    case.eps2 MB

    Step 3: Building the case part 2

    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Since I am not a big fan of the burned edges the laser cutter leaves, I decided to get rid of them by filling the gaps with wood filler and sanding them down. You can see the result in the first picture.

    I used a triangular shaped strip of wood to strengthen the connections. They will also be used to attach the bottom later on. Cut it to fit the corners, as shown in the pictures.
    If you want you can prime and spray paint it, but that's up to you.

    Step 4: Electronics

    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Safety precautions:
    In this instructable you'll have to wire some parts with 110 volts line power and most of the heating plate is conductive metal. Unless you are not 100% sure, what you are doing (and even then I would not work without one) it is very advisable to use a ground fault circuit interrupter adapter or a safety socket when working on and with the device. These sockets will shut off the current immediately when a current leakage is present (e.g. through you). Otherwise you have to wait until one of the fuses blows which needs much more power (in Germany line power fuses blow at about 3.6 kW). GFCIs are cheap safety features that I would everybody urge to use. You can buy them e.g. on amazon.com.

    Since I am planning on using heating power up to 1 kW, the main power is led through a 10 amp fuse and a mechanical power switch with a LED power indicator. These devices can disconnect the whole controller and heating circuit from the AC power. The ground wire is fed into the heating cartridge connector and should be attached to all conductive metal parts of the casing.
    As you can see in the electronics circuit plan, one of the 110 V lines from the switch is led to the heating cartridge connector through a solid state relay and a second mechanical switch, whereas the second line is wired directly to the heating cartridge connector. When pin 6 on the Atmega controller is set to HIGH and the second mechanical switch is turned on, the load circuit is closed and powers the heating cartridges. The second mechanical switch also contains a LED power indicator which visualizes the heating pulses.
    The low voltage supply for the controller circuit is generated by a small print transformer that generates 12 V AC which is subsequently fed into a four diode bridge rectifier and a LM7805 linear voltage regulator.
    For measuring the temperature, we use a K-type thermocouple and MAX6675 controller board (MOD-TC). The MAX6675 chip has an internal 12-bit AD converter and can be connected via a serial interface. The LCD (SainSmart IIC/I2C/TWI Serial 2004 20x4 LCD Module Shield), is connected with the Atmega's I2C port and the SD-Card interface board is wired with the SPI interface. For navigation and setting the parameters we use a rotary encoder with integrated push button.
    The heating plate also contains a thermal switch, which mechanically disconnects the heating cartridges when the temperature is over 190C.
    Programmable Temperature Controller + Hot Plate
    PCB_etch_bottom.pdf8 KB
    Programmable Temperature Controller + Hot Plate
    PCB_etch_top.pdf25 KB

    Step 5: Code

    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    You can find the code in the attached files. I implemented the following external libraries:
    PID library
    Bounce library
    Rotary Encoder
    I2C LC Display
    MAX6675

    NOTE: In "MAX6675.h" you have to replace line 11 ( #include "WProgram.h" ) with
    #if defined(ARDUINO) && ARDUINO >= 100
    #include "Arduino.h"
    #else
    #include "WProgram.h"
    #endif
    in order to make it run on Arduino 1.05

    Use the "LiquidCrystal" library in the sub folder 2004-1.0 code, ignore "LiquidCrystal_I2C".

    When a temperature is set manually or a program is selected from the SD card, the controller starts reading the temperature ever 0.1 seconds and calculates the next heating pulse length with PID library. As we are using a lot of different libraries I used up more than 99.5% of all the free flash memory on the atmega 328 chip. Worse, I had to shorten the text and menu for the LCD display, as I was running out of SRAM. Next time, I would probably be using an atmega 2560 or swap some strings into the EEPROM.


    The different programs have to be saved as comma separated text files with incrementing file names (1.txt,10.txt) in the root folder of the SD-card. The layout of the files should look like this:
    1. Line: title (maximum 20 characters)
    2. Line: first temperature set point, first heating rate, first duration
    3. Line: second temperature set point, second heating rate, second duration
    4. Line ...

    You can use up to a maximum of 10 lines per file. It has to be terminated with a blank line.

    I used an Arduino UNO to upload the following code to the Atmega:
    //Temperature Controller #include //LCD #include #include #include #include #include //Rotary Encoder #include Encoder roundEnc(2, 3); //Pushbutton on rotary encoder #include Bounce pushButton = Bounce(4 ,5); #define I2C_ADDR 0x3F // Define I2C Address where the LCD is #define BACKLIGHT_PIN 3 #define En_pin 2 #define Rw_pin 1 #define Rs_pin 0 #define D4_pin 4 #define D5_pin 5 #define D6_pin 6 #define D7_pin 7 LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin); //Temperatursensor int tCS = 7; // CS pin on MAX6675 int tSO = 9; // SO pin of MAX6675 int tSCK = 8; // SCK pin of MAX6675 int units = 1; // Units to readout temp (0 = raw, 1 = ?C, 2 = ?F) int TCError = 0; // ErrorFlag - TC Error int tempVal = 0; // Actual-Temperatur //Display unsigned long lastMeas = 0; // Last temperature measurement unsigned long lastDisp = 0; // Last display change unsigned long lastTempDisp = 0; // Last temperature update on display unsigned long lastLog = 0; // Last Logstate unsigned long logTime = 0; int lastDispTemp = -1; int dispOn = 0; // blink int cPosX = -1; // x-position (cursor) on display int cPosY = -1; // y-position on display String emptyString = " "; unsigned long lastDispPrg = 0; //Zeit seit letztem Update des Programm-Displays // Has the button on the rotary encoder been pressed? // 0 = no // 1 = yes // 2 = yes, for more than 2 seconds int buttonPressed = 0; // Rotational Encoder // 0 = not changed // 1 = clockwise // 2 = counterclockwise int rotationState = 0; int rotationNumber = 0; int lastRotationNumber = 0; int rotationBigLeap = 0; //was the rotationl encoder moved more than ~20 int oldRotState = 0; //Program state // 0: No program // 1: Manual Setpoint // 2: Load from SD // 3: not used // 4: Program is running int progState = 0; int subProgState = 0; int lastProgState = -1; int lastSubProgState = -1; // Programs from SD-Card int progCount = 0; byte progLnCount = 0; byte progTSp[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; byte progSprr[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; word progTime[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //Which type of program is running at the moment? //-1 = 0; //0 = Single-Point //>1 = SD-Card int progRun = -1; int runLine = -1; // Actual subprogram of the SDCard unsigned long runTime = 0; // How long is the step running already unsigned long stepTime = 0; // How much time to increase the ramp unsigned long lastPuls = 0; // Last heating puls int minutesPast = 0; boolean SDOpen = false; int aTsp = 0; int Tsp = 120; //Setpoint C int MaxT = 190; // Maximal T C byte Sprr = 15; //Rate C/m byte MaxR = 20; // Maximale Ramp int TAlert = 200; // Shutown Temperature int Tdig1 = Tsp/100; //Digits for Tsp int Tdig2 = (Tsp%100)/10; int Tdig3 = Tsp%10; int Rdig1 = Sprr/10; //Digits for Rp int Rdig2 = Sprr%10; // Initialize the MAX6675 Library for our chip // Setup Serial output and LED Pin // MAX6675 Library already sets pin modes for MAX6675 chip! MAX6675 temp(tCS,tSO,tSCK,units); //PID Parameters double Setpoint, Input, Output; PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT); void setup() { //Serial.begin(9600); lcd.begin (20,4); // Switch on the backlight lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE); lcd.setBacklight(HIGH); lcd.noBlink(); lcd.clear (); pinMode(10, OUTPUT); //SC-Pin SD-Card //pinMode(53, OUTPUT); //SC-Pin SD-Karte - Arduino mega //digitalWrite(53, HIGH); pinMode(6, OUTPUT); //heating pin } void loop() { //disable Heating digitalWrite(6, LOW); //get user input getButtonState(); getRotState(); //change display setState(); //Update display displayState(); //Update Temperature //Faster measurement when program is running. In the programming stage the LCD lags if (progState == 4){ getTemp(); updateProgram(); Input = tempVal; Setpoint = aTsp; myPID.Compute(); heatingPuls(); if ((lastLog + 1000) < millis()){ updateLog(); lastLog = millis(); } } else if ((lastMeas + 10000) TAlert) || ( TCError ==1 )) { stopProgram(); delay(5000); } } void getButtonState(){ unsigned long pushTime = pushButton.duration(); pushButton.update(); //How long was the button pressed - abort? boolean pushed = pushButton.fallingEdge(); if ((pushed == true) && (pushTime = 1500) ){ buttonPressed = 2; } else { buttonPressed = 0; } } void getRotState(){ //int roataionState = 0; //int rotationNumber = 0; - Scale 0-10 long tempState = roundEnc.read(); if (tempState > oldRotState) { rotationState = 1; // rotationNumber = (tempState%100)/10; int tempRotationNumber = (tempState%100)/10; if (tempRotationNumber != rotationNumber) { rotationBigLeap = 1; } rotationNumber = tempRotationNumber; if (rotationNumber < 0) { rotationNumber = 10 + rotationNumber; } oldRotState = (tempState%100); roundEnc.write(oldRotState); } else if (tempState < oldRotState) { rotationState = 2; // rotationNumber = (tempState%100)/10; int tempRotationNumber = (tempState%100)/10; if (tempRotationNumber != rotationNumber) { rotationBigLeap = 1; } rotationNumber = tempRotationNumber; if (rotationNumber 10) || (rotationNumber < 0)) { rotationNumber = 0; } oldRotState = digit * 10; roundEnc.write(oldRotState); } //Verarbeite Eingaben am Display void setState(){ //Rotationstate 1 = right //Rotationstate 2 = left //Long pressing: abort if ((progState != 0) && (buttonPressed == 2)) { stopProgram(); buttonPressed = 0; return; } //submenu 0,1 if ((progState == 0) && (rotationState != 0)){ int select = rotationNumber%2 + 1; subProgState = select; displayState(); return; } //choose menu entry //manual setpoint if ((progState == 0) && (buttonPressed == 1)){ if (subProgState == 1) { progState = 1; subProgState = 0; displayState(); buttonPressed = 0; return; } //SD-Card if (subProgState == 2) { progState = 2; subProgState = 0; displayState(); buttonPressed = 0; return; } } //Submenue 0,1 - manual mode if ((progState == 1) && (subProgState 3) { subProgState = 0; } displayState(); rotationBigLeap = 0; return; } if ((progState == 1) && (subProgState < 4) && (rotationState == 2) && (rotationBigLeap == 1)){ subProgState = subProgState - 1; if (subProgState MaxT) { Tsp = MaxT; } //Maximal Temperature Tdig1 = Tsp/100; //Digits for Tsp Tdig2 = (Tsp%100)/10; Tdig3 = Tsp%10; buttonPressed = 0; return; } //1., 2. Digit ramp if ((progState == 1) && (subProgState == 21) && (buttonPressed == 1)) { subProgState = 22; buttonPressed = 0; return; } if ((progState == 1) && (subProgState == 22) && (buttonPressed == 1)) { //Last digit subProgState = 2; Sprr = Rdig1 * 10 + Rdig2; if (Sprr > MaxR) { Sprr = MaxR; } //Maximal/minimal ramp if (Sprr < 1) { Sprr = 1; } Rdig1 = Sprr/10; //Digits for Rp Rdig2 = Sprr%10; buttonPressed = 0; return; } //Submenu 1,1 - SD-Programm if ((progState == 2) && (subProgState progCount) { subProgState = 1; } displayState(); rotationBigLeap = 0; return; } if ((progState == 2) && (subProgState < 11) && (rotationState == 2) && (rotationBigLeap == 1)){ subProgState = subProgState - 1; if (subProgState < 1) { subProgState = progCount; } displayState(); rotationBigLeap = 0; return; } if ((progState == 2) && (subProgState 0){ Tsp = progTSp[runLine]; Sprr = progSprr[runLine]; //Load new program? if (minutesPast >= progTime[runLine]) { runLine++; if (runLine > progLnCount) { stopProgram(); return; } runTime = millis(); } } //Ramp long timeStep = (1000.0 * 60.0) / (double)Sprr; if ((stepTime + timeStep < millis()) && (aTsp < Tsp)) { aTsp++; stepTime=millis(); } else if ((stepTime + timeStep Tsp)) { aTsp--; stepTime=millis(); } } void heatingPuls(){ //pulse every 0.1 s if ((lastPuls + 100) 1000) { dly = 1000; } else if (dly < 0) { dly = 0; } //Serial.println(dly); delay(dly); digitalWrite(6, LOW); lastPuls = millis(); } } void getTemp(){ // Read the temp from the MAX6675 float loop_temperature[2]; for (int ii=0; ii (lastDisp + 1000)) && (dispOn == 0)) { lcd.setCursor(0, 1); // go to the 2nd line lcd.print("-"); lastDisp = millis(); dispOn = 1; } else if ((subProgState == 0) && (millis() > (lastDisp + 1000))){ lcd.setCursor ( 0, 1 ); // go to the 2nd line lcd.print(emptyString); lastDisp = millis(); dispOn = 0; } else if ((subProgState == 1) && (lastSubProgState != 1)){ lcd.setCursor(0, 1); lcd.print("Setpoint"); lastSubProgState = 1; updateTemp(true); } else if ((subProgState == 2) && (lastSubProgState != 2)){ lcd.setCursor(0, 1); lcd.print("SDCard "); lastSubProgState = 2; updateTemp(true); } updateTemp(); } //Manual setpoint void display1(){ if (lastProgState != 1){ cPosX = -1; cPosY = -1; lcd.noBlink(); lcd.clear (); // go home lcd.print("Setpoint"); lcd.setCursor(0, 1); String line; printSingleLine(line); lcd.print(line); lastProgState = 1; lastSubProgState = -1; updateTemp(true); } //0. Back if ((subProgState == 0) && (lastSubProgState != 0)) { lcd.noBlink(); lcd.setCursor(0, 2); lcd.print("Back "); } //1. Set Temp if ((subProgState == 1) && (lastSubProgState != 1)) { lcd.noBlink(); lcd.setCursor(0, 1); String line; printSingleLine(line); lcd.print(line); lcd.setCursor(0, 2); lcd.print("TSp "); lastSubProgState = 1; } //2. Set Ramp if ((subProgState == 2) && (lastSubProgState != 2)) { lcd.noBlink(); lcd.setCursor(0, 1); String line; printSingleLine(line); lcd.print(line); lcd.setCursor(0, 2); lcd.print("Rp "); lastSubProgState = 2; } //3. Start if ((subProgState == 3) && (lastSubProgState != 3)) { lcd.noBlink(); lcd.setCursor(0, 2); lcd.print("Start"); lastSubProgState = 3; } //4. Change T 1. Digit if ((subProgState == 11) && (lastSubProgState != 11)) { //1. Digit cPosX = 1, cPosY = 1 cPosX = 5; cPosY = 1; lcd.blink(); resetRotState(Tdig1); lcd.setCursor(cPosX, cPosY); lcd.print(Tdig1); lcd.setCursor(cPosX, cPosY); lastSubProgState = 11; } else if (subProgState == 11) { lcd.setCursor(cPosX, cPosY); Tdig1 = rotationNumber; lcd.print(Tdig1); lcd.setCursor(cPosX, cPosY); } //5. Change T 2. Digit if ((subProgState == 12) && (lastSubProgState != 12)) { //2. Digit cPosX = 6, cPosY = 1 cPosX = 6; cPosY = 1; lcd.blink(); resetRotState(Tdig2); lcd.setCursor(cPosX, cPosY); lcd.print(Tdig2); lcd.setCursor(cPosX, cPosY); lastSubProgState = 12; } else if (subProgState == 12) { lcd.setCursor(cPosX, cPosY); Tdig2 = rotationNumber; lcd.print(Tdig2); lcd.setCursor(cPosX, cPosY); } //6. Change T 3. Digit if ((subProgState == 13) && (lastSubProgState != 13)) { //3. Digit cPosX = 7, cPosY = 1 cPosX = 7; cPosY = 1; lcd.blink(); resetRotState(Tdig3); lcd.setCursor(cPosX, cPosY); lcd.print(Tdig3); lcd.setCursor(cPosX, cPosY); lastSubProgState = 13; } else if (subProgState == 13) { lcd.setCursor(cPosX, cPosY); Tdig3 = rotationNumber; lcd.print(Tdig3); lcd.setCursor(cPosX, cPosY); } //4. Change Ramp 1. Digit if ((subProgState == 21) && (lastSubProgState != 21)) { //1. Digit cPosX = 15, cPosY = 1 cPosX = 15; cPosY = 1; lcd.blink(); resetRotState(Rdig1); lcd.setCursor(cPosX, cPosY); lcd.print(Rdig1); lcd.setCursor(cPosX, cPosY); lastSubProgState = 21; } else if (subProgState == 21) { lcd.setCursor(cPosX, cPosY); Rdig1 = rotationNumber; lcd.print(Rdig1); lcd.setCursor(cPosX, cPosY); } //5. Change Ramp 2. Digit if ((subProgState == 22) && (lastSubProgState != 22)) { //2. Digit cPosX = 16, cPosY = 1 cPosX = 16; cPosY = 1; lcd.blink(); resetRotState(Rdig2); lcd.setCursor(cPosX, cPosY); lcd.print(Rdig2); lcd.setCursor(cPosX, cPosY); lastSubProgState = 22; } else if (subProgState == 22) { lcd.setCursor(cPosX, cPosY); Rdig2 = rotationNumber; lcd.print(Rdig2); lcd.setCursor(cPosX, cPosY); } updateTemp(false, cPosX, cPosY); } void display2(){ if (lastProgState != 2){ lcd.noBlink(); lcd.clear (); // go home lcd.print("SDCard"); lcd.setCursor(0, 1); if (SDOpen != true) { if (! SD.begin(10)) { stopProgram(); return; } } SDOpen = true; progCount = 0; for (int ii = 1; ii < 10; ii++){ String fname = String(ii); fname += ".txt"; char fnamec[6]; fname.toCharArray(fnamec, 6); File myFile = SD.open(fnamec, FILE_READ); if (myFile) { progCount++; } myFile.close(); } if (progCount == 0) { stopProgram(); return; } lastProgState = 2; subProgState = 1; lastSubProgState = -1; updateTemp(true); } if ((subProgState < 10) && (lastSubProgState != subProgState)) { String fname = String(subProgState); fname += ".txt"; char fnamec[6]; fname.toCharArray(fnamec, 6); File myFile = SD.open(fnamec, FILE_READ); if (! myFile) { stopProgram(); } byte line = 0; byte Slength = 0; char tempString[1]; String HeadLine = ""; String ProgLine = ""; for (byte ii=0; ii 10) || (Slength > 20) ) { stopProgram(); return; } char* accum; char buffer[Slength+1]; ProgLine.toCharArray(buffer, Slength+1); const char sep[] = ","; progTSp[line-1] = atoi(strtok_r(buffer, sep, &accum)); if (progTSp[line-1] > MaxT) { progTSp[line-1] = MaxT; } progSprr[line-1] = atoi(strtok_r(NULL, sep, &accum)); if (progSprr[line-1] > MaxR) { progSprr[line-1] = MaxR; }; if (progSprr[line-1] millis()) { return; } if (lastProgState != 4) { lcd.noBlink(); lcd.clear (); updateTemp(true); lastProgState = 4; } if ( progRun == 0 ) { lcd.setCursor(0, 0); String line; printProcessLine(line); lcd.print(line); lcd.setCursor(0, 1); printSingleLine(line); lcd.print(line); } if ( progRun > 0 ) { lcd.setCursor(0, 0); String line; printProcessLine(line); lcd.print(line); lcd.setCursor(0, 1); printProgLine(runLine, line); lcd.print(line); if (runLine != progLnCount) { lcd.setCursor(0, 2); printProgLine(runLine+1, line); lcd.print(line); } else { lcd.setCursor(0, 2); lcd.print(emptyString); } } updateTemp(); } void updateLog(){ if (SDOpen != true) { if (! SD.begin(10)) { return; } } SDOpen = true; File myFile = SD.open("T.log", FILE_WRITE); if (! myFile) { return; } myFile.print(millis() - logTime); // myFile.print(", "); // myFile.print(aTsp); myFile.print(", "); myFile.println(tempVal); myFile.close(); } As you can see in the picture, for a first (and not optimized) try these are very nice results. A really nifty way to improve the system even more would be to implement an autotunig library (e.g. from here), but it would need more space than what is available on the atmega328. An other optimization would be to use a more accurate thermocouple controller.

    Step 6: Preparing the hot plate

    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    To find out, which the best position for the heating cartridges is and whether an adequate heat distribution can be reached using 1 cm thick aluminum, I simulated the stationary temperature profile. As you can see, at about 470 K (197C, 387 F) the local deviation is roughly 3 K which is enough for my purpose.

    Start by drilling the holes for the heating cartridges and the thermocouple. This part is a little bit tricky and you definitely need a drill press for this step. You also can try to use two aluminum plates and routing machine with a ball nose cutter. Next, drill the holes for the steel thread rods and locking screws into the aluminum plates and metal support. If you want to use a thermal switch as safety device, also drill the matching holes into the aluminum plate. Lubricate the heating cartridge and thermocouple with the copper paste to improve the heat transfer to the aluminum. Afterwards use lock screws to hold them in the plate.

    Step 7: Assembling the hot plate

    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Programmable Temperature Controller + Hot Plate
    Fix the thermal switch to the plate and solder one wire of each cartridge to one pin of the switch. Connect the other pin to the plug in the metal casing and the other wire directly to a pin of the metal case.
    To stabilize the heating plate, you can glue a small plastic casing into the metal support plate and fill it with lead.

    In order to get an impression how hot the metal casing of the support gets, I simulated the temperature profile of the heating plate and the steel rods below the plate. It turns out that at 540 K (270C, 518 F) the steel rods are about 370 K (97 C, 206 F) hot. Since I limit the temperature to 180C, I could also have used a wooden casing.

    I placed a warning on the hot plate with heat resistant spray paint and used the hot plate to burn it onto the aluminum.
  •  
     

    Tag cloud

    make build easy simple arduino making homemade solar laser printed portable cheap mini building custom cardboard wooden create super lego turn paracord chocolate your paper light intel