往常做的都是普通APP,沒(méi)有物聯(lián)網(wǎng)這個(gè)概念,所以對(duì)二進(jìn)制 扫步,十六進(jìn)制的理解少之又少,看到這些數(shù)值也很懵逼匈子。
進(jìn)制參考:https://www.cnblogs.com/wslook/p/9385415.html
異或河胎,與等算法參考:https://blog.csdn.net/xiaopihaierletian/article/details/78162863
首先要向硬件開(kāi)發(fā)者索要 波特率 串口地址 協(xié)議規(guī)則 是否有心跳 等 相關(guān)內(nèi)容,每種協(xié)議的校驗(yàn)虎敦,包括每個(gè)位置的數(shù)據(jù)都代表不同的意思游岳,一并都是16進(jìn)制的數(shù)值進(jìn)行通信。(我們的通信是已經(jīng)封裝好了其徙,具體內(nèi)容我也不清楚)
這里我只說(shuō)一些注意事項(xiàng)胚迫,以及校驗(yàn)等相關(guān)認(rèn)知 (我也是模模糊糊的)
1.遇到問(wèn)題之一:
將所有的串口地址都試了一遍,沒(méi)有一個(gè)有回應(yīng)的(其實(shí)心跳的話,很好試,只要打開(kāi)基本上就會(huì)有心跳過(guò)來(lái))
解決方案: 將硬件丟給硬件開(kāi)發(fā)者調(diào)試,應(yīng)該是線路沒(méi)接好或者其他的原因唾那,反正我不知道访锻,后來(lái)再給我的時(shí)候就可以了。
2.遇到問(wèn)題之二:
發(fā)起命令有應(yīng)答了,但是硬件調(diào)試發(fā)現(xiàn)硬件有應(yīng)答期犬,而接入到安卓設(shè)備河哑,安卓設(shè)備收到應(yīng)答的概率是30%。
起初懷疑安卓設(shè)備有問(wèn)題龟虎,返修璃谨。良久后還是不對(duì),查看自己的代碼 鲤妥,重點(diǎn)來(lái)了 : 因?yàn)槲业谝淮螌?duì)接協(xié)議佳吞,不知道他的命令是不可以多條同時(shí)發(fā)送的,一并發(fā)了七八條棉安。
然后我改了自己的代碼容达,逐條發(fā)送,應(yīng)答后再繼續(xù)下一步垂券。
解決方案:將安卓設(shè)備的插口換了一個(gè)花盐,就可以了,地址為“/dev/ttyS3” 之前用的是“/dev/ttyS2” 菇爪,其實(shí)兩個(gè)接口效果應(yīng)該是一樣的算芯,都是232 。當(dāng)然不知道什么原因凳宙,反正這個(gè)就是可以了熙揍。
3.協(xié)議說(shuō)明
其中一只協(xié)議的格式
1,幀頭是固定的不變的
2氏涩,地址是可變的届囚,可能不同的地址代表不一樣的功能
3,數(shù)據(jù)長(zhǎng)度代表的是 后面數(shù)據(jù)內(nèi)容的長(zhǎng)度(有些可能不一樣是尖,數(shù)據(jù)長(zhǎng)度可能會(huì)代表命令碼+數(shù)據(jù)內(nèi)容的總長(zhǎng)度) 意系。 例如:當(dāng)數(shù)據(jù)長(zhǎng)度 = (命令碼+數(shù)據(jù)內(nèi)容) 的長(zhǎng)度 , 數(shù)據(jù)內(nèi)容是 [0000], 命令碼是 [A1] 那這個(gè)時(shí)候 , 命令長(zhǎng)度就是03 饺汹。
4蛔添,命令碼,就是不同的命令代表不同的含義 兜辞,一般協(xié)議里面會(huì)寫(xiě)清楚 迎瞧,什么操作用什么命令碼,帶什么命令數(shù)據(jù)
5逸吵,數(shù)據(jù)內(nèi)容凶硅,配合命令碼帶入數(shù)據(jù),協(xié)議上一般都會(huì)寫(xiě)的扫皱。
7足绅,校驗(yàn)位 压语,很多種方式,異或校驗(yàn)啊编检,cr16校驗(yàn)等。反正這里傳的都是16進(jìn)制的數(shù)據(jù)
4扰才,解析硬件應(yīng)答過(guò)來(lái)的數(shù)據(jù)
ReceiveCallbacksh.java 是已經(jīng)封裝過(guò)的類(lèi)了,只知道這個(gè)方法就可以了
onReceive(String devicePath, String baudrateString, byte[] received, int size)
devicePath 代表串口地址允懂,baudrateString 代表波特率 received 收到的字節(jié)數(shù)據(jù),size收到數(shù)據(jù)的長(zhǎng)度
主要代碼:
@Override
public void onReceive(String devicePath, String baudrateString, byte[] received, int size) {
LogPlus.i("DataReceiver", "接收數(shù)據(jù)=" + 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(); // 標(biāo)記一下開(kāi)始的位置
int frameStart = mByteBuffer.position();
//校驗(yàn)幀頭 開(kāi)始==========
b = mByteBuffer.get();
if (b != Protocol.FRAME_HEAD_0) { // 第1個(gè)byte要3B
continue;
}
b = mByteBuffer.get();
if (b != Protocol.FRAME_HEAD_1) { // 第2個(gè)byte要B3
continue;
}
//校驗(yàn)幀頭 結(jié)束==========
b = mByteBuffer.get();
if (!(b == Protocol.ADDRESS_1||b == Protocol.ADDRESS_2||b == Protocol.ADDRESS_3
|| b == Protocol.ADDRESS_4 || b == Protocol.ADDRESS_5)) { //校驗(yàn)地址
continue;
}
byte[] ba = new byte[]{mByteBuffer.get()};
// 數(shù)據(jù)長(zhǎng)度
final int cmdDataLen =(int) ByteUtil.hexStr2decimal(ByteUtil.bytes2HexStr(ba));
// 總數(shù)據(jù)長(zhǎng)度 = 不包含數(shù)據(jù)位的長(zhǎng)度+剛動(dòng)態(tài)獲取的數(shù)據(jù)位長(zhǎng)度,就是總長(zhǎng)度 ,
// 看第三步的通信數(shù)據(jù)格式 , Protocol.PACK_LEN = 5 , cmdDataLen = received[3] 的值轉(zhuǎn)成10進(jìn)制后的值)
int total = Protocol.PACK_LEN + cmdDataLen;
// 如果可讀數(shù)據(jù)小于總數(shù)據(jù)長(zhǎng)度衩匣,表示不夠,還有數(shù)據(jù)沒(méi)接收
if (readable < total) {
// 重置一下要處理的位置,并跳出循環(huán)
mByteBuffer.reset();
break;
}
// 回到頭
mByteBuffer.reset();
// 拿到整個(gè)包
byte[] allPack = new byte[total];
mByteBuffer.get(allPack);
//生成異或字符串用于校驗(yàn)該應(yīng)答數(shù)據(jù)是否有效
String myHex = HexTool.getInstance().getXOR(ByteUtil.bytes2HexStr(allPack, 0, total-1));
//獲取串口應(yīng)答過(guò)來(lái)的校驗(yàn)位內(nèi)容
String reciveHex = ByteUtil.bytes2HexStr(allPack, total-1, 1);
// 校驗(yàn)通過(guò)
if (myHex.equalsIgnoreCase(reciveHex)) {
final byte[] data = new byte[cmdDataLen];//命令數(shù)據(jù)內(nèi)容
System.arraycopy(allPack, 5, data, 0, data.length);//應(yīng)答所有的數(shù)據(jù)
byte command = allPack[4];//當(dāng)前命令碼
// 收到有效數(shù)據(jù)
onReceiveValidData(allPack, data, command);
} else {
// 不一致則回到“第二位”蕾总,繼續(xù)找到下一個(gè)3BB3
mByteBuffer.position(frameStart + 2);
}
}
// 最后清掉之前處理過(guò)的不合適的數(shù)據(jù)
mByteBuffer.compact();
}
5, 發(fā)送數(shù)據(jù)
就是根據(jù)協(xié)議 按照他的順序和規(guī)則去發(fā)送命令
比如: "3B B3 00 01 A1 00 1F"
前面兩個(gè)字節(jié)代表幀頭, 第三個(gè)代表地址 ,第四個(gè)代表 命令數(shù)據(jù)長(zhǎng)度 ,因?yàn)槊顢?shù)據(jù)為00 所以是一位 ,即 地址為"01" A1代表的是命令碼 ,1F代表校驗(yàn)位琅捏。
上面數(shù)據(jù)是虛擬的生百,具體要什么值可參考協(xié)議。
全部代碼
public abstract class DataReceiver3BB3 implements ReceiveCallback {
private final ByteBuffer mByteBuffer;
public DataReceiver3BB3() {
mByteBuffer = ByteBuffer.allocate(1024);
mByteBuffer.clear();
}
/**
* 解析數(shù)據(jù)成功
* @param allPack 所有數(shù)據(jù)
* @param data 命令數(shù)據(jù)內(nèi)容
* @param command 命令碼
*/
public abstract void onReceiveValidData(byte[] allPack, byte[] data, byte command);
public void resetCache() {
mByteBuffer.clear();
}
/**
重點(diǎn)在這里
*/
@Override
public void onReceive(String devicePath, String baudrateString, byte[] received, int size) {
LogPlus.i("DataReceiver", "接收數(shù)據(jù)=" + 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(); // 標(biāo)記一下開(kāi)始的位置
int frameStart = mByteBuffer.position();
//校驗(yàn)幀頭 開(kāi)始==========
b = mByteBuffer.get();
if (b != Protocol.FRAME_HEAD_0) { // 第1個(gè)byte要3B
continue;
}
b = mByteBuffer.get();
if (b != Protocol.FRAME_HEAD_1) { // 第2個(gè)byte要B3
continue;
}
//校驗(yàn)幀頭 結(jié)束==========
b = mByteBuffer.get();
if (!(b == Protocol.ADDRESS_1||b == Protocol.ADDRESS_2||b == Protocol.ADDRESS_3
|| b == Protocol.ADDRESS_4 || b == Protocol.ADDRESS_5)) { //校驗(yàn)地址
continue;
}
byte[] ba = new byte[]{mByteBuffer.get()};
// 數(shù)據(jù)長(zhǎng)度
final int cmdDataLen =(int) ByteUtil.hexStr2decimal(ByteUtil.bytes2HexStr(ba));
// 總數(shù)據(jù)長(zhǎng)度 = 不包含數(shù)據(jù)位的長(zhǎng)度+剛動(dòng)態(tài)獲取的數(shù)據(jù)位長(zhǎng)度,就是總長(zhǎng)度 ,
// 看第三步的通信數(shù)據(jù)格式 , Protocol.PACK_LEN = 5 , cmdDataLen = received[3] 的值轉(zhuǎn)成10進(jìn)制后的值)
int total = Protocol.PACK_LEN + cmdDataLen;
// 如果可讀數(shù)據(jù)小于總數(shù)據(jù)長(zhǎng)度柄延,表示不夠,還有數(shù)據(jù)沒(méi)接收
if (readable < total) {
// 重置一下要處理的位置,并跳出循環(huán)
mByteBuffer.reset();
break;
}
// 回到頭
mByteBuffer.reset();
// 拿到整個(gè)包
byte[] allPack = new byte[total];
mByteBuffer.get(allPack);
//生成異或字符串用于校驗(yàn)該應(yīng)答數(shù)據(jù)是否有效
String myHex = HexTool.getInstance().getXOR(ByteUtil.bytes2HexStr(allPack, 0, total-1));
//獲取串口應(yīng)答過(guò)來(lái)的校驗(yàn)位內(nèi)容
String reciveHex = ByteUtil.bytes2HexStr(allPack, total-1, 1);
// 校驗(yàn)通過(guò)
if (myHex.equalsIgnoreCase(reciveHex)) {
final byte[] data = new byte[cmdDataLen];//命令數(shù)據(jù)內(nèi)容
System.arraycopy(allPack, 5, data, 0, data.length);//應(yīng)答所有的數(shù)據(jù)
byte command = allPack[4];//當(dāng)前命令碼
// 收到有效數(shù)據(jù)
onReceiveValidData(allPack, data, command);
} else {
// 不一致則回到“第二位”蚀浆,繼續(xù)找到下一個(gè)3BB3
mByteBuffer.position(frameStart + 2);
}
}
// 最后清掉之前處理過(guò)的不合適的數(shù)據(jù)
mByteBuffer.compact();
}
}