<< Summary

Control the Raspberry PI through email, in Java Raspberry PI

Content

The Problem

Top
You'll find many articles showing how to use a web server like Apache to control your Raspberry PI over the Internet.
What they don't say - or don't say loud enough - is that to do so, your web server needs to be visible to (and from) the outside world.
Otherwise, you cannot access it outside of your home or ad-hoc network.
What those articles present is a nice way to:
For example
You are at work, the Raspberry PI is at home.
Your Raspberry PI has a temperature sensor connected to it, and it is also connected to some heater switch. To do so, you need to be on the same network as the Raspberry PI, or the network the Raspberry PI is on needs to be visible from your location.
This does not happen if you don't have a registered IP address for your Raspberry PI.
It kind of kills the story...

A Solution

Top
A way to address this problem is to use emails.
This will not implement real-time messaging - like WebSocket would - but this will allow a form of communication, only relying on email service - like Yahoo! or Google. Those providers offer free email accounts, which is all you would need.

Required Components

Top
All you need on the client side (ie you, at work) is a browser, from which you can send and receive emails.
On the server side (ie the Raspberry PI, at home) you will need: The json parser is used to parse the content of the email messages. We choosed this technology in this example; others can be used as well, like XML, plain text messages, etc. Your call.

The Java code

Top
All the sources are available in an archive you can get at the bottom of this document.
email.properties
All properties concerning the emails accounts are externalized in this properties file.
Lines 1 to 5 concern the origins, and the destinations of the emails going back and forth, as well as the subjects used for the emails (events, acknowledgements, and requests).
Notice the URL at the top of the file (line 7), it could help you to find the servers you are interested in.

 1  pi.accept.emails.from=olivier.lediouris@gmail.com, olivier.lediouris@yahoo.com, olivier@lediouris.net
 2  pi.send.emails.to=olivier.lediouris@gmail.com, olivier.lediouris@yahoo.com, olivier@lediouris.net
 3  pi.email.subject=PI Request
 4  pi.ack.subject=PI Robot Ack
 5  pi.event.subject=PI Event
 6  #
 7  # See http://www.arclab.com/products/amlc/list-of-smtp-and-pop3-servers-mailserver-list.html
 8  #
 9  pi.yahoo.mail.protocol=pop3
10  pi.yahoo.incoming.server=pop.mail.yahoo.com
11  pi.yahoo.outgoing.server=smtp.mail.yahoo.com
12  pi.yahoo.mail.smtpauth=true
13  pi.yahoo.outgoing.server.port=587
14  pi.yahoo.mail.username=olivier.lediouris
15  pi.yahoo.mail.password=XXXXXX
16  pi.yahoo.mail.replyto=olivier@lediouris.net
17  #
18  pi.google.mail.protocol=pop3
19  pi.google.incoming.server=pop.gmail.com
20  pi.google.outgoing.server=smtp.gmail.com
21  pi.google.mail.smtpauth=true
22  pi.google.outgoing.server.port=465
23  pi.google.incoming.server.port=995
24  pi.google.mail.username=olivier.lediouris@gmail.com
25  pi.google.mail.password=XXXXXX
26  pi.google.mail.replyto=olivier.lediouris@gmail.com
27  #
28  pi.oracle.mail.protocol=imap
29  pi.oracle.incoming.server=stbeehive.oracle.com
30  pi.oracle.outgoing.server=stbeehive.oracle.com
31  pi.oracle.mail.smtpauth=true
32  pi.oracle.outgoing.server.port=465
33  pi.oracle.incoming.server.port=993
34  pi.oracle.mail.username=olivier.lediouris@oracle.com
35  pi.oracle.mail.password=XXXXXX
36  pi.oracle.mail.replyto=olivier.lediouris@oracle.com
37  #
      
pi4j.gpio.RaspberryPIEventListener.java
This is the interface used for the callback invoked when an event is triggered on the Reaspberry Pi's side. Namely, when the button will be pressed.
      

     1  package pi4j.gpio;
     2  
     3  import com.pi4j.io.gpio.event.GpioPinDigitalStateChangeEvent;
     4  
     5  public interface RaspberryPIEventListener
     6  {
     7    public void manageEvent(GpioPinDigitalStateChangeEvent event);
     8  }
      
      
pi4j.email.EmailReceiver.java
This is the utility class used to receive emails. Its behavior heavily depends on the values set in email.properties.
Do check out the method named setProps() (line 119). It might need to be tweaked to fit your email environment.
      

     1  package pi4j.email;
     2  
     3  import java.io.BufferedReader;
     4  import java.io.File;
     5  import java.io.FileInputStream;
     6  import java.io.FileOutputStream;
     7  import java.io.IOException;
     8  import java.io.InputStream;
     9  import java.io.InputStreamReader;
    10  import java.io.OutputStream;
    11  
    12  import java.util.ArrayList;
    13  import java.util.List;
    14  import java.util.Properties;
    15  import java.util.Set;
    16  
    17  import javax.mail.Address;
    18  import javax.mail.Flags;
    19  import javax.mail.Folder;
    20  import javax.mail.Message;
    21  import javax.mail.Multipart;
    22  import javax.mail.Part;
    23  import javax.mail.PasswordAuthentication;
    24  import javax.mail.Session;
    25  import javax.mail.Store;
    26  import javax.mail.internet.InternetAddress;
    27  import javax.mail.search.AndTerm;
    28  import javax.mail.search.FlagTerm;
    29  import javax.mail.search.FromStringTerm;
    30  import javax.mail.search.OrTerm;
    31  import javax.mail.search.SearchTerm;
    32  import javax.mail.search.SubjectTerm;
    33  
    34  public class EmailReceiver
    35  {
    36    private static String protocol;
    37    private static int outgoingPort;
    38    private static int incomingPort;
    39    private static String username;
    40    private static String password;
    41    private static String outgoing;
    42    private static String incoming;
    43    private static String replyto;
    44    private static boolean smtpauth;
    45    
    46    private static String sendEmailsTo;
    47    private static String acceptEmailsFrom;
    48    private static String acceptSubject;
    49    private static String ackSubject;
    50    
    51    private static boolean verbose = "true".equals(System.getProperty("verbose", "false"));
    52  
    53    private EmailSender emailSender = null; // For Ack
    54    private String provider = null;
    55    
    56    public EmailReceiver(String provider) throws RuntimeException
    57    {
    58      this.provider = provider;
    59      EmailReceiver.protocol = "";
    60      EmailReceiver.outgoingPort = 0;
    61      EmailReceiver.incomingPort = 0;
    62      EmailReceiver.username = "";
    63      EmailReceiver.password = "";
    64      EmailReceiver.outgoing = "";
    65      EmailReceiver.incoming = "";
    66      EmailReceiver.replyto = "";
    67      EmailReceiver.smtpauth = false;
    68      
    69      EmailReceiver.sendEmailsTo = "";
    70      EmailReceiver.acceptEmailsFrom = "";
    71      EmailReceiver.acceptSubject = "";
    72      EmailReceiver.ackSubject = "";
    73  
    74      Properties props = new Properties();
    75      String propFile = "email.properties";
    76      try
    77      {
    78        FileInputStream fis = new FileInputStream(propFile);
    79        props.load(fis);
    80      }
    81      catch (Exception e)
    82      {
    83        System.out.println("email.properies file problem...");
    84        throw new RuntimeException("File not found:email.properies");
    85      }
    86      EmailReceiver.sendEmailsTo     = props.getProperty("pi.send.emails.to");
    87      EmailReceiver.acceptEmailsFrom = props.getProperty("pi.accept.emails.from");
    88      EmailReceiver.acceptSubject    = props.getProperty("pi.email.subject");
    89      EmailReceiver.ackSubject       = props.getProperty("pi.ack.subject");
    90      
    91      EmailReceiver.protocol     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.protocol");
    92      EmailReceiver.outgoingPort = Integer.parseInt(props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "outgoing.server.port", "0"));
    93      EmailReceiver.incomingPort = Integer.parseInt(props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "incoming.server.port", "0"));
    94      EmailReceiver.username     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.username",   "");
    95      EmailReceiver.password     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.password",   "");
    96      EmailReceiver.outgoing     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "outgoing.server", "");
    97      EmailReceiver.incoming     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "incoming.server", "");
    98      EmailReceiver.replyto      = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.replyto",    "");
    99      EmailReceiver.smtpauth     = "true".equals(props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.smtpauth", "false"));
   100      
   101      if (verbose)
   102      {
   103        System.out.println("Protocol:" + EmailReceiver.protocol);
   104        System.out.println("Usr/pswd:" + EmailReceiver.username + "/" + EmailReceiver.password);
   105      }
   106    }
   107  
   108    private static SearchTerm[] buildSearchTerm(String str)
   109    {
   110      String[] sa = str.split(",");
   111      List<SearchTerm> lst = new ArrayList<SearchTerm>();
   112      for (String s : sa)
   113        lst.add(new FromStringTerm(s.trim()));
   114      SearchTerm[] sta = new SearchTerm[lst.size()];
   115      sta = lst.toArray(sta);
   116      return sta;
   117    }
   118    
   119    private Properties setProps()
   120    {
   121      Properties props = new Properties();
   122      props.put("mail.debug", verbose?"true":"false");
   123      
   124      // TASK smtp should be irrelevant for a receiver
   125      props.put("mail.smtp.host", EmailReceiver.outgoing);
   126      props.put("mail.smtp.port", Integer.toString(EmailReceiver.outgoingPort));
   127  
   128      props.put("mail.smtp.auth", "true");
   129      props.put("mail.smtp.starttls.enable", "true"); //  See http://www.oracle.com/technetwork/java/faq-135477.html#yahoomail
   130    //  props.put("mail.smtp.starttls.required", "true");
   131      props.put("mail.smtp.ssl.enable", "true");
   132  
   133      if ("pop3".equals(EmailReceiver.protocol))
   134      {
   135        props.setProperty("mail.pop3.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
   136        props.setProperty("mail.pop3.socketFactory.fallback", "false");
   137        props.setProperty("mail.pop3.port", Integer.toString(EmailReceiver.incomingPort));
   138        props.setProperty("mail.pop3.socketFactory.port", Integer.toString(EmailReceiver.incomingPort));
   139      }            
   140  
   141      if ("imap".equals(protocol))
   142      {
   143        props.setProperty("mail.imap.starttls.enable", "false");
   144        // Use SSL
   145        props.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
   146        props.setProperty("mail.imap.socketFactory.fallback", "false");
   147    
   148        props.setProperty("mail.imap.port", Integer.toString(EmailReceiver.incomingPort));
   149        props.setProperty("mail.imap.socketFactory.port", Integer.toString(EmailReceiver.incomingPort));
   150    
   151        props.setProperty("mail.imaps.class", "com.sun.mail.imap.IMAPSSLStore");
   152      }
   153      return props;
   154    }
   155  
   156    public boolean isAuthRequired()
   157    {
   158      return EmailReceiver.smtpauth;
   159    }
   160  
   161    public String getUserName()
   162    {
   163      return EmailReceiver.username;
   164    }
   165  
   166    public String getPassword()
   167    {
   168      return EmailReceiver.password;
   169    }
   170  
   171    public String getReplyTo()
   172    {
   173      return EmailReceiver.replyto;
   174    }
   175  
   176    public String getIncomingServer()
   177    {
   178      return EmailReceiver.incoming;
   179    }
   180  
   181    public String getOutgoingServer()
   182    {
   183      return EmailReceiver.outgoing;
   184    }
   185  
   186    public List<String> receive()
   187      throws Exception
   188    {
   189      return receive(null);
   190    }
   191    
   192    public List<String> receive(String dir)
   193      throws Exception
   194    {
   195      if (verbose) System.out.println("Receiving...");
   196      List<String> messList = new ArrayList<String>();
   197      Store store = null;
   198      Folder folder = null;
   199      try
   200      {
   201    //  Properties props = System.getProperties();
   202        Properties props = setProps();
   203        
   204        if (verbose)
   205        {
   206          Set<Object> keys = props.keySet();
   207          for (Object o : keys)
   208            System.out.println(o.toString() + ":" + props.get(o).toString());
   209        }
   210        if (verbose) System.out.println("Getting session...");
   211  //    Session session = Session.getInstance(props, null);
   212        Session session = Session.getInstance(props,
   213                          new javax.mail.Authenticator() 
   214                          {
   215                            protected PasswordAuthentication getPasswordAuthentication() 
   216                            {
   217                              return new PasswordAuthentication(username, password);
   218                            }
   219                          });    
   220        session.setDebug(verbose);
   221        if (verbose) System.out.println("Session established.");
   222        store = session.getStore(EmailReceiver.protocol);
   223        if (EmailReceiver.incomingPort == 0)
   224          store.connect(EmailReceiver.incoming, EmailReceiver.username, EmailReceiver.password);
   225        else
   226          store.connect(EmailReceiver.incoming, EmailReceiver.incomingPort, EmailReceiver.username, EmailReceiver.password);
   227        if (verbose) System.out.println("Connected to store");        
   228        folder = store.getDefaultFolder();
   229        if (folder == null)
   230          throw new RuntimeException("No default folder");
   231  
   232        folder = store.getFolder("INBOX");
   233        if (folder == null)
   234          throw new RuntimeException("No INBOX");
   235  
   236        folder.open(Folder.READ_WRITE);
   237        if (verbose) System.out.println("Connected... filtering, please wait.");
   238        SearchTerm st = new AndTerm(new SearchTerm[] { new OrTerm(buildSearchTerm(sendEmailsTo)), 
   239                                                       new SubjectTerm(acceptSubject),
   240                                                       new FlagTerm(new Flags(Flags.Flag.SEEN), false) });
   241     // st = new SubjectTerm("PI Request");
   242        Message msgs[] = folder.search(st);
   243  //    Message msgs[] = folder.getMessages();
   244  
   245        if (verbose) System.out.println("Search completed, " + msgs.length + " message(s).");
   246        for (int msgNum=0; msgNum<msgs.length; msgNum++)
   247        {
   248          try
   249          {
   250            Message mess = msgs[msgNum];
   251            Address from[] = mess.getFrom();
   252            String sender = "";
   253            try
   254            {
   255              sender = from[0].toString();
   256            }
   257            catch(Exception exception) 
   258            {
   259              exception.printStackTrace();
   260            }
   261  //        System.out.println("Message from [" + sender + "], subject [" + subject + "], content [" + mess.getContent().toString().trim() + "]");
   262            
   263            if (true)
   264            {
   265              if (!mess.isSet(javax.mail.Flags.Flag.SEEN) && 
   266                  !mess.isSet(javax.mail.Flags.Flag.DELETED))
   267              {
   268                String txtMess = printMessage(mess, dir);
   269                messList.add(txtMess);
   270                mess.setFlag(javax.mail.Flags.Flag.SEEN, true);
   271                mess.setFlag(javax.mail.Flags.Flag.DELETED, true);
   272                // Send an ack - by email.
   273                if (this.emailSender == null)
   274                  this.emailSender = new EmailSender(this.provider);
   275                this.emailSender.send(new String[] { sender }, 
   276                                      ackSubject, 
   277                                      "Your request [" + txtMess.trim() + "] is being taken care of.");
   278                if (verbose) System.out.println("Sent an ack to " + sender);
   279              } 
   280              else
   281              {
   282                if (verbose) System.out.println("Old message in your inbox..., received " + mess.getReceivedDate().toString());
   283              }
   284            }
   285          }
   286          catch(Exception ex)
   287          {
   288  //        System.err.println(ex.getMessage());
   289            ex.printStackTrace();
   290          }
   291        }
   292      }
   293      catch(Exception ex)
   294      {
   295        throw ex;
   296      }
   297      finally
   298      {
   299        try
   300        {
   301          if (folder != null)
   302            folder.close(true);
   303          if (store != null)
   304            store.close();
   305        }
   306        catch(Exception ex2)
   307        {
   308          System.err.println("Finally ...");
   309          ex2.printStackTrace();
   310        }
   311      }
   312      return messList;
   313    }
   314  
   315    public static String printMessage(Message message, String dir)
   316    {
   317      String ret = "";
   318      try
   319      {
   320        String from = ((InternetAddress)message.getFrom()[0]).getPersonal();
   321        if(from == null)
   322          from = ((InternetAddress)message.getFrom()[0]).getAddress();
   323        if (verbose) System.out.println("From: " + from);
   324        String subject = message.getSubject();
   325        if (verbose) System.out.println("Subject: " + subject);
   326        Part messagePart = message;
   327        Object content = messagePart.getContent();
   328        if (content instanceof Multipart)
   329        {
   330  //      messagePart = ((Multipart)content).getBodyPart(0);
   331          int nbParts = ((Multipart)content).getCount();
   332          if (verbose) System.out.println("[ Multipart Message ], " + nbParts + " part(s).");
   333          for (int i=0; i<nbParts; i++)
   334          {
   335            messagePart = ((Multipart)content).getBodyPart(i);
   336            if (messagePart.getContentType().toUpperCase().startsWith("APPLICATION/OCTET-STREAM"))
   337            {
   338              if (verbose) System.out.println(messagePart.getContentType() + ":" + messagePart.getFileName());
   339              InputStream is = messagePart.getInputStream();
   340              String newFileName = "";
   341              if (dir != null)
   342                newFileName = dir + File.separator;
   343              newFileName += messagePart.getFileName();
   344              FileOutputStream fos = new FileOutputStream(newFileName);
   345              ret = messagePart.getFileName();
   346              if (verbose) System.out.println("Downloading " + messagePart.getFileName() + "...");
   347              copy(is, fos);
   348              if (verbose) System.out.println("...done.");
   349            } 
   350            else // text/plain, text/html
   351            {
   352              if (verbose) System.out.println("-- Part #" + i + " --, " + messagePart.getContentType().replace('\n', ' ').replace('\r', ' ').replace("\b", "").trim());
   353              InputStream is = messagePart.getInputStream();
   354              BufferedReader br = new BufferedReader(new InputStreamReader(is));
   355              String line = "";
   356              while (line != null)
   357              {
   358                line = br.readLine();
   359                if (line != null)
   360                {
   361                  if (verbose) System.out.println("[" + line + "]");
   362                  if (messagePart.getContentType().toUpperCase().startsWith("TEXT/PLAIN"))
   363                    ret += line;
   364                }
   365              }
   366              br.close();
   367              if (verbose) System.out.println("-------------------");
   368            }
   369          }
   370        }
   371        else
   372        {
   373  //      System.out.println("  .Message is a " + content.getClass().getName());
   374  //      System.out.println("Content:");
   375  //      System.out.println(content.toString());
   376          ret = content.toString();
   377        }
   378        if (verbose) System.out.println("-----------------------------");
   379      }
   380      catch(Exception ex)
   381      {
   382        ex.printStackTrace();
   383      }
   384      return ret;
   385    }
   386  
   387    private static void copy(InputStream in, OutputStream out)
   388      throws IOException
   389    {
   390      synchronized(in)
   391      {
   392        synchronized(out)
   393        {
   394          byte buffer[] = new byte[256];
   395          while (true)
   396          {
   397            int bytesRead = in.read(buffer);
   398            if(bytesRead == -1)
   399              break;
   400            out.write(buffer, 0, bytesRead);
   401          }
   402        }
   403      }
   404    }
   405  }
      
        
pi4j.email.EmailSender.java
Same as the previous, but for outgoing emails. Notice that the two can be different. You might very well send emails using Yahoo! and receive others using Google.
This way, the Raspberry PI and you would have your own email addresses.
As before, check out the method named setProps() (line 164). It might need to be tweaked to fit your email environment.
      

     1  package pi4j.email;
     2  
     3  import com.sun.mail.smtp.SMTPTransport;
     4  
     5  import java.io.FileInputStream;
     6  
     7  import java.util.Properties;
     8  
     9  import javax.activation.DataHandler;
    10  import javax.activation.DataSource;
    11  import javax.activation.FileDataSource;
    12  
    13  import javax.mail.BodyPart;
    14  import javax.mail.Message;
    15  import javax.mail.MessagingException;
    16  import javax.mail.Multipart;
    17  import javax.mail.PasswordAuthentication;
    18  import javax.mail.Session;
    19  import javax.mail.Transport;
    20  import javax.mail.internet.AddressException;
    21  import javax.mail.internet.InternetAddress;
    22  import javax.mail.internet.MimeBodyPart;
    23  import javax.mail.internet.MimeMessage;
    24  import javax.mail.internet.MimeMultipart;
    25  
    26  public class EmailSender
    27  {
    28    private static String protocol;
    29    private static int outgoingPort;
    30    private static int incomingPort;
    31    private static String username;
    32    private static String password;
    33    private static String outgoing;
    34    private static String incoming;
    35    private static String replyto;
    36    private static boolean smtpauth;
    37    
    38    private static String sendEmailsTo;
    39    private static String eventSubject;
    40    
    41    private static boolean verbose = "true".equals(System.getProperty("verbose", "false"));
    42  
    43    public EmailSender(String provider) throws RuntimeException
    44    {
    45      EmailSender.protocol = "";
    46      EmailSender.outgoingPort = 0;
    47      EmailSender.incomingPort = 0;
    48      EmailSender.username = "";
    49      EmailSender.password = "";
    50      EmailSender.outgoing = "";
    51      EmailSender.incoming = "";
    52      EmailSender.replyto = "";
    53      EmailSender.smtpauth = false;
    54      EmailSender.sendEmailsTo = "";
    55      EmailSender.eventSubject = "";
    56  
    57      Properties props = new Properties();
    58      String propFile = "email.properties";
    59      try
    60      {
    61        FileInputStream fis = new FileInputStream(propFile);
    62        props.load(fis);
    63      }
    64      catch (Exception e)
    65      {
    66        System.out.println("email.properies file problem...");
    67        throw new RuntimeException("File not found:email.properies");
    68      }
    69      EmailSender.sendEmailsTo = props.getProperty("pi.send.emails.to");
    70      EmailSender.eventSubject = props.getProperty("pi.event.subject");
    71      
    72      EmailSender.protocol     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.protocol");
    73      EmailSender.outgoingPort = Integer.parseInt(props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "outgoing.server.port", "0"));
    74      EmailSender.incomingPort = Integer.parseInt(props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "incoming.server.port", "0"));
    75      EmailSender.username     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.username",   "");
    76      EmailSender.password     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.password",   "");
    77      EmailSender.outgoing     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "outgoing.server", "");
    78      EmailSender.incoming     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "incoming.server", "");
    79      EmailSender.replyto      = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.replyto",    "");
    80      EmailSender.smtpauth     = "true".equals(props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.smtpauth", "false"));
    81      
    82      if (verbose)
    83      {
    84        System.out.println("-------------------------------------");
    85        System.out.println("Protocol       : " + EmailSender.protocol);
    86        System.out.println("Usr/pswd       : " + EmailSender.username + "/" + EmailSender.password);
    87        System.out.println("Incoming server: " + EmailSender.incoming + ":" + EmailSender.incomingPort);
    88        System.out.println("Outgoing server: " + EmailSender.outgoing + ":" + EmailSender.outgoingPort);      
    89        System.out.println("replyto        : " + EmailSender.replyto);
    90        System.out.println("SMTPAuth       : " + EmailSender.smtpauth);
    91        System.out.println("-------------------------------------");
    92      }
    93    }
    94  
    95    public boolean isAuthRequired()
    96    {
    97      return EmailSender.smtpauth;
    98    }
    99  
   100    public String getUserName()
   101    {
   102      return EmailSender.username;
   103    }
   104  
   105    public String getPassword()
   106    {
   107      return EmailSender.password;
   108    }
   109  
   110    public String getReplyTo()
   111    {
   112      return EmailSender.replyto;
   113    }
   114  
   115    public String getIncomingServer()
   116    {
   117      return EmailSender.incoming;
   118    }
   119  
   120    public String getOutgoingServer()
   121    {
   122      return EmailSender.outgoing;
   123    }
   124    
   125    public String getEmailDest()
   126    {
   127      return EmailSender.sendEmailsTo;
   128    }
   129  
   130    public String getEventSubject()
   131    {
   132      return EmailSender.eventSubject;
   133    }
   134  
   135    public void send(String[] dest, 
   136                     String subject, 
   137                     String content)
   138    throws MessagingException, AddressException
   139    {
   140      send(dest, subject, content, null);
   141    }
   142    
   143    public void send(String[] dest, 
   144                     String subject, 
   145                     String content,
   146                     String attachment)
   147      throws MessagingException, AddressException
   148    {
   149      Properties props = setProps();
   150      
   151  //  Session session = Session.getDefaultInstance(props, auth);
   152      Session session = Session.getInstance(props,
   153                                            new javax.mail.Authenticator() 
   154                                            {
   155                                              protected PasswordAuthentication getPasswordAuthentication() 
   156                                              {
   157                                                return new PasswordAuthentication(username, password);
   158                                              }
   159                                            });    
   160      session.setDebug(verbose);
   161      Transport tr = session.getTransport("smtp");
   162      if (!(tr instanceof SMTPTransport))
   163        System.out.println("This is NOT an SMTPTransport:[" + tr.getClass().getName() + "]");
   164  
   165      Message msg = new MimeMessage(session);
   166      msg.setFrom(new InternetAddress(EmailSender.replyto));
   167      if (dest == null || dest.length == 0)
   168        throw new RuntimeException("Need at least one recipient.");
   169      msg.setRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(dest[0]));
   170      for (int i=1; i<dest.length; i++)
   171        msg.addRecipient(javax.mail.Message.RecipientType.CC, new InternetAddress(dest[i]));
   172      msg.setSubject(subject);
   173  
   174      if (attachment != null)
   175      {
   176        BodyPart messageBodyPart = new MimeBodyPart();
   177        messageBodyPart.setText(content);
   178        Multipart multipart = new MimeMultipart();
   179        // Set text message part
   180        multipart.addBodyPart(messageBodyPart);
   181        // Part two is attachment
   182        messageBodyPart = new MimeBodyPart();
   183        String filename = attachment;
   184        DataSource source = new FileDataSource(filename);
   185        messageBodyPart.setDataHandler(new DataHandler(source));
   186        messageBodyPart.setFileName(filename);
   187        multipart.addBodyPart(messageBodyPart);
   188        // Send the complete message parts
   189        msg.setContent(multipart);
   190      }
   191      else
   192      {
   193        msg.setText(content != null ? content : "");
   194        msg.setContent(content, "text/plain");
   195      } 
   196      msg.saveChanges();
   197      if (verbose) System.out.println("sending:[" + content + "], " + Integer.toString(content.length()) + " characters");
   198      Transport.send(msg);
   199    }
   200  
   201    private Properties setProps()
   202    {
   203      Properties props = new Properties();
   204      props.put("mail.debug", verbose?"true":"false");
   205      props.put("mail.smtp.host", EmailSender.outgoing);
   206      props.put("mail.smtp.port", Integer.toString(EmailSender.outgoingPort));
   207  
   208      props.put("mail.smtp.auth", "true");
   209      props.put("mail.smtp.starttls.enable", "true"); //  See http://www.oracle.com/technetwork/java/faq-135477.html#yahoomail
   210  //  props.put("mail.smtp.starttls.required", "true");
   211      props.put("mail.smtp.ssl.enable", "true");
   212      return props;
   213    }
   214  }
      
      
pi4j.gpio.GPIOController.java
Interaction with the GPIO on the Raspberry PI.
Notice the pins we use: We will need to make sure the wiring matches this setup.
      

     1  package pi4j.gpio;
     2  
     3  import com.pi4j.io.gpio.GpioController;
     4  import com.pi4j.io.gpio.GpioFactory;
     5  import com.pi4j.io.gpio.GpioPinDigitalInput;
     6  import com.pi4j.io.gpio.PinPullResistance;
     7  import com.pi4j.io.gpio.RaspiPin;
     8  import com.pi4j.io.gpio.event.GpioPinDigitalStateChangeEvent;
     9  import com.pi4j.io.gpio.event.GpioPinListenerDigital;
    10  
    11  public class GPIOController
    12  {
    13    private GpioController gpio = null;
    14    private OneLed yellowLed = null;
    15    private OneLed greenLed  = null;
    16    private GpioPinDigitalInput button = null;
    17    private RaspberryPIEventListener caller = null;
    18    
    19    public GPIOController(RaspberryPIEventListener listener)
    20    {
    21      this.caller = listener;
    22      this.gpio = GpioFactory.getInstance();
    23      this.yellowLed = new OneLed(this.gpio, RaspiPin.GPIO_01, "yellow");
    24      this.greenLed  = new OneLed(this.gpio, RaspiPin.GPIO_04, "green");    
    25      this.button = this.gpio.provisionDigitalInputPin(RaspiPin.GPIO_02, PinPullResistance.PULL_DOWN);
    26      this.button.addListener(new GpioPinListenerDigital() 
    27      {
    28        @Override
    29        public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) 
    30        {
    31          caller.manageEvent(event);
    32        }
    33      });
    34    }
    35      
    36    public void shutdown()
    37    {
    38      this.gpio.shutdown();
    39    }
    40    
    41    public void switchYellow(boolean on)
    42    {
    43      if (on)
    44        yellowLed.on();
    45      else
    46        yellowLed.off();
    47    }
    48    
    49    public void switchGreen(boolean on)
    50    {
    51      if (on)
    52        greenLed.on();
    53      else
    54        greenLed.off();
    55    }
    56  }
      
      
pi4j.gpio.OneLed.java
Utility class for led management.
      

     1  package pi4j.gpio;
     2  
     3  
     4  import com.pi4j.io.gpio.GpioController;
     5  import com.pi4j.io.gpio.GpioPinDigitalOutput;
     6  import com.pi4j.io.gpio.Pin;
     7  import com.pi4j.io.gpio.PinState;
     8  
     9  public class OneLed
    10  {
    11    private GpioPinDigitalOutput led = null;
    12    private String name;
    13    
    14    public OneLed(GpioController gpio, Pin pin, String name)
    15    {
    16      this.name = name;
    17      led = gpio.provisionDigitalOutputPin(pin, "Led", PinState.LOW);
    18    }
    19    
    20    public void on()
    21    {
    22      if ("true".equals(System.getProperty("verbose", "false")))
    23        System.out.println(this.name + " is on.");
    24      led.high();
    25    }
    26    
    27    public void off()
    28    {
    29      if ("true".equals(System.getProperty("verbose", "false")))
    30        System.out.println(this.name + " is off.");
    31      led.low();
    32    }
    33  }
      
      
pi4j.email.PIControllerMain.java
This is the main, the one to start from the Raspberry PI (see below)
      

     1  package pi4j.email;
     2  
     3  import com.pi4j.io.gpio.event.GpioPinDigitalStateChangeEvent;
     4  
     5  import java.text.SimpleDateFormat;
     6  
     7  import java.util.Date;
     8  import java.util.List;
     9  
    10  import org.json.JSONObject;
    11  
    12  import pi4j.gpio.GPIOController;
    13  import pi4j.gpio.RaspberryPIEventListener;
    14  
    15  public class PIControllerMain implements RaspberryPIEventListener
    16  {
    17    private final static SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");
    18    private static boolean verbose = "true".equals(System.getProperty("verbose", "false"));
    19  
    20    private static String providerSend    = "google";
    21    private static String providerReceive = "google";
    22    
    23    EmailSender sender = null;
    24  
    25    /**
    26     * Invoked like:
    27     *   java pi4j.email.PIControllerMain [-verbose] -send:google -receive:yahoo -help
    28     *   
    29     * This will send emails using google, and receive using yahoo.
    30     * Default values are:
    31     *   java pi4j.email.PIControllerMain -send:google -receive:google
    32     *   
    33     * Do check the file email.properties for the different values associated with email servers.
    34     * 
    35     * @param args See above
    36     */
    37    public static void main(String[] args)  
    38    {
    39      for (int i=0; i<args.length; i++)
    40      {
    41        if ("-verbose".equals(args[i]))
    42        {
    43          verbose = true;
    44          System.setProperty("verbose", "true");
    45        }
    46        else if (args[i].startsWith("-send:"))
    47          providerSend = args[i].substring("-send:".length());      
    48        else if (args[i].startsWith("-receive:"))
    49          providerReceive =args[i].substring("-receive:".length());  
    50        else if ("-help".equals(args[i]))
    51        {
    52          System.out.println("Usage:");
    53          System.out.println("  java pi4j.email.PIControllerMain -verbose -send:google -receive:yahoo -help");
    54          System.exit(0);
    55        }
    56      }
    57      
    58      PIControllerMain lmc = new PIControllerMain();
    59      GPIOController piController = new GPIOController(lmc);
    60      EmailReceiver receiver = new EmailReceiver(providerReceive); // For Google, pop must be explicitely enabled at the account level
    61      try
    62      {
    63        System.out.println("Waiting for instructions.");
    64        boolean keepLooping = true;
    65        while (keepLooping)
    66        {
    67          List<String> received = receiver.receive();
    68          if (verbose || received.size() > 0)
    69          System.out.println(SDF.format(new Date())  + " - Retrieved " + received.size() + " message(s).");
    70          for (String s : received)
    71          {
    72    //      System.out.println(s);
    73            String operation = "";
    74            try
    75            {
    76              JSONObject json = new JSONObject(s);
    77              operation = json.getString("operation");
    78            }
    79            catch (Exception ex)
    80            {
    81              System.err.println(ex.getMessage());
    82              System.err.println("Message is [" + s + "]");
    83            }
    84            if ("exit".equals(operation))
    85            {
    86              keepLooping = false;
    87              System.out.println("Will exit next batch.");
    88          //  break;
    89            }
    90            else
    91            {
    92              if ("turn-green-on".equals(operation))
    93              {
    94                System.out.println("Turning green on");
    95                piController.switchGreen(true);
    96              }
    97              else if ("turn-green-off".equals(operation))
    98              {
    99                System.out.println("Turning green off");
   100                piController.switchGreen(false);
   101              }
   102              else if ("turn-yellow-on".equals(operation))
   103              {
   104                System.out.println("Turning yellow on");
   105                piController.switchYellow(true);
   106              }
   107              else if ("turn-yellow-off".equals(operation))
   108              {
   109                System.out.println("Turning yellow off");
   110                piController.switchYellow(false);
   111              }
   112              try { Thread.sleep(1000L); } catch (InterruptedException ie) { ie.printStackTrace(); }
   113            }
   114          }
   115        }
   116        piController.shutdown();
   117        System.out.println("Done.");
   118        System.exit(0);
   119      }
   120      catch (Exception ex)
   121      {
   122        ex.printStackTrace();
   123      }
   124    }
   125  
   126    public void manageEvent(GpioPinDigitalStateChangeEvent event)
   127    {
   128      if (sender == null)
   129        sender = new EmailSender(providerSend);
   130      try
   131      {
   132        String mess = "{ pin: '" + event.getPin() + "', state:'" + event.getState() + "' }";
   133        System.out.println("Sending:" + mess);
   134        sender.send(sender.getEmailDest().split(","), 
   135                    sender.getEventSubject(), 
   136                    mess);
   137      }
   138      catch (Exception ex)
   139      {
   140        ex.printStackTrace();
   141      }
   142    }
   143  }
      
      

Wiring

Top
This diagram shows the name of the pins you want to use on the GPIO Connector of the Raspberry PI.

Raspberry PI P1 Connector map

As we said before, we need to use the GPIO pins 1, 4, and 2.
Attention!:
GPIO pin 1 is the pin #12
GPIO pin 4 is the pin #16
GPIO pin 2 is the pin #13
The two leds need a 220 Ohms resistor inline between their anode (+) and the GPIO pin they're connected on.
Their cathode (-) is connected on the Ground (pin #6)

Wiring

Demonstration

Top
The Raspberry PI is connected to the home wireless network with the small wireless dongle you can see on the pictures below.
An ethernet cable would have done the same job.
Also notice that there is no keyboard, mouse, nor screen connected to the device.
We used ssh to connect to a Linux session, from which we started the Java program, in background.

Click to enlarge
The Raspberry PI, connected to a breadboard.

At this point, the Raspberry PI is up and running, the java program has been started:
    
 Prompt> sudo ./run -send:google -receive:google [&] 
    
Hint: there is an help option.

 Prompt> ./run -help
 Usage:
   java pi4j.email.PIControllerMain -verbose -send:google -receive:yahoo -help
 Prompt> 
    
The run script looks like this:

 #!/bin/bash
 CP=./classes
 CP=$CP:/home/pi/pi4j/pi4j-distribution/target/distro-contents/lib/pi4j-core.jar
 CP=$CP:./lib/javax.mail_1.1.0.0_1-4-4.jar
 CP=$CP:./lib/json.jar
 #
 java -classpath $CP pi4j.email.LedControllerMain $*
    
Notice the different components on the breadboard:

Click to enlarge
Event on the Raspberry PI side. Button pushed.

Some event happens on the Raspberry PI's side. Someone pushed - and the released - the button.
Two emails have been sent:
    
 from   : olivier.lediouris@gmail.com
 subject: PI Event
 content: { pin: '"GPIO 2" <GPIO 2>', state:'HIGH' }
    
    
 from   : olivier.lediouris@gmail.com
 subject: PI Event
 content: { pin: '"GPIO 2" <GPIO 2>', state:'LOW' }
    
Now, you send the following email (in plain text), from your laptop, desktop, smart phone, tablet, what not:

 to     : olivier.lediouris@gmail.com
 subject: PI Request
 content: { operation: "turn-green-on" }
    

Click to enlarge
Green led is on.

The email is received by the Raspberry PI, the green led is turned on.
And you receive an acknowledgement, by email:

 from   : olivier.lediouris@gmail.com
 subject: PI Robot Ack
 content: Your request [{operation:"turn-green-on"}] is being taken care of.
    

Now you send the following email:

 to     : olivier.lediouris@gmail.com
 subject: PI Request
 content: { operation: "turn-yellow-on" }
    

Click to enlarge
Yellow led is on.

The email is received by the Raspberry PI, the yellow led is turned on.
And you receive an acknowledgement, by email:

 from   : olivier.lediouris@gmail.com
 subject: PI Robot Ack
 content: Your request [{operation:"turn-yellow-on"}] is being taken care of.
    

Now you send the following emails:

 to     : olivier.lediouris@gmail.com
 subject: PI Request
 content: { operation: "turn-yellow-off" }
    

 to     : olivier.lediouris@gmail.com
 subject: PI Request
 content: { operation: "turn-green-off" }
    

 to     : olivier.lediouris@gmail.com
 subject: PI Request
 content: { operation: "exit" }
    

Click to enlarge
Everything off, program terminated.

You receive all the acknowledgements, as usual.
All leds are turned off, and the program exits.
Done! Back to base.

Resources

Top

Oliv did it