Monday 29 March 2010

Processing in Netbeans

If you've ever browsed the Processing learning section you may have seen the article showing how to use Processing from within Eclipse.
How, I'm more of a Netbeans fan myself. This isn't a criticism of Eclipse, which I also use extensively. I just like Netbeans. Therefore, I'd like to share how to get Processing working from within the Netbeans IDE. I'll assume that you already have an installation of the Processing IDE. This howto is based almost directly on the Eclipse tutorial, so differences should be obvious.

Step 1. Download and install Netbeans

Netbeans can be downloaded from netbeans.org. Any of the bundles that contains Java SE will do. I use the 'All' bundle but you can get away with a smaller one.

Step 2. Create a new project

Ctrl-Shift-N creates a new project. For the project type, select Java >> Java Application. On the next page of the New Project dialog, give your project a name; I used 'ProcessingTest'. Also tick 'Create Main class' and enter a new like 'uk.co.mooduino.processingtest.Main'. You should use your own domain (this style of using a backward domain to create a unique namespace is common within java).

Step 3. Import the Processing library

To tell Netbeans about how Processing works, we need to import the Processing core library. Expand the 'ProcessingTest' project and then expend 'Libraries'. There should be one entry already, for the JDK. Right-click on the Libraries icon and select 'Add JAR/Folder...'. Browse wo where you installed the Processing IDE and then descend into the lib folder. Select 'core.jar'.

Step 4. Create a class and write your code!

Ctrl-N opens the New File dialog. Select 'Java Class' and give the class a name like 'MyProcessingSketch'. I'm going to use the same code as the Eclipse example;
package uk.co.mooduino.processingtest;

import processing.core.*;

public class MyProcessingSketch extends PApplet {

  public void setup() {
    size(200,200);
    background(0);
  }

  public void draw() {
    stroke(255);
    if (mousePressed) {
      line(mouseX,mouseY,pmouseX,pmouseY);
    }
  }
}

Step 5. Run!

Before we can run this code, we need to modify the Main class that Netbeans already created. Modify the code as follows:
package uk.co.mooduino.processingtest;

import processing.core.PApplet;

public class Main {

    public static void main(String[] args) {
        PApplet.main(new String[] {"--present", "uk.co.mooduino.processingtest.MyProcessingSketch"});
    }
}
Press F6 to execute the code. Your screen should switch to Processing running in fullscreen mode and the middle section should allow you to draw on it.



Monday 22 March 2010

Design patterns in Processing applications

This is a follow on to my previous two posts (here and here). In those post I constructed a simple circuit for measuring the ambient temperature and, with the help of an Arduino board, sending the temperature data down a USB cable. I also wrote a program in Processing to read that data and display it on my PC's screen. I program also stores the temperature data in a simple text file in the hopes that I'll find something interesting to do with it.
As I mentioned at the end of the last post, I was unhappy with the quality of the Processing code. It was somewhat spaghetti like, with three distinct functions all being handled by the same bundle of code. So, in this post, I'll discuss how I teased the code apart and in particularly how I used two design patterns.

Design Patterns are easy

Processing as a language makes it really easy to write quick and dirty code, as we've seen, but this doesn't mean you have to. To tidy my code up, I used two design pattterns; singleton and observer. These are two very easy design patterns to use in Java; the observer pattern is built in!

Singleton

The singleton pattern is simple; A class that is a singleton ensures that there is only on instance of that class and also provides a method to get a reference to the single instance. The Singleton pattern is somewhat notorious in Java but it needn't be as it only requires a couple of lines of code to implement. I've used Bill Pugh's solution which is the neatest.
First of all, in the Processing environment, click that right arrow in the tabbed section and select 'new tab' (or Ctl-Shift N). Give the tab the name 'TemperatureObservable' which is also the name of the class. The follow code implements the singleton pattern.
static class TemperatureObservable {
  private TemperatureObservable() {
    super();
  }
  
  private static class SingletonHolder { 
     private static final TemperatureObservable INSTANCE = new TemperatureObservable();
  }
  
  public static TemperatureObservable getInstance() {
    return SingletonHolder.INSTANCE;
  }
}
There are four things to notice.
  • The class is declared as static. Processing complains about the getInstance() method if it isn't.
  • The class's constructor is private. This ensures that only this class can create an instance of it.
  • There is a private static inner class, SingletonHolder, that creates an instance of the singleton class. The inner class is used so that the singleton instance is only constructed when it is needed. This is called lazy initialization and ensures that the singleton isn't constructed if it isn't going to be used.
  • The static method getInstance() returns the singleton; this is the only way that code outside of this class can gain access to the singleton.

Observer

This pattern is built into Java. It consists of two parts. Firstly, there is an object that we call the observable; this is an object that is expected to change as certain points. Other objects, called observers, register their interest in the observable; when the observable object's state changes, it notifies it's observers.
In this application, the TemperatureObservable objects, our singleton, is also the observable object. Java provides a Class called Observable and any classes that extend this base class automatically get the Observer pattern functionality. Of course, we need to pull in the code from TempDisplay that this object taking over; the TemperatureObservable's job will be to read the temperature data from the serial connection and notify it's observers that a new reading is available.
static class TemperatureObservable extends Observable {
  
  private static PApplet pApplet = null;
  private Serial port = null;
  private String temperature = "0";
  
  private TemperatureObservable() {
    super();
  }
  
  public static void setParent(PApplet pApplet) {
    TemperatureObservable.pApplet = pApplet;
  }
  
  public void addObserver(Observer o) {
    super.addObserver(o);
    if (this.countObservers() == 1) {
      this.setup();
    }
  }
  
  public void deleteObserver(Observer o) {
    super.deleteObserver(o);
    if (this.countObservers() == 0) {
      this.tearDown();
    }
  }
  
  public void deleteObservers() {
    super.deleteObservers();
    this.tearDown();
  }
  
  public void setup() {
    if (this.port == null) {
      String portName = Serial.list()[0];
      this.port = new Serial(TemperatureObservable.pApplet, portName, 9600);
      this.port.write("get temp");
      this.tick();
    }
  }
  
  public void tearDown() {
    if (this.port != null) {
      this.port.stop();
      this.port = null;
    }
  }
  
  public void tick() {
    if (this.port.available()>0) {
      TemperatureObservable.pApplet.delay(100);
      this.temperature = port.readString().trim();
      this.setChanged();
      this.notifyObservers(this.temperature);
    }
  }
  
  public String getTemperature() {
    return this.temperature;
  }
  
   private static class SingletonHolder { 
     private static final TemperatureObservable INSTANCE = new TemperatureObservable();
   }
  
  public static TemperatureObservable getInstance() {
    return SingletonHolder.INSTANCE;
  }
  
}
The new code allows a PApplet object to be registered with the TemperatureObservable class; this is only needed because the Serial object constructor expects a PApplet object as it's first argument. Further on, we also use the PApplet's delay() method. PApplet, by the way, is the superclass of the class you enter into the Processing sketch's first tab. How PApplet child class is called TempDisplay.
addObserver(), deleteObserver() and deleteObservers() are all methods in the Observable class that we override so that we can set up and tear down the serial connection on demand. Lastly, the tick() method is needed to tell the TemperatureObservable object to check the serial connection for a new temperature; remember that in the previous version, this check happened in the PApplet's draw() method.
In order to use the Observable singleton, we need an Observer. Java provides an Observer interface which has just one abstract method that we need to implement, update(). Objects that implement this interface can be passed to the Observable object's addObserver(). Our first observer is quite a simple one and is charged with maintaining the main TempDisplay object's copy of the current temperature (the one that it renders each time draw() is called).
static class TempDisplayObserver implements Observer {
  
  private TempDisplay display = null;
  
  public TempDisplayObserver(TempDisplay display) {
    this.display = display;
  }
  
  public void update(Observable o, Object arg) {
    this.display.setTemperature((String)arg);
  }
}
For this to work, the TempDisplay class needs a new method, setTemperature(). The TempDisplay code is as follows. Notice that the is much shorter than the version at the end last post, though I've also removed the code that saves the data to a file and we've yet to replace this.
import processing.serial.*;

String temperature = "0";
PFont font;


TemperatureObservable tempObs = null;

void setup() {
  TemperatureObservable.setParent(this);
  this.tempObs = TemperatureObservable.getInstance();
  this.tempObs.addObserver(new TempDisplayObserver(this));

  font = loadFont("Ziggurat-HTF-Black-32.vlw");
  textFont(font);
  textAlign(CENTER);
  size(200, 140);
  background(0);
  fill(0);
  smooth();
}

void draw() {
  background(255);
  this.tempObs.tick();
  text(temperature, width/2, height/2);
}

void setTemperature(String arg) {
  temperature = arg;
}
As you can see, at the start of the setup() method, we register the TempDisplay object with the TemperatureObservable class via it's setParent() method. we then get a reference to the singleton and add a new instance of TempDisplayObserver. Also notice in the draw() method, we call the singleton's tick() method.

File output again.

The second observer object is tasked with writing the data to the text file. Most of this code is simply copied from the previous version of TempDisplay.
static class TempFileObserver implements Observer {
  
  private String dataFolder = null;
  private PrintWriter output = null;
  private TimeZone tz = null;
  private DateFormat stamp = null;
  private Date lastSaveDate = null;
  
  public TempFileObserver(String dataFolder) {
    this.dataFolder = dataFolder;
    tz = TimeZone.getDefault();
    stamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
    stamp.setTimeZone(tz);
    lastSaveDate = new Date();
    String fileName = getFileName(lastSaveDate);
    output = getOutput(dataFolder, fileName);
  }
  
  public void update(Observable o, Object arg) {
    this.writeData((String)arg);
  }
  
  private void writeData(String temp) {
    if (this.output != null) {
      Date currentSaveDate = new Date();
      String message = stamp.format(currentSaveDate) + " " + temp;
      this.output.println(message);
      this.output.flush();
      if (this.isNextDay(this.lastSaveDate, currentSaveDate)) {
        this.output.flush();
        this.output.close();
        String fileName = this.getFileName(currentSaveDate);
        this.output = this.getOutput(dataFolder, fileName);
      }
      this.lastSaveDate = currentSaveDate;
    }
  }
  
  private String getFileName(Date date) {
      DateFormat dfm = new SimpleDateFormat("yyyyMMdd");
      dfm.setTimeZone(tz);
      return dfm.format(date) + ".data";
  }
  
  private PrintWriter getOutput(String folder, String file) {
    PrintWriter pw = null;
    File fileHandle = new File(folder, file);
    try {
      pw = new PrintWriter(new FileOutputStream(fileHandle, true));
    } catch (FileNotFoundException fnfe) {}
    return pw;
  }
  
  private boolean isNextDay(Date earlier, Date later) {
    boolean isNextDay = false;
    Calendar cEarlier = Calendar.getInstance();
    Calendar cLater = Calendar.getInstance();
    cEarlier.setTime(earlier);
    cLater.setTime(later);
    if (cLater.after(cEarlier)) {
      boolean dayIsAfter = cLater.get(Calendar.DAY_OF_YEAR) > cEarlier.get(Calendar.DAY_OF_YEAR);
      boolean yearIsAfter = cLater.get(Calendar.YEAR) > cEarlier.get(Calendar.YEAR);
      isNextDay = dayIsAfter || yearIsAfter;
    }
    return isNextDay;
  }   
}
To make use of this class, we just need to pass an instance of it to the singleton's addObserver() method. The TempDisplay's setup() method becomes:
void setup() {
  TemperatureObservable.setParent(this);
  this.tempObs = TemperatureObservable.getInstance();
  
  String dataFolder = selectFolder("Choose the folder where you want the temperature data to be recorded");
  if (dataFolder != null) {
    this.tempObs.addObserver(new TempFileObserver(dataFolder));
  }
  
  this.tempObs.addObserver(new TempDisplayObserver(this));
  //...
}

One last thing.

There is just one last thing that was bugging me; just occasionally, more than one temperature reading is available in the serial connection and this would cause the display to show more than one reading. This is easily fixed. We know that each reading should contain no white space and that individual reading are separated by a line break. Therefore, I changed the TemperatureObservable's tick method to split the value that it reads from the serial connection using a regular expression. The regular expression that I used is '[\\s]+'. This simply means 'one or more characters of white space.' The tick() method changes to the following:
public void tick() {
    if (this.port.available()>0) {
      TemperatureObservable.pApplet.delay(100);
      String outString = port.readString().trim();
      String[] temps = outString.split("[\\s]+");
      for (int i = 0; i < temps.length; i++) {
        this.temperature = temps[i];
        this.setChanged();
        this.notifyObservers(this.temperature);
      }
    }
  }

Conclusion

I was really pleased with the work I did here. Implementing these two design patterns was straight forward. It is just great that Processing allows me to simple programmes quickly while being able to leverage my Java knowledge. Maybe next time I'll do something more visually exciting.



Sunday 7 March 2010

File output with Processing

In my last post, I discussed how I put together a simple temperature probe, using an Arduino board, how the Arduino pushed the current temperature down a USB cable and finally how I consumed that data with a Processing application.
In this article I wanted to share my next steps. I want to be able to use the data from the sensor in a number of ways, not just in Processing, but in any language or platform. Therefore this week I investigated how to write the data to a permanent file on my hard drive. My initial plan had been to write an original program in Java that read the data from the serial port but I soon discovered that getting Java to talk to the serial port (at least on my Linux PC) wasn't exactly easy. Obviously it is possible because Processing is written in Java and this works find on my PC and so to save pulling my hair out, I decided at this time to continue with the Processing application that I started last time.

The task

I set my self the following tasks that I'd like the application to do.
  • The application must ask the user at runtime where on the hard drive the data files are to be stored.
  • The data file must be simple text that any file can read.
  • The data file mustn't individually get to large. Ideally, the application should generated one file per day, and automatically 'roll over' at the start of each day.
  • The data should be appended at the end of the data file if the file already exists.

The Code

First, I looked at how to choose a folder. Processing conveniently provides a function called selectFolder() that prompts the user to select a directory, so this was my starting point. There is also a function called createWriter() that is used to open a PrintWriter object pointing at a given file name.
String dataFolder;
PrintWriter output;

void setup() {
 dataFolder = selectFolder("Choose the folder where you want the temperature data to be recorded");
 if (dataFolder != null) {
  String fileName = "/" + year() + month() + day() + ".data";
  output = createWriter(dataFolder + fileName);
 }
}
The only issue with this code is the use of month() and day(); These return an integer value, meaning that on the day that I write this, the newly created file would be called '201037.data' when of course it should be called 20100307.data'. We could easily fix this by checking whether each value is less than 10 and adding the 0 if necessary but there is a more flexible way.
Processing allows us to make use of the normal Java classes that are part of the JVM. In this instance, the class that I want to use is called DateFormat and this allows you to take a Date object and create a formated string based on it. In Java, a Date object records a moment in time and by default when you instantiate a new Date object, it records the moment that it was created.
String dataFolder;
PrintWriter output;
TimeZone tz;

void setup() {
 dataFolder = selectFolder("Choose the folder where you want the temperature data to be recorded");
 if (dataFolder != null) {
  tz = TimeZone.getDefault();
  DateFormat dfm = new SimpleDateFormat("yyyyMMdd");
  dfm.setTimeZone(tz);
  String fileName = "/" + dfm.format(new Date()) + ".data";
  output = createWriter(dataFolder + fileName);
 }
}
This code creates the output file with the correctly formatted name.
Next, outputting the data to the file. I wanted each line in the data file to show the moment that the reading was received as well as the actual temperature. To time stamp each reading, I created another instance of DateFormat, this time with the hours, minutes, second and milliseconds at the end. I created a new function, writeData(), which is called from within draw() whenever a new temperature reading is received.
DateFormat stamp;

void setup() {
 stamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
 stamp.setTimeZone(tz);
}

void draw() {
 if (port.available()>0) {
  delay(100);
  temperature = port.readString().trim();
  writeData(temperature);
 }
 background(255);
 text(temperature, width/2, height/2);
}

void writeData(String temp) {
 if (output != null) {
  String message = stamp.format(new Date()) + " " + temp;
  output.println(message);
  output.flush();
 }
}
Notice also that in draw(), there is a call to trim() on the string returned from the serial port. This is to ensure that extra end of line characters are passed through to the data file. output.flush() is required to flush the data to the hard drive; if we didn't do this, the file wouldn't get written until some internal buffer was filled.
Next up, making sure that at the end of each day, the file name 'rolls over' and a new output file is created. In order to do this, we note the current time each time a temperature is recorded. if the current time is one calendar day after the previous time, then a new file has to be created. First, I wrote a function that would determine is one date is one calendar day later than another.
boolean isNextDay(Date earlier, Date later) {
 boolean isNextDay = false;
 Calendar cEarlier = Calendar.getInstance();
 Calendar cLater = Calendar.getInstance();
 cEarlier.setTime(earlier);
 cLater.setTime(later);
 if (cLater.after(cEarlier)) {
  boolean dayIsAfter = cLater.get(Calendar.DAY_OF_YEAR) > cEarlier.get(Calendar.DAY_OF_YEAR);
  boolean yearIsAfter = cLater.get(Calendar.YEAR) > cEarlier.get(Calendar.YEAR);
  isNextDay = dayIsAfter || yearIsAfter;
 }
 return isNextDay;
}
As you can see, this function makes use of another Java class, this time the Calendar class. The way Java handles dates, time and location specific information about date and time is quite confusing at first. Its important to remember that a Date object only records a moment in time. It isn't aware of what this moment means; whether it is a Tuesday or even which year it is. This is because different parts of the world have different names of Tuesday and even different names of the years (we're all used to the Gregorian calendar but this isn't the only calendar in use). Therefore Java gives us the Calendar class. With one of these, you can ask time specific questions and get answers that are correct for the PC that is running your software.
With this function in place, it was easy to update the writeData() function create a new file at the start of each day. I also created two other functions; These are simply refactored code from the setup() function:
Date lastSaveDate;

void setup() {
 dataFolder = selectFolder("Choose the folder where you want the temperature data to be recorded");
 if (dataFolder != null) {
  tz = TimeZone.getDefault();
  stamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
  stamp.setTimeZone(tz);
  lastSaveDate = new Date();
  String fileName = getFileName(lastSaveDate);
  output = getOutput(dataFolder, fileName);
 }
}

void writeData(String temp) {
 if (output != null) {
  Date currentSaveDate = new Date();
  String message = stamp.format(currentSaveDate) + " " + temp;
  output.println(message);
  output.flush();
  if (isNextDay(lastSaveDate, currentSaveDate)) {
   output.flush();
   output.close();
   String fileName = getFileName(currentSaveDate);
   output = getOutput(dataFolder, fileName);
  }
  lastSaveDate = currentSaveDate;
 }
}

String getFileName(Date date) {
 DateFormat dfm = new SimpleDateFormat("yyyyMMdd");
 dfm.setTimeZone(tz);
 return "/" + dfm.format(date) + ".data";
}

PrintWriter getOutput(String folder, String file) {
 return createWriter(folder + file);
}
With this code in place this last item on my list is to append new data to existing data. At the moment, when the application is started, the call to createWriter() deletes any data already in the target file. It is much better to add the new data to the end of what is already there. Processing doesn't seem to give us any help in this area so I again dived into Java, this time for the File and FileOutputStream classes.
Whereas the createWriter() function takes the absolute file name and returns the PrintWriter object, the new code builds the PrintWriter up in stages. First, we pass the folder and filename to the File constructor. This creates a pointer to a file location but doesn't actually create the file on the hard drive. Then were create an instance of FileOutputStream(). To this we pass the file handle and, importantly, we also pass the boolean true; This tells the FileOutputStream to open in append mode. Lastly, we create the PrintWriter, passing in the FileOutputStream.
These changes are to the getOutput() function declared in the last refactoring. No other changes were required.
PrintWriter getOutput(String folder, String file) {
 PrintWriter pw = null;
 File fileHandle = new File(folder, file);
 try {
  pw = new PrintWriter(new FileOutputStream(fileHandle, true));
 } catch (FileNotFoundException fnfe) {}
 return pw;
}

All together now

Putting this all together, we end up with the following code, which includes the code from the previous article.
String temperature = "0";
PFont font;
Serial port;
String dataFolder;
PrintWriter output;
TimeZone tz;
DateFormat stamp;
Date lastSaveDate;

void setup() {
 dataFolder = selectFolder("Choose the folder where you want the temperature data to be recorded");
 if (dataFolder != null) {
  tz = TimeZone.getDefault();
  stamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
  stamp.setTimeZone(tz);
  lastSaveDate = new Date();
  String fileName = getFileName(lastSaveDate);
  output = getOutput(dataFolder, fileName);
 }
 String portName = Serial.list()[0];
 port = new Serial(this, portName, 9600);
 port.write("get temp");
 font = loadFont("Ziggurat-HTF-Black-32.vlw");
 textFont(font);
 textAlign(CENTER);
 size(200, 140);
 background(0);
 fill(0);
 smooth();
}

void draw() {
 if (port.available()>0) {
  delay(100);
  temperature = port.readString().trim();
  writeData(temperature);
 }
 background(255);
 text(temperature, width/2, height/2);
}

void writeData(String temp) {
 if (output != null) {
  Date currentSaveDate = new Date();
  String message = stamp.format(currentSaveDate) + " " + temp;
  output.println(message);
  output.flush();
  if (isNextDay(lastSaveDate, currentSaveDate)) {
   output.flush();
   output.close();
   String fileName = getFileName(currentSaveDate);
   output = getOutput(dataFolder, fileName);
  }
  lastSaveDate = currentSaveDate;
 }
}

String getFileName(Date date) {
 DateFormat dfm = new SimpleDateFormat("yyyyMMdd");
 dfm.setTimeZone(tz);
 return dfm.format(date) + ".data";
}

PrintWriter getOutput(String folder, String file) {
 PrintWriter pw = null;
 File fileHandle = new File(folder, file);
 try {
  pw = new PrintWriter(new FileOutputStream(fileHandle, true));
 } catch (FileNotFoundException fnfe) {}
 return pw;
}

boolean isNextDay(Date earlier, Date later) {
 boolean isNextDay = false;
 Calendar cEarlier = Calendar.getInstance();
 Calendar cLater = Calendar.getInstance();
 cEarlier.setTime(earlier);
 cLater.setTime(later);
 if (cLater.after(cEarlier)) {
  boolean dayIsAfter = cLater.get(Calendar.DAY_OF_YEAR) > cEarlier.get(Calendar.DAY_OF_YEAR);
  boolean yearIsAfter = cLater.get(Calendar.YEAR) > cEarlier.get(Calendar.YEAR);
  isNextDay = dayIsAfter || yearIsAfter;
 }
 return isNextDay;
}

Next?

This code is all about saving the data from the temperature sensor to the hard drive so that I can use the data in other ways, and that is what I plan to do next. Two ideas have occurred to me; One suggestion that Hari made about the previous article was this it would be nice to graph the data, and so that's one thing I want to look at. As I'm a web developer to pay the bills, I also want to look at how I can consume the data in PHP to create a website showing the temperature data.
Before this, though, I think the code above desperately needs refactoring to separate out the three concerns of reading the data from the serial port, showing the data on the monitor and recording the data to the file.
So, until next time, stay happy.