PHP Frage zum Arduino Webserver / PHP / Text parsen

Hallo Forum,

bevor ich jetzt gleich Amok laufe, stelle ich mal mein (hoffentlich) letztes Problem hier ein. Ich habe einen Webserver auf dem Ardunio laufen mit dem enc28j60 Chipsatz. Alles funktioniert bestens. Jetzt möchte ich aber die Temperatur (nur Temperatur) per PHP Script auf meinem Infoframe anzeigen lassen. Dazu habe ich ein kleines zwischenscript geschrieben welches den Quellcode des Arduino Webservers parsed und mir in eine Text Datei auf meinem Server schreibt. Dies macht er alles wunderbar und es funktioniert auch alles Wunderbar. Hier mal der Inhalt der Textdatei:

<H1>THT - NET Webserver</H1>
<table>

Wohnzimmer: 25.0&deg;C / 44%
Aussen: 15.9 &deg;C / 0%</table>

alles in eine Zeile. So jetzt mein PHP Script:

// read Temperature File
$TEMPhandle = fopen ("arduino/Arduino_Temp_2.txt", "r");
    $TEMPbuffer = fgets($TEMPhandle, 100);
    $TEMPbuffer1 = fgets($TEMPhandle, 100);
fclose ($TEMPhandle);


// print temperature
//Sensor0
$pos = strpos($TEMPbuffer, 'Wohnzimmer:') + 12;
$posa = strpos($TEMPbuffer, ' ', $pos);

$TempAkt = substr($TEMPbuffer, $pos, $posa - $pos);

//Sensor1
$pos1 = strpos($TEMPbuffer1, 'Aussen:') + 8;
$pos1a = strpos($TEMPbuffer1, ' ', $pos1);

$TempAkt1 = substr($TEMPbuffer1, $pos1, $pos1a - $pos1);

Und jetzt mein Problem. Die Temperatur vom Wohnzimmer wird mir angezeigt. Jedoch nicht die von Aussen. Egal was ich mache. Es will einfach nicht und ich habe keinen schimmer warum… Kennt sich hier jemand mit PHP aus?

Hi,

hast du mal mit echo gecheckt, was die einzelnen variablen beinhalten?

Meine Vermutung:
$TEMPbuffer1 = fgets($TEMPhandle, 100);
gibt false zurück.

PHP-Referenz: “fgets — Liest eine Zeile von der Position des Dateizeigers”

Sprich: Du hast nur eine Zeile in der Datei, willst mit dem zweiten fgets aber eine zweite Zeile auslesen, weshalb du ein false von fgets zurückgegeben bekommst.

Noch eine kleine Kritik:
Warum nicht preg_match und regular expressions verwenden?
Damit kannst du auch gleich prüfen, ob deine Datei überhaupt einen “gültigen” Ihnalt hat.

// read Temperature File
$TEMPhandle = fopen ("arduino/Arduino_Temp_2.txt", "r");
    $TEMPbuffer = fgets($TEMPhandle, 100);
fclose ($TEMPhandle);
if(preg_match('/Wohnzimmer: ([\-0-9.]+)(.*?)Aussen: ([\-0-9.]+)/i', $TEMPhandle, $result)
{
 // suchmuster gefunden, daten in der datei gültig
 $temp1 = intval($result[1]);  // temperaturwerte sind als string im $result-array gespeichert
 $temp2 = intval($result[3]); // intval macht einen integer-wert (bzw float) aus ihnen

}
else
{
 echo 'Suchmuster konnte nicht gefunden werden!';
}

Hallo Kudin,

danke für deine Antwort. Das problem ist, ich verstehe nicht wie ich es eleganter lösen könnte. Das mit preg_match und regular expressions hatte ich zwar mal angesehen aber auch hier fällt es mir schwer zu verstehen wie ich da vorgehen muss. Da tue ich micht mit der Arduino Sprache leichter ^^

Spass beiseite. Ich habe auch versucht mir mit echo es anzeigen zu lassen, leider bekomme ich nur eine weiße Seite.

<?php
//***************************************************************************************************
//   Initialization
//***************************************************************************************************

// set error handling to only report errors (no warnings, infos...)
// error_reporting( E_ALL );

function datumDeutsch($datumsstring){
	$englisch = array("Morgen", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Motag");
	$deutsch = array("Morgen", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag", "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So", "Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember", "Jan", "Feb", "M&auml;r", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez", "Montag");
	return str_replace($englisch, $deutsch, $datumsstring);
}



// read Temperature File
$TEMPhandle = fopen ("arduino/Arduino_Temp_2.txt", "r");
    $TEMPbuffer = fgets($TEMPhandle, 100);
    $TEMPbuffer1 = fgets($TEMPhandle, 100);
fclose ($TEMPhandle);


// print temperature
//Sensor0
$pos = strpos($TEMPbuffer, 'Wohnzimmer:') + 12;
$posa = strpos($TEMPbuffer, '&', $pos);

$TempAkt = substr($TEMPbuffer, $pos, $posa - $pos);

//Sensor1
$pos1 = strpos($TEMPbuffer1, 'Aussen:') + 8;
$pos1a = strpos($TEMPbuffer1, '&', $pos1);

$TempAkt1 = substr($TEMPbuffer1, $pos1, $pos1a - $pos1);





// load configuration
$config = parse_ini_file("config.ini", true);
$sysconfig = $config['System'];
// include path for zend framework
if ($sysconfig['zendfw_path'] != "") {
	set_include_path(get_include_path() . PATH_SEPARATOR . $sysconfig['zendfw_path']);
}
// set locale for date/time formatting
$loc = setlocale(LC_ALL, 'de_DE.UTF8', 'de_DE', 'de', 'ge');

// includes
require_once 'library/tools.php';
require_once 'library/dbconn.php';
require_once 'library/iplugin.php';
 
// set the width and height of the new image in pixels
$image_width = $sysconfig['image_width'];
$image_height = $sysconfig['image_height'];

// create simple black image
$im = ImageCreateTrueColor($image_width, $image_height);
$backgroundcol = ImageColorAllocate($im, 0, 0, 0);
ImageFillToBorder($im, 0, 0, $backgroundcol, $backgroundcol);

// copy (resized) background image on background
$bgimagefile = 'resources/background.jpg';
$bg = @ImageCreateFromJpeg ($bgimagefile); /* Versuch, Datei zu öffnen */
if ($bg) {
	imagecopyresampled($im, $bg, 0, 0, 0, 0, $image_width, $image_height, imagesx($bg), imagesy($bg));
}

// open database connection
$dbconn = DbConnection::connect($sysconfig['db_host'], $sysconfig['db_name'], $sysconfig['db_user'], $sysconfig['db_password']);
if (!$dbconn)
	die('keine Datenbankverbindung möglich: ' . mysql_error());
	
// create cache directory if it doesn't exist already
if (!file_exists('cache/'))
	mkdir('cache');

// load and init plugins (calls, weather, calendar, mails...)
// every ini [group] except "System" is used as class name for a plugin and instantiated
$plugins = array();
foreach ($config as $key=>$elem) {
	if ($key != "System") {
		// include class file
		require_once 'plugins/' . $key . '.php';
		// create instance
		$plugins[$key] = new $key($dbconn, $config[$key]);
	}
}
	
// process call action if exist
if(($_GET['action'] == 'call') && $plugins['CallsPlugin'])
{
		$plugins['CallsPlugin']->processCallEvent($_GET['event'], $_GET['src_name'], $_GET['src_address'], $_GET['src_numb'], $_GET['dst_name'], $_GET['dst_address'], $_GET['dst_numb'], $_GET['duration']);
}

//***************************************************************************************************
//   Check if plugin data is outdated and should be updated
//***************************************************************************************************

// do update of plugins data if last update is older than config:updatetime minutes
$updateInterval = $sysconfig['updatetime'];
if (($updateInterval == null) || ($updateInterval == ""))
	$updateInterval = 5;
$updatePlugins = false;
$query  = "SELECT value FROM if_system where name = 'last_update'";
$result = mysql_query($query, $dbconn);
if (mysql_num_rows($result) > 0) {
	$row = mysql_fetch_assoc($result);
	$diff_seconds = (time() - $row['value']);
	if ($diff_seconds >= (5*60))
	{
		$updatePlugins = true;
		// update last_update date
		$query = "UPDATE if_system SET value = '".time()."' WHERE name = 'last_update'";
		mysql_query($query, $dbconn) or die('Error, insert query failed: '.mysql_error());
	}
	
} else {
	$updatePlugins = true;
	// insert last_update date
	$query = "INSERT INTO if_system (name, value) VALUES ('last_update', '".time()."')";
	mysql_query($query, $dbconn) or die('Error, insert query failed: '.mysql_error());
}
mysql_free_result($result);

// debug: never do updates
//$updatePlugins = false;


//***************************************************************************************************
//   Output
//***************************************************************************************************

// styles
$style = array(); 
$style['textcolor'] = ImageColorAllocate ($im, 255, 255, 255);
$style['font'] = 'resources/calibri.ttf';
$style['fontb'] = 'resources/calibrib.ttf';

// current vertical offset for main info area. Must be increased by each plugin writing to this area,
// so that following plugins knows correct y position to start output from
$currentYOffset = 160; 

// Do output of all plugins (calls, weather, calendar, mails...) with one exception:
// If the phone is currently ringing, skip displaying other plugins to show the name/number
// of the caller as fast and huge as possible and don't waste time/space for weather, mails etc...
if (($plugins['CallsPlugin']) && ($plugins['CallsPlugin']->isPhoneRinging())) {
	// only print calls
	$plugins['CallsPlugin']->doOutput($im, $style, $updatePlugins, $currentYOffset);
} else {
	// print all plugins in the order as they are in config
	foreach ($plugins as $plugin) {
		$plugin->doOutput($im, $style, $updatePlugins, $currentYOffset);
	}

				$opt1 = array(
					'width' => 150,
					'align' => ALIGN_RIGHT
				);
		imagettftextboxopt($im, 20, 0, $image_width-162, 113, $style['textcolor'], $style['font'], ' '.str_pad(number_format($TempAkt,1),3,'0',STR_PAD_LEFT).'°C', $opt1);
		$Baum = ImageCreateFromPNG ( 'resources/icons/tree.png' );
		ImageCopy($im, $Baum, $image_width-108, 111, 0, 0, imagesx($Baum), imagesy($Baum));
		ImageDestroy($Baum);
		imagettftextboxopt($im, 20, 0, $image_width-285, 113, $style['textcolor'], $style['font'], ' '.str_pad(number_format($TempAkt1,1),3,'0',STR_PAD_LEFT).'°C', $opt1);
		$Haus = ImageCreateFromPNG ( 'resources/icons/house.png' );
		ImageCopy($im, $Haus, $image_width-235, 112, 0, 0, imagesx($Haus), imagesy($Haus));
		ImageDestroy($Haus);


	// check again for active incomming call (maybe we just get one in the meantime while updating/printing all other plugins)
	// if so don't output any image to prevent overwriting the (incoming call) image already sent to another request
	if (($plugins['CallsPlugin']) && ($plugins['CallsPlugin']->isPhoneRinging()))
		die();
}

// print date & time
$opt = array(
    'width' => 450,
    'align' => ALIGN_LEFT
);
imagettftextboxopt($im, 72, 0, 20, 25, $style['textcolor'], $style['font'], strftime("%H:%M"), $opt);
imagettftextboxopt($im, 20, 0, 20, 100, $style['textcolor'], $style['font'], datumDeutsch(strftime("%A, %d. %B %Y (KW%V)")), $opt);   
imagesetthickness($im, 2);
imageline($im, 15, 140, $image_width-20, 140, $style['textcolor']);


// resize image
//$resized_image = imagecreatetruecolor(480, 324);
//imagecopyresampled($resized_image, $im, 0, 0, 0, 0, imagesx($resized_image), imagesy($resized_image), imagesx($im), imagesy($im));
//$im = $resized_image;   


// set the HTTP header type to jpeg
header("Content-type: image/jpeg"); 

// send the new PNG image to the browser
ImageJpeg($im);
 
// destroy the reference pointer to the image in memory to free up resources
ImageDestroy($im); 
 
// close database connection
DbConnection::disconnect();
 
?>

Wäre es mit deinem Code einfacher es zu realisieren? Auch das er auf minus Temperaturen richtig reagiert? Also weil da ja noch ein - vor der Zahl steht? Oben ist der gesamte Code der Index.php hoffe Du / Ihr versteht ihn.

kduin: Meine Vermutung: $TEMPbuffer1 = fgets($TEMPhandle, 100); gibt false zurück.

PHP-Referenz: "fgets — Liest eine Zeile von der Position des Dateizeigers"

Sprich: Du hast nur eine Zeile in der Datei, willst mit dem zweiten fgets aber eine zweite Zeile auslesen, weshalb du ein false von fgets zurückgegeben bekommst.

Ja ich dachte das er durch das erneute fget den inhalt der txt Datei nochmals ausließt. Also das in beiden $TEMPbuffern der selbe Inhalt drin steht. Dem ist wohl nicht so.....Mir qualmt der Kopf.... :~

Warum gibst Du die Temperaturen nicht einfach in einem String aus?

z.B. Temperatur Wohnzimmer, Schlafzimmer, Kinderzimmer, Garten

Ausgabe Arduino: 21|14|20|-15

php: $temp = explode("|", $stringVonArduino);

echo 'Temperatur Wohnzimmer '.$temp[0].'grad'; echo 'Temperatur Schlafzimmer '.$temp[1].'grad'; echo 'Temperatur Kinderzimmer '.$temp[2].'grad'; ... ..

Ich würde Dir empfehlen die Werte in einer anderen Form in der Textdatei zu speichern. Ich würde die Textdatei gar nicht überschreiben sondern immer mit der aktuellen Uhrzeit den neuen Wert in eine neue Zeile schreiben. Dann kannst Du den Verlauf auswerten, ist eine nette Spielerei.

Wenn Du $TEMPbuffer1 = fgets($TEMPhandle); gegen $TEMPbuffer1 = $TEMPbuffer; tauscht geht es.

$TEMPbuffer1 ist leer wenn nur 1 Zeile in der Txt Datei steht.

Danke für eure Antworten. Komme erst heute Abend dazu eure Vorschläge umzusetzten und auszuprobieren.

@Voralpenkreuz: Welche Form meinst Du? Ich benutze das ENC28J60 Ethernet Modul mit diesem Treiber hier http://trollmaker.com/article11/arduino-1-0-with-enc28j60-ethernet-shield-v1-1/

Dieser Treiber scheint doch recht abgespeckt zu sein im Gegensatz zum Offiziele Shield. Aus diesem Grunde habe ich mich für das Parsen des Webservers entschieden. Jedoch hast Du mich auf folgende Idee gebracht: Wäre es möglich die Daten auf meinem Server zu Speichern (So wie Du es vorgeschlagen hast) und dann mit dem Ardunio die Datei einzulesen und dann den Grafischen Temperaturverlauf über den Ardunio Webserver anzeigen zu lassen? Grund hierfür ist der fehlenede SD Slot ^^

Was tut ihr da 8)

http://php.net/manual/de/function.preg-match.php

da ist alles mit drinnen, schieb den betreffenden Wert zwischen 2 sichere Zeichen, schneide es raus. Vorzeichen, alles dabei. Einzeiler ;)

df6ih: Was tut ihr da 8) http://php.net/manual/de/function.preg-match.php

Wo ist da der Vorteil gegenüber dem Explode?

Dass explode() total übers Ziel hinausschießt. preg_match() ist exakt die Funktion, die exakt das tut, was er will. Alles andere ist mit der Hand durch den Rücken in den Hals. Totaler Pfusch. Was haben die Leute nur gegen Regular Expressions? Sicherlich sehen sie kompliziert aus - aber wenn man das Prinzip verstanden hat, sind sie extrem leistungsfähig.

Hi zusammen,

also ich habe mal grad den Code von Kudin eingebaut:

// read Temperature File
$TEMPhandle = fopen ("arduino/Arduino_Temp_2.txt", "r");
    $TEMPbuffer = fgets($TEMPhandle, 100);
fclose ($TEMPhandle);
if(preg_match('/Wohnzimmer: ([\-0-9.]+)(.*?)Aussen: ([\-0-9.]+)/i', $TEMPhandle, $result)
{
 // suchmuster gefunden, daten in der datei gültig
 $temp1 = intval($result[1]);  // temperaturwerte sind als string im $result-array gespeichert
 $temp2 = intval($result[3]); // intval macht einen integer-wert (bzw float) aus ihnen

}
else
{
 echo 'Suchmuster konnte nicht gefunden werden!';
}

leider geht dann gar nichts. Habe mit auskommentieren herausgefunden das es an dem Befehl preg_match liegt oder zumindest was damit zu tun hat. Wahrscheinlich ist das Suchmuster falsch? Ich kann mir auch kein Error anzeigen lassen, dachte das funktioniert mit Error_all aber irgendwie nicht bei mir...

Andere Frage: muss in der if Zuweisung nicht TEMPbuffer statt TEMPhandle stehen?

Du solltest die gesamte Arduino Webseite in eine Variable lesen und die dann in ihre Bestandteile zerlegen, 2 oder 3 bis 4 Zeilen. Dateien schreiben wird schnell nicht handlebar, wenn du da mehrere Betsandteile hast. Überdies ist das nicht zielführend, bremst nur.

muss in der if Zuweisung nicht TEMPbuffer statt TEMPhandle stehen?

doch, mein fehler...

Lass dir doch erstmal alle variablen ausgeben, dann können wir den fehler weiter einkreisen.

// read Temperature File
$TEMPhandle = fopen ("arduino/Arduino_Temp_2.txt", "r");
    $TEMPbuffer = fgets($TEMPhandle, 100);
 echo 'TEMPbuffer: '.$TEMPbuffer.'
';
 echo 'TEMPhandle: '.$TEMPhandle.'
';
fclose ($TEMPhandle);
if(preg_match('/Wohnzimmer: ([\-0-9.]+)(.*?)Aussen: ([\-0-9.]+)/i', $TEMPhandle, $result)
{
 // suchmuster gefunden, daten in der datei gültig
 $temp1 = intval($result[1]);  // temperaturwerte sind als string im $result-array gespeichert
 $temp2 = intval($result[3]); // intval macht einen integer-wert (bzw float) aus ihnen

 echo 'temp1: '.$temp1.'
';
 echo 'temp2: '.$temp2.'
';

}
else
{
 echo 'Suchmuster konnte nicht gefunden werden!';
}

Poste mal was dabei rauskommt.

Guten morgen,

habe gestern noch den Code versucht. Leider kommt nur "Interner Server Fehler 500" als ausgabe. Sonst nix mehr. Sowohl wenn ich den Code in die gepostete Index.php einbinde, sowie in eine "Nackte" Index.php welches nur den von Dir geposteten Code enthält. Beides mal erscheint der "Interner Server Fehler 500".

Bin grad etwas ratlos.....Kann man das den nicht irgendwie auf Error Reporting stellen? Irgendwie bin ich ja Blind und kann euch und mir ja auch keine weiteren Fehlerbeschreibung liefern.

@Voralpenkreuz: Nur zur Info, Du hast recht. Wenn ich den Code so umstelle wie Du es gepostet hast, funktioniert es. :)

Wäre es möglich die Daten auf meinem Server zu Speichern

Ja natürlich. Ich betreibe u.a. einen Arduino als Client. Dieser schickt mir alle 5 Minuten eine Request mit entsprechenden Daten an den Server. Dieser speichert die Daten, und gibt sie später entsprechend aufbereitet aus. In meinem Fall müssen bei einer Jahresstatistik über 100.000 Daten pro Variable verarbeitet/durchlaufen werden. Auch weniger sind ist für den Arduino zu viel. Wenn man dafür nur JavaScript verwendet, wird es auch für Clients (Smartphones) eng. Darum die ServerPHP Variante. Wenn der Arduino Server bleiben soll, musst Du die Daten eben per Cronjob pollen. Sonst als Client.

Ich würde es so machen: Der Arduino misst permanent die Temperatur. Bei einer Veränderung der Temperatur um 1 Grad, werden die Daten an den Server geschickt. Der Server speichert die Daten in der Form: Uhrzeit_Temp1_Temp2_Temp3[Zeilenumbruch] Also 1342771934_0293_0283_0288 in einer Datei. (Uhrzeit kommt vom Server)

Wenn dir das Aussehen der Datei und der Kommunikation egal ist, dann mach den String so, dass er immer die gleiche Länge hat. Also: …?T1=0293&T2=0283&T3=0288 Die TemperaturDaten werden inkl. Nullstellen in die Datei geschrieben. Beim Auslesen mit PHP beginnen die Temperaturwerte immer an der gleichen Stelle. Die Nullen werden ignoriert. Wenn Du als Temperaturwerte Kelvin nimmst, ersparst Du dir das Minus (mit seehr großer Wahrscheinlickeit). Du musst nur die Temperatur in Einer, Zehner und Hunderterstelle zerlegen, und diese an der jeweiligen Stelle im Request eintragen. Wenn der Messbereich zwischen (-173°C und 726°C liegt, reichen auch nur 3 Stellen)

Solange Du Kontrolle über beide Server hast, kannst Du Dir ja alles so einfach wie möglich machen.