欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java串口开发教程(javax.comm)

程序员文章站 2022-06-10 23:40:49
...

串口通讯是什么?

串口通信(Serial Communication), 是指外设和计算机间,通过数据信号线 、地线、控制线等,按位进行传输数据的一种通讯方式(注意,是一位一位的传输,区别于并口通讯,传输慢)。这种通信方式使用的数据线少,在远距离通信中可以节约通信成本,但其传输速度比并行传输低。。。虽然说慢,不如并,但不代表就要抛弃,某些项目还是很有用的,例如公司最近营养探索馆的一个血压探测仪,就是串口通讯,需要开发串口。

Javax.comm是什么?

Javax.comm是Sun公司提供的,用于开发平*立的通讯应用程序的扩展API,简单的说,就是年代久远的串口开发包。(ps:这里javax的x很准确地表明了它是一个扩展包,而不是核心包(core package),但由于历史原因,javax下的并不都是扩展包,比如swing包已经是Java核心架构的一部分了,不过为了与Java1.1编码兼容,仍使用javax.swing。)javax.comm可以访问RS232接口(串口)及有限制地访问IEEE-1284(并口)。

版本注意事项

  • javax.comm开发中,由于win32com.dll只能工作在32位模式下,所以基本就是说串口通讯只支持jdk16~1.7~1.8的32位版本(已测试)
  • spring boot 2.0以上只能工作在jdk1.8以上,所以这次我是从jdk1.7-32bit转到jdk1.8-32bit
  • win32com.dll这个东西太老了,又没有64位的。网上说建议使用RxTx,支持Linux 32/64、Windows 32/64,这个晚点有空再研究。http://mfizz.com/oss/rxtx-for-java

资源打包下载

  1. csdn
    https://download.csdn.net/download/moshowgame/10403876
  2. 百度网盘
    链接: https://pan.baidu.com/s/1uii3uoWwHp71MlkxjKjZag 密码: 957h

文件清单

  • comm.jar(javax.comm的包,添加到)
  • javax.comm.properties(javax.comm的配置,,放到jdk的bin和lib目录)
  • win32com.dll(串口调试DLL,放到jdk的bin和lib目录)
  • vspd(虚拟串口调试工具,可以虚拟COM1和COM2,监听COM1并发送数据,COM2就可以收到,本质上是映射了COM1<->COM2相互通讯)
  • 串口调试精灵,可以打开端口,设置一样的参数之后可以进行调试

用虚拟串口驱动添加COM1+COM2的端口

Java串口开发教程(javax.comm)

打开串口调试精灵监听端口

Java串口开发教程(javax.comm)
当然要根据设备的参数设置串口号,波特率,校验位,数据位,停止位,然后打开串口。(这里我的设备信息如下)

Java串口开发教程(javax.comm)

接着是启动程序,我这里是spring boot2,当然,可能是其他程序,反正,启动就对了。

Java串口开发教程(javax.comm)

我的程序分及部分,一个是initCom,就是传入参数初始化端口,
一个是writeCom,就是写数据到端口上(由于这是进行了虚拟端口映射,所以写到COM1的数据,监听COM2就可以收到了)

然后就是数据接收了,那个类中我设定58个字节就截取一次,放到StringList里面,

然后有个nowDataIndex来控制获取数据的下标。当然,可能也有更好的方法,但是当时就是这么想得。

其他的可以仔细看下代码,一些地方我已经给出了注释

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.TooManyListenersException;

import javax.comm.CommPortIdentifier;
import javax.comm.PortInUseException;
import javax.comm.SerialPort;
import javax.comm.SerialPortEvent;
import javax.comm.SerialPortEventListener;
import javax.comm.UnsupportedCommOperationException;

public class DSerialPort implements Runnable, SerialPortEventListener {

    private String appName = "串口通讯测试";
    private int timeout = 2000;// open 端口时的等待时间,延迟时间(毫秒数)
    private int threadTime = 0;

    private CommPortIdentifier commPort;
    private SerialPort serialPort;
    private InputStream inputStream;
    private OutputStream outputStream;
    //当前接收COM口的数据
    public static String receiptDataString="";
    //当前已取的数组下标
    public static int nowDataIndex =0;
    //当前接收COM口的数据自动切割成StringList
    public static ArrayList<String> receiptDataList=new ArrayList<String>();

    /**
     * @方法名称 :listPort
     * @功能描述 :列出所有可用的串口
     * @返回值类型 :void
     */
    @SuppressWarnings("rawtypes")
    public void listPort() {
        CommPortIdentifier cpid;
        Enumeration en = CommPortIdentifier.getPortIdentifiers();//获得端口列表

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

        while (en.hasMoreElements()) {
            cpid = (CommPortIdentifier) en.nextElement();
            if (cpid.getPortType() == CommPortIdentifier.PORT_SERIAL) {//判断是否串口
                System.out.println(cpid.getName() + ", " + cpid.getCurrentOwner());
            }
//          if (cpid.getPortType() == CommPortIdentifier.PORT_PARALLEL) {//判断是否并行口
//              System.out.println(cpid.getName() + ", " + cpid.getCurrentOwner());
//          }
        }
    }

    /**
     * @方法名称 :selectPort
     * @功能描述 :选择一个端口,比如:COM1
     * @返回值类型 :void
     * @param portName
     */
    @SuppressWarnings("rawtypes")
    public void selectPort(String portName) {

        this.commPort = null;
        CommPortIdentifier cpid;
        Enumeration en = CommPortIdentifier.getPortIdentifiers();

        while (en.hasMoreElements()) {
            cpid = (CommPortIdentifier) en.nextElement();
            if (cpid.getPortType() == CommPortIdentifier.PORT_SERIAL && cpid.getName().equals(portName)) {
                this.commPort = cpid;
                break;
            }
        }

        openPort();
    }

    /**
     * @方法名称 :openPort
     * @功能描述 :打开SerialPort
     * @返回值类型 :void
     */
    private void openPort() {
        if (commPort == null)
            log(String.format("无法找到串口!"));
        else {
            log("端口选择成功,当前端口:" + commPort.getName() + ",现在实例化 SerialPort:");

            try {
                serialPort = (SerialPort) commPort.open(appName, timeout);//打开端口
                serialPort.setSerialPortParams(2400, SerialPort.DATABITS_7, SerialPort.STOPBITS_1,SerialPort.PARITY_EVEN);
                //设置参数
                log("实例 SerialPort 成功!");
            } catch (PortInUseException e) {
                throw new RuntimeException(String.format("端口'%1$s'正在使用中!", commPort.getName()));
            }catch (UnsupportedCommOperationException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @方法名称 :checkPort
     * @功能描述 :检查端口是否正确连接
     * @返回值类型 :void
     */
    public void checkPort() {
        if (commPort == null)
            throw new RuntimeException("没有选择端口,请使用 " + "selectPort(String portName) 方法选择端口");

        if (serialPort == null) {
            throw new RuntimeException("SerialPort 对象无效!");
        }
    }

    /**
     * @方法名称 :write
     * @功能描述 :向端口发送数据,请在调用此方法前 先选择端口,并确定SerialPort正常打开!
     * @返回值类型 :void
     * @param message
     */
    public void write(String message) {
        checkPort();

        try {
            outputStream = new BufferedOutputStream(serialPort.getOutputStream());
        } catch (IOException e) {
            throw new RuntimeException("获取端口的OutputStream出错:" + e.getMessage());
        }

        try {
            outputStream.write(message.getBytes());
            log("信息发送成功!");
        } catch (IOException e) {
            throw new RuntimeException("向端口发送信息时出错:" + e.getMessage());
        } finally {
            try {
                outputStream.close();
            } catch (Exception e) {
            }
        }
    }
    public void write(byte[]  message) {
        checkPort();

        try {
            outputStream = new BufferedOutputStream(serialPort.getOutputStream());
        } catch (IOException e) {
            throw new RuntimeException("获取端口的OutputStream出错:" + e.getMessage());
        }

        try {
            outputStream.write(message);
            log("信息发送成功!");
        } catch (IOException e) {
            throw new RuntimeException("向端口发送信息时出错:" + e.getMessage());
        } finally {
            try {
                outputStream.close();
            } catch (Exception e) {
            }
        }
    }
    /**
     * @方法名称 :startRead
     * @功能描述 :开始监听从端口中接收的数据
     * @返回值类型 :void
     * @param time
     *            监听程序的存活时间,单位为秒,0 则是一直监听
     */
    public void startRead(int time) {
        checkPort();

        try {
            inputStream = new BufferedInputStream(serialPort.getInputStream());
        } catch (IOException e) {
            throw new RuntimeException("获取端口的InputStream出错:" + e.getMessage());
        }

        try {
            serialPort.addEventListener(this);//向SerialPort对象中添加串口事件监听器
        } catch (TooManyListenersException e) {
            throw new RuntimeException(e.getMessage());
        }

        serialPort.notifyOnDataAvailable(true);//设置串口有数据的事件true有效,false无效

        log(String.format("开始监听来自'%1$s'的数据--------------", commPort.getName()));
        if (time > 0) {
            this.threadTime = time * 1000;
//          this.threadTime = time* 10;
            Thread t = new Thread(this);
            t.start();
            log(String.format("监听程序将在%1$d秒后关闭。。。。", threadTime));
        }
    }
    /**
     * @方法名称 :startRead
     * @功能描述 :开始监听从端口中接收的数据
     * @返回值类型 :void
     * @param time
     *            监听程序的存活时间,单位为秒,0 则是一直监听
     */
    public void read(int time) {
        checkPort();

        try {
            inputStream = new BufferedInputStream(serialPort.getInputStream());
        } catch (IOException e) {
            throw new RuntimeException("获取端口的InputStream出错:" + e.getMessage());
        }

        try {
            serialPort.addEventListener(this);//向SerialPort对象中添加串口事件监听器
        } catch (TooManyListenersException e) {
            throw new RuntimeException(e.getMessage());
        }

        serialPort.notifyOnDataAvailable(true);//设置串口有数据的事件true有效,false无效

        log(String.format("开始监听来自'%1$s'的数据--------------", commPort.getName()));
        if (time > 0) {
            this.threadTime = time * 1000;
//          this.threadTime = time* 10;
            Thread t = new Thread(this);
            t.start();
            log(String.format("监听程序将在%1$d秒后关闭。。。。", threadTime));
        }
    }
    /**
     * @方法名称 :close
     * @功能描述 :关闭 SerialPort
     * @返回值类型 :void
     */
    public void close() {
        serialPort.close();
        serialPort = null;
        commPort = null;
    }

    public void log(String msg) {
        System.out.println(appName + " --> " + msg);
    }

    /**
     * 数据接收的监听处理函数
     */
    @Override
    public void serialEvent(SerialPortEvent arg0) {
        switch (arg0.getEventType()) {
        case SerialPortEvent.BI:/* Break interrupt,通讯中断 */
        case SerialPortEvent.OE:/* Overrun error,溢位错误 */
        case SerialPortEvent.FE:/* Framing error,传帧错误 */
        case SerialPortEvent.PE:/* Parity error,校验错误 */
        case SerialPortEvent.CD:/* Carrier detect,载波检测 */
        case SerialPortEvent.CTS:/* Clear to send,清除发送 */
        case SerialPortEvent.DSR:/* Data set ready,数据设备就绪 */
        case SerialPortEvent.RI:/* Ring indicator,响铃指示 */
        case SerialPortEvent.OUTPUT_BUFFER_EMPTY:/*
                                                     * Output buffer is
                                                     * empty,输出缓冲区清空
                                                     */
            break;
        case SerialPortEvent.DATA_AVAILABLE:/*
                                             * Data available at the serial
                                             * port,端口有可用数据。读到缓冲数组,输出到终端
                                             */
            byte[] readBuffer = new byte[1024];
            String readStr = "";
            String s2 = "";

            try {

                while (inputStream.available() > 0) {
                    inputStream.read(readBuffer);
                    readStr += new String(readBuffer).trim();
                }

                s2 = new String(readBuffer).trim();
                //接收的精华再这里
                //1。readStr为当次读入的,一般设备是1位1位读,模拟的时候就很多位,但是不重要
                //2。receiptDataString是用来缓存输入字符串的
                //3。receiptDataString.length()==XX这里可以设定你要接受的长度,然后接收指定数据
                //4。超长或者不符合长度,你可以看情况抛弃数据或者清空,或者累加
                //5。接受成功的数据,放入receiptDataList供获取调用
                //6。nowDataIndex是当前数组的下标,可以参考PortController中对数据获取的方法
                log("接收端口COM->返回数据(长度为" + readStr.length() + "):数据" + s2);
                receiptDataString+=readStr;
                log("receiptDataString->长度" + receiptDataString.length() + "),数据" + receiptDataString);
                if(receiptDataString.length()==58){
                    receiptDataList.add(receiptDataString);
                    receiptDataString="";
                    log("校验通过,数据接收成功");
                }else if(receiptDataString.length()>100){
                    receiptDataString="";
                }
            } catch (IOException e) {
            }
        }
    }

    @Override
    public void run() {
        try {
            Thread.sleep(threadTime);
            serialPort.close();
            log(String.format("端口'%1$s'监听关闭了!", commPort.getName()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        DSerialPort sp = new DSerialPort();
        //sp.listPort();
        sp.selectPort("COM1");
        sp.write("210.36.16.166");
        //sp.write("2");
        //sp.startRead(120);
    }
}

import java.util.ArrayList;
import org.microservice.tcbj.yytsg.checkcollectsys.util.ApiReturnObject;
import org.microservice.tcbj.yytsg.checkcollectsys.util.ApiReturnUtil;
import org.microservice.tcbj.yytsg.checkcollectsys.util.DSerialPort;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/port")
public class PortController {
        DSerialPort sp = new DSerialPort();

        String nowCom="";
        @RequestMapping("/index")
        public String index() {
            return "/port";
        }
        @RequestMapping("/initCom")
        public ApiReturnObject initCom(String comCode){
            sp.listPort();
            sp.selectPort(comCode);
            sp.startRead(0);
            nowCom=comCode;
            DSerialPort.receiptDataString="";
            DSerialPort.nowDataIndex=0;
            DSerialPort.receiptDataList=new ArrayList<String>();
            return ApiReturnUtil.success("");
        }
        @RequestMapping("/writeCom")
        public ApiReturnObject  writeCom(String comData){
            System.out.println("#comData->"+comData);
            sp.checkPort();
            //sp.selectPort(comCode);
            sp.write(comData);
            return ApiReturnUtil.success("传输数据成功");
        }
        @RequestMapping("/getDataFromCom")
        public ApiReturnObject   getDataFromCom(){
            String dataStr="";
            System.out.println(DSerialPort.receiptDataList.size());
            if(DSerialPort.receiptDataList.size()>DSerialPort.nowDataIndex){
                dataStr=DSerialPort.receiptDataList.get(DSerialPort.nowDataIndex);
                DSerialPort.nowDataIndex++;
            }
            return ApiReturnUtil.success(dataStr);
        }
        public static int receiptNumber=0;
        @RequestMapping("/closeCom")
        public ApiReturnObject  closeCom(String comCode,String comData){
            System.out.println("#comCode->"+comCode+"#comData->"+comData);
            if(sp!=null) sp.close();
            return ApiReturnUtil.success("传输数据成功");
        }
        @RequestMapping("/startScan")
        public ApiReturnObject  startScan(){
            //开始
            byte[] b = new byte[3];
            b[0] = (byte) 0x02;
            b[1] = (byte) 0x53;
            b[2] = (byte) 0x03;
            //sp.selectPort(nowCom);
            sp.write(b);
            return ApiReturnUtil.success("启动扫描成功");
        }
        @RequestMapping("/stopScan")
        public ApiReturnObject  stopScan(){
            byte[] b2 = new byte[3];
            b2[0] = (byte) 0x02;
            b2[1] = (byte) 0x52;
            b2[2] = (byte) 0x03;
            //sp.selectPort(nowCom);
            sp.write(b2);
            return ApiReturnUtil.success("停止扫描成功");
        }
}