Introduction: Immersion Cooker Controlled by Arduino

Simply put, immersion cooking is the process of cooking food immersed in hot water. But wait you say. Isn’t that the same as boiling my potatoes in hot water? Not quite. Food cooked by the immersion method is vacuumed sealed in bags.  Thus, nothing, not vitamins, not minerals, and most importantly, not flavor, is lost to the outside environment. Those juices and incredible flavors are all there in the bag, waiting to be savored.  Moreover, if you can control the heat of the water to be the exact desired temperature of what you’re cooking, the food can sit in the water for hours and never over cook. Still asking why?
Let’s say you own a large steak house with hundreds of customers a night, each desiring their steak cooked to perfection be it rare, medium-rare, medium, medium-well, or well done. Imagine five immersion cookers, each set to the perfect temp for each desired wellness. These cookers are filled with your best aged steaks, in vacuum sealed bags, seasoned to perfection. And, once these reach the perfect cooking temperature, holding them there denatures the proteins and just makes them all the more tender. The chef simply plucks your steak from the proper tank, puts a quick sear on it over their mesquite grill, lets it rest for a few minutes and sends it out on a sizzling platter; just that quick, just that simple. It makes my mouth water just writing this.
I first saw immersion cooking in action at The Kitchen, in Sacramento, CA. This is an incredible (and expensive) dining experience where the dining room and the kitchen are one, and between courses of the fixed price, single seating, meal, the patrons are invited to walk around the kitchen, talking to the chefs, learning from them how to prepare the meal, seeing how they use the tools of their trade.
Now imagine the home application. Sunday morning you pull a nice roast from the freezer, vacuum seal it and toss it in your immersion cooker. Just before dinner, you brown the outer surface in a skillet and serve it to an admiring family. Oh, let’s not forget, you cooked your vegetables in the cooker right alongside the roast.
“Isn’t this the same as crockpot cooking?”, you ask. As much as I love my crockpot for pulled pork, stews, and the like, letting a pricey piece of meat sit in a crockpot turns it to, well… pulled pork. It does not preserve the integrity of the meat.
I have priced these cookers from $750 to $1,000 in commercial cooking stores. So I set out to build own immersion cooker for considerably less.

Step 1: Disclaimers and Legalese

1. This project involves working with line voltages (120-240v). Additionally, the enclosure used to house the controller is not water-tight and care must be taken to ensure it does not get wet. Additionally, there are exposed heating elements used to heat the water in the tank. WHAT I AM SAYING IS THAT THERE IS A POTENTIAL RISK FOR ELECTROCUTION OR SERIOUS BURNS. Please do not build this project if you cannot assume this risk.
2. You can sustain serious health risks for under-cooked food. Please consult recipes and food labels when deciding on proper food cooking temperature.

WARNING:
1. NEVER OPERATE THE COOKER UNLESS THE HEATING ELEMENTS ARE IMMERSED IN WATER.
2. NEVER OPERATE THE COOKER UNLESS THE SUBMERSIBLE PUMP IS IMMERSED IN WATER.
3. CARE MUST BE TAKEN TO ENSURE THAT WATER OR OTHER LIQUID NEVER ENTERS THE CONTROLLER BOX.
4. NEVER IMMERSE YOUR HANDS INTO THE HOT WATER.
5. NEVER TOUCH THE HEATING ELEMENTS WITH POWER APPLIED.

Step 2: Parts List

1. 1 – Arduino Uno     $29.00
2. 1 – LM35 Temperature Sensor    $  0.99
3. 1 – SainSmart LCD/Keypad Shield   $  9.95
4. 1 – SainSmart 2 Channel Solid-State Relay  $  7.95
5. 1 – Electrical terminal     $ 3.75
6. 1 Project Box (6”x4”)     $5.99
7. 2 – 2KΩ resistors     $2.38
8. 2 – 200Ω resistors     $2.38
9. 20 Gauge hookup wire (3 spools, red, black, green) $8.39
10. Heat shrink tubing     $2.99
11. .023 plastic tubing (3ft)     $1.23
12. .023 nylon screw     $0.19
13. 2 – submersible water heaters    $18.00
14. 1 – submersible water pump 70 GPH   $16.99
15. 1 – plastic tub, 7 gallons     $7.99    
16. ¾” stand-offs      $1.99
17. Twist-on wire connectors    $3.21
Total:  $123.37

Step 3: Tools

• Solder iron with solder
• Hot glue gun
• Dremol Tool with various tips
• Screw drivers
• Needle-nose pliers
• Wire cutters/crimpers
• Multimeter

Step 4: Temperature Sensor Sub-Assembly

The temperature sensor sub-assembly uses the LM35 temperature sensor, some resistors, and wire. This sensor requires a circuit to provide the proper signals. This circuit is detailed in the attached drawings:

First measure the connection wires that will be used for the sensor. The sensor will sit on the floor of the Immersion Cooker on the opposite end from the heating elements. I did this to ensure that I was getting a temperature reading for all of the water, not just the water immediately surrounding the heating elements. You need three strands of wire long enough to traverse the floor and wall of the tub plus another three feet to reach to the control box. I used different wire colors as you will have a ground, 5 volt, and sensing wire.

Start by soldering an extension signal wired to the +VS terminal of the sensor. Next solder a 200Ω resistor to the central (Vout) terminal. Then solder a 2KΩ resistor to the Gnd terminal of the sensor. My local RadioShack™ did not have 200Ω or 2KΩ resistors so I used 220Ω and 2.2KΩ resistors. Next I twisted the loose ends of the resistors together. I then took a 220Ω and 2.2KΩ resistor and twisted their ends together on both ends, essentially placing them in parallel as in Figure 8 above. Add a signal wire and twist one end of the parallel resistors to the twisted ends of the resistors coming off of the LM35 sensor. Apply solder to this connection. Finally, solder a ground wire to the remaining twisted end of the parallel resistors. 

Next braid the wire strands so that you’ll be able to easily route the wire and sensor through the tubing that will become the water-tight temperature sensor channel on the bottom of the cooker.

Next construct water-tight the temperature sensor channel. First, use the hot glue gun to glue the nylon screw into the nylon tubing. Then glue the tubing along the center of the floor, and up the side of the tub.

You should be able to easliy thread your temperature sensor into the water-tight channel as depicted in the above photos. Trim the water tight channel at the upper edge of the tub.

Step 5: Heat Elements Sub-Assembly

Next, we’ll construct the heating elements. I went with two elements, although additional elements could be added and would allow the water to come to temperature quicker. The heating elements can be purchased at almost any hardware or kitchen supply shop. They are immersible and are typically used to heat water in a cup for coffee or tea.  Each element is rated at 300 watts. Using the equation A = W/V, Each electrode will draw approximately 2 amps, well within the 10 amp rating of our solid-state relays (SSR). Of note is that I initially tried to use the SainSmart 2 amp SSR but burned them out as the amps being drawn are slightly more than 2 amps.

The heating elements need to be mounted so that they do not contact the side of the tub and melt it. To do this, I found a couple of hard plastic coat hangers that would offset the elements.  I glued them together and then glued a piece of breadboard to them to provide additional protection against melting. I then secured the heating elements to them using a wire tie. This is shown in the pictures.

Finally, I used my Dremel tool to cut a slot in the side ridge of the tub and inserted the upside down coat hangers into the slot to mount the heating elements.

The submersible pump should have suction cups on the bottom and is simply connected to the side wall of the tub using these cups.  I then gathered all of the cords and used wire ties to bundle them together.

Step 6: Controller Sub-Assembly (part 1)

Now we turn our attentions to the controller.  For the controller I selected three major components, the Arduino Uno controller, the SainSmart LCD/Keypad shield and the SainSmart 10 amp, dual solid-state relay (SSR). First, drill holes in the bottom of the project box and fasten the power terminal to the box.   Next, cut the wall plugs off the ends of the submersible heating elements and the submersible pump at a length that allows them to reach the controller box sitting on the counter next to the tub. I reserved the plug from to submersible pump to be used for the mains power since it had the longest length but you can use any available power cord for this purpose.  Drill a hole large enough for all cables to enter the side of the project box and be routed to the terminal block. This hole can be sealed with a rubber grommet. If desired, you can place terminal ends on the power plug, the submersible pump, and one wire from each submersible heating element. Attach one wire from the mains power cable to each strip. Attach one wire from the submersible pump to each strip (the submersible pump will run continuously and is not routed through a relay).  Optionally, one wire from the power plug could be routed through a switch but I chose for this version of the cooker to just keep things simple. Finally, attach one wire from each submersible heating element to one side of the power strip. It does not matter which strip is used but on wired from each heating element should come off the same strip. Note that I tied a knot in the power cable inside the project box to prevent it (and the other cables) from being able to be pulled back through the hole in the box.

Next, cut two short length (~6") of twisted pair wire and connect them to the opposite strip that the submersible heating elements are fastened to.  These connections are shown in the pictures.

Step 7: Controller Sub-Assembly (part 2)

Now let’s look at the SSR.  The SSR expects four connections to the Arduino board, a +5v connection, a Gnd connection and two digital signal connections that control the two relays. I found it simplest to solder those connections to the bottom of the circuit board as. The SSR has three screw terminals per relay. Take two six inch strips of twisted pair wire and screw them into the center terminal. Then take two additional six inch strips of twisted pair wire and screw them into the left-most terminal from each relay as shown. Next fasten the entire board to the stand-offs so that the relays will rest against the faceplate of the project box.

Step 8: Controller Sub-Assembly (part 3)

Now solder the two digital control wires from the SSR to digital ports 2 and 3 of the SainSmart LCD/Keypad Shield. Solder three six inch wires to the 5v, Gnd, and analog port 1 of the SainSmart LCD/Keypad shield, respectively. 

Step 9: Controller Sub-Assembly (part 4)

Plug the SainSmart LCD/Keypad shield into the Arduino Uno making sure that all pins are properly engaged. Connect the Arduino to your computer by USB serial port and compile and download the attached code into the device. If you would like the device to default to Celsius operation, simply change the variable "iTempMode" from "FARENHEIT"  to "CELSIUS" before you compile. Note that it is possible to change between farenheit and celsius operation on-the-fly (see step 10).

Use the small screw in the top-left corner of the SainSmart LCD/Keypad shield to adjust the brightness of the display as necessary. Unplug the Arduino, shutting it down. Remove the Arduino Uno from the SainSmart LCD/Keypad shield.

Cut/drill holes in the faceplate and fasten the SainSmart LCD/Keypad shield and SainSmart SSR shield to the faceplate with screws.

Gather the red temperature sensor wire, the 5v wire from the SainSmart LCD/Keypad shield, and the Vss+ 5v wire from the SainSmart SSR together and solder them, protecting them with shrink tubing. Do the same for the black temperature sensor wire and the two Gnd wires. Connect the green wire from the temperature sensor to the analog port 1 wire.

Fasten one wire from an SSR to a wire from one of the heating elements using screw-on terminal connectors.  Repeat this for the remaining SSR and heating element. Then fasten the remaining wires from each SSR individually to a wire coming off the terminal block.

Plug in the Arduino Uno to the SainSmart LCD/Keypad shield.  Rather than trying to incorporate a separate 9 volt power supply, I decided to use an over-the-counter power supply purchased at RadioShack™.  I brought this connector in through the hole in the side of the project box and plugged it in to the Arduino Uno. The disadvantage of this arrangement is that it takes two wall sockets to run the cooker. The advantage is that you can still play with the controller (and check your connections including the temperature sensor) without having wall power applied to the heating elements.


Finally, fasten the faceplate to the project box. You can plug in the 9 volt power supply and you should see the device boot up and read the default set temperature (130°F) and the current temperature (room temperature). If you fill the tub with hot water, the temperature should rise.

Step 10: Basic Operations

To raise or lower the set temperature, use the ‘Up’ and ‘Down’ keys, respectfully. After adjusting to the desired temperature, use the ‘Select’ key to set the new “Set” temperature. The SSRs should click in response to changes in the set temperature verses the current temperature.  If you would rather work in centigrade, use the ‘Left’ key to bring up an options menu. Use the ‘Up’ and ‘Down’ keys to select between modes and the ‘Select’ key to enable your choice.  For future, I intend to add a clock and the clock functions will also be a sub-menu entered by this method. The ‘Rst’ key will reboot the device and the ‘Right’ key is currently ignored.

I highly recommend taking the Arduino/SainSmart circuits for a test drive before connecting to 120v power. Simply fill the tub with hot water. Plug in the Arduino 9v power supply. The LCD display should come up with a “Booting…” message and finally display “Immersion Cooker” and a software version number. During the booting phase, the software is instantiating a temperature reading array that is used to perform signal smoothing. After a few seconds, the default set temperature (130°F) and the current temperature should be displayed.  The temperature may vary significantly for the first few seconds as the sensor comes to true temperature but should settle in. I used an analog cooking thermometer in the water to verify the correctness of my temperature sensor readings.  As you raise or lower the set temperature, you should hear the SSRs click.  Additionally, you should check the continuity of the relays with a multi-meter to ensure the SSRs are working correctly.  I would also use a multi-meter to check all of my 120v connections to ensure there are no shorts.  Once you are assured that all of your electrical connections are correct, you can connect the 120v circuit to main power.

Step 11: Your First Meal

First, in order to use the cooker, you must have your food in vacuum sealed bags.  For my first meal, I seasoned some vegetables and vacuum sealed them. Next, I took about 20 large shrimp, and sealed them in a bag with a few pats of butter and some garlic. Finally, two New York strip steaks, nicely seasoned with a little salt, pepper, onion powder, and garlic powder, vacuum sealed in their own bag. 
I like my steaks medium rare which is about 140°F, and thus, that is where I set the temperature.  The shrimp will cook at a lower temperature and thus you will over cook them at this temperature. I would leave them in the water no longer than 30 minutes. Likewise, leaving the vegetables in the water for more than one hour will cause them to overcook.

But, let’s talk about the real star of the meal; those juicy New York steaks.  They can sit in the water for hours and will never overcook at this temperature. I would let them sit for at least one hour but the longer they stay at temperature, the more tender they will become.
When you’re ready to eat, simply pull the steaks out of their bags. Place them in fry pan in some canola oil over very high heat, just long enough to put a nice sear on them. Plate the shrimp, vegetables, and the steaks. If you’d like you can make a nice port wine reduction by taking three cups of port wine, one tablespoon of beef bullion, and some Italian spices and reducing them over medium heat until the liquid coats a spoon.  Bon Appetite’.

Step 12: Wiring Diagram

The circuits in this project are in the attached drawing.

Step 13:

/*****************************************************/
/* */
/* Title: Imersion Cooker Controller */
/* Version: 1.0 */
/* Version Date: 05/01/2012 */
/* Author: Edward A Ranzenbach */
/* MCWSM */
/* */
/* Description: */
/* This sketch runs on the Arduino Uno platform */
/* with a SainSmart 1602 LCD/Keyboard shield, */
/* SainSmart solid state relay device and LM35 */
/* temperature sensor. It is designed to control */
/* two common heating elements and a small */
/* immersible pump in an immersion cooker. The */
/* user simply dials in a set temperature using the */
/* keyboard and the contrioller strobes the heating */
/* elements to achieve and maintain the set water */
/* temperature. */
/* */
/* FUTURE: Add timer/clock functions */
/* */
/* History: */
/* */
/* 2012-05-01 - Edward A Ranzenbach */
/* Initial coding */
/* */
/*****************************************************/
#include <LiquidCrystal.h>
#define VERSION_STRING "1.0"
#define btnRIGHT 0
#define btnUP 1
#define btnDOWN 2
#define btnLEFT 3
#define btnSELECT 4
#define btnNONE 5
#define ELEMENT_1 2 // Element 1 control port
#define ELEMENT_2 3 // Element 2 control port
#define LED_PORT 13
#define THIRTY_SECONDS 30000; // 30,000 microseconds
#define KEYBOARD_PORT 0
#define TEMP_PORT 1
#define INCREASE 1
#define DECREASE -1
#define NO_CHANGE 0
#define SELECT 999
#define CELSIUS 0
#define FARENHEIT 1
#define TEMP_COUNT 10
#define KEY_BOUNCE_DELAY 333
int iTempMode = FARENHEIT; // default to Farenheit…
int iSetTemp = 130; // iSetTemp is global…
int iKeyPressIn = 0;
float fTemp[TEMP_COUNT]; // global temperature smoothing array…
int iCurrentIndex = 0; // iCurrentIndex is global index into temp array…
byte bDegreeSymbol[8] = // bitmask for ° symbol…
{
B11100,
B10100,
B11100,
B00000,
B00000,
B00000,
B00000,
B00000
};
byte bCheckMarkSymbol[8] = // bitmask for √ symbol…
{
B00000,
B00000,
B00000,
B00001,
B00010,
B10100,
B01100,
B01000
};
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // initialize the library with the numbers of the interface pins
void setup()
{
/*****************************************************/
/* */
/* Module: setup */
/* Version: 1.0 */
/* Version Date: 05/01/2012 */
/* Author: Edward A Ranzenbach */
/* Description: */
/* Initializes LCD display and displays splash */
/* screen. */
/* During the setup sequence, the temperature */
/* smoothing array is initialized with readings as */
/* well. */
/* */
/* History: */
/* */
/* 2012-05-01 - Edward A Ranzenbach */
/* Initial coding */
/* */
/*****************************************************/
String strLine1;
String strLine2;
String strElipses = ".";
lcd.createChar(0, bDegreeSymbol); // Create LCD characters
lcd.createChar(1, bCheckMarkSymbol);
lcd.begin(16, 2); // setup LCD display columns, rows...
DisplayLCD("Booting...", "");
for (int iIndex = 0; iIndex < TEMP_COUNT; iIndex++) // instantiate the temperature array for signal smoothing...
{
fTemp[iIndex] = analogRead(TEMP_PORT);
fTemp[iIndex] = ((fTemp[iIndex] * 3.3) / 1023); // 10 millivolts per degree Celsius
if (iTempMode == FARENHEIT)
{
fTemp[iIndex] = ConvertCelsiusToFarenheit(fTemp[iIndex]);// convert to Farenheit...
}
DisplayLCD("Booting...", strElipses);
strElipses += ".";
delay(100);
}
pinMode(ELEMENT_1, OUTPUT);
pinMode(ELEMENT_2, OUTPUT);
pinMode(LED_PORT, OUTPUT);
strLine1 = "Immersion Cooker"; // Create and display splash screen
strLine2 = "Version: ";
strLine2 += VERSION_STRING;
DisplayLCD(strLine1, strLine2);
delay(2000);
}
void loop()
{
/*****************************************************/
/* */
/* Module: loop */
/* Version: 1.0 */
/* Version Date: 05/01/2012 */
/* Author: Edward A Ranzenbach */
/* Description: */
/* Gets new SetTemp, if any. It then calls SetTemp */
/* to strobe the heating elements to adjust the */
/* temp. It then displays the temp info, delays one */
/* second then returns. The delay prevents the */
/* solid-state relays from being continuously */
/* turned on and off. */
/* History: */
/* */
/* 2012-05-01 - Edward A Ranzenbach */
/* Initial coding */
/* */
/*****************************************************/
String strLCDLine0 = "";
String strLCDLine1 = "";
iSetTemp = GetSetTemp(iSetTemp); // see if user wants to change temp...
SetTemp(iSetTemp); // control heating elements...
DisplayTemps(iSetTemp, GetCurrentTemp(), 'S'); // display temps…
delay(1000); // don't overdrive SSRs...
}
int GetSetTemp(int iSetTemp)
{
/*****************************************************/
/* */
/* Module: GetSetTemp */
/* Version: 1.0 */
/* Version Date: 05/01/2012 */
/* Author: Edward A Ranzenbach */
/* Description: */
/* Reads the keypad to see if adjustments are being */
/* made to the current set temp. The default is */
/* that no keys are being depressed. This returns */
/* btnNone from ReadKeypad and results in an */
/* immediate return to the caller. Otherwise, the */
/* set temperature is adjusted appropriately and */
/* new adjusted temperature. */
/* */
/* This module also allows the user to enter into */
/* a sub-menu where the can do things like changing */
/* the operating mode from Farenheit to Celsius. */
/* */
/* If the user has depressed any keys to make temp */
/* adjustments, they must depress the Select key */
/* to make those adjustments permanent and return */
/* to the caller. */
/* History: */
/* */
/* 2012-05-01 - Edward A Ranzenbach */
/* Initial coding */
/* */
/*****************************************************/
int iKeyIn;
int iNewSetTemp;
int iTempDelta; // INCREASE. DECREASE, or SELECT...
char cSetTemp[4];
boolean bSelected = false;
char cTempMode = 'S';
iNewSetTemp = iSetTemp; // start with current temp...
iKeyIn = ReadKeypad();
if (iKeyIn != btnNONE) // btnNONE = nothing to do, just return to caller…
{
do // until bSelected…
{
switch(iKeyIn)
{
case btnUP: // adjust temperature up…
iNewSetTemp += 1;
cTempMode = 'A'; // inform user adjustment in progress...
break;
case btnDOWN: // adjust temperature downward…
iNewSetTemp -= 1;
cTempMode = 'A'; // inform user adjustment in progress...
break;
case btnLEFT:
iNewSetTemp = Menu(); // options menu...
bSelected = true; // get out once menu options complete...
break;
case btnSELECT: // adjustments complete…
bSelected = true;
break;
default: // ignore other keys...
break;
}
DisplayTemps(iNewSetTemp, GetCurrentTemp(), cTempMode); // display each adjustment as it occurs...
delay(KEY_BOUNCE_DELAY); // smooth out key bounce...
iKeyIn = ReadKeypad(); // inner read loop...
} while (!bSelected);
}
return(iNewSetTemp);
}
void SetTemp(int iSetTemp)
{
/*****************************************************/
/* */
/* Module: SetTemp */
/* Version: 1.0 */
/* Version Date: 05/01/2012 */
/* Author: Edward A Ranzenbach */
/* Description: */
/* This module compares the iSetTemp parameter to */
/* the current temp. If there is a greater than one */
/* degree variance, it turns the heating elements */
/* on or off as appropriate. */
/* History: */
/* */
/* 2012-05-01 - Edward A Ranzenbach */
/* Initial coding */
/* */
/*****************************************************/
int iCurrentTemp;
iCurrentTemp = GetCurrentTemp(); // check current temp…
if (abs(iSetTemp - iCurrentTemp) >=1) // if temperature varied by more than 1 degree
{
if (iCurrentTemp > iSetTemp) // if temp too high, turn off heating elements
{
digitalWrite(ELEMENT_1, LOW);
digitalWrite(ELEMENT_2, LOW);
digitalWrite(LED_PORT, LOW);
}
else
{
digitalWrite(ELEMENT_1, HIGH); // if temp too low, turn on heating elements
digitalWrite(ELEMENT_2, HIGH);
digitalWrite(LED_PORT, HIGH);
}
}
}
int GetCurrentTemp()
{
/*****************************************************/
/* */
/* Module: GetCurrentTemp */
/* Version: 1.0 */
/* Version Date: 05/01/2012 */
/* Author: Edward A Ranzenbach */
/* Description: */
/* This module reads the current temp from the */
/* sensor and stores it to the signal smoothing */
/* array. The temperature from the sensor is in */
/* Celsius and we convert to Farenheit as required */
/* by the current operating mode. The user is then */
/* returned an interger representing the smoothed */
/* (i.e. average of all temps in the array) */
/* temperature. */
/* History: */
/* */
/* 2012-05-01 - Edward A Ranzenbach */
/* Initial coding */
/* */
/*****************************************************/
int iReturnTemp = 0;
float fCumulativeTemp = 0;
float fADCIn;
if ((iCurrentIndex) == TEMP_COUNT) // stay within array bounds...
{
iCurrentIndex = 0;
}
fADCIn = analogRead(TEMP_PORT); // get temp signal from sensor…
if (iTempMode == CELSIUS) // do conversion to Celsius…
{
fTemp[iCurrentIndex] = (((fADCIn * 3.7) / 1024) * 100);
}
else
{
fTemp[iCurrentIndex] = fADCIn; // straight reading is in farenheit…
}
for (int iIndex = 0; iIndex < TEMP_COUNT; iIndex++) // sum the last X readings...
{
fCumulativeTemp += fTemp[iIndex];
}
iReturnTemp = ((int)(fCumulativeTemp / TEMP_COUNT)); // return smoothed temp...
iCurrentIndex++; // bump index for next call...
return(iReturnTemp);
}
void DisplayTemps(int iSetTemp, int iCurrentTemp, char cTempMode)
{
/*****************************************************/
/* */
/* Module: DisplayTemps */
/* Version: 1.0 */
/* Version Date: 05/01/2012 */
/* Author: Edward A Ranzenbach */
/* Description: */
/* This module displays two lines of temperatures */
/* on the LCD display. The first is the set temp */
/* and is labelled based on whether we are */
/* adjusting it, or it is the set temperature. The */
/* second line of the display always the current */
/* temperature. */
/* */
/* If this module is called continuously, the */
/* will flicker so the caller should delay between */
/* calls. */
/* History: */
/* */
/* 2012-05-01 - Edward A Ranzenbach */
/* Initial coding */
/* */
/*****************************************************/
String strLine1;
String strLine2;
if (cTempMode == 'A') // Adjust mode...
{
strLine1 = " Adjust: ";
}
else
{
strLine1 = " Set: "; // Set mode...
}
strLine1 += iSetTemp;
strLine2 = String("Current: ");
strLine2 += iCurrentTemp;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(strLine1);
lcd.write(byte(0)); //degree symbol...
if (iTempMode == FARENHEIT)
{
lcd.print("F");
}
else
{
lcd.print("C");
}
lcd.setCursor(0, 1);
lcd.print(strLine2);
lcd.write(byte(0));
if (iTempMode == FARENHEIT)
{
lcd.print("F");
}
else
{
lcd.print("C");
}
}
void DisplayLCD(String strLine0, String strLine1)
{
/*****************************************************/
/* */
/* Module: DisplayLCD */
/* Version: 1.0 */
/* Version Date: 05/01/2012 */
/* Author: Edward A Ranzenbach */
/* Description: */
/* This module displays two lines text on the LCD */
/* display. */
/* If this module is called continuously, the */
/* will flicker so the caller should delay between */
/* calls. */
/* History: */
/* */
/* 2012-05-01 - Edward A Ranzenbach */
/* Initial coding */
/* */
/*****************************************************/
lcd.clear();
if (strLine0.length() > 0)
{
lcd.setCursor(0, 0);
lcd.print(strLine0);
}
if (strLine1.length() > 0)
{
lcd.setCursor(0, 1);
lcd.print(strLine1);
}
}
int ReadKeypad()
{
/*****************************************************/
/* */
/* Module: ReadKeypad */
/* Version: 1.0 */
/* Version Date: 05/01/2012 */
/* Author: Edward A Ranzenbach */
/* Description: */
/* Reads the SainSmart keypad. If the user */
/* depresses a key, a value is returned. That value */
/* may vary from device to device. Thus, we */
/* determine the value by seeing if we are close to */
/* the read value. */
/* History: */
/* */
/* 2012-05-01 - Edward A Ranzenbach */
/* Initial coding */
/* */
/*****************************************************/
int iKeyIn = analogRead(KEYBOARD_PORT);
if (iKeyIn > 1000) // this will be the most often result...
{
return btnNONE;
}
else if (iKeyIn < 50)
{
delay(KEY_BOUNCE_DELAY); // handle key bounce...
return btnRIGHT;
}
else if (iKeyIn < 195)
{
delay(KEY_BOUNCE_DELAY); // handle key bounce...
return btnUP;
}
else if (iKeyIn < 380)
{
delay(KEY_BOUNCE_DELAY); // handle key bounce...
return btnDOWN;
}
else if (iKeyIn < 555)
{
delay(KEY_BOUNCE_DELAY); // handle key bounce...
return btnLEFT;
}
else if (iKeyIn < 790)
{
delay(KEY_BOUNCE_DELAY); // handle key bounce...
return btnSELECT;
}
else return btnNONE; // if all else fails...
}
int Menu()
{
/*****************************************************/
/* */
/* Module: Menu */
/* Version: 1.0 */
/* Version Date: 05/01/2012 */
/* Author: Edward A Ranzenbach */
/* Description: */
/* This module provides the user a means of */
/* accessing other functionality within the */
/* immersion cooker. For version 1.0 this is limited */
/* to changing the operating mode between Farenheit */
/* and Celsius. For the future, this will include */
/* accessing clock functions. */
/* This module returns once the Select button is */
/* depressed. */
/* History: */
/* */
/* 2012-05-01 - Edward A Ranzenbach */
/* Initial coding */
/* */
/*****************************************************/
int iKeyIn;
boolean bSelected = false;
int iStartingTempMode = iTempMode;
DisplayLCD(" Celsius", "Farenheit"); // start by displaying current setting…
if (iTempMode == CELSIUS)
{
lcd.setCursor(10, 0);
}
else
{
lcd.setCursor(10, 1);
}
lcd.write(byte(1)); // display the checkmark for the current option...
do
{
iKeyIn = ReadKeypad(); // get user selection…
switch(iKeyIn)
{
case btnUP:
DisplayLCD(" Celsius", "Farenheit");
lcd.setCursor(10, 0);
lcd.write(byte(1)); // display the checkmark for the selected option...
iTempMode = CELSIUS;
break;
case btnDOWN:
DisplayLCD(" Celsius", "Farenheit");
lcd.setCursor(10, 1);
lcd.write(byte(1)); // display the checkmark for the selected option...
iTempMode = FARENHEIT;
break;
case btnSELECT: // this terminates the menu and implements the requested action...
bSelected = true;
break;
default: // ignore other keys...
break;
}
delay(KEY_BOUNCE_DELAY);
} while (!bSelected);
if (iTempMode != iStartingTempMode) // if we changed modes...
{
if (iTempMode == CELSIUS)
{
iSetTemp = (int) ConvertFarenheitToCelsius((float) iSetTemp); // convert iSetTemp to Celsius for display purposes...
}
else
{
iSetTemp = (int) ConvertCelsiusToFarenheit((float) iSetTemp);// convert iSetTemp to Farenheit for display purposes...
}
ConvertTempSmoothingArray(iTempMode); // convert the temps in the temperature smoothing array...
}
return(iSetTemp);
}
void ConvertTempSmoothingArray(int iNewTempMode)
{
/*****************************************************/
/* */
/* Module: ConvertTempSmoothingArray */
/* Version: 1.0 */
/* Version Date: 05/01/2012 */
/* Author: Edward A Ranzenbach */
/* Description: */
/* This module cycles through the temperature */
/* smoothing array converting temperatures between */
/* Farenheit and Celsius based upon the newly */
/* selected operating mode. */
/* History: */
/* */
/* 2012-05-01 - Edward A Ranzenbach */
/* Initial coding */
/* */
/*****************************************************/
int iIndex = 0;
switch(iNewTempMode)
{
case FARENHEIT:
for (int iIndex = 0; iIndex < TEMP_COUNT; iIndex++) // for each temp in the smoothing array...
{
fTemp[iIndex] = ConvertCelsiusToFarenheit(fTemp[iIndex]); // convert it to Farenheit...
}
break;
case CELSIUS:
for (int iIndex = 0; iIndex < TEMP_COUNT; iIndex++) // for each temp in the smoothing array...
{
fTemp[iIndex] = ConvertFarenheitToCelsius(fTemp[iIndex]); // convert it to Celsius...
}
break;
default:
break;
}
}
float ConvertCelsiusToFarenheit(float fCelsiusTemp)
{
/*****************************************************/
/* */
/* Module: ConvertCelsiusToFarenheit */
/* Version: 1.0 */
/* Version Date: 05/01/2012 */
/* Author: Edward A Ranzenbach */
/* Description: */
/* This module converts a temperature from Celsius */
/* to Farenheit */
/* History: */
/* */
/* 2012-05-01 - Edward A Ranzenbach */
/* Initial coding */
/* */
/*****************************************************/
return ((fCelsiusTemp * 1.8) + 32.0);
}
float ConvertFarenheitToCelsius(float fFarenheitTemp)
{
/*****************************************************/
/* */
/* Module: ConvertFarenheitToCelsius */
/* Version: 1.0 */
/* Version Date: 05/01/2012 */
/* Author: Edward A Ranzenbach */
/* Description: */
/* This module converts a temperature from */
/* Farenheit to Celsius. */
/* History: */
/* */
/* 2012-05-01 - Edward A Ranzenbach */
/* Initial coding */
/* */
/*****************************************************/
return ((fFarenheitTemp - 32.0) * .555);
}