All together not a cheap setup. You can find cheaper and ready solutions on the internet, but of course it is about tinkering ;)
And the python script for controlling the relay or the heater, respectively.
tl;dr: Read out the temperature sensor every X seconds and activate the heater if current temperature is below the target temperature. Ah, and create quite ugly plot.
import time
import os , sys
from datetime import datetime
import RPi.GPIO as GPIO
import matplotlib.pyplot as plt
class SousVide :
'''
target: Float. Target temperature in degree Celsius
interval: Float. Time between measurements in seconds
timeFrame: int. Length of history in view in minutes
'''
def __init__ ( self , target = 50.0 , interval = 5.0 , timeFrame = 60 ):
now = datetime . now (). replace ( microsecond = 0 ). time ()
print "SousVide started at " + str ( now )
print "Init target temperature set at " + str ( target ) + " degree Celsius"
# Set the correct GPIO pin to 'off'
GPIO . setmode ( GPIO . BOARD )
GPIO . setup ( 36 , GPIO . OUT )
GPIO . output ( 36 , GPIO . LOW )
# Search for the sensor
busDir = "/sys/bus/w1/devices/"
self . sensDir = ""
try :
for root , dirs , files in os . walk ( busDir ):
for d in dirs :
if d . startswith ( 'w1_bus_master' ):
continue
else :
self . sensDir = busDir + d + "/"
print "Found sensor!"
except Exception as e :
print e
print "Error: Thermo sensor not found."
print "Exiting..."
sys . exit ()
# Heater
self . heaterStatus = False
# Temperature
self . interval = interval # in seconds
self . frameSize = int ( timeFrame * 60 / self . interval )
self . tempHistory = [ 1.0 ] * self . frameSize
self . tempHistoryTime = [ str ( now )] * self . frameSize
self . heaterHistory = [ 0 ] * self . frameSize
self . curTemp = 0.0
self . target = target
# Plotting
self . plotFrameSize = self . frameSize
self . figure , self . ax = plt . subplots ()
self . ax2 = self . ax . twinx ()
plt . xticks ( range ( self . plotFrameSize ), self . tempHistoryTime , rotation = 45 )
#plt.locator_params(axis='x', nbins=5)
self . plotLine , = self . ax . plot ( range ( self . plotFrameSize ), self . tempHistory , "r-" )
self . plotLineH , = self . ax2 . plot ( range ( self . plotFrameSize ), self . heaterHistory , "b-" )
plt . title ( "Temp. History" )
plt . grid ( True )
self . ax . set_ylabel ( 'Degree (C)' )
self . ax . set_xlabel ( 'Time' )
self . ax . set_ylim (( 20 ,( self . target + 15 )))
self . ax2 . set_ylabel ( 'Heater (On/Off)' )
self . ax2 . set_xlabel ( 'Time' )
self . ax2 . set_ylim ([ - 1 , 2 ])
self . ax2 . set_yticks ( range ( 4 ), [ "" , "Off" , "On" , "" ])
plt . show ( block = False )
print "Initial plot created"
print "Start measuring..."
def getTemp ( self ):
""" Read sensor data and return temperature float """
sensData = open ( self . sensDir + "w1_slave" , "r" ). read ()
sensData = sensData . split ( " \n " )[ 1 ]
temp = sensData [ sensData . find ( "t=" ) + 2 :]
return float ( temp )
def plotTemp ( self ):
plt . xticks ( range ( self . plotFrameSize ), self . tempHistoryTime [ - ( self . plotFrameSize ):], rotation = 45 )
self . plotLine . set_xdata ( range ( self . plotFrameSize ))
self . plotLine . set_ydata ( self . tempHistory [ - ( self . plotFrameSize ):])
self . plotLineH . set_ydata ( self . heaterHistory [ - ( self . plotFrameSize ):])
self . figure . canvas . draw ()
def enableHeater ( self ):
GPIO . output ( 36 , GPIO . HIGH )
self . heaterStatus = True
def disableHeater ( self ):
GPIO . output ( 36 , GPIO . LOW )
self . heaterStatus = False
def cook ( self ):
try :
while 1 :
now = datetime . now (). replace ( microsecond = 0 ). time ()
self . curTemp = self . getTemp () / 1000
print "Temperature is: " + str ( self . curTemp ) + " C"
self . tempHistory . append ( self . curTemp )
self . tempHistoryTime . append ( str ( now ))
if ( self . curTemp < self . target ) and not self . heaterStatus :
diff = self . target - self . curTemp
print "Temperature difference to target temp. is: " + str ( diff )
print ">> Activate heater"
self . enableHeater ()
elif ( self . curTemp >= self . target ) and self . heaterStatus :
diff = self . target - self . curTemp
print "Temperature difference to target temp. is: " + str ( diff )
print ">> Deactivate heater"
self . disableHeater ()
else :
print ""
self . heaterHistory . append ( int ( self . heaterStatus ))
self . plotTemp ()
time . sleep ( self . interval )
except KeyboardInterrupt :
GPIO . output ( 36 , GPIO . LOW )
print ""
print "Thanks for cooking with me."
print "Adios!"
GPIO . cleanup ()
def brokenPipe ( self ):
# not sure if this works, not tried yet...
with open ( 'error.log' , 'a' ) as eLog :
elog . write ( str ( datetime . now ()) + " \n " )
elog . write ( "Died at" , self . curTemp , "degrees. \n " )
GPIO . output ( 36 , GPIO . LOW )
print ""
print "SSH connection died. Trying not to heat until boiling :("
print "Ciao"
GPIO . cleanup ()
# Lets go
sv = SousVide ( target = 58.0 , interval = 5.0 , timeFrame = 60 )
sv . cook ()
# This is basically just for the case the SSH connection dies...
import atexit
atexit . register ( sv . brokenPipe ())
https://www.raspberrypi.org/documentation/usage/gpio-plus-and-raspi2/
http://haldean.org/sousvide/
https://willy-tech.de/temperatursensor-an-raspberry-pi/ [German]
https://raspberrypi.stackexchange.com/questions/55405/gpio-pin-voltage-is-too-low-to-energize-relay?rq=1
http://www.tacticalcode.de/2012/12/transistor-simpler-schalter-mit-raspberry-pi-gpio.html [German]