Android硬件協(xié)議對(duì)接初識(shí)

往常做的都是普通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é)議的格式

image.png

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();
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市搜吧,隨后出現(xiàn)的幾起案子市俊,更是在濱河造成了極大的恐慌,老刑警劉巖滤奈,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摆昧,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蜒程,警方通過(guò)查閱死者的電腦和手機(jī)绅你,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)昭躺,“玉大人忌锯,你說(shuō)我怎么就攤上這事×祆牛” “怎么了汉规?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)驹吮。 經(jīng)常有香客問(wèn)我针史,道長(zhǎng),這世上最難降的妖魔是什么碟狞? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任啄枕,我火速辦了婚禮,結(jié)果婚禮上族沃,老公的妹妹穿的比我還像新娘频祝。我一直安慰自己泌参,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布常空。 她就那樣靜靜地躺著沽一,像睡著了一般。 火紅的嫁衣襯著肌膚如雪漓糙。 梳的紋絲不亂的頭發(fā)上铣缠,一...
    開(kāi)封第一講書(shū)人閱讀 50,050評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音昆禽,去河邊找鬼蝗蛙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛醉鳖,可吹牛的內(nèi)容都是我干的捡硅。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼盗棵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼壮韭!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起纹因,我...
    開(kāi)封第一講書(shū)人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤泰涂,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后辐怕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體逼蒙,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年寄疏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了是牢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡陕截,死狀恐怖驳棱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情农曲,我是刑警寧澤社搅,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站乳规,受9級(jí)特大地震影響形葬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜暮的,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一笙以、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧冻辩,春花似錦猖腕、人聲如沸拆祈。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)放坏。三九已至,卻和暖如春老玛,著一層夾襖步出監(jiān)牢的瞬間淤年,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工逻炊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人犁享。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓余素,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親炊昆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子桨吊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351