在之前的億佰特技術(shù)文章中,我們簡單介紹了利用JavaScript(ECMAScript 5)腳本解析Alink JSON實(shí)現(xiàn)Modbus協(xié)議采集RTU設(shè)備,本文將利用“功能定義”通過“READ_R”的不同“枚舉值”實(shí)現(xiàn)多臺(tái)Modbus從機(jī)設(shè)備的寄存器采集。
這次我們使用真實(shí)的RTU進(jìn)行開發(fā),一臺(tái)MA01-AXCX4040用于采集DI上傳,一臺(tái)GPS設(shè)備上傳NMEA0183協(xié)議的RMC定位字符串?dāng)?shù)據(jù)。
手動(dòng)發(fā)送數(shù)據(jù)過于麻煩,若采用云平臺(tái)腳本自動(dòng)發(fā)送對(duì)于PLC工程師比較陌生,這里小編將結(jié)合“MQTT模式”與串口服務(wù)器的“主動(dòng)上報(bào)網(wǎng)關(guān)”實(shí)現(xiàn)物模型的數(shù)據(jù)自動(dòng)更新。
一臺(tái)串口服務(wù)器,用于連接MQTT服務(wù)器和主動(dòng)輪詢RTU設(shè)備并上報(bào)服務(wù)器;
一臺(tái)MA01-AXCX4040遠(yuǎn)程IO聯(lián)網(wǎng)模塊(標(biāo)準(zhǔn)Modbus RTU設(shè)備),響應(yīng)主機(jī)請求(上報(bào)DI狀態(tài));
表1 MA01-AXCX4040部分寄存器描述
功能描述 | 寄存器類型 | 寄存器 | 數(shù)量 | 支持功能碼 |
干接點(diǎn)采集 | 離散量輸入 | 0x0000 | 4 | 0x02 |
開關(guān)量輸出 | 線圈 | 0x0000 | 4 | 0x01、0x05、0x0F |
Modbus地址 | 保持寄存器 | 0x07e8 | 1 | 0x03、0x06、0x10 |
一臺(tái)支持Modbus RTU協(xié)議的GPS采集模塊,通過讀取保持寄存器值顯示當(dāng)前定位信息(字節(jié)序:大端,字序:大端)共計(jì)70字節(jié)字符串;
表2 GPS定位模塊部分寄存器描述
功能描述 | 寄存器類型 | 寄存器 | 數(shù)量 | 支持功能碼 |
RMC數(shù)據(jù) | 保持寄存器 | 0x0005 | 35 (70字節(jié)大端字符) | 0x02 |
Modbus地址 | 保持寄存器 | 0x0002 | 1 | 0x03、0x06、0x10 |
保證設(shè)備供電與正確接入互聯(lián)網(wǎng),將采用阿里云MQTT服務(wù)器實(shí)現(xiàn)遠(yuǎn)程采集控制,因此無法在局域網(wǎng)中實(shí)現(xiàn)該功能。
參考之前的文章《利用云平臺(tái)腳本解析連接物模型(一)》,這里我就不再說明如何配置。
這里我就利用上次創(chuàng)建的設(shè)備修改“JavaScript腳本”和“功能定義”實(shí)現(xiàn)本文功能,不在額外的創(chuàng)建新設(shè)備。
修改原有的“讀取寄存器”配置,增加枚舉描述和枚舉值,這個(gè)值會(huì)在腳本中使用并且會(huì)作為指令的地址位使用,不可隨意定義。
添加ME31-AXCX4040相關(guān)功能定義“開關(guān)量采集第一路”,以類似方法可以定義其余三路開關(guān)量采集與開關(guān)量輸出反饋;
添加GPS相關(guān)功能定義“GPS定位數(shù)據(jù)”,使用字符串,長度限制70字節(jié)(可以不限制);
筆者僅學(xué)習(xí)過C語言,對(duì)于腳本語言并不了解,所有使用腳本函數(shù)均是修改阿里云教程和在W3SCHOOL中查詢得到,若有更簡單的字符串解析函數(shù),以你為準(zhǔn)。我這只是提供一種方法,不一定為最優(yōu)解。
/*此函數(shù)將設(shè)備上報(bào)數(shù)據(jù)轉(zhuǎn)換為Alink JSON物模型數(shù)據(jù)。*/
function rawDataToProtocol(bytes) {
? ?/*將設(shè)備上報(bào)的原始數(shù)據(jù)轉(zhuǎn)換為數(shù)組。其中bytes對(duì)象中存儲(chǔ)著設(shè)備上報(bào)原始數(shù)據(jù)。*/
? ?var uint8Array = new Uint8Array(bytes.length);
? ?for (var i = 0; i < bytes.length; i++) {
? ? ? ?uint8Array[i] = bytes[i] & 0xff;
? ?}
? ?var params = {}; ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 定義屬性存放對(duì)象。
? ?var jsonMap = { }; ? ? ? ? ? ? ? ? ? ? ? ? ? // 定義模擬Alink數(shù)據(jù)報(bào)對(duì)象。
? ?/*填寫Alink數(shù)據(jù)報(bào)協(xié)議頭部分。*/
? ?jsonMap['version'] = ALINK_VERSION; ? ? ? ? // Alink 協(xié)議版本號(hào)。
? ?jsonMap['id'] = ALINK_ID; ? ? ? ? ? ? ? ? ? // 消息ID。
? ?jsonMap['method'] = ALINK_PROP_POST_METHOD; // 設(shè)備上行數(shù)據(jù)方法:設(shè)備屬性上報(bào)。
? ?/*填寫Alink數(shù)據(jù)報(bào)屬性部分。*/
? ?//ME31-AXCX4040:01020100
? ?//GPS:020346(70字節(jié)數(shù)據(jù):$GNRMC,072905.00,A,3640.46260,N,11707.54950,E,000.0,000.0,050119,OK*20)2字節(jié)CRC
? ?if(uint8Array[0]==0x01){
? ? ? ?switch(uint8Array[3]){
? ? ? ? ? ?case 0x00: params['DI1']=0;break;
? ? ? ? ? ?case 0x01: params['DI1']=1;break;
? ? ? ?}
? ?}else if(uint8Array[0]==0x02){
? ? ? ?var rmc_str="";
? ? ? ?var count;
? ? ? ?for(count=3;count
//從最后插入數(shù)字轉(zhuǎn)字符,累計(jì)處理70次,得到GPS-RMC字符串
? ? ? ? ? ?rmc_str=rmc_str.concat(String.fromCharCode(uint8Array[count]));
? ? ? ?}
? ? ? ?params['GPS_RMC']=rmc_str;
? ?}
? ?jsonMap['params'] = params; ? ? ? ? ? ? ? ? // 將參數(shù)打包到數(shù)據(jù)幀中。
? ?return jsonMap; ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 返回結(jié)果會(huì)發(fā)送給物聯(lián)網(wǎng)平臺(tái)。
}
根據(jù)Modbus地址編碼平臺(tái)下發(fā)指令,如下所示:
/*此函數(shù)實(shí)現(xiàn)由物聯(lián)網(wǎng)平臺(tái)下發(fā)數(shù)據(jù)轉(zhuǎn)換為設(shè)備能識(shí)別的16進(jìn)制數(shù)。*/
function protocolToRawData(json)
{
? ?var method = json['method'];
? ?var id = json['id'];
? ?var version = json['version'];
? ?var payloadArray = [];
? ?if (method == ALINK_PROP_SET_METHOD) ? ?// 接收來自物聯(lián)網(wǎng)平臺(tái)的“設(shè)置設(shè)備屬性”的命令。
? ?{
? ? ? ?var send_params = json['params'];
? ? ? ?var prop_cur = send_params['READ_R']; ? // 將設(shè)置的具體值抽取出來。
? ? ? ?//按照自定義協(xié)議格式拼接rawdata。
? ? ? ?payloadArray = payloadArray.concat(buffer_uint8(prop_cur));
? ? ? ?if(prop_cur == 1){
? ? ? ? ? ?//添加查詢ME31-AXCX4040第一路指令
? ? ? ? ? ?payloadArray = payloadArray.concat(buffer_uint8(0x02));
? ? ? ? ? ?payloadArray = payloadArray.concat(buffer_uint8(0x00));
? ? ? ? ? ?payloadArray = payloadArray.concat(buffer_uint8(0x00));
? ? ? ? ? ?payloadArray = payloadArray.concat(buffer_uint8(0x00));
? ? ? ? ? ?payloadArray = payloadArray.concat(buffer_uint8(0x01));
? ? ? ? ? ?payloadArray = payloadArray.concat(buffer_uint8(0xB9));
? ? ? ? ? ?payloadArray = payloadArray.concat(buffer_uint8(0xCA));
? ? ? ?}else if(prop_cur == 2){
? ? ? ? ? ?//添加查詢GPS-RMC字符串
? ? ? ? ? ?payloadArray = payloadArray.concat(buffer_uint8(0x03));
? ? ? ? ? ?payloadArray = payloadArray.concat(buffer_uint8(0x00));
? ? ? ? ? ?payloadArray = payloadArray.concat(buffer_uint8(0x05));
? ? ? ? ? ?payloadArray = payloadArray.concat(buffer_uint8(0x00));
? ? ? ? ? ?payloadArray = payloadArray.concat(buffer_uint8(0x23));
? ? ? ? ? ?payloadArray = payloadArray.concat(buffer_uint8(0x14));
? ? ? ? ? ?payloadArray = payloadArray.concat(buffer_uint8(0x21));
? ? ? ?}
? ?}
? ?return payloadArray; ? ?// 返回時(shí),將數(shù)據(jù)發(fā)送至設(shè)備端。
}
ME31-AXAX4040上報(bào)數(shù)據(jù)解析;
GPS上報(bào)數(shù)據(jù)解析,以123456為例(實(shí)際數(shù)據(jù)為70字節(jié));
物模型下發(fā)讀取ME31-AXAX4040第一路DI狀態(tài)指令;
物模型下發(fā)讀取GPS數(shù)據(jù)指令;
在調(diào)試使用工具先測試功能是否正常,若不經(jīng)過此步驟直接連接設(shè)備,若通訊失敗難以分析問題導(dǎo)致無法實(shí)現(xiàn)功能。
配置連接參數(shù),參考《利用云平臺(tái)腳本解析連接物模型(一)》;
在腳本中我們指定了不同Modbus地址連接不同的設(shè)備,比如指定
MA01-AXCX4040使用地址1,指定GPS定位模塊使用地址2,在腳本解析中利用地址不同執(zhí)行不同的解析函數(shù),從而將數(shù)據(jù)保存到正確變量下。
Modbus協(xié)議的響應(yīng)幀只有地址碼和功能碼并不返回請求的寄存器地址導(dǎo)致設(shè)備無法調(diào)用正確的解析函數(shù),如果要實(shí)現(xiàn)同一設(shè)備地址的不同寄存器地址采集則需要在請求時(shí)記錄發(fā)送的寄存器地址,因此實(shí)現(xiàn)同一設(shè)備不同寄存器請求時(shí)需要采用與Modbus機(jī)制相同的采集機(jī)制(主從模式,主機(jī)請求從機(jī)應(yīng)答,一請一答),這個(gè)問題留到下篇文章中處理,現(xiàn)在只考慮每個(gè)地址讀取一個(gè)連續(xù)寄存器的情況。
軟件地址配置(出廠默認(rèn)為1):
硬件地址(全部撥向背離數(shù)字一側(cè),出廠默認(rèn)為31,需要修改為1):
查詢Modbus寄存器表可以知道保持地址的寄存器位(0x0002),通過Modbus指令配置Modbus地址為2,如下圖所示;
可通過串口服務(wù)器MQTT模式連接阿里云物模型,并通過平臺(tái)手動(dòng)請求RTU設(shè)備返回?cái)?shù)據(jù),串口服務(wù)器配置如下:
云平臺(tái)通過“在線調(diào)試”手動(dòng)發(fā)送查詢DI指令;
云平臺(tái)通過“在線調(diào)試”手動(dòng)發(fā)送查詢GPS指令;
可以利用串口服務(wù)器的“Modbus網(wǎng)關(guān)”主動(dòng)上報(bào)功能實(shí)現(xiàn),周期更新數(shù)據(jù),Modbus RTU輪詢間隔免費(fèi)用戶不建議小于1000ms,輪詢間隔指的是每條指令的間隔,例如這里配置的間隔為10s,平臺(tái)將以20s的周期刷新平臺(tái)數(shù)據(jù);