1

I am trying to write a simple Java program to display the analog input fed to an Arduino board upon a stimulus from a user on some physical sensors.

If I use the Arduino program itself and look at the serial monitor, I see the analog read based on user input updated instantly. However, when reading the input from my Java program, there is a significant delay (8-12 seconds) between the physical stimulus and the display of the value.

The code uploaded to Arduino is very simple, just cycle through a bunch of analog ports and print the data:

void setup() {
  Serial.begin(9600);
  pinMode(30, OUTPUT);
}

void loop() 
  digitalWrite(30, HIGH);

  for (int i = 0; i < 10; i++) {
    int sensorValue = analogRead(i);
    Serial.print(String(i) + ":");
    Serial.println(sensorValue);
  }
}

The Java code I am using is almost entirely copied from the example provided here, with the exception that I updated the port name to work for my system (Mac OS X 10.10.3).

Here is the Java code:

import java.io.BufferedReader;
import java.io.InputStreamReader;
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/cu.usbmodem1411",
            "/dev/cu.usbmodem1451",
    };

    /**
    * A BufferedReader which will be fed by a InputStreamReader 
    * converting the bytes into characters 
    * making the displayed results codepage independent
    */
    private BufferedReader 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;

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

        //First, Find an instance of serial port as set in PORT_NAMES.
        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 = new BufferedReader(new InputStreamReader(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 {
                String inputLine=input.readLine();
                System.out.println(inputLine);
            } 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();
        Thread t=new Thread() {
            public void run() {
                //the following line will keep this app alive for 1000 seconds,
                //waiting for events to occur and responding to them (printing incoming messages to console).
                try {Thread.sleep(1000000);} catch (InterruptedException ie) {}
            }
        };
        t.start();
        System.out.println("Started");
    }
}

All the program does is print out the values of the Arduino serial monitor, yet it takes way longer (8-12 seconds) through Java than it does from the Arduino program itself.

1) Why is this happening?
2) How do I fix it?

8
  • Are you sending full lines each time, including a full line terminator? – Ignacio Vazquez-Abrams Jul 10 '15 at 13:17
  • I am just a beginner, I think you mean a string terminator to denote a new line, but I am not sure what you are referring to. You can see all of the code. And it does work both in Arduino IDE and in Java just that Java takes much much longer, so I would think that is not the issue? Please elaborate – tam5 Jul 10 '15 at 13:42
  • After the 8-12 seconds, does it keep displaying at a fast enough speed? – Paul Jul 10 '15 at 13:55
  • This seems like a host OS / serial API / Java problem not an Arduino one. Try putting the readLine method in a blocking loop in your main thread to simplify things. Historically, there have been some very pathalogical interactions between application layers, operating systems, USB-serial firmwares, and the corresponding drivers - I've seen something that worked just fine with a C program effectively read at around 100 baud with a .NET client running on the same machine. – Chris Stratton Jul 10 '15 at 15:03
  • Try adding a delay (eg. delay(200);) into your main loop. You may be sending data too fast for the Java app to cope with. – Nick Gammon Jul 10 '15 at 21:15
1

Timing tests show that the Java code lags something like 25 or more seconds behind the input from the Arduino. This can be fixed by changing in the Java code these lines:

       try {
            String inputLine=input.readLine();
            System.out.println(inputLine);
        } catch (Exception e) {

to:

       try {
            while (input.ready ())
              {
              String inputLine=input.readLine();
              System.out.println(inputLine);
              }
        } catch (Exception e) {

This extra loop "drains" the input buffer of data in the one serialEvent call, rather than relying upon a serialEvent call per line of data.

I found when testing that the output is now highly responsive, even without a delay on the Arduino side.


My other answer of adding a delay call slowed output down enough for the Java app to catch up, but I must admit it isn't the best solution.

0

One more thing you could do is create a while(true) loop, after initialize() and call input.readLine(), so that you don't wait for the event to appear.

For some reason the event is delayed, but doing the while loop hasn't created any issues or exceptions for me so far.

My code is like this:

public void initialize() {
   //initializing stuff
   while(true){
       try {
           String inputLine = input.readLine();
           //do stuff here
       } catch (Exception e) {
           System.err.println(e.toString());
       }
   }   
}
-1

Try adding a delay, eg.

delay(200);

into your main loop. You may be sending data too fast for the Java app to cope with.

2
  • No. An arduino transmitting at a mere 9600 baud should not be able to outpace the program, unless there is something pathologically wrong with the program or the installation it is running on. If this has any effect at all, it was probably only in changing some highly unfortunate "just missed it" to some slightly less problematic one, but that actual problem remains and may rear its head again in a different configuration. This is not a sound solution or based on any sensible reasoning - if it helps at all, it is only by lucky accident. – Chris Stratton Jul 18 '15 at 0:33
  • Thank you for your comments, Chris. Rather than trying to salvage this answer (and have your comment make no sense) I have posted another answer. – Nick Gammon Jul 18 '15 at 2:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.