Flat earth : communication entre internet, processing et arduino

Mon projet lors du workshop "Hybrid scrapyard" mettait en jeu la connection entre un formulaire sur le web et le micro-contrôleur arduino, à travers un script de processing, qui ne servait qu’à passer l’info. Trois portions de code donc, présentées de manière brute ici.

1. La page web

Cette page présente un formulaire et en récolte après postage l’information pour la traiter. Le script php écrit ensuite l’information dans un simple fichier texte. Il récolte aussi l’image d’une webcam mais ceci a été abandonné lors de la présentation.
Le fichier html nécessite la librairie jquery, et le fichier a écrire "donnees.txt", ainsi qu’une css qui habille le tout.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Move the world</title>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon"/>
<link rel="stylesheet" type="text/css" href="move.css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
$(document).ready(function(){  
        $("#message").fadeOut(2000);       
        lawebcam=window.setInterval("webcam()",30000);
        });
}

function webcam(){
chiffrealea=(Math.random())*1000000 + "";
$("#ima").attr("src","webcam.jpg?" + chiffrealea);
}

</script>
</head>
<body>
<div id="general">
<h1>Petite terre carrée</h1>
<?php
if(isset($_POST['action'])){
        $move=addslashes($_POST['dieu']);
        $chemin_fichier="donnees.txt";
       
        // ecriture du fichier
        if(!$fichier = fopen($chemin_fichier, "r+")) {
                echo "<div style='color:red'>lecture du fichier ". $requete . "impossible</div>";
        } else {
       
        $recup="";
        while (!feof($fichier)) {
        $buffer = fgets($fichier, 4096);
        $recup .=$buffer;
        }
        fclose ($fichier);
        }
        // recuperer le compteur
        $lignes=split("/n",$recup);
        $compteur=intval($lignes[0]);
        $compteur++;
        echo "<span style='color:#fff'>compteur ".$compteur."</span>";
       
        $lehtml=$compteur." \n".$move;
       
        if(!$fichier = fopen($chemin_fichier, "w+")) {
                echo "<div style='color:red'>lecture du fichier ". $requete . "impossible</div>";
        } else {
        fputs($fichier, $lehtml);
        fclose ($fichier);
        echo "<div id='message'>Ainsi Soit-il.</div>";
        }       
       
}

?>
<form action="index.php" method="post">
<img src="god.jpg" />
<div id="introduction">

</div>

<div id="imgwebcam">
<img id="ima" src="webcam.jpg" />
</div>

<div id="formulaire">
<h2>Je veux...</h2>
<select name="dieu" id="dieu">
<option value="X">laisser faire...</option>
<option value="a">un feu de camp, et une guitare</option>
<option value="b">un centre-ville animé</option>
<option value="c">une bonne guerre</option>
<option value="d">une tremblement de terre</option>
<option value="e">Donner un coup de boost</option>
<option value="f">Ralentir un peu le cours des choses</option>
</select>

<input type="hidden" name="action" value="avance" />

<div class="lesubmit">
<input type="submit" value="Maintenant" />
</div>
</div>
<div class="clearer"></div>
</form>

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

La page est ici et active.

2. Le code processing

Le code processing lit le fichier "donnees.txt" et en lit la première ligne. C’est un compteur. Si le chiffre a changé, il prend le caractère écrit en deuxième ligne et l’envoie au micro-contrôleur par le port série. Ici, le Xbee fait la connection.

/* charge le fichier du web
donne l'info à arduino par le port série
*/
import processing.serial.*;

int compteurprecedent=0;
int value=0;

Serial port; // Create object from Serial class
char actuel;

void setup() {
 size(600, 140);
 PFont fontA = loadFont("TradeGothic-BoldCondTwenty-32.vlw");
 textFont(fontA, 32);
 smooth();
 background(30);
 println(Serial.list());

 frameRate(10);
 // Open the port that the board is connected to and use the same speed (9600 bps)
 port = new Serial(this, Serial.list()[0], 9600);
}

void draw() {
 fill(255);
 text("Just reading a file on the web", 10, 40);
 text("and pass the information to arduino", 10, 100);
 String lines[] = loadStrings("http://www.workplace.lescorsaires.be/active_world/donnees.txt");
 int compteuractuel=int(trim(lines[0]));

 // envoyer, un caractère par ligne dans le fichier texte pompé...

 if(compteurprecedent !=compteuractuel){
   println("compteur : " + compteuractuel);
   for(int li=1; li<lines.length;li++){
     actuel = lines[li].charAt(0);
     
     if (actuel != 88){
       println("on envoie -" + actuel + "-");
       port.write(actuel);
     }
   }
   compteurprecedent=compteuractuel;
 }

 // retour d'info de arduino
 while (port.available() > 0) {
   int inByte = port.read();
   println("reçu "  + char(inByte));
 }

}

void keyPressed()
{
 if(key == 'z' && value==0) {
   value = 1;
   port.write(90);
 } else {
   value = 0;
 }
}

3. Le code embarqué sur Arduino

Le code démarre de manière autonome et prévoit des événements dans le futur en fonction de paramètres : temps avant l’événement, durée de l’événement, a quel contact envoyer le signal. Il écoute ensuite le port série pour y recevoir les informations envoyée par processing.

// Flat earth par S. Noel

// definit les prochains evenements
int next[] = { 10,20,30,40,50 };
int average[] = { 420,340,320,200,180 };
int action[] = { 0,0,0,0,0 };
// move : 7 shake : 12, war : 11, fete : 6, campfire : 7, autre : 10
int sortie[] = { 12,7,11,9,6 };

int delai= 500;
int nbeve = 5;
void setup() {

 // dans le setup
 Serial.begin(9600);
 // declare toutes les sorties
 for(int u=0; u<nbeve; u++){
   pinMode(sortie[u], OUTPUT);
 }
 pinMode(13, OUTPUT);
}

void loop(){

 // decroit les evenements, verifie s'ils sont arrives à zero
 for(int u=0; u<nbeve; u++){
   next[u]--;

   if (next[u]<1){
     // declenche une action, met le compteur de l'evenement
     if (u == 0) { action[u]=16; }
     if (u == 1) { action[u]=8; }
     if (u == 2) { action[u]=60; }
     if (u == 3) { action[u]=36; }
     if (u == 4) { action[u]=70; }
     next[u]=random(50,average[u]);
     Serial.print(sortie[u]);
   }

   if(action[u] > 0){
     action[u]--;
     // active l'action
     digitalWrite(sortie[u], HIGH);
   }
   else {
     digitalWrite(sortie[u], LOW);
   }
 }

 if (Serial.available() > 0) {

   char valeur = Serial.read();

   switch(valeur) {
   case 'a':
     // feu de camp, - deville, + de shake, + de war
     next[3]=1; // feu de camp
     average[4]=average[4]+20; // moins de fetes
     average[0]=average[0]-20; // plus de shake
     average[2]=average[2]-20; // plus de war
     break;

   case 'b':
     // concert -ville : ville, - de feu de camp, - de war, + de shake
     next[4]=1; // feu de camp
     average[3]=average[3]+20; // moins de fetes
     average[0]=average[0]-20; // plus de shake
     average[2]=average[2]+20; // moins de war
     break;

   case 'c':
     // guerre
     next[2]=1; // guerre
     average[3]=average[3]+20; // moins de ville
     average[4]=average[4]-20; // plus de campfire
     average[0]=average[0]-20; // plus de shake
     break;

   case 'd':
     // tremblement : - de ville + campfire - shake, +guerre
     next[0]=1; // tremblement
     average[3]=average[3]+20; // moins de ville
     average[4]=average[4]-20; // plus de campfire
     average[0]=average[0]+20; // moins de shake
     average[2]=average[2]-20; // plus de guerre
     break;

   case 'e':
     // moins de delay
     delai=delai-30; // tremblement
     if(delai < 100) {
       delai=100;  
     }
     average[1]=average[1]-20; // plus de move
     break;

   case 'f':
     // augmente le delay
     delai=delai+30; // tremblement
     average[1]=average[1]+20; // moins de move
     break;

   case 'Z':
     // teste tout
     Serial.print(valeur);

     for(int u=0; u<nbeve; u++){
       digitalWrite(sortie[u], HIGH);
     }
     delay(2000);
     for(int u=0; u<nbeve; u++){
       digitalWrite(sortie[u], LOW);
     }
     break;  

   }
   
   // verifie les limites basses
   for(int u=0; u<nbeve; u++){
   if(average[u] < 50) {
       average[u]=50;  
     }
   }
   
   Serial.print(valeur);

 }
 delay(delai); // toutes les demi secondes par défaut
}

Le programme est basé sur 5 relais, donc 5 périphériques que l’on active ou désactive. Dans mon cas, 2 moteurs et 3 ensembles de diodes et lampes alimentés en 3 et 6 volts.

Le code n’est pas des plus propres, mais il a été produit intégralement pendant la durée du workshop.