Monday 6 July 2015

Rpi2 hysteresis to control temperature with HTML5 and DS18b20 temp sensor

I have not been playing around with the Raspberry Pi due to other commitments. However I have just completed a change that I have been planning for months.

The task is one that is pretty familiar in that I wanted to display temperature, set a temperature to which a 'switch' would go on and off. Oh a thermostat.

The layout, after a hell of a lot of tinker time, is pretty simple.

I will explain a bit about what is actually happening to put the code into context.

We have a HTML5 driven webpage which is built up with a mix of CSS and PHP. The temperture readings are provided via a DS18b20 sensor. The readings are then updating via Server-Sent Events by reading the sensor file. The setting of the set point is simple button events linked to two php functions that open a file called 'setpoint.txt'. The set point file is then polled by a python script. The script then compares the current set point to the temperature from the sensor with some hysteresis built in to reduce the number of 'on' and 'off' events.

Hmm sounds interesting. Lets cut some code and get it on your Pi. For this I have used the Raspberry Pi 2.

First create - index.php and insert the following code.

<?php
//This function opens the setpoint file and increases it by the value of 1 then closes

function uptemp() {

$setpoint = file_get_contents('setpoint.txt');
$myfile = fopen("setpoint.txt", "w") or die("Unable to open file!");
$txt = $setpoint + 1;
fwrite($myfile, $txt);
fclose($myfile);
}

//This function opens the setpoint file and decreases it by the value of 1 then closes

function downtemp(){
$setpoint = file_get_contents('setpoint.txt');
$myfile = fopen("setpoint.txt", "w") or die("Unable to open file!");
$txt = $setpoint - 1;
fwrite($myfile, $txt);
fclose($myfile);
}

?>
<!DOCTYPE HTML>
<html lang="en">
<head>
<link rel="stylesheet" href="css/style.css">
<?php 
if (isset($_POST['RedON']))
{
uptemp();
}

if (isset($_POST['RedOFF']))
{
downtemp();
}

if (isset($_POST['plugon']))
{
exec("/usr/local/bin/tdtool -n 1");
}
if (isset($_POST['plugoff']))
{
exec("/usr/local/bin/tdtool -f 1");
}

?>

  <title>Tell Stick Plug Control</title>
</head>
<body>
<h1 style="text-align: center;">Mawston Mansion</h1>
<div class="container">
    <div class="de">
        <div class="den">
          <div class="dene">
            <div class="denem">
              <div class="deneme">
<script type="text/javascript">
//check for browser support
if(typeof(EventSource)!=="undefined") {
        //create an object, passing it the name and location of the server side script
        var eSource = new EventSource("temp.php");
        //detect message receipt
        eSource.onmessage = function(event) {
                //write the received data to the page
                document.getElementById("serverData").innerHTML = event.data + "&deg;c";
        };
}
else {
        document.getElementById("serverData").innerHTML="Whoops! Your browser doesn't receive server-sent events.";
}

</script><div id="serverData"></div>
              </div>
            </div>
          </div>
        </div>
    </div>
</div>
<br><br>
<div>
<form method="post">
  <table
 style="width: 75%; text-align: left; margin-left: auto; margin-right: auto;"
 border="0" cellpadding="2" cellspacing="2">
    <body>

</div>


<div height: 50%;>
      <tr>
        <td style="text-align: center;"><button class="button" name="RedON">Increase Setpoint</button></td>
<td style="text-align: center;">Current setpoint is: <?
        $f = fopen("setpoint.txt", "r");

        // Read line from the text file and write the contents to the client
        echo fgets($f); 

        fclose($f);
?>&deg;c</td>
        <td style="text-align: center;"><button class="button" name="RedOFF">Decrease Setpoint</button></td>

<tr/>
<tr>
        <td style="text-align: center;"><button class="button" name="plugon">Heat lamp on</button></td>
<td></td>
        <td style="text-align: center;"><button class="button" name="plugoff">Heat lamp Off</button></td>
      </tr>
    </body>
  </table>
</form>

<p>

<?

?>
</p>
</div>

</body>
</html>

Then create a text file called 'setpoint.txt' and put a value of 5 in. No other information at all is needed for this file now.

Next we need our switching for the python script.

#!/usr/bin/env python

from subprocess import call
import time
import datetime

while 1:

file = open('/var/www/setpoint.txt')
tempx = int(file.read(2))
file.close()

with open("/sys/bus/w1/devices/28-000003dcfad1/w1_slave") as f, open("temps.txt", "w") as f1:
ts = time.time()
st =datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
secondline = f.readlines()[1].strip()
temperaturedata = secondline.split()[9]
temperature = float(temperaturedata[2:])
temperature /= 1000
f1.write(str(temperature) + "\n")
time.sleep(1)
if temperature + 0.5 >= tempx:
call(["tdtool", "--off", "1"])
print('The heating will remain off.', tempx, st)
print "The temperature is", temperature, "The setpoint is", tempx;
else:
print('We are switching the heat on.', tempx, st)
call(["tdtool", "--on", "1"])
print "The temperature is", temperature, "The setpoint is", tempx;

time.sleep(1)

Add a folder called 'CSS' to your /var/www folder and create a file called style.css and add the following code.

h1
{
color: white;
}

body
{
background-size:cover;
background: -webkit-radial-gradient(circle, #1a82f7, #2F2727);
color: white;
padding:50px;
margin:0px;
width:100%;
height:50%;
}
div
{
display:block;
}


.button {
   border-top: 1px solid #96d1f8;
   background: #65a9d7;
   background: -webkit-gradient(linear, left top, left bottom, from(#3e779d), to(#65a9d7));
   background: -webkit-linear-gradient(top, #3e779d, #65a9d7);
   background: -moz-linear-gradient(top, #3e779d, #65a9d7);
   background: -ms-linear-gradient(top, #3e779d, #65a9d7);
   background: -o-linear-gradient(top, #3e779d, #65a9d7);
   padding: 5px 10px;
   -webkit-border-radius: 8px;
   -moz-border-radius: 8px;
   border-radius: 8px;
   -webkit-box-shadow: rgba(0,0,0,1) 0 1px 0;
   -moz-box-shadow: rgba(0,0,0,1) 0 1px 0;
   box-shadow: rgba(0,0,0,1) 0 1px 0;
   text-shadow: rgba(0,0,0,.4) 0 1px 0;
   color: white;
   font-size: 14px;
   font-family: Georgia, serif;
   text-decoration: none;
   vertical-align: middle;
   }
.button:hover {
   border-top-color: #28597a;
   background: #28597a;
   color: #ccc;
   }
.button:active {
   border-top-color: #1b435e;
   background: #1b435e;
   }
#content
{
height: auto:
width: auto:
}

h1
{
color: white;
}

body
{
background-size:cover;
background: -webkit-radial-gradient(circle, #1a82f7, #2F2727);
color: white;
padding:50px;
margin:0px;
width:100%;
height:50%;
}
div
{
display:block;
}


.button {
   border-top: 1px solid #96d1f8;
   background: #65a9d7;
   background: -webkit-gradient(linear, left top, left bottom, from(#3e779d), to(#65a9d7));
   background: -webkit-linear-gradient(top, #3e779d, #65a9d7);
   background: -moz-linear-gradient(top, #3e779d, #65a9d7);
   background: -ms-linear-gradient(top, #3e779d, #65a9d7);
   background: -o-linear-gradient(top, #3e779d, #65a9d7);
   padding: 5px 10px;
   -webkit-border-radius: 8px;
   -moz-border-radius: 8px;
   border-radius: 8px;
   -webkit-box-shadow: rgba(0,0,0,1) 0 1px 0;
   -moz-box-shadow: rgba(0,0,0,1) 0 1px 0;
   box-shadow: rgba(0,0,0,1) 0 1px 0;
   text-shadow: rgba(0,0,0,.4) 0 1px 0;
   color: white;
   font-size: 14px;
   font-family: Georgia, serif;
   text-decoration: none;
   vertical-align: middle;
   }
.button:hover {
   border-top-color: #28597a;
   background: #28597a;
   color: #ccc;
   }
.button:active {
   border-top-color: #1b435e;
   background: #1b435e;
   }
#content
{
height: auto:
width: auto:
}

@import url(http://fonts.googleapis.com/css?family=Dosis:200,400,500,600);
html, body { height: 100%; }

.container { width: 300px; margin: 10px auto 0; }
.de .den, .de .dene, .de .denem, .de .deneme { position: absolute;  left: 50%; top: 50%; }
.de {
    position: relative;
    width: 240px;
    height: 240px;
    border-radius: 100%;
    box-shadow: 0 0 10px rgba(0, 0, 0, .1);
    background-color: transparent;
}
.den {
    position: relative;
    width: 210px;
    height: 210px;
    margin: -105px 0 0 -105px;
    border-radius: 100%;
    box-shadow: inset 0 2px 10px rgba(0, 0, 0, .5), 0 2px 20px rgba(255, 255, 255, 1);
    background: #df3341;
    background: -moz-linear-gradient(left, #df3341 0%, #d4f355 50%, #61c0ec 100%);
    background: -webkit-gradient(linear, left top, right top, color-stop(0%,#df3341), color-stop(50%,#d4f355), color-stop(100%,#61c0ec));
    background: -webkit-linear-gradient(left, #df3341 0%,#d4f355 50%,#61c0ec 100%);
    background: linear-gradient(to right, #df3341 0%,#d4f355 50%,#61c0ec 100%);
    position:relative;
}
.dene {
    width: 180px;
    height: 180px;
    margin: -90px 0 0 -90px;
    border-radius: 100%;
    box-shadow: inset 0 2px 2px rgba(255, 255, 255, .4), 0 3px 13px rgba(0, 0, 0, .85);
    background: #f2f6f5;
    background: -moz-linear-gradient(top, #f2f6f5 0%, #cbd5d6 100%);
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #f2f6f5), color-stop(100%, #cbd5d6));
    background: -webkit-linear-gradient(top, #f2f6f5 0%, #cbd5d6 100%);
    background: -o-linear-gradient(top, #f2f6f5 0%, #cbd5d6 100%);
}
.denem {
    width: 160px;
    height: 160px;
    margin: -80px 0 0 -80px;
    border-radius: 100%;
    background: #cbd5d6;
    background: -moz-linear-gradient(top, #cbd5d6 0%, #f2f6f5 100%);
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #cbd5d6), color-stop(100%, #f2f6f5));
    background: -webkit-linear-gradient(top, #cbd5d6 0%, #f2f6f5 100%);
}

.deneme {
    padding: 35px 10px 0 0px;
    width: 120px;
    height: 137px;
    display: inline-block;
    margin: -60px 0 0 -60px;
    color: #555;
    text-shadow: 1px 1px 1px white;
    font-family: 'Dosis';
    font-size: 40px;
    font-weight: 400;
    text-align: center;
}
.deneme span { position: absolute; left: 1px; top: 27px; font-size: 58px; font-weight: 200; }
.deneme strong { position: absolute; right: 10px; top: 15px; font-size: 28px; }
.setpoint { position: absolute; right: 10px; top: 15px; font-size: 28px; }
.setpoint1 {
  position: fixed; /* or absolute */
  top: 50%;
  left: 50%;
  /* bring your own prefixes */
  transform: translate(-50%, -50%);
}

Its as simple as that.

If you have followed my other videos you will already know how to get your switches working using the Tellstick but please refer to my other blog post on how to get set up.

As always any comments is appreciated and feel free to share.