往常做的都是普通APP,没有物联网这个概念,所以对二进制 ,十六进制的理解少之又少,看到这些数值也很懵逼。
进制参考:https://www.cnblogs.com/wslook/p/9385415.html
异或,与等算法参考:https://blog.csdn.net/xiaopihaierletian/article/details/78162863
首先要向硬件开发者索要 波特率 串口地址 协议规则 是否有心跳 等 相关内容,每种协议的校验,包括每个位置的数据都代表不同的意思,一并都是16进制的数值进行通信。(我们的通信是已经封装好了,具体内容我也不清楚)
这里我只说一些注意事项,以及校验等相关认知 (我也是模模糊糊的)
1.遇到问题之一:
将所有的串口地址都试了一遍,没有一个有回应的(其实心跳的话,很好试,只要打开基本上就会有心跳过来)
解决方案: 将硬件丢给硬件开发者调试,应该是线路没接好或者其他的原因,反正我不知道,后来再给我的时候就可以了。
2.遇到问题之二:
发起命令有应答了,但是硬件调试发现硬件有应答,而接入到安卓设备,安卓设备收到应答的概率是30%。
起初怀疑安卓设备有问题,返修。良久后还是不对,查看自己的代码 ,重点来了 : 因为我第一次对接协议,不知道他的命令是不可以多条同时发送的,一并发了七八条。
然后我改了自己的代码,逐条发送,应答后再继续下一步。
解决方案:将安卓设备的插口换了一个,就可以了,地址为“/dev/ttyS3” 之前用的是“/dev/ttyS2” ,其实两个接口效果应该是一样的,都是232 。当然不知道什么原因,反正这个就是可以了。
3.协议说明
其中一只协议的格式
1,帧头是固定的不变的
2,地址是可变的,可能不同的地址代表不一样的功能
3,数据长度代表的是 后面数据内容的长度(有些可能不一样,数据长度可能会代表命令码+数据内容的总长度) 。 例如:当数据长度 = (命令码+数据内容) 的长度 , 数据内容是 [0000], 命令码是 [A1] 那这个时候 , 命令长度就是03 。
4,命令码,就是不同的命令代表不同的含义 ,一般协议里面会写清楚 ,什么操作用什么命令码,带什么命令数据
5,数据内容,配合命令码带入数据,协议上一般都会写的。
7,校验位 ,很多种方式,异或校验啊,cr16校验等。反正这里传的都是16进制的数据
4,解析硬件应答过来的数据
ReceiveCallbacksh.java 是已经封装过的类了,只知道这个方法就可以了
onReceive(String devicePath, String baudrateString, byte[] received, int size)
devicePath 代表串口地址,baudrateString 代表波特率 received 收到的字节数据,size收到数据的长度
主要代码:
@Override
public void onReceive(String devicePath, String baudrateString, byte[] received, int size) {
LogPlus.i("DataReceiver", "接收数据=" + ByteUtil.bytes2HexStr(received, 0, size));
mByteBuffer.put(received, 0, size);
mByteBuffer.flip();
byte b;
int readable;
while ((readable = mByteBuffer.remaining()) >= Protocol.MIN_PACK_LEN) {
mByteBuffer.mark(); // 标记一下开始的位置
int frameStart = mByteBuffer.position();
//校验帧头 开始==========
b = mByteBuffer.get();
if (b != Protocol.FRAME_HEAD_0) { // 第1个byte要3B
continue;
}
b = mByteBuffer.get();
if (b != Protocol.FRAME_HEAD_1) { // 第2个byte要B3
continue;
}
//校验帧头 结束==========
b = mByteBuffer.get();
if (!(b == Protocol.ADDRESS_1||b == Protocol.ADDRESS_2||b == Protocol.ADDRESS_3
|| b == Protocol.ADDRESS_4 || b == Protocol.ADDRESS_5)) { //校验地址
continue;
}
byte[] ba = new byte[]{mByteBuffer.get()};
// 数据长度
final int cmdDataLen =(int) ByteUtil.hexStr2decimal(ByteUtil.bytes2HexStr(ba));
// 总数据长度 = 不包含数据位的长度+刚动态获取的数据位长度,就是总长度 ,
// 看第三步的通信数据格式 , Protocol.PACK_LEN = 5 , cmdDataLen = received[3] 的值转成10进制后的值)
int total = Protocol.PACK_LEN + cmdDataLen;
// 如果可读数据小于总数据长度,表示不够,还有数据没接收
if (readable < total) {
// 重置一下要处理的位置,并跳出循环
mByteBuffer.reset();
break;
}
// 回到头
mByteBuffer.reset();
// 拿到整个包
byte[] allPack = new byte[total];
mByteBuffer.get(allPack);
//生成异或字符串用于校验该应答数据是否有效
String myHex = HexTool.getInstance().getXOR(ByteUtil.bytes2HexStr(allPack, 0, total-1));
//获取串口应答过来的校验位内容
String reciveHex = ByteUtil.bytes2HexStr(allPack, total-1, 1);
// 校验通过
if (myHex.equalsIgnoreCase(reciveHex)) {
final byte[] data = new byte[cmdDataLen];//命令数据内容
System.arraycopy(allPack, 5, data, 0, data.length);//应答所有的数据
byte command = allPack[4];//当前命令码
// 收到有效数据
onReceiveValidData(allPack, data, command);
} else {
// 不一致则回到“第二位”,继续找到下一个3BB3
mByteBuffer.position(frameStart + 2);
}
}
// 最后清掉之前处理过的不合适的数据
mByteBuffer.compact();
}
5, 发送数据
就是根据协议 按照他的顺序和规则去发送命令
比如: "3B B3 00 01 A1 00 1F"
前面两个字节代表帧头, 第三个代表地址 ,第四个代表 命令数据长度 ,因为命令数据为00 所以是一位 ,即 地址为"01" A1代表的是命令码 ,1F代表校验位。
上面数据是虚拟的,具体要什么值可参考协议。
全部代码
public abstract class DataReceiver3BB3 implements ReceiveCallback {
private final ByteBuffer mByteBuffer;
public DataReceiver3BB3() {
mByteBuffer = ByteBuffer.allocate(1024);
mByteBuffer.clear();
}
/**
* 解析数据成功
* @param allPack 所有数据
* @param data 命令数据内容
* @param command 命令码
*/
public abstract void onReceiveValidData(byte[] allPack, byte[] data, byte command);
public void resetCache() {
mByteBuffer.clear();
}
/**
重点在这里
*/
@Override
public void onReceive(String devicePath, String baudrateString, byte[] received, int size) {
LogPlus.i("DataReceiver", "接收数据=" + ByteUtil.bytes2HexStr(received, 0, size));
mByteBuffer.put(received, 0, size);
mByteBuffer.flip();
byte b;
int readable;
while ((readable = mByteBuffer.remaining()) >= Protocol.MIN_PACK_LEN) {
mByteBuffer.mark(); // 标记一下开始的位置
int frameStart = mByteBuffer.position();
//校验帧头 开始==========
b = mByteBuffer.get();
if (b != Protocol.FRAME_HEAD_0) { // 第1个byte要3B
continue;
}
b = mByteBuffer.get();
if (b != Protocol.FRAME_HEAD_1) { // 第2个byte要B3
continue;
}
//校验帧头 结束==========
b = mByteBuffer.get();
if (!(b == Protocol.ADDRESS_1||b == Protocol.ADDRESS_2||b == Protocol.ADDRESS_3
|| b == Protocol.ADDRESS_4 || b == Protocol.ADDRESS_5)) { //校验地址
continue;
}
byte[] ba = new byte[]{mByteBuffer.get()};
// 数据长度
final int cmdDataLen =(int) ByteUtil.hexStr2decimal(ByteUtil.bytes2HexStr(ba));
// 总数据长度 = 不包含数据位的长度+刚动态获取的数据位长度,就是总长度 ,
// 看第三步的通信数据格式 , Protocol.PACK_LEN = 5 , cmdDataLen = received[3] 的值转成10进制后的值)
int total = Protocol.PACK_LEN + cmdDataLen;
// 如果可读数据小于总数据长度,表示不够,还有数据没接收
if (readable < total) {
// 重置一下要处理的位置,并跳出循环
mByteBuffer.reset();
break;
}
// 回到头
mByteBuffer.reset();
// 拿到整个包
byte[] allPack = new byte[total];
mByteBuffer.get(allPack);
//生成异或字符串用于校验该应答数据是否有效
String myHex = HexTool.getInstance().getXOR(ByteUtil.bytes2HexStr(allPack, 0, total-1));
//获取串口应答过来的校验位内容
String reciveHex = ByteUtil.bytes2HexStr(allPack, total-1, 1);
// 校验通过
if (myHex.equalsIgnoreCase(reciveHex)) {
final byte[] data = new byte[cmdDataLen];//命令数据内容
System.arraycopy(allPack, 5, data, 0, data.length);//应答所有的数据
byte command = allPack[4];//当前命令码
// 收到有效数据
onReceiveValidData(allPack, data, command);
} else {
// 不一致则回到“第二位”,继续找到下一个3BB3
mByteBuffer.position(frameStart + 2);
}
}
// 最后清掉之前处理过的不合适的数据
mByteBuffer.compact();
}
}