Arduino ethernet shield

Bonjour, je suis nouveau autant sur le forum que dans le monde de l'arduino.

Je cherche à activer et déactiver un relais en réseau en utilisant un arduino UNO, un shield ethernet et un relais arduino. Le montage fonctionne bien, j'ai un souci dans le code.

Après plusieurs recherche sur internet, j'ai trouvé un code correspondant à ce que je voulais. Il devait prendre en charge 8 relais, étant donné que je souhaitais alléger le code, j'ai modifier le code pour qu'il corresponde à ma situation. Seulement, mon code ne fonctionne pas et je n'arrive pas à trouver la solution, ce n'est pas faute de chercher :slight_smile: mais parfois à force de trop cherche, on en fini par manquer l'immanquable. Je me dirige donc vers vous pour avoir votre avis.

Voici le code :

#include <SPI.h>
#include <Ethernet.h>
 
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 1, 200 };         
byte gateway[] = { 192, 168, 1, 200 };          
byte subnet[] = { 255, 255, 0, 0 };             
EthernetServer server(80);                                               
 
int Relais1 = 2;

 
String readString = String();
String Valeur;
 
void setup()
{
 
  Ethernet.begin(mac, ip, gateway, subnet);
  pinMode(Relais1, OUTPUT);
  digitalWrite(Relais1, LOW);

  Serial.begin(9600);
}
 
 
int essai(String n)
{
  //Action Relais 1
  if (n=='A')
  {
    digitalWrite(Relais1, HIGH);
  }
  if (n=='a')
  {
    digitalWrite(Relais1, LOW);
  }

  
}
 
void loop()
{
  EthernetClient client = server.available();
  if (client)
  {
    while (client.connected())
    {
      if (client.available())
      {
        char c = client.read();
        if (readString.length() < 30)
        {
          readString.concat(c);
        }
        if (c == '\n')
        {
          Serial.println(readString);
          Valeur = readString.substring (9,10);
          essai(Valeur);
          
          client.print("<html><head><title>Domotique</title><style language='css'>table{text-align:center; color: white;}h1{color: white;}</style></head>");
          client.println("<body BGCOLOR=CornflowerBlue><h1>Arduino</h1>");
 
          client.println("<form method='get'>");
 
          //checkbox Relais 1
          if (digitalRead(Relais1)==LOW)
          {
            //Si le relais n'est pas activé, on créer une checkbox non cochée
            client.println("<input type=hidden name=R1 value='A'><input type='checkbox' onclick='this.form.submit();'>Relais 1");
          }
          else
          {
            //Si le relais est activé, on créer une checkbox cochée
            client.println("<input type=hidden name=R1 value='a'><input type='checkbox' onclick='this.form.submit();' checked>Relais 1");
          }

 
 
          client.println("</form>");
          client.println("</body></html>");
          readString="";
          client.stop();
        }
      }
    }
  }
}

Merci par avance de vous pencher sur mon soucis.

Qu'est-ce qui ne fonctionne pas? quel est le comportement attendu versus ce que vous observez? Qu'avez vous testé?

Lorsque je me connecter sur la page internet de l'adresse IP, la page s'affiche bien mais je ne peux pas cocher la case pour changer la position du relais.

Je pense que ça vient du code car j'ai une erreur lorsque je vérifie ou téléverse le code.

C:\Users\Delta\Documents\Arduino\sketch_nov26a\sketch_nov26a.ino: In function 'int essai(String)':

C:\Users\Delta\Documents\Arduino\sketch_nov26a\sketch_nov26a.ino:30:10: warning: invalid conversion from 'char' to 'const char*' [-fpermissive]

if (n=='A')

^

In file included from C:\Users\Delta\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.13\cores\arduino/Arduino.h:223:0,

from sketch\sketch_nov26a.ino.cpp:1:

C:\Users\Delta\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.13\cores\arduino/WString.h:143:16: note: initializing argument 1 of 'unsigned char String::operator==(const char*) const'

unsigned char operator == (const char *cstr) const {return equals(cstr);}

^

C:\Users\Delta\Documents\Arduino\sketch_nov26a\sketch_nov26a.ino:34:10: warning: invalid conversion from 'char' to 'const char*' [-fpermissive]

if (n=='a')

^

In file included from C:\Users\Delta\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.13\cores\arduino/Arduino.h:223:0,

from sketch\sketch_nov26a.ino.cpp:1:

C:\Users\Delta\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.6.13\cores\arduino/WString.h:143:16: note: initializing argument 1 of 'unsigned char String::operator==(const char*) const'

unsigned char operator == (const char *cstr) const {return equals(cstr);}

^

Le croquis utilise 12 346 octets (38%) de l'espace de stockage de programmes. Le maximum est de 32 256 octets.
Les variables globales utilisent 935 octets (45%) de mémoire dynamique, ce qui laisse 1 113 octets pour les variables locales. Le maximum est de 2 048 octets.

Vous comparez une String avec un char...

Je suis bien obligé de convertir en char, non ?

Je ne vois pas trop comment modifier le code pour que ça fonctionne, j'ai encore du mal avec les variables.

faut surtout laisser tomber les Strings avec un grand 'S' (la classe). vous n'êtes pas sur un gros ordinateur de bureau avec plein de mémoire, vous êtes sur un tout petit micro processeur avec peu de mémoire et il faut en prendre soin

Mais bon en attendant que vous enleviez ces Strings, ce que vous voulez faire c'est comparer le premier caractère de la String avec la lettre 'A' ou 'a' n'est-ce pas? donc il faut aller chercher ce premier caractère.

la classe String à une fonction qui vous retourne le tableau de caractères de sa mémoire: c_str() qu'on pourrait utiliser pour ensuite prendre le premier, ou une méthode qui va chercher un caractère à une certaine place directement charAt(). ici comme on ne veut qu'un seul caractère on peut utiliser cette fonction et votre code devient (j'ai rajouté un else car ce n'est pas la peine de faire les 2 tests si votre caractère est 'A', il ne sera pas 'a')

int essai(String n)
{
  //Action Relais 1
  if (n.charAt(0) == 'A') {
    digitalWrite(Relais1, HIGH);
  } else if (n.charAt(0) == 'a') {
    digitalWrite(Relais1, LOW);
  }
}

mais sérieusement il faut vous débarrasser du code qui bâtit la String

 char c = client.read();
        if (readString.length() < 30)
        {
          readString.concat(c);
        }

pour simplement bâtir un tableau de caractères. Vous verrez que votre sketch va maigrir considérablement
en plus si vous êtes intéressé que par le premier caractère, ce n'est pas la peine de tout stocker!

Merci, avec ce code ça fonctionne.

En faisant ceci, j'essai de comprendre le système car si je veux contrôler 2 ou plusieurs relais, je veux pouvoir ajouter plusieurs checkbox en fonction du nombre de relais.

Peut-être que je me complique un peu la vie :slight_smile: pour le moment je découvre comment ça fonctionne et je suis ouvert à toute proposition permettant de m'améliorer.

faut continuer à explorer - mais éviter la classe String. ça demande à peine plus d'effort mais vous vous éviterez à terme des pb de gestion mémoire sans doute.

Pour essayer avec un deuxième relais avant d'essayer d'alléger, j'ai fait ceci

#include <SPI.h>
#include <Ethernet.h>
 
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 1, 200 };          
byte gateway[] = { 192, 168, 1, 200 };           
byte subnet[] = { 255, 255, 0, 0 };              
EthernetServer server(80);                                                
 
int Relais1 = 7;
int Relais2 = 8;
 
String readString = String();
String Valeur;
 
void setup()
{
 
  Ethernet.begin(mac, ip, gateway, subnet);
  pinMode(Relais1, OUTPUT);
  digitalWrite(Relais1, LOW);
  pinMode(Relais2, OUTPUT);
  digitalWrite(Relais2, LOW);
  Serial.begin(9600);
}
 
 
int essai(String n)
{
  //Action Relais 1
  if (n.charAt(0) == 'A') {
    digitalWrite(Relais1, HIGH);
  } if (n.charAt(0) == 'a') {
    digitalWrite(Relais1, LOW);
  }

    //Action Relais 2
  if (n.charAt(0) == 'Z') {
    digitalWrite(Relais2, HIGH);
  }if (n.charAt(0) == 'z') {
    digitalWrite(Relais2, LOW);
  }
}
 
void loop()
{
  EthernetClient client = server.available();
  if (client)
  {
    while (client.connected())
    {
      if (client.available())
      {
         char c = client.read();
        if (readString.length() < 30)
        {
          readString.concat(c);
        }
        if (c == '\n')
        {
          Serial.println(readString);
          Valeur = readString.substring (9,10);
          essai(Valeur);
           
          client.print("<html><head><title>Domotique</title><style language='css'>table{text-align:center; color: white;}h1{color: white;}</style></head>");
          client.println("<body BGCOLOR=CornflowerBlue><h1>Arduino</h1>");
 
          client.println("<form method='get'>");
 
          //checkbox Relais 1
          if (digitalRead(Relais1)==LOW)
          {
            //Si le relais n'est pas activé, on créer une checkbox non cochée
            client.println("<input type=hidden name=R1 value='A'><input type='checkbox' onclick='this.form.submit();'>Relais 1");
          }
          else
          {
            //Si le relais est activé, on créer une checkbox cochée
            client.println("<input type=hidden name=R1 value='a'><input type='checkbox' onclick='this.form.submit();' checked>Relais 1");
          }

   //checkbox Relais 2
          if (digitalRead(Relais2)==LOW)
          {
            //Si le relais n'est pas activé, on créer une checkbox non cochée
            client.println("<input type=hidden name=R2 value='Z'><input type='checkbox' onclick='this.form.submit();'>Relais 2");
          }
          else
          {
            //Si le relais est activé, on créer une checkbox cochée
            client.println("<input type=hidden name=R2 value='z'><input type='checkbox' onclick='this.form.submit();' checked>Relais 2");
          }
 
          client.println("</form>");
          client.println("</body></html>");
          readString="";
          client.stop();
        }
      }
    }
  }
}

Seulement, j'ai qu'une seule checkbox qui fonctionne sur la page web, dans l'adresse de la page, la checkbox ne passe pas de 'Z' à 'z'.

regardez le code que vous générez en HTML quand vous avez 2 boutons

<html><head><title>Domotique</title><style language='css'>table{text-align:center; color: white;}h1{color: white;}</style></head>
<body BGCOLOR=CornflowerBlue><h1>Arduino</h1>
<form method='get'>
<input type=hidden name=R1 value='A'><input type='checkbox' onclick='this.form.submit();'>Relais 1
<input type=hidden name=R2 value='Z'><input type='checkbox' onclick='this.form.submit();'>Relais 2
</form>
</body></html>

prenez ce code, mettez le dans un fichier toto.html et ouvrez le dans un navigateur web, vous allez avoir quelque chose qui ressemble à cela:

maintenant clickez sur une des cases à cocher et regardez l'URL qui est générée, elle est du genre:

[i]xxxx[/i]/test.html[color=red]?R1=A&R2=Z[/color]

Notez que après le ? vous avez maintenant les 2 valeurs [color=red]?R1=A&R2=Z[/color] en paramètre qui vont arriver à votre shield.

Que fait votre programme quand il reçoit une requête? il construit la requête dans la String (pas bien :slight_smile: ) readString tant que vous n'avez pas 30 caractères reçus

char c = client.read();
        if (readString.length() < 30)
        {
          readString.concat(c);
        }

et tant que le caractère reçu n'est pas un retour chariot (qui marque la fin de le requête)

if (c == '\n')
        {
          Serial.println(readString);
          Valeur = readString.substring (9,10);
          essai(Valeur);

là vous allez chercher de manière spécifique et stocker dans Valeur Valeur = readString.substring ([color=red]9,10[/color]); une String composée d'un seul caractère qui commence à la position 9 (inclus) et finit à 10 (exclu) --> bref vous ne regardez que le caractère à l'index N° 9 --> ça veut dire que vous ne lisez pas dans votre URL les 2 valeurs R1 et R2 mais uniquement celle de R1.

--> il faut donc être un peu plus rusé et aller extraire les 2 valeurs... maintenant que vous savez lire un caractère (charAt()) à une position donnée, peut être que vous n'allez pas utilisé substring()...

Merci, je vais me pencher là dessus, je vais essayer de trouver comment faire et que ce soit valable pour plusieurs relais.

Je reviens vers vous si je sèche ou pour afficher mon code trouvé.

un moyen simple serait de parcourir la chaîne reçue, de chercher les signes '=' et de regarder la lettre qui suit. comme vous avez des lettres différentes pour chacun des relais, vous vous fichez de R1 ou R2. à chaque fois que vous trouvez '=' la lettre qui suit vous dit dans quel état doit être ce relai là

Je me mélange les pinceaux avec les différents type ^^ et plus je lis pour comprendre, moins je sais où j'en suis.

:slight_smile:

et bien laissez tomber la classe String, comme ça vous n'aurez que des char ou des tableaux de char...

Oui j'en suis convaincu, ce serait plus simple mais quand j'essaie, je ne fais que des fautes de syntaxe donc du coup je reviens à mon point initial pour essayer de recommencer correctement.

Je me suis amusé à regarder un peu - voici une idée à creuser.

J'ai juste un peu changé votre page HTML pour que chacune des check box soit dans son propre formulaire (avec comme vous faisiez un champ caché) donc comme ça quand on valide une check box, on ne soumet qu'un seul Relai qu'il suffit de décoder.

Le code gère autant de relais que vous voulez, vous mettez les pins dans un tableau au début.

j'ai essayé de mettre pas mal de commentaires pour que ce soit compréhensible.

bien entendu j'ai viré les Strings :slight_smile:

#include <SPI.h>
#include <Ethernet.h>

#define MAX_HTTT_REQUEST 30  // définir une taille convenable pour vos URL

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 1, 200);

// Définir le port HTTP utilsé par notre serveur. 80 est le défaut
EthernetServer server(80);

// Définir les pins utilisées par les relais
const byte relaisPins[] = {7, 8}; // attention à ne pas prendre des PINs utilisées par le shield Ethernet
const byte nbRelais = sizeof(relaisPins) / sizeof(relaisPins[0]);


void envoiReponse(char * httpRequest, EthernetClient& client)
{
  // On analyse la requête sous forme de "GET /?Rxx=H HTTP/1.1" ou "GET /?Rxx=L HTTP/1.1"
  // il faut extraire xx et le H ou L
  // on va aussi recevoir d'autres requêtes non GET dans ce cas on retourne simplement la page
  char * startPtr;
  if (startPtr = strchr(httpRequest, '?')) {  // On cherche d'abord le ?
    if (startPtr = strchr(startPtr, 'R')) { // On cherche le 'R'
      // bon il semble que l'on ait bien une requête sur un relai
      byte relaiID = atoi(startPtr + 1); // l'index du Relai suit le R
      if (startPtr = strchr(startPtr, '=')) { // On cherche le '=' et l'ancienne valeur suit 'L' ou 'H'
        byte ancienneValeur = *(startPtr + 1) == 'L' ? HIGH : LOW;
        if (relaiID < nbRelais) digitalWrite(relaisPins[relaiID], ancienneValeur);
      }
    }
  }

  // On bâti la réponse en HTML
  client.println(F("HTTP/1.1 200 OK"));
  client.println(F("Content-Type: text/html"));
  client.println(F("Connection: close"));  // fermer la connexion après cette réponse
  client.println(); // fin du header
  client.println(F("<!DOCTYPE HTML>"));
  client.println(F("<html>"));
  client.println(F("<head><title>Domotique</title><style language='css'>table{text-align:center; color: white;}h1{color: white;}</style></head>"));
  client.println(F("<body BGCOLOR=CornflowerBlue><h1>Arduino</h1>"));

  // Pour chacun des relais on construit un formulaire avec un champs caché et une checkbox
  for (byte n = 0; n < nbRelais; n++) {
    //checkbox Relais n
    byte statusRelai = digitalRead(relaisPins[n]);
    client.println(F("<form method='get'>"));
    client.print(F("<input type='hidden' name='R")); // on génère R0, R1, R2,... comme nom
    client.print(n);
    client.print(F("' value='"));
    if (statusRelai == HIGH) client.print(F("H'>")); else client.print(F("L'>"));
    client.print(F("<input type='checkbox'"));
    if (statusRelai == HIGH) client.print(F(" checked"));      //Si le relais est activé, checkbox cochée
    client.print(F(" onchange='this.form.submit();'> Relais "));
    client.println(n);
    client.println(F("</form>"));
  } // fin de pour chaque relai
  client.println(F("</body></html>")); // fin de notre page web
}


void setup() {
  Serial.begin(115200);

  // initialise l'état des relais
  for (byte n = 0; n < nbRelais; n++) {
    pinMode(relaisPins[n], OUTPUT);
    digitalWrite(relaisPins[n], LOW);
  }

  // démarre la connexion Ethernet et le serveur:
  Ethernet.begin(mac, ip);
  server.begin();
  Serial.print("Server joignable @ ");
  Serial.println(Ethernet.localIP());
}

void loop() {
  // On écoute pour voir si un nouveau client se connecte
  EthernetClient client;
  char httpRequest[MAX_HTTT_REQUEST + 1]; // buffer pour l'URL reçue
  unsigned int httpRequestIndex;
  int recu;
  boolean commandeRecue, ligneVide;

  client = server.available();
  if (client) {
    // On vient de recevoir une requête HTTP
    // Une requête bien formée se termine par une ligne vide

    // la chaîne est vide au début
    httpRequest[0] = '\0';
    httpRequestIndex = 0;
    commandeRecue = false;
    ligneVide = true;

    while (client.connected()) {
      if (client.available()) {
        if ((recu = client.read()) != -1) { // si pas d'erreur de lecture
          if ((char) recu != '\r') { // on ignore les '\r'
            if ((char) recu != '\n') {
              ligneVide = false;
              if (!commandeRecue) { // on mémorise
                httpRequest[httpRequestIndex++] = (char) recu; // on mémorise le caractère
                httpRequest[httpRequestIndex] = '\0'; // on termine la chaîne de caractère proprement
                if (httpRequestIndex >= MAX_HTTT_REQUEST) { // on vérifie si on déborde
                  httpRequestIndex = MAX_HTTT_REQUEST - 1; // on continuera à écrire sur le dernier caractère, tant pis...
                } // fin de test de débordement buffer
              }
            } else {
              if (ligneVide) { // si la ligne précédente était vide
                break; // c'est la fin de la commande HTTP
              } else {
                ligneVide = true;
                if (!(commandeRecue = !strncmp(httpRequest, "GET", 3))) { // si ce n'est pas un GET
                  httpRequest[0] = '\0'; // on vide la chaine
                  httpRequestIndex = 0;
                } // fin de si GET
              } // fin de si dernière ligne de la requête
            } // fin de différent de '\n'
          } // fin de différent de '\r'
        } // fin de si pas d'erreur de lecture
      } // fin de si données disponible
    } // fin de tant que le client est connecté

    // ici on a fini de lire la requête HTTP et on a dans httpRequest le GET (s'il y en a un)
    envoiReponse(httpRequest, client);
    delay(5); // On donne un peu de temps au navigateur pour recevoir les données
    client.stop(); // on termine la connexion

  } // fin de on a un client
}