JAX London 2014: A retrospective
Deep dish

JavaFX and Near Field Communication on the Raspberry Pi

JohanVos
yummy-pi

Learn how to use Java SE 8 to communicate with a near field communication (NFC) card reader, show a status message on a screen, or send information to a back-end system.

Use your Java skills to create end-to-end applications that span card readers on embedded devices to back-end systems.

JavaFX has already proven to be an excellent platform running on the Raspberry Pi. If you are working on client-side Java development on embedded devices, you probably know that JavaFX and the Raspberry Pi make for a great combination. In a number of cases, embedded devices are used in kiosk environments. A card reader is connected to an embedded device, and users have to scan a card to initiate an action—for example, to be allowed to enter a room, pay for something, or gain some credits.

There are different types of card readers, different types of cards, and different protocols. In this article, we look at how Java SE 8, which is available for the Raspberry Pi, can be used to communicate with a near field communication (NFC) card reader, show a status message on a screen, or send information to a back-end system.

Note: The source code for the examples shown in this article is available at https://bitbucket .org/johanvos/javafx-pi-nfc.

Components of Our System

The NFC specification defines a number of standards for the near field communication between devices, including radio communication between devices that are close to each other. Various cards are equipped with a chipset. In this article, we will use a MIFARE DESFire card

You can use your existing Java skills to create end-to-end applications that span different environments.

A card reader needs to be chosen that works out of the box on the Raspberry Pi. The USB-based ACR122U card reader from Advanced Card Systems Ltd. is a widely used device that can easily be connected to the Raspberry Pi and allows it to read MIFARE cards.

Note: It is recommended that a powered USB hub be used for connecting the card reader to the Raspberry Pi, because card readers consume a lot of power.

In order to have visual feedback, we need to attach a screen to the Raspberry Pi. A variety of screens are available, including a regular HDMI monitor and smaller screens suited for cars, kiosks, and copiers. Although this is the setup we will use in this article, different setups are possible.

Set Up the Raspberry Pi

In order to run the code provided for this project, you need a working setup. We will use an image for the Raspberry Pi that is available for download here.

In order to have the ACR122U card reader work with this image, you have to install two additional packages. Run the following commands to do this:

sudo apt-get update
sudo apt-get install libpcsclite1 pcscd

Install Java

Download Java SE 8 and install on the Raspberry Pi.

Java SE comes with a package, javax.smartcardio, that allows developers to communicate with smart card readers. By default, this package will look for a personal computer/smart card (PC/SC) implementation on the system.

Now that you have installed the required Linux packages on your Raspberry Pi, the only remaining task is that we have to point the Java Virtual Machine to the location of the pcsclite library.

This is done by setting the system property sun.security.smartcardio.library to the location of the pcsclite library as follows:

java -Dsun.security.smartcardio
.library=/path/to/libpcsclite.so

The JavaFX Application

Making a connection with a card reader and reading cards are operations that can take some time. Hence, it is not a good idea to execute those tasks at the JavaFX application thread level. Instead, we will create a dedicated thread that will deal with the card reader and uses the standard JavaFX way to update the user interface. 

In a kiosk, a card reader is connected to an embedded device, and users scan a card to initiate an action.

Creating the user interface. We will create a very simple user interface that shows the identifier of the latest successfully scanned card. This identifier is stored in StringProperty latestId, as follows:

private StringProperty latestId = 
new SimpleStringProperty(" --- ")

Our simple user interface contains only an HBox with a label containing some static information followed by a label containing the identifier of the latest scanned card. It is constructed as shown in Listing 1.

HBox hbox = new HBox(3);
Label info = new Label("Last checkin by: ");
Label latestCheckinLabel = new Label();
latestCheckinLabel.textProperty().bind(latestId);
hbox.getChildren().addAll(info, latestCheckinLabel);
Scene scene = new Scene(hbox, 400, 250);
primaryStage.setScene(scene);
primaryStage.show();

Listing 1

Communicating with the card reader. The thread responsible for the communication with the card reader is created in a function calleddoCardReaderCommunication, which is called in the start() method of the JavaFX application. A JavaFX Task is created, assigned to a new thread, and started, as follows:

Task task = new Task() { 
   … 
};
Thread thread = new Thread(task);
thread.start();

This Task performs the following work: 

  1. It detects a card reader.
  2. It detects a card being recognized by the card reader.
  3. It reads the identifier from the card.
  4. It updates the value of latestId.
  5. It waits for the card to be removed.
  6. It goes back to Step 2. 

Detecting a card reader. Before we can access a card reader we need a TerminalFactory. The javax.microcard package provides a static method for getting a default TerminalFactory, and it allows us to create more-specific TerminalFactory instances for different types of card readers. The default TerminalFactory will use the PC/SC stack that we installed on the Raspberry Pi, and it is obtained by calling the code shown in Listing 2. Using the code shown in Listing 3, we can query terminalFactory to detect the card readers that are installed.

TerminalFactory terminalFactory = TerminalFactory.getDefault();

Listing 2

List<CardTerminal> cardTerminalList = 
terminalFactory.terminals().list();

Listing 3

In this case, our setup was successful, and cardTerminal List contains one element. This CardTerminal is obtained by running the code shown in Listing 4.

CardTerminal cardTerminal = cardTerminalList.get(0);

Listing 4

Detecting cards. Once we have a reference to the card reader, we can start reading cards. We will do this using the infinite loop shown in Listing 5. In real-world cases, a dummy card could be used to stop reading.

while (true) {
    cardTerminal.waitForCardPresent(0);
    handleCard(cardTerminal);
    cardTerminal.waitForCardAbsent(0);
}

Listing 5

The thread will block until the reader detects a card. Once a card is detected, we will handle it as discussed in the next section. Then, the thread will block again until the card is removed.

Handling cards. The handleCard method is called when we know that a card is presented to the card reader. Note that a card can be removed during the process of reading, and proper exception handling is required.

A connection to the card is obtained by calling the code shown in Listing 6.

Card card = cardTerminal.connect("*");

Listing 6

Before we can read the identifier on the card, we need to know what type of card we are reading. Different card types use different addresses to store the identifier. In our simple demo, we will limit ourselves to MIFARE DESFire cards, but the example can easily be extended to other card types.

Information on the type of card we are reading can be obtained by inspecting the ATR (answer-to-reset) bytes of the card as follows:

ATR atr = card.getATR();

We compare the bytes in this atr object with the bytes we expect on a DESFire card, as follows:

Arrays.equals(
desfire, atr.getBytes());

As shown in Listing 7desfire is a static byte array containing the information we expect.

static byte[] desfire = 
new byte[]{0x3b, (byte) 0x81, (byte) 0x80,
        0x01, (byte) 0x80, (byte) 0x80};

Listing 7

If we have a supported card type (that is, a DESFire card), we can query the identifier. In order to do so, we need to transmit an application protocol data unit (APDU) command over the basic logic channel of the card, and then read the response. Using the javax .microcard package, this is done as shown in Listing 8.

CardChannel channel = card.getBasicChannel();
CommandAPDU command = new CommandAPDU(getAddress);
ResponseAPDU response = channel.transmit(command);
byte[] uidBytes = response.getData();
final String uid = readable(uidBytes);

Listing 8

The getAddress parameter we are passing is a static byte array that defines how to read the identifier for a DESFire card, and it is declared as shown in Listing 9. Different card types might require a different byte array.

static byte[] getAddress = 
new byte[]{(byte) 0xff, (byte) 0xca, 0, 0, 0};

Listing 9

The function readable(byte[]) translates the byte array into a more human-readable format by converting each byte to its hexadecimal String representation (see Listing 10).

private static String readable(byte[] src) {
        String answer = "";
        for (byte b : src) {
            answer = answer + String.format("%02X", b);
        }
        return answer;
}

Listing 10 

Now that we have a readable identifier for the card, we need to show the identifier in the user interface. Because the identifier is obtained on a thread different from the JavaFX application thread (that is, from the communication thread), we need to make sure that we don’t change the latestId StringProperty directly from the communication thread. Rather, we need to put the change on the JavaFX application thread as shown here.

Platform.runLater(new Runnable() {
    public void run() {
        latestId.setValue(uid);
    }
});

This separation of concerns also guarantees that the communication with the card reader is not disturbed by activity in the user interface and vice versa.

Sending the data to a back end. In many cases, visual feedback is required when a reader scans a card. In other cases, the information needs to be sent to a back-end system. In the next section, we will send data to the back end using a very simple client-server module by which identifiers are sent from the Raspberry Pi to a Java EE 7 back end and then visualized on a web page. Sending data from a JavaFX application to a REST-based back-end system is easily done using DataFX.

The code shown in Listing 11 will send the identifier uid to a REST endpoint at http:// 192.168.1.6:8080/webmonitor/ rest/card/checkin.

private void sendToBackend(String uid) {
      RestSource restSource = RestSourceBuilder.create()
              .host("http://192.168.1.6:8080")
              .path("webmonitor")
              .path("rest/card/checkin")
              .formParam("id", uid)
              .build();
      ObjectDataProvider odp = ObjectDataProviderBuilder.create()
              .dataReader(restSource).build();
      odp.retrieve();
  }

Listing 11

Note that we will use the POST method, because a form parameter is specified in the REST request. This method should be called whenever a new identifier is read. Because DataFX deals with the threading, we have to call this method from the JavaFX application thread. Hence, we can do this in the same code block in which we set the value of the latestId property (see Listing 12).

Platform.runLater(new Runnable() {
    public void run() {
        latestId.setValue(uid);
        sendToBackend(uid);
    }
});

Listing 12

In real-world applications, the server might send additional information, such as the real name of the user of the card, back to the client. The client application can use this information to make the user interface more personal.

The Back End

We will now create a very simple back end that accepts the REST requests and shows them on a web page. Because it is not assumed that the user of the web page knows when cards are being scanned, it makes sense to dynamically update the web page whenever a card has been scanned. This can easily be done leveraging the WebSocket API in Java EE 7. It is surprising how little code is required for doing this. 

It’s easy to create an embedded Java application, enrich it with a simple JavaFX user interface, connect it to a card reader, connect it to a Java EE system, and visualize the information using HTML5.

We will start with the web page, which is a simple HTML page with a division named checkins that will contain the list of identifiers and a time stamp indicating when each card scan happened.

When the HTML page is loaded, a WebSocket is created, pointing to our simple back end. The content of the checkins division is populated whenever a message arrives over the WebSocket. This is easily achieved using the code shown in Listing 13.

function openConnection() {
    connection = 
new WebSocket('ws://localhost:8080/webmonitor/endpoint');

    connection.onmessage = function(evt) {
        var date = new Date();
        var chld = document.createElement("p");
        chld.innerHTML = date + " :  " + evt.data;
        var messages = document.getElementById("checkins");
        messages.appendChild(chld);
    };
}

Listing 13

Note that the HTML page will open a WebSocket toward localhost:8080/webmonitor/endpoint, while the JavaFX application will push identifiers to localhost:8080/webmonitor/rest/card/checkin. The glue for this is our single-file back-end system, implemented in a class named CardHandler.

Because CardHandler provides a REST endpoint, it extends javax .ws.rs.core.Application, and it contains an@ApplicationPath annotation specifying that the REST interface is located at the rest value. The handler itself is registered at a pathcard, and the particular method for receiving identifiers is associated with the path checkin. This method also takes a form parameter named id, which contains the identifier (see Listing 14).

@ApplicationPath("rest")
@Path("card")
public class CardHandler extends Application {

    @Path("checkin")
    @POST
    public Response checkin(
@FormParam("id") String id) throws IOException {
        System.out.println("Received card with id " + id);
        return Response.ok().build();
    }

Listing 14

If we want to send this information to WebSocket clients, for example, to the simple web page we created in the previous section, we need to register a WebSocket endpoint as well. We can leverage CardHandler for this, because a class can have multiple annotations. Doing so, we put all the back-end code into a single file. This is probably not the best idea for a more complex production system, but by doing it here, we can see how easy it is to create an enterprise application in Java, combining REST and WebSockets.

When the WebSocket endpoint receives a connection request, the created session is stored in a Set. When the connection is closed, the session is removed from the Set, as shown in Listing 15.

@ServerEndpoint(value="/endpoint")
public class CardHandler extends Application {

    private static Set<Session> sessions = new HashSet<>();
    
    @OnOpen
    public void onOpen (Session s) {
        sessions.add(s);
    }
    
    @OnClose
    public void onClose (Session s) {
        sessions.remove(s);
    }

}

Listing 15

We can now modify the checkin method created in Listing 14, and send the identifier to the connected WebSocket clients, as shown inListing 16.

   @Path("checkin")
    @POST
    public Response checkin(@FormParam("id") String id) throws IOException {
        System.out.println("Received card with id " + id);
        System.out.println("sessions = "+sessions);
        for (Session session: sessions) {
            session.getBasicRemote().sendText(id);
        }
        return Response.ok().build();
    }

Listing 16

Conclusion

In this article, we saw how easy it is to create an embedded Java application, enrich it with a simple JavaFX user interface, connect it to external hardware (a card reader), connect it to a Java EE system, and visualize the information using HTML5.

The underlying systems all use the same Java SE code. As a consequence, developers can use their existing Java skills to create end-to-end applications that span different environments.

Originally published in the March/April 2014 issue of Java MagazineSubscribe today.

About the author

vos-headshot

Johan Vos started working with Java in 1995. He is a cofounder of LodgON, where he is working on Java-based solutions for social networking software. An enthusiast of both embedded and enterprise development, Vos focuses on end-to-end Java using JavaFX and Java EE.

 

 

 

 

(1) Originally published in the March/April 2014 Edition of Java Magazine 
(2) Copyright © [2014] Oracle.

Author
JohanVos
Johan Vos started working with Java in 1995, as part of his PhD research. He joined the Blackdown team and ported Java 1.2 to Linux/SPARC. Johan has been a Java consultant ever since, and worked for a number of companies (e.g. The Reference, Acunia). He co-founded LodgON, where the main focus is on social networking software and recently became a Java Champion. He was part of the Core Platform Expert Group of OSGi that created the OSGi platform specifications. His main technology interests are currently GlassFish and JavaFX. He holds an Msc in civil engineering (mining) and a PhD in Applied Physics.
Comments
comments powered by Disqus