The Beginning

For a while now, I’ve been wanting to create a Sous Vide cooker. However, after a very short amount of research, the cost of the commercial devices put me off the idea and I kind of forgot about it.

Then, a few months back I got my first Arduino and started playing with it for a while. After a week or two, the power and flexibility of the device became very clear and I started to look into how I could use the Arduino in more complicated projects.

And then it came to me: use the Arduino to create a Sous Vide cooker. If you search on Google you’ll find plenty of slow cookers that have been hacked into working as Sous Vide devices using an Arduino to control the temperature of the water. So that’s what I’ve decided to do.

However, instead of “copying” one of the controllers that are already available, I have decided to create my own from scratch, both as a learning process and so that I can make adoptions easier as I see fit.

I’m also looking at making a record of my progress on via this blog for anyone else who is interested in doing something similar.

Unhacked Response

Before designing any kind of controller for the SC, I decided it would be good to see its temperature response as I had read that the temperature from a SC is generally too high and unstable for use with Sous Vide. For this, I set the temperature of the SC to “High”, connected a waterproof DS18B20 temperature sensor. The code itself was simple enough, just read the temperature from the sensor once a second and print it to Serial.

The graph below shows the temperature response of the SC when set to High.

Temperature Response of Slow Cooker on High Setting

Temperature Response of Slow Cooker on High Setting

There’s a few things to note about the temperature response:

  • It’s slow! The SC took about 4 hours to get to a steady state value and the maximum temperature gain was 0.69C per minute. This isn’t very surprising given that it’s only 200W.
  • The steady state temperature is way higher than would be useful for sous vide cooking: 93C.
  • When zoomed into the steady state, the temperature was actually oscillating, which makes me think that the heating element is being switched on and off – about once every 10 seconds – which potentially makes things a bit more difficult.

Ziegler-Nichols step response

Though the next few entries of this blog, I’m going to go through a number of different tuning methods, hopefully coming up with an automated tuning system.

The first tuning system I used was theĀ Ziegler-Nichols step response method. With this method, I was able to use the above step-response (with the SC set to high), and calculate the appropriate PID values. From this, I was able to come up with the following system values:

  • A = 8.7
  • L = 753.9

Which gives the following PI values:

  • K = 0.138
  • Ti = 1507
  • Td = 377

PID Controller

Hardware

As already mentioned, I’m using a cheap Slow Cooker to heat the water to the desired temperature and an Arduino as the controller.

I’m also using a waterproof temperature sensor similar to this one and a 25A SSR (a bit overkill I know. but I hope to use the same relay to play with an oven later on).

The relay was connected to a wall plug socket that the slow cooker was plugged into. That way, the Arduino is controlling the relay, which is controlling the plug socket, which is controlling the power to the slow cooker.

The setup is quite simple, but be VERY careful if you’re not sure what you’re doing! The diagram below shows the electrical setup.

Schematic diagram of Sous Vide slow cooker

Slow cooker PID controller

Software

Although creating a PID controller in C++ is quite simple (and something that I’ve done before), I decided to use the Arduino PID library. The reasons for this were simplicity and so that I had a base to work from should I wish to improve the system.

For the moment, the setpoint is set at compile time and the temperature (and output) values are printed via the serial port to be saved in a file (via AtmelStudio). Later on, I hope to include a LCD display (showing the current temperature, when the setpoint has been reached, and the desired temperature), a few buttons (or a knob) for changing the setpoint mid-cycle, and a LED to represent the PWM being sent to the relay.

The program uses the Timer1 library to create both the slow PWM and the sampling time period, both of which are set to 1 second. Then once a second, the program measures the current temperature, calculates a moving average (of the last 10 samples), calls the above PID library to compute the desired output, and then changes the PWM duty cycle accordingly.

Below is the code used in this project.


#include "OneWire.h"
#include "DallasTemperature.h"
#include "TimerOne.h"
#include "Streaming.h"
#include "PID_v1.h"

int T = 1; // Time period of testing in seconds
#define ONE_WIRE_BUS 2 // Pin input for temperature probe
#define RelayOutput 10 // Pin connected to the relay
double setpoint = 58; // Desired temperature
double temp;

// A and L were calculated by hand using the step response.
double A = 8.6695;
double L = 753.86957;

double K = 1.2 / A;
double Ti = 2 * L;
double Td = L / 2;
double eTot = 0;

double Kp = K;
double Ki = K / Ti;
double Kd = K * Td;

double history[10];

double out;
PID myPID(&temp, &out, &tempSp, Kp, Ki, Kd, DIRECT);

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensor(&oneWire);

void callback(){
// Obtaining the current temperature
sensor.requestTemperatures();
temp = sensor.getTempCByIndex(0);

// Calculating the moving average of the last 10 data points
for (int i = 0; i < 9; i++)
history[i] = history[i+1];
history[9] = temp;
double tTemp = 0;
for (int i = 0; i < 10; i++)
tTemp += history[i];
tTemp /= 10;
temp = tTemp;

// Calculat the PID output
myPID.Compute();
Timer1.pwm(RelayOutput, out*1024);

Serial << temp << “\t” << tempSp << “\t” << out*1024 << endl;
}

void setup()
{
// Setting up serial line
Serial.begin(9600);

// Setting up sensor
sensor.begin();
sensor.setResolution(0, 12);

// Setting up timer
Timer1.initialize(1000000*T);
Timer1.attachInterrupt(callback);
Timer1.pwm(RelayOutput, 0);

// Setup the PID controller
myPID.SetMode(AUTOMATIC);
myPID.SetOutputLimits(0, 1);
myPID.SetSampleTime(1000);

}

void loop()
{
// Not actually anything in here!
}

I’m sure there‘s ways of making the above code more efficient, but for now it does the job well and I’ll look for ways of improving it more in the near future.

Results

Using the above hardware, code, and PID parameters, I was able to obtain the following step response with the setpoint equal to 58C.

The response wasn’t that bad for a first attempt. The system has an initial overshoot of 4C and had a steady state oscillation of about 0.2C. It’s certainly usable, and I’m sure the initial overshoot wouldn’t be anywhere near as bad if I filled the tank with water that was pre-heated to a higher temperature (this tank was tested with old water from a tank in the middle of a Scottish winter!).

Temperature response with setpoint at 58C

Temperature response with setpoint at 58C

Over the next few blog entries I’ll be looking at different tuning methods and seeing what works best for this (and similar) systems.

Advertisements