Salut,
Bon ben voici des éléments de réponse, qui serviront j'espère à tous ceux qui veulent faire des clients mail, http, etc avec Arduino. Ma réponse va essayer d'être un peu plus générale que le problème particulier exposé ici.
Le problème fondamental que je vois dans beaucoup de ces tentatives est que les programmeurs (nous bricoleurs de l'arduino
) oublient qu'il s'agit d'un DIALOGUE, c'est-à-dire parler au serveur, mais surtout écouter:
- attendre que la réponse arrive (et pas de façon fixe pendant 3 secondes par exemple),
- la lire en entier,
- l'analyser.
Par exemple, si on demande l'heure à quelqu'un dans la rue, ça implique aussi d'attendre qu'il réponde et l'écouter - c'est-à-dire pas partir en courant au milieu de la réponse.
Pour en revenir à ton problème, c'est surtout la fonction GetReponse () qu'il faut revoir:
void GetResponse() {
if (client.available()) {
char c = client.read();
while (client.available()) { // Store command char by char.
ServerResponse +=c;
c = client.read();
}
Serial.println("<<<" + ServerResponse);
ServerResponse="";
}
}
Dans ta version, tu suppose qu'au bout de 3 secondes (le delay(wait) avant l'appel à GetResponse () ), le serveur a envoyé une réponse, et qu'il a envoyé toute la réponse, et uniquement la réponse.
C'est ici qu'il faut savoir ce qu'est une réponse du serveur, et là il faut lire la doc, c'est-à-dire les RFC, pour le protocole POP il s'agit par exemple de la RFC 1725.
Celle-ci nous dit, en résumé, que pour certaines commandes envoyées au serveur, la réponse se compose toujours d'une ligne (donc une suite de caractères terminée par les caractères CR (13) et LF (10)), et que pour d'autres commandes la réponse se compose de plusieurs lignes, et la dernière ne comporte que le caractère '.' suivi des caractères CR (13) et LF (10).
Donc on va créer deux fonctions pour traiter les deux cas, et selon la commande envoyée au serveur il faudra appeler l'une ou l'autre de ces fonctions.
Version pour lire une réponse d'une ligne:
void GetResponse1Line ()
{
char c, prevC;
bool finLigne;
// initialisation des variables
prevC = c = 0;
Serial.print ("<<< ");
do
{
// on met de côté le dernier caratère reçu (0 lors du 1er passage dans la boucle)
prevC = c;
// tant qu'on n'a rien reçu, on attend
while (!client.available());
// lecture et affichage du caractère
c = client.read();
Serial.print (c);
// si ce caratère est LF (10) et que le précédent était CR (13), alors on a rencontré la fin de la ligne
finLigne = c == 10 && prevC == 13;
}
while (!finLigne); // si fin de ligne, on sort - sinon on continue
}
Version pour lire une réponse de plusieurs lignes:
short GetResponseMultiLine ()
{
char c, prevC, prevPrevC;
bool finLigne;
bool finReponse;
short longueurLigne;
short nombreLignes;
// initialisation des variables
prevPrevC = prevC = c = 0;
finReponse = false;
nombreLignes = 0;
Serial.print ("<<< ");
do
{
longueurLigne = 0;
do
{
// on met de côté le dernier et l'avant-dernier caratères reçus (0 lors du 1er passage dans la boucle)
prevPrevC = prevC;
prevC = c;
// tant qu'on n'a rien reçu, on attend
while (!client.available());
// lecture et affichage du caractère
c = client.read();
Serial.print (c);
// si ce caratère est LF (10) et que le précédent était CR (13), alors on a rencontré la fin de la ligne
finLigne = c == 10 && prevC == 13;
// on incrémente le compteur de longueur de ligne
longueurLigne++;
}
while (!finLigne); // si fin de ligne, on sort - sinon on continue
// on incrémente le compteur de nombre de ligne
nombreLignes++;
// si la ligne comportait 3 caractères et que le 1er était '.' (et donc par conséquent les deux autres sont forcément 'CR' et 'LF'), c'est la dernière ligne de la réponse
finReponse = longueurLigne == 3 && prevPrevC == '.';
}
while (!finReponse); // si fin de réponse, on sort - sinon on continue
return nombreLignes; // on renvoie le nombre de lignes reçues
}
Bien sûr il faut un peu modifier SendMsg () pour supprimer le delay () et ne pas appeler GetResponse () (GetResponse1Line () et GetResponseMultiLine () seront appelés en amont, juste après l'appel à SendMsg () ):
void SendMsg (char *m)
{
// en envoie la commande
client.println (m);
// affichage de ce qu'on a envoyé
Serial.print (">>> ");
Serial.println (m);
}
Envoyer une commande serait donc un appel à SendMsg () suvi d'un appel à GetResponse1Line () ou GetResponseMultiLine (), selon ce qu'on envoie au serveur - la fonction setup () pourrait devenir ceci:
void setup()
{
short nombre;
// j'ai une arduino méga, j'initialse le spi un peu différemment
pinMode (4, OUTPUT);
digitalWrite (4, HIGH);
pinMode (53, OUTPUT);
Serial.begin(9600);
Serial.println("Program started");
Serial.println("Starting network module...");
Ethernet.begin(mac, ip);
delay(2000);
Serial.println("connecting...");
if (client.connect(server, 110))
{
Serial.println("connected");
GetResponse1Line ();
SendMsg ("user ****");
GetResponse1Line ();
SendMsg ("pass ****");
GetResponse1Line ();
SendMsg ("list");
nombre = GetResponseMultiLine () - 2;
Serial.print (nombre, DEC);
Serial.println (" mails reçus");
SendMsg ("quit");
GetResponse1Line ();
client.stop ();
}
else
{
Serial.println("connection failed");
}
}
Maintenant il serait bien de mettre ton interrogation du serveur dans une fonction à part:
short NombreDeMessages (void)
{
short nombre;
Serial.println("connecting...");
if (client.connect(server, 110))
{
Serial.println("connected");
GetResponse1Line ();
SendMsg ("user ****");
GetResponse1Line ();
SendMsg ("pass ****");
GetResponse1Line ();
SendMsg ("list");
nombre = GetResponseMultiLine () - 2;
SendMsg ("quit");
GetResponse1Line ();
client.stop ();
}
else
{
Serial.println("connection failed");
nombre = -1;
}
return nombre;
}
et de ne mettre dans setup () que ce qui concerne la mise en route:
void setup()
{
short nombre;
// j'ai une arduino méga, j'initialse le spi un peu différemment
pinMode (4, OUTPUT);
digitalWrite (4, HIGH);
pinMode (53, OUTPUT);
Serial.begin(9600);
Serial.println("Program started");
Serial.println("Starting network module...");
Ethernet.begin(mac, ip);
nombreDeMessages = ancienNombreDeMessages = -1;
delay(2000);
}
Dans ton programme, si tu veux connaître le nombre de messages dans ta boîte au lettres, il suffira d'appeler la fonction NombreDeMessages () comme ceci par exemple:
short nombreDeMessages;
short ancienNombreDeMessages;
void loop (void)
{
nombreDeMessages = NombreDeMessages ();
if (nombreDeMessages == -1)
Serial.println ("Erreur !");
else if (nombreDeMessages != ancienNombreDeMessages)
Serial.println ("You've got mail !");
else
Serial.println ("No new mail...");
ancienNombreDeMessages = nombreDeMessages;
delay (60000); // 1 minute d'attente
}
Cet code n'est pas parfait - en effet on a ignoré le contenu réel des réponses du serveur - qui peuvent commencer par "+OK" si tout va bien, ou par "-ERR" si tout va mal (par exemple mauvais mot de passe, commande inconnue, etc).
Par ailleurs, le cas de coupure réseau lorsqu'on dialogue avec le serveur n'est pas géré - il faudrait tester avec client.connected () avant chaque appel à client.available (), ainsi que vérifier qu'un client.read () ne renvoie pas -1.
Edit: orthographe/grammaire