Ethernet et PHP

Bon finalement je m'y mets aussi....

J'ai reçu mon Ethernet shield et j'ai fais mes premiers essai.

Pour afficher une page html en local pas de soucis particulier, mais ensuite....
J'ai mis un fichier test.text dans un répertoire "test" à la racine de mon site et là ça a pas été du gâteau.

J'ai utilisé ce code :

J'ai remplacé l'IP de Gougle par celle de mon site.
Je pensais qu'en faisant simplement "GET /test/test.txt HTTP/1.0"

Je pourrais afficher le contenu de mon fichier.txt.... et bien non.
Donc j'ai changer cette URL dans tout les sens et rien à faire...
Ensuite j'ai été jeter un coup d'oeil sur les commandes de protocole HTTP et j'ai trouvé qu'il fallait écrire ça de cette façon "GET http://www.mon-site.com/test/test.txt HTTP/1.0"
Et là Bingo :smiley: le contenu s'affiche, mais précédé de touttes les informations concernant cette requête.

J'ai ensuite mis ça dans mon fichier test dont j' changé lextension en .php :

<?php echo "coucou"; ?>

Je change mon URL comme ceci "GET http://www.mon-site.com/test/test.php HTTP/1.0"

Et "coucou" s'affiche avec un peu moins d'info qu'avec le fichier .txt

Donc ça marche, mais pas à tout les coups, je lance le serial monitor, ça m'affiche "coucou", j'appuie sur le bouton reset du shield et la ça ne marche plus qu'une fois sur... 5ou 7 fois.

Quelqu'un aurait-il une solutions pour que cela fonctionne à chaque requête ou pression sur reset ?

Salut,

Ca ressemble beaucoup à un probleme de saturation de la memoire vie du micro.

Et là Bingo   le contenu s'affiche, mais précédé de touttes les informations concernant cette requête.

Lorsque tu manipules des strings de grande taille, tu as vite fait de saturer la bete.
Par ailleurs, et ça je ne l'ai jamais compris, il faudrait qu'un guru veuille bien nous aiguiller... Et bien un simple :
Serial.println("......");
.... te flingue la RAM que c'en est pas croyable !!

Fais ça pour verifier :

 int availableMemory() {
  int size = 2048; // Use 2048 with ATmega328
  byte *buf;

  while ((buf = (byte *) malloc(--size)) == NULL)
    ;

  free(buf);

  return size;
}

Penche toi sur les PROGMEM pour tes strings (stockage en flash) et nettoie tes Serial.print, voir si ça ne reglerait pas le probleme ?!!

J'ai rajouté un client.flush et un serial.flush, mais c'est toujours pareil, la connection s'établit de façon aléatoire.

Si tu n'as fait que ca, j'ai bien peur que ca ne t'aide pas beaucoup.

Un des problemes du compilateur dans sa version actuelle est de ne pas donner l'info sur la ram restant. Lorsque celle-ci est depassee, l'execution est aleatoire... donc ca colle avec ton symptome.

Verifie avec la fonction ci-dessus que tu disposes bien de ram libre.
Et commente tout tes Serial.print.
Verifie que tu n'as pas declaré de tableau (de string par exemple) de taille trop importante (au dessus de 64 par ex), et est ce que par hasard tu ne tenterais pas de stocker en ram le resultat de la page dans un tableau trop petit ?

Ca ressemble BEAUCOUP à un overflow mais sans le code...

bonne chance

Et hop....

/*
Web client

This sketch connects to a website (http://www.mon-site.com)
using an Arduino Wiznet Ethernet shield.

Circuit:

  • Ethernet shield attached to pins 10, 11, 12, 13

created 18 Dec 2009
by David A. Mellis

*/

#include <Ethernet.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = {
192,168,1,177 };
byte server[] = {
XX,XX,XX,XX }; // mon-site.com

// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 80 is default for HTTP):
Client client(server, 80);

void setup() {
// start the Ethernet connection:
Ethernet.begin(mac, ip);
// start the serial library:
Serial.begin(9600);
// give the Ethernet shield a second to initialize:
delay(1000);
Serial.println("connecting...");

// if you get a connection, report back via serial:
if (client.connect()) {
client.flush();
Serial.flush();
Serial.println("connected");
// Make a HTTP request:
client.println("GET http://www.mon-site.com/test/test.php HTTP/1.0");
client.println();

}
else {
// if you didn't get a connection to the server:
Serial.println("connection failed");
}
}

void loop()
{

// if there are incoming bytes available
// from the server, read them and print them:
if (client.available()!=0) {
int b=client.available();
for (int a=0; a < b;a++){
char c = client.read();
Serial.print(c);
Serial.flush();

}
client.flush();
Serial.flush();
client.stop();
}

// if the server's disconnected, stop the client:
if (!client.connected()) {
Serial.println();
Serial.println("disconnecting.");
client.flush();
Serial.flush();
client.stop();

// do nothing forevermore:
for(;; )
;
}

}

Pas trop différent de celui de la page que j'ai mis en lien dans mon premier post.
j'ai rajouté une boucle for pour la lecture de la chaine entrante (qui fait environ 140 caractères)

En fait ce serait pas mal de segmenter cette chaine si j'ai bien compris ?

A bah effectivement, ca ne peut pas venir de la RAM !

Bon par contre tu as : une initialisation et ta requete dans le setup
Et la lecture dans le loop !

Du coup, je suppose qu'il existe un petit laps de temps uffisant pour louper les données lors du passage de l'un à l'autre...

Bref essaie avec tout ds la loop, dans un enchainement direct :

SERVEUR_distant_choisi.println( REQUETE_GET );
SERVEUR_distant_choisi.println();
            
            
//////////// on lit le resultat :
      while( SERVEUR_distant_choisi.connected() ) {

            if (SERVEUR_distant_choisi.available()) {

                  nveau_caractere_serveur_distant = SERVEUR_distant_choisi.read();
                  .
                  .
                  ///////// et donc la tu en fais ce que tu veux naturellement, sauf le stocker dans un tableau de 2048, mais ca t'as pigé !
                  .
                  .
                  
            }
      
      }

Par ailleurs, ce qui consomme de la ram, c'est de faire :

Serial.println("beaucoup de caracteres ici alors qu on aurait pu faire un print tout petit comme tu le fais");

Essaie voir tu verras l'impact sur la ram !

En gros quand ça fonctionne j'ai ça :

connecting...
connected
HTTP/1.1 200 OK
Date: Thu, 26 Aug 2010 17:15:36 GMT
Server: Apache
Content-Length: 6
Connection: close
Content-Type: text/html

coucou
disconnecting.

Et après :

connecting...
connection failed
disconnecting.

pareille quand ça ne fonctionne pas depuis le début...

Ok, mais est-ce que tu as essayé un des elements ci-dessus ?
Je crois aussi que la boucle for ne sert à rien, available() renvoie du bool il me semble, et pas le nombre de caracteres..

Ma boucle for sert bien à quelque chose :

Ethernet : Client class

available()

Description

Returns the number of bytes available for reading (that is, the amount of data that has been written to the client by the server it is connected to).

Syntax

client.available()

Parameters

none

Returns

The number of bytes available.

Par contre j'ai supprimé celle ci (tout à la fin):

for(;; );

J'ai fait des modifs et il me semble que ça marche à chaque coup, mais je dois faire encore quelques essais.

Salut,

la connexion client-serveur est de type TCP/IP, ce qui nécessite une connexion, et implicitement une déconnexion.

Lorsque tu envoies ta requête au serveur, tu établis dabord une connexion, la requête est ensuite envoyée, puis le serveur répond et... te déconnecte... (d'où le "Connection: close" de l'en-tête de réponse)

Contrairement à HTTP/1.0, HTTP/1.1 ne supporte plus la possibilité de laisser la connexion ouverte (keep alive).

Du coup, je pense qu'il faut que tu refasses une demande de connexion à chaque fois que tu souhaites faire une requête à ton serveur.

Gromain59

Salut Gromain,
j'ai justement changé mon code dans ce sens :

/*
Web client

This sketch connects to a website (http://www.mon-site.com)
using an Arduino Wiznet Ethernet shield.

Circuit:

  • Ethernet shield attached to pins 10, 11, 12, 13

created 18 Dec 2009
by David A. Mellis

*/

#include <Ethernet.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = {
192,168,1,177 };
byte server[] = {
xx;
xx;
xx }; // mon-site.com

// Initialize the Ethernet client library
// with the IP address and port of the server
// that you want to connect to (port 80 is default for HTTP):
Client client(server, 80);

void setup() {
// start the Ethernet connection:
Ethernet.begin(mac, ip);
// start the serial library:
Serial.begin(9600);
// give the Ethernet shield a second to initialize:
delay(1000);
// Serial.println("connecting...");

// if you get a connection, report back via serial:

}

void loop()
{
Serial.println("connecting...");
if (client.connect()) {
client.flush();
Serial.flush();
Serial.println("connected");
// Make a HTTP request:
client.println("GET http://www.mon-site/test/test.php HTTP/1.0");
client.println();

}
else if (!client.connect()){
// if you didn't get a connection to the server:
Serial.println("connection failed");
}

// if there are incoming bytes available
// from the server, read them and print them:
if (client.available()!=0) {
int b=client.available();
for (int a=0; a < b;a++){
char c = client.read();
Serial.print(c);
Serial.flush();

}
client.flush();
Serial.flush();
client.stop();
}

// if the server's disconnected, stop the client:
if (!client.connected()) {
Serial.println();
Serial.println("disconnecting.");
client.flush();
Serial.flush();
client.stop();

// do nothing forevermore:
// for(;; )
// ;
}
delay(1000);
}

J'ai changé ça :

if (!client.connected())

Du coup ça ne m'écrit pas "disconnecting" au début et à la fin de chaque boucle.

Et un pt'it delay (1000) pour ne pas faire plus d'un requête à la seconde...

connecting...
connected
connecting...
connection failed
HTTP/1.1 200 OK
Date: Thu, 26 Aug 2010 19:06:36 GMT
Server: Apache
Content-Length: 6
Connection: close
Content-Type: text/html

coucou
disconnecting.
connecting...
connected
connecting...
connection failed
HTTP/1.1 200 OK
Date: Thu, 26 Aug 2010 19:06:39 GMT
Server: Apache
Content-Length: 6
Connection: close
Content-Type: text/html

coucou
disconnecting.
connecting...
connected
connecting...
connection failed
HTTP/1.1 200 OK
Date: Thu, 26 Aug 2010 19:06:41 GMT
Server: Apache
Content-Length: 6
Connection: close
Content-Type: text/html

coucou
disconnecting.

En fait je viens de m'apercevoir que le cycle ouverture-lecture-fermeture se fait sur deux loop(), le premier passage ouvre la connection et le deuxième passage lit et ferme... :-?

Bon maintenant exercice suivant:

chaque lecture est répertoriée sur le fichier test.php, ce dernier devra renvoyer :

coucou >nombre de lecture<

Faut que je me remette à PHP moi ;D

à mon avis, le fait que le serveur ferme la connexion est suffisant.

Tu penses que je n'ai pas besoin de fermer la connection depuis l'Arduino, le serveur s'en occupant ?

Edit :

Si j'enlève les client.stop(), ça ne marche qu'une fois et après je n'arrive plus à me connecter

Sur que c'est mieux comme ça !

Mais il y a encore une source d'erreur : lorsque tu fais ta requete, la reponse arrive quasi immediatement (et oui fini les modems des annees 90), mais il ne faut pas pousser :

Ton arduino aura le temps de faire une boucle et donc de louper la communication au passage etc... Et rebelotte dans la suivante...

Donc oui ca marche, mais c'est un peu bancal. Il n'y a qu'une méthode (chez moi ca marche parfaitement) : tu entres dans un while en attendant que la reponse arrive, et tu la traites tant que tu veux, et ce jusqu'a ce que le serveur distant te deconnecte de lui meme, car c'est effectivement ce qui se passe.

Donc voici a nouveau la syntaxe globale a partir de la requete :

SERVEUR_distant_choisi.println( REQUETE_GET );
SERVEUR_distant_choisi.println();


//////////// on lit le resultat :
      while( SERVEUR_distant_choisi.connected() ) {

            if (SERVEUR_distant_choisi.available()) {

                  nveau_caractere_serveur_distant = SERVEUR_distant_choisi.read();
                  .
                  .
                  ///////// et donc la tu en fais ce que tu veux naturellement, sauf le stocker dans un tableau de 2048, mais ca t'as pigé !
                  .
                  .

            }

      }

Ensuite si tu essaies des serveurs distants differents, tu verras que sur certains tu vois passer le header http 200..., et sur d'autres non : ca se traite, mais c'est un autre probleme.

Bon courage

Si j'enlève les client.stop(), ça ne marche qu'une fois et après je n'arrive plus à me connecter

tu as eu la bonne réponse lol

tu entres dans un while en attendant que la reponse arrive, et tu la traites tant que tu veux

Note que si tu ne veux/peux pas bloquer le travail de l'arduino en attendant les données, tu peux activer l'interruption matériel du shield ethernet (Int0, pin2)

Merci pour vos réponses.

neoirto

Ensuite si tu essaies des serveurs distants differents, tu verras que sur certains tu vois passer le header http 200..., et sur d'autres non : ca se traite, mais c'est un autre probleme.

Coté serveur distant ?

neoirto

Mais il y a encore une source d'erreur : lorsque tu fais ta requete, la reponse arrive quasi immediatement (et oui fini les modems des annees 90), mais il ne faut pas pousser :

Ton arduino aura le temps de faire une boucle et donc de louper la communication au passage etc... Et rebelotte dans la suivante...

Pour ça j'ai rajouté un delay(500) ici :

client.println("GET http://www.mon-site.com/test/test.php HTTP/1.0");
client.println();
delay(500);

Et effectivement maintenant à chaque boucle ça marche :slight_smile:

Par contre utiliser un while me dérange un peu, n'y at-il pas une autre solution ?

Gromain

Note que si tu ne veux/peux pas bloquer le travail de l'arduino en attendant les données, tu peux activer l'interruption matériel du shield ethernet (Int0, pin2)

Est ce que tu pourrais développer ?

Salut,

l'idée d'utiliser les interruptions est peut-être un peu en avance sur son temps ;D

The solder jumper marked "INT" can be connected to allow the Arduino board to receive interrupt-driven notification of events from the W5100, but this is not supported by the Ethernet library. The jumper connects the INT pin of the W5100 to digital pin 2 of the Arduino.

Le chip Wiznet active une sortie pour signaler l'apparition d'un évênement. Mais comme tu peux le lire, ce n'est pas encore supporté directement par la bibliothèque Ethernet.
D'après le datasheet du W5100 (http://www.wiznet.co.kr/UpLoad_Files/ReferenceFiles/W5100_Datasheet_v1.2.2.pdf), il faut que l'arduino acquit l'interruption en écrivant dans le registre d'interruption afin qu'il W5100 puisse générer une nouvelle interruption. Ce doit être le même mécanisme que pour lui affecter son Mac id, alors si tu te sens le courage d'exploiter toutes les capacités du chip...
:wink:

Gromain

Ok, donc une piste à développer et à garder sous le coude ;).

Pour l'instant j'ai encore à faire pour comprendre (à peu près) comment tout ça fonctionne, après si jamais je m'attaquerais à ce genre de chose.

Edit:

Dans la datasheet w5100:

/INT
INTERRUPT
This pin Indicates that W5100 requires MCU attention
after socket connecting, disconnecting, data receiving or
timeout. The interrupt is cleared by writing IR(Interrupt
Register) or Sn_IR (Socket nth Interrupt Register). All
interrupts are maskable. This pin is active low.