Introduction: Cheap Arduino Controled Yogurt Maker
This is what you'll need:
- Styrofoam box big enough to fit your yogurt jar
- 8 Ohm 25W power resistor (USD 3.80 at aliexpress)
- 12V 2A power supply (got mine from an old laptop)
- Arduino Nano board (USD 11 at dealextreme)
- 12V relay module for Arduino (USD 3.40 at dealextreme)
- DS18B20 thermometer (USD 2.50 at dealextreme)
- 4.7k 1/4W resistor
- Heat sink or piece of scrap metal
- Some wiring
Since I got the Styrofoam box and power supply for free, the total amount I spent on this was less than 21 bucks!
Step 1: Wiring the Box
In order to better dissipate this heat I screwed the resistor to a piece of scrap metal sheet I had lying around. A better option would be a heat sink+fan from an old computer, but I don't have one right now...
One wire from the resistor connects to the negative of the power supply, the other one connects to the C (center) connector of the relay. The NO (normally open) connects to the 12V of the power supply.
The 12V relay module has 3 pins: Vin goes to the 12V, GND to the negative and data goes to Arduino pin 3. This module has an optocoupler, which protects the Arduino from any interference from the relay.
The Dallas DS18B20 thermometer also has 3 pins (look for the data sheet). All 3 connect to the Arduino: 1 goes to GND pin, 2 to pin 2 and 3 to +5V pin. Use some thin wire, soldering and heat shrinking tube to connect the thermometer to the Arduino. Note that you need to solder a pull up resistor (4.7K) between pin 2 and 3. Position the thermometer in the box with some tape.
Step 2: Programing the Arduino
/*
Script by Manuel Schutze - May 2013
Connections:
DS18B20 thermometer - pin 2
Relay module - pin 3
SD CS - pin 10
SD MOSI - pin 11
SD MISO - pin 12
SD CLK - pin 13
*/
// Include necessary libraries
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SD.h>
// Setup vars
long intervalTemp = 1000; // interval between temperature measurements
long intervalSD = 5000; // interval between saving data on the SD card
float tempMin = 42.5; // min temp (< truns on heat)
float tempMax = 43.5; // max temp (> truns off heat)
int rele = 3; // pin where the relay is connected
const int chipSelect = 10; // pin CS (SD card)
// Internal vars (don't change)
long previousMillisTemp = 0;
long previousMillisSD = 0;
float tempAtual = 0; // current temp
int stat = 0; // 0 = relay off / 1 = relay on
static char tempBuff[15];
File datafile;
// Configures thermometer
OneWire oneWire(2); // pin where the themometer is connected
DallasTemperature sensors(&oneWire);
DeviceAddress insideThermometer;
void setup() {
sensors.begin();
sensors.getAddress(insideThermometer, 0);
//sensors.setResolution(insideThermometer, 11); //set thermometer do max resolution
pinMode(rele, OUTPUT);
pinMode(10, OUTPUT);
Serial.begin(9600);
Serial.println("### Yogurtator ###");
if (!SD.begin(chipSelect)) {
Serial.println("SD Card failed, or not present");
} else {
Serial.println("SD card initialized.");
// Create a new file
char filename[] = "DATA00.TXT";
for (uint8_t i = 0; i < 100; i++) {
filename[4] = i/10 + '0';
filename[5] = i%10 + '0';
if (! SD.exists(filename)) {
// only open a new file if it doesn't exist
datafile = SD.open(filename, FILE_WRITE);
break; // leave the loop!
}
}
if (! datafile) {
Serial.println("Couldnt create file");
} else {
Serial.print("Logging to: ");
Serial.println(filename);
}
}
Serial.print("Found ");
Serial.print(sensors.getDeviceCount(), DEC);
Serial.println(" thermometers.");
Serial.print("Device 0 Resolution: ");
Serial.println(sensors.getResolution(insideThermometer), DEC);
Serial.print("Temp min: ");
Serial.print(tempMin);
Serial.print(" Temp max: ");
Serial.println(tempMax);
Serial.println("##################");
}
void loop() {
unsigned long currentMillis = millis();
// Checks current temp and prints it on serial
if((currentMillis - previousMillisTemp > intervalTemp)||(currentMillis - previousMillisTemp < 0)) {
// Updates previous millis
previousMillisTemp = currentMillis;
// Gets temperature
sensors.requestTemperatures();
tempAtual = sensors.getTempCByIndex(0);
// Controls relay
if(tempAtual != 0) {
if(tempAtual < tempMin) {
digitalWrite(rele, HIGH);
stat = 1;
}
if(tempAtual > tempMax) {
digitalWrite(rele, LOW);
stat = 0;
}
}
// Prints current temperature on serial
Serial.print("Temp: ");
Serial.print(sensors.getTempCByIndex(0));
Serial.print("C - Heat: ");
if(stat == 0) {
Serial.println("off");
} else {
Serial.println("on");
}
}
// Saves data on the SD card
if((currentMillis - previousMillisSD > intervalSD)||(currentMillis - previousMillisSD < 0)) {
// Updates previous millis
previousMillisSD = currentMillis;
// Creates string to save on file
String dataString = "";
dtostrf(tempAtual, 5, 2, tempBuff);
dataString.concat(tempBuff);
dataString.concat(",");
dataString.concat(stat);
// If the file is available, write to it:
if (datafile) {
datafile.println(dataString);
datafile.flush();
}
// If the file isn't open, pop up an error:
else {
Serial.println("Error opening data file.");
}
}
}
/* Code ends here! */
Everything is commented. Just set the min and max temperature and upload it to your Arduino board.
This code prints the current temperature and relay status on the serial port every 1 second, sou you have an idea what is happening inside the box!
Note that the code also calls for an SD card to save the data. This is optional and commented on the next step.
Step 3: Saving Data on a SD Card (optional)
Just insert a FAT formatted SD card and the program will create a new TXT file every time you turn it on and save the temperature and relay status every 5 seconds.
If you open the created file in Excel you can make a graph like the one on the image. Looking at the graph you see that it would be probably more interesting to set a narrower temperature range and improve heat transfer from the resistor in order to reduce temperature variations inside the box. Well, I'll post some update on this later.
Hope you have fun and make some nice yogurts!
Step 4: Update: FAN + PID Temperature Control
Ok, so a little update here. Instead of the metal plate, I added a small heat sink + fan. And ffollowing the suggestion of a fellow visitor, I added PID for temperature control. Basically PID is an algorithm that adjusts a certain output so that an input maches a set point. It has 3 constants: Kp, Ki and Kd.
In this case, the input is the measured temperature inside the box, the set point is the temperature we want (43ºC) and the output is how long the heat will be on during one cicle (60s). I found that good constants in my case are Kp=35, Ki=30 and Kd=5.
The result is a much more precise temperature control. Compare the graph below with the one on step 3. Notice that the switching off for 1 second during initial heating was a problem with the time rounding in my code, but I fixed that.
The updated code can be downloaded at: https://drive.google.com/file/d/0B-zJPVnSxmW2TXZnQVBTX0VJQzg/view?usp=sharing&resourcekey=0-Nypg74copoSEwy7HuM2G3Q
The PID lybrary can be downloaded at: http://playground.arduino.cc/Code/PIDLibrary
I'm getting some nice (and cheap) yogurt of this box!