前言:
最近接觸了一個關於PLC工控的小項目,大概場景是,對方一個茶葉工廠。已經通過各種設備組成了自動化的工控系統。並且也讓我的一個朋友做了茶園監控和茶園天氣環境等的web頁面展示,但是還沒有工控設備的數據顯示。

需求:
工控設備已經連接到了一台作為上機位的電腦上,所以要獲取設備數據。需要在同一局域網上,通過modbus tcp請求對方已經開放的端口。拿到數據儲存到數據庫,最后web界面只用按時間順序獲取數據庫的數據。
因為訪問對方電腦需要他們提供授權,所以這里演示就以modbus的調試工具,以及后面PHP代碼請求示例。
工具:
1. Modbus Slave: 從機端模擬軟件,這里測試可以把他作為服務端,PHP為客戶端就是取該機子的數據。
2. Modbus Poll: 主機仿真器,用於測試和調試Modbus從設備,這里測試也只是把他當做客戶端使用。
3. ModScan32: 主機/從機模擬程序 ,以后介紹。
4. MThings: 一個國產免費軟件, 既可以模擬主機設備 又可以模擬從機設備,以后介紹。
以上軟件,可以掃描下面二維碼,輸入“modbus模擬” 獲取地址。
工具操作:
一. Modbus Slave
1. 創建TCP/IP連接。
(1). 點擊connection->connection,彈出參數窗口,可以按下面確認。

(2). 配置函數,點擊setup->slave definition,彈出參數窗口,默認OK就可以。

(3). 修改某項數據的值,雙擊對應的框,彈出后修改OK就可以。

2. 從機參數說明:
(1). ID, 機子的設備標識,是slave definition的slave ID
(2). F, 當前節點的函數碼,主機獲取代碼獲取設置數據,需要指定的函數。

3. 查看發送和接收數據明細。
(1). 點擊display,彈出面板。


4. 注意
modbus slave每次連接只能維持10分鍾,可能是沒有激活。
一. Modbus Poll
1. 通過Tcp獲取從機上的數據。
(1). 連接,點擊connection->connection, 選擇TCP/IP。
(2). 修改為slave機子對應的IP地址和端口,點擊保存。
(3). 連接成功后,查看讀寫定義,可以按指定slave配置修改。

(4). 連接失敗,Mbpoll面板會提示紅色字體。面板文字說明如下。
Tx = 4表示向主站發送數據幀次數,圖中為4次; Error = 0表示通訊錯誤次數,圖中為0次; ID = 1表示模擬的Modbus子設備的設備地址,圖中地址為1;F = 03表示所使用的Modbus功能碼,圖中為03功能碼; SR = 1000ms表示掃描周期。紅字部分,表示當前的錯誤狀態,“No Connection”表示未連接狀態。
(5). 查看讀寫數據。

PHP代碼演示:
1. modbus類庫包下載。
composer require adduc/phpmodbus

2. 編寫請求 "03 Read Holding Registers"函數示例代碼。
<?php /** * author: bqs * desc: 請求modbus地址 * 公眾號: ZERO開發 */ require_once 'vendor/adduc/phpmodbus/Phpmodbus/ModbusMaster.php'; // Modbus master UDP $modbus = new ModbusMaster("127.0.0.1", "TCP"); // Read multiple registers try { $recData = $modbus->readMultipleRegisters(1, 0, 5); } catch (Exception $e) { // Print error information if any echo $modbus; echo $e; exit; } var_dump($recData);die; // Print data in string format echo PhpType::bytes2string($recData); ?>
3. 環境要求。
1. PHP的LAMP環境已經搭建完畢 2. 可以不用配置虛擬域名,直接localhost訪問modubus_tcp_pro.php文件 3. PHP版本最好是5.5,因為7.0以上運行會對類的構造函數命名報錯 4. PHP5.5擴展開啟了php_sockets 5. 運行成功后,返回數據,數組的索引需要計算匹配modbus slave的地址名 6. 計算方式: (索引-1)/2
4. readMultipleRegisters說明。
參數1:unitId, modbus設備ID,參考slave的slave ID
參數2:reference, 地址號,在設備內存中,數據的地址引用,參考slave配置的地址
參數3:quantity,線圈,要去設備中讀取的數據量,參考slave配置的quantity
5. 請求異常的幾種情況。
(1). socket_connect() failed
slave的連接停止了,需要重新開啟。
(2). Modbus response error code: 2 (ILLEGAL DATA ADDRESS)

從機設備上數據的內容地址不對,可以根據slave的definition的參數,報錯可以查看ModbusMaster類的responseCode方法。
請求的quantity數超過slave定義的quantity數量也會報內容地址錯誤,請求只能小於定義的數量。
6. 關於返回的數組。

如果請求的是5個數據,phpmodbus會返回元素為10的數組。如果是2個,則返回4個元素數組,以此類推。
7. 關於返回數組與slave的數據塊地址數據對應的方式。

8. 獲取設備上指定數據塊的實際的數據。
(1). 枚舉某數據塊下索引對應的標識。
// 數據庫設備的數據描述 $devicesDataBlock = [ "0" => "weather", "1" => "water", "2" => "voice", "3" => "electric", "4" => "air" ];
(2). 根據返回數組的過濾出有用的索引,並匹配設備數據標識。
實際的數據塊索引 = (返回數組的索引-1)/2
前提處於2的不能有余數,所以只需要對結果做判斷,完整代碼如下。
<?php /** * author: bqs * desc: 請求modbus地址 * 公眾號: ZERO開發 */ require_once 'vendor/adduc/phpmodbus/Phpmodbus/ModbusMaster.php'; // Modbus master UDP $modbus = new ModbusMaster("127.0.0.1", "TCP"); // Read multiple registers try { $recData = $modbus->readMultipleRegisters(1, 0, 5); } catch (Exception $e) { // Print error information if any echo $modbus; echo $e; exit; } // 數據庫設備的數據描述 $devicesDataBlock = [ "0" => "weather", "1" => "water", "2" => "voice", "3" => "electric", "4" => "air" ]; $realData = []; foreach($recData as $key => $value) { $indexs = ($key-1)/2; if (($key-1)%2 == 0) { $realData[$devicesDataBlock[$indexs]] = $value; } } var_dump($realData);die; // Print data in string format echo PhpType::bytes2string($recData); // 00050000000601030000000A ?>


