Thursday, January 08, 2015

Version 3.0.1.5 available

New version - for both the Weather Wizard and the Navigation Console - 3.0.1.5 is available, at the usual place.
Few new features, minor UI improvements, and big uptake for the Raspberry PI.
A new feature for the Weather Wizard in headless mode, you can download several composites at the same time (one after the other actually). Use the -composite: and -pattern: parameters, separate each value with a comma.
For example, on Windows:
 set PRMS=-composite:./patterns/01.Favorites/01.3.00.Pacific.Sfc.500.Tropic.GRIB.ptrn
 set PRMS=%PRMS%,./patterns/01.Favorites/06.01.AllPac.Faxes.Satellite.ptrn
 set PRMS=%PRMS% -interval:360 
 set PRMS=%PRMS% "-pattern:/yyyy/MM-MMM | | yyyy-MM-dd_HHmmss_z | _Pacific | waz,/yyyy/MM-MMM | | yyyy-MM-dd_HHmmss_z | _Pacific.SatPic | waz"
 ::
 set command=java %JAVA_OPTIONS% -client -classpath "%CP%" -Dheadless=true main.splash.Splasher %PRMS%
On Linux and Mac:
 PRMS=-composite:./patterns/01.Favorites/01.3.00.Pacific.Sfc.500.Tropic.GRIB.ptrn
 PRMS=$PRMS,./patterns/01.Favorites/06.01.AllPac.Faxes.Satellite.ptrn
 PRMS=$PRMS -interval:360 
 PRMS=$PRMS "-pattern:/yyyy/MM-MMM | | yyyy-MM-dd_HHmmss_z | _Pacific | waz,/yyyy/MM-MMM | | yyyy-MM-dd_HHmmss_z | _Pacific.SatPic | waz"
 #
 command=java $JAVA_OPTIONS -client -classpath "$CP" -Dheadless=true main.splash.Splasher $PRMS

Monday, July 28, 2014

The self-sufficient Raspberry PI

Here is a picture of the Raspberry PI setup to log the data from a GPS, and fueled by a solar panel.
This one has the small touchscreen from Adafruit (zoom on it). The tray and the breadboard are not mandatory in this setting. Next step will be to use two servos to orient the solar panel so it faces the sun. This would be based on the project available on Google Code. That's the cool thing about using Java on the Raspberry PI. Whatever works in Java works on it.

Thursday, July 10, 2014

How the Raspberry PI makes sense on board

A computer can be helpful on board, for several reasons.
  • Some useful navigation softwares need one to run (SailMail, for example, that allows you to receive faxes through the SSB, send and receive emails, all kinds of useful things)
  • Some chart plotter softwares (like OpenCPN, the best) require a computer to run as well, to plot the current position on an electronic chart.
  • The on-board electronics can be read from a computer (through NMEA, or some other proprietary protocols), and the data they emit can be used as parameters for calculations, like performance evaluation, routing, current estimation (tricky, but so useful).

Electricity can be precious on board (specially on a sail boat, where it is in short supply), it is required to run several important devices, like the autopilot, the water maker, and to some extend, the fridge. A laptop can draw a substantial amount of current, specially if its battery is old (2 to 3 amps and more, I have evidences). In those conditions, leaving it on all the time can be questionable. On top of that, turning it down, and turning it back up takes time...

Serial port access

The data we are interested in usually (like in 99% of the cases) come from a Serial port (USB, or 9-pins). A big detail to mention is that a Serial port can only be accessed by one process at a time. That means that when your chart plotter accesses the serial port, no other program can access it, even it is is not the same data the other program is interested in.
The chart plotter will be interested in the GPS data, another soft might be interested in the wind data; but there is no way around, one program, one port.
GPSd does not address this issue (even if it pretends to), and - in my opinion - makes things more complex. It's only interested in GPS data (not in Speed Through Water, not in Wind Data, etc), but it locks the port like everyone else, and just rebroadcasts them in another bloody format! Why isn't it just rebroadcasting the NMEA sentences as they've been read?.., I have no idea.

Introducing the Raspberry PI

The Raspberry PI does much more than the boards like Arduino, Sparkfun, Beaglebones, and others (those are great, don't get me wrong, I am not spitting in the soup), it is a fully featured Linux (Debian) computer, that can - as such - do all a computer can do, like multi tasking, multi threading, remote access (SSH, VNC), network access (Ethernet and Wireless). It only has 512 Mb of RAM. But that is enough, as we will see. Its hard disk is replaced by an SD card, ranging from 4GB to whatever you want. I use 16Gb cards. And at work, it draws less than 500mA, which is ridiculous. You can plug several kinds of screen on the Raspberry PI, a TV screen using an HDMI port, or a rear camera car monitor (3.5", 4.3", or 7") using an RCA port. Turning those screens off when not needed will also contribute to save some energy.
The Raspberry PI can read the serial port - Ok, exclusively - and re-broadcast the data on whatever channel (TCP, HTTP, UDP, RMI, whatever). This way they can be accessed from this channel, the data remaining the same. As soon as a device is turned on, it can join the ad-hoc network created by the Raspberry PI, and immediately read the data it broadcasts.

A program like OpenCPN is smart enough to support several kinds of channels.
HTTP is also an option to consider, smart phones and tablets are ready for that, without any modifications. HTML5 and CCS3 will do the job. Those devices have browsers that understand those technologies. It is very easy to display the data read by the Raspberry PI on an iPhone.
In addition, the Raspberry PI can log the data read from the NMEA Port, its SD card is big enough to log several days of data. Another feature of the Raspberry PI is its GPIO Header. This is the bridge to the world of sensors. For example, the BMP180 will allow the Raspberry PI to read the air temperature and the atmospheric pressure. Those data can very well be injected in the NMEA stream (they both have an NMEA equivalent). Some navigation station already provide this kind of interfaces, but in case yours does not, you will then get those data for less than $10.
See some implementation details here.

Even further, it is not difficult to come up with a small setting the Raspberry PI can use to monitor the tension of the batteries on the boat. This one does not - as far as I know - have an NMEA equivalent. It can be injected in the NMEA stream though, it will be considered as a custom sentence. And it can be logged too, along with the rest.
If needed, the data can be displayed on the Raspberry PI as they are read. I did some tests with a graphical interface, as well as in character mode, just to keep the energy consumption as low as possible. The screen is turned off when not needed, for the same reason.

Useful links



Basically, you can reproduce the settings used during the last Aremica's Cup, for a tiny fraction of their budget! The Raspberry PI is less than $40.
Compare with this...

If it does not work for you, do let me know, it should. I might be able to help.

Friday, May 09, 2014

Embedded small screen on the Raspberry PI

Adafruit provides a small touch screen, the size of the Raspberry PI... It works like a charm! See below...
It comes with detailed explanations about the way to setup the software.
The only thing I had to mess with was the hardware..., the way to hook up the screen on the board.
I must be a software guy... ;0)
And with the 26-pin connector on the right (under the board), you can still use the cobbler.
The ncurses menu at startup turned out to be useful.
There is a soft keyboard that can be useful too in this context. Install it by running
 sudo apt-get install matchbox-keyboard
Here is what it can look like:

The screenshot above is taken using scrot,
 sudo apt-get install scrot
Cool stuff.
A Swing UI works fine on this little screen, even if it may need to be reworked to fit the screen. But it's all good.

Friday, March 21, 2014

Persistence Of Vision (POV) first tests

I got the small "miniPOV" kit from Adafruit, assembled it, and attached it to the a spoke of the back wheel of a bike... The first tests are already positive.

The text you read should say "Make!".
Next, I'll be working on some utilities to help people make their own text and upload them to the chip. Stay tuned.

Thursday, December 12, 2013

Control the Raspberry PI through email, in Java

The Problem

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:
  • Know what the conditions are on the Raspberry PI
  • Ask remotely to the Raspberry PI to take some action
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.
  • When the temperature in the home drops below a given threshold, it sends you a message (through whatever channel, WebSocket or so)
  • On reception of the message, you - wherever you are - decide to ask the Raspberry PI to turn the heater on
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

A way to address this problem is to use emails.
This will not implement real-time messaging - like WebSocket - 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

A way to address this problem is to 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

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.
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.mail.Message;
    10  import javax.mail.MessagingException;
    11  import javax.mail.PasswordAuthentication;
    12  import javax.mail.Session;
    13  import javax.mail.Transport;
    14  import javax.mail.internet.AddressException;
    15  import javax.mail.internet.InternetAddress;
    16  import javax.mail.internet.MimeMessage;
    17  
    18  public class EmailSender
    19  {
    20    private static String protocol;
    21    private static int outgoingPort;
    22    private static int incomingPort;
    23    private static String username;
    24    private static String password;
    25    private static String outgoing;
    26    private static String incoming;
    27    private static String replyto;
    28    private static boolean smtpauth;
    29    
    30    private static String sendEmailsTo;
    31    private static String eventSubject;
    32    
    33    private static boolean verbose = "true".equals(System.getProperty("verbose", "false"));
    34  
    35    public EmailSender(String provider) throws RuntimeException
    36    {
    37      EmailSender.protocol = "";
    38      EmailSender.outgoingPort = 0;
    39      EmailSender.incomingPort = 0;
    40      EmailSender.username = "";
    41      EmailSender.password = "";
    42      EmailSender.outgoing = "";
    43      EmailSender.incoming = "";
    44      EmailSender.replyto = "";
    45      EmailSender.smtpauth = false;
    46      EmailSender.sendEmailsTo = "";
    47      EmailSender.eventSubject = "";
    48  
    49      Properties props = new Properties();
    50      String propFile = "email.properties";
    51      try
    52      {
    53        FileInputStream fis = new FileInputStream(propFile);
    54        props.load(fis);
    55      }
    56      catch (Exception e)
    57      {
    58        System.out.println("email.properies file problem...");
    59        throw new RuntimeException("File not found:email.properies");
    60      }
    61      EmailSender.sendEmailsTo = props.getProperty("pi.send.emails.to");
    62      EmailSender.eventSubject = props.getProperty("pi.event.subject");
    63      
    64      EmailSender.protocol     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.protocol");
    65      EmailSender.outgoingPort = Integer.parseInt(props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "outgoing.server.port", "0"));
    66      EmailSender.incomingPort = Integer.parseInt(props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "incoming.server.port", "0"));
    67      EmailSender.username     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.username",   "");
    68      EmailSender.password     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.password",   "");
    69      EmailSender.outgoing     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "outgoing.server", "");
    70      EmailSender.incoming     = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "incoming.server", "");
    71      EmailSender.replyto      = props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.replyto",    "");
    72      EmailSender.smtpauth     = "true".equals(props.getProperty("pi." + (provider != null ? (provider + ".") : "") + "mail.smtpauth", "false"));
    73      
    74      if (verbose)
    75      {
    76        System.out.println("-------------------------------------");
    77        System.out.println("Protocol       : " + EmailSender.protocol);
    78        System.out.println("Usr/pswd       : " + EmailSender.username + "/" + EmailSender.password);
    79        System.out.println("Incoming server: " + EmailSender.incoming + ":" + EmailSender.incomingPort);
    80        System.out.println("Outgoing server: " + EmailSender.outgoing + ":" + EmailSender.outgoingPort);      
    81        System.out.println("replyto        : " + EmailSender.replyto);
    82        System.out.println("SMTPAuth       : " + EmailSender.smtpauth);
    83        System.out.println("-------------------------------------");
    84      }
    85    }
    86  
    87    public boolean isAuthRequired()
    88    {
    89      return EmailSender.smtpauth;
    90    }
    91  
    92    public String getUserName()
    93    {
    94      return EmailSender.username;
    95    }
    96  
    97    public String getPassword()
    98    {
    99      return EmailSender.password;
   100    }
   101  
   102    public String getReplyTo()
   103    {
   104      return EmailSender.replyto;
   105    }
   106  
   107    public String getIncomingServer()
   108    {
   109      return EmailSender.incoming;
   110    }
   111  
   112    public String getOutgoingServer()
   113    {
   114      return EmailSender.outgoing;
   115    }
   116    
   117    public String getEmailDest()
   118    {
   119      return EmailSender.sendEmailsTo;
   120    }
   121  
   122    public String getEventSubject()
   123    {
   124      return EmailSender.eventSubject;
   125    }
   126  
   127    public void send(String[] dest, 
   128                     String subject, 
   129                     String content)
   130      throws MessagingException, AddressException
   131    {
   132      Properties props = setProps();
   133      
   134  //  Session session = Session.getDefaultInstance(props, auth);
   135      Session session = Session.getInstance(props,
   136                                            new javax.mail.Authenticator() 
   137                                            {
   138                                              protected PasswordAuthentication getPasswordAuthentication() 
   139                                              {
   140                                                return new PasswordAuthentication(username, password);
   141                                              }
   142                                            });    
   143      session.setDebug(verbose);
   144      Transport tr = session.getTransport("smtp");
   145      if (!(tr instanceof SMTPTransport))
   146        System.out.println("This is NOT an SMTPTransport:[" + tr.getClass().getName() + "]");
   147  
   148      Message msg = new MimeMessage(session);
   149      msg.setFrom(new InternetAddress(EmailSender.replyto));
   150      if (dest == null || dest.length == 0)
   151        throw new RuntimeException("Need at least one recipient.");
   152      msg.setRecipient(javax.mail.Message.RecipientType.TO, new InternetAddress(dest[0]));
   153      for (int i=1; i<dest.length; i++)
   154        msg.addRecipient(javax.mail.Message.RecipientType.CC, new InternetAddress(dest[i]));
   155      msg.setSubject(subject);
   156      msg.setText(content != null ? content : "");
   157      msg.setContent(content, "text/plain");
   158      msg.saveChanges();
   159      if (verbose) System.out.println("sending:[" + content + "], " + Integer.toString(content.length()) + " characters");
   160  
   161      Transport.send(msg);
   162    }
   163  
   164    private Properties setProps()
   165    {
   166      Properties props = new Properties();
   167      props.put("mail.debug", verbose?"true":"false");
   168      props.put("mail.smtp.host", EmailSender.outgoing);
   169      props.put("mail.smtp.port", Integer.toString(EmailSender.outgoingPort));
   170  
   171      props.put("mail.smtp.auth", "true");
   172      props.put("mail.smtp.starttls.enable", "true"); //  See http://www.oracle.com/technetwork/java/faq-135477.html#yahoomail
   173  //  props.put("mail.smtp.starttls.required", "true");
   174      props.put("mail.smtp.ssl.enable", "true");
   175      return props;
   176    }
   177  }
      
      
pi4j.gpio.GPIOController.java
Interaction with the GPIO on the Raspberry PI.
Notice the pins we use:
  • Yellow led on pin #01 (line 23)
  • Green led on pin #04 (line 24)
  • Button on pin #02 (line 25)
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     * Do check the file email.properties for the different values associated with email servers.
    31     * 
    32     * @param args See above
    33     */
    34    public static void main(String[] args)  
    35    {
    36      for (int i=0; i<args.length; i++)
    37      {
    38        if ("-verbose".equals(args[i]))
    39        {
    40          verbose = true;
    41          System.setProperty("verbose", "true");
    42        }
    43        else if (args[i].startsWith("-send:"))
    44          providerSend = args[i].substring("-send:".length());      
    45        else if (args[i].startsWith("-receive:"))
    46          providerReceive =args[i].substring("-receive:".length());  
    47        else if ("-help".equals(args[i]))
    48        {
    49          System.out.println("Usage:");
    50          System.out.println("  java pi4j.email.PIControllerMain -verbose -send:google -receive:yahoo -help");
    51          System.exit(0);
    52        }
    53      }
    54      
    55      PIControllerMain lmc = new PIControllerMain();
    56      GPIOController piController = new GPIOController(lmc);
    57      EmailReceiver receiver = new EmailReceiver(providerReceive); // For Google, pop must be explicitely enabled at the account level
    58      try
    59      {
    60        System.out.println("Waiting for instructions.");
    61        boolean keepLooping = true;
    62        while (keepLooping)
    63        {
    64          List<String> received = receiver.receive();
    65          if (verbose || received.size() > 0)
    66          System.out.println(SDF.format(new Date())  + " - Retrieved " + received.size() + " message(s).");
    67          for (String s : received)
    68          {
    69    //      System.out.println(s);
    70            String operation = "";
    71            try
    72            {
    73              JSONObject json = new JSONObject(s);
    74              operation = json.getString("operation");
    75            }
    76            catch (Exception ex)
    77            {
    78              System.err.println(ex.getMessage());
    79              System.err.println("Message is [" + s + "]");
    80            }
    81            if ("exit".equals(operation))
    82            {
    83              keepLooping = false;
    84              System.out.println("Will exit next batch.");
    85          //  break;
    86            }
    87            else
    88            {
    89              if ("turn-green-on".equals(operation))
    90              {
    91                System.out.println("Turning green on");
    92                piController.switchGreen(true);
    93              }
    94              else if ("turn-green-off".equals(operation))
    95              {
    96                System.out.println("Turning green off");
    97                piController.switchGreen(false);
    98              }
    99              else if ("turn-yellow-on".equals(operation))
   100              {
   101                System.out.println("Turning yellow on");
   102                piController.switchYellow(true);
   103              }
   104              else if ("turn-yellow-off".equals(operation))
   105              {
   106                System.out.println("Turning yellow off");
   107                piController.switchYellow(false);
   108              }
   109              try { Thread.sleep(1000L); } catch (InterruptedException ie) { ie.printStackTrace(); }
   110            }
   111          }
   112        }
   113        piController.shutdown();
   114        System.out.println("Done.");
   115        System.exit(0);
   116      }
   117      catch (Exception ex)
   118      {
   119        ex.printStackTrace();
   120      }
   121    }
   122  
   123    public void manageEvent(GpioPinDigitalStateChangeEvent event)
   124    {
   125      if (sender == null)
   126        sender = new EmailSender(providerSend);
   127      try
   128      {
   129        String mess = "{ pin: '" + event.getPin() + "', state:'" + event.getState() + "' }";
   130        System.out.println("Sending:" + mess);
   131        sender.send(sender.getEmailDest().split(","), 
   132                    sender.getEventSubject(), 
   133                    mess);
   134      }
   135      catch (Exception ex)
   136      {
   137        ex.printStackTrace();
   138      }
   139    }
   140  }
      
      

Wiring

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

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:
  • A green led
  • A yellow led
  • A push button

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

Some event happens on the Raspberry PI's side. Someone pushed 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