[DIYbio] Re: Accurate Arduino-controlled Water Bath

the circuit diagram is not posted. can i have the circuit diagram cos am finding it difficult to get the actual circuit diagram. thanks

On Sunday, June 12, 2011 2:33:42 PM UTC+1, Cathal Garvey wrote:
Hey all,
I made some code to control a water bath, and it's working pretty nicely with a small camping kettle I have here.
Ingredients: Arduino, LM35dz (Temperature sensor), SMT 2000/4 (Solid State Relay), 650W camping Kettle.

I've attached pictures to show my setup, and a sample graph of temperatures I've set and reached.
Yes, I'm cooking an egg sous-vide to test it out in that picture. I'll let you know how it goes, I'm waiting for 2700 seconds on the serial monitor 'til it's done, according to this article (45 mins at 64C).
Here's the code:
//======Begin Code Dump======
/* Kettle Water Bath
  By Cathal Garvey, @onetruecathal, cathal...@gmail.com
  For DIYbio and Indiebiotech.com
  
  Co-licensed under GNU LGPL or Creative Commons Attribution, Sharealike license.
  Attribution preferably to "Cathal Garvey, Indiebiotech.com".
  
  Water baths are a useful piece of lab equipment, used to maintain accurate temperatures
  over a long period of time. They are used for activating enzymes, destroying proteins,
  incubating live samples, and old-school PCR. These days they also see use in Sous-Vide
  cooking and the art of Molecular Gastronomy, for feats such as boiling the whites of an
  egg without affecting the yolk at all (due to the difference in denaturation temperature
  between the two).
  
  However, water baths, for all their simplicity, are stupendously expensive. Worse, they
  can be awkward to DIY easily; most existing published projects feature expensive
  temperature controllers. However, with a camping kettle (to ensure low current draw),
  an LM35 temperature sensor, a Solid State Relay (the model I used was "SMT 2000/4", 
  4A max) and an Arduino, you can make a highly accurate and extremely versatile water
  bath for your DIYbio or cooking needs.
  
  Quite simple:
  1) First, solder leads to your LM35 and waterproof it. I used sugru to encase the
  whole sensor and the bare leads to stop them conducting through water to one another.
  Keep whatever waterproofing you use very thin to ensure decent heat conduction through.
  2) Splice the Solid State Relay into the power cable of the kettle. THIS IS DANGEROUS.
  Don't do it until the kettle is plugged out (DUH). Strip some of the kettle power lead
  to reveal the inner cables with their individual insulation. Take one of the power leads
  (not the ground; it may be easier to do this near the plug socket for ease of ID), and
  cut it, then strip both cut ends. Solder the 240V/4A pins of the SSR to the two cut
  ends of the cable. Then solder two breadboard jumper leads or similar wires to the
  signal pins of the SSR. Seal the relay pins and solder joins individually in electrical
  tape, then tape the SSR to the rest of the kettle wire, and then tape over the whole
  thing to seal it. When you're done, there should be no way that the cables will short
  or become exposed, putting you or someone else at risk. Keep it that way!
  3) Plug the wire for the middle pin of the LM35 into an Analog-in pin on the arduino,
  and change the code below to represent which one you used. Plug the wire for the ground
  into an Arduino ground socket, and the wire for power-in (Vs) into the 5V socket. Then
  put the sensor into the water, but not touching the element of the kettle!
  4) Plug the wires from your SSR into the arduino: One into the ground, another into a
  digital pin of your choice. The code is configured below to use pin 13 to make use of
  the handy built-in LED on that pin. The SSR has polarity; that is, one pin is ground,
  another is Voltage-in (Vin): test them using the Arduino's 5V and Gnd pins to ID them.
  The Vin pin should go in your digital socket (i.e. pin 13), the other into a Gnd pin.
  5) Take a jumper and connect pin 11 to ground. Make sure you've got the code below on
  the arduino before you do so; with the wrong code, this could burn out pin 11! You
  can turn this feature off, but that jumper will act as a "Firing Pin", only starting
  the water bath program when you pull the pin (but putting the pin back won't stop the
  program then; it's like a Grenade, once pulled it's too late to go back (without
  pressing reset (or pulling the plug (Whee nested brackets))))
  6) Set the program below to your desired target and failsafe temperatures, upload it
  to the Arduino, open the serial monitor if desired, and pull the pin. Watch it to be
  sure it's hitting and maintaining the right temperature. Experiment with the failsafe
  to ensure it's working correctly.
  7) Make **Sure** your temperature sensor is always immersed in the water. If the water
  evaporates and the sensor is dry, the kettle may get stuck on. That's dangerous.
  
  Enjoy, don't kill yourself with it please, and let me know if you try it out, I'd love
  to hear your feedback or experience with it.
  
  All the best,
  Cathal
*/

//Pins in use for Cyclercan (change as required):
const int TempPin = A0;
const int HeatPin = 13;
const int FiringPin = 11; // Set this pin to HIGH (i.e. w/switch) to start the cycler.

//Temperatures used for cycling: Change according to enzyme and primers used:
float TargetTemp = 64;
float FailSafeTemp = 70;
float GlobalError = 0.5; //Within how many degrees to maintain temperature.
float TempHolder; //Used to hold temperature readings per-cycle.

//When not pulsing, serial data must be paced but delays can't be used or program flow is interrupted.
//The following two values only apply to the PushData function.
long TimeHolder; //Used to pace data output when temperature is stable
long DataInterval = 1500; //Minimum time to wait between pushing serial data

//Timer parameters used to control heat-pulse delivery:
unsigned long RestTracker; //Used to track how long a between-pulse rest has lasted.
unsigned long HeatTracker; //Used to track how long a heat-pulse has lasted
//Heat Pulsing parameters; for fine-tuning your setup.
const int HeatPulseDur = 1000; //Sets the amount of time in milliseconds the heat-source is given each pulse
const int RestPulseDur = 1200; //Sets the time between pulses while temperature equilibriates and/or sensor updates

boolean DataLogging = true; //Alternative to Verbose; outputs csv.
boolean Debug = false; // Tell me everything, including annoying temperature read data
boolean WaitTilPinPull = true; //Sets the arduino to wait for firingpin to pull before going.

void setup()
 {
  Serial.begin(9600); //opens serial port, sets data rate to 9600 bps
  pinMode(FiringPin,INPUT);
  pinMode(TempPin,INPUT);
  pinMode(HeatPin,OUTPUT);
  if(DataLogging){
    delay(5000); //Gives you time to set up the serial monitor
    Serial.println("Time(s),Target(C),Actual(C)");
  }
 }

void loop(){
  
  while(WaitTilPinPull){ //Check are we in preflight mode, and don't start til the pin is pulled.
   digitalWrite(HeatPin,LOW); //This is just a failsafe, it'll already be low..
   if(DataLogging){PushData(0);}
   if(digitalRead(FiringPin) == HIGH){
     WaitTilPinPull = false;
   }
  } //End preflight mode.
  
   TempHolder = ReadLM35(TempPin); //Set temperature-handler to current sensor reading
   if(TempHolder >= FailSafeTemp){ //An overheat failsafe to account for potential clock malfunction/overflow
    while(true){ //The perma-true query means yes, this gets locked into a while loop forever. It's a failsafe, after all.
     digitalWrite(HeatPin,LOW);
     Serial.print("Failsafe temperature ");
     Serial.print(FailSafeTemp);
     Serial.println("C reached, system shutdown");
     delay(1000);
    }
   } //End failsafe
   HoldTemp(TargetTemp); //Maintain temperature at preset value
 }

void HoldTemp(float TargetTemp){
      if(TempHolder < (TargetTemp-GlobalError)){ //If temperature is not yet at target...
        if(DataLogging){PushData(TargetTemp);}
        HeatPulse(); //Calls Heatpulse to deliver a pulse of heat. Lovely.
      } else{ //That is, if the temperature is at least TargetTemp-GlobalError..
          digitalWrite(HeatPin,LOW); //This is **critical**; otherwise negative control of the pin is left entirely to a function
                                     // (HeatPulse()) that may or may not be called, above!
          if(DataLogging){PushData(TargetTemp);} //Keep on loggin'
        }
    }

void HeatPulse(){
//This function was written to use timer variables so that the code wouldn't depend on "delay()" functions to work;
// this makes things more flexible, but due to the potential for datatype tomfoolery I've included failsafes in the main loop.
// It wouldn't be a good idea to remove the failsafes or put any while or delay functions in this function unless you know what
// you are doing and debug it carefully; a heater that gets stuck to "on" is pretty bad news.

  if((millis() - HeatTracker) > HeatPulseDur){
   digitalWrite(HeatPin,LOW);
   if(Debug){Serial.print("millis() = ");Serial.print(millis());Serial.println(" - Ran Heat-off if-loop in HeatPulse()");}
  }

  
  if((millis() - HeatTracker) > (HeatPulseDur + RestPulseDur)){
   HeatTracker = millis();
   digitalWrite(HeatPin,HIGH);  
   if(Debug){Serial.print("millis() = ");Serial.print(millis());Serial.println(" - Ran Heat-on if-loop in HeatPulse()");}
  }
  
}


void PushData(float Targ){
  if((millis() - TimeHolder) > DataInterval){
    Serial.print(millis()/1000);
    Serial.print(",");
    Serial.print(Targ);
    Serial.print(",");
    Serial.println(ReadLM35(TempPin));
    if(Debug){
     Serial.print("HeatTracker: ");
     Serial.print(HeatTracker);
     Serial.print(" - RestTracker: ");
     Serial.print(RestTracker);
     Serial.print(" - millis()-HeatTracker= ");
     Serial.print(millis() - HeatTracker);
     Serial.println(); 
    }
    TimeHolder = millis(); //Reset TimeHolder
  }
}

float ReadLM35(int tempPin){
 float temp;
 boolean SensorDebug = false;
 int ReadNums = 100; //Set to ten, I experienced temperature staying generally 3/4C above target; erroneous sensor readings. Keep high.
 if(SensorDebug){Serial.println();} //Keeps the debug a bit tidier
 for(int i = 1; i < (ReadNums+1); i++){ //Read the sensor value five times and add them all up.
   temp = temp + analogRead(tempPin);
   if(SensorDebug){Serial.print("Debug: Reading #");Serial.print(i);Serial.print(": ");Serial.println(temp);} //Spews counts
 }
 temp = temp / ReadNums; //Average of five reads (for more accuracy, fewer outliers)
 temp = (5.0 * temp * 100.0)/1024.0;  //convert the analog data to temperature 
 return temp;
}
//======End Code Dump=======

--
-- You received this message because you are subscribed to the Google Groups DIYbio group. To post to this group, send email to diybio@googlegroups.com. To unsubscribe from this group, send email to diybio+unsubscribe@googlegroups.com. For more options, visit this group at https://groups.google.com/d/forum/diybio?hl=en
Learn more at www.diybio.org
---
You received this message because you are subscribed to the Google Groups "DIYbio" group.
To unsubscribe from this group and stop receiving emails from it, send an email to diybio+unsubscribe@googlegroups.com.
To post to this group, send email to diybio@googlegroups.com.
Visit this group at http://groups.google.com/group/diybio.
To view this discussion on the web visit https://groups.google.com/d/msgid/diybio/85cd9146-79eb-4a6a-80b0-04712d3bf95c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

  • Digg
  • Del.icio.us
  • StumbleUpon
  • Reddit
  • RSS

0 comments:

Post a Comment