串行连接(Arduino --> Java)

wel*_*ton 5 javafx arduino

这将是我的第一篇文章,我将尽力做到清晰简洁。我检查了该论坛上的其他一些帖子,但无法找到满意的答案。

我的问题涉及 JavaFX 和 jSSC(java 简单串行连接)库的使用。我设计了一个非常简单的 GUI 应用程序,它将托管四个不同的图表。其中两张图表将显示过去一小时内温度和太阳能传感器的读数,而另外两张图表则显示较长一段时间内(14 小时)的数据。最终,我想让它更加灵活,并在读数大致为零(夜间)时将应用程序设置为“睡眠”。

如何流式传输数据以实时显示这些数据?

在参考了一些在线资源和“JavaFX 8 Intro.by Examples”之后,我已经能够构建大部分串行连接类。我在处理数据读数时遇到问题,无法将其显示在图表上。

public class SerialComm  implements SerialPortEventListener {
Date time = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("mm");

boolean connected;
StringBuilder sb;
private SerialPort serialPort;

final StringProperty line = new SimpleStringProperty("");

//Not sure this is necessary
private static final String [] PORT_NAMES = {
    "/dev/tty.usbmodem1411", // Mac OS X
    "COM11", // Windows
};
//Baud rate of communication transfer with serial device
public static final int DATA_RATE = 9600;

//Create a connection with the serial device
public boolean connect() {
    String [] ports = SerialPortList.getPortNames();
    //First, Find an instance of serial port as set in PORT_NAMES.
    for (String port : ports) {
         System.out.print("Ports: " + port);
         serialPort = new SerialPort(port);
    }
    if (serialPort == null) {
        System.out.println("Could not find device.");
        return false;
    }

    //Operation to perform is port is found
    try {
        // open serial port
        if(serialPort.openPort()) {
             System.out.println("Connected");
        // set port parameters
        serialPort.setParams(DATA_RATE,
                SerialPort.DATABITS_8,
                SerialPort.STOPBITS_1,
                SerialPort.PARITY_NONE);
                serialPort.setEventsMask(SerialPort.MASK_RXCHAR);

        serialPort.addEventListener(event -> {
            if(event.isRXCHAR()) {
                try {
                    sb.append(serialPort.readString(event.getEventValue()));
                    String str = sb.toString();
                    if(str.endsWith("\r\n")) {
                            line.set(Long.toString(time.getTime()).concat(":").concat(
                                    str.substring(0, str.indexOf("\r\n"))));
                                                System.out.println("line" + line);

                        sb = new StringBuilder();
                    }
                } catch (SerialPortException ex) {
                    Logger.getLogger(SerialComm.class.getName()).log(Level.SEVERE, null, ex);                    }
            }            
        });
    }                          
} catch (Exception e) {
    System.out.println("ErrOr");
    e.printStackTrace();
        System.err.println(e.toString());
    }       
    return serialPort != null;
}

@Override
public void serialEvent(SerialPortEvent spe) {
    throw new UnsupportedOperationException("Not supported yet."); 
}

public StringProperty getLine() {
    return line;
}
Run Code Online (Sandbox Code Playgroud)

}

在 try 块中,我理解端口参数,但 eventListener 是我遇到困难的地方。字符串生成器的重要性是在从设备读取数据时将其附加到新数据中。 我将如何解释这两个传感器读数?我是否可以通过创建单独的数据速率来区分来自每个传感器的传入数据来做到这一点?

我希望这一点是清楚的,并且我已经提供了足够的信息,但不是太多。感谢您的帮助。

- - - - - - - - - - - - - - - -更新 - - - - - - - - - --------

自从你回复 Jose 以来,我已经开始添加我的代码。在 JavaFX 类中添加侦听器时,我遇到了一些问题。我不断收到 NullPointerException,我认为这是 String[]data 未由 SerialCommunication 类中的任何数据初始化。

serialPort.addEventListener(event -> {
            if(event.isRXCHAR()) {
                try {
                    sb.append(serialPort.readString(event.getEventValue()));
                    String str = sb.toString();

                    if(str.endsWith("\r\n")) {
                            line.set(Long.toString(time.getTime()).concat(":").concat(
                                    str.substring(0, str.indexOf("\r\n"))));
                                                System.out.println("line" + line);
                        sb = new StringBuilder();
                    }
                } catch (SerialPortException ex) {
                    Logger.getLogger(SerialComm.class.getName()).log(Level.SEVERE, null, ex);
                }
            }            
        });
    }                          
} catch (Exception e) {
        System.err.println(e.toString());
    }       
Run Code Online (Sandbox Code Playgroud)

我正在将时间添加到正在读取的数据中。正如 Jose 在下面提到的,我在 arduino 代码中向数据变量添加了标签,我使用的是: Serial.print("Solar:"); Serial.println(solarData);

JavaFx监听器的粗略代码:

serialPort.getLine().addListener((ov, t, t1) -> {
        Platform.runLater(()-> {
            String [] data = t1.split(":");

            try {
                 //data[0] is the timestamp
                //data[1] will contain the label printed by arduino "Solar: data"

                switch (data[1]) {
                    case "Solar":
                        data[0].replace("Solar:" , "");
                        solarSeries.getData().add(new XYChart.Data(data[0], data[1]));
                        break;
                    case "Temperature":
                        temperatureSeries.getData().add(new XYChart.Data(data[0], data[1]));
                        break;
                }
Run Code Online (Sandbox Code Playgroud)

这段代码出现 NullPointerException 的原因是 String [] 数据数组未初始化吗?

异常错误

端口:/dev/tty.usbmodem1411Connected 线程“EventThread /dev/tty.usbmodem1411”中的异常 java.lang.NullPointerException 在 SerialComm.lambda$connect$0(SerialComm.java:61) 在 SerialComm$$Lambda$1/1661773475.serialEvent(来源未知)位于 jssc.SerialPort$LinuxEventThread.run(SerialPort.java:1299)

Jos*_*eda 2

jsscSerialPortEventListener库中定义的允许监听串口事件。这些事件之一是当 Arduino 板发送一些数据并且输入缓冲区上有一些字节时发生的事件。RXCHAR

event.getEventValue()返回一个int包含字节数的数据,并从这些字节中serialPort.readString(event.getEventValue())获取格式。String

请注意,此方法不会返回整行,因此您需要监听回车符和换行符。一旦找到"\r\n",您就可以获得该行,并重置StringBuilder下一行:

sb.append(serialPort.readString(event.getEventValue()));
String str=sb.toString();
if(str.endsWith("\r\n")){
    line.set(str.substring(0,str.indexOf("\r\n")));
    sb=new StringBuilder();
}
Run Code Online (Sandbox Code Playgroud)

其中line是可观察的String

final StringProperty line=new SimpleStringProperty("");
Run Code Online (Sandbox Code Playgroud)

在 Arduino 方面,如果您想以不同的速率从不同的传感器发送值,我建议您在 Arduino 草图中为每个传感器定义一些标识字符串,并为每个值打印其传感器的 id。

例如,这些将是您通过串行事件侦听器获得的读数:

ID1,val1
ID1,val2
ID2,val3
ID1,val4
ID3,val5
...
Run Code Online (Sandbox Code Playgroud)

最后,在 JavaFX 线程上,定义一个侦听器来更改line并处理String以获取传感器和值。像这样的东西:

serial.getLine().addListener(
    (ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
    Platform.runLater(()->{
        String[] data=newValue.split("\\,");
        if(data[0].equals("ID1"){
            // add to chart from sensor 1, value data[1]; 
        } else if(data[0].equals("ID2"){
            // add to chart from sensor 2, value data[1]; 
        } else if(data[0].equals("ID3"){
            // add to chart from sensor 3, value data[1]; 
        }
    });        
});
Run Code Online (Sandbox Code Playgroud)

请注意,您需要添加Platform.runLater(),因为从串口获取数据并更新的线程line不在 JavaFX 线程上。