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
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
7
desfire 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