Modbus 是一種通信協議,用于工業自動化領域中的設備之間的通信。它是一種串行通信協議,廣泛應用于連接不同設備、傳感器和執行器的工業控制系統。 Modbus 在工業控制系統、自動化設備、能源管理系統等領域得到廣泛應用。
Modbus 協議的基本特點:
通信方式: Modbus 支持串行通信和以太網通信。串行通信通常使用 RS-232、RS-485 或 RS-422 等標準。
協議類型: Modbus 協議有不同的變體,包括 Modbus RTU(二進制形式)和 Modbus ASCII(ASCII 文本形式)。此外,基于以太網的 Modbus TCP/IP 也是一種常見的變體。
數據幀: Modbus 通信使用數據幀(Frame)進行信息傳遞。幀包括地址、功能碼、數據和錯誤檢測字段。
功能碼: Modbus 定義了一系列功能碼,用于執行不同的操作。例如,讀取保持寄存器、寫入單個寄存器等。
寄存器: 數據在 Modbus 中以寄存器的形式存儲。有輸入寄存器、保持寄存器等不同類型。
主從結構: Modbus 通信通常涉及到主從結構。主設備(Master)發起通信請求,而從設備(Slave)響應請求。
數據幀
Modbus 通信使用數據幀(Frame)進行信息傳遞。幀包括地址、功能碼、數據和錯誤檢測字段。
網絡架構
每種類型的設備(PLC, HMI,控制面板,驅動程序,運動控制,I/O設備…)都可以使用MODBUS協議發起遠程操作。
同樣的通信可以在串行線上完成,也可以在以太網TCP/IP網絡上完成。網關允許使用MODBUS協議在幾種類型的總線或網絡之間進行通信
MODBUS協議定義了三個PDUs.
? MODBUS Request PDU, mb_req_pdu
? MODBUS Response PDU, mb_rsp_pdu
? MODBUS Exception Response PDU, mb_excep_rsp_pdu
Modbus主從通信
Modbus 通信通常涉及到一個主設備(Master)和一個或多個從設備(Slave)。以下是建立 Modbus 通信連接的一般步驟:
主設備(Master)的操作:
確定通信參數: 主設備需要確定與從設備通信時所需的參數,包括通信端口、波特率、奇偶校驗等。
選擇通信方式: 主設備可以選擇串行通信(RS-232、RS-485、RS-422等)或以太網通信(Modbus TCP/IP)。
創建 Modbus 請求幀: 主設備通過構建包含適當功能碼和數據的 Modbus 請求幀來發起通信請求。功能碼指示所需的操作,例如讀取寄存器、寫入寄存器等。
發送請求幀: 主設備通過選定的通信方式將請求幀發送到從設備。
等待響應: 主設備等待從設備的響應。響應幀包含所請求的數據或操作的結果。
解析響應: 主設備解析從設備發送的響應幀,提取所需的信息。
從設備(Slave)的操作:
接收請求幀: 從設備通過選定的通信方式接收主設備發送的請求幀。
解析請求: 從設備解析主設備發送的請求幀,確定所需執行的操作和相關參數。
執行操作: 從設備執行主設備請求的操作,例如讀取寄存器、寫入寄存器等。
創建響應幀: 從設備根據執行結果創建包含響應數據的 Modbus 響應幀。
發送響應幀: 從設備通過選定的通信方式將響應幀發送回主設備。
在通信過程中,主從設備都應該能夠處理可能的錯誤情況。這可能包括超時、通信中斷、校驗錯誤等。
modbus協議的主從通信
在 Linux 下使用 C/C++ 實現 Modbus 主從通信涉及到底層的串口或網絡通信編程,以及構建 Modbus 請求和響應幀的操作。以下是一個簡單的示例,使用串口通信(RS-485)實現 Modbus 主從通信,主設備讀取從設備的保持寄存器的例子.
安裝 Modbus 庫
sudo apt-get install libmodbus-dev
Modbus 從設備代碼
從設備代碼示例(modbus_slave.cpp)
#include <modbus/modbus.h>int main() {modbus_t *ctx;uint16_t holding_registers[2] = {0x1234, 0xABCD};// Create a new context with Modbus RTU over RS-485ctx = modbus_new_rtu("/dev/ttyUSB0", 9600, 'N', 8, 1);if (ctx == NULL) {fprintf(stderr, "Unable to create Modbus context\n");return -1;}// Set the Modbus device IDmodbus_set_slave(ctx, 1);// Start the Modbus server (listen for requests)modbus_mapping_t *mb_mapping = modbus_mapping_new(0, 0, 2, 0);modbus_mapping_set_base_address(mb_mapping, 0, 0);modbus_mapping_set_pointer(ctx, mb_mapping);while (1) {// Handle Modbus requestsmodbus_receive(ctx, mb_mapping);modbus_reply(ctx, mb_mapping, 0x01); // 0x01 is the Modbus function code for reading holding registers}// Clean upmodbus_mapping_free(mb_mapping);modbus_close(ctx);modbus_free(ctx);return 0;
}
Modbus 主設備代碼
主設備代碼示例(modbus_master.cpp)
#include <modbus/modbus.h>int main() {modbus_t *ctx;uint16_t holding_registers[2];// Create a new context with Modbus RTU over RS-485ctx = modbus_new_rtu("/dev/ttyUSB0", 9600, 'N', 8, 1);if (ctx == NULL) {fprintf(stderr, "Unable to create Modbus context\n");return -1;}// Set the Modbus device IDmodbus_set_slave(ctx, 1);// Connect to the Modbus serverif (modbus_connect(ctx) == -1) {fprintf(stderr, "Modbus connection failed: %s\n", modbus_strerror(errno));modbus_free(ctx);return -1;}// Read holding registers from the Modbus serverint num_registers = modbus_read_registers(ctx, 0, 2, holding_registers);if (num_registers == -1) {fprintf(stderr, "Modbus read failed: %s\n", modbus_strerror(errno));} else {printf("Holding Register 0: 0x%04X\n", holding_registers[0]);printf("Holding Register 1: 0x%04X\n", holding_registers[1]);}// Disconnect and clean upmodbus_close(ctx);modbus_free(ctx);return 0;
}
編譯和運行
# 編譯從設備代碼
g++ modbus_slave.cpp -o modbus_slave -lmodbus# 編譯主設備代碼
g++ modbus_master.cpp -o modbus_master -lmodbus# 運行從設備
./modbus_slave# 在另一個終端運行主設備
./modbus_master
實際中需要根據具體設備的 Modbus 協議文檔和通信方式進行適當的配置和編碼。同時,對于網絡通信,可以使用 Modbus TCP/IP,而不是串口通信。