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>
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.