Loggen pi

Uit RobotMC.be
Ga naar: navigatie, zoeken

De raspberry Pi als datalogger

JH Pi2.jpg

Alle gebruikte bestanden vindt je in deze ZIP-file :

  • log.py  : Dit is de python sketch die elk uur naar MySQL logt.
  • bar_dag.php : De php-file die de dagoverzichten grafisch voorsteld.
  • bar_maand_php : De php-file die de maandoverzichten grafisch voorsteld.
  • data_dag.php : Deze file gaat de dag-data opvragen uit MySQL, wordt gebruikt in bar_dag.php.
  • data_maand.php :Maand-data opvragen uit MySQL, wordt gebruikt in bar_maand.php.

Alle php bestanden komen in de directory var/www van de pi. De python sketch komt in de directory pi/user. Opgelet, je moet ook nog een databank in MySQL creeren met de juiste naam, structuur en de accounts om te lezen/schrijven in deze database.

Media:D3_graph.zip

Wat is een LAMP server ?

Lamp staat voor Linux als operating systeem, Apache als webserver, MySQL als database en de P staat voor PHP, de taal voor dynamische web content. Deze combinatie wordt zeer veel gebruikt om dynamische websites te hosten. Je vindt op het net talloze tutorials hoe je deze software op de Pi kunt installeren. Daarnaast heb ik ook nog "PHPMyAdmin" op de Pi geinstalleerd. Met deze tool kan je de MYSQL database zeer eenvoudig vanuit een Browser configuren / bedienen. Volgende tools op je PC zijn ook nuttig/noodzakelijk als je de Pi remote wil bedienen :

  • Een vast IP-adres voor de PI. Bij een telenet router gebruik je best een adres in de range van 192.168.0.1 tot 192.168.0.100. Deze adressen worden normaal niet gebruikt voor dynamische IP-adressen. Wel even natuurlijk even het gebruikte domein kontroleren, niet alle telenet routers zitten op 192.168.0.xxx
  • Om via SSH de command line te bedienen gebruik ik "Putty"
  • Als je met Python werkt is bedienen van de GUI ook nodig : ik gebruik "VNCviewer"
  • Om eenvoudig files te kopieren tussen PC /Pi is "WinSCP" erg handig


Het uiteindelijke resultaat :

JH Pi website.png

Hoe de sensoren verbinden aan de GPIO

Mijn systeem bestaat uit volgende sensoren :

  • 3 stuks S0 Puls tellers : Deze meten het elektrisch verbruik en geven bij elk verbruikt Wh een puls uit van 30 ms via een optokoppelaar.
  • 2 stuks Magnetische Sensoren NXP KMI18 : Deze tellen de omwentelingen van de gasteller (10 liter/puls)en waterteller (1 liter/puls).
  • 2 stuks Temperatuursensoren DS18B20 : Meten de temp. en kunnen uitgelezen worden via het 1-wire protokol.

Al deze komponenten hebben voldoende aan 3,3 VDC voedingspanning, dus ik heb gewoon de 3,3 V pin van de GPIO gebruikt om alles te voeden. De S0 Puls tellers bevatten een opto-koppelaar die in geleiding komt bij de puls van 30 ms. Deze GPIO zijn dan geschakeld als Pull-Down. De KMI18 sensoren schakelen naar massa, deze GPIO zijn dus als Pull-Up geschakeld. De DS18B20 wordt standaard aan GPIO4 geschakeld met de PI, alhoewel de laatste revisie van Raspian ook toelaat om een andere GPIO in te stellen. Je moet nog wel een externe PullUp van ca 4,7 k woorzien, de interne PullUps hebben een te grote weerstand (50 k). Ik heb alles rechtstreeks aangesloten, maar als je een foutje maakt blaas je wel de ingangen van de PI op ! Het schema vindt je hier :

JH GPIO PI.png

Met Python de pulsen tellen en opslaan in MySQL

Pulsen tellen met Python is een eitje dacht ik, maar dat viel in het begin wel tegen : er werden systematisch ca 4% pulsen te weinig geteld ! Eerst was ik in de vaste overtuiging dat de oorzaak lag in het feit dat de pulsen van 30 ms te kort waren, maar dat bleek niet het geval : de maximale latency als je de ingangen polt (met Python !)is ca 12 ms. Een puls van 30 ms kan dus in principe niet gemist worden ! Deze latency wordt veroorzaakt door het operating system : de Python Sketch wordt regelmatig stilgelegd om andere taken uit te voeren. Het probleem werd veroorzaakt door in de afloop 2* de toestand van de GPIO binnen te lezen. Indien deze toestand wijzigde tussen deze 2 afvrages, werd de puls niet geteld. De oplossing was eenvoudig : eenmaal de toestand van de GPIO binnen lezen en deze kopieren in een een variabele. Daarna gebruik je in je afloop nog alleen deze variabele. Python beschikt over goede librarys om de 1-wire sensoren uit te lezen. Je moet wel de 1-wire Linux modules installeren op de Pi. Ook de verbinding met de MySQL database is vrij eenvoudig. Mijn Python programma doet het volgende :

  • Voortdurend de GPIO pollen van de S0 puls counters / KMI18 sensoren. Bij elke positieve flank wordt er een counter opgehoogd.
  • De syteemtijd wordt ook elke doorloop ingelezen, als het ingestelde interval bereikt wordt, start de volgende afloop :
    • Temperaturen uitlezen van de DS18B20 sensoren
    • Counters + temperaturen uploaden naar de MYSQL databank
    • Counters resetten.
    • Volgend interval vastleggen.

Ik geef dus geen tijdstip door aan MySQL. De database zelf gaat steeds een "Timestamp" opslaan te samen met de gegevens. Als de Raspberry bij de opstart verbinding heeft met het internet, gaat hij automatisch de tijd ophalen van een NPT-server. Ook met het winteruur/zomeruur wordt dan rekening gehouden. Let wel, als er geen verbinding is start de Pi op met de tijd van de laatste shutdown ! Ik heb het interval op 1 uur gezet, vooral om de levensduur van de SD-kaart te behouden. Als je elke minuut naar de SQL database schrijft, zijn dat reeds 14400 benaderingen per dag ! Het is ook raadzaam om je SD-kaart voldoende groot te kiezen (min 8 GB), omdat via "wear leveling" niet steeds dezelfde geheugencellen geschreven worden. Op die manier wordt de levensduur ook verlengd.

Opdat het python programma "log.py"in de achtergrond kan lopen, moet je het opstarten met : "sudo python log.py &"

Op deze manier blijft het programma lopen, ook als je het terminalvenster sluit. Zorg er ook voor dat er geen enkele print opdracht meer in het programma staat, anders zal er steeds naar een file geprint worden in de achtergrond. Opgelet, alvorens de python sketch te starten moet eerst onze MySQL databank ingesteld worden (dit gaat het makkelijkst met de PHP MyAdmin tool):

  • Eerst moet je een databank creeren, in het voorbeeld is dit de databank "CSV_DB".
  • In deze databank moer je een tabel aanleggen met de juiste velden, hier is dat "TABLE 1".
  • De velden krijgen een naam en een datatype, vb Temp_1, Temp_2, Gas, Water enz. Temperaturen worden opgeslagen als decimale getallen met 2 cijfers achter de komma, de waarden van de pulstellers worden als integer opgeslagen. Datum en tijd tenslotte krijgen als formaat "Timestamp".
  • Daarna moet je nog twee accounts aanmaken :
    • Een account met leesrechten, deze wordt gebruikt om met PHP data op te halen uit MySQL.
    • Een account met schrijfrechten, deze wordt dan in de python sketch gebruikt om data naar de MySQL database te kunnen schrijven.
  • Om de sketch automatisch te laten starten bij het booten van de Pi heb ik een job aangemaakt in "crontab" :
    • Editeren van crontab doe je met "sudo crontab -e"
    • Nu voeg je volgende regel toe : "@reboot python /home/pi/log.py &

Hieronder zie je de python code :

#!/usr/bin/env python2.7  
# script by Jan Heynen
# Om uitvoerbaar te maken : sudo chmod +x log.py via terminal !!
# Starten in background : python log.py &
# Lijst alle python processen : ps aux | grep python
# Stoppen proces : sudo kill PID (= procesnummer)
# Stoppen alle python processen : killall python
# Info in terminal tijdens testen : verwijder # voor de printopdrachten. 
# Indien sketch actief in background : geen print opdrachten, worden steeds weggeschreven naar een log-file !

import RPi.GPIO as GPIO
import time
import datetime
import os
import fnmatch
import MySQLdb as mdb
import logging

logging.basicConfig(filename='/home/pi/log_error.log',
  level=logging.DEBUG,
  format='%(asctime)s %(levelname)s %(name)s %(message)s')
logger=logging.getLogger(__name__)
GPIO.setmode(GPIO.BCM)  
# Initialisatie tijdvariabelen, om op vaste tijdstippen steeds een actie uit te voeren
heden=datetime.datetime.now()
heden_minuut=heden.replace(second=0,microsecond=0)
#print heden_minuut
heden_uur=heden.replace(minute=0,second=0,microsecond=0)
#print heden_uur
next_minute=heden_minuut+datetime.timedelta(minutes=1)
next_hour=heden_uur+datetime.timedelta(hours=1)

# GPIO 17,18 & 27 set up as inputs. All pulled down, for reading SO-Puls from wattmeters 
# GPIO 17 = El.boven, 1Wh=2 pulses, GPIO 18 = Zon, 1 Wh=2 pulses, GPIO 27= El.onder 1 Wh= 1puls
# GPIO 22, 23 are set up as inputs, pulled up for reading pulses KMI18 gas & water counter.  
# this enables us to demonstrate both rising and falling edge detection  
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)  
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) 
GPIO.setup(27, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) 
GPIO.setup(22, GPIO.IN, pull_up_down=GPIO.PUD_UP)  
GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_UP) 
#Definition / initialisation of variable necessary, otherwise runtime fault
start_1=0
start_2=0
start_3=0
start_4=0
start_5=0
wattcounter_1=0
wattcounter_2=0
wattcounter_3=0
watercounter=0
gascounter=0
last_state17=0
last_state18=0
last_state22=0
last_state23=0
last_state27=0

# Definitie van puls-functies : ophogen van counters / berekenen actueel vermogen van S0-counters.

def wattpuls_1(channel):
    global start_1
    global wattcounter_1
    stop_1=time.time()
    elapsed_1=stop_1-start_1
    start_1=time.time()
    wattcounter_1 = wattcounter_1+1
    watt_1=1800/elapsed_1
#    print "Rising edge on port 17 (Zon) Time : %.4f Counter : %d Watt : %d " %(elapsed_1,wattcounter_1,watt_1)
     
def wattpuls_2(channel):
    global start_2
    global wattcounter_2
    stop_2=time.time()
    elapsed_2=stop_2-start_2
    start_2=time.time()
    wattcounter_2 = wattcounter_2+1
    watt_2=1800/elapsed_2
#    print "Rising edge on port 18 (El.boven) Time : %.4f Counter : %d Watt : %d " %(elapsed_2,wattcounter_2,watt_2)  

def wattpuls_3(channel):
    global start_3
    global wattcounter_3
    stop_3=time.time()
    elapsed_3=stop_3-start_3
    start_3=time.time()
    wattcounter_3 = wattcounter_3+1
    watt_3=3600/elapsed_3
#    print "Rising edge on port 27 (El.onder) Time : %.4f Counter : %d Watt : %d " %(elapsed_3,wattcounter_3,watt_3)
    
def gaspuls(channel):
    global start_4
    global gascounter
    stop_4=time.time()
    elapsed_4=stop_4-start_4
    start_4=time.time()
    gascounter = gascounter + 1
#    print "Falling edge on port 22 (Gas) Counter : %d " %gascounter

def waterpuls(channel):
    global start_5
    global watercounter
    stop_5=time.time()
    elapsed_5=stop_5-start_5
    start_5=time.time()
    watercounter = watercounter + 1
#    print "Falling edge on port 23(Water) Counter : %d " %watercounter
    

# Functie om variabelen weg te schrijven naar de MySQL database, daarna counters resetten.
def insertDB(IDs, temperature):
  try:
    con = mdb.connect('localhost',
                      'pi_write',
                      'write',
                      'CSV_DB');
    cursor = con.cursor()
    sql = "INSERT INTO `TABLE 1`(`Temp_1`,`Temp_2`,`Zon`,`El_Boven`,`El_Onder`,`Water`,`Gas`) VALUES('%s','%s','%s','%s','%s','%s','%s')"\
          % (temperature[0],temperature[1],wattcounter_1,wattcounter_2,wattcounter_3,watercounter,gascounter)
    cursor.execute(sql)
    sql = []
    con.commit()
    con.close()
  except mdb.Error, e:
    logger.error(e)

# Eigenlijke loop waar de ingangen gepold worden, indien flankdetectie functie-aanroep.
# Opgelet, eerst pin-toestand inlezen in variabele, dan flank van variabele bewaken !!!
while True:
    Pin17=GPIO.input(17)
    Pin18=GPIO.input(18)
    Pin22=GPIO.input(22)
    Pin23=GPIO.input(23)
    Pin27=GPIO.input(27)
    #Hier worden alle ingangen gepold en bij +flank wordt de functie aangeroepen
    if Pin17:
        if last_state17==0:wattpuls_2(0)
    last_state17=Pin17  
    if Pin18:
        if last_state18==0:wattpuls_1(0)
    last_state18=Pin18
    if Pin27:
        if last_state27==0:wattpuls_3(0)
    last_state27=Pin27
    if Pin22:
        if last_state22==0:gaspuls(0)
    last_state22=Pin22
    if Pin23:
        if last_state23==0:waterpuls(0)
    last_state23=Pin23
    # Every minute do nothing !
    if(datetime.datetime.now()>next_minute):      
        next_minute=next_minute+datetime.timedelta(minutes=1)
        #print next_minute
    # Get readings from temp.sensors / counters and store them in MySQL every hour    
    if(datetime.datetime.now()>next_hour):     
        next_hour=next_hour+datetime.timedelta(hours=1)
        #print next_hour  
        temperature = []
        IDs = []
        for filename in os.listdir("/sys/bus/w1/devices"):
            if fnmatch.fnmatch(filename, '28-*'):
                with open("/sys/bus/w1/devices/" + filename + "/w1_slave") as f_obj:
                    lines = f_obj.readlines()
                if lines[0].find("YES"):
                    pok = lines[1].find('=')
                    temperature.append(float(lines[1][pok+1:pok+6])/1000)
                    IDs.append(filename)
                else:
                    logger.error("Error reading sensor with ID: %s" % (filename))
        if (len(temperature)>0):
            insertDB(IDs, temperature)
            wattcounter_1=0
            wattcounter_2=0
            wattcounter_3=0
            watercounter=0
            gascounter=0

Met PHP de data ophalen uit MySQL en weergeven in een grafiek

Dankzij de Apache Webserver kunnen we nu in PHP een webpagina programmeren. Mijn doelstelling was eenvoudig :

  • Het grafisch verloop van electriciteits, water en gas verbruik in een staafdiagram, met een 24 uur overzicht.
  • Het grafisch verloop van de temperatuur met een lijdiagram, ook over 24 uur.
  • Dezelfde grafieken, maar dan een maandoverzicht.
  • Voor de temperaturen het daggemiddelde in een maandoverzicht.
  • De dag en maand vrij te kiezen.

Dit heb ik gerealiseerd als volgt :

Data ophalen uit MySQL in JSon formaat

Deze php file wordt aangeroepen met als variabele "duedate". Het formaat van de aanroep is : data.php?duedate=2015-08-14. Het datumformaat is belangrijk, ik ga steeds uit van "jaar-maand-dag". De naam van de php-file is data.php. Deze files worden standaard opgeslagen in de directory var/www van de Pi. In deze directory gaat de Apache webserver immers alle web pagina's opzoeken. De laatste 24 records voor deze datum worden dan teruggegeven in een "JSon array" formaat. De inlog data in deze file moeten uiteraard overeenstemmen met de account die is aangemaakt in de MySQL database. Als je de file aanroept vanuit een browser, worden de records ook naar het beeldscherm geprint.

De query die MySQL verstaat is :

*"SELECT * FROM  `TABLE 1`  WHERE `Unix_time`>='{$duedate}' ORDER BY `Unix_time` ASC LIMIT 24"

In mensentaal :

  • "Kies van TABLE 1 alle velden waarvan de Datum groter of gelijk is dan $duedate, rangschik stijgend volgens $duedate, en niet meer dan 24 records.

Het resultaat van deze query ziet er dan zo uit :

[{"Date Time":"14\/03\/2015 18:00","Water":"9","Gas":"62","El_Onder":"109","El_Boven":"351","Zon":"79","Temp_1":"21.00","Temp_2":"38.00","Temp_3":"7.00","Unix_time":"2015-03-14 18:00:00"},{"Date Time":"14\/03\/2015 19:00","Water":"1","Gas":"53","El_Onder":"720","El_Boven":"469","Zon":"5","Temp_1":"21.00","Temp_2":"32.00","Temp_3":"7.00","Unix_time":"2015-03-14 19:00:00"}, enz....]

Dit is dus de vorm van het JSon formaat. De veldnamen zelf worden dan verder gebruikt in de php / js script om de grafieken te maken. Als we nu een maandoverzicht willen maken, moeten er dus eerst dag-gemiddeldes berekend worden. Dit is een gelukkig eenklein kunstje voor MySQL :

Onze Query moet er dan als volgt uit zien :

*"SELECT   `Unix_time`, SUM(Water)  AS `Water`, SUM(Gas) AS `Gas`, SUM(El_Onder) AS `El_Onder`, SUM(EL_Boven) AS `El_Boven`, SUM(Zon) AS `Zon`, 
AVG(Temp_1) AS `Temp_1`, AVG(Temp_2) AS `Temp_2`, AVG(Temp_3) AS `Temp_3` FROM  `TABLE 1`  WHERE `Unix_time`>= '{$duedate}' GROUP BY DATE(`Unix_time`) LIMIT 30"

In mensentaal : Kies van TABLE 1 het gemiddelde van alle genoemde velden waarvan de de datum gelijk is en groter dan "duedate", op volgorde van "Unix_time" en maximum 30 resultaten, en met de veldnamen zoals aangegeven (SUM(Water) AS `Water`). Om verschillende grafieken te maken, heb ik ervoor gekozen om kompleet andere php pagina's te creeren. Ook voor de dag/ maand data op te halen pas ik dus de php file aan, en deze krijgt dan een andere naam.

<?php
$hostname = 'localhost';
$username = 'pi_read';
$password = 'read';
$database = 'CSV_DB';
$duedate="2015-05-19";
$duedate = htmlspecialchars($_GET["duedate"]);

try {
    $dbh = new PDO("mysql:host=$hostname;dbname=$database", $username, $password);
	
    /*** The SQL SELECT statement ***/
    $sth = $dbh->prepare("SELECT * FROM  `TABLE 1`  WHERE `Unix_time`>='{$duedate}' ORDER BY `Unix_time` ASC LIMIT 24" );
    $sth->execute();
    /* Fetch all of the remaining rows in the result set */
    $result = $sth->fetchAll(PDO::FETCH_ASSOC);
    /*** close the database connection ***/
    $dbh = null;    
    }
catch(PDOException $e)
    {
        echo $e->getMessage();
    }
echo json_encode($result);
$json_data = json_encode($result); 
?>

Grafieken maken met PHP / Java Script / D3

Om grafieken te maken in PHP zijn er talloze mogelijkheden. Ik heb gekozen voor de "D3" library in combinatie met Java Script, en wel omdat ik een erg goede tutorial gevonden had voor deze aanpak. D3 is een zeer uitgebreide library waarmee je alle mogelijke grafieken kunt genereren in een php web pagina. Deze D3 library kan je extern opvragen, ofwel kan je deze ook kopieren naar de pi in de directory www/var.

  • De webpagina wordt standaard opgeroepen met als mogelijke variabele een datum
  • Deze datum wordt gebruikt om de data op te vragen van de MySQL databank
  • Er wordt een array in JSon formaat teruggestuurd
  • In de php is er een functie voorzien voor staafgrafieken en een functie voor lijngrafieken
  • Er wordt dan telkens 5* een staafgrafiek getekend met verschillende data en één lijngrafiek met verschillende temperaturen.
  • Op de pagina zijn bovenaan nog knoppen voorzien om een andere dag / datum te kiezen.
  • Ook staat er nog een knop voor een maandoverzicht, deze roept dan een andere php pagina op.

Een uitvoerige tutorial over D3 vindt je hier : [1]

<!DOCTYPE html>
<meta charset="utf-8">

<head>
	<style>			 /* set the CSS */
	.axis { font: 14px sans-serif;}
	.axis path,.axis line {fill: none;stroke: #000;shape-rendering: crispEdges;}

body { font: 12px Arial;}
path {stroke: steelblue;stroke-width: 2;fill: none;}
	.axis path,.axis line {fill: none;stroke: grey;stroke-width: 1; shape-rendering: crispEdges;}

	</style>
</head>

<body>
<!-- set inputs for the query -->    
<div id="new_input">
    &nbsp &nbsp
    <input name="Month" type="button" value="Maand" 
    onclick="Maand()" >
     &nbsp &nbsp
    <input name="Day_min_Button" type="button" value="Dag-" 
    onclick="Dag_min()" >
	 &nbsp &nbsp
    <input name="Day_plus_Button" type="button" value="Dag+" 
    onclick="Dag_plus()" >
	&nbsp &nbsp
    Datum: <input type="text" name="end" id="end" value= "2015-07-20"
    style="width: 80px;">
	&nbsp &nbsp
    <input name="updateButton" 
    type="button" 
    value="Update" 
    onclick="updateData()" />
</div>
<?php
/*Just for your server-side code*/
header('Content-Type: text/html; charset=ISO-8859-1');
?>
<!-- load the d3.js library from the internet--> 	
<!--<script src="http://d3js.org/d3.v3.min.js"></script>-->
<!-- load the d3.js library from local in dir var/www/D3--> 	
<script src="D3/d3.v3.min.js"></script>
<script>

function Draw_Graf($source,$end){

var margin = {top: 40, right: 20, bottom: 70, left: 40},
    width = 800 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;	
var x = d3.scale.ordinal().rangeRoundBands([0, width], .05);

var y = d3.scale.linear().range([height, 0]);

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom")
    .tickFormat(d3.time.format("%H h"));

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .ticks(10);
	
//Create SVG element
var svg = d3.select("body")
	.append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", 
          "translate(" + margin.left + "," + margin.top + ")");
		  
var inputURL = "data4.php?duedate="+$end;

// Get the data
d3.json(inputURL, function(error, data) {
	data.forEach(function(d) {
        d.Unix_time = parseDate(d.Unix_time);
        d.Water = eval("+d."+$source);;
    });	
  
  var label_string = " liter";
  if ($source=="Gas") label_string = " decaliter";
  if ($source=="El_Onder") label_string =" Wh";
  if ($source=="El_Boven") {
			label_string =" Wh";
			data.forEach(function(d) {d.Water=d.Water/2;});}
  if ($source=="Zon") {
			label_string =" Wh";
			data.forEach(function(d) {d.Water=d.Water/2;});}
			
  x.domain(data.map(function(d) { return d.Unix_time; }));
  y.domain([0, d3.max(data, function(d) { return d.Water; })]);
  
  var date_string= (d3.max(data, function(d) { return d.Unix_time; }));	//Dit geeft de datum, tijd, GMS.... als standaard date string
  var format = d3.time.format("%c");		//we willen een date string met het juiste formaat ! %c - date and time, as "%a %b %e %H:%M:%S %Y".
  var date_string2=format(new Date(date_string)); // hiermee wordt de string gevoncerteerd naar het juiste formaat !!
  var dagverbruik =( d3.sum(data, function(d) {return d.Water;}));
 
  
  
  svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis)
      .selectAll("text")
      .style("text-anchor", "end")
      .attr("dx", "-.8em")
      .attr("dy", "-.55em")
      .attr("transform", "rotate(-90)" );
		
  svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
      .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".9em")
      .style("text-anchor", "end")
	  .style("font-size", "14px") 
      .style("font-weight", "bold") 
      .text($source + label_string);
  svg.append("text")					//Dagverbruik 
        .attr("x", (width / 4*1))				
        .attr("y", -20 + (margin.top/2))
        .attr("text-anchor", "middle")	
        .style("font-size", "20px") 	
		.text ("Dagverbruik = "+ dagverbruik + label_string);		
  svg.selectAll("bar")				//Teken de bars
      .data(data)
      .enter().append("rect")
      .style("fill", "steelblue")
	  .style("opacity", .4)
	  .style("stroke", "red")
      .attr("x", function(d) { return x(d.Unix_time); })
      .attr("width", x.rangeBand())
      .attr("y", function(d) { return y(d.Water); })
      .attr("height", function(d) { return height - y(d.Water); });  
	  
   var yTextPadding = 14;
		svg.selectAll(".bartext")
		.data(data)
		.enter()
		.append("text")
		.attr("class", "bartext")
		.attr("text-anchor", "middle")
		.style("font-weight", "bold") 
		//.attr("fill", "white")
		.attr("x", function(d,i) {return x(d.Unix_time)+x.rangeBand()/2;})
		.attr("y", function(d,i) {return y(d.Water)+yTextPadding;})
		.text(function(d){if(d.Water>0)return d3.round(d.Water);});		 
});
}

function Draw_Line($end){

// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 50, bottom: 40, left: 50},
    width = 800 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;

// Parse the date / time
var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;

// Set the ranges
var x = d3.time.scale().range([0, width]);
var y0 = d3.scale.linear().range([height, 0]);
var y1 = d3.scale.linear().range([height, 0]);
var y2 = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x).orient("bottom")
				.tickFormat(d3.time.format("%H h"));
var yAxisLeft = d3.svg.axis().scale(y0).orient("left").ticks(8);
var yAxisRight = d3.svg.axis().scale(y1).orient("right").ticks(8);
var yAxisMiddle = d3.svg.axis().scale(y2).orient("right").ticks(8);
// Define the line 1
var valueline = d3.svg.line()
	.interpolate("basis") 
    .x(function(d) { return x(d.Unix_time); })
    .y(function(d) { return y0(d.Temp_1); });
	
//Define the line 2
var valueline2 = d3.svg.line()
	.interpolate("basis") 
    .x(function(d) { return x(d.Unix_time); })
    .y(function(d) { return y1(d.Temp_2); });
	
//Define the line 3
var valueline3 = d3.svg.line()
	.interpolate("basis") 
    .x(function(d) { return x(d.Unix_time); })
    .y(function(d) { return y2(d.Temp_3); });	
	
// Adds the svg canvas
var svg = d3.select("body")
    .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
    .append("g")
        .attr("transform",
              "translate(" + margin.left + "," + margin.top + ")");

// Get the data
var inputURL = "data4.php?duedate="+$end;
// Get the data
d3.json(inputURL, function(error, data) {
	data.forEach(function(d) {
        d.Unix_time = parseDate(d.Unix_time);
		d.Temp_1 = +d.Temp_1;
		d.Temp_2 = +d.Temp_2;
    });	

//format the date string
 var date_string= (d3.max(data, function(d) { return d.Unix_time; }));	//Dit geeft de datum, tijd, GMS.... als standaard date string
 var format = d3.time.format("%c");		//we willen een date string met het juiste formaat ! %c - date and time, as "%a %b %e %H:%M:%S %Y".
 var date_string2=format(new Date(date_string)); // hiermee wordt de string gevoncerteerd naar het juiste formaat !!
 var mean_temp_1 = d3.format(".2f")(d3.mean(data, function(d) {return d.Temp_1;}));
 var mean_temp_2 = d3.format(".2f")(d3.mean(data, function(d) {return d.Temp_2;}));
 var mean_temp_3 = d3.format(".2f")(d3.mean(data, function(d) {return d.Temp_3;}));
// Scale the range of the data, Temp_1=out, Temp_2=heating, Temp_3= in 	
x.domain(d3.extent(data, function(d) { return d.Unix_time; }));

var maxY = d3.max(data, function(d) { return Math.max(d.Temp_1, d.Temp_3); }); 
var minY = d3.min(data, function(d) { return Math.min(d.Temp_1, d.Temp_3); }); 
//y0.domain([minY,maxY]);
y0.domain(d3.extent(data, function(d) { return d.Temp_1; }));
y1.domain(d3.extent(data, function(d) { return d.Temp_2; }));
y2.domain(d3.extent(data, function(d) { return d.Temp_3; }));


// Add the valueline path.
svg.append("path")
	.attr("d", valueline(data));
// Add the valueline 2 path.
svg.append("path")
	.style("stroke", "red")
	.attr("d", valueline2(data));	
// Add the valueline 3 path.
svg.append("path")
	.style("stroke", "green")
	.attr("d", valueline3(data));	

// Add the X Axis
svg.append("g")
	.attr("class", "x axis")
	.attr("transform", "translate(0," + height + ")")
	.call(xAxis);

// Add the Y0 Axis
svg.append("g")
	.attr("class", "y axis")
	.style("fill", "steelblue")
	.style("font-weight", "bold") 
	.call(yAxisLeft);
// Add the Y1 Axis
svg.append("g")
	.attr("class", "y axis")
	.attr("transform", "translate(" + width + " ,0)")  
	.style("font-weight", "bold")  
    .style("fill", "red") 
	.call(yAxisRight);

// Add the Y2 Axis
svg.append("g")
	.attr("class", "y axis")
	.attr("transform", "translate(" + 5 + " ,0)")  
	.style("font-weight", "bold")  
    .style("fill", "green") 
	.call(yAxisMiddle);	
	
//Add the labels

 svg.append("text")					//Titel van de grafiek
        .attr("x", (width / 2))				
        .attr("y", 0 - (margin.top / 2))
        .attr("text-anchor", "middle")	
        .style("font-size", "20px") 
        .style("text-decoration", "underline") 	
        .text("Temperaturen buiten / verwarming");
		
 svg.append("text")                // text label for the x axis
		.attr("x", width / 2 )
        .attr("y",  height + margin.bottom)
        .style("text-anchor", "middle")
		.style("font-size", "16px") 
        .style("font-weight", "bold") 
        .text("Time");

 svg.append("text")					//text label voor de Y-as
        .attr("transform", "rotate(-90)")
        .attr("y",  0 - margin.left)
        .attr("x", 0 - (height / 2))
        .attr("dy", "1em")
        .style("text-anchor", "middle")
		.style("font-size", "16px") 
        .style("font-weight", "bold") 
        .text("Temperatuur");
	
 svg.append("text")					//Dagverbruik 
        .attr("x", (width / 4*3))				
        .attr("y", 20 + (margin.top/2))
        .attr("text-anchor", "middle")	
        .style("font-size", "20px") 
		.style("fill", "steelblue")	
		.text ("Gemiddelde Temp Buiten = "+ mean_temp_1 + " °C");	
 svg.append("text")					//Dagverbruik 
        .attr("x", (width / 4*3))				
        .attr("y", 40 + (margin.top/2))
        .attr("text-anchor", "middle")	
        .style("font-size", "20px") 
		.style("fill", "red")	
		.text ("Gemiddelde Temp Verw. = "+ mean_temp_2 + " °C");	
 svg.append("text")					//Dagverbruik 
        .attr("x", (width / 4*3))				
        .attr("y", 60 + (margin.top/2))
        .attr("text-anchor", "middle")	
        .style("font-size", "20px") 
		.style("fill", "green")	
		.text ("Gemiddelde Temp Binnen = "+ mean_temp_3 + " °C");	
});		
}

var format_date = d3.time.format("%Y-%m-%d");					//changing the date in the right format
document.getElementById('end').value = format_date(new Date()); // returns a string with the actual date

var end = document.getElementById('end').value;

Draw_Graf("Gas",end);
Draw_Graf("Water",end);
Draw_Graf("El_Onder",end);
Draw_Graf("El_Boven",end);
Draw_Graf("Zon",end);
Draw_Line(end);

function Maand(){window.location.href = "bar_m3.php";}

// ** Update data section (Called from the onclick dag-)
function Dag_min() {
var parse_Day = d3.time.format("%Y-%m-%d").parse;
var format = d3.time.format("%Y-%m-%d");
var actual_date=parse_Day(document.getElementById('end').value); // returns a Date object out of a string
var past_date=d3.time.day.offset(actual_date, -1)		//
document.getElementById('end').value = format(past_date);
updateData();
}

function Dag_plus() {
var parse_Day = d3.time.format("%Y-%m-%d").parse;
var format = d3.time.format("%Y-%m-%d");
var actual_date=parse_Day(document.getElementById('end').value); // returns a Date object out of a string
var past_date=d3.time.day.offset(actual_date, +1)		//
document.getElementById('end').value = format(past_date);
updateData();
}
// ** Update data section (Called from the onclick)
function updateData() {
end = document.getElementById('end').value;
d3.selectAll("svg").remove();

Draw_Graf("Gas",end);
Draw_Graf("Water",end);
Draw_Graf("El_Onder",end);
Draw_Graf("El_Boven",end);
Draw_Graf("Zon",end);
Draw_Line(end);
}
</script>
</body>