text.skipToContent text.skipToNavigation
Christmas Gift Guide



WIFI Email Notifier

This project was inspired by one of my colleagues who wanted to be notified if his server room got too hot, but you could easily change the trigger to some other form of input, or even to simply report some data back to you at a regular rate.

The first thing you'll need to check is whether this project will work with your WIFI network and email address. Most email servers are fussy about who they will accept email from, but if your email address is provided by your ISP, it will generally accept email from connections coming in through that ISP. For example, I have Virgin Mobile Broadband, and the virginbroadband.com.au email server is happy to receive emails while I'm connected to Virgin Mobile Broadband. If you aren't sure, you can even check this with a computer before buying the components. Check out the How It Works section below.

Shopping List:

XC4430 Leonardo Board
XC4614 WIFI Shield
XC4494 Analog Temperature Sensor Module

Of course, if you want to sense something different, you can use a different sensor module. For example, to have your Arduino email you when it's raining, use a XC4603 Rain Sensor Module.

Connections:

This is another easy to build project- the WIFI Shield plugs into the top of the Leonardo, and the Temperature Sensor Module plugs into the A0 headers on the Shield.

The important connections are listed below:

Leonardo WIFI Shield Temp Sensor Function
5V 5V VCC Power
GND GND GND Ground
D0(RX) TX   Data from Shield to Leonardo
D1(TX) RX   Data from Leonardo to Shield
A0   OUT Analog temperature signal

You should also ensure that the two small slide switches on the shield are set to the 'ON' position.

Sketch uploading:

There aren't any special libraries that need to installed, just open the sketch file, change the WIFI and email settings as detailed in the 'How It Works' section below. Then select the Leonardo board and correct serial port and upload. Open the serial monitor and select 'Both NL & CR' for line ending and 115200 baud rate. If everything is working, you should get something similar to the following:

If you press enter, it will manually trigger an email transmission.

If the email is triggered immediately and the air temperature is not above 35 degrees, check the sensor value (24 in the above picture). You may have wire the temperature sensor wrong, or there is a fault with it. You can also check the temperature sensor response by warming it in your hand. The software is set by default to only send an automatic email every 15 minutes.

How It Works:

The WIFI shield is an impressive piece of gear for its size- all the smarts are hidden under the metal shield in one corner of the board. Still, we have to do a bit of work to get it to send emails. After the initialisation code which makes sure that the shield is connected to a WIFI network, the code does the following when triggered:

1. Connect to the email server (smtp server name) on port 25
2. The server sends back a "220" response in greeting.
3. Send "HELO email server name" to server.
4. The server sends back a "250" response if no error was found.
5. Send "MAIL FROM :<sender email address>" to server.
6. The server sends back a "250" response if no error was found.
7. Send "RCPT TO:<recipient email address>" to server.
8. The server sends back a "250" response if no error was found.
9. Send "DATA" to server.
10. The server sends back a "354" response if it is ready to receive data.
11. The body of the email, including subject line as "Subject: subject" is sent to the server
12. The end of the data is indicated by a <CR><LF>.<CR><LF> sequence.
13. The server sends back a "250" response if no error was found.
14. Send "QUIT" to server.
15. Server sends back "221" code and closes the connection.

Note that items in italics will depend on your ISP/email address, and the subject and email body will depend on what you want to communicate. For my @virginbroadband.com.au email address, the smtp server is smtp.virginbroadband.com.au, while the email server name in the HELO command is just virginbroadband.com.au (just what's to the right of the @ in the email address). Your ISP should have information on this- look for 'outgoing mail server name'.

The Wikipedia page on the SMTP protocol also contains an example of such the process in action. You can emulate this process with a telnet program if you want to check that it works before buying the parts.

On Windows 7 and Windows 10, the telnet program is not enabled by default, but can be enabled by selecting Control Panel>Programs>Programs and Features, click on 'Turn Windows features on or off", then ensure 'Telnet Client' is selected and press OK.

To run telnet, open a Command Prompt and type: telnet servername 25
If port 25 doesn't work, try 587. The following shows a sample session (with email addresses blanked):

The T lines are typed into telnet (and followed by the 'enter' key), while the S lines are the server responses. If you make an error while typing, you may not be able to use backspace to change it, as the keystrokes are sent to the server as soon as they are typed. You can usually try again, or try closing and reopening the connection if that doesn't work.

Here's what it looks like when I receive the email from the above telnet session:

If you see a '571' error code, then your connection is probably being blocked to avoid spam, and you probably won't be able to use that ISP/email address combination with the Arduino project. If you can't otherwise get telnet to send an email, then you probably won't have working settings you can use.

To change the sketch to suit your settings, you need to change the following lines at the start to suit the settings that worked for you in telnet:

 

#define SSIDNAME "********"
#define SSIDPWD "********"
#define HOSTNAME "smtp.virginbroadband.com.au"
#define SERVERNAME "virginbroadband.com.au"
#define HOSTPORT "25"
#define RCPTEMAIL "*****@virginbroadband.com.au"
#define SUBJECTLINE "Subject: Temperature Alert from Arduino"

 

Of course, you'll need to make sure the WIFI name (SSID) and password are set to match your ISP too. The sketch provides some diagnostic information to the serial port monitor, so you can see that everything works, and if not, where it fails. You can also manually trigger an email send by pressing the 'enter' key in the serial monitor.

Improvements:

The first thing you would want to do after personalising to suit your WIFI and email settings is to personalise the message that is sent. Anything that is sent with the WIFIsend() command between the subject line and "\r\n.\r\n" becomes the body of the text. Unlike the serial.print commands, the WIFIsend() command does not automatically translate numbers into strings, so you have to use the ltoa() function to put the string equivalent of the number in the num[] character array.

 

There's no reason you can't report multiple values- to output multiple analog inputs, you could use something like:

 

  ltoa(analogRead(A0),num,10);    // convert number to string
  WIFIsend("\r\Analog 0 is:");      //explanation
  WIFIsend(num);      //value      //value
  ltoa(analogRead(A1),num,10);    // convert number to string
  WIFIsend("\r\Analog 1 is:");      //explanation
  WIFIsend(num);      //value      //value

 

If you need to change the minimum time between automatic emails, then the value RPTDELAY sets this in milliseconds (900000 by default, which is 15 minutes or 900 seconds). The UL after the number means it is an unsigned long number, which can hold a value up to approximately 50 days.

If the line:

 

if((coretemp>35)&&(rpttimeout+RPTDELAY<millis())){   

 

is changed to:

 

if((rpttimeout+RPTDELAY<millis())){  

 

Then the email will be sent unconditionally every 15 minutes (or whatever period you choose). This could be an easy way to do datalogging from a remote location.

The line:

 

for(int i=0;i<3;i++){     //maximum of three attempts

 

in the doemail() function determines the maximum number of times the sketch attempts to send the email. If you have an intermittent WIFI connection, you could increase the 3 value to increase the chances of a message getting through.

And of course if you don't want to know the temperature, you could use a different sensor.

Code:

 

#define WIFI Serial1
#define DEBUG Serial

#define SSIDNAME "********"
#define SSIDPWD "********"
#define HOSTNAME "smtp.virginbroadband.com.au"
#define SERVERNAME "virginbroadband.com.au"
#define HOSTPORT "25"
#define RCPTEMAIL "*****@virginbroadband.com.au"
#define SUBJECTLINE "Subject: Temperature Alert from Arduino"
char num[15]="";
long coretemp;
long rpttimeout;    //time between reporting in milliseconds
#define RPTDELAY 900000UL

void setup() {
  WIFI.begin(115200);   //start serial ports
  DEBUG.begin(115200);
  while((!DEBUG)&&(millis()<10000));    //wait for serial port, or start anyway after 10 seconds
  wifiinit();     //send starting settings- find AP, connect and set to station mode only
  DEBUG.println("XC4614 is ready and waiting...");
  DEBUG.println("Press Enter to manually trigger an email.");
  rpttimeout=millis()-RPTDELAY;     //set timeout so report can occur immediately if necessary
}

void loop() {
  coretemp=analogRead(A0)/10-24;      //approximate temperature conversion- is good for +-2 degrees below 60
  while(DEBUG.available()){
    int d=DEBUG.read();
    if(d==13){        //if enter pressed, trigger email
      doemail();
    }
  }
  if((coretemp>35)&&(rpttimeout+RPTDELAY<millis())){   //if it's been more than delay since last report
    doemail();
    rpttimeout=millis();
  }
}

void doemail(){
  DEBUG.println("OK, working...");
  int c;
  for(int i=0;i<3;i++){     //maximum of three attempts
    c=processcmd();         //if result is not zero, email was sent ok
    DEBUG.print("Attempt ");
    DEBUG.print(i+1);
    DEBUG.print(" result:");
    DEBUG.println(c);
    if(c){
      DEBUG.println("Email sent successfully.");
      return;
      }      //finish if successful      
    delay(2000);        //wait a bit
  }
  DEBUG.println("Email not sent.");
}

int processcmd(){
  DEBUG.print("Connect:");
  int c;
  c=WIFIcmd("AT+CIPSTART=\"TCP\",\""  HOSTNAME  "\"," HOSTPORT  ,"CONNECT\r\n",10000);        //start connection
  DEBUG.println(c);
  if(!c){DEBUG.println("Could not connect");return 0;}                                        //connection timed out
  WIFIsend("HELO " SERVERNAME "\r\n");      //connect to host
  if(!WIFIreceive("250")){WIFIclose;return 0;}    //
  WIFIsend("MAIL FROM:<" RCPTEMAIL ">\r\n");      //sender (same as recipient)
  if(!WIFIreceive("250")){WIFIclose;return 0;}    //
  WIFIsend("RCPT TO:<" RCPTEMAIL ">\r\n");      //recipient (same as recipient)
  if(!WIFIreceive("250")){WIFIclose;return 0;}    //
  WIFIsend("DATA\r\n");                         //content of email starts here
  if(!WIFIreceive("354")){WIFIclose;return 0;}    // 354 means server is ready to receive content
  WIFIsend(SUBJECTLINE "\r\n\r\n");      //subject line
  ltoa(coretemp,num,10);    // convert number to string
  DEBUG.println(num);
  WIFIsend("\r\nCore temperature is ");      //general message
  WIFIsend(num);      //temperature
  WIFIsend(" degrees.\r\n\r\n.\r\n");      //end of email is indicated by \r\n.\r\n
  if(!WIFIreceive("250")){WIFIclose;return 0;}    //    250 here means mail successfully sent
  WIFIsend("QUIT\r\n");      //close connection
  if(!WIFIreceive("221")){WIFIclose;return -1;}    //  221 means connection closed cleanly, so fail here is not a problem as long as email was sent
  WIFIclose();
  return 1;
}

void WIFIclose(){
  DEBUG.print("DISC!:");                                  //smtp to be closed from client side
  DEBUG.println(WIFIcmd("AT+CIPCLOSE","OK\r\n",2000));  
}

int WIFIreceive(char* s){     //wait for received ipd data to match string s
  int k=0;
  long t;
  t=millis();
  while(millis()-t<2000){
    if(WIFI.find(s)){
      t=millis()-2001;
      k=1;
    }
  }
  return k;
}

int WIFIsend(char* s){  //send string s in single connection mode
  long t;
  WIFI.print(F("AT+CIPSEND="));
  WIFI.println(strlen(s));      //data has length
  t=millis();
  while(millis()-t<1000){
    while(WIFI.available()){
      if(WIFI.find(">")){t=millis()-1001;}   //we've got the prompt
    }    
  }
  WIFI.print(s);
  t=millis();
  while(millis()-t<1000){
    while(WIFI.available()){
      if(WIFI.find("SEND OK")){t=millis()-1001;}   //send ok, timeout t
    }    
  }
  delay(100);     //still seems to need a delay, especially with consecutive sends
}

int WIFIcmd(char* c,char* r,long tmout){   //command c (nocrlf needed), returns true if response r received, otherwise times out
  long t;
  WIFI.println(c);
  t=millis();
  while(millis()-t<tmout){
    while(WIFI.available()){
      if(WIFI.find(r)){return 1;}   //response good
    }    
  }
  return 0;       //response not found
}

void wifipurge(){
  while(WIFI.available()){
    WIFI.read();
    }
}

void wifiinit(){
  DEBUG.print(F("Reset:"));
  DEBUG.println(WIFIcmd("AT+RST","ready\r\n",5000));
  DEBUG.print("QAP:");
  DEBUG.println(WIFIcmd("AT+CWQAP","OK\r\n",5000));
  DEBUG.print("AP:");
  DEBUG.println(WIFIcmd("AT+CWJAP=\""  SSIDNAME  "\",\"" SSIDPWD  "\"","WIFI GOT IP\r\n",10000));
  delay(2000);
  DEBUG.print("IP data:");
  long t;
  WIFI.println("AT+CIFSR");
  t=millis();
  while(t+1000>millis()){
    if(WIFI.available()) {
      int inByte = WIFI.read();
      DEBUG.write(inByte);
    }
  }
  DEBUG.print(F("Echo:"));
  DEBUG.println(WIFIcmd("ATE0","OK\r\n",1000));
  DEBUG.print(F("Mode:"));
  DEBUG.println(WIFIcmd("AT+CWMODE=1","OK\r\n",2000));
}

 

Downloads:

WIFI_Email_Notifier.ino