Código email PIR

Hola. He estado buscando un código para que, a través de una shield Ethernet, me envíe un correo cuando un sensor PIR detecte un movimiento.

He encontrado varios, pero no consigo que ninguno funcione. Ya tengo un servidor, con un usuario y contraseña, por lo que necesitaría un código para subir a la placa Arduino UNO de esos que te pide desde dónde se envía (FROM), a dónde se envía (TO) y, si fuera posible, con la autentificación del servidor, es decir, que pueda poner el usuario y la contraseña.

Espero haberme explicado bien y gracias por vuestra ayuda.

Y porque no pones uno de los tantos que encontraste y trabajamos desde ahi?

Este es el código que estoy utilizando para intentar enviar y recibir un mail, pero no lo consigo.

#include <SPI.h>;
#include <Ethernet.h>;
 
// Arduino network information
byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
EthernetClient client;
char smtpServer[] = "mail.smtp2go.com"; // Utilizo la plataforma smtp2go
void setup()
{
  Serial.begin(9600);  // for debug mode
  setupComm();
}
void loop()
{
  email("hallo");
  delay(1000);
}
//  ethernet shield connection init
void setupComm()
{
  Serial.println("Trying to connect");
  if (!Ethernet.begin(mac)){
    Serial.println("Failed to DHCP");
    // verifyng connection
    while(true);
  }
  delay(10000);
  // IP address is:
  Serial.print("IP address: ");
  for (byte thisByte = 0; thisByte < 4; thisByte++) {
    Serial.print(Ethernet.localIP()[thisByte], DEC);
    Serial.print(".");
  }
  Serial.println();
}
// now I send email
bool email(char* text)
{
  bool success = false;
  Serial.println("Sending email...");
  if (client.connect(smtpServer, 2525)){            //2525 is SMTP Server port
    Serial.println("connected");
    delay(100);
    client.println("EHLO arduino");
    for(int i=0; i<999; ++i){
      if(client.read() > 0)
        break;
    }
    Serial.println("responded");
    client.println("AUTH LOGIN");                     //see "http://base64-encoder-online.waraxe.us/"
    client.println("xxxxxxxxxx");                    //Aqui pongo el usuario codificado en base 64
    client.println("yyyyyyyyyy");                   //Aqui pongo la contraseña codificada en base 64
    // Put your "from" email address here
    client.println("MAIL FROM:<xxxxxxx@hotmail.com>"); //Seems you can write what you want here...
    for(int i=0; i<999; ++i){
      if(client.read() > 0)
        break;
    }
    client.println("RCPT TO:<yyyyyyyy@gmail.com>");
    for(int i=0; i<999; ++i){
      if(client.read() > 0)
        break;
    }
    client.println("DATA");
    for(int i=0; i<999; ++i){
      if(client.read() > 0)
        break;
    }
    //Sender
    client.println("from: xxxxxxx@hotmail.com"); //Sender address
    client.println("to: yyyyyyyy@gmail.com");  //Receiver address
    client.println("SUBJECT: From your arduino");
    client.println("");
    client.println(text);
    client.println(".");
    client.println("QUIT");
    for (int i = 0; i<999; ++i){
      if(i > 998){
        Serial.println("error: No response");
      }
      if(client.read() > 0)
        break;
    }
    success = true;
    client.println();
    Serial.println("end");
  }
  else {
    Serial.println("Failed");
    client.println("QUIT"); //Disconnection
  }
  client.stop();
  return success;
}

Acá tienes un ejemplo de smtp2go, lo adaptaste a tu caso?

DICE muy claramente

Step 1
Below is a simple script which can be used to send email through your SMTP2GO account from an Arduino device.

You need to encode (in Base64) your SMTP2GO username and password using one of several tools that are available.

/*
Email client sketch for IDE v1.8.1 and w5100

*/

//Used Modules: Arduino Mega + W5100 Ethernet shield.

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


// this must be unique
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x59, 0x67 }; 
// change network settings to yours
IPAddress ip( 192, 168, 2, 2 ); 
IPAddress gateway( 192, 168, 2, 1 );
IPAddress subnet( 255, 255, 255, 0 );

char server[] = "mail.smtp2go.com"; 
int port = 2525; // You can also try using Port Number 25, 8025 or 587.

File myFile;

EthernetClient client;

void setup()
{

Serial.begin(115200);
pinMode(4,OUTPUT);
digitalWrite(4,HIGH);
Ethernet.begin(mac);
delay(2000);
Serial.println(Ethernet.localIP());
Serial.println(F("Ready. Press 'e' to send."));

}

void loop()
{
byte inChar;

inChar = Serial.read();

if(inChar == 'e')
{
if(sendEmail()) Serial.println(F("Email sent"));
else Serial.println(F("Email failed"));
}
}

byte sendEmail()
{
byte thisByte = 0;
byte respCode;

if(client.connect(server,port) == 1) {
Serial.println(F("connected"));
} else {
Serial.println(F("connection failed"));
return 0;
}

if(!eRcv()) return 0;

Serial.println(F("Sending hello"));
// replace 1.2.3.4 with your Arduino's ip
client.println("EHLO 1.2.3.4");
if(!eRcv()) return 0;

Serial.println(F("Sending auth login"));
client.println("auth login");
if(!eRcv()) return 0;

Serial.println(F("Sending User"));
// Change to your base64 encoded user
client.println(F("cm7zaXtrYY5kWXI=")); 


if(!eRcv()) return 0;

Serial.println(F("Sending Password"));
// change to your base64 encoded password
client.println(F("QmlcZzJGb5Nx")); 


if(!eRcv()) return 0;

// change to your email address (sender)
Serial.println(F("Sending From"));
client.println("MAIL From: <sender@example.com>");
if(!eRcv()) return 0;

// change to recipient address
Serial.println(F("Sending To"));
client.println("RCPT To: <recipient@example.com>");
if(!eRcv()) return 0;

Serial.println(F("Sending DATA"));
client.println("DATA");
if(!eRcv()) return 0;

Serial.println(F("Sending email"));

// change to recipient address
client.println("To: Rachel <recipient@example.com>");

// change to your address
client.println("From: Susan <sender@example.com>");

client.println("Subject: Your Subject"); 

client.println("Hi! Simple test message");

client.println(".");

if(!eRcv()) return 0;

Serial.println(F("Sending QUIT"));
client.println("QUIT");
if(!eRcv()) return 0;

client.stop();

Serial.println(F("disconnected"));

return 1;
}

byte eRcv()
{
byte respCode;
byte thisByte;
int loopCount = 0;

while(!client.available()) {
delay(1);
loopCount++;

// if nothing received for 10 seconds, timeout
if(loopCount > 10000) {
client.stop();
Serial.println(F("\r\nTimeout"));
return 0;
}
}

respCode = client.peek();

while(client.available())
{ 
thisByte = client.read(); 
Serial.write(thisByte);
}

if(respCode >= '4')
{
efail();
return 0; 
}

return 1;
}


void efail()
{
byte thisByte = 0;
int loopCount = 0;

client.println(F("QUIT"));

while(!client.available()) {
delay(1);
loopCount++;

// if nothing received for 10 seconds, timeout
if(loopCount > 10000) {
client.stop();
Serial.println(F("\r\nTimeout"));
return;
}
}

while(client.available())
{ 
thisByte = client.read(); 
Serial.write(thisByte);
}

client.stop();

Serial.println(F("disconnected"));
}

Por lo que veo tu ejemplo si lo hace.
Cual es el resultado que recibes?

Hola. Siento haber tardado en responder, pero he estado todo este tiempo haciendo pruebas para poder enviar un email. Por fin lo conseguí; el fallo estaba en la contraseña de autentificación. Al abrir la cuenta en SMTP2GO me asignaron una contraseña por defecto para enviar email vía SMTP. Yo la codificaba en base 64, pero que no había manera de recibir ningún correo. Al final cambié a una contraseña personalizada y por fin pude conseguirlo. Lo comento por si le sirve a alguien en un futuro.

Ahora viene el segundo problema. Yo quería no sólo enviar porque sí un mail a través de Arduino, sino que lo hiciese al suceder un evento; en mi caso, cuando un sensor de PIR detectara movimiento. Lo que me muestra el monitor serial es lo siguiente:

Sistema Listo
Movimiento detectado!!!
conectando espere…
connectado :slight_smile:
end
Sistema Listo
Movimiento detectado!!!
conectando espere…
connectado :slight_smile:
end
Sistema Listo

Lo hace 2 veces seguidas y luego vuelve a entrar en modo “a la espera de movimiento”, pero no llega a lanzar el correo. Adjunto el código a ver si me ayudáis a encontrar el fallo.

#include <SPI.h>
#include <Ethernet.h>
int led = 5;                // Pin 13 encenderá un led al detectar un movimiento
int SensorPir = 6;               // elegir el pin de entrada para el sensor PIR
int valor = 0;                    // variable para leer el estado del pin 6

void(* Resetea) (void) = 0;//Funcíon Reset por soft para el arduino (como si apretaramos el botón reset)

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };//Dirección MAC de nuestro módulo ethernet
char server[] = "mail.smtp2go.com"; //Cambiar por la url del servidor a usar   
IPAddress ip(192, 168, 1, 35);// Tomará esta ip si el servidor DHCP falla
EthernetClient client;

void setup() {
 pinMode(led, OUTPUT);      // declarmos led como salida(pin 5)
  pinMode(SensorPir, INPUT);     // declaramos SensorPir como entrada(pin 6)
   digitalWrite(led, LOW);//Led 5 apagado
  Serial.begin(9600);//Velocidad del puerto serie
  while (!Serial) {
    ; 
  }
Serial.println("Sistema Listo");//Nos indica que ya estamos en condiciones de enviar un correo

}

void loop() {
valor = digitalRead(SensorPir);  // leemos el valor de entrada
  if (valor == HIGH) {            // comprobamos si la entrada es HIGH
    digitalWrite(led, HIGH);  // Enciende el led
    Serial.println("Movimiento detectado!!!");
    delay(2000);
      Envio_mail();//Llama a la función Envio_mail     
  }  
}
//////////////////////////////////
void Envio_mail() {
 delay(300);
////////////DHCP Activado////////////////////////////////   
  if (Ethernet.begin(mac) == 0) {
    Serial.println("No se pudo configurar Ethernet mediante DHCP");
    
    Ethernet.begin(mac, ip);
  }
////////////DHCP desactivado////////////////
//Ethernet.begin(mac, ip);
 
  delay(100);
  Serial.println("conectando espere...");
 
  if (client.connect(server, 2525)) {
    Serial.println("connectado :-)");//Si se establece la conexión
    
   client.println("AUTH LOGIN");                     //see "http://base64-encoder-online.waraxe.us/"
    client.println("xxxxxxxxx");                    //Aqui pongo el usuario codificado en base 64
    client.println("xxxxxxxxxx");                   //Aqui pongo la contraseña codificada en base 64
    // Put your "from" email address here
    client.println("MAIL FROM:<xxxx@hotmail.com>"); //Seems you can write what you want here...
    for(int i=0; i<999; ++i){
      if(client.read() > 0)
        break;
    }
    client.println("RCPT TO:<yyyy@gmail.com>");
    for(int i=0; i<999; ++i){
      if(client.read() > 0)
        break;
    }
    client.println("DATA");
    for(int i=0; i<999; ++i){
      if(client.read() > 0)
        break;
    }
    //Sender
    client.println("from: xxxx@hotmail.com"); //Sender address
    client.println("to: yyyy@gmail.com");  //Receiver address
    client.println("SUBJECT: From your arduino");
        client.println("QUIT");
    for (int i = 0; i<999; ++i){
      if(i > 998){
        Serial.println("error: No response");
      }
      if(client.read() > 0)
        break;
    }
  
    client.println();
    Serial.println("end");
  }
  else {
    Serial.println("Failed");
    client.println("QUIT"); //Disconnection
  }
  client.stop();
  

 Resetea();     
  }

Gracias.

Felicitaciones por la resolución del problema.

Gracias surbyte por tus felicitaciones y por tu labor en el foro.

Desafortunadamente, como indicaba anteriormente, no he podido terminar el proyecto, pues aunque he conseguido enviar un mail cargando un sketch específico para ello, aún no he logrado envíar el correo al detectar el movimiento. Mis conocimientos de programación son pocos, pero intuyo que simplemente puede ser alguna llave mal colocada (o que me falta poner alguna), pues es como si al conectar con la red saltara directamente al final, sin pasar por la parte de "envío de mail".

¿Alguna sugerencia o idea de lo que puede estar fallando? Gracias.

Buenas Rubikub, según el sketch que has puesto y el la salida que indicas, si te entra en la función de enviar_mail, por tanto como es lógico el fallo para que no te envie el correo debe estar en esa función. Comparala con el sketch de envio que te ha funcionado, debe haber alguna diferencia.

De todas formas no creo que ese código sea bueno para tu propósito, pienso que una forma mejor de hacerlo sea mediante una interrupción, me explico…conecta el sensor PIR a un pin con interrupción, por ejemplo el pin 2 que tiene atada la interrupción 0 en el arduino UNO (tendrás que leer un poco acerca de las interrupciónes). Cuando se produzca la interrupción incrementas un contador. Después en el loop vas comparando el valor del contador con su valor anterior que almacenas en otra variable y si este contador se ha incrementado (ha detectado movimiento), llamas a la función enviar_mail…y te olvidas de resetear el arduino, como haces ahora.

Además también deberías plantearte añadirle algún watchdog por si se cuelga.

Saludos,

Hola, Alfredomrh y surbyte. Por fin he conseguido mi propósito. He estado buscando por internet otros códigos que hicieran la función que necesitaba y, aunque no ha sido sencillo, al final he dado con uno en el que haciendo unas pequeñas modificaciones, ha funcionado. Lo adjunto por si alguien lo necesita. Por mi parte, y si el moderador lo considera oportuno, puede cerrar el tema y darlo por solucionado. Gracias a ambos.

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

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // La mac que tenga tu tarjeta
char smtpServer[] = "mail.smtp2go.com"; // Servidor SMTP (yo utilizo la plataforma smtp2go)
EthernetClient client;

/////////////////////////////
//VARS
//the time we give the sensor to calibrate (10-60 secs according to the datasheet)
int calibrationTime = 30;       

//the time when the sensor outputs a low impulse
long unsigned int lowIn;        

//the amount of milliseconds the sensor has to be low
//before we assume all motion has stopped
long unsigned int pause = 5000; 

boolean lockLow = true;
boolean takeLowTime; 

int pirPin = 3;    //the digital pin connected to the PIR sensor's output
int ledPin = 13;
int nPIR_detect;

void setup()
{
 Ethernet.begin(mac);
 Serial.begin(9600);

  pinMode(pirPin, INPUT);
  digitalWrite(pirPin, LOW);
 
  //give the sensor some time to calibrate
  Serial.print("Calibrando el sensor ");
    for(int i = 0; i < calibrationTime; i++){
      Serial.print(".");
      delay(1000);
      }
    Serial.println(" hecho");
    Serial.println("SENSOR ACTIVO");
    delay(50);
     nPIR_detect = 0;
}

void loop()
{
  delay(1000);
  if(PIR_detected())  // PIR : HIGH
  {
     if (client.available()) {
       char c = client.read();
       Serial.print(c);
     }

    Serial.println("Conectando...");

     if (client.connect(smtpServer, 2525)) {
       Serial.println("Conectado");
       client.println("EHLO arduino");
     client.println("AUTH LOGIN");                                //ver "http://base64-encoder-online.waraxe.us/"
    client.println("xxxxxxxxxxxxxxxxxxx");           //Aquí pongo el usuario codificado en base 64
    client.println("xxxxxxxxxxxxxxxxxxx");                   //Aquí pongo la contraseña codificada en base 64
       client.println("MAIL FROM:<xxxx@hotmail.com>");    //La cuenta con la que se envía el correo
       client.println("RCPT TO:<yyyy@gmail.com>");       // La cuenta a la que se envía el correo
       client.println("DATA");
       client.println("From: <xxxx@hotmail.com>");        //La cuenta con la que se envía el correo
       client.println("TO: <yyyy@gmail.com>");            // La cuenta a la que se envía el correo
       client.println("SUBJECT: Movimiento detectado!!!");  // Título del correo que se enviará
       client.println();
       client.println("Movimiento detectado.");                   // Texto que aparecerá en el correo
       client.println("Alarma!!!.");                         // Texto que aparecerá en el correo
       client.println(".");
       client.println(".");
      
       delay(1000);
       client.stop();
       Serial.println("Correo enviado");    
       delay(30000); 
     }
     else
     {
       Serial.println("Fallo de conexión");
     }
  }
}

boolean PIR_detected()
{
  boolean bPIR;
 
     if(digitalRead(pirPin) == HIGH){
       digitalWrite(ledPin, HIGH);   //the led visualizes the sensors output pin state
       if(lockLow){ 
         //makes sure we wait for a transition to LOW before any further output is made:
         lockLow = false;           
         Serial.println("---");
         Serial.print("Movimiento detectado a los ");
         Serial.print(millis()/1000);
         Serial.println(" seg");
         delay(50);
         }        
         takeLowTime = true;
        
         bPIR = true;
       }
    
     if(digitalRead(pirPin) == LOW){      
       digitalWrite(ledPin, LOW);  //the led visualizes the sensors output pin state
      
       if(takeLowTime){
        lowIn = millis();          //save the time of the transition from high to LOW
        takeLowTime = false;       //make sure this is only done at the start of a LOW phase
        }
       //if the sensor is low for more than the given pause,
       //we assume that no more motion is going to happen
       if(!lockLow && millis() - lowIn > pause){ 
           //makes sure this block of code is only executed again after
           //a new motion sequence has been detected
           lockLow = true;                       
           Serial.print("Movimiento finalizado a los ");      //output
           Serial.print((millis() - pause)/1000);
           Serial.println(" seg");
           delay(50);
           }
           bPIR = false;
       }
    return bPIR;
}

En lo personal me alegro que lo resolvieran. También me alegra que sepas buscar lo que necesitas pero y acá mi comentario me da pena que no insistieras con el código que ya tenías para que hiciera lo que necesitabas.

Te dejo la inquietud x si estas interesado.

Tomo nota.

de todas formas no creo que ese código sea bueno para tu propósito, pienso que una forma mejor de hacerlo sea mediante una interrupción,

No había leido esto!!
Claro que todos tenemos criterios diferentes para las cosas pero nada mas desatinado que usar una interrupción para algo tan trivial y lento como un PIR detectando algo.

El micro tiene todo el tiempo del mundo para detectar un cambio de estado que es todo lo que necesita para enviar un email.
Si lo envia o no depende de hacer bien las cosas en ese aspecto.

En este caso

void loop() {
valor = digitalRead(SensorPir);  // leemos el valor de entrada
  if (valor == HIGH) {            // comprobamos si la entrada es HIGH
    digitalWrite(led, HIGH);  // Enciende el led
    Serial.println("Movimiento detectado!!!");
    delay(2000);
      Envio_mail();//Llama a la función Envio_mail     
  }  
}

El problema esta en delay(2000); y en la forma en que se detecta el movimiento.
Detectar valor == HIGH eso ocurre por mucho tiempo tanto como este ajustado el PIR para hacerlo y puede ser 10 o mas segundos.
Cuantas veces el loop se cumple en ese tiempo?

En el segundo caso veo

void loop()
{
  delay(1000);
  if(PIR_detected())  // PIR : HIGH
  {

Arranca mal y luego tiene una función PIR_detected() hecha con millis() donde de nuevo hay un delay(50) menor pero delay al fin.

Entonces que sentido tiene el delay(1000)? Ninguno.

Lo que se debe hacer es ver el cambio de estado, que el PIR pase de NO detectado a Detectado, y no usar delay, y el micro tiene toda la vida para observar que eso ocurra y luego si enviar el email y tomar la precaución que no vuelva a hacerlo por X tiempo para no llenar el celular de SMS.

De nuevo.. es mi visión del tema.

surbyte:
No había leido esto!!
Claro que todos tenemos criterios diferentes para las cosas pero nada mas desatinado que usar una interrupción para algo tan trivial y lento como un PIR detectando algo.

Entiendo que por la lentitud del sensor no interese manejarlo con una interrupción, la verdad es que no lo he probado. Mi idea era usar una interrupción para incrementar un contador y después enviar un mail cuando el contador alcance un valor.

Tomo nota...

Puedes usar la interrupción si claro que puedes.

El otro día en un comentario que me hace IgnoranteAbsoluto acerca de usar sprintf y un buffer que consume RAM le dije... algo asi como "y cual es el problema en usar RAM si esta ahi para usarla y si la necesitas entonces si estoy de acuerdo con preservar RAM"

Luego de mi comentario respecto de la interrupción me parece que se invierten las razones..
Puedes usar la interrupción? Si claro que puedes. Hay alguna restricción. No.
Hay un método mejor? No. Ambos son igualmente eficaces, el que yo propuse y usar Interrupciones.
Asi que todo queda a criterio de cada uno.

Si me preguntas, tu usarías interrupciones para un PIR? Mi respuesta es NO. Uso interrupciones cuando de acuerdo a mi criterio se require su uso. Una señal lenta o incluso una rápida sin ningun desespero por detectarla no tiene razón para mi en usar interrupciones.

Eso es todo.. quería escribirlo para que quede claro que son caminos a seguir.

Perfecto, agradezco tu explicación y entiendo que son diferencias de criterio, pero por lo que explicas tienes razón, tu enfoque es más adecuado en este caso.

Saludos,