連通硬件
首先,要確保你能用機器指令連通硬件,連通工具windows下推薦sscom,mac下推薦RS232 Tool 他們都能發(fā)送十六進制數(shù)據(jù)且都是免費
這里講一下異步串口協(xié)議
一,232(三根線,雙方都是2線接3線反序接,再加一根地線,故名232協(xié)議),雙工短距,用途最廣泛,一般電腦調試硬件都是用這個,因為短距傳輸速率可以設成超高不用擔心丟幀問題
二,485 (分Rx+,Rx-接口,順序連接),半工長距,用于遠距離呼叫應答
三,422 (分Rx+,Rx-,Tx+,Tx-接口,反序連接),雙工長距,接線比較復雜
選好協(xié)議,裝好驅動,拿到硬件文檔,一般硬件文檔中會給協(xié)議的幀頭和幀體,幀尾一般用于校驗,一般用奇偶校驗或是crc冗余校驗,需要自己去計算,推薦一個網(wǎng)站www.23bei.com,這里可以根據(jù)通信協(xié)議自動生成校驗碼.另外單片機的數(shù)據(jù)位與軟件是高低位反序的,記得自己手工做好調整
自己組裝好硬件指令之后,根據(jù)與硬件協(xié)議好的波特率,校驗停止與數(shù)據(jù)位,就可以和硬件通信了.這里由于是232協(xié)議,近距離傳輸,就可以選擇超高速的230400波特率,每秒速度接近30M,硬件指令響應都是微秒級別的,沒有任何延遲.
配置serialport
serialport是node環(huán)境下唯一一個串口連接包,想要用node操控硬件,這個庫是必須要攻克的一道難關
npm install serialport --save
之后,我進入node_modules發(fā)現(xiàn)這個包是一堆cpp文件,作者為了提高效率,使用c++來操控硬件,我們首先要把這個包編譯成node能夠識別的.node二進制文件.
首先確保你的node版本在V8.0以上,phtyon為2.7版本(尤其不能用3以上的版本),具備基本的C++編譯環(huán)境(vs C++ 或者 Xcode 都可以)
我是在electron框架下編譯的,還要額外下載electron,electron-rebuild兩個包,然后使用electron-rebuild命令就可以編譯serialport了,npm下載electron-rebuild過程中可能會報404錯誤,這個不用管,因為作者已經不再維護了,所以如果你的系統(tǒng)比較新的話他會找不到對應的serialport二進制文件,我們下好自己編譯就行了.
如何封裝硬件指令
js中變量的大小都是8字節(jié)浮點數(shù),64bit,這個在TCP中問題不大,但是在硬件傳輸協(xié)議中,這么大的變量是在災難級的,所以我們參照c語言對js變量從新定義
封裝通信幀偽代碼:
//定義數(shù)據(jù)幀都為8位無符號整形, Uint8Array類似于set,內置方法也雷同
//幀長度為頭部(指令含義),幀體(指令內容),幀尾(CRC,檢測是否有傳輸錯誤),也可自己定義其他的通信協(xié)議
const crc = require('crc')
calcCRC() {
//引用crc包
const r = crc.crc16modbus(headFrame+ bodyFrame)
//>>為拋棄高8位,這是個反序操作
return Uint8Array.of(r >> 8, r)
}
let Frame = new Uint8Array(headFrame.length + bodyFrame.length+CRC.length)
Frame.set(headFrame)
//假設幀頭為2字節(jié)
Frame.set(bodyFrame, 2)
Frame.set(calcCRC(), headFrame.length + bodyFrame.length)
return Frame
檢測返回的數(shù)據(jù)幀同理,先判斷其長度,如果過短是丟幀,如果過長是緩存溢出,需要截取,如果長度正好合適就判斷其crc,如果吻合則沒有傳輸錯誤,解析幀體向上層傳遞
解析幀偽代碼:
parse() {
// length為約定的指令長度,由于約定crc+幀頭為四字節(jié),所以即使空包也要大于四
// data.length 為返回的指令長度,由于length可以使變長度指令,所以要判斷一下data.length
if ((length > 0 && data.length < length) ||data.length < 4) {
data = null
return PARSE_CODE.LACK_DATA
}
const err = parseBody()
// 過短提前返回
if (err === PARSE_CODE.LACK_DATA) {
data = null
return PARSE_CODE.LACK_DATA
}
this.head = (data[0] << 8) + data[1]
// 檢測crc
if (!this.checkCRC()) {
data = null
return PARSE_CODE.CRC_DISS
}
data = null
return err
}