Sunday, June 22, 2014

Java 8 Program to Record GPS Data to a JavaDB Database on the Beaglebone Black

I posted yesterday about how to install the Java 8 JDK on the Beaglebone Black.  The Java JDK includes the JavaDB database (previously known as Derby).  This example shows how to read GPS data over a serial connection and record the data to JavaDB database table.  I have developed and tested this code on the Beaglebone Black Rev. C running Debian.  This example assumes that you are working at the Beaglebone Black's command line (using a monitor, keyboard, and mouse connected to the Beaglebone or via SSH).

While JavaDB is included with the JDK, a couple steps are needed to configure it.  I am using JavaDB in network server mode so that it can be accessed simultaneously from programs running in separate JVMs.

I am using the Copernicus II DIP GPS module from Sparkfun.

Configuring JavaDB


Add the following exports to your .bashrc to set the environment variables for JavaDB.  You should already have JAVA_HOME set after installing Java 8.

export DERBY_HOME=$JAVA_HOME/db
export PATH=$PATH:$DERBY_HOME/bin 

Also run the export statements at the command line or log out and back in to add these values to the environment.

Edit the /usr/jdk1.8.0/jre/lib/security/java.policy file to allow the server to accept connections by adding the following line before the closing brace of the grant section.

permission java.net.SocketPermission "localhost:1527", "listen";

Start the JavaDB server with the following command -


$JAVA_HOME/db/bin/startNetworkServer &

Creating the Database Table


This example uses a simple database table called gps_readings in a database named gpsdb.  Run ij to create the table.  At the ij> prompt, run the following command to create the database:

connect 'jdbc:derby://localhost:1527/gpsdb;create=true';

After the DB has been created, you can connect in future using the same connect statement or you can drop the ;create=true to cut down on typing.

Here is what I ran at the ij command prompt to create the gps_readings table:

ij> create table gps_readings (
> utc_time_date timestamp not null,
> lat varchar(15) not null,
> long varchar(15) not null,
> constraint pk_gps_readings_utc_time_date primary key(utc_time_date)
> );


Connecting the Copernicus II GPS Module


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

The Java Code


import gnu.io.*;
import java.io.*;
import java.util.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.time.LocalDate;
import java.time.ZoneId;

public class Gps {
    private static String port = "/dev/ttyS80";
    private InputStream inStream;
    private OutputStream outStream;
    // NMEA command to set Copernicus II to output $GPGLL every second.
    private static String nmeaString = "$PTNLSNM,0002,01*55\r\n"; 

    private Connection connect = null;
    private Statement statement = null;
    private static String driverClass = "org.apache.derby.jdbc.ClientDriver";
    private String jdbcURL = "";
    private static String sql = "INSERT INTO GPS_READINGS(UTC_TIME_DATE, LAT, LONG) VALUES(?,?,?)";

    // Constructor takes JDBC URL for JavaDB server as argument
    public Gps(String url) { jdbcURL = url; }

    public void recordGPS() {
        try {
            // echo & its args have to be called using an array of Strings,
            // just using a single String (as with ln command below) does 
            // not work.
            String[] configPinsCmd = { "bash", "-c", 
                "echo BB-UART1>/sys/devices/bone_capemgr.9/slots" };
            Process p = Runtime.getRuntime().exec(configPinsCmd);
            p.waitFor();
            // RXTXComm library uses /dev/ttyS80, so symbolic link needed
            String lnPortCmd = "ln -s /dev/ttyO1 /dev/ttyS80";
            p = Runtime.getRuntime().exec(lnPortCmd);
            p.waitFor();
            CommPortIdentifier portId = CommPortIdentifier.getPortIdentifier(port);
            SerialPort serialPort = (SerialPort) portId.open("GPS", 5000);
            // Change serial port speed as needed
            serialPort.setSerialPortParams(19200, SerialPort.DATABITS_8,
                SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
            serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
            inStream = serialPort.getInputStream();
            outStream = serialPort.getOutputStream();
            byte[] nmeaCmd = nmeaString.getBytes();
            String gpsData = "";
            outStream.write(nmeaCmd, 0, nmeaCmd.length);
            Class.forName(driverClass).newInstance();
            connect = DriverManager.getConnection(jdbcURL);
            PreparedStatement statement = connect.prepareStatement(sql);
            while(true) {
                if(inStream.available() > 0) {
                int b = inStream.read();
                    if(b != 13) {
                        gpsData += (char)b;
                    }
                    else {
                        gpsData = gpsData.trim();
                        System.out.println(gpsData);
                        String[] datum = gpsData.split(",");
                        gpsData = "";
                        // Check for valid $GPGLL NMEA sentence
                        if(datum.length < 8 || !("$GPGLL").equals(datum[0]) || datum[1] == null || 
                               !("A").equals(datum[6])) {
                           continue;
                        }
                        else {
                            LocalDate todayUTC = LocalDate.now(ZoneId.of("UTC"));
                            String t = datum[5].substring(0,2) + '.';
                            t += datum[5].substring(2,4) + '.';
                            t += datum[5].substring(4,6);
                            statement.setString(1, todayUTC.toString() + '-' + t);
                            statement.setString(2, datum[1] + ' ' + datum[2]);
                            statement.setString(3, datum[3] + ' ' + datum[4]);    
                            statement.executeUpdate();
                        }
                    }
                }
            }
        } 
        catch (Exception ex) {
          ex.printStackTrace();
        }
        finally {
            try {
                statement.close();
                connect.close();
            } 
            catch(Exception exc) {
                exc.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Gps copernicus = new Gps("jdbc:derby://localhost:1527/gpsdb");
        copernicus.recordGPS();
   }
}

Compiling the Code


The Ubuntu distribution that came installed on my Beaglebone Black Rev. C included the Java RXTXComm library.  If you need to get it, you can run the following apt-get command to install it.

apt-get install librxtx-java

Use the following javac command to compile the code -

javac -cp /usr/share/java/RXTXcomm.jar:/usr/jdk1.8.0/db/lib/derby.jar Gps.java

Running the Program at the Command Line


With the JavaDB network server running, use the following command to run the program -

java -Djava.library.path=/usr/lib/jni/ -cp /usr/share/java/RXTXcomm.jar:/usr/jdk1.8.0/db/lib/derbyclient.jar:. Gps

While the program is running, you will see the GPS GLL sentences printed out to the console.  The readings are parsed and saved to the DB table.  You can use ij to connect to the database and run a query against the table to see the entries.  Using JavaDB in network server mode allows you to query the DB table while the program is running. 

Note that the command to run the program needs the java.library.path parameter set to the location of the native library (.so file) for RXTXComm.  Be sure to include the derbyclient.jar (not derby.jar) file in the classpath (-cp).



No comments:

Post a Comment