Go Down

Topic: Programmateur d'arrosage avec gestion web (Read 726 times) previous topic - next topic

sancho119

May 24, 2018, 11:24 pm Last Edit: May 24, 2018, 11:36 pm by sancho119
Bonjour,

J'essaie péniblement de finir un programmateur d'arrosage, avec une supervision sur page web (pour une future intégration domotique).

Je réalise mon système en 3 mode : 1 mode arrêt (facile !), un mode automatique (avec déclenchement à heures fixes, et suivant une durée déterminée), et un mode manuel.
J'avais commencé par le mode automatique, en autonome sur un arduino mega.
Puis en passant sur le monde manuel, je me suis dit que ce serait pas mal de le faire à distance, d'où l'idée de revoir l'ensemble avec une gestion par un page html (qui ne sera pas généré par l'arduino). L'idée étant d'avoir une page principale avec la possibilité d'afficher 3 pages différentes pour chaque mode.

Je me suis donc lancé dans la partie mode manuel, en essayant de faire allumer mes réseaux d'arrosage (pour l'instant juste représentés par des leds), via une page web, avec un retour d'état.
Je parviens à faire allumer mes leds, mais je voulais également un retour d'état, et là ça bloque.
Je me suis inspiré du blog d'eskimon (https://eskimon.fr/tuto-arduino-803-arduino-et-ethernet-serveur#code-complet) mais je ne parviens pas à le reproduire pour mon cas (j'ai d'ailleurs essayé le sien sans rien modifier, ça ne fonctionne pas non plus :( )

Sur Firefox, j'ai un message dans la console :

XMLHttpRequest { onreadystatechange: onreadystatechange(), readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, responseURL: "", status: 0, statusText: "", responseType: "", response: "" }
ArduinoArrosage.js:49:5
Blocage d'une requête multiorigines (Cross-Origin Request) : la politique « Same Origin » ne permet pas de consulter la ressource distante située sur http://192.168.0.123/?r=1. Raison : l'en-tête CORS « Access-Control-Allow-Origin » ne correspond pas à « (null) ».

et sur Chrome j'ai :
XMLHttpRequest {onreadystatechange: null, readyState: 1, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}onabort: nullonerror: nullonload: nullonloadend: nullonloadstart: nullonprogress: nullonreadystatechange: ƒ ()ontimeout: nullreadyState: 4response: ""responseText: ""responseType: ""responseURL: "http://192.168.0.123/?r=5"responseXML: nullstatus: 200statusText: "OK"timeout:
Réponse reçue:
Uncaught SyntaxError: Unexpected end of JSON input
at JSON.parse (<anonymous>)
at afficher (ArduinoArrosage.js:68)
at XMLHttpRequest.requete.onreadystatechange (ArduinoArrosage.js:58)
afficher @ ArduinoArrosage.js:68
requete.onreadystatechange @ ArduinoArrosage.js:58
XMLHttpRequest.send (async)
executer @ ArduinoArrosage.js:50

Je vous mets mon code arduino, sans l'init et le setup :

Code: [Select]


void loop()
{
  // Regarde si un client est connecté et attend une réponse
  EthernetClient client = serveur.available();
  if (client) {
    // Quelqu'un est connecté !
    Serial.println("On envoi !");
    url = ""; // on remet à zéro notre chaîne tampon
    index = 0;
    while (client.connected()) { // Tant que le client est connecté
      if (client.available()) { // A-t-il des choses à dire ?
        // traitement des infos du client
        char carlu = client.read(); //on lit ce qu'il raconte
        if (carlu != '\n') { // On est en fin de chaîne ?
          // non ! alors on stocke le caractère

          url[index] = carlu;
          index++;
        } else {
          // on a fini de lire ce qui nous intéresse
          // on marque la fin de l'url (caractère de fin de chaîne)
          url[index] = '\0';
          Serial.println("message reçu de la page HTML :");
          Serial.println(carlu);
          boolean ok = interpreter(); // essaie d'interpréter la chaîne
          if (ok) {
            // tout s'est bien passé = on met à jour les réseaux
            Serial.println("On allume !");
            action();
          }
          // et dans tout les cas on répond au client
          repondre(client);
          // on quitte le while
          break;
        }
      }
    }
    // Donne le temps au client de prendre les données
    delay(10);
    // Ferme la connexion avec le client
    client.stop();
  }
}

void repondre(EthernetClient client) {
  // La fonction prend un client en argument

 Serial.println("\nRepondre"); // debug
  // On fait notre en-tête
  // Tout d'abord le code de réponse 200 = réussite
  client.println("HTTP/1.1 200 OK");
  // Puis le type mime du contenu renvoyé, du json
  client.println("Content-Type: application/json"); //application/json
  // Autorise le cross origin
  client.println("Access-Control-Allow-Origin: *");
  // Et c'est tout !
  // On envoi une ligne vide pour signaler la fin du header
  client.println();


  // Puis on commence notre JSON par une accolade ouvrante
  client.println("{");
  // On affiche les réseaux dans un tableau
  client.println("\"reseau\": {");
  // Le réseau 1
  client.print("\t\t\"1\": ");
  client.print(etats[0]);
  client.println(",");
  // Le réseau 2
  client.print("\t\t\"2\": ");
  client.print(etats[1]);
  client.println(",");
  // Le réseau 3
  client.print("\t\t\"3\": ");
  client.print(etats[2]);
  client.println(",");
  // Le réseau 4
  client.print("\t\t\"4\": ");
  client.print(etats[3]);
  client.println(",");
  // Le réseau 5
  client.print("\t\t\"5\": ");
  client.print(etats[4]);
  client.println(",");
  // Le réseau 6
  client.print("\t\t\"6\": ");
  client.println(etats[5]);
  client.println("\t\t}");
  // Et enfin on termine notre JSON par une accolade fermante
  client.println("}");
  Serial.println("réponse envoyée !");
}

boolean interpreter() {
  // On commence par mettre à zéro tous les états
  for (int i = 0; i < 6; i++) {
    etats[i] = LOW;
  }

  // Puis maintenant on va chercher les caractères/marqueurs un par un.
  index = 4; // Index pour se promener dans la chaîne (commence à 4 pour enlever "GET "
  while (url[index] != 'r' && url[index+1] != '=') { // On commence par chercher le "r="
    index++; // Passe au caractère suivant
    if (index == 100) {
      // On est rendu trop loin !
      Serial.println("Oups, probleme dans la recherche de 'r='");
      return false;
    }
  }
  // Puis on lit jusqu'à trouver le ' ' de fin
  while (url[index] != ' ') { // On cherche le ' '
    if (url[index] >= '1' && url[index] <= '6') {
      // On a trouvé un chiffre identifiant un réseau
      char reseau = url[index] - '0'; // On ramène ça au format décimal
      etats[reseau - 1] = HIGH; // Puis on met le réseau dans un futur état haut
    }
    index++; // Passe au caractère suivant
    if (index == 100) {
      // On est rendu trop loin !
      Serial.println("Oups, probleme dans la lecture des broches");
      return false;
    }
    // NOTE : Les virgules séparatrices sont ignorées
  }

  // Rendu ici, on a trouvé toutes les informations utiles !
  Serial.println("interprétation OK");
  return true;
}



Sauriez-vous me dire où est l'erreur ?
Merci par avance, en espérant que ce soir lisible...

sancho119


et mon code .js

Code: [Select]

var reseau = []; // Tableau réseaux
var etatReseau = []; // Tableau d'etat des broches
var adresse = "http://192.168.0.123:80/"; // L'url+port du shield

document.addEventListener('DOMContentLoaded', setup, false);

function setup() {
    // fonction qui va lier les variables à leur conteneur HTML
    reseau[1] = document.getElementById("reseau1");
    reseau[2] = document.getElementById("reseau2");
    reseau[3] = document.getElementById("reseau3");
    reseau[4] = document.getElementById("reseau4");
    reseau[5] = document.getElementById("reseau5");
    reseau[6] = document.getElementById("reseau6");
    etatReseau[1] = document.getElementById("etatReseau1");
    etatReseau[2] = document.getElementById("etatReseau2");
    etatReseau[3] = document.getElementById("etatReseau3");   
    etatReseau[4] = document.getElementById("etatReseau4");
    etatReseau[5] = document.getElementById("etatReseau5");
    etatReseau[6] = document.getElementById("etatReseau6");
   
    // La fonction concernant le bouton
    var bouton = document.getElementById("envoyer");
    bouton.addEventListener('click', executer, false);
}

function executer() {
    // Fonction qui va créer l'url avec les paramètres puis
    // envoyer la requête
    var requete = new XMLHttpRequest(); // créer un objet de requête
    var url = adresse;
    url += "?r=";
    for(i=1; i <= 6; i++) { // Pour les réseaux 1 à 6 de notre tableau
        if(reseau[i].checked) // si la case est cochée
            url += i + ",";
    }
    // enlève la dernière virgule si elle existe
    if(url[url.length-1] === ',')
        url = url.substring(0, url.length-1);
   
    console.log(url) // Pour debugguer l'url formée   
    requete.open("GET", url, true);
    console.log(requete) // Pour debugguer la requete 
    requete.send(null); // On envoie !
    requete.onreadystatechange = function() { // on attend le retour
        if (requete.readyState == 4) {
        console.log(requete.readyState);       
        console.log(requete.status);
        console.log("Réponse reçue: %s", requete.responseText);// Revenu !
            if (requete.status == 200) {// Retour s'est bien passé !
                // fonction d'affichage (ci-dessous)
                afficher(requete.responseText);
            } else { // Retour s'est mal passé :(
                alert(requete.status, requete.statusText);
            }
        }
    };
}

function afficher(json) {
    // Affiche l'état des réseaux revenu en json
    var donnees = JSON.parse(json);
    console.log("données reçue: %s",donnees);
   
    for(i=1; i <= 6; i++) { // Pour les reseaux 1 à 6 de notre tableau
        etatReseau[i].checked = donnees["reseau"][i];
    }
}

J-M-L

#2
May 26, 2018, 04:23 am Last Edit: May 26, 2018, 04:24 am by J-M-L
Quote
Je vous mets mon code arduino, sans l'init et le setup :
...
Code: [Select]

...
   url = ""; // on remet à zéro notre chaîne tampon
...
   url[index] = carlu;
...
Pas une bonne idée de nous donner que la moitié des infos... par exemple comment est définie url ?
Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums
Pas de messages privés SVP

hbachetti

Salut

J'ai déjà en ce genre de problème sur un serveur que j'ai développé dans mon entreprise.
Sur chaque poste client est installé un petit serveur HTTP qui sert de passerelle pour communiquer avec un matériel relié au PC par une ligne série.
Le javascript communique avec ce petit serveur comme tu le fais avec ton ARDUINO, sauf que cela se passe chez moi sur 127.0.0.1.
EN HTTP tout se passait bien.
En HTTPS par contre la console affichait ce message "Blocage d'une requête multiorigines".
J'ai été donc obligé de modifier mon petit serveur local pour qu'il accepte les requêtes HTTPS.
Ce serveur est utilisé en production chez nous - en interne - et aussi à l'étranger à travers un proxy HTTPS pour des raisons de sécurité.
Donc, en fonction de l'endroit où se situe le poste client on a :
- en interne : serveur principal en accès direct HTTP et serveur local en HTTP
- en externe : serveur principal en accès HTTPS et serveur local en HTTPS

La demande "Access-Control-Allow-Origin: *" ne résoud en rien le problème.

Donc pour résumer : l'URL que tu entres dans ton navigateur FIREFOX ou CHROME commence t-elle par HTTPS ?

@+
Linux is like a wigwam: no Windows, no Gates, and an Apache inside ...

J-M-L

#4
May 26, 2018, 10:04 am Last Edit: May 26, 2018, 10:10 am by J-M-L
On entend par origine la combinaison d'un protocole, un hôte et d'un numéro de port. Le navigateur isole les différents documents en fonction de leur origine et, en théorie, il n'est pas possible d'accéder à un contenu d'une origine A depuis l'origine B

Plus d'infos sur le multi-origines (nécessaire pour la sécurité) en lisant ceci par exemple et donc aller ensuite explorer CORS (Cross-Origin Resource Sharing)
Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums
Pas de messages privés SVP

sancho119

Bonjour,

Merci pour vos réponses.

J-M-L, la création de mon url est dans mon fichier javascript.

Code: [Select]

function executer() {
    // Fonction qui va créer l'url avec les paramètres puis
    // envoyer la requête
    var requete = new XMLHttpRequest(); // créer un objet de requête
    var url = adresse;
    url += "?r=";
    for(i=1; i <= 6; i++) { // Pour les réseaux 1 à 6 de notre tableau
        if(reseau[i].checked) // si la case est cochée
            url += i + ",";
    }
    // enlève la dernière virgule si elle existe
    if(url[url.length-1] === ',')
        url = url.substring(0, url.length-1);
    
    console.log(url) // Pour debugguer l'url formée    
    requete.open("GET", url, true);


Elle est de la forme http://192.168.0.123:80/?r=1
192.168.0.123:80 est l'adresse de mon arduino sur mon réseau, avec le port 80.

hbachetti, mon adresse est bien de la forme http (enfin celle pour envoyer la requete à mon arduino).
celle du retour est de la forme file:///C:/dossier ou est présent pour le moment ma page .html, mon .css et mon .js

J-M-L,  merci pour tes liens, je vais les lire avec attention.

hbachetti

Salut

Le mélange d'origines HTTP HTTPS n'est pas possible, mais cela serait étonnant que la même règle ne s'applique pas pour HTTP et FILE.
Il faudrait vérifier en faisant la présentation de ta page par un serveur APACHE, mais je suis prêt à parier que c'est le cas.

@+
Linux is like a wigwam: no Windows, no Gates, and an Apache inside ...

sancho119

Salut,

Je ne connais pas Apache.
Je viens de l'installer sur mon pc, et de le configurer (enfin je crois)
Je peux ouvrir ma page (qui s'appelle ArduinoArrosage.html) sur http://127.0.0.1/ArduinoArrosage.html

Malheureusement, j'ai toujours la même erreur quand je veux allumer une led. Elle s'allume mais je n'ai pas l'info en retour de l'état.
Ce qui me semble bizarre, c'est que j'ai l'impression que sous firefox il n'y a pas de retour du tout (statut de la réponse =0), alors que sous chrome, j'ai bien 200

sancho119

Il ya peut être une solution avec l'utilisation de JSONP, mais j'avoue que j'ai du mal à le mettre en oeuvre...
JSONP

hbachetti

Je me suis souvenu que le serveur doit être configuré également pour autoriser le "Access-Control-Allow-Origin".

ICI

@+
Linux is like a wigwam: no Windows, no Gates, and an Apache inside ...

sancho119

J'ai ajouté à la fin du fichier de config du serveur apache

<IfModule mod_headers.c>

    #Accept cross-domain requests

    Header always set Access-Control-Allow-Origin "*"

</IfModule>

Je ne sais plus trop quoi faire, et surtout où...

hbachetti

Linux is like a wigwam: no Windows, no Gates, and an Apache inside ...

aladec

#12
May 30, 2018, 09:06 am Last Edit: May 30, 2018, 09:10 am by aladec
Bonjour,
Une suggestion serait d'utiliser un Arduino Yun qui offre des possibilités de gestion assez simple via des pages web (html ou php).
J'ai réalisé un projet "thermostat commandé via Internet" (http://forum.arduino.cc/index.php?topic=452658.0).
L'Arduino Yun existe maintenant en Rev2 à un prix qui devient assez intéressant.
La programmation via les commandes REST est assez simple à mettre en oeuvre.
Arduino Yun et Arduino Uno R3

sancho119

Hello,

J'ai essayé de modifier le fichier config de apache, mais il ne démarre plus...
Pour le Yun, j'ai déjà un mega avec un shield, j'aimerais tout de même pouvoir l'utiliser.

Est-ce que c'est le serveur qui doit autoriser le cross domain, ou juste le site ?
Sur une page html avec un fichier javascript, comment autorise-t-on le cross domain?

hbachetti

Quote
J'ai essayé de modifier le fichier config de apache, mais il ne démarre plus...
Il y a certainement une erreur de syntaxe.
Lance-le en ligne de commande, il affichera peut-être un message.
Linux is like a wigwam: no Windows, no Gates, and an Apache inside ...

Go Up