java的串口发送和监听

参考:

(1)Java使用RXTX进行串口SerialPort通讯:Java使用RXTX进行串口SerialPort通讯 - 简书

(2)rxtx-2.2pre2-bins.zip下载:https://download.csdn.net/download/zhp614/5943663

我:

(1)添加插件依赖,并 把 rxtxSerial.dll 文件 放到 C:\Windows\System32 目录。

<!-- RXTX串口通信包,需要将 rxtxParallel.dll、rxtxSerial.dll 直接放到 C:\Windows\System32 目录 -->
        <dependency>
            <groupId>org.rxtx</groupId>
            <artifactId>rxtxcomm</artifactId>
            <version>2.2pre2</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/src/main/resources/libs/RXTXcomm.jar</systemPath>
        </dependency>

(2)16进制转换工具类:

package utils;

public class HexUtils
{
    /**
     * 接收串口返回的二进制字节流时,
     * 二进制字节流数组(一个字节是8位二进制,如00000001)转换成十六进制字符串
     */
    public static final String bytesToHexString(byte[] bArray) {
        StringBuffer sb = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = 0; i < bArray.length; i++) {
            sTemp = byteToHexString(bArray[i]);
            sb.append(sTemp);
        }
        return sb.toString();
    }

    public static final String byteToHexString(byte bt_8){
        // 先将8位二进制的一个字节转为 32位二进制的整数类型,避免在后续的转换中丢失数据
        int it_32 = byteToInt(bt_8);
        // 再将 it_32 转为 16进制字符串
        String hex = Integer.toHexString(it_32);
        if (hex.length() < 2){
            hex = "0" + hex;
        }
        hex = hex.toUpperCase();
        return hex;
    }

    /**
     * 给串口发送命令时,
     * hex字符串转byte数组 并且添加最后一位校验码
     * @param inHex 待转换的Hex字符串
     * @return 转换后的byte数组结果
     */
    public static byte[] hexToByteArray(String inHex) {
        byte[] result = new byte[(inHex.length() / 2)];
        int j = 0;
        for(int i = 0; i < inHex.length(); i += 2){
            result[j] = hexToByte(inHex.substring(i, i + 2));
            j++;
        }
        return result;
    }

    /**
     * Hex字符串转byte
     * @param inHex 待转换的Hex字符串
     * @return 转换后的byte
     */
    public static byte hexToByte(String inHex) {
        int b =  Integer.parseInt(inHex, 16);
        return (byte)b;
    }

    /**
     * 十进制字符串转 16进制字符串
     */
    public static String decToHex(String dec){
        return bytesToHexString(dec.getBytes());
    }

    /**
     * 字节 转 数字
     */
    public static int byteToInt(byte bt_8){
        int it_32 = bt_8 & 0xFF;
        return it_32;
    }
}

(3)串口监听自定义interface:

/**
 * @date 2020-06-08
 * @author wuguixin 吴桂鑫
 * @qq 1393180819
 */
package utils.serialPort;

/**
 * 回调方法接口
 * @author 吴桂鑫 wuguixin
 */
public interface IMyPortListener
{
    // 监听
    boolean onSerialPort(byte[] return_bytes);

    // 监听之前 刷新
    void refreshData();

    // 监听时获取数据
    Object getData();
}

(4)串口工具类:

/**
 * @date 2020-06-08
 * @author wuguixin 吴桂鑫
 * @qq 1393180819
 */
package utils.serialPort;

import gnu.io.*;
import utils.HexUtils;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

/**
 * RxtxAPI 的核心是抽象的CommPort类及其两个子类:SerialPort类和ParallePort类。
 * 其中,SerialPort类是用于串口通信的类,P
 * arallePort类是用于并行口通信的类。
 * CommPort类还提供了常规的通信模式和方法,例如:getInputStream( )方法和getOutputStream( )方法,
 * 专用于与端口上的设备进行通信。
 * 然而,这些类的构造方法都被有意的设置为非公有的(non-public)。
 * 所以,不能直接构造对象,而是先通过静态的CommPortIdentifer.getPortIdentifiers()获得端口列表,
 * 再从这个端口列表中选择所需要的端口,并调用CommPortIdentifer对象的Open( )方法,
 * 这样,就能得到一个CommPort对象。当
 * 然,还要将这个CommPort对象的类型转换为某个非抽象的子类,表明是特定的通讯设备,
 * 该子类可以是SerialPort类和ParallePort类中的一个。
 */
public class SerialPortUtil implements SerialPortEventListener{
    // 串口名称
    private String portName;
    // 波特率
    private int baudRate;

    //open 端口时的等待时间
    private int timeout = 2000;
    // 数据位
    private int dataBits = 8;
    // 停止位
    private int stopBits = 1;
    // 检验位 0 表示none
    private int parity = 0;

    // 串行端口
    private SerialPort serialPort;

    private static SerialPortUtil util;

    private IMyPortListener myPortListener;


    public static SerialPortUtil getInstance(String portName,int baudRate)throws Exception{
        // 如果不存在,则创建
        if(null == util){
            util = new  SerialPortUtil(portName,baudRate);
        }
        // 如果 串口名称 和 波特率 不一样,则重新创建
        else if( (!util.portName.equals(portName)) || (util.baudRate != baudRate) ){
            util.clear();
            util = new  SerialPortUtil(portName,baudRate);
        }
        return util;
    }

    public void clear(){
        serialPort.close();
        util = null;
    }


    private SerialPortUtil(String portName,int baudRate) throws Exception{
        this.portName = portName;
        this.baudRate = baudRate;
        init();
    }

    /**
     * 根据 串口名称  与 对应串口 相连接
     * @throws Exception
     */
    private void init() throws Exception {
        // 串口是否在 可用串口list中
        List ports = findPortNameList();
        if(ports.size()<=0){
            throw new Exception("错误: 没有找到可用的串口");
        }
        if(!ports.contains(portName)){
            throw new Exception("错误: 该串口不存在");
        }
        CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
        if ( portIdentifier.isCurrentlyOwned() ) {
            throw new Exception("错误: 该串口已被占用");
        }
        // 调用 open方法 连接 串口
        CommPort commPort = portIdentifier.open(this.getClass().getName(),timeout);
        if ( !(commPort instanceof SerialPort) ) {
            throw new Exception("错误: 无法处理非串行端口");
        }
        serialPort = (SerialPort) commPort;
        // 设置串口参数:波特率、数据位、停止位、检验位
        serialPort.setSerialPortParams(baudRate,dataBits,stopBits,parity);
        // 监听
        serialPort.addEventListener(this);
        // 设置当通信中断时唤醒中断线程
        serialPort.notifyOnBreakInterrupt(true);
        System.out.println("连接" + portName + "成功");
    }

    /**
     * 发送命令 给 串口
     * @param command 命令
     */
    public boolean sendCommand(String command, IMyPortListener myPortListener){
        try{
            // 清空旧数据
            myPortListener.refreshData();
            System.out.println(command);
            // 设置当有数据到达时唤醒监听接收线程
            serialPort.notifyOnDataAvailable(true);
            // 在发送命令之前,设置相应命令的监听处理方式
            this.myPortListener = myPortListener;
            byte[] writerBuffer = HexUtils.hexToByteArray(command);
            // 添加校验位
            writerBuffer = addCRC(writerBuffer);
            System.out.println("发送命令:"+HexUtils.bytesToHexString(writerBuffer));
            OutputStream out = serialPort.getOutputStream();
            out.write(writerBuffer);
            System.out.println("发送命令成功");
            return true;
        }catch (Exception e){
           e.printStackTrace();
           clear();
           System.out.println("发送命令失败");
        }
        return false;
    }


    /**
     * 读取串口返回信息
     */
    public byte[] readData(){
        byte[] readBuffer = null;
        InputStream inputStream = null;
        try{
            inputStream = serialPort.getInputStream();
            // 通过输入流对象的available方法获取数组字节长度
            readBuffer = new byte[inputStream.available()];
            // 从线路上读取数据流
            int len = 0;
            while ((len = inputStream.read(readBuffer)) != -1) {
                break;
            }
            inputStream.close();
            System.out.println("读取数据流成功");
        }catch (Exception e){
            e.printStackTrace();
            System.out.println("读取数据流失败");
            clear();
        }
        return readBuffer;
    }

    /**
     * 列出所有可用的串口名称
     */
    public static List<String> findPortNameList(){
        List<String> portNameList = new ArrayList<>();
        Enumeration<CommPortIdentifier> en = CommPortIdentifier.getPortIdentifiers();

        System.out.println("now to list all Port of this PC:" +en);

        while(en.hasMoreElements()){
            CommPortIdentifier port = en.nextElement();
            if(port.getPortType() == CommPortIdentifier.PORT_SERIAL){
                portNameList.add(port.getName());
            }
        }
        return portNameList;
    }

    @Override
    public void serialEvent(SerialPortEvent event)
    {
        int type = event.getEventType();
        if(type == SerialPortEvent.DATA_AVAILABLE){
            // 有数据到达
            byte[] return_bytes = util.readData();
            // 处理数据
            boolean flag = this.myPortListener.onSerialPort(return_bytes);
            if(flag){
                // 设置当 处理数据返回为 true 时 关闭 监听接收线程
                serialPort.notifyOnDataAvailable(false);
            }
        }
    }

    /**
     * 添加最后一位校验码
     * @param bytes
     * @return
     */
    public static byte[] addCRC(byte[] bytes){
        byte[] result = new byte[bytes.length + 1];
        for(int i=0; i < bytes.length; i++){
            result[i] = bytes[i];
        }
        int crc =  bytes[3] + bytes[4]+ bytes[5]+ bytes[6]
                + bytes[7]+ bytes[8]+ bytes[9]+ bytes[10]
                + bytes[11]+ bytes[12]+ bytes[13]+ bytes[14] + 0x42;
        result[result.length-1] = (byte)crc;
        return result;
    }
}

(5)例子:

(5.1)wifi监听:

package operation.wifiManage;

import lombok.Data;
import utils.HexUtils;
import utils.serialPort.IMyPortListener;

@Data
public class WifiReadListener implements IMyPortListener
{
    // wifi mac指令前缀
    static final String WIFI_COMMAND_PREFIX = "F8F8A8";

    // wifi mac地址长度
    static final Integer WIFI_MAC_LENGTH = 6;

    // 监听到的mac地址
    String mac;

    // 是否监听到了mac地址
    Boolean flag;

    @Override
    public boolean onSerialPort(byte[] return_bytes)
    {
        if(null != return_bytes){
            String return_hex = HexUtils.bytesToHexString(return_bytes);
            if(return_hex.startsWith(WIFI_COMMAND_PREFIX)){
                // 获取 经典 mac地址
                for(int i=0;i< WIFI_MAC_LENGTH ;i++){
                    String po = HexUtils.byteToHexString(return_bytes[i+3]);
                    if(i==0){
                        mac = po;
                    }else{
                        mac = mac + ":" + po;
                    }
                }
                flag = true;
                return true;
            }
        }
        return false;
    }

    @Override
    public void refreshData()
    {
        mac = null;
        flag = false;
    }

    @Override
    public Object getData()
    {
        if(flag){
            return mac;
        }else{
            return null;
        }
    }

}

(5.2)发送读取wifi地址的命令并获取返回mac:

//wifi读取命令
static String WIFI_MAC_QUERY = "F8F8A8"+"00"+"00"+"00"+"00"+"00"+"00"+"00"+"00"+"00"+"00"+"00"+"00";

//wifi监听事件类
WifiReadListener wifiReadListener = new WifiReadListener();

private void readMac(){
		// todo 调用读取接口
		try{
			SerialPortUtil serialPortUtil = SerialPortUtil.getInstance("COM4",38400);
			// 发送命令
			serialPortUtil.sendCommand(WIFI_MAC_QUERY, wifiReadListener);
			System.out.println("读取中...");

			Long startTime = System.currentTimeMillis();
			while(true){
				Object res = wifiReadListener.getData();
				if(null != res){
					System.out.println("读取成功:"+res);
				}
				Long interval = System.currentTimeMillis() - startTime;
				if(interval>2000){
					System.out.println("读取超时,没有监听到目标数据");
					break;
				}
			}
		}catch (Exception e){
			e.printStackTrace();
			System.out.println("读取命令发送失败,"+e.getMessage());
		}
	}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
监听串口数量的变化,可以使用Java串口通信库,例如RXTX或JSSC,来实现。具体步骤如下: 1. 首先需要获取当前系统中可用的串口列表。可以使用RXTX或JSSC提供的方法来获取串口列表。 2. 使用Java的定时器或线程来定期检查当前可用的串口数量。可以通过比较当前可用的串口列表和之前获取的串口列表来判断串口数量是否发生了变化。 3. 如果发现串口数量发生了变化,可以触发相应的处理逻辑,例如发送通知或重新连接串口等。 下面是一个使用JSSC实现监听串口数量变化的示例代码: ``` import jssc.SerialPortList; import java.util.Arrays; import java.util.Timer; import java.util.TimerTask; public class SerialPortMonitor { private String[] previousPorts; private Timer timer; public SerialPortMonitor() { // 获取初始串口列表 previousPorts = SerialPortList.getPortNames(); // 定期检查串口数量变化 timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { String[] currentPorts = SerialPortList.getPortNames(); if (!Arrays.equals(currentPorts, previousPorts)) { // 串口数量发生变化,触发处理逻辑 handlePortChange(currentPorts); previousPorts = currentPorts; } } }, 0, 1000); } public void stop() { timer.cancel(); } private void handlePortChange(String[] currentPorts) { // 处理逻辑 System.out.println("Serial port list changed:"); System.out.println("Previous ports: " + Arrays.toString(previousPorts)); System.out.println("Current ports: " + Arrays.toString(currentPorts)); } public static void main(String[] args) throws InterruptedException { SerialPortMonitor monitor = new SerialPortMonitor(); Thread.sleep(5000); // 模拟等待时间 monitor.stop(); } } ``` 这个示例程序会每秒钟检查一次当前可用的串口列表,如果发现串口数量变化,就会输出相关信息。在实际应用中,可以根据需要修改处理逻辑。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值