Daten senden und empfangen ohne Zeitverzögerung

Dieses Sketch sendet in einem vorgegebenen Intervall die Temperaturwerte mehrerer DS1820 Temperatursendoren (DS18S20 und DS18B20) an ein PHP-Script das diese Daten wiederum in eine MySQL-Datenbank speichert. Gleichzeitig können über verschiedene Kommandos, die über das Netzwerk gesendet werden, Pins des Arduino geschaltet werden. Wenn man weitere Temperatursensoren anschließen und dessen Werte in der Datenbank speichern will, geht man wie folgt vor.

  • Anpassen der Anzahl der angeschlossenen Sensoren hinter #define AnzahlSensoren
  • Den Wert hinter showDeviceAdresses auf true stellen um die DeviceAdressen aller angeschlossenen TemperaturSensoren über den Seriellen Monitor sehen zu können
  • Die DeviceAdresse des neuen Sensors aus dem Seriellen Monitor kopieren und in einer neuen Zeile unter die bereits vorhandenen DeviceAdressen im Arduino Sketch einfügen. Beachten Sie dass, bis auf die letzte DeviceAdresse, alle Adressen durch ein Komma voneinander getrennt sein müssen und dass sich jede Adresse in einer eigenen Zeile befinden muss.
  • Den Wert hinter showDeviceAdresses können Sie nun wieder auf false stellen.
Beim nächsten Aufruf der Funktion zum senden der Temperaturwerte an das phpScript wird nun auch der Wert des neu angeschlossenen Temperatur-Sensors übertragen und in der Datenbank gespeichert. Am php-Script SaveTempToMySQL oder an der Datenbanktabelle arduino_sensorwerte muss nichts geändert werden.

Hinweise zu den Variablen die an die eigenen Gegebenheiten angepasst werden müssen

  • ONE_WIRE_BUS = der digitale Pin des Arduino mit dem die Datenleitung des TemperaturSensors verbunden ist,
  • DeviceAdress = die DeviceAdressen aller Temperatursensoren, Jede Adresse in einer eigenen Zeile und durch Komma getrennt,
  • mac = die MacAdresse des EthernetShields,
  • ip = die IP-Adresse, über die der Arduino ansprechbar sein soll,
  • gateway = IP-Adresse des Gateway,
  • subnet = Teilnetzmaske,
  • serverip = IP-Adresse des Servers auf dem sich das PHP-Script befindet an das die Temperatur-Werte gesendet werden soll. Diese kann man mit ping www.domainname.de herausfinden,
  • SERVERURL = Name der Domain auf der das PHP-Script liegt an das die Temperatur-Werte gesendet werden soll,
  • SERVERPATH = URL zu dem PHP-Script,
  • SERVERKEY = Kennwort das in der PHP-Datei SaveTempToMySQL.php steht,
  • USERAGENT = UserAgenten der in der PHP-Datei SaveTempToMySQL.php steht,
  • interval = Ein Wert der festlegt in welchem Intervall die Temperatur-Daten an das PHP-Script gesendet werden sollen. 58.000 steht dabei für 5 Minuten,
  • showDeviceAdresses = true wenn die DeviceAdressen der TemperaturSensoren im seriellen Monitor ausgegeben werden sollen, false wenn diese nicht angezeigt werden sollen
/**************************************************************************
* Projekt: Steuerung einer Heizanlage                                     *
* Autor: Enrico Sadlowski                                                 *
* Letzte Aktualisierung: 29.11.2012                                       *
*                                                                         *
* Beschreibung:                                                           *
*-------------------------------------------------------------------------*
* Temperaturwerte von DS18x20 Temperatursensoren an PHP-Script schicken   *
* Arduino Pins über Webbrowser steuern und Werte der Pins einlesen        *
 *************************************************************************/

#include <SPI.h>
#include <Ethernet.h>
#include <DallasTemperature.h>
#include <OneWire.h>

/*************************************************************************
                                                                         *
  Ab hier die Angaben entsprechend den eigenen Gegebenheiten anpassen    *
                                                                         *
*************************************************************************/                                                 
#define showDeviceAdresses false                                 // true wenn die DeviceAdressen aller Sensoren angezeigt werden sollen
#define ONE_WIRE_BUS     7                                       // Arduino-Pin an dem der DS18x20 Temperatursensor hängt

/*************************************************************************
  In jeder Zeile eine DeviceAdress eines Temperatursensors, alle bis auf *
  die letzte Zeile durch Komma voneinander trennen                       *
*************************************************************************/
DeviceAddress sensorAdress[] = {
  0x10,0x16,0x2E,0x57,0x02,0x08,0x00,0x2F,
  0x10,0x11,0x40,0xC0,0x01,0x08,0x00,0x9A,
  0x28,0xE3,0xAE,0xBE,0x03,0x00,0x00,0x9F,
  0x28,0x92,0xB3,0xBE,0x03,0x00,0x00,0xA0,
  0x10,0x2D,0x2E,0x57,0x02,0x08,0x00,0x3A,
  0x28,0xC9,0xC6,0xBE,0x03,0x00,0x00,0xC9
};

/*************************************************************************
  Server- und Clienteinstellungen                                        *
*************************************************************************/
EthernetServer server(80);
byte mac[]       = { 0x5A, 0xA2, 0xDA, 0x0D, 0x56, 0x7A };      // MAC-Adresse des Ethernet-Shields
byte ip[]        = { 192,168,2,111 };                           // IP-Adresse über die der Arduino erreichbar sein soll
byte gateway[]   = { 192,168,2,1 };                             // Gateway-Adresse des Routers
byte subnet[]    = { 255, 255, 255, 0 };                        // Teilnetzmaske
byte serverip[]  = { 176, 9, 52, 230 };                         // IP-Adresse des Servers auf dem das PHP-Script läuft

EthernetClient client;
#define SERVERURL "hbkolbs.bplaced.net"                         // Domain auf der das PHP-Script liegt
#define SERVERPATH "/SaveTempToMySQL.php"                       // Pfad zur PHP-Datei
#define SERVERKEY "8562"                                        // Key, der mit dem Key in der PHP-Datei übereinstimmen muss
#define USERAGENT "Arduino"                                     // Temp-Werte werden nur gespeichert wenn der Useragenten mit dem in der PHP-Datei übereinstimmt

/**************************************************************************
  Variablen für Interval in dem die Temperaturwerte zum Server gesendet   *
  werden sollen. Dient als Ersatz für delay um das Sketch weiterhin       *
  reagieren zu lassen falls Daten über das Netzwerk geschckt werden       *
**************************************************************************/
long prevTime = 0;                                              // vergangene Zeit
long interval = 58000;                                          // 58.000 = 5 Minuten bei delay(5) in der zweiten Zeile in void loop()

/*************************************************************************
                                                                         *
  Ab hier sollte nichts mehr geändert werden                             *
                                                                         *
*************************************************************************/
bool connected = false;
int i = 0;
int numSensors = 0;
OneWire ds(ONE_WIRE_BUS);
DallasTemperature sensors(&ds);

void setup() 
{
  Serial.begin(9600);
  Serial.println();
  Serial.println();
  Serial.println("Heizungssteuerung");

  Serial.println("Initialisiere Ethernet.");
  Ethernet.begin(mac, ip);                        

  sensors.begin();                                
  numSensors = sensors.getDeviceCount(); 
  Serial.println("Suche nach Temperatur-Sensoren.");
  if(numSensors > 0)
  {
    Serial.println("Es wurden "+String(numSensors)+" Temperatur-Sensoren gefunden.");
  }
  else 
  {
    Serial.println("Es wurden keine Sensoren gefunden.");
  }
  if(showDeviceAdresses == true) lookUpSensors();

  Serial.println("Interval zum Senden der Temperaturwerte gestartet.");
}

void loop() 
{
  getNetworkCommands();

  prevTime++;
  delay(5);

  if(prevTime > interval)
  {
    numSensors = sensors.getDeviceCount();

    sendTempValues();

    prevTime = 0;
  }  
}

/****************************************************************************
  Erkennen ob Kommandos über das Netzwerk an den Arduino geschickt werden.  *
  Je nach empfangenem Kommando Pins schalten                                *
****************************************************************************/  
void getNetworkCommands()
{

  EthernetClient client = server.available();

  if (client) 
  {
    char command     = client.read();
    byte pinnumber   = client.read();
    byte pinvalue    = client.read();
    byte returnvalue = 0;

    //Über das Netzwerk gesendete Kommandos auswerten
    switch(command) 
    {
    case 'S': //Pin setzen
      Serial.println("Kommando zum setzen von Pin "+String(pinnumber)+" auf "+String(pinvalue)+" empfangen");
      pinMode(pinnumber,OUTPUT);
      digitalWrite(pinnumber,pinvalue);
      returnvalue = pinvalue; // gesetzten Wert zurückliefern
      break;

    case 'R': //Pin lesen und den Wert an Client schicken
      returnvalue = digitalRead(pinnumber);
      Serial.println("Kommando zum lesen von Pin "+String(pinnumber)+" empfangen. Der Wert ist: "+String(returnvalue));
      break;

    default:
      Serial.println("Unbekanntes Kommando empfangen");   
      break;
    }
    client.write(returnvalue+48);
    client.flush();
    delay(10);
    client.stop();
  }
}

/*******************************************
                                           *            
  Temperaturwerte an WebServer schickenn   *
                                           *
*******************************************/
void sendTempValues()
{  
  int temparray[numSensors];

  if(!connected)   
  {
    Serial.println("Keine Verbindung zum Server.");
    Serial.println("Verbindung wird aufgebaut.");

    if(client.connect(serverip, 80))  // Verbindung zum Server aufbauen
    {
      connected = true;

      for(i=0; i<numSensors; i++)
      {
        int temp = getTemperature(sensorAdress[i]);  //Temperatur der Sensoren ermitteln
        if(temp > 100) temp = 0;
        if(temp < 0) temp = 0;
        temparray[i] = temp;                         //Temperaturwerte aller Sensoren in array speichern  
      }

      Serial.print("Verbindung zum Server hergestellt.");
      client.print("GET "+String(SERVERPATH)+"?");
      client.print("key="+String(SERVERKEY)+"&");
      client.print("s="+String(numSensors)+"&");

      for (i=0; i<numSensors; i++)
      {
        client.print("t");
        client.print(i+1);
        client.print("=");
        client.print(temparray[i]);
        if (i < numSensors-1)
        {
          client.print("&");
        }
      }      

      client.println(" HTTP/1.1");
      client.println("Host: "+String(SERVERURL));
      client.println("User-Agent: "+String(USERAGENT));
      client.println("Accept: text/html");
      client.println("Connection: close");
      client.println();
    }
    else
    {
      Serial.println("Verbindung zum Server kann nicht hergestellt werden.");
    }
  }
  else 
  { //Rückgabe des Servers auslesen
    delay(500);                                                
    while (client.connected() && client.available())             // Wenn verbunden und verfügbar 
    {          
      char c = client.read();                                    // Antwort des Servers lesen
      Serial.print(c);
    }
    Serial.println();
    client.stop();
    connected = false;
  }
}

/******************************************************
                                                      *
  Temperatur eines Sensors in Grad Celsius ermitteln  *
                                                      *
******************************************************/ 
float getTemperature(byte* address)
{
  byte type_s;
  byte data[12];
  float celsius;
  int tr;

  writeTimeToScratchpad(address);
  readTimeFromScratchpad(address,data);

  switch (address[0]) 
  {
    case 0x10: // DS18S20 oder alter DS1820
      type_s = 1;
      break;
    case 0x28: // DS18B20
      type_s = 0;
      break;
    case 0x22: // DS1822
      type_s = 0;
      break;
  }

  unsigned int raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3;                       // 9 bit resolution default
    if (data[7] == 0x10) 
    {
      // count remain gives full 12 bit resolution
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  } else {
    byte cfg = (data[4] & 0x60);
    if (cfg == 0x00) raw = raw << 3;      // 9 bit resolution, 93.75 ms
    else if (cfg == 0x20) raw = raw << 2; // 10 bit res, 187.5 ms
    else if (cfg == 0x40) raw = raw << 1; // 11 bit res, 375 ms
  }
  celsius = (float)raw / 16.0;
  return celsius;
}

/**************************************************************
                                                              *
  Hilfsfunktionen zum ermitteln der Temperatur eines Sensors  *
                                                              *
**************************************************************/
void writeTimeToScratchpad(byte* address)
{
  ds.reset();
  ds.select(address);
  ds.write(0x44,1);
  delay(1000);
}

void readTimeFromScratchpad(byte* address, byte* data)
{
  ds.reset();
  ds.select(address);
  ds.write(0xBE);
  for (byte i=0;i<9;i++)
  {
    data[i] = ds.read();
  }
}

/****************************************************
  Funktion zum ermitteln der Device-Adressen aller  * 
  angeschlossenen DS18X20 Temperatursensoren        *
****************************************************/
void lookUpSensors()
{
  byte address[8];
  int i=0;
  byte ok = 0, tmp = 0;

  Serial.println("--Suche gestartet--");
  while (ds.search(address))
  {
    tmp = 0;
    //0x10 = DS18S20
    if (address[0] == 0x10)
    {
      Serial.print("Device is a DS18S20 : ");
      tmp = 1;
    } 
    else
    {
      //0x28 = DS18B20
      if (address[0] == 0x28)
      {
        Serial.print("Device is a DS18B20 : ");
        tmp = 1;
      }
    }
    //display the address, if tmp is ok
    if (tmp == 1)
    {
      if (OneWire::crc8(address, 7) != address[7])
      {
        Serial.println("but it doesn't have a valid CRC!");
      } 
      else
      {
        //all is ok, display it
        for (i=0;i<8;i++)
        {
          Serial.print("0x");
          if (address[i] < 16) 
          {
            Serial.print('0');                           // add a leading '0' if required.
          }
          Serial.print(address[i], HEX);                 // print the actual value in HEX
          if (i < 7) 
          {
            Serial.print(", ");
          }
        }
        Serial.print("\r\n");
        ok = 1;
      }
    }//end if tmp
  }//end while
  if (ok == 0)
  {
    Serial.println("Keine Sensoren gefunden");
  }
  Serial.println("--Suche beendet--");
}

MySQL-Datenbanktabelle zum speichern der Temperaturwerte

arduino_sensorwerte

CREATE TABLE IF NOT EXISTS `arduino_sensorwerte` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `sensorid` bigint(11) NOT NULL,
  `datumzeit` datetime NOT NULL,
  `sensorwert` decimal(10,2) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `sensorid` (`sensorid`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 ;

PHP Script zum empfangen der Temperaturdaten vom Arduino und speichern dieser Daten in die MySQL-Datenbank

SaveTempToMySQ.php

<?php
header('Content-type: text/plain');
/**************************************************************************************
  Autor:   Enrico Sadlowski                                                        		*
  Kontakt: profwebapps@gmail.com                                                   		*
  Letzte Aenderung: 25.11.2012																												*
                                                                                   		*
  PROJEKT: Arduino-MultiSensor-Temperatur-Ueberwachung                             		*
  ------------------------------------------------------------------------------------*
                                                                                   		*
  Arduino ruft dieses Script auf, uebergibt Temperaturwerte und ID des Sensors 				*
  in der Variablen (Values), sowie einen Key (key), und den USER_AGENT (User-Agent)		*
  Der übergebene KEY muss als SHA1 HashTag mit der Konstante KEY hier im Script 			*
  übereinstimmen, 																																		*
  Der übergebene User-Agent muss als SHA1-HashTag mit der Konstanten USERAGENT 				*
  übereinstimmen,   																																	*
  Wenn Useragent und Key passen und die Variable Values nicht leer ist, werden die 		*
  Temperaturwerte mit Datum und SensorID in die MySQL-Datenbank geschrieben						*
																																											*
**************************************************************************************/
include("inc/common.inc.php");

define("KEY","8562"); //Muss als SHA1 HashTag vom Arduino an dieses Script übergeben werden
define("USERAGENT","Arduino");	 //Muss als SHA1 HashTag vom Arduino an dieses Script übergeben werden

if($_SERVER['HTTP_USER_AGENT'] == USERAGENT)
{
	if(isset($_GET['key']))
	{
		if($_GET['key'] == KEY)
  	{
   		$DATUM = date("Y-m-d H:i:s");

			if(isset($_GET['s']))
			{			
				for($i=1;$i<=$_GET['s'];$i++)
				{
					$query = "INSERT INTO arduino_sensorwerte (sensorid, datumzeit, sensorwert) VALUES (".$i.", '".$DATUM."', '".$_GET['t'.$i]."')";
  				$result = mysql_query($query) or die(mysql_error());
  				if(mysql_affected_rows() > 0)
  	  		{
    	  		$result = "Temperaturwerte gespeichert";
     			} 
     			else 
     			{
     				$result = "Fehler beim speichern der Daten in der MySQL-Datenbank";
					}
				}
			}
  	} else $result = "Falscher Key";
	} else $result = "Kein Key übergeben";
} else $result = "Falschen Useragent empfangen";

print_r($result);
?>

PHP Script zum senden von Kommandos an das Arduino Board

index.php Die Datei steuerung.php sollte nicht direkt aufgerufen werden wenn man Pins am Arduino schalten will. Vielmehr sollte die Datei index.php beim ersten Start geöffnet werden um direkt den Status der einzelnen Pins am Arduino erkennen zu können. Die Datei index.php prüft ob die die Session Variable firstStart gesetzt ist. Ist diese vorhanden, so wird sie gelöscht. Nur wenn die Session Variable firstStart nicht vorhanden ist, wird der Status aller Pins am Arduino, die auch in der Datei Steuerung.php angegeben sind, abgefragt und im Formular gesetzt. So kann man schon beim öffnen der Seite sehen ob ein Pin HIGH oder LOW ist, also ob z.B. eine Lampe ein- oder ausgeschaltet ist.

<?php
session_start();

if(isset($_SESSION['firstStart'])) unset($_SESSION['firstStart']);

header("location: steuerung.php");
?>

steuerung.php Diese Datei zeigt ein Formular an über das sich die einzelnen Pins des Arduino steuern lassen. Beschreibung der einzelnen Variablen die an die eigenen Gegebenheiten angepasst werden sollten.

  • $arduino_ip = die IP die im Arduino-Sketch hinter byte ip[] angegeben wurde. Über diese IP stellt man die Verbindung zum Arduino her.
  • $arduino_port = Der Port über den auf den Arduino zugegriffen werden soll
  • $digitalPinAnzahl = Anzahl der Pins am Arduino die geschaltet werden sollen
  • $digitalPinNummern = Array mit den Pins an denen am Arduino Verbraucher angeschlossen sind und die geschaltet werden sollen. Der Wert hinter => steht dabei für die Pin-Nummer, der Wert vor => ist ein fortlaufender Index, der bei 1 beginnen sollte.
  • $digitalPinNamen = Array mit den Namen die jedem Pin zugewiesen werden sollen. So ist es leichter diese zu unterscheiden. Im Formular werden die Namen der Pins angezeigt und nicht die Nummern.
<?php
session_start();

$arduino_ip 		= "192.168.2.111";   // IP des Ethernet Shields
$arduino_port 		= "80";		    // Port zum Zugriff auf das Ethernet Shield			
$digitalPinAnzahl  	= 2; 		    // Anzahl der zu steuernden Digitalpins
$digitalPinNummern 	= array(1=>8,2=>9); // PinNummern der digitalen Pins die geschaltet werden sollen (Anzahl von $digitalPinAnzahl)
$digitalPinNamen  	= array(1=>"Beleuchtung Wohnzimmer",2=>"Heizung Kinderzimmer"); // Namen für die einzelnen digitalen Pins wie sie auf der Seite angezeigt werden sollen

/* Daten an Arduino senden */
function arduino_send($ip,$port,$command) 
{
  $res = @fsockopen($ip, $port, $errno, $errstr, 30);
  if (!$res) 
  {
    echo "Fehler, kein Kontakt zum Arduino.<br />Kommando konnte nicht gesendet werden<br /><br />$errstr ($errno)<br />\n";
  } 
  else 
  {
    $out = "GET / HTTP/1.1\r\n";
    $out .= "Host: www.example.com\r\n";
    $out .= "Connection: Close\r\n\r\n";
    fwrite($res, $command);
    $ret =fread($res,10);
    return $ret;

    while (!feof($res)) 
    {
    	echo fgets($res, 128);
    }
    fclose($res);
  }
}

/*
Digitale Ausgänge steuern
Wenn eine CheckBox unchecked war und angeklickt wird, dann wird 1 übergeben. 
Wenn eine CheckBox checked war und unchecked wird, dann prüfen ob der Wert in der Session 1 ist.
Wenn der Wert in der Session 1 ist dann Pin auf LOW schalten 
*/
if(isset($_GET['send']) && $_GET['send'] == 1)
{
  for($i=1;$i<=$digitalPinAnzahl;$i++)
	{
		if(isset($_GET['DP'.$i]) && $_SESSION['DP'.$i] == 0) 
		{
			$DP=1;
			arduino_send($arduino_ip, $arduino_port, "S".chr($digitalPinNummern[$i]).chr($DP), chr($DP)); //PinNr, Wert
		}
		else if(!isset($_GET['DP'.$i]) && $_SESSION['DP'.$i] == 1)
		{
			$DP=0;
			arduino_send($arduino_ip, $arduino_port, "S".chr($digitalPinNummern[$i]).chr($DP), chr($DP)); //PinNr, Wert
		}
	}
}
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
	<head>
		<title>Arduinosteuerung</title>
	</head>
<body>
	<h1>Arduinosteuerung</h1>

<form action="" method="get">
<input type="hidden" name="send" value="1">

<table border="1">
<?php
	for($i=1; $i<=$digitalPinAnzahl; $i++)
	{		
		if(!isset($_SESSION['firstStart']))
		{
			if(arduino_send($arduino_ip, $arduino_port,"R".chr($digitalPinNummern[$i]).chr(1)) == 1) 
			{
				$checked = " checked=\"checked\"";
				$_SESSION['DP'.$i] = 1;
			}
			else
			{
				 $checked = "";
				 $_SESSION['DP'.$i] = 0;
			}
		} // if
		else
		{
			if($_SESSION['DP'.$i]==1) $checked = " checked=\"checked\""; else $checked = "";

			if(isset($_GET['DP'.$i])) 
			{
				$_SESSION['DP'.$i] = 1;
				$checked = " checked=\"checked\""; 
			}	
			else 
			{
			  $_SESSION['DP'.$i] = 0;
				$checked = "";
			}
		}	

		echo "	<tr>\n";
		echo "		<td>".$digitalPinNamen[$i]."</td>\n";
		echo "		<td><input type=\"checkbox\" name=\"DP".$i."\" value=\"1\" onchange=\"this.form.submit()\"".$checked."></td>\n";
		echo "	</tr>\n";

	} // for
	$_SESSION['firstStart'] = 1;
?>
</table>
</form>

</body>
</html>

Über Enrico S.

Programmierer, Webdesigner, Grafiker, Blogger, Screencaster, Arduino- und eMobility Enthusiast.

Kommentare geschlossen.