Tuesday, October 8, 2013

Reading from an I2C Device with Java & JNA on a Raspberry Pi

Here is a brief example of using Java Native Access (JNA) with a simple C shared library to access an I2C device (TMP102 temperature sensor) on a Raspberry Pi.

The 09/25/2013 release of Raspbian Linux has Java 7 already installed.  The version of Java in this release supports "hard float," so it works better with JNA than "soft float." This is important in this example because the C code that accesses the TMP102 returns a float (which doesn't work on "soft float" versions of Java, such as the one found at present on the Beaglebone Black).

To use JNA, you need jna.jar and jna-platform.jar from Git Hub and put them in the /usr/share/java directory.

C Code for Shared Library


The code for the shared library consists of two files: tmp102.h and tmp102.c.

The tmp102.h file is only one line -

extern float getTempC();

Here is the tmp102.c file -

#include <stdio.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include "tmp102.h"

float getTempC() {
        int devHandle;
        int readBytes;
        char b[2];
        // initialize buffer
        b[0] = 0x00;
        int devI2CAddr = 0x48;
        // open device on /dev/i2c-1
        if ((devHandle = open("/dev/i2c-1", O_RDWR)) < 0) {
                printf("Error: Couldn't open device! %d\n", devHandle);
                return 1;
        }
        // connect as i2c slave
        if (ioctl(devHandle, I2C_SLAVE, devI2CAddr) < 0) {
                printf("Error: Couldn't find device on address!\n");
                return 1;
        }
        // begin transmission and request acknowledgement
        readBytes = write(devHandle, b, 1);
        if (readBytes != 1)
        {
                printf("Error: No ACK-Bit, couldn't establish connection!");
        }
        else
        {
                // read response
                readBytes = read(devHandle, b, 2);
                if (readBytes != 2)
                {
                        printf("Error: Received no data!");
                }
                else
                {
                        float t = (((b[0] << 8) | b[1]) >> 4) * 0.0625;
                        return t;
                }
        }
        // close connection and return
        close(devHandle);
        return 1;
}

 Run the following command to compile this code as an .so file.

gcc tmp102.c -o tmp102.so -shared

Place the resulting .so file (tmp102.so) to the same directory as the JNA JAR files.

Java Code


Two Java files are required, an interface (Tmp102Library.java) and the Java code that uses the interface to call the method via JNA (tmp102.java).

Here is the code for the interface -

 import com.sun.jna.Library;
import com.sun.jna.Native;

public interface Tmp102Library extends Library {
   Tmp102Library INSTANCE = (Tmp102Library) Native.loadLibrary("tmp102.so",
        Tmp102Library.class);
   float getTempC();
}

Here is the Java program itself - 

import com.sun.jna.Library;

public class tmp102 {
    public static void main(String[] args) {
        System.setProperty("jna.library.path","/usr/share/java");
        try {
            float temp = Tmp102Library.INSTANCE.getTempC();
            System.out.println("Temp: " + Float.toString(temp));
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

}

Run these commands to compile the Java -

javac -cp /usr/share/java/jna-4.0.0.jar:/usr/share/java/jna-platform-4.0.0.jar:. tmp102.java


javac -cp /usr/share/java/jna-4.0.0.jar:/usr/share/java/jna-platform-4.0.0.jar Tmp102Library.java

Then use the following to run the program -

java -cp /usr/share/java/jna-4.0.0.jar:/usr/share/java/jna-platform-4.0.0.jar:. tmp102