springboot 对接RS485串口协议 实现和设备通信

📅 2026/7/8 4:52:20
springboot 对接RS485串口协议 实现和设备通信
引入依赖!-- 串口通信 -- dependency groupIdcom.fazecast/groupId artifactIdjSerialComm/artifactId version2.9.2/version /dependency dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.7.13/version /dependency引入工具packagecom.lhbl.digitaltwin.utils.com;importcom.fazecast.jSerialComm.SerialPort;importcom.fazecast.jSerialComm.SerialPortDataListener;importcom.fazecast.jSerialComm.SerialPortEvent;importcom.lhbl.digitaltwin.utils.web.ConvertHexStrAndStrUtils;importlombok.extern.slf4j.Slf4j;importorg.springframework.stereotype.Component;importjava.util.ArrayList;importjava.util.List;importjava.util.Map;importjava.util.concurrent.ConcurrentHashMap;importjava.util.stream.Collectors;Slf4jComponentpublicclassSerialPortManager{// 波特率 根据实际情况改publicstaticfinalintSERIAL_BAUD_RATE9600;privatestaticfinalMapString,SerialPortHolderPORT_MAPnewConcurrentHashMap();FunctionalInterfacepublicinterfaceSerialPortCallback{voidonDataReceived(StringportName,byte[]data);}privatestaticclassSerialPortHolder{volatileSerialPortserialPort;volatilebooleanconnected;SerialPortCallbackcallback;SerialPortHolder(SerialPortserialPort,booleanconnected,SerialPortCallbackcallback){this.serialPortserialPort;this.connectedconnected;this.callbackcallback;}}privatestaticclassDataListenerimplementsSerialPortDataListener{privatefinalStringportName;privatefinalSerialPortCallbackcallback;DataListener(StringportName,SerialPortCallbackcallback){this.portNameportName;this.callbackcallback;}OverridepublicintgetListeningEvents(){returnSerialPort.LISTENING_EVENT_DATA_AVAILABLE;}OverridepublicvoidserialEvent(SerialPortEventevent){if(callback!null){byte[]datareadSerialPortData(portName);if(data!null){try{callback.onDataReceived(portName,data);}catch(Exceptione){log.error(串口数据处理失败: {},e.getMessage());}}}}}publicstaticListStringgetSerialPortList(){SerialPort[]serialPortsSerialPort.getCommPorts();ListStringportNameListnewArrayList();for(SerialPortserialPort:serialPorts){log.debug(可用串口: {},serialPort.getSystemPortName());portNameList.add(serialPort.getSystemPortName());}returnportNameList.stream().distinct().collect(Collectors.toList());}publicstaticvoidconnectSerialPort(StringportName){connectSerialPort(portName,SERIAL_BAUD_RATE,null);}publicstaticvoidconnectSerialPort(StringportName,IntegerbaudRate,SerialPortCallbackcallback){if(PORT_MAP.containsKey(portName)){log.warn(串口已连接: {},portName);return;}try{SerialPortserialPortopenSerialPort(portName,baudRate);if(serialPort.isOpen()){SerialPortCallbackdataCallbackcallback!null?callback:SerialPortManager::defaultDataHandler;serialPort.addDataListener(newDataListener(portName,dataCallback));PORT_MAP.put(portName,newSerialPortHolder(serialPort,true,dataCallback));log.info(串口连接成功: {},portName);}else{log.error(串口连接失败: {}请查看端口,portName);}}catch(Exceptionex){log.error(串口连接异常: {},ex.getMessage(),ex);}}privatestaticvoiddefaultDataHandler(StringportName,byte[]data){try{StringhexStrConvertHexStrAndStrUtils.bytesToHexStr(data);if(hexStr.length()8){StringcomStatushexStr.substring(6,8);if(00.equals(comStatus)){log.debug(行程开关关闭 - 端口: {}, 数据: {},portName,hexStr);}elseif(FF.equals(comStatus)){log.debug(行程开关闭合 - 端口: {}, 数据: {},portName,hexStr);}}}catch(Exceptione){log.error(串口数据解析失败: {},e.getMessage());}}publicstaticSerialPortopenSerialPort(StringportName,IntegerbaudRate){SerialPortserialPortSerialPort.getCommPort(portName);if(baudRate!null){serialPort.setBaudRate(baudRate);}if(!serialPort.isOpen()){serialPort.openPort(1000);}serialPort.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED);serialPort.setComPortParameters(baudRate,8,SerialPort.ONE_STOP_BIT,SerialPort.NO_PARITY);serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_BLOCKING|SerialPort.TIMEOUT_WRITE_BLOCKING,1000,1000);returnserialPort;}publicstaticvoidcloseSerialPort(StringportName){SerialPortHolderholderPORT_MAP.remove(portName);if(holder!nullholder.serialPort!nullholder.serialPort.isOpen()){holder.serialPort.closePort();log.info(串口关闭: {},portName);}}publicstaticvoidcloseAllSerialPorts(){PORT_MAP.forEach((portName,holder)-{if(holder.serialPort!nullholder.serialPort.isOpen()){holder.serialPort.closePort();log.info(串口关闭: {},portName);}});PORT_MAP.clear();}publicstaticvoidsendSerialPortData(StringportName,byte[]content){SerialPortHolderholderPORT_MAP.get(portName);if(holder!nullholder.serialPort!nullholder.serialPort.isOpen()){holder.serialPort.writeBytes(content,content.length);log.debug(串口发送数据 - 端口: {}, 长度: {},portName,content.length);}}publicstaticbyte[]readSerialPortData(StringportName){SerialPortHolderholderPORT_MAP.get(portName);if(holder!nullholder.serialPort!nullholder.serialPort.isOpen()){try{inti0;while(holder.serialPort.bytesAvailable()0i5){Thread.sleep(20);}byte[]readBuffernewbyte[holder.serialPort.bytesAvailable()];intnumReadholder.serialPort.readBytes(readBuffer,readBuffer.length);if(numRead0){returnreadBuffer;}}catch(InterruptedExceptione){log.error(串口读取中断: {},e.getMessage());Thread.currentThread().interrupt();}}returnnull;}publicstaticbooleanisConnected(StringportName){SerialPortHolderholderPORT_MAP.get(portName);returnholder!nullholder.connectedholder.serialPort!nullholder.serialPort.isOpen();}}开机连接串口SpringBootApplicationpublicclassDigitalTwinApplicationimplementsApplicationRunner{publicstaticvoidmain(String[]args){SpringApplication.run(DigitalTwinApplication.class,args);}Overridepublicvoidrun(ApplicationArgumentsargs)throwsException{SerialPortManager.connectSerialPort(COM4,9600,(comName,data)-{System.out.println(data);});}}定时任务实时发送ComponentSlf4jpublicclassScheduledCOM{privateStringsendMsg00 04 00 00 00 03 B1 DA;Scheduled(fixedRate1000)publicvoidreadComData(){newThread(newRunnable(){Overridepublicvoidrun(){byte[]bytesHexUtil.decodeHex(sendMsg);SerialPortManager.sendSerialPortData(bytes);}}).start();}}其他辅助工具publicclassConvertHexStrAndStrUtils{privatestaticfinalchar[]HEXES{0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f};publicstaticStringbytesToHexStr(byte[]bytes){if(bytesnull||bytes.length0){returnnull;}StringBuilderhexnewStringBuilder(bytes.length*2);for(byteb:bytes){hex.append(HEXES[(b4)0x0F]);hex.append(HEXES[b0x0F]);}returnhex.toString().toUpperCase();}publicstaticbyte[]hexStrToBytes(Stringhex){if(hexnull||hex.length()0){returnnull;}char[]hexCharshex.toCharArray();byte[]bytesnewbyte[hexChars.length/2];// 如果 hex 中的字符不是偶数个, 则忽略最后一个for(inti0;ibytes.length;i){bytes[i](byte)Integer.parseInt(hexChars[i*2]hexChars[i*21],16);}returnbytes;}publicstaticStringstrToHexStr(Stringstr){StringBuildersbnewStringBuilder();byte[]bsstr.getBytes();intbit;for(inti0;ibs.length;i){bit(bs[i]0x0f0)4;sb.append(HEXES[bit]);bitbs[i]0x0f;sb.append(HEXES[bit]);}returnsb.toString().trim();}publicstaticStringhexStrToStr(StringhexStr){//能被16整除,肯定可以被2整除byte[]arraynewbyte[hexStr.length()/2];try{for(inti0;iarray.length;i){array[i](byte)(0xffInteger.parseInt(hexStr.substring(i*2,i*22),16));}hexStrnewString(array,StandardCharsets.UTF_8);}catch(Exceptione){e.printStackTrace();return;}returnhexStr;}}流程启动时先对串口进行 连接 连接上后发送数据给设备 数据格式和设备的数据格式一致时 设备会返回数据到回调 data 里面 处理这个data即可