Java Spring Boot实现物联网设备串口通信
本文实现了利用Java语言编写程序实现物联网设备串口通讯发送数据并且接口数据功能
·
Java Spring Boot实现物联网设备串口通信
底层使用了Labview与PLC,底层使用了什么技术由硬件工程师决定,可以不用关心底层使用了何种技术。
pom.xml
<dependency>
<groupId>org.rxtx</groupId>
<artifactId>rxtx</artifactId>
<version>2.1.7</version>
</dependency>
本文实现了利用Java语言编写程序实现下图所示发送数据并且接口数据功能,你的设备需要支持串口通信,串口:COM1,波特率:19200
主程序入口需要添加destory方法,用于程序关闭后删除监听器、关闭串口
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
@PreDestroy
public void destory() {
//关闭应用前 关闭端口
SerialPortUtil serialPortUtil = SerialPortUtil.getSerialPortUtil();
serialPortUtil.removeListener(PortInit.serialPort, new MyLister());
serialPortUtil.closePort(PortInit.serialPort);
}
}
SerialPortUtil.java
import gnu.io.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.TooManyListenersException;
/**
* @author mar
* @date 2021年08月10日 09:54
*/
public class SerialPortUtil {
private static final Logger logger = LoggerFactory.getLogger(SerialPortUtil.class);
private static SerialPortUtil serialPortUtil = null;
static {
//在该类被ClassLoader加载时就初始化一个SerialTool对象
if (serialPortUtil == null) {
serialPortUtil = new SerialPortUtil();
}
}
//私有化SerialTool类的构造方法,不允许其他类生成SerialTool对象
private SerialPortUtil() {
}
/**
* 获取提供服务的SerialTool对象
* @return serialPortUtil
*/
public static SerialPortUtil getSerialPortUtil() {
if (serialPortUtil == null) {
serialPortUtil = new SerialPortUtil();
}
return serialPortUtil;
}
/**
* 查找所有可用端口
* @return 可用端口名称列表
*/
public ArrayList<String> findPort() {
//获得当前所有可用串口
Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();
ArrayList<String> portNameList = new ArrayList<>();
//将可用串口名添加到List并返回该List
while (portList.hasMoreElements()) {
String portName = portList.nextElement().getName();
portNameList.add(portName);
}
return portNameList;
}
/**
* 打开串口
* @param portName 端口名称
* @param baudrate 波特率 19200
* @param databits 数据位 8
* @param parity 校验位(奇偶位) NONE :0
* @param stopbits 停止位 1
* @return 串口对象
* // * @throws SerialPortParameterFailure 设置串口参数失败
* // * @throws NotASerialPort 端口指向设备不是串口类型
* // * @throws NoSuchPort 没有该端口对应的串口设备
* // * @throws PortInUse 端口已被占用
*/
public SerialPort openPort(String portName, int baudrate, int databits, int parity, int stopbits) {
try {
//通过端口名识别端口
CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
//打开端口,并给端口名字和一个timeout(打开操作的超时时间)
CommPort commPort = portIdentifier.open(portName, 2000);
//判断是不是串口
if (commPort instanceof SerialPort) {
SerialPort serialPort = (SerialPort) commPort;
try {
//设置一下串口的波特率等参数
serialPort.setSerialPortParams(baudrate, databits, stopbits, parity);
} catch (UnsupportedCommOperationException e) {
}
System.out.println("Open " + portName + " sucessfully !");
return serialPort;
} else {
logger.error("不是串口");
}
} catch (NoSuchPortException e1) {
logger.error("没有找到端口");
e1.printStackTrace();
} catch (PortInUseException e2) {
logger.error("端口被占用");
e2.printStackTrace();
}
return null;
}
/**
* 关闭串口
* @param serialPort 待关闭的串口对象
*/
public void closePort(SerialPort serialPort) {
if (serialPort != null) {
serialPort.close();
}
}
/**
* 往串口发送数据
* @param serialPort 串口对象
* @param order 待发送数据
* // * @throws SendDataToSerialPortFailure 向串口发送数据失败
* // * @throws SerialPortOutputStreamCloseFailure 关闭串口对象的输出流出错
*/
public void sendToPort(SerialPort serialPort, byte[] order) {
OutputStream out = null;
try {
out = serialPort.getOutputStream();
out.write(order);
out.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 从串口读取数据
* @param serialPort 当前已建立连接的SerialPort对象
* @return 读取到的数据
* // * @throws ReadDataFromSerialPortFailure 从串口读取数据时出错
* // * @throws SerialPortInputStreamCloseFailure 关闭串口对象输入流出错
*/
public byte[] readFromPort(SerialPort serialPort) {
InputStream in = null;
byte[] bytes = null;
try {
in = serialPort.getInputStream();
int bufflenth = in.available();
while (bufflenth != 0) {
bytes = new byte[bufflenth];
in.read(bytes);
bufflenth = in.available();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return bytes;
}
/**
* 添加监听器
* @param port 串口对象
* @param listener 串口监听器
* // * @throws TooManyListeners 监听类对象过多
*/
public void addListener(SerialPort port, SerialPortEventListener listener) {
try {
//给串口添加监听器
port.addEventListener(listener);
//设置当有数据到达时唤醒监听接收线程
port.notifyOnDataAvailable(true);
//设置当通信中断时唤醒中断线程
port.notifyOnBreakInterrupt(true);
} catch (TooManyListenersException e) {
// throw new TooManyListeners();
logger.error("太多监听器");
e.printStackTrace();
}
}
/**
* 删除监听器
* @param port 串口对象
* @param listener 串口监听器
* // * @throws TooManyListeners 监听类对象过多
*/
public void removeListener(SerialPort port, SerialPortEventListener listener) {
//删除串口监听器
port.removeEventListener();
}
/**
* 设置串口的Listener
* @author mar
* @date 2021/8/20 11:04
* @param serialPort
* @param listener
*/
public static void setListenerToSerialPort(SerialPort serialPort, SerialPortEventListener listener){
try {
//给串口添加事件监听
serialPort.addEventListener(listener);
} catch (TooManyListenersException e) {
e.printStackTrace();
}
//串口有数据监听
serialPort.notifyOnDataAvailable(true);
//中断事件监听
serialPort.notifyOnBreakInterrupt(true);
}
}
MyLister.java 串口监听器
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import lombok.SneakyThrows;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Date;
/**
* 串口监听器
* @author mar
* @date 2021年08月10日 09:56
*/
public class MyLister implements SerialPortEventListener {
@SneakyThrows
@Override
public void serialEvent(SerialPortEvent arg0) {
if (arg0.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
SerialPortUtil serialPortUtil = SerialPortUtil.getSerialPortUtil();
byte[] bytes = serialPortUtil.readFromPort(PortInit.serialPort);
String byteStr = new String(bytes, 0, bytes.length).trim();
System.out.println("===========start===========");
System.out.println(new Date() + "【读到的字符串】:-----" + byteStr);
String needData = printHexString(bytes);
System.out.println(new Date() + "【字节数组转16进制字符串】:-----" + needData);
System.out.println(new Date() + "【16进制字符串转字符串】:" + hexStringToString(needData));
System.out.println("===========end===========");
}
}
/**
* 字节数组转16进制字符串
* @param b 字节数组
* @return 16进制字符串
*/
public static String printHexString(byte[] b) {
StringBuilder sbf = new StringBuilder();
for (byte value : b) {
String hex = Integer.toHexString(value & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sbf.append(hex.toUpperCase()).append(" ");
}
return sbf.toString().trim();
}
/**
* 十六进制字符串转byte[]
* @param hex 十六进制字符串
* @return byte[]
*/
public static byte[] hexStr2Byte(String hex) {
if (hex == null) {
return new byte[] {};
}
// 奇数位补0
if (hex.length() % StaticConstant.TWO != 0) {
hex = "0" + hex;
}
int length = hex.length();
ByteBuffer buffer = ByteBuffer.allocate(length / 2);
for (int i = 0; i < length; i++) {
String hexStr = hex.charAt(i) + "";
i++;
hexStr += hex.charAt(i);
byte b = (byte) Integer.parseInt(hexStr, 16);
buffer.put(b);
}
return buffer.array();
}
/**
* 16进制转换成为string类型字符串
* @param s 待转换字符串
*/
public static String hexStringToString(String s) {
if (s == null || "".equals(s)) {
return null;
}
s = s.replace(" ", "");
byte[] baKeyword = new byte[s.length() / 2];
for (int i = 0; i < baKeyword.length; i++) {
try {
baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
} catch (Exception e) {
e.printStackTrace();
}
}
try {
s = new String(baKeyword, StandardCharsets.UTF_8);
} catch (Exception e1) {
e1.printStackTrace();
}
return s;
}
}
PortInit.java 测试代码,项目启动时发送指令获取串口服务器数据
import gnu.io.SerialPort;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import java.util.ArrayList;
import static com.xm.dcim.utils.plc.MyLister.hexStr2Byte;
/**
* 示例代码
* @author mar
* @date 2021年08月10日 09:55
*/
@Component
public class PortInit implements ApplicationRunner {
public static SerialPort serialPort = null;
@Override
public void run(ApplicationArguments args) {
String portname = "COM1";
//TestA();
//查看所有串口
SerialPortUtil serialPortUtil = SerialPortUtil.getSerialPortUtil();
ArrayList<String> port = serialPortUtil.findPort();
System.out.println("发现全部串口:" + port);
System.out.println("打开指定portname:" + portname);
//打开该对应portname名字的串口
PortInit.serialPort = serialPortUtil.openPort(portname, 19200, SerialPort.DATABITS_8, SerialPort.PARITY_NONE, SerialPort.PARITY_ODD);
byte[] HEX = hexStr2Byte("A55A230000000022");
serialPortUtil.sendToPort(PortInit.serialPort, HEX);
//给对应的serialPort添加监听器
serialPortUtil.addListener(PortInit.serialPort, new MyLister());
}
}
下面的方法,可以写成定时器,让它根据设置的时间去执行发送指令
public void plcAnalytic() {
String portName = "COM1";
//查看所有串口
SerialPortUtil serialPortUtil = SerialPortUtil.getSerialPortUtil();
//打开该对应portName名字的串口
if (PortInit.serialPort == null) {
PortInit.serialPort = serialPortUtil.openPort(portName, 19200, SerialPort.DATABITS_8, SerialPort.PARITY_NONE, SerialPort.PARITY_ODD);
byte[] hex = MyLister.hexStr2Byte("A55A230000000022".replaceAll("\\s*", ""));
serialPortUtil.sendToPort(PortInit.serialPort, hex);
//给对应的serialPort添加监听器-->设置串口的Listener
SerialPortUtil.setListenerToSerialPort(PortInit.serialPort, serialPortEvent -> {
if (serialPortEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
byte[] bytes = serialPortUtil.readFromPort(PortInit.serialPort);
String needData = MyLister.printHexString(bytes);
//数据校验 校验数据长度、起始5A A5、数据sum位
if (PlcUtil.makeChecksum(needData)) {
//数据解析方法 此处编写你的解析方法,根据获取到的数据编写适合自己的方法
}
}
});
}
}
更多推荐
已为社区贡献3条内容
所有评论(0)