Search code examples
javaserial-portarduinoreal-timejfreechart

Adding serial communication data to a real-time Java graph


Right now I have a Java file that can read serial data, and it just prints it in the console. In another file, I have a JFreeChart graph that generates random data into a real-time graph.

These files are both in the same project. How could I add the serial data to the real time graph, instead of the random data I am using now?

The first code below is the file of the real-time graph.

public class test224 extends ApplicationFrame implements ActionListener {
    String s;
    String tempbuff;
    private TimeSeries series;

    private double lastValue = 10.0;

    private Timer timer = new Timer(1000, this);
    DynamicTimeSeriesCollection dataset;
    float[] newData = new float[1];

    public test224(final String title) {
        super(title);
        this.series = new TimeSeries("Sensor Value", Millisecond.class);

        final TimeSeriesCollection dataset = new TimeSeriesCollection(this.series);
        final JFreeChart chart = createChart(dataset);
        timer.setInitialDelay(1000);
        chart.setBackgroundPaint(Color.LIGHT_GRAY);
        final JPanel content = new JPanel(new BorderLayout());
        final ChartPanel chartPanel = new ChartPanel(chart);
        content.add(chartPanel);
        chartPanel.setPreferredSize(new java.awt.Dimension(800, 500));
        setContentPane(content);
        timer.start();
    }

    private JFreeChart createChart(final XYDataset dataset) {
        final JFreeChart result = ChartFactory.createTimeSeriesChart(
            "Dynamic Line Chart of Arduino Data",
            "Zeit",
            "Wert",
            dataset,
            true,
            true,
            false
        );

        final XYPlot plot = result.getXYPlot();

        plot.setBackgroundPaint(new Color(0xffffe0));
        plot.setDomainGridlinesVisible(true);
        plot.setDomainGridlinePaint(Color.lightGray);
        plot.setRangeGridlinesVisible(true);
        plot.setRangeGridlinePaint(Color.lightGray);

        ValueAxis xaxis = plot.getDomainAxis();
        xaxis.setAutoRange(true);

        xaxis.setFixedAutoRange(60000.0);
        xaxis.setVerticalTickLabels(true);

        ValueAxis yaxis = plot.getRangeAxis();
        yaxis.setRange(0.0, 300.0);

        return result;
    }

    public void actionPerformed(final ActionEvent e) {
        final double factor = 0.9 + 0.2*Math.random();
        this.lastValue = this.lastValue * factor;

        final Millisecond now = new Millisecond();
        this.series.add(new Millisecond(), this.lastValue);
    }

    public static void main(final String[] args) {
        final test224 demo = new test224("Dynamic Line And TimeSeries Chart");
        demo.pack();

        RefineryUtilities.centerFrameOnScreen(demo);
        demo.setVisible(true);
    }

In the action performed is where the random data is generated. Then in this code of the other file, this prints the Arduino serial data to the console, so how could I get this to show up in the real time graph?

public synchronized void serialEvent(SerialPortEvent oEvent) {
    if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
        try {
            inputLine=input.readLine();
            System.out.println(inputLine);

        }
        catch (Exception e) {
        }
    }
}

Solution

  • You need to either move the content of your serialEvent into your actionPerformed method by merging the classes or provide a public addDataPoint(double point) method in test224 that can be called from the class containing your serial event. I think that in either case you will need to move the processing in serialEvent into a separate thread.

    You could try something like this:

    @Override
    public void actionPerformed(final ActionEvent e) {
    
      //Initialise the serial port here before starting the new thread
    
      Runnable task = new Runnable() {
    
        @Override
        public void run() {
            if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
                try {
                    inputLine = input.readLine();
                    System.out.println( inputLine );
                    final Millisecond now = new Millisecond();
                    series.add(new Millisecond(), Double.parseDouble(inputLine));  
    
                } catch (Exception e) {                             
                }
            }
        }
      };
      Thread serialThread = new  Thread(task);
      serialThread.start();
    }
    

    The public addDataPoint(double point) solution is preferable as you maintain a separation of concerns.

    public synchronized void serialEvent(SerialPortEvent oEvent) {
        if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
            try {
                inputLine=input.readLine();
                System.out.println(inputLine);
                dataChart.addPoint(Double.parseDouble(inputLin));
    
            } catch (Exception e) {                             
            }
        }
    }
    

    You will still need to ensure that the Serial Point is being monitored on a separate thread.