-
c#监听串口是怎么监听不到
2015-03-13 06:47:55*功能:监听串口 *创建作者: ***/ 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针串口实物示意图
图 2 RS232 9针串口的针脚示意图想更加深入了解串口知识的读者请参阅以下内容:串行接口、RS-232、RS-422、rs485
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.javapackage 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串口通讯demo源码.zip
2019-09-06 16:58:23内含已经写好的串口监听类,定时器,以及串口数据转换方法,二进制、十进制互转、十进制、十六进制以及明文之间的互转。java串口通讯功能其实是固定了的,照抄就可以;数据转换才是最大的工作量,因为串口通讯时走的... -
Wpf 串口通讯,更新uI后,关闭串口会卡死,怎么解决?
2018-09-17 02:42:54//如果正在关闭,忽略操作,直接返回,尽快的完成串口监听线程的一次循环 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)Copycomm.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:15android串口通信——电子锁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 0X01到0X40 0X99 XXXX
2、开锁命令如下0x8A:
命令 板地址 锁地址 状态 校验码 (异或) 0X8A 0X01-0XC8 0X01—18 0X11 xx 如:上位机发 0X8A 0X01 0X01 0X11 0X9B ( 16 进制), 1 秒后返回 命令 板地址 锁地址 状态 校验码 0X8A 0X01 0X01 0X11 0X9B (锁为开) 0X8A 0X01 0X01 0X00 0X8A (锁为关) 如:上位机发 0X8A 0X02 0X01 0X11 0X98 ( 16 进制),开从控制柜柜门, 1 秒后返回: 命令 板地址 锁地址 状态 校验码 0X8A 0X02 0X01 0X11 0X98 (锁为开) 0X8A 0X02 0X01 0X00 0X89 (锁为关)
3、读锁状态命令 0X80(门开关状态反馈):
起始 板地址 锁地址 命令 校验码 (异或) 0X80 0X01-0XC8 0X00—18 0X33 XX 如:上位机发 0X80 0X01 0X01 0X33 0XB3 ( 16 进制),返回 命令 板地址 锁地址 状态 校验码 0X80 0X01 0X01 0X11 0X91 (锁为开) 0X80 0X01 0X01 0X00 0X80 (锁为关) 如:上位机发 0X80 0X01 0X00 0X33 0XB2 ( 16 进制),返回 起始 板地址 状态 1 状态 2 状态 3 状态 4 命令 校验码 0X80 0X01 0XFF 0XFF 0XFF 0XFF 0X33 0XB2 状态:从状态 4 开始到状态 1 低位到高位对应的锁为 1—32. 如:上位机发 0X80 0X02 0X01 0X33 0XB0 ( 16 进制),读从控制柜柜门,返回 命令 板地址 锁地址 状态 校验码 0X80 0X02 0X01 0X11 0X92 (锁为开) 0X80 0X02 0X01 0X00 0X83 (锁为关) 如:上位机发 0X80 0X02 0X00 0X33 0XB1 ( 16 进制),读取从控制柜所有柜门,返回 起始 板地址 状态 1 状态 2 状态 3 状态 4 命令 校验码 0X80 0X02 0XFF 0XFF 0XFF 0XFF 0X33 0XB1 状态:从状态 4 开始到状态 1 低位到高位对应的锁为 1—32.
三、开锁的控制和关锁的监听
波特率默认是:9600
1.DevicesUtils 硬件操作类的github地址
硬件的操作类和前面的几篇文章所描述的是一样,获取可以查看项目中的 DevicesUtils:
https://github.com/qiwenming/QwmLockDemo/blob/master/app/src/main/java/com/qwm/qwmlockdemo/utils/DevicesUtils.java2.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(); } }
四、图示
五、源码下载
-
微信小程序蓝牙模块实现特征值读取和特征值监听Demo
2018-08-08 21:26:10在网上没有微信小程序蓝牙板块特征值读取和...串口调试助手上发送指令,小程序也能正确监听到(图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
版权声明:本文为博主原创文章,转载请注明出处
-
Android 硬件开发之旅 -- 232串口通讯
2019-12-12 22:05:29前一篇文章提到我们的外接设备里有打印机,一开始是直接用usb连接到Android主板上,然后进行通讯发送打印指令,但是经常打印命令发一半就莫名其妙的停掉。后来,我加了usb插入和拔出的监听,发现原因就是usb被拔出了... -
SIM900A 通过RS232串口进行短信的发送。
2020-01-16 21:04:00保留了232口,在学习或者开发时可以监听51低端单片机和模块指令执行情况,能更快的找出原因,节省开发和学习的时间。 2、此模块供电要求:5V 供电 ,电脑调试初期电脑USB供电可以满足要求。因为我们加了1000UF的大... -
java串口通信及处理大量数据
2019-03-01 16:36:10后来发现,java从发送指令到监听到数据返回的时间大概在150ms到300ms之间,尝试了多种方法也无法解决。 2.大量的数据存储,根据计算,1天的数据量在4亿3200万条左右,这个数据怎么存到数据库中才能保证查询不会太... -
Linux指令一周通 (技术图书大系).azw3
2019-05-22 13:19:064.25 setserial指令:设置或显示串口的相关信息 4.26 smbclient指令:可存取SMB/CIFS服务器的用户端程序 4.27 smbd指令:Samba服务端 4.28 ssh指令:加密的远程登录工具 4.29 statserial指令:samba服务器程序 4.30 ... -
ASR6501模组Cortex-M0+ SX1262 PSoC+LoRaWAN节点模块 CubeCell系列AT指令中文手册
2020-07-26 10:57:02AT指令语法3.1语法概述3.2 AT指令集基础控制指令(括号中为译者注释,并不会显示)用户AT指令用户AT指令执行逻辑“ 普通LoRa模式”专有指令设置LoRa监听/发送参数接收模式接收数据输出模式选择LoRaWAN模式专有指令... -
java的rxtx串口通信怎么接收16进制数据
2016-07-18 05:53:44现在做的项目是往cpld发送指令控制,发送16进制的指令需要接收cpld返回的16进制数据判断指令是否正确执行,这是按照网上的例子写的,现在解决了发送指令,但是接收部分不知道如何接收16进制的返回数据,用串口调试... -
使用java进行串口通信的编程,并且需要处理大量的数据量。
2019-03-01 16:31:19后来发现,java从发送指令到监听到数据返回的时间大概在150ms到300ms之间,尝试了多种方法也无法解决。 2.大量的数据存储,根据计算,1天的数据量在4亿3200万条左右,这个数据怎么存到数据库中才能保证查询不会太... -
4G模块WH-LTE-7S4网络AT指令使用实例
2020-10-15 14:00:162、在公网服务器上开一个监听,如图 3、勾选连接服务器A,地址设置为公网服务器的公网IP,端口号为服务器上上位机软件的端口号。点击“设置并保存所有参数”,如图 4、重启后,在串口发送区发送数据,此时在... -
java后端使用线程池进行(modbus协议)串口通信的编程,并且需要处理大量的数据量?
2019-07-11 10:40:21后来发现,java从发送指令到监听到数据返回的时间大概在150ms到300ms之间,尝试了多种方法也无法解决。 2.大量的数据存储,根据计算,1天的数据量在4亿3200万条左右,这个数据怎么存到数据库中才能保证查询不会太过... -
【MQTT学习】ESP8266 AT指令连接阿里云
2019-06-13 13:14:59前段时间使用Mqtt-fx工具测试了一下基于mqtt的阿里云连接测试,学习的目的就是将整个流程转移到都硬件设备上,为了更深度的学习,这里使用ESP8266模块和USB串口工具进行测试。在开始前需要用到几个软件工具,一个是... -
舟正科级DAMQ-4202基于LabVIEW的数据通信采集
2020-10-20 19:25:16通过串口监听工具监听串口数据,在使用官网配套软件的使用,使得获取到最基本的一些发出指令,其后输入结尾的是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-01Lora模块的使用测试
2020-11-04 09:25:00文章目录一、ATK-LORA-01二、模块的三种工作状态三、模块的四种通信工作模式3.1 通信的一般模式 03.1.1 无线透明数据传输3.1.2 定向数据传输3.1.3 广播和数据监听3.2 唤醒模式 13.3 省电模式 23.4 信号强度模式 3四... -
esp8266-01/01s AT连接mqtt 发布/订阅,简单测试(新手向)
2020-12-28 14:17:47前言:esp8266推出已久,esp8266-01/01s是常见的两款,本文使用串口助手通过AT指令来连接mqtt服务器并测试发布和订阅主题: 01和01s区别不大,一样使用即可,下图实为01s 打开mqtt服务器监视当前连接(看... -
Java开发、TCP通讯、物联网标准、解决方案
2020-07-02 10:40:38一:设备连接4G串口服务器,主动透传数据(十六进制)至TCP通讯链路。 二:通过指令取读取寄存器的数据 通过webSocket 显示实时数据至页面,对历史数据进行分析 根据具体业务和设备数量,可以加个消息中间件(kafka... -
JS十六进制字符串转字符串问题
2019-06-14 16:49:02然后问题来了,用串口软件监听DTU接收的消息内容,翻译成十六进制之后 这段请求帧变成了“01 05 00 02 C3 BF 00 2D C3 BA” 这是... -
初学,做一个树莓派的人脸识别开门,python3运行不报错但也运行不起来
2021-01-19 16:42:19//监听软串口 myservo.attach(9); //舵机控制 myservo.write(0); delay(10000); } void loop() { while(Serial.available()) { char c; c = Serial.read(); //读取串口数据 Serial.println(c); ...
-
monit mysql_monit配置监控启动nginx php mysql redis mongodb 服务器
-
monit监控mysql_monit监控软件安装配置
-
MySQL Router 实现高可用、负载均衡、读写分离
-
云开发后台+微信扫码点餐小程序+cms网页管理后台 含后厨端和用户端
-
Galera 高可用 MySQL 集群(PXC v5.7+Hapro)
-
为首次部署MongoDB做好准备:容量计划和监控
-
soundshed-app:浏览和管理放大音-源码
-
挖矿病毒及应急响应
-
MySQL 多平台多模式(安装、配置和连接 详解)
-
mongodb oplog java_实时同步MongoDB Oplog开发指南
-
数字信号控制器电磁敏感度的环境温度影响研究
-
mui如何引入jquery_动态加载(异步加载)jquery/MUI类库 页面加载完成后加载js类库...
-
转行做IT-第15章 Collection、泛型、Iterator
-
非常好用的ocr识别软件.rar
-
DS:数据科学分支-源码
-
基于python的dango框架购物商城毕业设计毕设源代码使用教程
-
MySQL 事务和锁
-
VMware vSphere ESXi 7 精讲/VCSA/VSAN
-
cart263:CART263的课程作业存储库-源码
-
MySQL 多实例安装 及配置主从复制实验环境