精华内容
下载资源
问答
  • *功能:监听串口 *创建作者: ***/ public partial class jianting : Form { public jianting() { InitializeComponent(); } private SerialPort Sp = new SerialPort(); public delegate void ...
  • Java串口编程:串口数据的发送与监听读取

    万次阅读 多人点赞 2015-03-15 11:23:53
    本人在近期的开发工作中遇到向串口发送设备控制指令的需求,遂对串口编程进行了略微深入的钻研,在此对自己的一些心得和经验进行总结,以供大家参考与交流。 串口介绍  串口全称为串行接口,一般指COM接口,是...

    本人在近期的开发工作中遇到向串口发送设备控制指令的需求,遂对串口编程进行了略微深入的钻研,在此对自己的一些心得和经验进行总结,以供大家参考与交流。
    #串口介绍 #
      串口全称为串行接口,一般指COM接口,是采用串行通信方式的扩展接口。其特点是数据位的传送按位顺序进行,最少只需一根传输线即可完成,成本低但传送速度慢。由于串口(COM)不支持热插拔及传输速率较低,目前部分新主板和大部分便携电脑已取消该接口。现在串口多用于工业控制和测量设备以及部分通信设备中。
      根据美国电子工业协会(EIA: Electronic Industry Association)制定的标准,串口可以分为RS-232、RS-422以及RS-485等种类,其中以RS-232类型的接口最为典型和常见,本文所使用的是RS-232类型的9针串口(RS-232类型有25接口,但是现在几乎不再使用)。如图 1所示,是RS-232类型9针串口的实物示意图。RS-232类型9针串口每一个引脚的作用说明如图 2所示。
        图 1 RS232 9针串口实物示意图
            图 1 RS232 9针串口实物示意图

    图 2 RS232 9针串口的针脚示意图
            图 2 RS232 9针串口的针脚示意图

    想更加深入了解串口知识的读者请参阅以下内容:串行接口RS-232RS-422rs485

    Java对串口编程的API包#

    目前比较常见的针对Java的串口包有3个来源:一是1998年SUN发布的串口通信API:comm2.0.jar(Windows环境下)和comm3.0.jar(Linux/Solaris环境下);二是IBM的串口通信API;三是一些开源的API。本文介绍的是在Windows环境下使用java语言对串口进行编程,所以选取SUN的官方API(comm2.0.jar)。comm2.0.jar和comm3.0.jar的下载地址如下:
      comm2.0.jar:下载
      comm.jar:下载

    对串口编程的环境搭建

    软件环境搭建##

    在本文写作时,本人所使用的软件开发环境为:Windows7,Jdk1.6.0_10,Eclipse3.4.1。Java对串口编程的环境搭建分为以下步骤:
      1.下载并安装jdk,本人jdk的根目录是“D:\ProgramFiles\Java\jdk1.6.0_10”,在接下来的文章中路径“D:\ProgramFiles\Java\jdk1.6.0_10”将使用“%JAVA_HOME%”来代替;
      2.下载comm2.0.jar(下载链接见上文)并将串口编程必须的3个文件拷贝到jdk对应的文件夹中:
        2.1.将win32com.dll文件拷贝到“%JAVA_HOME%\bin”以及“%JAVA_HOME%\jre\bin”目录下
        2.2 将comm.jar文件拷贝到“%JAVA_HOME%\lib”以及“%JAVA_HOME%\jre\lib\ext”目录下
        2.3 将javax.comm.properties文件拷贝到“%JAVA_HOME%\lib”以及“%JAVA_HOME%\jre\lib”下
      3 新建工程并将comm.jar添加到工程文件中:
        3.1 新建java工程serialPortProgramming,并将该工程的运行时环境(JRE)指定为步骤1中新安装的jdk。
        3.2 在工程中新建“lib”文件夹,并将comm.jar文件拷贝到该文件夹下,右键点击该文件选择【Build Path】—【Add to Build Path】。
    ##“硬件” 环境准备 ##
      Java对串口编程,首先设备上需要有串口(这不废话吗),但如今的大多数电脑主板上并不带串口,所以本人用Virtual Serial Port Driver软件虚拟出一对串口COM11和COM21,以方便文章的写作和实验的进行。
      下载 Virtual Serial Port Driver7.1 并安装,使用压缩包中的“vspdctl.dll”文件替换软件安装根目录中的“vspdctl.dll”文件即可完成破解。安装Virtual Serial Port Driver之后用该软件创建一对端口(COM11和COM21),在此创建的一对串口将在之后的实验中再次使用到。因为串口COM11和COM21是通过软件虚拟的、相互连接的一对串口,所以从COM11发送的数据COM21会接收到,反之亦然。
      当然如果自己的设备上有串口的话也可以不用创建虚拟串口,只需要将一个串口的数据发送引脚(引脚3,如图 2所示)和另一个串口的数据接收引脚(引脚2)使用一根铜线链接即可实现数据的收发。如果设备上只有一个串口,要实现串口数据的收发,可以将串口的引脚2和引脚3使用铜线相连接,这样从本串口发送的数据就会通过本串口接收到。

    实例一:获取本地串口并实现打开与关闭

    在上文创建好的工程中新建包“com.serialPort.writer”并新建类OpenerAndCloser,该类实现串口的获取、打开与关闭。

    OpenerAndCloser.java

    package com.serialPort.writer;
    
    import java.util.Enumeration;
    import javax.comm.CommPortIdentifier;
    import javax.comm.NoSuchPortException;
    import javax.comm.PortInUseException;
    import javax.comm.SerialPort;
    import javax.comm.UnsupportedCommOperationException;
    
    /**
     * 该类实现3个功能
     * 1.列举出本地所有的串口;
     * 2.打开所有串口(但是未向串口中写数据);
     * 3.关闭打开的串口。
     */
    public class OpenerAndCloser {
    	public static void main(String[] args){
    		//1.获取本地所有的端口并输出其名称:
    		//1.1.用于标识端口的变量
    		CommPortIdentifier portIdentifier = null;
    		
    		//1.2.记录所有端口的变量
    		Enumeration<?> allPorts 
    			= CommPortIdentifier.getPortIdentifiers();
    		
    		//1.3.输出每一个端口
    		while(allPorts.hasMoreElements()){
    			portIdentifier 
    				= (CommPortIdentifier) allPorts.nextElement();
    			System.out.println("串口:" + portIdentifier.getName());
    		}
    		
    		//2.打开COM11和COM21端口
    		//2.1.获取两个端口
    		CommPortIdentifier com11 = null;
    		CommPortIdentifier com21 = null;
    		try {
    			com11 = CommPortIdentifier.getPortIdentifier("COM11");
    			com21 = CommPortIdentifier.getPortIdentifier("COM21");
    		} catch (NoSuchPortException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		
    		//2.2.打开两个端口,但是什么都没干
    		SerialPort serialCom11 = null;
    		SerialPort serialCom21 = null;
    		try {
    			//open方法的第1个参数表示串口被打开后的所有者名称,
    			//第2个参数表示如果串口被占用的时候本程序的最长等待时间,以毫秒为单位。
    			serialCom11 
    				= (SerialPort)com11.open("OpenerAndCloser", 1000);
    			serialCom21 
    				= (SerialPort)com21.open("OpenerAndCloser", 1000);
    		} catch (PortInUseException e) {
    			//要打开的端口被占用时抛出该异常
    			e.printStackTrace();
    		}
    		
    		//2.3.设置两个端口的参数
    		try {
    			serialCom11.setSerialPortParams(
    					9600, //波特率
    					SerialPort.DATABITS_8,//数据位数
    					SerialPort.STOPBITS_1,//停止位
    					SerialPort.PARITY_NONE//奇偶位
    			);
    			
    			serialCom21.setSerialPortParams(
    					9600, //波特率
    					SerialPort.DATABITS_8,//数据位数
    					SerialPort.STOPBITS_1,//停止位
    					SerialPort.PARITY_NONE//奇偶位
    			);
    		} catch (UnsupportedCommOperationException e) {
    			e.printStackTrace();
    		}
    		
    		//3.关闭COM11和COM21端口
    		//关闭端口的方法在SerialPort类中
    		serialCom11.close();
    		serialCom21.close();
    	}
    }
    
    

    在以上的代码中,有两个较为重要的类,在此做以说明,它们是类CommPortIdentifier和类SerialPort。这两个类都来自于comm.jar,CommPortIdentifier类代表本地串口,可以通过该类的静态方法getPortIdentifier或getPortIdentifiers获取本地的串口,该类的实例方法open用于打开串口。SerialPort类同样代表本地串口,不过其代表的是打开的串口,可以通过该类的实例方法close关闭已经打开的串口,也可以通过该类的实例方法获取串口的输入输出流,实现往串口数据的读写操作。
      执行Com11Writer类的main方法,就会发现控制台输出了本地机器的所有串口(包括虚拟串口和物理串口)。

    实例二:串口数据的读写

    向串口写数据##

    在包“com.serialPort.writer”下新建Com11Writer类,该类实现往COM11写入数据“Hello World!”的功能,向串口COM11写入的数据会发送到与其相连的另一个串口COM21,并被COM21所接收,从串口接收数据的方式将在下文讲到,以下是Com11Writer的源代码:

    Com11Writer.java

    package com.serialPort.writer;
    
    import java.io.IOException;
    import java.io.OutputStream;
    import javax.comm.CommPortIdentifier;
    import javax.comm.NoSuchPortException;
    import javax.comm.PortInUseException;
    import javax.comm.SerialPort;
    
    /**
     * Com11Writer类的功能是向COM11串口发送字符串“Hello World!”
     */
    public class Com11Writer {
    	
    	public static void main(String[] args) {
    		//1.定义变量
    		CommPortIdentifier com11 = null;//用于记录本地串口
    		SerialPort serialCom11 = null;//用于标识打开的串口
    		
    		try {
    			//2.获取COM11口
    			com11 = CommPortIdentifier.getPortIdentifier("COM11");
    			
    			//3.打开COM11
    			serialCom11 = (SerialPort) com11.open("Com11Writer", 1000);
    			
    			//4.往串口写数据(使用串口对应的输出流对象)
    			//4.1.获取串口的输出流对象
    			OutputStream outputStream = serialCom11.getOutputStream();
    			
    			//4.2.通过串口的输出流向串口写数据“Hello World!”:
    			//使用输出流往串口写数据的时候必须将数据转换为byte数组格式或int格式,
    			//当另一个串口接收到数据之后再根据双方约定的规则,对数据进行解码。
    			outputStream.write(new byte[]{'H','e','l','l','o',
    					' ','W','o','r','l','d','!'});
    			outputStream.flush();
    			//4.3.关闭输出流
    			outputStream.close();
    			
    			//5.关闭串口
    			serialCom11.close();
    		} catch (NoSuchPortException e) {
    			//找不到串口的情况下抛出该异常
    			e.printStackTrace();
    		} catch (PortInUseException e) {
    			//如果因为端口被占用而导致打开失败,则抛出该异常
    			e.printStackTrace();
    		} catch (IOException e) {
    			//如果获取输出流失败,则抛出该异常
    			e.printStackTrace();
    		}
    	}
    }
    
    

    从串口读数据##

    从串口COM11发送的数据最终将到达与其连通的串口COM21,如果COM21处于可用状态,则到达的数据将被缓存,等待程序的读取。从串口读入数据有多种模式,本文将介绍“轮询模式”和事件监听模式。
      “轮询模式”是指程序(线程)每隔固定的时间就对串口进行一次扫描,如果扫描发现串口中有可用数据,则进行读取。Com21PollingListener类使用“事件监听模式”读取串口COM21接收到的数据:

    Com21PollingListener.java

    package com.serialPort.listener;
    
    import java.io.IOException;
    import java.io.InputStream;
    import javax.comm.CommPortIdentifier;
    import javax.comm.NoSuchPortException;
    import javax.comm.PortInUseException;
    import javax.comm.SerialPort;
    
    /**
     * Com21PollingListener类使用“轮训”的方法监听串口COM21,
     * 并通过COM21的输入流对象来获取该端口接收到的数据(在本文中数据来自串口COM11)。
     */
    public class Com21PollingListener {
    	public static void main(String[] args){
    		//1.定义变量
    		CommPortIdentifier com21 = null;//未打卡的端口
    		SerialPort serialCom21 = null;//打开的端口
    		InputStream inputStream = null;//端口输入流
    		
    		try{
    			//2.获取并打开串口COM21
    			com21 = CommPortIdentifier.getPortIdentifier("COM21");
    			serialCom21 = (SerialPort) com21.open("Com21Listener", 1000);
    			
    			//3.获取串口的输入流对象
    			inputStream = serialCom21.getInputStream();
    			
    			//4.从串口读入数据
    			//定义用于缓存读入数据的数组
    			byte[] cache = new byte[1024];
    			//记录已经到达串口COM21且未被读取的数据的字节(Byte)数。
    			int availableBytes = 0;
    			
    			//无限循环,每隔20毫秒对串口COM21进行一次扫描,检查是否有数据到达
    			while(true){
    				//获取串口COM21收到的可用字节数
    				availableBytes = inputStream.available();
    				//如果可用字节数大于零则开始循环并获取数据
    				while(availableBytes > 0){
    					//从串口的输入流对象中读入数据并将数据存放到缓存数组中
    					inputStream.read(cache);
    					//将获取到的数据进行转码并输出
    					for(int j = 0;j < cache.length && j < availableBytes; j++){
    						//因为COM11口发送的是使用byte数组表示的字符串,
    						//所以在此将接收到的每个字节的数据都强制装换为char对象即可,
    						//这是一个简单的编码转换,读者可以根据需要进行更加复杂的编码转换。
    						System.out.print((char)cache[j]);
    					}
    					System.out.println();
    					//更新循环条件
    					availableBytes = inputStream.available();
    				}
    				//让线程睡眠20毫秒
    				Thread.sleep(20);
    			}
    		}catch(InterruptedException e){
    			e.printStackTrace();
    		}catch (NoSuchPortException e) {
    			//找不到串口的情况下抛出该异常
    			e.printStackTrace();
    		} catch (PortInUseException e) {
    			//如果因为端口被占用而导致打开失败,则抛出该异常
    			e.printStackTrace();
    		} catch (IOException e) {
    			//如果获取输出流失败,则抛出该异常
    			e.printStackTrace();
    		}
    	}
    }
    

    “事件监听模式”是为串口注册一个事件监听类,当有数据到达串口的时候就会触发事件,在事件的响应方法中读取串口接收到的数据。Com21EventListener类使用“事件监听模式”读取串口COM21接收到的数据:
    Com21EventListener.java

    package com.serialPort.listener;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.TooManyListenersException;
    import javax.comm.CommPortIdentifier;
    import javax.comm.NoSuchPortException;
    import javax.comm.PortInUseException;
    import javax.comm.SerialPort;
    import javax.comm.SerialPortEvent;
    import javax.comm.SerialPortEventListener;
    
    /**
     * Com21EventListener类使用“事件监听模式”监听串口COM21,
     * 并通过COM21的输入流对象来获取该端口接收到的数据(在本文中数据来自串口COM11)。
     * 使用“事件监听模式”监听串口,必须字定义一个事件监听类,该类实现SerialPortEventListener
     * 接口并重写serialEvent方法,在serialEvent方法中编写监听逻辑。
     */
    public class Com21EventListener implements SerialPortEventListener {
    	
    	//1.定义变量
    	CommPortIdentifier com21 = null;//未打卡的端口
    	SerialPort serialCom21 = null;//打开的端口
    	InputStream inputStream = null;//输入流
    	
    	//2.构造函数:
    	//实现初始化动作:获取串口COM21、打开串口、获取串口输入流对象、为串口添加事件监听对象
    	public Com21EventListener(){
    		try {
    			//获取串口、打开窗串口、获取串口的输入流。
    			com21 = CommPortIdentifier.getPortIdentifier("COM21");
    			serialCom21 = (SerialPort) com21.open("Com21EventListener", 1000);
    			inputStream = serialCom21.getInputStream();
    			
    			//向串口添加事件监听对象。
    			serialCom21.addEventListener(this);
    			//设置当端口有可用数据时触发事件,此设置必不可少。
    			serialCom21.notifyOnDataAvailable(true);
    		} catch (NoSuchPortException e) {
    			e.printStackTrace();
    		} catch (PortInUseException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		} catch (TooManyListenersException e) {
    			e.printStackTrace();
    		}
    	}
    
    	//重写继承的监听器方法
    	@Override
    	public void serialEvent(SerialPortEvent event) {
    		//定义用于缓存读入数据的数组
    		byte[] cache = new byte[1024];
    		//记录已经到达串口COM21且未被读取的数据的字节(Byte)数。
    		int availableBytes = 0;
    		
    		//如果是数据可用的时间发送,则进行数据的读写
    		if(event.getEventType() == SerialPortEvent.DATA_AVAILABLE){
    			try {
    				availableBytes = inputStream.available();
    				while(availableBytes > 0){
    					inputStream.read(cache);
    					for(int i = 0; i < cache.length && i < availableBytes; i++){
    						//解码并输出数据
    						System.out.print((char)cache[i]);
    					}
    					availableBytes = inputStream.available();
    				}
    				System.out.println();
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    	
    	//在main方法中创建类的实例
    	public static void main(String[] args) {
    		new Com21EventListener();
    	}
    }
    

    读写程序的联合运行

    串口能接收到数据的前提是该串口处于打开(可用)状态,如果串口处于关闭状态,那么发送到该串口的数据就会丢失。所以在实验的过程中,如果使用铜线连接同一个串口的引脚2和引脚3,一定要注意的是千万不能在向串口发送完数据之后关闭该串口,然后再次打开串口去读取数据,一定要让串口始终处于打开状态直到程序运行结束。
      基于以上的说明,在本文所涉及到的实例中,首先运行Com21PollingListener类(或Com21EventListener类)中的main方法打开端口监听程序,然后再运行Com11Writer类的main方法通过COM11向COM21发送数据,这样程序就能从COM21读取数据。

    参考与鸣谢

    在本文写作的过程中参考了很多网络资源,其中参考了如下几篇文章,在此对所有作者的无私奉献表示衷心的感谢。
      http://blog.csdn.net/luoduyu/article/details/2182321
      http://www.cnblogs.com/dyufei/archive/2010/09/19/2573913.html
      http://www.cnblogs.com/dyufei/archive/2010/09/19/2573912.html
      http://www.cnblogs.com/dyufei/archive/2010/09/19/2573911.html

    本文实例的源工程文件可以从以下链接获取

    • 链接: https://pan.baidu.com/s/1m9qXcYQMcppkEcD8YVxEbQ
    • 密码: x5xw

    打赏作者,让他更勤奋

    这里写图片描述

    有问题也可以联系作者进一步交流嗷

    这里写图片描述

    展开全文
  • javaWeb调用串口通讯

    2020-08-10 09:51:52
    内含已经写好的串口监听类,定时器,以及串口数据转换方法,二进制、十进制互转、十进制、十六进制以及明文之间的互转。java串口通讯功能其实是固定了的,照抄就可以;数据转换才是最大的工作量,因为串口通讯时走的...
  • 内含已经写好的串口监听类,定时器,以及串口数据转换方法,二进制、十进制互转、十进制、十六进制以及明文之间的互转。java串口通讯功能其实是固定了的,照抄就可以;数据转换才是最大的工作量,因为串口通讯时走的...
  • //如果正在关闭,忽略操作,直接返回,尽快的完成串口监听线程的一次循环 try { Listening = true;//设置标记 int n = comm.BytesToRead; byte[] buf = new byte[n];//当前的串口数据 comm.Read(buf, 0, n);//...
  • java串口编程-读取称重仪表中净重

    千次阅读 2011-10-12 17:08:30
    一、需求说明  将仪表和计算机串口相连,计算机通过软件向仪表发送指令,然后仪表返回结果,在计算机软件界面上显示。...2、使用串口通讯工具、串口监听工具进行调试  网上可以下到很多相关软件,主要...

    一、需求说明

            将仪表和计算机串口相连,计算机通过软件向仪表发送指令,然后仪表返回结果,在计算机软件界面上显示。

     

    二、实现过程

    1、查看仪表说明书

            仪表型号为XK3190-A9,主要是查看相关参数(波特率、数据位、停止位、奇偶校验),通讯方式及指令规则。

     

    2、使用串口通讯工具、串口监听工具进行调试

            网上可以下到很多相关软件,主要是方便测试。串口通讯工具、串口监听工具(AccessPort)

     

    3、将仪表“通讯方式”改成“指令模式”

            根据说明书,在仪表上输入:打印设置--->98--->输入--->....

     

    4、java编码(重点)

    (1)使用comm在java程序中管理本地端口,目前,常见的Java串口包有SUN在1998年发布的串口通信API:comm2.0.jar(Windows下)、comm3.0.jar(Linux/Solaris);IBM的串口通信API以及一个开源的实现。鉴于在Windows下SUN的API比较常用以及IBM的实现和SUN的在API层面都是一样的,那个开源的实现又不像两家大厂的产品那样让人放心,这里就只介绍SUN的串口通信API在Windows平台下的使用。

    (2)附件中“javacomm20-win32.zip”



     

    (3)解压该压缩包,从commapi\Readme.html开始读起

    (4)Copy win32com.dll to your <JDK>\bin directory.
    (5)Copy comm.jar、javax.comm.properties to your <APP>\lib directory.

    (6)研究java.comm包中的相关类及功能



    ----javax.comm.CommPortIdentifier 这个类主要用于对串口进行管理和设置,是对串口进行访问控制的核心类。

     

    ----javax.comm.SerialPort 这个类用于描述一个RS-232串行通信端口的底层接口,它定义了串口通信所需的最小功能集。通过它,用户可以直接对串口进行读、写及设置工作。 

     

     

    (7)开始编写自己的代码

     

    ##### 串口参数配置(配置到spring容器里) #####

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
    
    <beans>
    	<bean class="self.phoenix.application.SerialParameters">
    		<!-- 端口名称 -->
    		<property name="portName"><value>COM1</value></property>
    		<!-- 波特率 -->
    		<property name="baudRate"><value>4800</value></property>
    		<!-- 输入流控制 -->
    		<property name="flowControlIn"><value>0</value></property>
    		<!-- 输出流控制 -->
    		<property name="flowControlOut"><value>0</value></property>
    		<!-- 数据位 -->
    		<property name="databits"><value>8</value></property>
    		<!-- 停止位 -->
    		<property name="stopbits"><value>1</value></property>
    		<!-- 奇偶校验 -->
    		<property name="parity"><value>0</value></property>
    	</bean>
    </beans>

     

    #####用于处理注册驱动、打开端口、发送指令、关闭端口及释放资源#####

     

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    import javax.comm.CommDriver;
    import javax.comm.CommPortIdentifier;
    import javax.comm.NoSuchPortException;
    import javax.comm.PortInUseException;
    import javax.comm.SerialPort;
    import javax.comm.UnsupportedCommOperationException;
    
    import self.phoenix.application.SerialConnectionException;
    import self.phoenix.application.SerialParameters;
    
    public class SerialConnUtils {
    	
    	private OutputStream os= null;
        private InputStream is = null;
        private CommPortIdentifier portId = null;
    	private SerialPort sPort = null;
    	private SerialParameters parameters = null;
    	private CommDriver commDriver = null;
    	
    	public SerialConnUtils(SerialParameters parameters) {
    		this.parameters = parameters;
    		if(commDriver == null){
    			try {
    				commDriver = (CommDriver) Class.forName("com.sun.comm.Win32Driver").newInstance();
    				commDriver.initialize();
    			} catch (InstantiationException e) {
    				e.printStackTrace();
    			} catch (IllegalAccessException e) {
    				e.printStackTrace();
    			} catch (ClassNotFoundException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    	
    	public void openConnection() throws SerialConnectionException {
    		// Obtain a CommPortIdentifier object for the port you want to open.
    		try {
    		    portId = CommPortIdentifier.getPortIdentifier(parameters.getPortName());
    		} catch (NoSuchPortException e) {
    		    throw new SerialConnectionException(e.getMessage());
    		}
    		
    		// Open the port represented by the CommPortIdentifier object. Give
    		// the open call a relatively long timeout of 30 seconds to allow
    		// a different application to reliquish the port if the user 
    		// wants to.
    		try {
    		    sPort = (SerialPort)portId.open("SerialDemo", 30000);
    		} catch (PortInUseException e) {
    		    throw new SerialConnectionException(e.getMessage());
    		}
    		
    		try {
    		    setConnectionParameters();
    		} catch (SerialConnectionException e) {	
    		    sPort.close();
    		    throw e;
    		}
    		
    		// Set receive timeout to allow breaking out of polling loop during
    		// input handling.
    		/*try {
    		    sPort.enableReceiveTimeout(30);
    		} catch (UnsupportedCommOperationException e) {
    		}*/
    	}
    	
    	public void setConnectionParameters() throws SerialConnectionException {
    
    		// Save state of parameters before trying a set.
    		int oldBaudRate = sPort.getBaudRate();
    		int oldDatabits = sPort.getDataBits();
    		int oldStopbits = sPort.getStopBits();
    		int oldParity = sPort.getParity();
    		int oldFlowControl = sPort.getFlowControlMode();
    
    		// Set connection parameters, if set fails return parameters object
    		// to original state.
    		try {
    			sPort.setSerialPortParams(parameters.getBaudRate(),
    					parameters.getDatabits(), parameters.getStopbits(),
    					parameters.getParity());
    		} catch (UnsupportedCommOperationException e) {
    			parameters.setBaudRate(oldBaudRate);
    			parameters.setDatabits(oldDatabits);
    			parameters.setStopbits(oldStopbits);
    			parameters.setParity(oldParity);
    			throw new SerialConnectionException("Unsupported parameter");
    		}
    
    		// Set flow control.
    		try {
    			sPort.setFlowControlMode(parameters.getFlowControlIn()
    					| parameters.getFlowControlOut());
    		} catch (UnsupportedCommOperationException e) {
    			throw new SerialConnectionException("Unsupported flow control");
    		}
    	}
    	
    	/**
    	 * 向端口发送十六进制指令 02 41 44 30 35 03
    	 * 返回:02 41 44 2B 30 30 30 35 36 36 31 31 41 03
    	 * @return 净重
    	 * @throws SerialConnectionException
    	 */
    	public double sendSerialCommand() throws SerialConnectionException {
    
    		//十六进制指令
    		byte[] baKeyword = new byte[6];
    		baKeyword[0] = 0x02;
    		baKeyword[1] = 0x41;
    		baKeyword[2] = 0x44;
    		baKeyword[3] = 0x30;
    		baKeyword[4] = 0x35;
    		baKeyword[5] = 0x03;
    		
    		//净重
    		Double netWeight = 0.0;
    
    		try {
    			//向串口发送指令
    			os = sPort.getOutputStream();
    			os.write(baKeyword, 0, baKeyword.length);
    			
    			//用于从串口读数据,返回14字节的信息
        		byte[] readBuffer = new byte[14]; 
        		int numBytes = 0; 
        		is = sPort.getInputStream();
        		
        		numBytes = is.read(readBuffer);
        		
        		System.out.println(new String(readBuffer));
        		
        		//4~12为数据内容
        		String value = Hex2StringUtils.byte2HexStrByi(readBuffer,4,12);
    			if (value != null && !value.trim().equals(""))
    				netWeight = Double.valueOf(value) / 1000;
        		
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		return netWeight;
    	}
    
    	public void closeConnection() {
    		// Check to make sure sPort has reference to avoid a NPE.
    		if (sPort != null) {
    			try {
    				// close the i/o streams.
    				os.close();
    				is.close();
    			} catch (IOException e) {
    				System.err.println(e);
    			}
    
    			// Close the port.
    			sPort.close();
    			
    			//Release resources
    			portId = null;
    		}
    	}
    }
    

     

     ###### ACSII与16进制转换的Utils类 #####

    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    
    public class Hex2StringUtils {
    
    	public static void main(String[] args) throws IOException {
    
    	}
    
    	/*
    	 * 16进制数字字符集
    	 */
    	private static String hexString = "0123456789ABCDEF";
    
    	/*
    	 * 将字符串编码成16进制数字,适用于所有字符(包括中文)
    	 */
    	public static String encode(String str) {
    		// 根据默认编码获取字节数组
    		byte[] bytes = str.getBytes();
    		StringBuilder sb = new StringBuilder(bytes.length * 2);
    		// 将字节数组中每个字节拆解成2位16进制整数
    		for (int i = 0; i < bytes.length; i++) {
    			sb.append(hexString.charAt((bytes[i] & 0xf0) >> 4));
    			sb.append(hexString.charAt((bytes[i] & 0x0f) >> 0));
    		}
    		return sb.toString();
    	}
    
    	/*
    	 * 将16进制数字解码成字符串,适用于所有字符(包括中文)
    	 */
    	public static String decode(String bytes) {
    		ByteArrayOutputStream baos = new ByteArrayOutputStream(
    				bytes.length() / 2);
    		// 将每2位16进制整数组装成一个字节
    		for (int i = 0; i < bytes.length(); i += 2)
    			baos.write((hexString.indexOf(bytes.charAt(i)) << 4 | hexString
    					.indexOf(bytes.charAt(i + 1))));
    		return new String(baos.toByteArray());
    	}
    
    	public static String byte2HexStr(byte[] b) {
    		String hs = "";
    		String stmp = "";
    		for (int n = 0; n < b.length; n++) {
    			stmp = (Integer.toHexString(b[n] & 0XFF));
    			if (stmp.length() == 1)
    				hs = hs + "0" + stmp;
    			else
    				hs = hs + stmp;
    			// if (n<b.length-1) hs=hs+":";
    		}
    		return hs.toUpperCase();
    	}
    
    	public static String byteHexStr(byte b) {
    		String hs = "";
    		String stmp = "";
    		// /for (int n = 0; n < b.length; n++) {
    		stmp = (Integer.toHexString(b & 0XFF));
    		if (stmp.length() == 1)
    			hs = "0" + stmp;
    		else
    			hs = stmp;
    		// if (n<b.length-1) hs=hs+":";
    		// }
    		return hs.toUpperCase();
    	}
    
    	public static String byte2HexStrByi(byte[] b, int start, int end) {
    		String hs = "";
    		String stmp = "";
    		for (int n = start; n < end; n++) {
    			stmp = (Integer.toHexString(b[n] & 0XFF));
    			if (stmp.length() == 1)
    				hs = hs + decode("0" + stmp);
    			else
    				hs = hs + decode(stmp);
    			// if (n<b.length-1) hs=hs+":";
    		}
    		return hs.toUpperCase();
    	}
    }
    

     

    ##### 调用函数,取得净重值 #####

    public double getSerialPortNetWeight() {
    		double netWeight = 0;
    		SerialConnUtils serialConnUtils = null;
    		try {
    			//调用串口通讯工具类
    			serialConnUtils = new SerialConnUtils(parameters);
    			//打开连接
    			serialConnUtils.openConnection();
    			//发送指令,并取得返回值
    			netWeight = serialConnUtils.sendSerialCommand();
    		} catch (Exception ex) {
    			ex.printStackTrace();
    		} finally {
    			//释放资源
    			serialConnUtils.closeConnection();
    		}
    		return netWeight;
    	}

     

     

    5、在串口监听工具配合下进行软件调试

     

    129     [00000000]  IRP_MJ_CREATE                   Port Opened - javaw.exe
    130     [00000000]  IOCTL_SERIAL_SET_BAUD_RATE      Baud Rate: 4800
    131     [00000000]  IOCTL_SERIAL_SET_LINE_CONTROL   StopBits: 1, Parity: No, DataBits: 8
    132     [00000000]  IOCTL_SERIAL_SET_BAUD_RATE      Baud Rate: 9600
    133     [00000000]  IOCTL_SERIAL_SET_LINE_CONTROL   StopBits: 1, Parity: No, DataBits: 8
    134     [00000000]  IOCTL_SERIAL_SET_BAUD_RATE      Baud Rate: 9600
    135     [00000000]  IOCTL_SERIAL_SET_LINE_CONTROL   StopBits: 1, Parity: No, DataBits: 8
    136     [00000000]  IOCTL_SERIAL_SET_BAUD_RATE      Baud Rate: 4800
    137     [00000000]  IOCTL_SERIAL_SET_LINE_CONTROL   StopBits: 1, Parity: No, DataBits: 8
    138     [00000000]  IOCTL_SERIAL_SET_BAUD_RATE      Baud Rate: 4800
    139     [00000000]  IOCTL_SERIAL_SET_LINE_CONTROL   StopBits: 1, Parity: No, DataBits: 8
    140     [00000000]  IOCTL_SERIAL_SET_BAUD_RATE      Baud Rate: 4800
    141     [00000000]  IOCTL_SERIAL_SET_LINE_CONTROL   StopBits: 1, Parity: No, DataBits: 8
    142     [00000000]  IRP_MJ_WRITE                    Length: 0006, Data: 02 41 44 30 35 03 
    143     [00000013]  IRP_MJ_READ                     Length: 0014, Data: 02 41 44 2B 30 30 30 35 36 36 31 31 41 03 
    144     [00001356]  IRP_MJ_CLOSE                    Port Closed
    145     [00000000]  IRP_MJ_CREATE                   Port Opened - javaw.exe
    146     [00000000]  IOCTL_SERIAL_SET_BAUD_RATE      Baud Rate: 4800
    147     [00000000]  IOCTL_SERIAL_SET_LINE_CONTROL   StopBits: 1, Parity: No, DataBits: 8
    148     [00000000]  IOCTL_SERIAL_SET_BAUD_RATE      Baud Rate: 9600
    149     [00000000]  IOCTL_SERIAL_SET_LINE_CONTROL   StopBits: 1, Parity: No, DataBits: 8
    150     [00000000]  IOCTL_SERIAL_SET_BAUD_RATE      Baud Rate: 9600
    151     [00000000]  IOCTL_SERIAL_SET_LINE_CONTROL   StopBits: 1, Parity: No, DataBits: 8
    152     [00000000]  IOCTL_SERIAL_SET_BAUD_RATE      Baud Rate: 4800
    153     [00000000]  IOCTL_SERIAL_SET_LINE_CONTROL   StopBits: 1, Parity: No, DataBits: 8
    154     [00000000]  IOCTL_SERIAL_SET_BAUD_RATE      Baud Rate: 4800
    155     [00000000]  IOCTL_SERIAL_SET_LINE_CONTROL   StopBits: 1, Parity: No, DataBits: 8
    156     [00000000]  IOCTL_SERIAL_SET_BAUD_RATE      Baud Rate: 4800
    157     [00000000]  IOCTL_SERIAL_SET_LINE_CONTROL   StopBits: 1, Parity: No, DataBits: 8
    158     [00000000]  IRP_MJ_WRITE                    Length: 0006, Data: 02 41 44 30 35 03 
    159     [00000013]  IRP_MJ_READ                     Length: 0014, Data: 02 41 44 2B 30 30 30 36 39 36 31 31 36 03 
    160     [00000349]  IRP_MJ_CLOSE                    Port Closed
    

     

    Length:0006,      Data:02 41 44 30 35 03    为发送的十六进制指令

    Length:0014,      Data:02 41 44 2B 30 30 30 36 39 36 31 31 36 03 为返回的十六进制内容

     

    三、总结:调试过程中,串口监听工具起着很重要的作用。

    展开全文
  • android串口通信——电子锁

    千次阅读 2016-02-22 16:12:15
    android串口通信——电子锁android串口通信电子锁 本文解决的问题 一锁孔板基础 拨码开关设置板地址 锁地址 二锁孔板的基本指令 1板地址查询0x80 2开锁命令如下0x8A 3读锁状态命令 0X80门开关状态反馈 三开锁的控制...

    android串口通信——电子锁

    本文解决的问题

    1.如何打开电子锁

    2.如何判断电子锁是否关闭

    一、锁孔板基础

    锁孔板的图
    锁孔板的图

    锁孔板中主要说一哈图示中的1和2两个部分:

    图中编号 说明
    1 拨码开关(设置板地址)
    2 锁的接口,这个是确定锁的地址的

    1.拨码开关(设置板地址)

    板地址的设置:拨码开关从 8 到 1 表示从高位到低位,拨到数字端为 1,另一端为 0。 00000001 表示板地址为 1,00000010 表示板地址为 2,00000011 表示板子地址为 3,依次类推。

    3.锁地址

    图中2的,每个接口都会有对应的编号,这个就做锁的编号。
    那么我们想要操作一把锁,需要知道的是:板编号和锁编号。因为我们是可以接入多个板的。
    例如:现在板的的编号是:00000001,锁的编号是 1 ,那么我们想要操作这个锁,就需要使用到 板1锁1来控制。

    二、锁孔板的基本指令

    1、板地址查询0x80:

    命令头 板地址 状态 校验码 (异或)
    0X80 0X01 0X00 0X99 0X18
    返回:
    命令头 固定 从机板地址 固定 校验位
    0X80 0X01 0X010X40 0X99 XXXX

    2、开锁命令如下0x8A:

    命令 板地址 锁地址 状态 校验码 (异或)
    0X8A 0X01-0XC8 0X0118 0X11 xx
    
    如:上位机发 0X8A 0X01 0X01 0X11 0X9B16 进制), 1 秒后返回
    命令 板地址 锁地址 状态 校验码
    0X8A 0X01 0X01 0X11 0X9B (锁为开)
    0X8A 0X01 0X01 0X00 0X8A (锁为关)
    
    如:上位机发 0X8A 0X02 0X01 0X11 0X9816 进制),开从控制柜柜门, 1 秒后返回: 
    命令 板地址 锁地址 状态 校验码
    0X8A 0X02 0X01 0X11 0X98 (锁为开)
    0X8A 0X02 0X01 0X00 0X89 (锁为关)

    3、读锁状态命令 0X80(门开关状态反馈):

    起始 板地址 锁地址 命令 校验码 (异或)
    0X80 0X01-0XC8 0X0018 0X33 XX
    
    如:上位机发 0X80 0X01 0X01 0X33 0XB316 进制),返回
    命令 板地址 锁地址 状态 校验码
    0X80 0X01 0X01 0X11 0X91 (锁为开)
    0X80 0X01 0X01 0X00 0X80 (锁为关)
    
    如:上位机发 0X80 0X01 0X00 0X33 0XB216 进制),返回
    起始 板地址 状态 1 状态 2 状态 3 状态 4 命令 校验码
    0X80 0X01 0XFF 0XFF 0XFF 0XFF 0X33 0XB2
    状态:从状态 4 开始到状态 1 低位到高位对应的锁为 132.
    
    如:上位机发 0X80 0X02 0X01 0X33 0XB016 进制),读从控制柜柜门,返回
    命令 板地址 锁地址 状态 校验码
    0X80 0X02 0X01 0X11 0X92 (锁为开)
    0X80 0X02 0X01 0X00 0X83 (锁为关)
    
    如:上位机发 0X80 0X02 0X00 0X33 0XB116 进制),读取从控制柜所有柜门,返回 
    起始 板地址 状态 1 状态 2 状态 3 状态 4 命令 校验码
    0X80 0X02 0XFF 0XFF 0XFF 0XFF 0X33 0XB1
    状态:从状态 4 开始到状态 1 低位到高位对应的锁为 132.

    三、开锁的控制和关锁的监听

    波特率默认是:9600

    1.DevicesUtils 硬件操作类的github地址

    硬件的操作类和前面的几篇文章所描述的是一样,获取可以查看项目中的 DevicesUtils:
    https://github.com/qiwenming/QwmLockDemo/blob/master/app/src/main/java/com/qwm/qwmlockdemo/utils/DevicesUtils.java

    2.StringUtils 字符串的工具类

    package com.qwm.qwmlockdemo.utils;
    
    import android.util.Log;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * @author qiwenming
     * @creation 2015-6-18 下午5:27:20
     * @instruction 字符串工具
     */
    public class StringUtils {
    
        /**
         * byte数组转为对应的16进制字符串
         * 
         * @param src
         * @return
         */
        public static String bytesToHexString(byte[] src) {
            StringBuilder stringBuilder = new StringBuilder("");
            if (src == null || src.length <= 0) {
                return null;
            }
            for (int i = 0; i < src.length; i++) {
                int v = src[i] & 0xFF;
                String hv = Integer.toHexString(v);
                if (hv.length() < 2) {
                    stringBuilder.append(0);
                }
                stringBuilder.append(hv);
            }
            return stringBuilder.toString();
        }
    
        /**
         * byte数组转为对应的16进制字符串
         * 
         * @param src
         * @return
         */
        public static String bytesToHexString(byte[] src, int length) {
            StringBuilder stringBuilder = new StringBuilder("");
            if (src == null || length <= 0) {
                return null;
            }
            for (int i = 0; i < length; i++) {
                int v = src[i] & 0xFF;
                String hv = Integer.toHexString(v);
                if (hv.length() < 2) {
                    stringBuilder.append(0);
                }
                stringBuilder.append(hv);
            }
            return stringBuilder.toString();
        }
    
        /**
         * 十六进制编码字符串转为对应的二进制数组
         * 
         * @param s
         * @return
         */
        public static byte[] hexStringToBytes(String s) {
            byte[] baKeyword = new byte[s.length() / 2];
            for (int i = 0; i < baKeyword.length; i++) {
                try {
    
                    baKeyword[i] = (byte) (Integer.parseInt(
                            s.substring(i * 2, i * 2 + 2), 16));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return baKeyword;
        }
    
        /**
         * 十六进制转ascii
         * 
         * @param hex
         * @return
         */
        public static String convertHexToString(String hex) {
    
            StringBuilder sb = new StringBuilder();
            StringBuilder temp = new StringBuilder();
    
            // 49204c6f7665204a617661 split into two characters 49, 20, 4c...
            for (int i = 0; i < hex.length() - 1; i += 2) {
    
                // grab the hex in pairs
                String output = hex.substring(i, (i + 2));
                // convert hex to decimal
                int decimal = Integer.parseInt(output, 16);
                // convert the decimal to character
                sb.append((char) decimal);
    
                temp.append(decimal);
            }
    
            return sb.toString();
        }
    
        /**
         * 10进制字符串 转为16进制字符串
         * @param dec
         * @return
         */
        public static String convertDecToHexString(String s) {
            Log.i("", s);
            String str = Integer.toHexString(Integer.parseInt(s));
            if(str.length()%2==1){
                str = "0"+str;
            }
            return str;
        }
    
        /**
         * 通过做异或运算,求出校验码
         * @param cmd
         * @return
         */
        public static String xor(String cmd)
        {
            if(cmd.length()%2!=0){
                cmd = "0"+cmd;
            }
            int result = 0;
            for (int i = 0; i < cmd.length()-1; i=i+2) {
                //System.out.println(cmd.substring(i,i+2));
                result ^= Integer.valueOf(cmd.substring(i, i + 2), 16);
                System.out.println("16-->"+ Integer.valueOf(cmd.substring(i, i + 2), 16));
                System.out.println("result:"+result);
            }
            return Integer.toHexString(result);
        }
    
        /**
         * 以"-"拆分字符串
         * @param str
         * @return
         */
        public static String[] splitString(String str){
            return str.split("-");
        }
    
        public static String takeCity(String str){
            String nstr = null;
            if(str!=null){
                nstr=str.substring(0, str.length()-1);
            }
            return nstr;
        }
    
        /**
         * 时间戳转为日期
         * @param datestr
         * @return
         */
        public static String getSimpDate(String datestr){
            SimpleDateFormat sdf = new SimpleDateFormat("MM-dd HH:mm");
            String date = sdf.format(new Date(Long.parseLong(datestr) ));
            return date;
        }
        /**
         * 时间戳转为日期
         * @param smdateint
         * @return
         */
        public static String getSimpDate(long smdateint){
            SimpleDateFormat sdf = new SimpleDateFormat("MM-dd HH:mm");
            String date = sdf.format(new Date(smdateint));
            System.out.println(date);
            return date;
        }
    }

    3.OpenLockActivity 开锁和关锁监听的操作类

    package com.qwm.qwmlockdemo;
    
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.support.v7.app.AppCompatActivity;
    import android.util.Log;
    import android.view.View;
    import android.widget.TextView;
    
    import com.qwm.qwmlockdemo.bean.LockBean;
    import com.qwm.qwmlockdemo.bean.SerialPortSendData;
    import com.qwm.qwmlockdemo.utils.DevicesUtils;
    import com.qwm.qwmlockdemo.utils.StringUtils;
    
    import java.util.TimerTask;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author qiwenming
     * @date 2016/2/22 0022 下午 1:48
     * @ClassName: OpenLockActivity
     * @ProjectName: 
     * @PackageName: com.qwm.qwmlockdemo
     * @Description: 开锁和关锁的界面
     * 开锁的步骤阐述:
     * 1.获取到需要打开的锁
     * 2.打开锁
     * 3.打开关锁的监听
     */
    public class OpenLockActivity  extends AppCompatActivity {
        /**
         * 标题
         */
        private TextView titleTv;
        /**
         * 倒计时
         */
        private TextView timeTv;
        private LockBean lockBean;
        /**
         * 关闭的查询指令
         */
        public String closecmd = "";
        /**
         * 关闭的标志
         */
        private String closeStopStr1;
        private String openStopStr2;
        /**
         * 检查所状态
         */
        private SerialPortSendData data;
        /**
         * 硬件操作类
         */
        private DevicesUtils device;
        /**
         * 倒计时的时间 s
         */
        private int second = 300;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.open_lock_layout);
    //        setContentView(R.layout.open_lock_layout);
            titleTv = (TextView)findViewById(R.id.tv_title);
            timeTv = (TextView)findViewById(R.id.tv_time);
            //获取传递过来的bean
            lockBean = (LockBean) getIntent().getSerializableExtra("lockBean");
            titleTv.setText("板"+lockBean.boardAddStr+"锁"+lockBean.lockAddStr+"打开");
            openBox();
        }
    
        /**
         * 放回
         * @param view
         */
        public void black(View view){
            finish();
        }
    
    
        /**
         * 开锁
         * 读取数据时候  开头的8a 有时候会有 时候没有  这样的处理 我们只能根据结尾的  9b来判断了
         */
        private void openBox() {
            String opencmd = "";
            String comCloseStr = "";
            //拼接指令: 8a(80) + 板编号 + 锁编号 + 11(00)+亦或值
            opencmd = "8a"+StringUtils.convertDecToHexString(lockBean.boardAddStr)+ StringUtils.convertDecToHexString(lockBean.lockAddStr);
            comCloseStr = "80"+StringUtils.convertDecToHexString(lockBean.boardAddStr)+StringUtils.convertDecToHexString(lockBean.lockAddStr);
            closecmd = comCloseStr+"33";
    
            //计算关闭的校验码
            closeStopStr1 = "00"+StringUtils.xor(comCloseStr+"00");
            openStopStr2 = "11"+StringUtils.xor(comCloseStr+"11");
            Log.i("closeStopStr1------", closeStopStr1);
            Log.i("closeStopStr2------", openStopStr2);
            closecmd+=StringUtils.xor(closecmd);
    
            //开锁的数据封装
            final String openStopstr = "11"+StringUtils.xor(opencmd+"11");
            final String stopstr1 = "00"+StringUtils.xor(opencmd+"00");
            opencmd+=openStopstr;
            SerialPortSendData sendData = new SerialPortSendData(lockBean.addressStr, Integer.parseInt(lockBean.bauteRateStr), opencmd, "", "",openStopstr, false);
            sendData.stopStr = openStopstr;
            sendData.stopStr1 = stopstr1;
    
            //检查锁状态的数据封装
            data = new SerialPortSendData(lockBean.addressStr, Integer.parseInt(lockBean.bauteRateStr), closecmd, "", "", closeStopStr1, false);
            data.stopStr = closeStopStr1;
            data.stopStr1 = openStopStr2;
            device = new DevicesUtils();
            //发送指令AAA
            device.toSend(this, sendData, new DevicesUtils.ReciverListener() {
                @Override
                public void onReceived(String receviceStr) {
                    Log.i("onReceived_receviceStr", receviceStr);
                    countdown();
                    if(receviceStr.length()<4){//代表开锁失败
                        mHandler.sendEmptyMessageDelayed(CLOSEBOX, 1000);
                        return;
                    }
                    String stop = receviceStr.substring(receviceStr.length()-4, receviceStr.length());
                    if(stop.equals(openStopstr)){
                        mHandler.sendEmptyMessageDelayed(CLOSEBOX, 1000);
                    }
                }
    
                @Override
                public void onFail(String fialStr) {
                }
                @Override
                public void onErr(Exception e) {
                }
    
            });
        }
    
        public final int CLOSEBOX = 101;
        public final int COUNTDOWN = 102;
        /**
         * 用户动态监听
         */
        private Handler mHandler = new Handler(){
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case CLOSEBOX:
                        if(second>1){//去监听锁的状态
                            closeLock();
                        }else{
                            mHandler.removeMessages(CLOSEBOX);
                            mHandler.removeMessages(COUNTDOWN);
                            finish();
                        }
                        break;
                    case COUNTDOWN:
                        second--;
                        timeTv.setText(second+"s");
                        if(second<=0&& this!=null){
                            finish();
                        }else{
                            mHandler.sendEmptyMessageDelayed(COUNTDOWN, 1000);
                        }
                        break;
                    default:
                        break;
                }
            };
        };
    
        /**
         * 倒计时
         */
        private void countdown(){
            final Handler handler=new Handler(){
                public void handleMessage(Message msg) {
                    timeTv.setText(second+"s");
                };
            };
            final ScheduledExecutorService ses= Executors.newScheduledThreadPool(1);
            ses.scheduleAtFixedRate(new TimerTask() {
                @Override
                public void run() {
                    second-=1;
                    handler.sendEmptyMessage(second);
                    if(second==0){
                        ses.shutdown();
                        finish();
                    }
                }
            }, 0, 1, TimeUnit.SECONDS);
        }
    
    
        /**
         * 关锁检测
         *    1.创建一个 handler 来发送数据 读取锁的状态
         *    2.app关箱子
         *    3.后台
         *
         */
        public void closeLock() {
            Log.i("closeBox", "closeBox--------");
            device = new DevicesUtils();
            device.toSend(this, data, new DevicesUtils.ReciverListener() {
                @Override
                public void onReceived(String receviceStr) {
                    Log.i("closeBox_receviceStr", receviceStr);
                    if (receviceStr.length() < 4) {
                        mHandler.sendEmptyMessageDelayed(CLOSEBOX, 1000);
                        return;
                    }
                    String stop = receviceStr.substring(receviceStr.length() - 4, receviceStr.length());
                    if (stop.equals(closeStopStr1)) {
                        mHandler.removeMessages(CLOSEBOX);
                        mHandler.removeMessages(COUNTDOWN);
                        finish();
                    } else {
                        mHandler.sendEmptyMessageDelayed(CLOSEBOX, 1000);
                    }
                }
    
                @Override
                public void onFail(String fialStr) {
                    mHandler.sendEmptyMessageDelayed(CLOSEBOX, 1000);
                }
    
                @Override
                public void onErr(Exception e) {
                    mHandler.sendEmptyMessageDelayed(CLOSEBOX, 1000);
                }
            });
        }
    
        @Override
        protected void onDestroy() {
            mHandler.removeMessages(CLOSEBOX);
            mHandler.removeMessages(COUNTDOWN);
            super.onDestroy();
        }
    }
    

    四、图示

    这里写图片描述
    这里写图片描述
    这里写图片描述

    五、源码下载

    展开全文
  • 在网上没有微信小程序蓝牙板块特征值读取和...串口调试助手上发送指令,小程序也能正确监听到(图3):   Demo源码下载地址:https://download.csdn.net/download/qq_36456827/10591811 串口调试助手下载地址:...

    在网上没有微信小程序蓝牙板块特征值读取和监听的Demo,所以自己写了一个,现在分享出来,下面是Demo的页面

    发送指令后(图2)在串口调试助手上正确收到相应值:

    串口调试助手上发送指令,小程序也能正确监听到(图3):

     

    Demo源码下载地址:https://download.csdn.net/download/qq_36456827/10591811

    串口调试助手下载地址:https://download.csdn.net/download/qq_36456827/10591839


    下面是部分源码的介绍:

     

    wxml:

    <view class="container">
     <view class='btn' bindtap='onConnect'>{{isConnected?'已连接':'连接蓝牙'}}</view>
     <view class='btn' style='margin-top:24rpx' catchtap='onGetuuid'>
        {{serviceId&&characteristicId?'已获取设备信息':'获取设备信息'}}
     </view>
     <input placeholder='在此输入发送指令' bindinput='onCommand'></input>
     <view class='btn' style='margin-top:0' catchtap='onSendCommand' >发送指令</view>
     <view class='btn' style='margin-top:24rpx' catchtap='onCloseConnect'>断开连接</view>
    </view>

    页面布局很简单,就四个按钮和一个文本框,下面主要介绍几个比较关键的js函数

     onConnect() {
        let that = this;
        wx.openBluetoothAdapter({
          success: function(res) {
            if (!that.data.isConnected) {
              that.startBluetoothDevicesDiscovery();//开始搜索蓝牙设备
              wx.showLoading({
                title: '搜索中',
              })
            }
          },
          fail: function(res) {
            wx.showToast({
              title: '请先开启蓝牙',
              icon: 'none',
              duration: 1000
            })
          }
        });
      },

    onConnect()函数首先会检查用户的蓝牙是否开启,如果开启会调用startBluetoothDevicesDiscovery()函数开始搜索蓝牙设备,否则提示用户开启蓝牙

     

    下面是连接蓝牙的函数,间歇性多次调用搜索函数(可能需要多次搜索才能搜索到相应设备),作了超时处理,这里的搜索和连接的超时时间都设置为12秒

    getConnect: function() {
        let that = this;
        let timer = setInterval(function() {
            wx.getBluetoothDevices({
              success: function(res) {
                console.log("devices", res);
                for (var i = 0; i < res.devices.length; i++) {
                  if (res.devices[i].name == that.data.deviceName) {
                     //因为ios和安卓搜到的deviceId不一样,所以这里用设备名来查找
                    wx.hideLoading();
                    wx.showLoading({
                      title: '连接中',
                    })
                    that.setData({
                      isFinded: true,
                      deviceId: res.devices[i].deviceId
                    });
                    clearInterval(timer);
                    console.log('设备号', that.data.deviceId);
                    console.log("开始尝试建立连接");
                    wx.createBLEConnection({
                      deviceId: that.data.deviceId,
                      timeout: 12000,
                      success: function(res) {
                        console.log(res);
                        if (res.errCode == 0) {
                          console.log('连接成功')
                          that.setData({
                            isConnected: true
                          });
                          wx.stopBluetoothDevicesDiscovery();//连接成功后停止搜索
                        } else {
                          wx.showModal({
                            title: '提示',
                            content: '不能正常对蓝牙设备进行连接',
                            showCancel: false
                          })
                        }
                      },
                      fail: (res) => {
                        wx.hideLoading();
                        if (res.errCode == 10012) {
                          wx.showModal({
                            title: '提示',
                            content: '连接超时',
                            showCancel: false
                          })
                        }
                        console.warn("fail", res);
                      },
                      complete: () => {
                        wx.hideLoading();
                      }
                    })
                    break;
                  }
                }
              }
            });
          },3000);
        setTimeout(function() {
          if (!that.data.isFinded && !that.data.isConnected) {
            clearInterval(timer);
            that.setData({
              isFailed: false
            });
            wx.hideLoading();
            wx.showModal({
              title: '提示',
              content: '搜索蓝牙超时',
              showCancel: false
            })
          }
        }, 12000);
     }

     

    接下来是获取serviceId和CharacteristicId,获取到相应的id后直接对特征值进行监听

     onGetuuid(){
        let that = this;
        if(that.data.isConnected && that.data.isFailed){
        wx.showLoading({
          title: '获取serviceId',
        })
        console.log("开始获取设备信息");
        wx.getBLEDeviceServices({
          deviceId: that.data.deviceId,
          success(getServicesRes) {
            console.log("getServicesRes", getServicesRes);
            let service = getServicesRes.services[0]
            let serviceId = service.uuid
            wx.showLoading({
              title: '获取characteristicId',
            })
            wx.getBLEDeviceCharacteristics({
              deviceId: that.data.deviceId,
              serviceId: serviceId,
              success(getCharactersRes) {
                console.log("getCharactersRes", getCharactersRes);
                wx.hideLoading();
                let characteristic = getCharactersRes.characteristics[0]
                let characteristicId = characteristic.uuid
                that.setData({
                  serviceId: serviceId,
                  characteristicId: characteristicId
                })
                console.log('成功获取uuId', that.data.serviceId, that.data.characteristicId);
                wx.notifyBLECharacteristicValueChange({
                  state: true,
                  deviceId: that.data.deviceId,
                  serviceId: serviceId,
                  characteristicId: characteristicId,
                  success() {
                    console.log('开始监听特征值')
                    wx.onBLECharacteristicValueChange(function (onNotityChangeRes) {
                      console.log('监听到特征值更新', onNotityChangeRes);
                      let characteristicValue = that.ab2hex(onNotityChangeRes.value);
                      wx.showModal({
                        title: '监听到特征值更新',
                        content: `更新后的特征值(16进制格式):${characteristicValue}`,
                        showCancel: false
                      })
                    })
                  },
                  fail: (res) => {
                    console.warn("监听特征值失败");
                  }
                })
              },
              fail: (err) => {
                console.warn("获取特征值信息失败", err);
              },
              complete: (res) => {
                wx.hideLoading();
              }
            })
          },
          fail: (err) => {
            console.warn("获取服务信息失败", err);
          },
          complete: () => {
            wx.hideLoading();
          }
        })
        }else{
          wx.showToast({
            title: '请先连接蓝牙',
          })
        }
      },

    这里只是简单的选取了serviceId和CharacteristicId的第一个值,可根据业务需要作更改

     

    接下来是发送指令的函数

     onSendCommand() {
        let that = this;
        if (that.data.serviceId && that.data.characteristicId) {
          wx.writeBLECharacteristicValue({
            deviceId: that.data.deviceId,
            serviceId: that.data.serviceId,
            characteristicId: that.data.characteristicId,
            value: that.str2ab(that.data.command),
            success: function(res) {
              console.log("发送指令成功");
              wx.showToast({
                title: '发送成功',
                icon: 'none'
              })
            },
            fail: function(res) {
              console.warn("发送指令失败", res)
            }
          })
        }else{
          wx.showModal({
            title: '提示',
            content: '请先获取设备信息',
            showCancel:false
          })
        }
      }

    需要注意的是支持发送的数据类型为ArrayBuffer,所以先要进行转码处理

      str2ab(str) {
        var buf = new ArrayBuffer(str.length);
        var bufView = new Uint8Array(buf);
        for (var i = 0, strLen = str.length; i < strLen; i++) {
          bufView[i] = str.charCodeAt(i)
        }
        return buf
      }

    除此之外,BLE数据包传输规定了一次只能传输20byte,所以如果数据量较大,需要做分包传输处理

      let chunkCount = Math.ceil(command.length / 18) let subCommands = []
      for (let i = 0; i < chunkCount; i++) {
        subCommands.push(`${chunkCount}${i}${command.substr(i * 18, 18)}`)
      }//hunkCount 表示总共多少包,i表示当前是第几个包

    此外,需要前一个数据包返回结果后再发送后续数据包,否则一些安卓设备会引起发送异常,对此官方文档也有相关介绍:

    所以为每个数据包发送增加相应间隔(setTimeout),减少一些安卓设备发送异常:

      setTimeout(function() {
         send(i + 1)
      }, 20)

     

     

    最后附上微信小程序官方的蓝牙模块API文档:https://developers.weixin.qq.com/miniprogram/dev/api/bluetooth.html

     

     

    版权声明:本文为博主原创文章,转载请注明出处

    展开全文
  • 前一篇文章提到我们的外接设备里有打印机,一开始是直接用usb连接到Android主板上,然后进行通讯发送打印指令,但是经常打印命令发一半就莫名其妙的停掉。后来,我加了usb插入和拔出的监听,发现原因就是usb被拔出了...
  • 保留了232口,在学习或者开发时可以监听51低端单片机和模块指令执行情况,能更快的找出原因,节省开发和学习的时间。  2、此模块供电要求:5V 供电 ,电脑调试初期电脑USB供电可以满足要求。因为我们加了1000UF的大...
  • 后来发现,java从发送指令监听到数据返回的时间大概在150ms到300ms之间,尝试了多种方法也无法解决。 2.大量的数据存储,根据计算,1天的数据量在4亿3200万条左右,这个数据怎么存到数据库中才能保证查询不会太...
  • 4.25 setserial指令:设置或显示串口的相关信息 4.26 smbclient指令:可存取SMB/CIFS服务器的用户端程序 4.27 smbd指令:Samba服务端 4.28 ssh指令:加密的远程登录工具 4.29 statserial指令:samba服务器程序 4.30 ...
  • AT指令语法3.1语法概述3.2 AT指令集基础控制指令(括号中为译者注释,并不会显示)用户AT指令用户AT指令执行逻辑“ 普通LoRa模式”专有指令设置LoRa监听/发送参数接收模式接收数据输出模式选择LoRaWAN模式专有指令...
  • 现在做的项目是往cpld发送指令控制,发送16进制的指令需要接收cpld返回的16进制数据判断指令是否正确执行,这是按照网上的例子写的,现在解决了发送指令,但是接收部分不知道如何接收16进制的返回数据,用串口调试...
  • 后来发现,java从发送指令监听到数据返回的时间大概在150ms到300ms之间,尝试了多种方法也无法解决。 2.大量的数据存储,根据计算,1天的数据量在4亿3200万条左右,这个数据怎么存到数据库中才能保证查询不会太...
  • 2、在公网服务器上开一个监听,如图 3、勾选连接服务器A,地址设置为公网服务器的公网IP,端口号为服务器上上位机软件的端口号。点击“设置并保存所有参数”,如图 4、重启后,在串口发送区发送数据,此时在...
  • 后来发现,java从发送指令监听到数据返回的时间大概在150ms到300ms之间,尝试了多种方法也无法解决。 2.大量的数据存储,根据计算,1天的数据量在4亿3200万条左右,这个数据怎么存到数据库中才能保证查询不会太过...
  • 【MQTT学习】ESP8266 AT指令连接阿里云

    万次阅读 多人点赞 2019-06-13 13:14:59
    前段时间使用Mqtt-fx工具测试了一下基于mqtt的阿里云连接测试,学习的目的就是将整个流程转移到都硬件设备上,为了更深度的学习,这里使用ESP8266模块和USB串口工具进行测试。在开始前需要用到几个软件工具,一个是...
  • 通过串口监听工具监听串口数据,在使用官网配套软件的使用,使得获取到最基本的一些发出指令,其后输入结尾的是CRC,因为没有使用Modbus,所以需要另外编写一个子VI来进行计算 普通的收发指令通过事件窗口来进行,...
  • 2020-08-06

    2020-08-06 11:18:20
    有线串口数据、指令监听抓取经验总结 一、配备软件 所需软件为串口助手或串口测试软件或者串口仿真软件,(modbus rtu需要用到CRC计算器) 二、配套硬件 TTL转USB模块两个(监听其他通信系统自己配一个即可)...
  • 使用Web页面配置ESP8266的参数

    千次阅读 2018-07-18 18:57:52
     使用Web页面配置ESP8266的参数相对于使用串口AT指令配置更加直观和简单。与配置路由器方式类似。 基本思路  基本思路是ESP8266工作AP模式下,作为TCP Server监听TCP Client的连接。因为网页HTTP默认的端口是80...
  • 完整的FX2N监控命令解释

    千次阅读 2013-12-06 11:41:07
    同时监听PC串口数据,把串口数据剪贴到Wordpad,这样得到约264MB的数据。 通过大量数据的分析归纳,得出了完整的监控命令解释。 下以其中一例程说明这问题。 左边是梯形图指令,右是下载到PLC的指令码。 ...
  • 文章目录一、ATK-LORA-01二、模块的三种工作状态三、模块的四种通信工作模式3.1 通信的一般模式 03.1.1 无线透明数据传输3.1.2 定向数据传输3.1.3 广播和数据监听3.2 唤醒模式 13.3 省电模式 23.4 信号强度模式 3四...
  • 前言:esp8266推出已久,esp8266-01/01s是常见的两款,本文使用串口助手通过AT指令来连接mqtt服务器并测试发布和订阅主题: 01和01s区别不大,一样使用即可,下图实为01s 打开mqtt服务器监视当前连接(看...
  • 一:设备连接4G串口服务器,主动透传数据(十六进制)至TCP通讯链路。 二:通过指令取读取寄存器的数据 通过webSocket 显示实时数据至页面,对历史数据进行分析 根据具体业务和设备数量,可以加个消息中间件(kafka...
  • 然后问题来了,用串口软件监听DTU接收的消息内容,翻译成十六进制之后 这段请求帧变成了“01 05 00 02 C3 BF 00 2D C3 BA”![图片说明](https://img-ask.csdn.net/upload/201906/14/1560501772_690458.png) 这是...
  • //监听串口 myservo.attach(9); //舵机控制 myservo.write(0); delay(10000); } void loop() { while(Serial.available()) { char c; c = Serial.read(); //读取串口数据 Serial.println(c); ...

空空如也

空空如也

1 2
收藏数 33
精华内容 13
关键字:

串口监听指令