ask ask - 16 days ago 6
C Question

Problems interfacing Arduino with Java

I followed the Interfacing with Java tutorial, but ran into some problems on testing. Currently my Arduino program works fine when I try with manual inputs.

Segment of C Code on Arduino:

char line[20];
int line_pos = 0;
char action[10];
unsigned long duration;
boolean data_ready = false;

void setup()
{
Serial.begin(9600);
delay(1500);
Serial.println("Ready!");
}

void loop()
{
char state;
if(Serial.available()>0)
{
delay(20);
state = Serial.read();

if(state == '!')
{
data_ready = true;
line[line_pos] = state;
line_pos = 0;
}
else
{
line[line_pos] = state;
line_pos = line_pos + 1;
}

if(data_ready == true)
{
split(line);
Serial.print(action);
delay(20);
Serial.print("!");
delay(20);
Serial.print(duration);
delay(20);
Serial.print("!");

Serial.println("Data has been split");

if(strcmp(action, "left") == 0)
{
Serial.println("Received left");
}
else if(strcmp(action, "right") == 0)
{
Serial.println("Received rightt");
}
else if(strcmp(action, "straight") == 0)
{
Serial.println("Received straight");
}

memset(line, 0, 20);
data_ready = false;
}
}
}

void split(char input[20])
{
char *param, *ptr;

param = strtok_r(input, "#", &ptr);
strncpy(action, param, sizeof(action));
action[sizeof(action)-1] = '\0';

param = strtok_r(NULL, "!", &ptr);
duration = strtoul(param, &param, 10);
}


When I input "left#123!" I get the following output in the COM which comes out as expected. No problems here:

Ready!
left!123!Data has been split
Received left


Now here's my Java code:

import java.io.InputStream;
import java.io.OutputStream;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import java.util.Enumeration;

public class SerialTest implements SerialPortEventListener {
SerialPort serialPort;
/** The port we're normally going to use. */
private static final String PORT_NAMES[] = {
"/dev/tty.usbserial-A9007UX1", // Mac OS X
"/dev/ttyUSB0", // Linux
"COM3", // Windows
};
/** Buffered input stream from the port */
private InputStream input;
/** The output stream to the port */
private OutputStream output;
/** Milliseconds to block while waiting for port open */
private static final int TIME_OUT = 2000;
/** Default bits per second for COM port. */
private static final int DATA_RATE = 9600;

private String display = "";

public void initialize() {
CommPortIdentifier portId = null;
Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();

// iterate through, looking for the port
while (portEnum.hasMoreElements()) {
CommPortIdentifier currPortId = (CommPortIdentifier) portEnum.nextElement();
for (String portName : PORT_NAMES) {
if (currPortId.getName().equals(portName)) {
portId = currPortId;
break;
}
}
}

if (portId == null) {
System.out.println("Could not find COM port.");
return;
}

try {
// open serial port, and use class name for the appName.
serialPort = (SerialPort) portId.open(this.getClass().getName(),
TIME_OUT);

// set port parameters
serialPort.setSerialPortParams(DATA_RATE,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);

// open the streams
input = serialPort.getInputStream();
output = serialPort.getOutputStream();

// add event listeners
serialPort.addEventListener(this);
serialPort.notifyOnDataAvailable(true);
} catch (Exception e) {
System.err.println(e.toString());
}
}

/**
* This should be called when you stop using the port.
* This will prevent port locking on platforms like Linux.
*/
public synchronized void close() {
if (serialPort != null) {
serialPort.removeEventListener();
serialPort.close();
}
}

/**
* Handle an event on the serial port. Read the data and print it.
*/
public synchronized void serialEvent(SerialPortEvent oEvent) {
if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
try {
int available = input.available();
byte chunk[] = new byte[available];
input.read(chunk, 0, available);

System.out.println(display);

display += (new String(chunk)).trim();
if(display.contains("!"))
{
display = display.substring(0, display.indexOf("!"));

if(display.equals("Ready"))
{
String reply = "left#123!";
byte reply_byte[] = new byte[reply.length()];
reply_byte = reply.getBytes("UTF-16LE");
output.write(reply_byte);
}

display = "";
}

} catch (Exception e) {
System.err.println(e.toString());
}
}
// Ignore all the other eventTypes, but you should consider the other ones.
}

public static void main(String[] args) throws Exception {
SerialTest main = new SerialTest();
main.initialize();
System.out.println("Started");
}
}


Here's what Java code prints out in console:

Rea
l
0
Data
Datahas
Datahasbee
Datahasbeen sp
Datahasbeen split


The serial input doesn't seem to be read fluidly, but in repeating parts. Can anybody help me diagnose the problem and give me a solution?

Answer

There are at least two things to watch out for here, but this is based on my experience of languages other than Java so it might not behave in exactly the same way.

The first is that the event looks like it is being fired after a small number of characters, perhaps your Arduino does not send fast enough for it to all appear at once. This causes your Java code to repeat the output because you are printing display and then appending the new chunk to it. If you do not find an exclamation mark it never gets cleared out. You might be better off printing chunk to see exactly what you have received.

The second is that you might only receive events for new data, so if new data comes in between the event getting fired and you calling input.read() then another event is not fired. You are best to have a loop in your event handler that continues while there is still data available, e.g.:

public synchronized void serialEvent(SerialPortEvent oEvent) {
    if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
        try {
            int available = input.available();
            while (available > 0)
            {
                byte chunk[] = new byte[available];
                input.read(chunk, 0, available);

                System.out.println(new String(chunk));

                // Perform your packet processing here

                // See if there is any more data that came in while we were
                // processing the event
                available = input.available();
            }

        } catch (Exception e) {
            System.err.println(e.toString());
        }
    }
    // Ignore all the other eventTypes, but you should consider the other ones.
}
Comments