Showing posts with label IoT. Show all posts
Showing posts with label IoT. Show all posts

Tuesday, January 8, 2013

NXTBeeFX: A JavaFX based app for Raspberry Pi to control a Lego NXT robot wirelessly

Hi there, and happy new year! 

After my last series of posts about ArduinoFX (Java Embedded on the Raspberry Pi with sensors, Arduino and XBee, and a JavaFX based client app, monitoring the measures), it happened that Oracle released JDK 8 (with JavaFX) for ARM Early Access, and finally we all could try JavaFX on our Rasperry Pi

It also happened that I left the NXTLegoFX post pending...

But, now, with JavaFX running on the Raspberry Pi I had to give it a try, so I decided to use again my kid's Lego Mindstorms NXT 2.0 to have something real to interact with, and resume in part the pending post. 

And just at the time of finishing this post, Lego unveiled their new version EV3 for the summer of 2013... so before ditching the NXT, let's have some fun with it!

A few months ago I bought a NXTBee, the naked version without XBee, from Dexter Industries. Please, read their wiki with further explanations, and their downloads section, to find NXT-G blocks for the official NXT firmware.


It's like the XBee shield for Arduino, but for the NXT. So the project was clear to me: connect wirelessly the NXT with the Raspberry Pi via serial communication, and develop a JavaFX based application for the Pi. No server required this time.

Here you have a little overview:


And this is the bill of materials:
 
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. 

 1. LeJOS and NXTBee

I've already talked about leJOS, so you'll know by now it's a firmware that replace the NXT one, including a tiny Java Virtual Machine, so it allows you to program the robot with Java. Please, check this for detailed explanation.

The official distribution (by now 0.9.1 beta 3) was released in February 2012 and it didn't have the classes required to interact with the NXTbee. 

Fortunately, Mark Crosbie has developed NXTBee, a leJOS class to interact with the NXTBee, along with some sample Java code in leJOS showing how to send and receive data. Basically, the NXTBee, attached to Port 4 of the NXT, uses a RS485 serial link. The NXTBee class uses a thread as a data pump, continually polling the RS485 port and storing data received on the port in an internal circular buffer, and reading data from the circular buffer and writing it onto the RS485 port. It returns an InputStream and an OutputStream object which can be used by programs to read and write data to and from the NXTBee.

His work has been included for future versions of leJOS, and you can find it in their repository. For now, in our projects, we just need to include NXTBee.java and CircularByteBuffer.java with the rest of our classes, before compiling and downloading to the NXT.

Test #1. Read from and write to NXT
So as a first test we'll just check communication to and from the NXT with the NXTBee and a XBee, to other XBee plugged to the PC via XBee Explorer, with the help of X-CTU software (or equivalent hyperterminal like program).

Please, first read how to set the proper configuration of the XBee antennas here, in case you haven't done it yet, and change the DL parameter in the Coordinator XBee #1:
  • Addressing. DL: Destination Address Low: FFFF
so it will be able to read and write to the XBee #2.


Now, in Netbeans, we'll make a new project, apart from the leJOS samples project. For that, follow these steps:

1. In your usual Java projects folder, create a new folder named NXTBeeTest1, and copy build.properties and build.xml files you'll find in the folder LeJOS NXJ Samples\org.lejos.example. Also, create a src folder inside.

2. In Netbeans, choose New Project->Java->Java Free Form Project. In Name and Location step, browse to the NXTBeeTest1 folder. Change project name to NXTBeeTest1

 
In Build and Run Actions step, in Run Project, select uploadandrun. In Source Package Folders step,  press Add Folder, and select src folder. Finally, in Java Sources Classpath step, press Add Jar/Folder, and browse to C:\Program Files\leJOS NXJ\lib\nxt\classes.jar. Click finish.


3. In the src folder, create package lejos.nxt.addon, download NXTBee.java from here and add it there. Also, create package lejos.internal.io and download CircularByteBuffer.java from here to that folder. Create package org.lejos.jpl.nxtbee and add a new class, NXTBeeTest1 with the following code:

public static void main(String[] args) {

    NXTBee nb = new NXTBee(9600, true, true);

    Thread t = new Thread(nb);
    t.setDaemon(true);
    t.start();

    //
    // PART 1. SEND DATA FROM XBEE #1, RECEIVE IT IN XBEE #2 WITH NXTBEE
    // Press RIGHT on the NXT to finish Part 1
    //
    InputStream is = nb.getInputStream();  
    DataInputStream dis = new DataInputStream(is);

    Delay.msDelay(1000);

    LCD.clear();
    LCD.drawString("NXTBee Receiving", 0, 0);

    byte[] b = new byte[20];
    try {
        while(Button.RIGHT.isUp()){
            if(dis.available() > 0) {
                int bytesRead = dis.read(b);
                LCD.drawString("Read " + bytesRead + " bytes",0, 3);
                String s = new String(b);
                LCD.drawString(s, 0, 5);
            }
            Delay.msDelay(1000);
        }
    } catch(Exception e) {}
    try {
        dis.close();            
    } catch (IOException ex) {}

    //
    // PART 2. SEND DATA FROM XBEE #2 WITH NXTBEE, RECEIVE IT IN XBEE #1
    // Press ENTER on the NXT to finish Part 2
    //
    OutputStream os = nb.getOutputStream();
    DataOutputStream dos = new DataOutputStream(os);

    SensorPort sp = SensorPort.getInstance(0);
    TouchSensor touch = new TouchSensor(sp);

    LCD.clear();
    LCD.drawString("NXTBee Sending", 0, 1);

    try {
        while(Button.ENTER.isUp()){
            if (touch.isPressed()){
                LCD.drawString("Touch on ", 0, 2);
                dos.writeBytes("Touch on");
            } else {
                LCD.drawString("Touch off ", 0, 2);
                dos.writeBytes("Touch off");
            }
            dos.writeByte(13); dos.writeByte(10); // CRLF                
            Delay.msDelay(2000);
        }
    } catch(Exception e) { }
    try {
        dos.close();            
    } catch (IOException ex) {}
}

4. Edit build.properties and change these two lines:

main.class=org.lejos.jpl.nxtbee.NXTBeeTest1
output.basename=NXTBeeTest1

Edit build.xml and change description:

<project name="NXTBeeTest1" default="uploadandrun">
<description>org.lejos.jpl.nxtbee.NXTBeeTest1 build file</description>

Now, plug XBee #2 to the NXTBee, and this one to sensor port #4 on the NXT brick. In port #1 attach also the Touch sensor.

Finally connect the NXT brick to your PC by USB or by Bluetooth and switch on the NXT. Run project. If everything is alright, it should build and download NXTBeeTest1 to the NXT, and start the first part: In the NXT LCD screen you should see "NXTBee Receiving". 


Now, plug the XBee #1 to your PC, via XBee Explorer, and open X-CTU. Read the XBee and go to Terminal tab. Write something, like "Test1". It should appear on the NXT LCD display:


Press firmly the right button on your NXT, and now you should see on the terminal tab the status of the touch sensor (press and release a few times).



2. Robotics: Arbitrator and Behavior

To introduce some robotic way of thinking in our project we can examine the sample BumperCar distributed with the samples bundle. It requieres a wheeled vehicle with two independently controlled motors connected to motor ports A and C, and a touch sensor connected to sensor port 1 and an ultrasonic sensor connected to port 3.

So we build this simple robot. You can follow this fantastic guide, and finally add the ultrasonic sensor.


As we can read here, the concepts of Behavior Programming as implemented in leJOS NXJ are very simple:
  •     Only one behavior can be active and in control of the robot at any time.
  •     Each behavior has a fixed priority.
  •     Each behavior can determine if it should take control.
  •     The active behavior has higher priority than any other behavior that should take control.
Basically, for each task the robot must perform, a behavior class is defined. This class will override the three public methods from Behavior interface:
  • boolean takeControl() indicates if this behavior should become active, returning quickly without performing long calculations.
  • void action() performs its task when the behavior becomes active. A behavior is active as long as its action() method is running, so the action() method should exit when its task is complete, or promptly when suppress() is called.
  • void suppress() immediately terminates the code running in the action() method. It also should exit quickly.
Once all the behaviors are created, they are given to an Arbitrator class, to regulate which behavior should be activated at any time. The order in the array of behaviors determines the priority of each one: 0 index means lowest priority. 

Whent its start() method is called, it begins arbitrating: deciding which behavior will become active. For that, it calls the takeControl() method on each Behavior object, starting with the object with the highest index number in the array, till it finds a behavior that wants to take control. If the priority index of this behavior is greater than that of the current active behavior, the active behavior is suppressed.

In the bumpercar sample, there are two behaviors. The first one defines its primary task, drive forward, and the second orders the robot to reverse and turn whenever the touch sensor strikes an object, or the ultrasonic sensor gets an echo from a close object, with priority over the first behavior:

Behavior b1 = new DriveForward(); // low priority
Behavior b2 = new DetectWall();   // high priority
Behavior[] behaviorList = {b1, b2};
Arbitrator arbitrator = new Arbitrator(behaviorList);
arbitrator.start();

Test #2 The BumperCar

This test is quite simple: if you have built the bumpercar, just run samples project, type "bumpercar" in the dialog, wait till it's downloaded, press a button on your NXT to start, and test if it behaves as expected.

3. Serial communication with the NXT

Now let's insert a new behavior in our robot, one that takes care of remote communication with the NXT, so we can override its autonomous behavior, providing manual control. A rear touch sensor will be added.

We create a new Java Free Form Project, named NXTBeeNXT, in the very same way as in the previous test1 with NXTBeeTest1.

First of all we'll add Brick, a singleton class to wrap the NXT motors and sensors, so they can be accessed from any behavior. Also, from this class any change in the robot status will be notified to the PC or Raspberry Pi, via XBee.

Then we add three behaviors: DriveForward, Remote and DetectWall.

Finally we define the main class, NXTBeeNXT.

Here you can see part of the Remote code. It just listens for commands from the inputstream and takes control or perform actions accordingly. In case an obstacle is found, this behavior will be suppressed and its action will be stopped:

public class Remote implements Behavior
{
    private DataInputStream dis;
    private boolean _suppressed = false;
    private String s;
    
    public Remote(DataInputStream dis){
        this.dis=dis;
    }
  
    public boolean takeControl()
    {
        if(Brick.getInstance().isStopped()){
            // cancel all behaviors, stop the arbitrator
            return false;
        }
      
        if(Brick.getInstance().getBehState()==Brick.state.MANUAL){
            // Take control if we're in Manual mode, don't read remote orders here
            return true;
        }
        
        // Read remote orders from PC/Raspi
        byte[] b = new byte[20];
        try {
            if(dis.available() > 0) {
                dis.read(b);
                s = new String(b);
            }
        } catch(Exception e) {}
    
        // Take control if orders are Stop or change from Auto (driving forward) to Manual mode
        return s!=null && (s.startsWith(Brick.STOP) || s.startsWith(Brick.MANUAL));
    }

    public void suppress()
    {
        _suppressed = true;
    }

    public void action()
    {
        
        _suppressed=false;
        
        LCD.clearDisplay();
        if(s.startsWith(Brick.STOP)){
            // Notify Stop order
            Brick.getInstance().setBehState(Brick.state.STOPPED);
            LCD.drawString("Bumper Car STOP",0,1);
        } else if(s.startsWith(Brick.MANUAL)){
            // Notify order to enter in Manual Mode
            Brick.getInstance().setBehState(Brick.state.MANUAL);
            
            // Start reading serial port and process the orders
            byte[] b = new byte[20];
            try {
                // This action will be suppressed if the robot finds an obstacle
                while(!_suppressed) {                    
                    if(dis.available() > 0) {
                        dis.read(b);
                        s = new String(b);
                        if(s.startsWith(Brick.LEFT)){
                            LCD.drawString("LEFT    ",0,3);
                            // start Motor.C rotating forward, with A stopped, so 
                            // the robot turns left
                            Brick.getInstance().getLeftMotor().stop();
                            Brick.getInstance().getRightMotor().rotate(360, true);
                        } else if(s.startsWith(Brick.RIGHT)){
...
                        } else if(s.startsWith(Brick.AUTO)){
                            LCD.drawString("AUTO    ",0,3);
                            // Return to Auto mode. Motors are stopped
                            Brick.getInstance().getLeftMotor().stop();
                            Brick.getInstance().getRightMotor().stop();
                            // Notify forward (auto) state
                            Brick.getInstance().setBehState(Brick.state.FORWARD);
                            // ends the action
                            _suppressed=true;
                        }
                    }
                    Delay.msDelay(500);
                }
            } catch(Exception e) { }
        }
        s="";
    }
}

All the code for this project can be found in my GitHub repository here.

Test #3 The remotely controlled BumperCar

We add to the bumpercar a second touch sensor, to look for obstacles when the robot moves backward in Manual mode, plugged to port #2. The XBee #2 must be plugged to the NXTBee and this should be plugged to port #4 on the NXT.



If you've just clonned the code from the repository, open the project, and run it, with your NXT switched on. After it has been compiled, built and downloaded, it will start and you'll see in the NXT display "NXTBee waiting...". Leave the NXT on the floor, in a relative clear wide area.

Now plug XBee #1 in the USB port of your PC, via XBee Explorer, and open X-CTU, read the modem configuration and go to Terminal tab. Now press 'S' to start Auto mode in the NXT. Let the BumperCar find some obstacles and react to them. Press 'P' to Stop in case something is wrong, and 'Q' to quit the program. On the NXT, from files menu, select NXTBeeNXT.nxj and click Enter. Now press again 'S' and enter in manual mode with 'M'. Now you can press:
  • 'F' to move forward,
  • 'B' to move backward,
  • 'L' to turn left,
  • 'R' to turn right,
  • 'V' to speed up,
  • 'W' to speed down and
  • 'A' to go to Auto mode

The red numbers appering on the terminal screen after commands are typed are the echo back from the NXT notifying its status: 0 means stopped, 1 driving forward, 2 wall detected and 3 manual mode.

Note that if you drive manually against any obstacle, DetectWall behavior will override your control, trying to avoid the obstacle and returning to auto mode.


  
4. JavaFX for Raspberry Pi

To run JavaFX on the Raspberry Pi, you'll need a hard float Raspbian version. I covered here how to install the soft float version, so follow the same instructions again, but now with this hard float version 2012-12-16-wheezy-raspbian.zip. When the Pi boots for the first time, in the config menu option memory_split, now you should give 128 MB to video.

At the first login, edit /boot/config.txt file and uncomment these two lines, and select the resolution of your display:

framebuffer_width=1280
framebuffer_height=1024


There are already several blogs out there covering how to install Java in a hard float Raspbian Wheezy version, like this or this. Basically you have to follow these steps:

1. Download Oracle JDK 8 (with JavaFX) for ARM Early Access from here in your PC. With ssh, copy the file to a folder in your Pi, like /home/pi/Downloads/. In a terminal window in your PC, or in the Pi, run these commands:

mkdir -p /opt
cd /home/pi/Downloads
sudo tar zxvf jdk-8-ea-b36e-linux-arm-hflt-29_nov_2012.tar.gz -C /opt 
rm jdk-8-ea-b36e-linux-arm-hflt-29_nov_2012.tar

If you type /opt/jdk1.8.0/bin/java -version, you should see "java version '1.8.0-ea'".

2. Download JavaFX samples from here. Copy the file to /home/pi/Downloads by ssh. Run in a terminal window:

cd /home/pi/Downloads
unzip javafx_samples-8_0_0-ea-linux.zip
rm javafx-samples-8.0.0-ea.zip
mv javafx-samples-8.0.0-ea /home/pi/javafx

And that's all!

Test #4. JavaFX sample 

Now we can test several of the included samples. Note that, for the moment, the application takes the whole screen and you can't quit if it doesn't have a button for it.  

So one way to do it is running your sample from a terminal window in a VNC session. You'll see the application in your display connected with HDMI to the Pi, and all the text output (System.out, System.err) in your terminal window. And you can kill the application anytime with Ctrl+C, or with ps -a, find the id of the java application and type kill <javaid>.

To run the sample, plug a mouse in your Pi, and type this line in the terminal window:

sudo /opt/jdk1.8.0/bin/java -Djavafx.platform=eglfb -cp /opt/jdk1.8.0/jre/lib/jfxrt.jar:/home/pi/javafx/StopWatch.jar stopwatch.MainScreen


Press Ctrl+C to finish.

5. Pi4J library

The Pi4j project is intended to provide a bridge between the native libraries and Java for full access to the Raspberry Pi, so you can easily access to GPIO pins for your Java project.

To install the library, follow these steps:

sudo wget http://pi4j.googlecode.com/files/pi4j-0.0.5-SNAPSHOT.deb
sudo dpkg -i pi4j-0.0.5-SNAPSHOT.deb


It will install in /opt/pi4j/lib four jars. Also, in /opt/pi4j/examples you'll find several samples.

Test #5. Control GPIO

Before testing the first sample, please review the GPIO pins labelling here

Now add a LED with a 330 Ω pull-up resistor in a breadboard, and connect anode to pin #1 and cathode to GND.


On your Pi, compile the sample first:

cd /opt/pi4j/examples
sudo /opt/jdk1.8.0/bin/javac -classpath .:classes:/opt/pi4j/lib/'*' ControlGpioExample.java

And now run it:

sudo /opt/jdk1.8.0/bin/java -classpath .:classes:/opt/pi4j/lib/'*' ControlGpioExample 


  
6. The JavaFX application

Finally, we'll design the JavaFX GUI application to remotely control the BumperCar from the Raspberry Pi.

The first thing we should do is install jdk1.8.0-ea in our PC, so we can use our favourite IDE to develope and build the project. Then, we only need to send the jar to the Pi and test it. For that:

1. Unzip jdk-8-ea-b36e-linux-arm-hflt-29_nov_2012.tar.gz in a folder, like C:\Java.
2. In Netbeans go to Tools, select Ant Variables, click Add, and define J8_HOME and browse to the folder "C:\Java\jdk1.8.0". 
3. Create a new JavaFX FXML Application, named NXTBeeFX, and open build.xml. Add at the end this target:

<target depends="-pre-init,-init-private" name="-init-user">
    <property file="${user.properties.file}"/>
    <property name="javac.compilerargs" value="-bootclasspath ${var.J8_HOME}/jre/lib/rt.jar"/>
    <property name="javac.classpath" value="${var.J8_HOME}/jre/lib/jfxrt.jar:
       ${var.J8_HOME}/jre/lib/ext/RXTXcomm-2.2pre2.jar:
       ${var.J8_HOME}/jre/lib/ext/pi4j-core.jar"/>
    <!-- The two properties below are usually overridden -->
    <!-- by the active platform. Just a fallback. -->
    <property name="default.javac.source" value="1.6"/>
    <property name="default.javac.target" value="1.6"/>
</target>

4. Add RXTXcomm-2.2pre2.jar and pi4j-core.jar to the project. Copy them from your Pi to your PC, by ssh, from /usr/share/java and /opt/pi4j/lib.

Now edit NXTBeeFX.fxml in the JavaFX Scene Builder, and add the buttons and labels required:


There'is one issue I've found while creating this app: you can't apply InnerShadow or DropShadows effects. If you do, you'll get a RuntimeException. A bug has already been filed: http://javafx-jira.kenai.com/browse/RT-27464.

Now, in NXTBeeFXController, we create the methods for the buttons, and initialize those from the manual panel to change the text label to a graphic icon:

btnRight.setId("key-button");
icon = new Label();
icon.getStyleClass().add("arrowRight");
btnRight.setText(null);
btnRight.setGraphic(icon);
btnRight.setPrefSize(50, 50);

taken from a SVG path, defined in the css file:

#key-button.button .arrowRight {
    -fx-shape: "M 14.007057,41.909369 C 2.3562491,41.605509 12.432093,7.29355
                31.877087,12.49765 l 0,-9.3754104 16.648482,14.5865794 
                -16.648482,15.29753 0,-9.66838 c -15.93811,-5.71097 
               -9.177528,18.43258 -17.87003,18.5714 z";
    -fx-translate-x: -6;
    -fx-translate-y: -4; 
}

Another issue I've found is that the icon is centered in the button at it's expected when running on my PC, but on the Raspberry Pi it appears to the right and to the bottom of the button, so that's the reason for translating it back to the center.

So this is how the manual panel looks like after a little bit of styling:



In the initialize method, we also start a thread to read the serial port (via XBee) and get all the responds from the NXT.

// Initialize Serial Port, with the XBee #1 connected on the USB port

serial=new Serial();
try {
    System.out.println("Connecting to serial port...");
    serial.connect( "/dev/ttyUSB0" );
} catch( Exception e ) {
    System.out.println("Error connecting to serial port: "+e.getMessage());
}

//
// Service to start reading serial port for NXT Status
// It will stop and close when requested
//
serviceSerial=new Service<Void>(){

    @Override
    protected Task<Void> createTask() {

        return new Task<Void>(){

            @Override
            protected Void call() throws Exception {
                System.out.println("start reading...");
                serial.read();
                return null;
            }    
            @Override protected void cancelled() {
                System.out.println("cancelling...");
                serial.disconnectInput();
                super.cancelled();
            }
        };
    }
};
serviceSerial.start();

Also, the GPIO pines are initialized, to set an alarm in case the NXT finds an obstacle.

    // create gpio controller
    final GpioController gpio = GpioFactory.getInstance();

    // provision gpio pin #01 as an output pin
    final GpioPinDigitalOutput pinRed =
       gpio.provisionDigitalOutputPin(RaspiPin.GPIO_01, "MyLEDRed", PinState.LOW);

    // provision gpio pin #07 as an output pin
    final GpioPinDigitalOutput pinGreen = 
       gpio.provisionDigitalOutputPin(RaspiPin.GPIO_07, "MyLEDGreen", PinState.LOW);
    
    public void setAlarmOn() {
        pinRed.high();
        pinGreen.low();
    }
    
    public void setAlarmOff() {
        pinRed.low();
        pinGreen.high();
    }

Note that a second LED is added to pin 7.


All the code for this project can be found in my GitHub repository here. Clone it, build it, and then you must send the jars to the Pi, by ssh:



To run the project you can create a file typing nano bash.sh. There you should type:

#!/bin/bash

sudo /opt/jdk1.8.0/bin/java -Djavafx.platform=eglfb -Djava.library.path="/usr/lib/jni" -cp /opt/jdk1.8.0/jre/lib/jfxrt.jar:/home/pi/javafx/NXT/RXTXcomm-2.2pre2.jar:/home/pi/javafx/NXT/pi4j-core.jar:/home/pi/javafx/NXT/NXTBeeFX.jar  nxtbeefx.NXTBeeFX

Save (Ctrl+O) and exit (Ctrl+X). Now check all is in place and you can run it:

/home/pi/Downloads/javafx/NXT/bash.sh




CONCLUSION

As a short conclusion after this long post, let me just say that JavaFX on the Raspberry Pi performs really well, being just an Early Access release. It will be improved and several issues will be fixed, and things that were not included, like media, will be in.

So if you have a Raspberry Pi, don't wait any longer and start testing your JavaFX apps.

Again, leJOS has proved to be a really mature platform, enabling a really easy integration between Java/JavaFX and robotics applications for Lego Mindstorms NXT. 

Finally, in this video you'll find most of the details I've been talking about in this post. 

If you have the chance, take your NXT, grab the code, and give it a try! Any comment will be absolutely welcome.

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.