[Réglé] Réception de mail avec arduino

Salut,
Bon je crois pas qu'il ai de topic a ce sujet après avoir regardé mais dites moi si jamais (parce que je me doute qu'il y en a déjà eu ... :roll_eyes: )
Donc voila mon but ca serait de faire en sorte exécuter une fonction lors de l'arrivé d'un message dans la boite de réception
Je suis parti du cours du site du zero et j'ai combiné avec un programme d'envoi de mail ( et je trouve que je me suis bien débrouille en temps que novice :smiley: )

Voila le code :

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

// Local network configuration:
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x23, 0x5D };
byte ip[] = { 192,168,1, *** };

byte server[] = { 193,251,214,115 }; // pop.laposte.net
int time = 1000;
int wait = 3000;
String ServerResponse="";
EthernetClient client;

void setup(){
  Serial.begin(9600);
  Serial.println("Program started, waiting for router...");
  delay(time);
  Serial.println("Starting network module...");
  Ethernet.begin(mac, ip);
  delay(2000);
  Serial.println("connecting...");
  if (client.connect(server, 110)) {
     Serial.println("connected");
     GetResponse();
     SendMsg("user ***"); /* Username*/
     SendMsg("pass ***"); /* password */
     SendMsg("list"); /* lister les mails */
     // commande a inserer pour savoir si il y a un message dans la boite de reception
     // si oui lancer une fonction puis le suprimer pour pouvoir recommencer l'opperation
     SendMsg("dele 1"); /* supprimer le message */
     SendMsg("QUIT");
     client.println();
  } else {
    Serial.println("connection failed");
  }
}
 
void loop()
 {
  if (client.available()) {
    char c = client.read();
    Serial.print(c);
  }
  if (!client.connected()) {
    Serial.println();
    Serial.println("disconnecting.");
    client.stop();
    for( ; ; )
      ;
  }
 }
 
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="";
  }
}

void SendMsg(String m) {
   client.println(m);
   Serial.println(">>>" + m);
   delay(wait); /* wait for a response */
   GetResponse();
}

Seulement c’était trop beau pour être vrai et je sais pas comment faire pour savoir si il y a un message ou si il n'y en a pas
Le problème ne viens pas de la commande mais de la réponse qui doit être décompose pour savoir combien de ligne elle comporte:

Sans message
<<<+OK scan listing follows
.

Avec message
<<<+OK scan listing follows
1 2323
.

J'implore donc les maîtres de la carte arduino de m'aider ]:slight_smile:
Merci d'avance

bon ben je me sens très seul ... =(
Si vous avez lu mon message est ce que vous pouvez laissez un post pour que je soit sur d'avoir bien fait la manip

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 :slight_smile: ) oublient qu'il s'agit d'un DIALOGUE, c'est-à-dire parler au serveur, mais surtout écouter:

  1. attendre que la réponse arrive (et pas de façon fixe pendant 3 secondes par exemple),
  2. la lire en entier,
  3. 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

Et pour ceux qui ont eu le courage de lire jusqu'au bout, voici le code complet :slight_smile:

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


byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x23, 0x5D };
//byte ip[] = { 192,168,1, *** };
byte ip[] = { 10,0,0,2 };

//byte server[] = { 193,251,214,115 }; // pop.laposte.net
byte server[] = { 217,70,184,11 }; // mail.gandi.net

short nombreDeMessages;
short ancienNombreDeMessages;

EthernetClient client;

void setup()
{
	// 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);
}

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		
}

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;
}

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
}

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
}

void SendMsg (char *m)
{
   client.println (m);
   
   Serial.print (">>> ");
   Serial.println (m);
}

Edit: orthographe/grammaire

re-Edit: petite correction du code suite remarque d'Artouste

Comment dire ...
MERCI BEAUCOUP :*
Ton explication est très complète et pas trop difficile à comprendre. :slight_smile:
J'avais pas trouvé d'explication comme la tienne et je dois dire que ça va certainement beaucoup me servir (parce que apparemment ça s'applique à tous les serveur html, pop, smtp, ... ?) .
Donc encore un grand merci ( je sais je me répète :smiley: )
Bonne continuation

Content que ça te serve :smiley:

Le principe d'envoyer une commande au serveur, puis de lire correctement la réponse et l'analyser, c'est commun à tous ces protocoles; la différence sera dans le formatage de la commande et de la réponse: on a vu qu'en pop, la réponse fait 1 ligne pour certaines commandes, plusieurs lignes pour d'autres avec ´.´ seul sur une ligne pour indiquer que c'est la dernière.

En http par exemple, une requête est composée d'un entête et éventuellement de données. L'entête comporte plusieurs lignes, la dernière devant être vide (c'est-à-dire CR-LF seul); c'est comme ça que le serveur sait que tu as envoyé tout l'entête. Si tu envoies des données à la suite de l'entête, un champ de l'entête devra indiquer la longueur des données que tu envoies.
La réponse du serveur est bâtie sur le même principe: entête avec n lignes suivies d'une ligne vide, puis x octets de données (le code html d'une page web en général), la longueur x étant spécifiée dans l'entête, ou alors jusqu'à ce que le serveur ferme la connexion.

Pour le FTP le formatage est encore différent: commandes sur une ligne, réponse sur une ou plusieurs lignes, qui commencent toutes par ´-´ sauf la dernière qui commence par ´ ´.

Ceci dit, une recherche sur ´rfc http´,´rfc smtp´, etc te donnera les détails du protocole.

cbrandt:
Et pour ceux qui ont eu le courage de lire jusqu'au bout, voici le code complet :slight_smile:

...

Edit: orthographe/grammaire

Bonjour
c'est intéressant et moi j'ai eu le courage :grin:
rapide tentative d'utilisation/verif
mais au début du bout 8) ça coince à la compil
(j'ai peut etre loupé qq chose , tentatives en 022 , 1.0.4 , 1.5.3 )

sketch_sep02b.ino: In function 'void setup()':
sketch_sep02b.ino:20: warning: unused variable 'nombre'
sketch_sep02b.ino: In function 'short int NombreDeMessages()':
sketch_sep02b.ino:64: warning: deprecated conversion from string constant to 'char*'
sketch_sep02b.ino:66: warning: deprecated conversion from string constant to 'char*'
sketch_sep02b.ino:68: warning: deprecated conversion from string constant to 'char*'
sketch_sep02b:69: error: void value not ignored as it ought to be
sketch_sep02b.ino:70: warning: deprecated conversion from string constant to 'char*'
sketch_sep02b.ino: In function 'void GetResponseMultiLine()':
sketch_sep02b:159: error: return-statement with a value, in function returning 'void

mea maxima culpa =(

une sombre histoire de copier-coller

ligne 113, remplacer

void GetResponseMultiLine ()

par

short GetResponseMultiLine ()