After receiving some parts, I was finally able to make progress on my Kiln controller. Although my goal was to only use stuff I have lying around, I ended up ordering an AD595-AQ amplifier chip, some more durable K-Type thermocouple probes, as well as a socket to plug them into. I also bought a 220V outlet and plug. This was all pretty cheap except for the chip which was about $20, or $40 total since I bought two of them, since they are apparently easy to fry if you touch the compensation terminals when it's running.
The controller is divided into two parts, the low voltage sensor/processing board and the high voltage box:
Below is the box, which is made of plastic and a grounded steel faceplate. Inside is a high powered 4PST mechanical relay which is switched using 110AC through a much smaller 5V SPDT relay I salvaged from my sparkfun hobby kit. And also a very simple milled pcb with some connectors and soldering to keep everything in place. Unfortunately I forgot to take a picture of the interior before I closed everything up.
High Voltage Switching Box |
Also from my sparkfun hobby kit is an Arduino Uno, a BJT, and a small breadboard. I can't remember where the LCD is from. I think I pulled it out of a stereo.
Shown below, the breadboard is pretty full with the 2-digit numeric LCD and amplifier circuitry taking up all the space. I ended up super-gluing the thermocouple socket to the plastic base plate since I ran out of room.
Low Voltage Amplifier and Control (Green LED = Relay, Red LED = Alarm) |
The LCD displays the temperature reading (well, the two leading digits), the BJT toggles the relay and the AD595 amplifies the thermocouple voltage to ~10 mV/°C and provides an ice point reference.
Originally, I was hoping that the Arduino would have enough resolution to read the thermocouple voltage directly. However, this was not the case since even when the reference voltage was decreased to its lowest level of 1.1V (normally 5V) the best that the 10-bit DAQ can manage is reading millivolt changes. K-Type thermocouples have a sensitivity of ~41µV/°C and it was desired to read the temperature to within a couple of degrees.
Voltage reading from my thermocouple versus temperature suggest they are linear and indeed K-Type |
Thus, the AD595 was selected to provide a gain of 247. It also has some other nifty features such as a failure alarm, which on my board outputs to the red LED and lights up when the thermocouple becomes disconnected.
One problem with the AD595 is that it's amplification is limited by the input voltage. With only 5V available from the Arduino the output was limited to 500°C. This was not sufficient for heat treating steel.
Luckily, by using the ~12VDC directly from the Arduino wall adapter I was able to increase the range to ~1000°C, which should be adequate. This required the addition of a voltage divider to the AD595 output, since the Arduino is also limited to readings of 5VDC or less. It also meant that it could no longer solely be run from USB, but that wasn't really a problem since it will only be used in the shop anyway.
|
|
A relay can only be on or off and this limits the control somewhat. Nevertheless, I decided to add PID control using this library. This really was overkill, at least until I get a voltage regulator or even a SSR to reduce the switching noise. The small relay just makes a click, but the bigger one you can definitely hear across the room.
But it worked! With a 100°C set point I was able to keep the temperature within a range of about ±10°C. Without much tuning it was kind of jittery, but I think this can be improved with better Kp, Kd, and Ki values or by turning of one or two of the kiln elements. However I believe I am at a point where the performance is good enough to run an actual heat treatment cycle. Which in and itself is a whole new challenge...
Since it's kind hard to discern my circuit from the mess of wires above, I figure I should provide some kind of a reference. Apart from the added LCD and BJT switch, my board implements pretty much the exact same circuit found here as well that shown in the AD595 datasheet.
The LCD is very straightforward and uses 7 pins to set the digit from 0-9 and 2 more pins to select which digit to display. It then toggles back and forth forever to give the appearance of displaying two digits simultaneously.
I've also included my Arduino code below, which as of March 2014 is not yet configured to run any kind of heat treatment cycle. It can however read the temperature, display it on the LCD, switch the relay, and maintain a setpoint temperature. The display magnitude (*1, *10, or *100) can be determined from the behaviour of the Arduino's built-in LED on Pin 13.
Although I've had to put my kiln project on hold for a while due to moving away from my shop for work, I hope you've enjoyed this brief update!
#include <PID_v1.h> // 1-------- 2-------- // | A | | A | // F| |B F| |B // |---G---| |---G---| // E| |C E| |C // | D | | D | // --------- --------- // | | | | | | | | | | -> pins and segments they control // CA1 CA2 G F E D C B A Decimel (not used) //pinout const int A = 2; const int B = 3; const int C = 4; const int D = 5; const int E = 6; const int F = 7; const int G = 8; const int CA1 = 9; const int CA2 = 10; const int RELAY = 12; //Define Variables we'll be connecting to double Setpoint, Input, Output; //Specify the links and initial tuning parameters; PID(&Input, &Output, &Setpoint, Kp, Ki, Kd, Direction) PID myPID(&Input, &Output, &Setpoint,200,50,0, DIRECT); int WindowSize = 5000; unsigned long windowStartTime; //used to manage display flicker //ideally replace with milli counter values ************************************ int count = 0; //Used for 2-digit display int char1, char2; boolean flip = true; //arefvolts multiplier based on DEFAULT analog reference setting below float arefVolts = 5; //Amplification derived from AD595 datasheet; 247.3 (10 mV/°C divided by 40.44 uV/°C). //Therefore, each degree C increase results in a 0.01V increase, requring a 100 multiplier to go from voltage to temperature //Doubled to 200 to account for voltage divider; increases theoretical maximum to 1000°C float multiplier = 200; // factor to tune float alpha = 0.1; //Store value from pin vbetween 0 and 1023 int analog0 =0; //store estimated pin voltage and temperature values using supplied constants float volts, temperature1, temperature2=0; //store fixed temperature value for display int temp; //Use to show magnitude //off = less than 100 //on = greater than 99, less than 1000 //blink = greater than 1000 int led = 13; //Keep track of time elapsed from program initialization unsigned long milliseconds, minutes; // the setup routine runs once when you press reset: void setup() { //Setpoint set for testing purposes at 100 Setpoint = 100; //tell the PID to range between 0 and the full window size myPID.SetOutputLimits(0, WindowSize); //turn the PID on myPID.SetMode(AUTOMATIC); windowStartTime = millis(); // initialize pins pinMode(led, OUTPUT); //set the reference pin voltage to default, allows readings of up to 5V in 4.88mV increments in 1024 resolution analogReference(DEFAULT); //Temporary serial, for debugging Serial.begin(9600); // setup serial //Initialize pins pinMode(A, OUTPUT); pinMode(B, OUTPUT); pinMode(C, OUTPUT); pinMode(D, OUTPUT); pinMode(E, OUTPUT); pinMode(F, OUTPUT); pinMode(G, OUTPUT); pinMode(CA1, OUTPUT); pinMode(CA2, OUTPUT); pinMode(RELAY, OUTPUT); } // the loop routine runs over and over again forever: void loop() { analog0=analogRead(0); //Get input voltage volts=(analog0/1023.0)*arefVolts; //Convert to estimated temperature temperature2=volts*multiplier; //Average old temperature with new to prevent jitter temperature1= alpha*temperature2 + (1-alpha)*temperature1; //Display display(temp); //Change status led to indicate magnitude of temperature display if(temperature1>99&&temperature1<1000){ digitalWrite(led, HIGH); }else if(temperature1<1000) { digitalWrite(led, LOW); }else{ if(count>400){ digitalWrite(led, HIGH); }else{ digitalWrite(led, LOW); } } delay(1); count++; if(count>400){ //Format for display temp=truncate(temperature1); // read the temp sensor on analog pin 0 Serial.println(Output); count=0; } milliseconds = millis(); minutes = milliseconds/60000; // Tests Relay pin by toggling every minute // if((minutes%2)==0){ // digitalWrite(RELAY, LOW); // }else{ // digitalWrite(RELAY, HIGH); // } Input = temperature1; myPID.Compute(); /************************************************ * turn the output pin on/off based on pid output ************************************************/ unsigned long now = millis(); if(now - windowStartTime>WindowSize){ //time to shift the Relay Window windowStartTime += WindowSize; } if(Output > now - windowStartTime){ digitalWrite(RELAY,HIGH); } else{ digitalWrite(RELAY,LOW); } } void display(int number){ char1 = number/10; char2 = number%10; if(flip==true){ digitalWrite(CA1, HIGH); digitalWrite(CA2, LOW); light(char1); flip=false; }else if(flip==false){ digitalWrite(CA1, LOW); digitalWrite(CA2, HIGH); light(char2); flip=true; } } void light(int character){ if(character==0){ digitalWrite(A, LOW); digitalWrite(B, LOW); digitalWrite(C, LOW); digitalWrite(D, LOW); digitalWrite(E, LOW); digitalWrite(F, LOW); digitalWrite(G, HIGH); }else if(character==1){ digitalWrite(A, HIGH); digitalWrite(B, LOW); digitalWrite(C, LOW); digitalWrite(D, HIGH); digitalWrite(E, HIGH); digitalWrite(F, HIGH); digitalWrite(G, HIGH); }else if(character==2){ digitalWrite(A, LOW); digitalWrite(B, LOW); digitalWrite(C, HIGH); digitalWrite(D, LOW); digitalWrite(E, LOW); digitalWrite(F, HIGH); digitalWrite(G, LOW); }else if(character==3){ digitalWrite(A, LOW); digitalWrite(B, LOW); digitalWrite(C, LOW); digitalWrite(D, LOW); digitalWrite(E, HIGH); digitalWrite(F, HIGH); digitalWrite(G, LOW); }else if(character==4){ digitalWrite(A, HIGH); digitalWrite(B, LOW); digitalWrite(C, LOW); digitalWrite(D, HIGH); digitalWrite(E, HIGH); digitalWrite(F, LOW); digitalWrite(G, LOW); }else if(character==5){ digitalWrite(A, LOW); digitalWrite(B, HIGH); digitalWrite(C, LOW); digitalWrite(D, LOW); digitalWrite(E, HIGH); digitalWrite(F, LOW); digitalWrite(G, LOW); }else if(character==6){ digitalWrite(A, LOW); digitalWrite(B, HIGH); digitalWrite(C, LOW); digitalWrite(D, LOW); digitalWrite(E, LOW); digitalWrite(F, LOW); digitalWrite(G, LOW); }else if(character==7){ digitalWrite(A, LOW); digitalWrite(B, LOW); digitalWrite(C, LOW); digitalWrite(D, HIGH); digitalWrite(E, HIGH); digitalWrite(F, HIGH); digitalWrite(G, HIGH); }else if(character==8){ digitalWrite(A, LOW); digitalWrite(B, LOW); digitalWrite(C, LOW); digitalWrite(D, LOW); digitalWrite(E, LOW); digitalWrite(F, LOW); digitalWrite(G, LOW); }else if(character==9){ digitalWrite(A, LOW); digitalWrite(B, LOW); digitalWrite(C, LOW); digitalWrite(D, HIGH); digitalWrite(E, HIGH); digitalWrite(F, LOW); digitalWrite(G, LOW); } } int truncate(float temperature){ int temp = temperature; if(temperature>99){ for(int i=0; temp > 999; i++){ temp = temp/10; } temp = temp/10; } return temp; }
No comments:
Post a Comment