Tuesday, June 24, 2014

Java Code to Read GPS Data from a Sparkfun Copernicus II DIP Module & Store Readings in a JavaDB Database Table on a Raspberry Pi

This example uses Java 8 to connect to the Copernicus II GPS module via a serial connection. For instructions on how to install Java 8 on a Raspberry Pi, see this Adafruit tutorial.

To install the RXTXComm serial library for Java, run the following apt-get command:

apt-get install librxtx-java

Last year, I posted a similar example that uses MySQL, but since JavaDB (also known as Derby) comes with the Java 8 installation, it is very convenient and easy to use.

Configuring JavaDB


While JavaDB comes with the Java 8 JDK, a small amount of configuration is needed.  JavaDB can run in embedded or in network server mode.  In this example, I am running it as a network server so that it can be accessed by more than one application running in different Java virtual machines.

 Make sure that your JAVA_HOME environment variable is set.  If you used the Adafruit tutorial above and have the JDK installed in /opt/jdk1.8.0, add the following line to your .bashrc file.  If you have your JDK installed in a different location, adjust as needed.


export JAVA_HOME=/opt/jdk1.8.0

Then add the following lines to set DERBY_HOME and adjust your path to include the JavaDB executables.

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

Run source ~/.bashrc to load the settings from your edited .bashrc file. 

Edit your java.policy file to allow access to port 1527.  If you have the JDK installed in /opt/jdk1.8.0, your policy file should be /opt/jdk1.8.0/jre/lib/security/java.policy.  Add the following line to this file before the closing bracket of the grant block -

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

Now start the JavaDB server with the following command:

/opt/jdk1.8.0/db/bin/startNetworkServer &

It may take a moment to start, but the output should indicate that the security policy has been applied and that the server is now running on port 1527.

For this example, I used the ij client utility to connect to JavaDB, create the gpsdb database, and create the gps_readings table.  I won't go into a lot of detail about ij, but you can find good documentation online.  With the path to the JavaDB/Derby binaries included in your path, you can simple run the ij command to start the command line DB client.

To create the database, I used the following connect statement at the ij> prompt:

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

I ran the following SQL at the ij> prompt to create the gps_readings table:

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  Raspberry Pi (Rev. B)
VCC         3.3V       
GND         GND
TX-B        GPIO15
RX-B                 GPIO14

Java 8 Code


Here is the source code from Gps.java -

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 {
            // RXTXComm library uses /dev/ttyS80, so symbolic link needed
            String lnPortCmd = "ln -s /dev/ttyAMA0 /dev/ttyS80";
            Process 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 {
                        System.out.println(gpsData);
                        gpsData = gpsData.trim();
                        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 & Running the Program

I have tested compiling and running the program as root (superuser).

Use the following command to compile the program -

javac -cp /usr/share/java/RXTXcomm.jar:$JAVA_HOME/db/lib/derby.jar Gps.java

After compiling, use the following command to run the program.  Note that when running the code you need to use derbyclient.jar.

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

When the program runs, the GPS data read from the Copernicus II is printed out in the terminal window and records are inserted into the gps_readings table.  You can use ij to query the data in the database.  Since JavaDB is running in network server mode, you can access it using ij while the Java Gps program is running.  


No comments:

Post a Comment