Sunday, October 6, 2013

Using Java & JNA to Access an I2C Device on a Beaglebone Black

Having gotten Java 7 installed on my Beaglebone Black, I wanted to find a way to access an I2C device from a Java program.  To keep things simple, I used a TMP102 temperature sensor from Sparkfun.  I started looking into JNI, but I decided to try JNA to access a simple C library to read the TMP102.

I downloaded the 2 JAR files for JNA (jna.jar & jna-platform.jar) from Git Hub and put them in the /usr/share/java directory.

Note (07/02/2014): I am having trouble with this under Java 8.  The code compiles, but it throws the exception NoClassDefFoundError: com/sun/jna/Library, even though the JAR files are included in my classpath.


C Library


The header file for the C library (tmp102.h) consists of a single line declaring the only method in this example:

extern int getTempC();

Here is the code for the library that will compiled into a .so file.

Note that the method returns an integer.  I wanted to used a float or a double, but I found that JNA did not return the correct values.  The values returned were completely nonsensical.  This is apparently related to differences between software and hardware float support. When I tried to return the data from the sensor to the Java program as a char */string, the result was a segment fault.  So, I decided to multiply the reading by 1000 and return the result as an integer (that the Java program can divide to get the temperature). 

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

int 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 (int)(t*1000);
                }
        }
        // close connection and return
        close(devHandle);
        return 1;
}


The following command compiles the code to a shared library (tmp102.so).

gcc tmp102.c -o tmp102.so -shared

Copy the resulting tmp102.so file to the directory with the JNA JAR files (/usr/share/java).


Java Code


The first part of the Java code is the interface file (Tmp102Library.java):

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

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

Here is the Java code for the program that uses the interface to access the library via JNA:

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()/1000F;
                        System.out.println("Temp: " + Float.toString(temp));
                } catch(Exception e) {
                        e.printStackTrace();
                }
        }

}


The following commands compile the Java files:

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

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

The following command runs the program:

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

Wednesday, October 2, 2013

Oracle Java (JDK) 7 on the Beaglebone Black

Last May I posted about installing OpenJDK 6 on the Beagleone Black.  I now see that there are instructions on the BeagleBoard.org Web site for installing Oracle Java (JDK) 7 on the Beaglebone Black.  The process is very simple and I had no problems with the installation.  No additional components are needed to get the Linux ARM v6/v7 VFP Soft Float ABI version running on the Beaglebone Black.

To reduce the amount of space taken up by the installation, you can delete the src.zip file included in the tar.gz file.


Tuesday, October 1, 2013

GPS for the Beaglebone Black: Sparkfun Copernicus II DIP Module

Note (06/20/2014) - For an updated and improved version tested on the Beaglebone Black Rev. C running Ubuntu, see this post.

Earlier in the summer, I posted about using the D2523T 50 Channel Helical GPS Receiver with the Beaglebone Black.  I have also found the Copernicus II DIP Module from Sparkfun to be a very good GPS option. At $74.95, the price is similar to the D2532T.  To help with reception indoors at my desk while doing development, I am using this antenna.

The Python code below is very similar to my earlier post, but I have added a line showing how easy it is to write configuration commands to the module using the PySerial library.

For more information on the NMEA configuration commands, see Appendix C of the Copernicus II manual. The command must include a 2-digit checksum after the asterisk.  See this page for an online tool to calculate the checksum.

Connections


GPS Module  Beaglebone Black
VCC         P9 3
GND         P9 1
TX-B        P9 26
RX-B        P9 24

See this post for the configuration needed to use the Beaglebone Black's pins for serial (UART) communication.


Python Code



import serial
import time
import datetime
import re
import os

os.system("echo uart1 > /sys/devices/bone_capemgr.9/slots")

# Adjust the connection speed as needed. 
# You can use Triimble Studio on your PC to detect/set speed
# if you can't figure it out. 
serial = serial.Serial("/dev/ttyO1", baudrate=115200)

# Send NMEA config cmd to output only $GPRMC message every second
nmeaCmd = "$PTNLSNM,0100,01*56\r\n"
serial.write(nmeaCmd)

resp = ""

while True:
        while (serial.inWaiting() > 0):
                resp += serial.read()
                if "\r\n" in resp:
                        if "$GPRMC" in resp:
                                data = resp.split(',')
                                info = "UTC: %s  Date: %s  N Lat: %s  W Long: %s" % (
                                        data[1], data[9], data[3], data[5])
                                print info
                        resp = ""