Accueil > Développement web > AJAX - Affichage en temps réel de température relevée par une sonde.
6546 visites

AJAX - Affichage en temps réel de température relevée par une sonde.

dimanche 5 mai 2013 (), par Vader[FR]

A la demande de absolut_man sur jeuxvideopc.com, qui était intéressé par ma proposition d’aide pour réaliser un site web,

Description

Le projet de bac spé ISN consiste en un robot qui mesure la température et qui la communique sur un site internet. Pas d’une grande utilité à priori mais cela permet de balayer tout le programme de l’année.

Deux cartes arduinos sont utilisées pour la mesure météorologique :

  • une sur le robot
  • une connectée à l’ordinateur qui communiquera à intervalle régulier les données transmise par l’autre carte

Le problème était : comment faire pour mettre à jour automatiquement les données sur le site internet ?

en fait c’est assez facile
il faut sur le serveur :

  • une base de données
  • un script php qui fait la mise à jour, script appelé par un programme sur le PC qui envoie les informations à mettre en base (sonde, date, température...).
  • une page html avec un script JavaScript mettant à jour les données affichées grâce à l’AJAX.
  • un script php fournissant au script AJAX les données contenues en base.

sur le pc, il suffit d’utiliser la librairie python permettant de faire des requêtes HTTP.

voir .

la requête sera donc du type http://le_site/page_php.php?paramètre1=valeur1&paramètre2=valeur2.
la page php permettra de mettre à jour la base de données.
python ne créé pas de liste, il envoie les données 1 par 1 à un script php, qui insère les données dans une base.

un script php affichera ensuite une donnée unique ou une liste, selon ce qui est prévu. donnée unique mise à jour par l’AJAX, liste éventuellement sous forme graphique pour présenter l’évolution.

Base de données

la base n’a besoin que d’une seule table : releves, contenant 3 champs

  • numéro identifiant de la sonde - champ sonde de type int, entier.
  • date + heure du relevé de température - champ date de type datetime, contenant donc date et heure.
  • température relevée - champ temperature de type float, un nombre "flottant" donc à virgule, appartenant à l’ensemble mathématique des nombres réels ℝ.

tous ces champs sont non nuls, non uniques.

l’index PRIMARY KEY (clé primaire, permettant d’identifier de manière unique une ligne de la table) sera la combinaison du numéro de sonde et de la date/heure du relevé de température.

Script PHP mise à jour BDD

Ce script est extrêmement simple : Il reçoit en entrée plusieurs variables et les insère dans la base.

<?php
	if (isset($_GET["sonde"])){
		$sonde=$_GET["sonde"];
	}else{
		$sonde=1; // par défaut
	}

	if (isset($_GET["temper"])){
		$temper=$_GET["temper"];
	}

le script PHP étant appelé par un script python, l’envoi des données se fait dans l’url de la requête html générée par le script python.

la méthode est donc GET.
la récupération des variables se fait alors par $_GET["variable"]

par sécurité autant que par simplicité, on vérifie l’existence de la variable (dans la requête) avant d’essayer de récupérer sa valeur, avec la fonction isset(variable).
Et pour simplifier encore plus, on rend certaines variables optionnelles en prévoyant des valeurs par défaut.

Ainsi, s’il n’y a qu’une seule sonde et/ou qu’on ne souhaite pas se compliquer en envoyant la date/heure sous un format complexe, le script php s’en charge.

	if (isset($_GET["quand"])){
		$quand=$_GET["quand"];
	}else{
		$quand=date("Y-m-d H:i:s"); // par défaut, date actuelle
		// format YYYY-MM-DD HH:MM:SS ===> datetime en MYSQL		
	}

la date/heure, justement, doit être au format datetime de MySQL pour être insérée en base, à savoir de type YYYY-MM-DD HH:MM:SS

	// connexion à la base SQL et insertion des données
	include_once "connect.php";

Pour se connecter à la base, on fait appel à un autre script, ce qui permet d’avoir plusieurs scripts PHP différents utilisant la même méthode de connexion, rendant les changements et corrections plus faciles.
<?php
        mysql_connect('adresse serveur MySQL','utilisateur MySQL','mot de passe','MYSQL');
        mysql_select_db("nom de la base");
?>

Puis, si la température est définie et que la variable contient une valeur, on insère les données.

	if (isset($temper)&&$temper!=null){
		$reki="insert into releves (sonde,date,temperature) values ($sonde,'$quand',$temper)";
		$resi=mysql_query($reki);
	}
?>

null est un mot clé signifiant "rien". Pas zéro, pas chaîne vide, mais rien, pas de valeur. la variable existe mais n’est pas initialisée. C’est un peu comme s’il y avait un feu tricolore, mais éteint. Il est là, il existe, mais il n’est pas vert, orange ou rouge et n’a donc pas de "valeur".

la requête SQL est très simple, l’ordre étant insert dans la table releves dans les champs correspondants sonde, date, temperature des valeurs $sonde, $quand, $temper.

Par sécurité, on aurait pu ajouter un système de clé ou autre moyen d’authentifier le "client" envoyant la requête de mise à jour, que ce soit au niveau du script php, au niveau du serveur web Apache (SSL), ou même ou niveau du pare-feu iptables (chaîne mangle) sous Linux.
Le problème ne se posant pas sur ce projet et par souci de simplicité, cela n’a pas été envisagé.

Page HTML

La page html est assez simple

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="fr" dir="ltr" xml:lang="fr" xmlns="http://www.w3.org/1999/xhtml">

On indique :

  • le type de document xhtml1 transitionnal. Les types de documents normalisés W3C définissent le contenu utilisable et permettent un traitement homogène par les navigateurs respectant ces normes.
  • la langue
  • la direction d’affichage : dir = ltr, left-to-right, de gauche à droite
	<head>
		<title>Projet Celcius (ISN)</title>
		<meta http-equiv="content-Type" content="text/html; charset=UTF-8"/>
		<meta name="description" content="Projet Celcius"/>
		<link media="all" type="text/css" href="style.css" rel="stylesheet"/>
		<script type="text/javascript" src="absolut.js"> </script>
	</head>

en entête de page, on fournit d’autres informations comme le titre de la page, l’encodage des caractères, la description rapide du contenu.
Une feuille de style CSS et un fichier de script JavaScript sont également appelés.

	<body onload="AuChargement();">
		<div id="head">
			<h1>Projet Celcius - Mesure de la température</h1>
		</div>

le style CSS associé

body {
	margin-top: 0px;
	margin:0;
	background-image:url("ciel2.jpg");
	background-repeat:repeat;
	font-family: Helvetica, sans-serif;
	font-size: 1em;
	color: black;
}

Le corps de page n’a pas de marge, possède une image d’arrière plan qui sera répétée (et non étirée ou fixée), on utilise la police d’écriture Helvetica, de taille 1em et la couleur du texte sera le noir.

body a, body a:visited {
	color: brown;
	cursor:pointer;
	text-decoration:underline;
}

les liens seront en marron et soulignés, qu’ils aient été visités ou non.

body a:hover {
	color: darkred;
}

Au survol de la souris (évènement "hover"), les liens passeront en rouge sombre.

La page se divise en 2 blocs : entête et contenu.

style CSS

#head, #content h2, #content h3, #temperature {
	text-align:center;
}

Dans l’entête, le texte est centré.
De même que pour les titres (entre balises <h2></h2> et <h3></h3>) dans le contenu et le contenu du bloc "température".

le bloc contenu contiendra un petit texte explicatif ainsi que le bloc "température", dont le contenu sera mis à jour et dédié à l’affichage de la température.

		<div id="content">
			<h2>&curren; Bienvenue sur le site de notre Projet ISN &curren;</h2>
			<p><b>Intitulé du projet :</b> Réaliser un programme et mettre en oeuvre des équipements permettant à un robot Mindstorm NXT de se déplacer dans une pièce en évitant les obstacles et de transmettre des
mesures de température rafra&icirc;chies automatiquement sur un site internet.</p>
			<h3>Température en temps réel :</h3>

style CSS

Pour que le texte soit lisible et non collé aux bords de la page, une propriété de style inclut une marge à l’intérieur du bloc entre les bords du bloc et le contenu.

#content {
	padding:5px;
}

"padding" représente un espace à l’intérieur, entre le bord du contenant et son contenu. "margin" représente un espace à l’extérieur, entre le bord d’un élément et les éléments qui l’entourent.
Des propriétés différentes peuvent être déclarées pour les bords gauche, haut, droit et bas : padding-left, padding-top, padding-right, padding-bottom, margin-left, ...

Le bloc "température" contient par défaut une valeur quelconque, qui sera ensuite mise à jour par le script JavaScript qui demandera à un script PHP une valeur en base de données, avant d’insérer ladite valeur dans la page.

			<div id="temperature"><i>en attente de mise à jour</i></div>

style CSS

#temperature{
	font-size:1.5em;
	color:darkred;
}

Au cas où l’utilisateur aurait désactivé JavaScript, un bloc sera par défaut affiché. Si Javascript est activé, une fonction cachera ce bloc.

			<div id="jsornotjs">
					Vous avez désactivé JavaScript. JavaScript est requis pour le rafra&icirc;chissement des donn&eacute;es de temp&eacute;rature relev&eacute;es par le robot sur le site.
			</div>

style CSS

#jsornotjs{
	color:darkred;
	font-weight:bold;
}

Puis on ferme le bloc contenu, le corps de page et la page.

		</div>
	</body>
</html>

Ce qui donne ceci :

Script JS de mise à jour de la page en AJAX

AJAX consiste à envoyer de manière asynchrone une requête de type XMLHttpRequest et d’effectuer ensuite un traitement du document XML fourni en réponse.

Il faut donc d’abord une fonction pour créer l’objet XMLHttpRequest

création de l’objet XMlHttpRequest

function get_requete_maj_temp(){
	var requete_maj_temp=null;

Ici, la variable requete_maj_temp est initialisé à vide. Si la fonction s’exécute correctement, elle contiendra un objet de type XMLHttpRequest

Selon le navigateur, la fonction à utiliser est différente :

  • window.XMLHttpRequest sous Firefox
  • new ActiveXObject("Msxml12.XMLHTTP") ou new ActiveXObject("Microsoft.XMLHTTP") sous IE, selon la version.
	if (window.XMLHttpRequest){ // Firefox
		requete_maj_temp=new XMLHttpRequest();
	}else if (window.ActiveXObject){ // IE

des blocs try { } catch (erreur) { } permettent "d’essayer" de réaliser certaines actions, et de récupérer le code/message d’erreur dans la variable erreur avant d’exécuter d’autres actions en cas d’échec de l’essai.
Cela permet dans cette fonction de tester les différentes versions d’Internet Explorer.

		try{
			requete_maj_temp=new ActiveXObject("Msxml12.XMLHTTP");
		}catch(e){
			try{
				requete_maj_temp=new ActiveXObject("Microsoft.XMLHTTP");
			}catch(e1){
				requete_maj_temp=null;
			}
		}
	}else{ // non supporte
		alert("Votre navigateur ne supporte pas les XMLHTTPRequest.");
	}

Une fois fini, que l’objet soit créé ou non, on retourne la variable.
Celle-ci contient null si on n’a pas pu créer d’objet XMLHttpRequest

	return requete_maj_temp;
}

Ensuite, cet objet doit être envoyé à un script sur un serveur

création et envoi de la requête

On fournit à cette fonction l’adresse où envoyer la requête.

function requete_requete_maj_temp(url){

On tente de créer la requête en utilisant la fonction déclarée précédemment.

	requete_maj_temp=get_requete_maj_temp();

Si la variable n’existe pas ou si elle contient null, on affiche un message d’erreur et on quitte la fonction par la directive return.

	if ((!requete_maj_temp)&&(requete_maj_temp!=null)){
		alert("Votre navigateur ne peut creer de requete AJAX.");
		return;
	}

Sinon, on définit la fonction JavaScript chargée de traiter la réponse et on envoie la requête.

	requete_maj_temp.onreadystatechange=function(){traitementReponse_requete_maj_temp(requete_maj_temp)}
	requete_maj_temp.open("GET",url,true);
	requete_maj_temp.send(null);
}

traitement de la réponse

function traitementReponse_requete_maj_temp(requete_maj_temp){
	var contenu;

la variable <contenu contiendra le document réponse XML.

Lorsque la requête sera dans l’état "4", à savoir présence d’une réponse, le traitement sera effectué.

	if (requete_maj_temp.readyState==4){

On charge le contenu du document XML de réponse dans la variable

contenu=requete_maj_temp.responseXML;

Puis on récupère une par une les valeurs des différentes balises XML (tag) avec getElementsByTagName(code_balise), qui renvoie un tableau de tous les éléments ayant ce tag Html/XML.
On cherche généralement le premier élément du tableau,

 dont on récupère la valeur.

<code>
		temper=contenu.getElementsByTagName("temperature")[0].firstChild.nodeValue;
		dater=contenu.getElementsByTagName("date")[0].firstChild.nodeValue;
		heurer=contenu.getElementsByTagName("heure")[0].firstChild.nodeValue;
		sonde=contenu.getElementsByTagName("sonde")[0].firstChild.nodeValue;

Ensuite, si le bloc dédié à l’affichage des données existe dans la page Html affichée, on le met dans une variable afin de pouvoir y écrire les données reçues avec la directive variable_bloc.innerHTML=chaîne texte.

		if (this.document.getElementById('temperature')!=null){
			var div_temp=this.document.getElementById('temperature'); // le div
			div_temp.innerHTML="sonde "+sonde+" : "+temper+"°C le "+dater+" à "+heurer;

Enfin, pour continuer de mettre à jour les données affichées, on définit un compte à rebours, ici de 10 secondes (10 000 milli-secondes) avec la directive SetTimeout pour appeler la fonction lançant la mise à jour et définie juste après.

			setTimeout("maj_temp()",10000);
		}
	}
}

fonction de chargement de la température

il aurait été possible de faire appel direct à function requete_requete_maj_temp(url) mais cette fonction encapsule l’url pour faire un appel plus simple
on pourrait aussi accepter des paramètres de maj_temp.php, comme le numéro de sonde

function maj_temp(){
	if (this.document.getElementById('temperature')!=null){
		requete_requete_maj_temp('maj_temp.php');
	}
}

La fonction de mise à jour est lancée au chargement de la page (évènement onLoad de l’élément body), via cette fonction AuChargement, qui permet de lancer plusieurs traitements.
Cette fois, le compte à rebours est de 1 seconde, pour initialiser dans la page une valeur de la base de données.

// fonction pour les scripts a lancer au chargement de la page => body onLoad
function AuChargement(){
	pisteIE();
	cacherjs();
	setTimeout("maj_temp()",1000); // 1 seconde
}

Script PHP envoyant les données au script JS

Ce script, appelé par la fonction JavaScript, est extrêmement simple.
Il récupère en base la dernière valeur de température pour une sonde donnée, la sonde "par défaut" étant la 1.

<?php
	// recuperation des champs
	if (isset($_GET["sonde"])){
		$id=$_GET["sonde"];
	}else{
		$id=1; /* sonde par defaut */
	}

On génère ensuite l’entête du document réponse...

	
	header('content-Type:text/xml,charset=UTF-8');

...avant de se connecter à la base, récupérer les valeurs...

	include_once "connect.php";
	
	$rek="select sonde,DATE_FORMAT(date,'%d/%m/%Y') as date, DATE_FORMAT(date,'%H:%i:%s') as heure,temperature from releves where sonde=$id order by date DESC limit 0,1";
	$res=mysql_query($rek);
	$row=mysql_fetch_array($res,MYSQL_ASSOC);
	
	$sonde=$row["sonde"];
	$date=$row["date"]; // a transformer en texte
	$heure=$row["heure"]; // de meme
	$temperature=$row["temperature"];

... et de les mettre dans le document XML.

  • d’abord, créer le document par new DOMDocument("1.0","utf-8");
  • puis y mettre la balise racine createElement('root'); suivi de appendChild();
  • ajouter les éléments par createElement(nom_balise, valeur); suivi de appendChild();
	$doc=new DOMDocument("1.0","utf-8");
	$racine=$doc->createElement('root');
	$doc->appendChild($racine);
	$xmlsonde=$doc->createElement('sonde',$sonde);
	$racine->appendChild($xmlsonde);
	$xmldate=$doc->createElement('date',$date);
	$racine->appendChild($xmldate);
	$xmlheure=$doc->createElement('heure',$heure);
	$racine->appendChild($xmlheure);
	$xmltemp=$doc->createElement('temperature',$temperature);
	$racine->appendChild($xmltemp);

et enfin envoyer le document.

	echo $doc->saveXML();	
?>

Répondre à cet article

Total 436238 visites depuis 4630 jours | | SPIP | | Plan du site | Suivre la vie du site RSS 2.0