Monday, December 17, 2012

ArduinoFX: A JavaFX GUI for Home Automation with Raspberry Pi and Arduino. Part II

Hi There!

If you haven't read the first part of this series, please take your time, and read about the basic configuration required for the application I'm blogging about in this second part.

Briefly, as a recap, we have an Arduino with a RHT03 temperature and relative humidity sensor and a XBee antenna, plugged in an Arduino shield. The measures are sent wirelessly to a second XBee antenna, plugged via a XBee Explorer to a Raspberry Pi USB port.

In the Raspberry Pi, with soft-float Debian wheezy distro, we have installed Java Embedded Suite 7.0. For now, we are just reading the measures.

Now, if you keep on reading this second part, we are going to create the embedded server in the Raspberry Pi, with the following main tasks: 
  • Back-end task: it will repeatedly read the serial port to get the measures from the Arduino and store them in a database.
  • Web services: it will respond to web requests returning the current values measured.
  • RESTful web services: it will respond to REST requests returning lists in json format with the data measured between two dates.
And then we will create the JavaFX based client application, that will mainly:
  • Connect to the server to get last measures and show them in a LED Matrix Panel control.
  • Connect to the server to get a list of measures between two dates, showing them in a chart. 
  • Show the status of the connection anytime.
Embedded application development is fundamentally cross-platform work. The target device on which the application will be deployed, the Raspberry Pi in this case, doesn't have the hardware resources to support development tools such as Netbeans. Therefore, we must build on our host computer, copy the required build artifacts to the target, and debug and tune the application running on the target. 

1. Setting the Environment

Basically, we should recreate the embedded environment in our PC, so we can create the project in our favourite Java IDE, compile and build it, and then move the jar/war files by ssh to the Raspberry Pi, where we'll use the provided scripts by the Java EmbeddedSuite to run the application. 

As pointed out here, we need at least JDK 1.7, NetBeans 7.1 and Ant 1.8. In that case, follow the next steps to add the required Ant variable JES_HOME:
  • Download JES from here, in case you haven't done it yet following Part I of this series.
  • Unzip jes-7.0-ga-bin-b11-linux-arm-runtime-15_nov_2012 and move all its content to a local folder like C:\Java
  • In Netbeans go to Tools, select Ant Variables, click Add, and define JES_HOME and browse to the folder "jes7.0", and click Ok.

Now we can open the embedded samples projects on NetBeans and build them. We can make any change, rebuild again, and send them by ssh to the Pi, and run them.

2. Modifying HelloService sample

Open this project and check the content of the Ant file build.xml. You will see several customized targets. The first one is used to set the javac compiler arguments, specifying the rt.jar from JES_HOME instead from the regular JAVA_HOME.

<target depends="-pre-init,-init-private" name="-init-user">
    <property file="${user.properties.file}"/>
    <property name="javac.compilerargs" value="-bootclasspath ${var.JES_HOME}/jre/lib/rt.jar"/>
    <property name="default.javac.source" value="1.7"/>
    <property name="default.javac.target" value="1.7"/>
</target>

Now I'm going to make a slight change in the Message string of HelloService.java:

@Path("/")
public class HelloService {

    public static final String MESSAGE = "Hello World from <b>ArduinoFX</b> Embedded Server!";
    
    @GET
    @Produces({MediaType.TEXT_HTML, MediaType.TEXT_PLAIN})
    public String getText() {
        return MESSAGE;
    }

    @GET
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public HelloBean getTextAsBean() {
        return new HelloBean(MESSAGE);
    }
}

Build the project, and send jar/war files to /usr/java/jes7.0/samples/helloservice/dist/ in your Pi, by ssh. Please check Installation in the first post in case you haven't installed Java Embedded Suite on your Raspberry Pi yet.



Now on your Pi run the sample with the gfhost script and the path to the war file:

cd /usr/java/jes7.0/samples/dist/run/
./gfhost.sh ../../helloservice/dist/helloservice.war


Note it takes around one minute to deploy, so be patient.

Now invoke the service from your web browser. The first time it's called, takes around 15 seconds to respond, but subsequent calls are faster.



Finally, on the server, click Enter to undeploy.
 
3. Embedded Server

Now it's time to create from the scratch our server, with a little help of the provided samples.

These are the steps you need to follow carefully in order for the server to run in your Pi with Java Embedded Suite 7.0.

1. Copy RXTXcomm-2.2pre2.jar to JES_HOME/jre/lib/ext local folder. Do the same in the Pi, copying it to /usr/java/jes7.0/jre/lib/ext.

2. In your Pi, edit /usr/java/jes7.0/samples/dist/run/config.sh and create this variable:

JES_EXT_CLASSPATH="$JES_HOME/jre/lib/ext/RXTXcomm-2.2pre2.jar"

and add it to the classpath:

JES_CLASSPATH="$JES_JAVADB_CLASSPATH:$JES_GLASSFISH_CLASSPATH:

   $JES_JERSEY_SERVLET_CLASSPATH:$JES_EXT_CLASSPATH"

3. Now in Netbeans create a new Java SE application. I'll name it Embedded. Uncheck Create main class.

4. Edit project properties. In Sources, change Source Packages to src/java. In Libraries, add the following jars:
  • JES_HOME/javadb/lib/derby.jar
  • JES_HOME/glassfish/lib/glassfish-jes.jar
  • JES_HOME/jersey/lib/jsr311-api.jar
  • JES_HOME/jre/lib/ext/RXTXcomm-2.2pre2.jar
Finally, in Build/Packaging, uncheck Copy Dependency Libraries

5. Create Embedded/src/webapp and Embedded/src/webapp/WEB-INF folders, and copy web.xml from HelloSuite/src/webapp/WEB-INF.
 
6. Edit build.xml from HelloSuite sample, copy the four last targets, and paste them at the end of Embedded/build.xml. In this way rt.jar will be selected from JES_HOME to compile the project, and the war will be created. In the last two targets change hellosuite.jar for embedded.jar and hellosuite.war for embedded.war.

7. Add a class with all the required JAX-RS web services, com.jpl.embedded.EmbeddedREST. Basically it should have two services: 
  • Return the last reading of the sensor, in json format.
  • Return an array of readings between two dates, in json format.
8. Add a class com.jpl.embedded.JAXRXConfig to register this latter class:

@ApplicationPath("/")
public class JAXRXConfig extends Application {

    @Override public Set<Class<?>> getClasses() {
        final Set<Class<?>> classes = new HashSet<Class<?>>();
        // register root resource the first time there is a REST request
        classes.add(EmbeddedREST.class);
        return classes;
    }
}

This servlet must be registered in the web.xml file, modifying the servlet from HelloSuite:

<servlet>
   <servlet-name>com.jpl.embedded.JAXRXConfig</servlet-name>
</servlet>

9. Finally, add a servlet com.jpl.embedded.ConfigServlet. It must be loaded just after deploying the project, so the database is created (only the first time) and the connection is established. Also we start a scheduled task to read the serial port, and storing the measures periodically. When the project is undeployed, it should stop this task and close the serial port.

In order to load RXTXcomm, which uses native code, a system property must be set before starting the task:

@WebServlet(urlPatterns={"/ArduinoOnline"}, loadOnStartup=0)
public class ConfigServlet extends HttpServlet {

    static {
        System.setProperty( "java.library.path", "/usr/lib/jni" );
    }
    
    private XBee xbee;
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        
        // Initialize BDD
        CStore.getInstance();
        
        // Initialize Serial communication, starts reading port
        xbee=new XBee();
    }
    
    @Override public void destroy() {
        xbee.disconnect();
    }
}

10. The rest of auxiliary classes we'll use are:
  • com.jpl.embedded.comms.Serial. Connect to serial port and read measures from Arduino, via XBee antennas.
  • com.jpl.embedded.model.BeanHT. JavaBean with temperature and humidity values and time of reading.
  • com.jpl.embedded.model.CSensor. Singleton with the most recent BeanHT read.
  • com.jpl.embedded.service.CStore. Singleton to open and close the connection to the database, and to write and read BeanHT values.
  • com.jpl.embedded.service.XBee. Open and close the connection to the serial port, and start a scheduled task to record BeanHT values every 30 seconds in the database.
All the source code for java classes mentioned in points 7 to 10 can be found in my GitHub repository. Feel free to clone it and use the code to test your own embedded server.

So finally, we can compile and build the project. If everything is in place, we could send embedded.jar and war to /usr/java/jes7.0/samples/embedded/dist in the Pi by ssh.

11. To deploy the server, in your Raspberry Pi:

cd /usr/java/jes7.0/samples/dist/run/
./gfhost.sh ../../embedded/dist/embedded.war

Note the deploy takes around one minute. After that, the servlet is initialized, so the database connection is established and the serial port connection is opened. The background task starts and the measures coming from the Arduino are read and stored in the database.



12. Now we can test the different web services, from a browser. 

Test the servlet. It takes around 18 seconds to load, but only the first time.



Test the RESTful web services. The first run takes around 80 seconds to complete.





Finally, to undeploy the server, just press <Enter> twice on the terminal. All the tasks will be smoothly stopped, and serial port and database connections will be closed.



4. The JavaFX GUI

Now we are going to design the JavaFX based client, a simple UI with three main tasks:
  • Connect to the embedded server to get last measures and show them in a MatrixPanel control.
  • Show the status of the connection anytime.
  • Connect to the server to get a list of measures between two dates, showing them in a chart. 
User Interface

First, let's start by describing the user interface. With the help of the JavaFX Scene Builder the basic layout is done.


There're three main boxes, ready to hold several custom controls from the JFXtras project, that will be added from the controller:
  • A top VBox for the MatrixPanel control. This LED panel like control will show the last values of temperature and relative humidity read from the server every 30 seconds.
  • A central HBox for the CalendarTextField and ChoiceBox controls. These controls will allow the user to choose an initial date and an ending date to ask for the values stored in the database in the server. The ChoiceBox allows you to select the number of items readed between these two dates.
  • A bottom HBox for the SimpleIndicator. It will simply show if the connection is established to the server (green) or not (red).
In the controller class, these controls are first created, and later are added when the initialize method is called.
 
    private final MatrixPanel animatedPanel = 
          MatrixPanelBuilder.create()
                            .ledWidth(192).ledHeight(18)
                            .prefWidth(650.0).prefHeight(400.0)
                            .frameDesign(FrameDesign.DARK_GLOSSY)
                            .frameVisible(true)
                            .build();
    
    private final CalendarTextField lStartCalendarTextField = 
                                new CalendarTextField().withShowTime(true);
    private final CalendarTextField lEndCalendarTextField = 
                                new CalendarTextField().withShowTime(true);
        
    private final SimpleIndicator indicator = SimpleIndicatorBuilder.create()
                               .prefHeight(40).prefWidth(40)
                               .innerColor(Color.rgb(0,180,0).brighter())
                               .outerColor(Color.rgb(0,180,0).darker())
                               .build();
    @Override
    public void initialize(URL url, ResourceBundle rb) {
   
        vbox.getChildren().add(0,animatedPanel);
        hbox.getChildren().addAll(lStartCalendarTextField,lEndCalendarTextField);
        hbox.getChildren().add(sizeChoiceBox);
        hboxStatus.getChildren().add(0, indicator);

        ...
    }

So when the application is launched, it will look like this:


Rest services

When the application starts, a scheduled task is launched with the purpose of repeatedly ask the server for the last values of temperature and humidity. This values are then passed to the content of the matrixpanel.

    private long EVENT_CYCLE = 30000; // ms
    private final ScheduledExecutorService scheduler = 
                    Executors.newSingleThreadScheduledExecutor();
    private ScheduledFuture scheduleAtFixedRate = null;
    
    @Override
    public void start(Stage stage) throws Exception {
        
        scheduleAtFixedRate = scheduler.scheduleAtFixedRate(new LastHT(), 0, 
                               EVENT_CYCLE, TimeUnit.MILLISECONDS);
        ...
   }

Where LastHT class perform a web request to http://<IP>:<PORT>/embedded/last REST service and reads the last measured values from response in json format, deserializing it to a BeanHT object.

This object is then stored, and if any of temperature or humidity have changed, the matrixpanel content is updated. For that, a JavaFX thread must be used.
 
Platform.runLater(new Runnable() {

    @Override
    public void run() {
        Content contentTemp = ContentBuilder.create()
                        .color(MatrixColor.GREEN)
                        .type(Type.TEXT)
                        .txtContent("Temperature: " + String.format("%.1f",
             MonitoringServiceStub.getInstance().getLastMeasure().getTemp()) + " ºC")
                        .font(MatrixFont.FF_8x16).fontGap(Gap.SIMPLE)
                        .origin(0, 1).area(0, 0, 191, 18)
                        .align(Align.RIGHT).effect(Effect.SCROLL_LEFT)
                        .lapse(20).postEffect(PostEffect.PAUSE)
                        .pause(3000).order(RotationOrder.FIRST)
                        .build();
        ...
        animatedPanel.setContents(Arrays.asList(contentTemp, contentHum));
    }
});

When the user clicks on the Evaluation button, the request for a list of values between two dates starts. But since the server is a tiny computer with low CPU power, it will take some time to be performed. For that reason, we need to set this as a task from a service.

Service<void> chartService=new Service<void>(){

    @Override
    protected Task<void> createTask() {

        return new Task<void>(){

            @Override
            protected Void call() throws Exception {

                ChartHT ht=new ChartHT(
                      lStartCalendarTextField.getValue().getTimeInMillis(),
                      lEndCalendarTextField.getValue().getTimeInMillis(),
                      sizeChoiceBox.getSelectionModel().getSelectedItem().toString());
                
                ht.run();

                return null;
            }                    
        };
    }
};

@FXML
public void getEvolution(ActionEvent a){        
    chartService.restart(); 
}

The way we will know the task has been completed (maybe a minute later) is looking for changes in the stateProperty of the service. 

When its value reach State.SUCCEEDED, we can proceed with creating the chart and plotting the data deserialized from the json array returned by the server, again in a new JavaFX thread:

chartService.stateProperty().addListener(new ChangeListener<state>(){

    @Override
    public void changed(ObservableValue ov, State t, State t1) {
        if(t1==State.SUCCEEDED) {

            Platform.runLater(new Runnable() {

                @Override
                public void run() {

                    final CategoryAxis xAxis = new CategoryAxis();
                    final NumberAxis yAxis = new NumberAxis();

                    final LineChart<String,Number> lineChart = new 
                             LineChart<String,Number>(xAxis,yAxis);

                    XYChart.Series series1 = new XYChart.Series();
                    series1.setName("Temperature (ºC)");
                    XYChart.Series series2 = new XYChart.Series();
                    series2.setName("Relative Humidity (%)");

                    for(IObservableMeasure d : 
                          MonitoringServiceStub.getInstance().getChartMeasures()){
                        series1.getData().add(new XYChart.
                                  Data(d.getTime(),d.getTemp()));
                        series2.getData().add(new XYChart.
                                  Data(d.getTime(),d.getHum()));
                    }

                    lineChart.getData().addAll(series1,series2);

                    hbox2.getChildren().add(1,lineChart);
                }
            });
        }                
    }
});

All the source code of this project is in my GitHub repository. Feel free to clone it, and test it. Once you have installed the embedded server in your Raspberry Pi, you will need to set the IP address and port in the client:

public class MonitoringServiceStub implements MonitoringService {    
    /* SET YOUR SERVER IP AND PORT HERE */
    public static final String urlServer="http://192.168.0.39:8080";  
    ...

Finally, this is a short video of the JavaFX client in action.


Conclusion

Here I conclude this new proof of concept. As a recap, we have communicated an Arduino with a RHT03 temperature and relative humidity sensor and a XBee antenna, wirelessly to a second XBee antenna, plugged via a XBee Explorer to a Raspberry Pi USB port.

In the Raspberry Pi, with soft-float Debian wheezy distro, we have installed Java Embedded Suite 7.0. We have created an embedded server in the Pi, with the following main tasks: 
  • Back-end task: it repeatedly reads the serial port to get the measures from the Arduino and store them in a database.
  • Web services: it responds to web requests returning the current values measured.
  • RESTful web services: it responds to REST requests returning lists in json format with the data measured between two dates.
And then we have created the JavaFX based client application, that mainly:
  • Connects to the server to get last measures and show them in a LED Matrix Panel control.
  • Connects to the server to get a list of measures between two dates, showing them in a chart. 
  • Shows the status of the connection anytime.
If you have followed this two part series post, I hope it has caught your attention, and you're willing to give it a try. Let me say here: PLEASE, DO IT AT HOME!!

I'd love to hear from you if you have any questions regarding the setup of this project, or any part of it.

Thursday, December 13, 2012

ArduinoFX: A JavaFX GUI for Home Automation with Raspberry Pi and Arduino. Part I

Hi there!

I'll make a little break to the NXTLegoFX series posts, and blog about a very similar application I've made the last week: ArduinoFX. It's a JavaFX based simple GUI for remote monitoring and control of sensors, intended mainly for home automation. Though, to be fair, it's mainly a proof of concept.

More or less, both JavaFX applications share the same architecture, though the difference is now the back-end: instead of the Mindstorms Lego NXT now we've got an Arduino Uno with a low cost digital humidity and temperature sensor, RHT03.

Also, as a server, instead of a regular computer, I'll be using a Raspberry Pi, a single-board tiny computer with an ARM1176JZF-S CPU, powered with the new Java Embedded Suite 7.0, just released by Oracle in October 2012.

And for comunnications, instead of leJOS and Bluetooth, Arduino and Raspberry Pi will be connected wirelessly by two XBee antennas, using ZigBee protocol.

I'll try to describe briefly each and everyone of this components, focusing in how to install what's needed, and with each one ot them I'll make a simple test, so this could serve as a guide for thouse of you who want to give it a try.

This first part deals with all the hardware stuff, and the basic software required to work out, and I leave for Part II the JavaFX application and the embedded server development. 

Disclaimer: I'm not responsible in any way for what you do on your spare time with what I say here! The following procedures are provided without any warranty. Also, you should know by now that I'm not related to any of the companies mentioned in this blog.

Bill of Materials

Main components requiered to follow this guide:




 1. Arduino

I'm using an Arduino Uno, revision 3, a microcontroller board based on the ATmega328, with 14 digital input/output pins, 6 analog input pins, USB connection, power jack, operating at 5 V, with 32 kB of flash memory and a 16 MHz clock speed. 

It can be programmed easily with its own IDE. Its language is an implementation of Wiring. Programs are written in a simplified version of the C++ language, although only two main functions are needed to make a runnable program: setup(), a function run once at the start of a program, and loop(), a function called repeatedly until the board is powered off. 

For monitoring temperature and relative humidity values in my room, I have a DHT22 sensor, also known as RHT03. You can find here the schematics and here an open source library to read these values. You must install it in the libraries folder of Arduino IDE, before compiling your project.

Using a breadboard, connect pin 1 of the sensor to the 5 V pin of Arduino board. Pin 2 goes to Arduino pin 7, using a 1 kΩ pull-up resistor. Pin 4 goes to Arduino board ground. There're few blogs out there where you can find more detailed instructions, like this.

Let's add now a XBee antenna, so the data can be send remotely to a second antenna plugged in another Arduino or in a XBee Explorer plugged in the USB port of a computer. To connect the antenna with the Arduino I'll use an Arduino XBee Shield, from Libellium, though you can use other similar shields, or even plug the XBee on the breadboard through a breakout board.

Here it's the schematics done with the open-source tool Fritzing. The XBee is plugged on to the shield, and this is plugged on Arduino (according to the dotted purple line in the picture below). Note also the wire from pin 2 is reconnected through pin 7 on the shield.

Important tips (that work for me, at least): 
  • Never plug/unplug the shield with the Arduino powered on. 
  • Always power off, and then unplug the shield before loading the code to the Arduino. 
  • Then plug the board to the USB port, download the code, and unplug the USB. 
  • Finally, plug the shield and then power on. 
  • Remember that the jumpers on the shield must be on the XBee pins (on the two pins towards the interior of the board, see the picture below).

And this is how I did it.

Code

This is the code to make the Arduino microcontroller print the values of the measures taken by the sensor through the serial port:

#include <dht22.h>
#include <stdio.h>

// Data wire is plugged into port 7 on the Arduino
#define DHT22_PIN 7
// Setup a DHT22 instance
DHT22 myDHT22(DHT22_PIN);
char buf[128];

void setup(void)
{
  Serial.begin(9600);
  Serial.println("DHT22 Library Loaded");
}

void loop(void)
{ 
  DHT22_ERROR_t errorCode;
  // minimum 2s warm-up after power-on, then read every 2 seconds.
  delay(2000);  
  errorCode = myDHT22.readData();

  switch(errorCode){
      case DHT_ERROR_NONE:                
             sprintf(buf, "{%hi.%01hi,%i.%01i}",myDHT22.getTemperatureCInt()/10,
               abs(myDHT22.getTemperatureCInt()%10),
               myDHT22.getHumidityInt()/10, myDHT22.getHumidityInt()%10);
             Serial.println(buf);
             break;
      case DHT_ERROR_CHECKSUM:      Serial.print("check sum error "); break;
      case DHT_BUS_HUNG:            Serial.println("BUS Hung "); break;
      case DHT_ERROR_NOT_PRESENT:   Serial.println("Not Present "); break;
      case DHT_ERROR_ACK_TOO_LONG:  Serial.println("ACK time out "); break;
      case DHT_ERROR_SYNC_TIMEOUT:  Serial.println("Sync Timeout "); break;
      case DHT_ERROR_DATA_TIMEOUT:  Serial.println("Data Timeout "); break;
      case DHT_ERROR_TOOQUICK:      Serial.println("Polled to quick "); break;
    }
  }
}

To download the compiled code, just open the Arduino IDE, create a new sketch, paste the code and verify it. Then plug the Arduino without the shield, and without any wires connected, and select your serial port in Tools->Serial Port. Then download the compiled code, and unplug the USB cable.


Test #1

Let's make a simple test, before going any further, to check the Arduino works properly. Connect the sensor but not the shield nor the XBee, so sensor's pin 2 goes straight to pin 7 in the Arduino board. Plug again the USB cable, and in the Arduino IDE go to Tools->Serie Monitor, and you should see the measures appearing on the screen:


2. XBee

In order for the antennas to be connected, it is neccessary to establish a ZigBee network between them. For that, Digi International software X-CTU is required (only works in Windows, for other OS see xbeeconfigure sketch). 

Also, it's recommended to use a XBee Explorer, so just by attaching a mini USB cable you will have direct access to the serial and programming pins on the XBee unit. Otherwise, you'll have to use the access through the Arduino board, but changing the jumpers in the shield to the USB position and removing the microcontroller, which is always a little bit risky.


Anyway, for our project we'll need this Explorer, as we intend to connect it to the Raspberry Pi USB port.

There are quite good blogs out there showing how to configure both antennas (like this or this), so I'm going to give just a short description of how I do it.

Coordinator XBee #1

First, with the XBee #1 in the Explorer, plugged to the USB port, open the X-CTU application and select the correct USB serial port from the list displayed in the PC-Settings tab.

Then, go to Modem Configuration tab, uncheck Always update firmware and click on Read button and wait a few seconds. A valid name should appear under Modem label. Otherwise, click on Download new versions button, and try again.

Once you've got a right modem (in my case XB24-ZB), on the function set list, select Zigbee Coordinator AT. See left picture below. This one should be afterwards plugged to the Raspberry Pi.

A few parameters must be set:
  • Networking, ID: Pan ID. The network ID, 0 to 0xFFFF. For instance, 1111
  • Addressing. NI: Node Identifier, for instance COORDINATOR
  • Addressing. DL: Destination Address Low: 0 
  • Serial Interfacing. BD Baud Rate, 9600

then click Write button, and these parameters will be stored in your XBee #1. Close X-CTU and unplug the Explorer.


Router XBee #2

Plug on the Explorer the second XBee, and start again X-CTU. Choose the serial port and read the modem parameters. Now select Zigbee Router AT and set these parameters:
  • Networking, ID: Pan ID. The network ID, the same as #1: 1111
  • Addressing. NI: Node Identifier, for instance ARDUINO
  • Addressing. DL: Destination Address Low: 0 
  • Serial Interfacing. BD Baud Rate, 9600
Check that the Operating Channel is the same for both (CH=19). Then click Write button, and these parameters will be stored in your XBee #2. See right picture above. Close X-CTU and plug this anntena on the Arduino shield.

Note: You can use also AT commands in the Terminal Tab, just by entering +++ and waiting for an OK response. Then you can get or set the usual parameters, with AT and the parameter code: ATID, ATNI, ATDL, an so on.

Though this configuration is pretty simple, it gives you the idea of how you could set a mesh of nodes around the same coordinator, in case you have several arduinos around your house, with their own XBee antenna.


 Source: http://www.rfwireless-world.com/Tutorials/Zigbee_tutorial.html

Test #2

So let's now check if the ZigBee network is working properly. Let's plug the shield with the XBee #2 on the Arduino board, check the RHT03 connections (its pin 2 now goes through pin 7 in the shield) and power on the Arduino. Then plug the XBee #1 via Explorer to the PC and start X-CTU, select serial port, read the modem and go to Terminal tab to see the readings that should appear:

Otherwise, please check carefully the previous steps. Note that instead of X-CTU you can use any hyperterminal like software in your OS.

3. Raspberry Pi

First of all, we need to set the Raspberry Pi. For that you can follow this guide, or this. Briefly, you need:
  • A Raspberry Pi model B (with 256 or 512 MB RAM)
  • A USB keyboard and USB mouse (optional)
  • A display with HDMI, a HDMI cable, or a display with DVI and a HDMI to DVI conversor
  • A power supply of 5 V and minimum 0.7 A, with micro USB connector (some mobile chargers will do).
  • And a SD card, 4 or 8 GB, not-generic, class 6 to 10 recommended.

In order to install Java Embedded, for now we must use soft-float Debian wheezy image. Download it from here here, extract it and write it to the SD with Win32DiskImager for Windows. For other OS please check here.

Once the SD card is ready, insert it in the socket below the Raspberry Pi, connect the display and the keyboard and power on. Then it should boot up, and enter in the raspi-config menu, where we are going to config a few things (look here for a further explanation):
  • expand-rootfs: Press ok to use the whole SD card space.
  • change_locale: from en_GB.UTF8 to es_ES.UTF8 in my case, select yours.
  • change_timezone: Select continent (Europe) and major city (Madrid, in my case).
  • memory_split: you can give only 32MB to video, leaving the rest for the ARM, as in this case we're developing a server. You can change this later on.
  • ssh: Press enable to allow ssh connections.
Select Finish and the Pi will reboot.

Network

Now let's setup the network. My advise is that you set a static IP address. For that (look here for further instructions), edit with root privileges this file:

sudo nano /etc/network/interfaces

And change dhcp for static, adding your IP and LAN data configuration. This is mine:

auto lo
iface lo inet loopback
iface eth0 inet static
address 192.168.0.39
netmask 255.255.255.0
gateway 192.168.0.1

Save (Ctrl+O) and exit (Ctrl+X). Now edit this other file:

sudo nano /etc/resolv.conf

and add your DNS nameservers. This is my data:

nameserver 192.168.0.1
nameserver 62.42.230.24
nameserver 62.42.63.52

Save and exit, reboot and plug in the ethernet cable. Now we're able to install software from the repository, like a VNC server, so we can access remotely to the Pi headless, without display, keyboard nor mouse:

sudo apt-get install tightvncserver

After it has been installed, type:

sudo /usr/bin/tightvncserver

When prompted, select a password for remote users. Allow it to start at boot time, editing the file: 

sudo nano /etc/rc.local

At the end type:

su -c "/usr/bin/tightvncserver -geometry 1280x1024" pi

Save, exit and reboot. Now from your favourite VNC client try to connect to the Pi, using its IP address, the port 5901, and the specified password for remote users.

Test #3

In order to read serial port communications install minicom:

sudo apt-get install minicom

Then plug in the XBee #1 via Explorer in the USB port of the Pi. Power on the Arduino, and start minicom:

minicom -b 9600 -o -D /dev/ttyUSB0

You should see the readings appearing on the screen:


To close it, press Ctrl+A+Z+X.

4. Java Embedded Suite 7.0

It was launched by Oracle at the end of September 2012. As you can read here,
  • Oracle Java SE Embedded 7 provides runtime for Java embedded applications
  • Java DB provides a database to store local content securely
  • Glassfish for Embedded Suite provides an application server for Web pages
  • Jersey RESTful Web Services Framework for hosting and accessing Web services.
with the main goal of optimization for embedded devices and server systems.

 
Installation

In your PC, download the distribution for Raspberry Pi (ARM v6 soft-float) from here and the samples from here. Now connect by ssh and go to directory /home/pi and make a new directory there, install. Then copy both files to /home/pi/install.

Now on your Pi, go to /home/pi/install and unzip the code bundle, which creates /home/pi/install/jes7.0/Unzip also the samples bundle, which creates /home/pi/install/jes7.0/samples/. Then delete the bundles:


unzip jes-7.0-ga-bin-b11-linux-arm-runtime-15_nov_2012
rm jes-7.0-ga-bin-b11-linux-arm-runtime-15_nov_2012
unzip jes-7.0-ga-b11-linux-samples-15_nov_2012.zip
rm jes-7.0-ga-b11-linux-samples-15_nov_2012.zip

Now with root privileges create /usr/java directory and move /home/pi/install/jes7.0 to /usr/java:

sudo mv jes7.0 ../../../usr/java

Finally, we need to add to PATH the route for Java, and add a JAVA_HOME variable. For that edit the file:

sudo nano /etc/bash.bashrc

And nearly at the end, add:

export PATH=/usr/java/jes7.0/jre/bin:$PATH
JAVA_HOME="/usr/java/jes7.0/jre"
export JAVA_HOME

Save and exit, and reboot. Login, and check if everything is fine by typing java -version:


You should try now all the samples from the Suite that come within the bundle, following this guide, going to /usr/java/jes7.0/samples/dist/run directory.

Test #4
 

The next step in our tests is check if we can read the serial port with Java Embedded. For that we'll make a small project to read the serial port. First of all, we need to install RXTXcomm library and test java embedded serial reading with xbee.jar.

sudo apt-get install librxtx-java

It will install /usr/share/java/RXTXcomm-2.2pre2.jar. Back on your PC, create a new project in your Java IDE, with the class listed below, add this jar (copy it by ssh from your Pi), compile and build the project, and send it to the Pi by ssh (both jars), to /home/pi/java, for instance.

public class EmbSerial {

    private CommPort m_commPort=null;
    private SerialPort m_serialPort=null;
    
    public void connect( String portName ) throws Exception {
        
        CommPortIdentifier portIdentifier = 
                           CommPortIdentifier.getPortIdentifier(portName);
        if(portIdentifier.isCurrentlyOwned()){
            System.out.println( "Error: Port is currently in use" );
        } else {
            int timeout = 2000;
            m_commPort = portIdentifier.open(this.getClass().getName(), timeout);
            if( m_commPort instanceof SerialPort ) {
                m_serialPort = (SerialPort)m_commPort;
                m_serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8,
                                SerialPort.STOPBITS_1, SerialPort.PARITY_NONE );
                final InputStream in = m_serialPort.getInputStream();
                new Thread(){                    
                    @Override public void run() {
                        byte[] buffer=new byte[1024];
                        int len = -1;
                        try {
                            while((len = in.read(buffer))>-1) {
                                System.out.print(new String(buffer,0,len));
                            }
                        } catch( IOException e ) {
                            System.out.println("Error reading: " + e.getMessage());
                        }
                    }  
                }.start();
            } else {
                System.out.println( "Error: Not a serial port" );
            }
        }
    }
        
    public void disconnect(){
        if(m_serialPort!=null){
            m_serialPort.removeEventListener();
            m_serialPort.close();
        }
        if(m_commPort!=null){
            System.out.println("Port closed");
            m_commPort.close();            
        }
    }
    
    public static void main(String[] args) {        
        final EmbSerial serial=new EmbSerial();        
        try {
            serial.connect("/dev/ttyUSB0");
        } catch(Exception e) {
            System.out.println("Error connecting: " + e.getMessage());
            return;
        }
        
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override public void run() {
                serial.disconnect();
            }
        }));
    }
}

Run it by:

java -Djava.library. path=/usr/lib/jni -jar EmbSerial.jar

You should get the readings on the terminal. Press Ctrl+C to finish.



Part I conclusion

Well, if you have followed closely the detailed procedures and performed successfully all the tests, congratulations!

Now you're ready for bigger adventures, so in the next part of this series we'll create the embedded server in the Raspberry Pi: 
  • Back-end task: it will repeatedly read the serial port to get the measures from the Arduino and store them in a database.
  • Web services: it will respond to web requests returning the current values measured.
  • RESTful web services: it will respond to REST requests returning lists in json format with the data measured between two dates.

And we will create the JavaFX based client application, that will mainly:
  • Connect to the server to get last measures and show them in a LED Matrix Panel control.
  • Connect to the server to get a list of measures between two dates, showing them in a chart. 
  • Show the status of the connection anytime.
This is a preview of this app:


Please, have a breath after this really long post and, if you're ready, continue reading Part II. In the meantime, I'd love to hear any comments from you.

Monday, November 26, 2012

NXTLegoFX: JavaFX based application to play with a Lego Mindstorms NXT. Part I

Hi there!

This is the first of a series of posts related to a new application in JavaFX I'm developing with the initial intention of interacting with a Lego® Mindstorms® NXT.

The first purpose of this app is, as a preliminary proof of concept, to show how easy is to integrate different software and hardware technologies. For that I'll start by introducing you briefly to the following topics:
  • Lego Mindstorms NXT: computational module, with graphical programming environment, initially intended for kids to play with robotics.
  • leJOS, firmware to replace the NXT one, includes a tiny Java Virtual Machine, and allows you to programm the robot with Java.
  • Glassfish, application server to run Java apps and RESTful web services, implemented using HTTP and the principles of REST.
  • JavaFX, the new Java platform for creating and delivering rich Internet applications.
If you're already an expert in any of these items, please, feel free to skip to the last part of the post. If you're not, there're plenty of blogs out there covering deeply any of them, as this post pretends to be just a glance.

Please notice I don't consider myself in any way an expert in any of the topics I'm covering here. Quite on the contrary, this is an exercise of traversal and shallow engineering.

Before I begin, it's fair I mention the guy whose idea I've taken to start with all of this: Sébastien Stormacq @Sebtso. His entry blog Put a Java Brain in your Lego Robot, from 2010, opened my eyes, with his code I didn't need to start from the scratch. Also I want to thank the team members of leJOS community. They keep vey much alive this fantastic project.

1. The schematics

For starters, let's see a simple overview of the intended project and the technologies involved.


2. Lego Mindstorms

As I said before, there are plenty of blogs out there describing in full detail what it is or what you can do with it, so just for the very beginners here it's in short the main specs.

Lego Mindstorms NXT is a programmable robotics kit released by Lego in 2006. The NXT 2.0 was released in 2009. The kit includes 3 servo motors and 4 sensors (ultrasonic, 2 touch and color), 7 connection cables, a USB interface cable, and the NXT Intelligent Brick, which is the brain of a Mindstorms machine.
The brick is the computational module:
  • Based on a 32-bit Atmel AT91SAM7S256 ARM7TDMI microcontroller (ARMv4) with 64 kB RAM, 256 kB flash and 48 MHz.
  • Attached to it a 8-bit Atmel AVR ATmega48 controller via I2C interface, 512 Bytes RAM, used to collect data from sensors and control motors.
  • With one USB 2.0 port, Bluetooth support and a 100x64 pixels black and white display.
Let me say here that, relatively speaking, the brick architecture could be compared to a quite underrated Raspberry Pi (ARMv6 ARM1176JZF-S 32-bit, 512 MB RAM, 700 MHz) with and Arduino (ATMega328, 8-bit, 2 kB RAM) attached.

On the software side, bundled with the kit comes NXT-G, a graphical programming environment that enables the creation and downloading of programs to the NXT. It is powered by LabVIEW from National Instruments. These programs make use of the standard firmware provided by Lego.


This is intended for kids to start programming, very much like MIT's Scratch. It's open to new blocks related to sensors from third-party providers like HiTechnic or Dexter Industries.

While NXT-G provides a very user friendly approach to programming NXT robots, for more advanced programming with a text-based language, there is plenty of unofficial languages for the NXT out there. Three of the most popular of them are NXC/NBC, RobotC and LeJOS NXJ.

3. LeJOS NXJ

Well, why I've choosen LeJOS NXJ in the first place, a Java based language, instead of the others C based languages it should be clear to a regular reader of this blog.

You can find a complete description of leJOS, the firmware replacement for the official one, and leJOS NXT, the language, here. You can find a full tutorial here.  

Download the latest version (at the time of this post, 0.9.1beta-3) here. Choose Windows (*_win32_setup.exe) or Linux and Mac OS X version (*.tar.gz).

In short, the firmware includes a tiny Java virtual machine, which allows Lego Mindstorms robots to be programmed with Java. So, first of all, you have to flash this firmware. 
Note you'll lose every file stored in the NXT. This process is reversible, and you can restore Lego official firmware afterwards.

To flash the NXT, please follow the detailed Getting Started instructions here according to your operative system. At the end, you'll have your NXT with a brand new leJOS logo and a main menu, quite similar to the official one. Here you'll find detailed instructions of how to use it. 

If you want to use bluetooth for communication, enter in the submenu, select Power On, Visibility On, and enter a 4 digit pin (1234 by default). In your PC, search the device and connect to it. In Windows (my case) this is quite simple and it works smoothly. But in Mac OS X, on the contrary, it seems bluetooth is not working, from what I've heard. Edited: Please, check this Comment from Philipp Dörfler below for instructions to use bluetooth with your NXT in Mac OS.


On the other side, on your PC you'll find in the installation path a bin folder with a bunch of Swing based PC GUI tools, which are batch files like nxjbrowse, to list the files in your NXT, the nxjflashg, to flash the NXT or nxjcontrol, that gives you full control of your NXT:


Programming the NXT

You can either write Java programs that run on the NXT or run on the PC and control the NXT remotely. You can use any Java IDE or just command line tools.

In the installation path you'll find the samples.zip file, which contains samples to download and execute on the NXT (samples folder) or to run them on your PC (pcsamples folder). Ant build scripts are provided. 

Running samples on the NXT

The samples require classes.jar in the classpath. When you run the project from the IDE, a dialog ask you to enter the name of the sample you want to download and run. The view sample, for instance, shows a menu in the NXT LCD and you can read sensors or control motors with the buttons.

Here you have a sneak peek of the code of this program:

package org.lejos.sample.view;
import lejos.nxt.Battery;
import lejos.nxt.Button;
import lejos.nxt.LCD;
import lejos.util.TextMenu;

public class View {
 
    public static void main (String[] aArg) throws Exception
    {
        String[] viewItems = {"System", "Sensors", "Motors", "Exit"};
        TextMenu main = new TextMenu(viewItems, 1, "View Example");
        ...
        int selection;

        for(;;)
        {
            LCD.clear();
            selection = main.select();
            if (selection == 0) // System Info
            {
                LCD.clear();
                LCD.drawString(sys, 0, 0);
                LCD.drawString(batt, 0, 2);
                LCD.drawInt(Battery.getVoltageMilliVolt(), 4, 10, 2);
                LCD.drawString(tot, 0, 3);
                LCD.drawInt((int)(Runtime.getRuntime().totalMemory()), 5, 10, 3);
                LCD.drawString(free, 0, 4);
                LCD.drawInt((int)(Runtime.getRuntime().freeMemory()), 5, 10,4);
                LCD.refresh();

                Button.ESCAPE.waitForPressAndRelease();
            }
            ...
        }
    }
}
As you can see, it's pretty straightforward.

Running on your PC with remote control of the NXT 

In this case, you must add the jar files from lib/pc, pccomm.jar and pctools.jar, and from lib/pc/3rdparty bluecove.jar. But you must not add classes.jar

Here it's one of the samples:

package org.lejos.pcsample.tachocount;
import lejos.nxt.Motor;
import lejos.nxt.Sound;
import lejos.nxt.remote.NXTCommand;
import lejos.pc.comm.NXTComm;
import lejos.pc.comm.NXTCommLogListener;
import lejos.pc.comm.NXTCommandConnector;
import lejos.pc.comm.NXTConnector;

/**
 * @author Lawrie Griffiths and Brian Bagnall
 */
public class TachoCount {
 
    public static void main(String [] args) throws Exception {
        NXTConnector conn = new NXTConnector();
        conn.addLogListener(new NXTCommLogListener() {
            public void logEvent(String message) {
                System.out.println(message);    
            }
            public void logEvent(Throwable throwable) {
                System.err.println(throwable.getMessage());   
            }   
        });
        if(!conn.connectTo("btspp://NXT", NXTComm.LCP)) {
            System.err.println("Failed to connect");
            System.exit(1);
        }
        NXTCommandConnector.setNXTCommand(new NXTCommand(conn.getNXTComm()));

        System.out.println("Tachometer A: " + Motor.A.getTachoCount());
        System.out.println("Tachometer C: " + Motor.C.getTachoCount());
        Motor.A.rotate(5000);
        Motor.C.rotate(-5000);
        System.out.println("Tachometer A: " + Motor.A.getTachoCount());
        System.out.println("Tachometer C: " + Motor.C.getTachoCount());
        conn.close();
    } 
}

Notice that the NXTConnector object uses Lego Mindstorms NXT Communications Protocol LCP to establish communication between PC and the NXT either via bluetooth or via USB. As valid URL for your specific NXT, you can set "btspp://<NXT name>", "btspp://<NXT address>", "usb://<NXT name>" or "usb://<NXT address>". To coonect to any device just use "btspp://" or "usb://".

Look at the other samples, to find out the rest of the possibilities.

4. The Glassfish server

With the ability to communicate to the NXT from a PC, a further step is the installation of a server on the PC to allow the connection of several devices simultaneously to the same NXT(s). 

So I'll try to develop an application, deployed in a Glassfish application server, that will respond to REST requests from these devices, by establishing a synchronized connection with the NXT.

The LegoNXT class

Continuing the previous work of Sébastien Stormacq, based on a singleton object to guarantee one only access to the NXT, regardless the number of users trying to access to it, I'll add a blocking key, so only one request is processed at a time. We must keep in mind that we are targeting a very slow processor. I also add another key to know if the NXT is connected or not.

Here you can see part of the code:

public class LegoNXT {

    private static LegoNXT singleton = null;

    private static boolean legoBlocked=false;
    private static boolean legoConnected=false;
    
    public static LegoNXT getSingleton() {

        final Object lock = LegoNXT.class;

        if (singleton == null) {
            synchronized(lock) {
                singleton = new LegoNXT();                
            }
        }
        return singleton;
    }

    private LegoNXT() {}
    
    public void connect(String NXTName, String Protocol) {
        final String connectString = Protocol.concat("://").concat(NXTName); 
        legoBlocked=true;
        legoConnected=false;
        try {
            Logger.getLogger(getClass().getName()).log(Level.INFO, "Initializing communication with NXT");
            NXTConnector cmd = new NXTConnector();
            cmd.addLogListener(new NXTCommLogListener() {
                @Override public void logEvent(String message) {
                    Logger.getLogger(getClass().getName()).log(Level.INFO, message);
                }
                @Override public void logEvent(Throwable throwable) {
                    Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Exception while talking to NXT brick", throwable);
                }
            });
            
            if(!cmd.connectTo(connectString, NXTComm.LCP)) {
                Logger.getLogger(getClass().getName()).log(Level.WARNING, "Failed to connect");
                legoBlocked=false;
                return;
            }

            NXTCommandConnector.setNXTCommand(new NXTCommand(cmd.getNXTComm()));
            Logger.getLogger(getClass().getName()).log(Level.FINE, "Done");
            legoConnected=true;

        } catch (Exception e) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Exception while initializing connection", e);
            legoBlocked=false;
        }
    }

    public void disconnect() {
        try {
            NXTCommandConnector.close();
        } catch (IOException ex) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
        }
        legoConnected=false;
        legoBlocked=false;
    }
    
    public boolean isLegoBlocked() { return legoBlocked; }
    public boolean isLegoConnected(){ return legoConnected; }

    ...
    /* NXT methods */

The RESTful web services

The first RESTful web service will search for any NXT device connected to the server.

@Path("search")
public class SearchResource {
    @GET
    @Produces("application/json")
    public String getJson() {
        NXTConnectionManager man= new NXTConnectionManager();
        NXTInfo[] search = man.search();        
        JSONObject json = null;
        try{            
            JSONArray arr=new JSONArray();        
            for(NXTInfo info:search){
                JSONObject element = new JSONObject();
                element.put(CData.JSON_RESPONSE_DEVICENAME, info.name);
                element.put(CData.JSON_RESPONSE_DEVICEADDRESS, info.deviceAddress);
                if(info.protocol==NXTCommFactory.USB){
                    element.put(CData.JSON_RESPONSE_PROTOCOL, "USB");
                } else if(info.protocol==NXTCommFactory.BLUETOOTH){
                    element.put(CData.JSON_RESPONSE_PROTOCOL, "BLUETOOTH");
                }
                arr.put(element);
            }
            json = new JSONObject();
            json.put(CData.JSON_RESPONSE_DEVICES, arr);
        } catch(JSONException jse){
            return "{}";
        }
        return json.toString();
    }
}

So when you start the Glassfish server, from any browser you can search for NXT devices connected, and it will answer with a JSON array. In my case, with just one device:


The second service, once you've selected the NXT to connect to, will process a request to ask the NXT for several of its system properties:

@Path("device")
public class DeviceResource {

    private LegoNXT nxt = null;

    @GET
    @Produces("application/json")
    public String getJson(@QueryParam("NXTName") @DefaultValue("NXT") String NXTName,
                          @QueryParam("Protocol") @DefaultValue("usb") String Protocol) {
        String result = null;
        if (nxt == null) {
            nxt = LegoNXT.getSingleton();
        }
        int cont=0;
        while(nxt.isLegoBlocked()){
            try{
                Thread.sleep(100);
                cont+=1;
            } catch(InterruptedException ie) {}
            if(cont>CData.TIME_OUT){ 
                String msg = "Device was blocked";
                Logger.getLogger(getClass().getName()).log(Level.WARNING, msg);
                result = "{ \"status\" : \"" + msg + "\" }";
                nxt = null; 
                return result;
            }
        }
        
        try {
            nxt.connect(NXTName,Protocol);
            result = new JSONStringer().object()
                    .key(CData.JSON_RESPONSE_DEVICENAME)
                    .value(nxt.getDeviceName())
                    .key(CData.JSON_RESPONSE_BATTERYLEVEL)
                    .value((double)nxt.getDeviceBateryLevel())
                    .key(CData.JSON_RESPONSE_BLUETOOTHADDRESS)
                    .value(nxt.getDeviceBluetoothAddress())
                    .key(CData.JSON_RESPONSE_FIRMWAREVERSION)
                    .value(nxt.getDeviceFirmwareVersion())
                    .key(CData.JSON_RESPONSE_VOLUME)
                    .value(nxt.getVolume())
                    .key(CData.JSON_RESPONSE_STATUS)
                    .value("OK")
                    .key(CData.JSON_RESPONSE_TIME)
                    .value(((double)cont)/10d)
                    .endObject().toString();
            nxt.disconnect();
        } catch (Exception ex) {
            String msg = "Can not get device information";
            Logger.getLogger(getClass().getName()).log(Level.WARNING, msg, ex);
            result = "{ \"status\" : \"" + msg + "\" }";
            nxt = null;
        }
        return result;
    }
}

Several other RESTful web services are also added to the server in this way. 

EDITED on the 1st of February of 2013: By request I've uploaded the server code to my GitHub repo, so if you're interested, grab the code here and give it a try!

5. The client requests

Those RESTful web services must be called from the client side, and the returned json string must be processed to extract the contained information. For that I'll use the GSON open source Java library, which allows deserialization of collections or nested classes. So I just need one POJO class with the properties expected to be read from the json string for one device, EntSearch, and one POJO class with an arraylist of the first one, EntDeviceSearch, to collect all NXT devices found and their properties:

public class EntSearch {
    private String devicename;    
    private String deviceaddress;    
    private String deviceprotocol;    
    private int batterylevel;
    private String firmwareversion;    
    private String status;    
    private int volume;    
    private double time;

    /* getters and setters */
}
public class EntDeviceSearch {
    private ArrayList<entsearch> devices = null;

    /* getters and setters */
}

So the request to the server to search for NXT devices, and the deserialization of the response, can be done very easily:
 
     public void search() {
        URL theJsonUrl=null;
        try {
            theJsonUrl = new URL("http://localhost:8080/WebLego/NXT/search");            
        } catch (MalformedURLException ex) {
            System.out.println("URL Error: " + ex.getMessage());
            return;
        }

        String jSonTxt="";
        try {
            InputStream in=theJsonUrl.openStream();
            jSonTxt=IOUtils.toString(in);
        } catch (IOException ex) {
            System.out.println("URL Error: " +theJsonUrl+" "+ ex.getMessage());
            return;
        }

        EntDeviceSearch devices=null;
        try{
            devices=new Gson().fromJson(jSonTxt,EntDeviceSearch.class);
        } catch(JsonSyntaxException jse){
            System.out.println("GSON Error: "+jse.getMessage());
            return;
        }

        if(devices!=null){
            System.out.println("Found Devices: "+devices.getDevices().size());
            for(EntSearch ent: devices.getDevices()){
                ... // process each entry accordingly
            }
        }
        else{
            System.out.println("Search Error");     
        }       
    }
 
6. The JavaFX UI

Finally we reach the User Interface part. Now we only need to desing a fancy scene and connect it to the server via the RESTful web services. As this is a proof of concept, well be adding just a few controls to the scene, in this first version.

I'll use a Tab Pane with two tabs. The first one, labeled Communication, will have a TableView to list all NXT devices found, just by calling the search RESTful web service. Once a row is selected, the other service (device) is called to fetch the data from the picked device.

You should know by now that there're quite fancy custom JavaFX controls in jfxtras.org. Just drop the last available jar (at the time of this writting, jfxtras-labs-2.2-r5-SNAPSHOT.jar) to your project. To show the data values, a Battery, several Leds and an Indicator from JFXtras project will be used.

This is how it looks like the Communication tab, for now:



Values from the NXT such as battery level (in mV), speaker volume level and connection status are read with the device REST service. To set the volume, a new REST service is used.

I should explain here the model behind this application, as it connects the UI part (FXML, the controller, the controls) with the REST requests from the client to the server part. There're a few tricky things here, as the controls values (JavaFX thread) are bound to the readings of the REST responses (standard Java thread).

But due to the clearly exceded length of this post, I must stop here, and leave it to a second part... So let me just add a short video showing what we've got of the NXTLegoFX application so far:



See you in the (hopefully coming soon) next part of this post! Love to hear any comments from you in the meantime.