by Jose Cruz
Learn how to connect sensors to the Raspberry Pi and control them with Java.
Part 1 of this series explained how to connect electronic sensors to the Raspberry Pi Model B using general-purpose input/output (GPIO) interfaces. Part 2 focused on using inter-integrated circuit bus (I2C) interfaces to connect sensors.
This article focuses on using universal asynchronous receiver/transmitter (UART) interfaces to connect new modules that do the following:
In addition, because we won't have all UART interfaces you need, this article describes how to convert an I2C interface to UART using the SparkFun I2C/SPI-to-UART breakout board (SC16IS750). We'll also use a little mono audio amplifier, the SparkFun module TPA2005D1.
Note: The complete example code for this NetBeans IDE 8.0.2 project can be downloaded from the attached zip file at the bottom of this article.
Enabling UART on the Raspberry Pi
UART is a serial port that consists of two signals—a transmit signal (TxD) and a receive signal (RxD)—made available on pin 8 (BCM or GPIO 14) and pin 10 (BCM or GPIO 15), respectively, on the GPIO header. You can view the Raspberry pinout here.
Remember that all GPIO pins operate at 0 and 3.3 V logic levels, not the common RS-232 serial port level of +/- 12 V. If you wish to connect a UART device, you need a board or adapter to convert the higher signal levels to the lower GPIO signal levels. For example, you can use a MAX3232CPE transceiver and some capacitors. See this tutorial.
By default, the Raspberry Pi uses its built-in UART interface as the serial console. To have control of it, we need to run a few commands to release it.
First, back up the /boot/cmdline.txt
file by running the following command:
|
$ sudo cp /boot/cmdline.txt /boot/cmdline_backup.txt
|
Then, enter the following command from the command line to edit the file.
|
$ sudo nano /boot/cmdline.txt
|
Change the following line in the file, from this:
|
dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200
console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait
|
to this:
|
dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4
elevator=deadline rootwait
|
Next, enter the following command from the command line to edit the /etc/inittab
file:
|
$ sudo nano /etc/inittab
|
Then change the following line in the file from this:
|
#spawn a getty on Raspberry Pi serial line
T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100
|
to this (to comment out the second line):
|
#spawn a getty on Raspberry Pi serial line
#T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100
|
As the last step, reboot the Raspberry Pi.
 
Circuits We Will Create
Based on the block diagram shown in Figure 1 and the components shown Figure 2, we create the circuits shown in Figure 3.

Figure 1. Block diagram of the circuits we will create

Figure 2. Components we will use

Figure 3. Schematic of the circuits we will create
 
Connecting the GPS Receiver Engine Board
The EM-406a is a compact, high-performance, low power consumption GPS engine board. It uses the SiRF Star III LP Single GPS chipset, which can track up to 20 satellites at a time, and has the following features:
- Very high sensitivity (tracking sensitivity: -159 dBm)
- Extremely fast TTFF (Time To First Fix) at low signal levels
- Support for the NMEA 0183 data protocol
- Built-in SuperCap to maintain system data for rapid satellite acquisition
- Built-in patch antenna
- Foliage lock for weak signal tracking
- All-in-view 20-channel parallel processing
- Snap lock 100ms reacquisition time
Let's now connect the device to the Raspberry Pi's GND, GPS Tx to UART RxD, GPS Rx to UART TxD, and VCC 5v pins, as shown in Figure 3, and create a Java ME 8 class called GPSEM406ADevice
to control the GPS device. See Listing 1.
|
public class GPSEM406ADevice {
private UARTConfig config=null; //UART config parameters object
private UART uart = null; //UART object
private static String nmea = ""; //NMEA protocol data read from UART event listener
private String time = null; //Time from GPS
private final String longitude\[\] ={null, null}; //Longitude read from GPS
private final String latitude\[\] = {null, null}; //Latitude read from GPS
private final String altitude\[\] = {null, null}; //Altitude read from GPS
|
Listing 1. Creating the GPSEM406ADevice
class
Next, create a class constructor that initializes and activates UART using the DeviceManager
API and the UARTConfig
class (see Listing 2) to establish the following conditions for serial communication and activate the input data listener to receive status from the GPS device.
- Controller Number:
DeviceConfig.DEFAULT
- Channel Number:
DeviceConfig.DEFAULT
- Baud Rate: 4800
- Data Bits:
DATABITS_8
- Parity:
PARITY_NONE
- Stop Bits:
STOPBITS_1
- Flow control:
FLOWCONTROL_NONE
|
public GPSEM406ADevice() throws IOException {
//Config UART
config = new UARTConfig(DeviceConfig.DEFAULT, DeviceConfig.DEFAULT, 4800,
DATABITS_8, PARITY_NONE, STOPBITS_1, FLOWCONTROL_NONE);
//Create UART object
uart = (UART) DeviceManager.open(UART.class, config);
// Initialize GPS to talk NMEA protocol
GPS_Switch_Mode_To_NMEA();
//Create read data listener to UART
uart.setEventListener(UARTEvent.INPUT_DATA_AVAILABLE, new MyUARTListener());
}
|
Listing 2. Establishing the initial conditions for the GPS device and UART
The GPS_Switch_Mode_To_NMEA
method (see Listing 3) initializes the GPS device to talk the NMEA protocol by using several byte commands (see explanation here) that establishes this type of text protocol.
|
private static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i + 1), 16));
}
return data;
}
private void GPS_Switch_Mode_To_NMEA() {
byte[] data = hexStringToByteArray("A0A20018810201010001010105010101000100010001000100012580013AB0B3");
ByteBuffer buffer = ByteBuffer.allocateDirect(data.length);
buffer.put(data);
buffer.clear();
...
uart.write(buffer);
I2CUtils.I2Cdelay(10000);
Logger.getGlobal().log(Level.FINE, "GPS Config Ok...");
...
}
|
Listing 3. Method to initialize the GPS device to talk the NMEA protocol
Then create the following format and get methods (see Listing 4):
formatTime
: Format read time
formatLat
: Format read latitude
formatLong
: Format read longitude
formatAlt
: Format read altitude
getTime
: Read time
getLatitude
: Read latitude
getLongitude
: Read longitude
getAltitude
: Read altitude
|
private String formatTime(String time) {
return (time != null) ? time.substring(0, 2) + ":" + time.substring(2, 4) + ":" +
time.substring(4, 6) : "";
}
private String formatLat(String[] pos) {
return ((pos[0] != null) && (pos[1] != null)) ? pos[0].substring(0, 2) +
"* " + pos[0].substring(2, 4) + "' " + pos[0].substring(5, 9) + "\" " + pos[1] : "";
}
private String formatLong(String[] pos) {
return ((pos[0] != null) && (pos[1] != null)) ? pos[0].substring(0, 3) +
"* " + pos[0].substring(3, 5) + "' " + pos[0].substring(6, 10) + "\" " + pos[1] : "";
}
private String formatAlt(String[] alt) {
return ((alt[0] != null) && (alt[1] != null)) ? alt[0] + " " + alt[1] : "";
}
public String getTime() {
return formatTime(time);
}
public String getLongitude() {
return formatLong(longitude);
}
public String getLatitude() {
return formatLat(latitude);
}
public String getAltitude() {
return formatAlt(altitude);
}
|
Listing 4. Format and get methods
To read all UART data, we need to create a listener class called MyUARTListener
that implements UARTEventListener
with a synchronized method eventDispatched
(see Listing 5).
|
class MyUARTListener implements UARTEventListener {
@Override
public synchronized void eventDispatched(UARTEvent event) {
if (event.getID() == INPUT_DATA_AVAILABLE) {
ByteBuffer buffer = ByteBuffer.allocateDirect(10);
try {
int nrocar = uart.read(buffer);
char c;
for (int i = 0; i \< nrocar; i++) {
c = (char) buffer.get(i);
nmea = nmea.concat(String.valueOf(c));
}
if (nmea.contains("\\r\\n")) {
//System.out.println(nmea);
if (nmea.contains("$GPGGA")) {
processNMEA(nmea);
}
nmea = "";
}
} catch (IOException ex) {
Logger.getGlobal().log(Level.WARNING, ex.getMessage());
}
}
}
}
|
Listing 5. Listener class MyUARTListener
When data is available, we parse it with the processNMEA
method (see Listing 6). We use this method to read all the GPS data into a special String that begins with the $GPGGA
tag.
|
private void processNMEA(String message) {
try {
StringTokenizer tokens = new StringTokenizer(message, ",");
// pull off the first token and check if it is the message we want
//$GPGGA,130612.255,,,,,0,00,,,M,0.0,M,,00 (no info)
//$GPGGA,161229.487,3723.2475,N,12158.3416,W,1,07,1.0,9.0,M,,,,0000*18 (with info)
tokens.nextToken(); // $GPGGA position
// Next token is the time
time = tokens.nextToken();
latitude\[0\] = tokens.nextToken();
latitude\[1\] = tokens.nextToken();
if ((latitude\[0\].equals("0")) || (latitude\[1\].equals("00"))) {
Logger.getGlobal().log(Level.FINE, "Time: " + formatTime(time) + " - No GPS satellite link...");
} else {
longitude\[0\] = tokens.nextToken();
longitude\[1\] = tokens.nextToken();
// Skip the next three tokens
tokens.nextToken(); // Position indicator
tokens.nextToken(); // Satellites used
tokens.nextToken(); // Horizontal Dilution of precision
altitude\[0\] = tokens.nextToken();
altitude\[1\] = tokens.nextToken();
Logger.getGlobal().log(Level.FINE, "Time: " + getTime() + "\\n"
+ " Latitude: " + getLatitude() + " Longitude: " + getLongitude() + "\\n"
+ " Altitude: " + getAltitude() + "\\n");
}
} catch (Exception ex) {
}
}
|
Listing 6. Parser method processNMEA
Finally, free all resources by closing the UART object (see Listing 7).
|
public void close() {
...
uart.setEventListener(UARTEvent.INPUT_DATA_AVAILABLE, null);
uart.close();
...
}
|
Listing 7. Closing the UART object and freeing the listener
Let's now create the Test MIDlet class called TestGPSEM406A
to receive the time, latitude, longitude, and altitude from satellites, as shown in Listing 8.
|
public class TestGPSEM406A extends MIDlet {
private LoggingHandler loggerHandler = LoggingHandler.getInstance();
private GPSEM406ADevice gps;
@Override
public void startApp() {
loggerHandler.start();
Logger.getGlobal().setLevel(Level.ALL);
try {
gps = new GPSEM406ADevice();
} catch (IOException ex) {
ex.printStackTrace();
}
}
@Override
public void destroyApp(boolean unconditional) {
gps.close();
}
}
|
Listing 8. Test MIDlet for the EM-406a GPS device
Converting I2C Interface to UART by Connecting the I2C/SPI-to-UART Breakout Board
This is a breakout board for the SC16IS750, which is a handy chip used to convert I2C or SPI serial signals to a single-channel, high-performance UART interface. The input pins of the SC16IS750 are 5.5 V–tolerant, so this board should work with both 3.3 V and 5 V controllers. The following are some of its general features:
- Single full-duplex UART interface
- Selectable I2C-bus or SPI interface
- 64-byte first-in, first-out (FIFO) transmitter and receiver
- Full compatibility with industrial standard 16C450 and equivalent
- Baud rates up to 5 Mb/sec in 16x clock mode
- Auto hardware flow control using RTS/CTS
- Auto software flow control with programmable Xon/Xoff characters
- Single or double Xon/Xoff characters
- Receive and transmit FIFO levels
- Programmable special character detection
- Line break generation and detection
- Software reset
This chip has the following registers (addresses are shown in hexadecimal):
When LCR bit 7 = 0:
- 00h: XHR (Receive/transmit holding register)
- 02h: FCR (FIFO control register)
- 03h: LCR (line control register)
- 04h: MCR (Modem control register)
- 05h: LSR (line status register)
- 06h: MSR (modem status register)
- 08h: XLVL (transmit FIFO level register)
- 09h: RXLVL (receive FIFO level register)
When LCR bit 7 = 1:
- 00h: DLL
- 01h: DLH (division register)
To support these registers, create an enumeration called SC16IS750
, as shown in Listing 9.
|
public enum SC16IS750 {
XHR(0x00),
FCR(0x02),
LCR(0x03),
MCR(0x04),
LSR(0x05),
MSR(0x06),
TXLVL(0x08),
RXLVL(0x09),
DLL(0x00),
DLH(0x01);
public int cmd;
private SC16IS750(int cmd) {
//SC16IS740 expects a R/W bit first,
//followed by the 4 bit register address of the byte.
//So shift the bits left by three bits:
this.cmd = cmd \<\< 3;
}
...
}
|
Listing 9. Enumeration that defines all SC16IS750 registers
Let's now connect the SC16IS750 breakout board to the Raspberry Pi's 3.3 V, SCL, SDA, and GND pins, as shown in Figure 3, and create a Java ME 8 class called SC16IS750Device
with its constructor using the control address to initialize the converter registers, as shown in Listing 10.
|
public class SC16IS750Device extends I2CRpi {
private static final int SC16IS750\_write = 0x48;
public SC16IS750Device() throws IOException {
super(SC16IS750\_write);
configUARTregs();
}
|
Listing 10. SC16IS750Device
class with its constructor establishing the control address
Then create the following operation methods (see Listing 11):
configUARTregs
: Configure all registers to establish I2C-to-UART conversion
write
: Write a String to buffer data
bytesToRead
: Bytes at buffer to be read
read
: Read all bytes at buffer
|
private void configUARTregs() {
...
//Line Control Register: Enable Writing DLH & DLL
//& set no Parity, 1 stop bit, and 8 bit word length
SC16IS750.LCR.write(device, (byte) 0b10000011);
//Division registers DLL & DLH
// Write '96' to get 9600 baud rate
// Assumes you have the version with the ~14MHz crystal
// (16x9600 = 153600 = 14.7456Mhz/96)
SC16IS750.DLL.write(device, (byte) 96);
SC16IS750.DLH.write(device, (byte) 00);
//Line Control Register: Disable Writing DLH & DLL
//Same setup
SC16IS750.LCR.write(device, (byte) 0b00000011);
//Modem Control Register
//Normal Operating Mode
SC16IS750.MCR.write(device, (byte) 0b00000000);
//FIFO Control Register: Enable the FIFO and no other features
SC16IS750.FCR.write(device, (byte) 0b00000111);
...
}
public void write(String cad) {
ByteBuffer buffer = ByteBuffer.allocateDirect(cad.length());
buffer.put(cad.getBytes());
buffer.clear();
try {
device.write(SC16IS750.XHR.cmd, 1, buffer);
} catch (IOException ex) {
Logger.getGlobal().log(Level.WARNING,ex.getMessage());
}
}
public int bytesToRead(){
return SC16IS750.RXLVL.read(device);
}
public int read(){
return SC16IS750.XHR.read(device);
}
|
Listing 11. Operation methods
Connecting the Text-to-Speech Module to the I2C/SPI-to-UART Breakout Board
The Emic 2 text-to-speech module is a multilanguage voice synthesizer that converts a stream of digital text into natural sounding speech. Its simple command-based interface makes it easy to integrate into any embedded system.
Here are some of its general features.
- High-quality speech synthesis for English and Spanish languages
- Nine predefined voice styles comprising male, female, and child
- Dynamic control of speech and voice characteristics, including pitch, speaking rate, and word emphasis
- Industry-standard DECtalk text-to-speech synthesizer engine (5.0.E1)
- Communication: Asynchronous 9600 b/sec serial (8N1)
- On-board audio power amplifier and 1/8-inch (3.5 mm) audio jack
Let's now connect the Emic 2 module's GND, SOUT, and SIN pins to the SC16IS750 breakout board's GND, RX, and TX pins, respectively, and connect its 5 V pin to the Raspberry Pi's 5 V pin, as shown in Figure 3. Then, create a Java ME 8 class called EMIC2Device
with its constructor that creates an SC16IS750 object using class SC16IS750Device
, as shown in Listing 12.
|
public class EMICI2CDevice {
private SC16IS750Device sc;
// Define all messages for Emic
private final String\[\] emic2Msgs = {
"Emic 2 Ok.", //0
"Hello my friends.", //1
"I am testing I2C to UART converter.", //2
"See you later.", //3
"Bye.", //4
"Hola como estan.", //5
"Ahora hablo Espanol.", //6
"Adios amigos."//7
};
public EMICI2CDevice() throws IOException {
//I2C Emic interface using SC16IS750
sc = new SC16IS750Device();
}
|
Listing 12. EMIC2Device
class with its constructor creating an SC16IS750 object
Create the following operation methods (see Listing 13):
msg
: Write a predefined message
getMsg
: Get a predefined message
write
: Write a String
writeCommand
: Send a command
waitResponse
: Wait for Emic 2 response characters
close
: Close UART communication
|
public void msg(int msgnum) {
write(emic2Msgs[msgnum]);
Logger.getGlobal().log(Level.FINE, emic2Msgs[msgnum]);
}
public String getMsg(int num) {
return emic2Msgs[num];
}
public void write(String cad) {
cad = "S " + cad.concat("\r\n");
sc.write(cad);
// Wait for response from Emic-2. It responds to all commands with :
waitResponse(2);
}
public void writeCommand(String cad) {
cad = cad.concat("\r\n");
sc.write(cad);
// Wait for response from Emic-2.
waitResponse(5);
}
private void waitResponse(int nrobytes) {
int work = 0;
//wait two chars :\n
while (work != nrobytes) {
work = sc.bytesToRead();
}
for (; 0 < work; work--) {
sc.read();
}
}
public void close() {
sc.close();
}
|
Listing 13. Operation methods
We can connect an headphone to the onboard 1/8-inch (3.5 mm) audio jack, but if we want to use a little speaker, the SparkFun mono audio amplifier module TPA2005D1 is recommended, as shown in Figure 3, with an additional 10K volume control potentiometer. We must connect SP- and SP+ from the Emic 2 to IN- and IN+ on the amplifier module's connector.
Let's now create the test MIDlet class called TestEMIC2
to hear the Emic 2 speak predefined messages in English and Spanish, as shown in Listing 14.
|
public class TestEMIC2 extends MIDlet {
@Override
public void startApp() {
...
EMICI2CDevice sc = new EMICI2CDevice();
sc.writeCommand("W200");
sc.writeCommand("L0"); //Set English
sc.writeCommand("N0");
sc.msg(0);
sc.msg(1);
sc.msg(2);
sc.msg(3);
sc.msg(4);
sc.write("End of English Test.");
sc.writeCommand("W200");
sc.writeCommand("L1"); //Set Spanish
sc.writeCommand("N0");
sc.msg(5);
sc.msg(6);
sc.msg(7);
sc.write("Fin de la prueba en Espanol.");
sc.close();
...
}
...
}
|
Listing 14. Test MIDlet for Emic 2
Performing Some Additional Configuration
Before running our TestEM406A
MIDlet using NetBeans IDE 8.0.2, it is important to establish the appropriate API permissions. To do this, in the IDE, select project JavaMEDemos, and then right-click and select Properties to show the Project Properties window. Select Application Descriptor, and then select the API Permissions tab. Include the following permission, as shown in Figure 4:
|
jdk.dio.uart.UARTPermission *:* , open
|

Figure 4. Establishing API permissions
Conclusion
The Raspberry Pi has a UART port that can be used for serial communication with some devices such as the EM-406a GPS device, but remember that it is very important to check voltage levels, because this port works at 3.3 V, not at the classic serial level of +/- 12 V.
By default, the UART port is used by the serial console and it must be released before you can manipulate it.
If you need to connect additional serial devices, you can use SC16IS750 modules that convert the I2C or SPI protocols to UART. You can use up to fifteen modules with distinct I2C addresses, and you can manipulate up to fifteen serial devices.
In the next article in this series, we will examine other types of sensors using other types of interfaces, such as serial peripheral interface bus (SPI).
See Also
About the Author
Jose Cruz (@joseacruzp) is a software engineer who has been working with Java since 1998. He is a lead developer of Java, Java ME, and Java EE at Ferreteria EPA C.A. in Venezuela. From an early age, his hobby has been electronics. This has led him to combine computing with electronics and develop projects where Java and embedded devices such as Arduino and Raspberry Pi are used.
Join the Conversation
Join the Java community conversation on Facebook, Twitter, and the Oracle Java Blog!