You can use the serial communications interface to communicate with devices such as displays, sensors, and data acquisition systems.

The ConnectCore 8M Mini provides access to three UART interfaces:

  • UART3 four-wire RS-232 (RX/TX/RTS/CTS) on J45 connector:

    • Rx: J45 pin 3

    • Tx: J45 pin 1

    • CTS: J45 pin 9

    • RTS: J45 pin 7

  • UART4 is routed to the XBee User connector:

    • Rx: J41 pin 2

    • Tx: J41 pin 3

    • CTS: J42 pin 9

    • RTS: J42 pin 5

  • MCA UART2 is routed to the XBee Cellular socket and to the J45 expansion connector:

    • Rx on MCA_IO2 - J39 pin 2 / J45 pin 4

    • Tx on MCA_IO1 - J39 pin 3 / J45 pin 2

    • CTS on MCA_IO3 - J40 pin 9 / J45 pin 10

    • RTS on MCA_IO4 - J40 pin 5 / J45 pin 8

These three UART interfaces have hardware flow control lines (RTS and CTS).

See the ConnectCore 8M Mini Hardware Reference Manual for information about the available serial ports.

Digi adds to Android an API to manage these serial port interfaces. With this API, you can perform common operations such as configuring the connection, sending and receiving data, etc. See the Digi APIx javadoc for a complete list of the available methods in this API.

Unless noted, all serial API methods require com.digi.android.permission.SERIAL permission.

If your application does not have the *com.digi.android.permission.SERIAL permission it will not have access to any serial port service feature.

First, a new SerialPortManager object must be instantiated by passing the Android Application Context.

Instantiating the SerialPortManager
import android.app.Activity;
import android.os.Bundle;

import com.digi.android.serial.SerialPortManager;

public class SerialPortSampleActivity extends Activity {

    SerialPortManager serialPortManager;

    [...]

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Instantiate the Serial Port manager object.
        serialPortManager= new SerialPortManager(this);

        [...]
    }

    [...]
}

The serial port API allows you to:

Open/close a serial port

The SerialPortManager allows you to create a SerialPort object for a specific port name. The listSerialPorts() method lists all the available serial ports in the device.

You can also specify other serial port parameters using a SerialPortParameters object or directly in the openSerialPort() method.

Method Description

openSerialPort(String)

Creates and opens a SerialPort object for the provided port name.

openSerialPort(String, SerialPortParameters)

Creates and opens a SerialPort object for the provided port name with the given configuration.

openSerialPort(String, int, int, int, int, int, int)

Creates and opens a SerialPort object for the provided port name with the given port parameters.

When they are not specified, the default serial port parameter values are:

  • Baud rate: 9600

  • Data bits: 8

  • Stop bits: 1

  • Parity: None

  • Flow control: None

  • Read timeout: 3 seconds

The SerialPort class includes constant definitions for the possible values of data bits, stop bits, parity, and flow control. For example: SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE, SerialPort.FLOWCONTROL_NONE.

The three methods that open the serial port may fail for the following reasons:

  • If the serial port name is invalid or does not exist, the openSerialPort() method throws a NoSuchPortException.

  • If the serial port is already in use by other application(s), the openSerialPort() method throws a PortInUseException.

  • If the serial port parameters are invalid, the openSerialPort() method throws a UnsupportedCommOperationException.

Opening serial ports
import com.digi.android.serial.SerialPort;
import com.digi.android.serial.SerialPortManager;
import com.digi.android.serial.SerialPortParameters;

[...]

SerialPortManager serialPortManager = ...;

// Get the list of available serial ports.
String[] portNames = serialPortManager.listSerialPorts();

// Define a serial configuration: 115200/8/N/1 and hardware flow control.
SerialPortParameters params = new SerialPortParameters(
		9600,			     					      /* baudrate:     9600 */
		SerialPort.DATABITS_8,						      /* data bits:    8 */
		SerialPort.STOPBITS_1,						      /* stop bits:    1 */
		SerialPort.PARITY_NONE,		     				      /* parity:       none */
		SerialPort.FLOWCONTROL_RTSCTS_IN | SerialPort.FLOWCONTROL_RTSCTS_OUT, /* flow ctrl:    hardware */
		2000								      /* read timeout: 2s */);

// Open the available serial ports with the previous configuration.
SerialPorts[] ports = new SerialPorts[portNames.length];
for (int i = 0; i < portNames.length; i++)
    ports[i] = serialPortManager.openSerialPort(portNames[i], params);

[...]

Once you have finished with a serial port, you must close it. This frees the port so that other applications can use it.

To close a serial port, use the close() method of the SerialPort object.

Closing serial ports
import com.digi.android.serial.SerialPort;
import com.digi.android.serial.SerialPortManager;

[...]

SerialPortManager serialPortManager = ...;
SerialPort port = ...;

[...]

// Close the serial port.
port.close();

[...]

Configure a serial port

Before sending and receiving data, the serial port must be configured. You can set the configuration when opening a port, but the SerialPort class also offers methods to get and change values once the port is opened:

Method Description

getName()

Retrieves the serial port name.

getBaudRate()

Retrieves the configured baud rate.

getDataBits()

Retrieves the configured number of data bits.

getStopBits()

Retrieves the configured number of stop bits.

getParity()

Retrieves the configured parity.

getFlowControl()

Retrieves the configured flow control.

setPortParameters(int, int, int, int, int)

Configures the serial port with the given values for baud rate, number of data bits, number of stop bits, parity, and flow control.

The port configuration may fail if the given port parameters are invalid. In this case the setPortParameters() method throws a UnsupportedCommOperationException.

Configuring a serial port
import com.digi.android.serial.SerialPort;
import com.digi.android.serial.SerialPortManager;

[...]

SerialPortManager serialPortManager = ...;
SerialPort port = ...;

[...]

// Read old configuration.
System.out.println("Port: " + port.getName() + "\n"
				 + "   Baud rate: " + port.getBaudRate() + "\n"
				 + "   Data bits: " + port.getDataBits() + "\n"
				 + "   Stop bits: " + port.getStopBits() + "\n"
				 + "   Parity: " + port.getParity() + "\n"
				 + "   Flow control: " + port.getFlowControl());

// Set a new configuration: 38400/8/E/1, no flow control.
port.setPortParameters(38400, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
        SerialPort.PARITY_EVEN, SerialPort.FLOWCONTROL_NONE);

[...]
When you are done with the serial port you need to close it by calling the close() method.

Monitor serial port events

You can monitor different serial port events, such as data available, lines state changes, or errors during the communication. To do so, follow these steps:

Step 1: Subscribe to serial port notifications

Use the registerEventListener(ISerialPortEventListener) method of the SerialPort object to subscribe to serial port notifications.

Registering for serial port events notifications
import com.digi.android.serial.SerialPort;

[...]

SerialPort port = ...;

// Create the serial port listener.
MySerialPortListener mySerialPortListener = ...;

// Register the serial port listener.
port.registerEventListener(mySerialPortListener);

[...]

The subscribed listener class, MySerialPortListener, must implement the ISerialPortEventListener interface. This interface defines the serialEvent(SerialPortEvent) method that is called every time a new serial port event occurs.

The received event is represented by the SerialPortEvent object. To distinguish between the different notifications, use the getEventType() method to get the EventType and filter.

ISerialPortEventListener implementation example, MySerialPortListener
import com.digi.android.serial.ISerialPortEventListener;
import com.digi.android.serial.SerialPort;
import com.digi.android.serial.SerialPortEvent;

public class MySerialPortListener implements ISerialPortEventListener {
    @Override
    public void serialEvent(SerialPortEvent event) {
        switch (event.getEventType()) {
            case BI:
                System.out.println("Break interrupt received");
                break;
            case CD:
                System.out.println("Carrier detect received");
                break;
            case CTS:
                System.out.println("CTS line activated");
                break;
            case DSR:
                System.out.println("DSR line activated");
                break;
            case RI:
                System.out.println("Ring Indicator received");
                break;
            case FE:
                System.out.println("Received framing error");
                break;
            case PE:
                System.out.println("Received parity error");
                break;
            case OE:
                System.out.println("Buffer overrun error");
                break;
            case DATA_AVAILABLE:
                System.out.println("Data to read available");
                break;
            default:
                System.out.println("Unknown event");
                break;
        }
    }
}

Only one listener can be subscribed per SerialPort object. If you try to register more, a TooManyListenerException will be thrown.

Step 2: Enable notifications

Once the listener is implemented, you can use it to listen to particular serial port events.

By default, the listener is not notified about any of the previous events. To notify the listener, request the reception of each SerialPort event type individually. Use the following methods:

Method Description

notifyOnBreakInterrupt(boolean)

Enables/disables Break interrupt notifications.

notifyOnCarrierDetect(boolean)

Enables/disables Carrier Detect notifications.

notifyOnCTS(boolean)

Enables/disables CTS notifications.

notifyOnDataAvailable(boolean)

Enables/disables data to read is available.

notifyOnDSR(boolean)

Enables/disables DSR notifications.

notifyOnFramingError(boolean)

Enables/disables framing error notifications.

notifyOnOutputEmpty(boolean)

Enables/disables output buffer empty notifications.

notifyOnOverrunError(boolean)

Enables/disables overrun error notifications.

notifyOnParityError(boolean)

Enables/disables parity error notifications.

notifyOnRingIndicator(boolean)

Enables/disables Ring Indicator notifications.

Enabling serial port notifications
import com.digi.android.serial.SerialPort;

[...]

SerialPort port = ...;
MySerialPortListener mySerialPortListener = ...;

port.registerEventListener(mySerialPortListener);

// Enable notifications.
port.notifyOnDataAvailable(true);
port.notifyOnCTS(true);
port.notifyOnDSR(true);
port.notifyOnRingIndicator(true);
port.notifyOnBreakInterrupt(true);
port.notifyOnCarrierDetect(true);
port.notifyOnFramingError(true);
port.notifyOnOverrunError(true);
port.notifyOnParityError(true);

[...]

Step 3: Unsubscribe to serial port notifications

If you no longer wish to be notified about any serial port event, use the unregisterEventListener() method to unsubscribe to the registered listener.

Unregistering serial port event notifications
import com.digi.android.serial.SerialPort;

[...]

SerialPort port = ...;
MySerialPortListener mySerialPortListener = ...;

port.registerEventListener(mySerialPortListener);

[...]

// Remove the serial port event listener.
port.unregisterEventListener();

[...]

When you are done with the serial port you need to:

  1. Unsubscribe your registered listener (if any) with the unregisterEventListener() method.

  2. Then close the port by calling the close() method.

Communicate with serial devices

When the serial port is open, you can communicate with the serial device connected to it (transmit and receive data).

Send data

You can send data through the serial port in two ways:

  • Write directly to the serial port using the writeData(byte[] data, int numBytes) method.

  • Get the serial port’s OutputStream object and invoke one of the existing write() methods.

Sending serial data
import java.io.OutputStream;
import com.digi.android.serial.SerialPort;

[...]

SerialPort port = ...;

String dataToSend = "This is the data to send";

// Send data through the serial port directly.
port.writeData(dataToSend.getBytes(), dataToSend.getBytes().length);

// Get the port output stream.
OutputStream outStream = port.getOutputStream();

// Send data through the serial port using the output stream.
outStream.write(dataToSend.getBytes());

[...]

When you are done with the serial port you need to:

  1. Unsubscribe your registered listener (if any) with unregisterEventListener() method.

  2. Then close the port by calling the close() method.

Receive data

You can receive data from the serial port in two ways:

  • Read directly from the serial port using the readData(byte[] buffer, int numBytes) method.

  • Get the serial port’s InputStream object and invoke one of the existing read() methods.

Receiving serial data
import java.io.InputStream;
import java.io.IOException;
import com.digi.android.serial.SerialPort;

private static final int BUFFER_SIZE = 1024;

[...]

SerialPort port = ...;

[...]

private void readData() {
    try {
        byte[] readBuffer = new byte[BUFFER_SIZE];

        // Read the serial port.
        int numBytes = port.readData(readBuffer, BUFFER_SIZE);

        if (numBytes > 0)
            System.out.println("Read: " + new String(readBuffer, 0, numBytes));
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private void readDataInputStream() {
    // Get the port input stream.
    InputStream inStream = port.getInputStream();

    int availableBytes = inStream.available();
    if (availableBytes > 0) {
        byte[] readBuffer = new byte[availableBytes];

        // Read the serial port input stream.
        int numBytes = inStream.read(readBuffer, 0, availableBytes);

        if (numBytes > 0)
            System.out.println("Read: " + new String(readBuffer, 0, availableBytes));
    }
}

[...]

Use the data available serial port event (EventType.DATA_AVAILABLE) of your registered ISerialPortEventListener to know when to read data:

ISerialPortEventListener implementation example, MySerialPortListener
import com.digi.android.serial.ISerialPortEventListener;
import com.digi.android.serial.SerialPort;
import com.digi.android.serial.SerialPortEvent;

public class MySerialPortListener implements ISerialPortEventListener {
    @Override
    public void serialEvent(SerialPortEvent event) {
        switch (event.getEventType()) {
        case DATA_AVAILABLE:
            /* Uncomment one of the following methods to read data. */
            // readData();
            // readDataInputStream()
            break;
        }
    }
}

Do not forget to subscribe your listener to receive data available events and to enable this notification on your SerialPort object.

Registering for serial port events notifications
import com.digi.android.serial.SerialPort;

[...]

SerialPort port = ...;

// Create the serial port listener.
MySerialPortListener mySerialPortListener = ...;

// Register the serial port listener.
port.registerEventListener(mySerialPortListener);

// Enable data available notifications.
port.notifyOnDataAvailable(true);

[...]

When you are done with the serial port you need to:

  1. Unsubscribe your registered listener (if any) with unregisterEventListener() method.

  2. Then close the port by calling the close() method.

Manage serial port lines

The SerialPort class also offers different methods to get the current state of the port lines and set some of them:

Method Description

isCD()

Gets the state of the CD (Carrier Detect) line.

isCTS()

Gets the state of the CTS (Clear To Send) line.

isDSR()

Gets the state of the DSR (Data Set Ready) line.

isDTR()

Gets the state of the DTR (Data Terminal Ready) line.

setDTR(boolean)

Sets or clears the DTR (Data Terminal Ready) line.

isRI()

Gets the state of the RI (Ring Indicator) line.

isRTS()

Gets the state of the RTS (Request To Send) line.

setRTS(boolean)

Sets or clears the RTS (Request To Send) line.

sendBreak(int)

Sends a break signal in the duration of the specified number of milliseconds.

Managing serial port lines
import com.digi.android.serial.SerialPort;
import com.digi.android.serial.SerialPortManager;

[...]

SerialPortManager serialPortManager = ...;
SerialPort port = ...;

[...]

// Read lines state.
System.out.println("CD: " + port.isCD());
System.out.println("CTS: " + port.isCTS());
System.out.println("DSR: " + port.isDSR());
System.out.println("DTR: " + port.isDTR());
System.out.println("RI: " + port.isRI());
System.out.println("RTS: " + port.isRTS());

// Set lines state.
port.setDTR(true);
port.setRTS(true);

// Send a break signal of 1 second.
port.sendBreak(1000);

[...]

When you are done with the serial port you need to:

  1. Unsubscribe your registered listener (if any) with unregisterEventListener() method.

  2. Then close the port by calling the close() method.

Serial example

The Serial Port Sample Application demonstrates the usage of the serial port API. In this example, you can list all the available serial ports, configure them, and send and receive data.

You can import the example using Digi’s Android Studio plugin. For more information, see Import a Digi sample application. To look at the application source code, go to the GitHub repository.