最近在做一个小程序和硬件连接有关的项目,硬件是一块没有联网的开发板,首先要通过BLE蓝牙发送消息,将wifi账号和密码通过蓝牙发送到板子上,结果这个硬件厂商比较奇葩的是,他们板子的蓝牙消息走的是串口,直接去监听ubuntu系统的蓝牙还监听不到蓝牙消息,这就很奇葩了,问过厂家后才知道他们蓝牙走的是串口,那直接去监听蓝牙肯定监听不到了,没办法,改代码吧,从串口获取蓝牙消息。
比较常用的有两种方法,第一种是使用jSerialComm串口监听方式,第二种是rxtx串口监听方式,第二种官网下载jar包的时候出现404,目前我也还没有找到合适的jar包,有兴趣的可以了解一下,所以这里就只上第一种方法了。
好了,话不多说,上代码
一、maven引入
com.fazecast
jSerialComm
2.6.0
二、代码
import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
@Component
@Slf4j
public class jSerialCommUtil {
// 当前串口对象引用
static SerialPort serialPort;
// 堵塞队列:用来存放串口发送到服务端的数据
public BlockingQueue msgQueue = new LinkedBlockingQueue();
// 线程控制标识
public static boolean flag = true;
/**
* 打开电脑上指定的串口
*
* @param portName 端口名称,如 COM1,为 null 时,默认使用电脑中能用的端口中的第一个
* @param b 波特率(baudrate),如 9600
* @param d 数据位(datebits),如 8
* @param s 停止位(stopbits),如 1
* @param p 校验位 (parity),如 0
* @return 打开的串口对象,打开失败时,返回 null
*/
public final SerialPort openSerialPort(String portName, int b, int d, int s, int p) {
SerialPort[] commPorts = SerialPort.getCommPorts();
for (SerialPort itemPort : commPorts) {
System.out.println("板子的串口名称---------"+itemPort.getSystemPortName());
if (portName.equals(itemPort.getSystemPortName())) {
//判断串口是否打开,如果没打开,就打开串口。
if (itemPort.isOpen()) {
System.out.println("串口 " + portName + "已打开...");
return null;
}
//设置超时
itemPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_BLOCKING | SerialPort.TIMEOUT_WRITE_BLOCKING, 1000, 1000);
// 第一个参数为波特率,默认9600;
// 第二个参数为每一位的大小,默认8,可以输入5到8之间的值;
// 第三个参数为停止位大小,只接受内置常量,可以选择(ONE_STOP_BIT, ONE_POINT_FIVE_STOP_BITS, TWO_STOP_BITS);
// 第四位为校验位,同样只接受内置常量,可以选择 NO_PARITY, EVEN_PARITY, ODD_PARITY, MARK_PARITY,SPACE_PARITY。
itemPort.setComPortParameters(b, d, s, p);
//设置串口的控制流,可以设置为disabled,或者CTS, RTS/CTS, DSR, DTR/DSR, Xon, Xoff, Xon/Xoff等
//serialPort.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED);
boolean isOpen = itemPort.openPort();
if (isOpen) {
serialPort = itemPort;
System.out.println("打开串口 " + portName + " 成功...");
try {
Thread.sleep(1000);//打开串口后要休眠一段时间才能接收到输入流
} catch (InterruptedException e) {
e.printStackTrace();
}
itemPort.addDataListener(new SerialPortDataListener() {
@Override
public int getListeningEvents() {
return SerialPort.LISTENING_EVENT_DATA_RECEIVED;
}
@Override
public void serialEvent(SerialPortEvent serialPortEvent) {
byte[] newData = serialPortEvent.getReceivedData();
String weight = new String(newData);
if (StringUtils.isNotBlank(weight)) {
msgQueue.add(weight);
}
System.out.println(String.format("串口%s接收到数据大小:%s,串口数据内容:%s"
, serialPortEvent.getSerialPort().getSystemPortName(), newData.length, new String(newData)));
//TODO 这里已经从串口获取到蓝牙消息了,处理你自己的业务逻辑
}
});
return serialPort;
} else {
System.out.println("打开串口 " + portName + " 失败...");
}
}
}
if (serialPort == null) {
System.out.println("打开串口 " + portName + " 失败...,请检查串口名称是否正确,或是否被占用");
}
return null;
}
}
OK,到此就搞定了,已经可以从串口获取到蓝牙消息。
最后别忘了关闭串口,当然是否关闭串口也要根据自己的业务逻辑来
/**
* 关闭串口
*
* @param serialPort 要关闭的串口对象
*/
public Boolean closeSerialPort(SerialPort serialPort) {
if (serialPort != null) {
//关闭串口。该函数同样会返回一个boolean值,表明串口是否成功关闭
return serialPort.closePort();
}
return false;
}
这个方法有个好处就是,不用写个死循环去一直监听,也不用使用webscoket一直去访问,也算是大大减轻了服务器压力。但是得看硬件厂家是不是也和这家一样奇葩,走的是串口
方法到这里就结束了,然后把这个打成jar包,部署到开发板上,通过BLE调试助手app,就可以试着发送蓝牙消息了。
注意:板子中的波特率这个不一定都是9600,这个要咨询一下开发板的硬件厂家,串口名称也需要问一下厂家。数据位,停止位,校验位一般情况下都是固定的,8、1、0写死就行。