Modbus là gì ?
Modbus là một giao thức truyền thông nối tiếp ban đầu được Schneider Electric xuất bản năm 1979 để sử dụng với các bộ điều khiển logic lập trình (PLC) của họ. Modbus nằm ở cấp độ 2 của mô hình OSI và sử dụng kiến trúc chính / phụ (hoặc máy khách-máy chủ). Đơn giản và mạnh mẽ, nó đã trở thành một giao thức truyền thông tiêu chuẩn thực tế, và giờ đây nó là một phương tiện phổ biến để kết nối các thiết bị điện tử công nghiệp. Giao thức truyền thông Modbus trình bày các tính năng sau:
- Modbus đã được phát triển với các ứng dụng công nghiệp nhiều năm
- Xuất bản công khai và miễn phí bản quyền
- Dễ dàng phát triển và bảo trì
Modbus cho phép giao tiếp giữa nhiều thiết bị được kết nối với cùng một mạng, ví dụ như một hệ thống đo nhiệt độ và độ ẩm và truyền kết quả đến máy tính. Nhiều loại dữ liệu được đặt tên từ việc sử dụng nó trong rơle lái xe: đầu ra vật lý một bit được gọi là cuộn dây và đầu vào vật lý một bit được gọi là đầu vào riêng biệt hoặc tiếp điểm.
Danh sách này bao gồm một số cách sử dụng phổ biến nhất của tiêu chuẩn Modbus
- Nhiều ứng dụng Master-Slave
- Cảm biến và dụng cụ
- Mạng công nghiệp
- Xây dựng và cơ sở hạ tầng
- Ứng dụng năng lượng và giao thông
A. Modbus Master và Modbus Slave
Trong giao tiếp modbus, trước tiên phải xác định Slave modbus và Slave modbus.
Xem hình ảnh dưới đây:
1. PC / Computer, CNC được định nghĩa là Modbus Master
2. PLC được định nghĩa là Modbus Slave
Modbus Master là một thiết bị Yêu cầu dữ liệu và ghi dữ liệu vào Slave Modbus.
Modbus Slave là một số thiết bị Phản hồi dữ liệu cho Modbus Master.
Một Modbus Master có thể được kết nối lên tới 247 Modbus Slaves.
Mỗi Slave Modbus có một Địa chỉ / ID duy nhất từ 1 đến 247.
Một số PLC có thể được sử dụng làm Slave Modbus: Modicon PLC, PLC PLC, Allen Bradley PLC và các loại khác.
Để thử nghiệm, tôi sử dụng PLC PLC S7-200.
B. Mã chức năng Modbus
Để giao tiếp giữa modbus master và modbus Slave sử dụng giao thức modbus, giao thức modbus có một số mã và chức năng của nó.
Danh sách dưới đây cho thấy các mã modbus được Modicon hỗ trợ:
Tôi sử dụng mã chức năng 03 và mã chức năng 16 để kiểm tra modbus và mã chức năng này được sử dụng phổ biến nhất nói chung. Đối với một mã chức năng khác, vui lòng thử nghiệm , tải xuống giao thức modbus hoàn chỉnh, nhấp vào đây
Mã chức năng 03 của ModBus
Mã chức năng 03 được sử dụng để đọc nội dung nhị phân của Sổ đăng ký trong phạm vi địa chỉ từ 40000 đến 49999 và thường được gọi là 4x.
Các tham số của Mã chức năng 03 / Đọc các thanh ghi giữ:
Yêu cầu / Truy vấn của Modbus Master:
Ví dụ: Tôi muốn đọc các thanh ghi từ địa chỉ bắt đầu 0 trong Slave modbus với địa chỉ 1 và số lượng thanh ghi (số điểm) đọc là 5 thanh ghi.
Mã yêu cầu là:
Hướng dẫn kết nối Modbus bằng Raspberry Pi
Bước 1: Bill of Material
- Raspberry Pi A+, B+, B2 or B3
- RS422/RS485 serial HAT
Bước 2: Đấu dây
Hệ thống dây điện rất đơn giản. Bạn chỉ phải kết nối các đầu A và B của HAT với dòng A và B của hệ thống Modbus. Thiết bị đầu cuối Y và Z không được sử dụng cho loại ứng dụng này. Đối với khoảng cách xa, nên sử dụng các cặp xoắn cho A và B.
Bước 3: Cài đặt công tắc DIP
Our RS422/RS485 HAT comes with 3 DIP switch banks. You have to set these DIP switches for Modbus as shown in the picture above. Switch 1: 1-OFF 2-ON 3-ON 4-OFF
Switch 2: 1-OFF 2-OFF 3-ON 4-ON
Switch 3: 1-OFF or ON* 2-OFF 3-OFF 4-OFF
*Depending of the position of the RS422/RS485 HAT in the Modbus line you have to switch the terminating resistor ON or OFF. Please switch the resistor to ON position only if the HAT is on one end of the bus line. In all other cases switch the terminating resistor OFF.
Bước 4: Giải phóng Line nối tiếp và kích hoạt UART (Raspbian Stretch & Buster)
Cách dễ nhất là sử dụng công cụ raspi-config để chuyển UART sang chân GPIO14 / 15.
- take a fresh Raspbian image
- sudo raspi-config
- goto ‘5 Interfacing Options’
- goto ‘P6 Serial’
- ‘Would you like a login shell to be accessible over serial?’ –> NO
- ‘Would you like the serial port hardware to be enabled?’ –> YES
- Finish raspi-config
- reboot the Raspberry Pi
Now you can access the UART via /dev/serial0
Bước 5: Cài đặt Software
Bạn sẽ tìm thấy một triển khai Modbus RTU và Modbus ASCII dễ sử dụng và đã thử nghiệm bằng ngôn ngữ Python trên:
Hướng dẫn Modbus RTU Giao tiếp giữa PLC và Raspberry Pi bằng Python
Giao tiếp Modbus trong các ứng dụng này sử dụng giao tiếp nối tiếp / RS232 và Modbus RTU.
Ứng dụng này sử dụng modbus đơn giản với lập trình Python trên Raspberry Pi và không sử dụng modbus từ bên thứ ba.
Đối với phần cứng được sử dụng trong ứng dụng này:
1. PLC
2. Raspberry Pi (Chỉ dành cho cài đặt, sử dụng: màn hình, bàn phím, chuột, bộ chuyển đổi, đĩa flash)
3. Cáp PLC nối tiếp
4.Mô-đun nối tiếp và
5. Đầu nối chéo. Đối với kết nối giữa PLC và Raspberry Pi, Cài đặt Raspberry Pi (Tự động đăng nhập, Cài đặt PySerial, Cài đặt chân UART, Tự động chạy Python sau khi khởi động):
http://program-plc.blogspot.com/2014/06/home-automation- with-raspberry-pi.html
Kết nối giao tiếp Modbus RTU giữa PLC và Raspberry Pi:
Python Code of Modbus Communication on Raspberry Pi:
import serial
import random
#Sub Program
#CRC Calculation
def CRCcal(msg):
#CRC (Cyclical Redundancy Check) Calculation
CRC = 0xFFFF
CRCHi = 0xFF
CRCLo = 0xFF
CRCLSB = 0x00
for i in range(0, len(msg)-2,+1):
CRC = (CRC ^ msg[i])
for j in range(0, 8):
CRCLSB = (CRC & 0x0001);
CRC = ((CRC >> 1) & 0x7FFF)
if (CRCLSB == 1):
CRC = (CRC ^ 0xA001)
CRCHi = ((CRC >> 8) & 0xFF)
CRCLo = (CRC & 0xFF)
return (CRCLo,CRCHi)
#CRC Valdation
def CRCvalid(resp):
CRC = CRCcal(resp)
if (CRC[0]==resp[len(resp)-2]) & (CRC[1]==resp[len(resp)-1]):return True
return False
#Modbus Function Code 16 = Preset Multiple Registers
def Func16Modbus(slave,start,values):
Slave_Address = slave
Function = 16
Starting_Address = start
NumberofRegisters = len(values)
Byte_Count = NumberofRegisters * 2
message = [0 for i in range(9 + 2 * NumberofRegisters)]
#index0 = Slave Address
message[0] = (Slave_Address & 0xFF)
#index1 = Function
message[1] = (Function & 0xFF)
#index2 = Starting Address Hi
message[2] = ((Starting_Address >> 8) & 0xFF)
#index3 = Starting Address Lo
message[3] = (Starting_Address & 0xFF)
#index4 = Number of Registers Hi
message[4] = ((NumberofRegisters >> 8) & 0xFF)
#index5 = Number of Registers Lo
message[5] = (NumberofRegisters & 0xFF)
#index6 = Byte Count
message[6] = (Byte_Count & 0xFF)
for i in range(0, NumberofRegisters):
#Data Hi, index7 and index9
message[7 + 2 * i] = ((values[i] >> 8) & 0xFF)
#Data Lo, index8 and index10
message[8 + 2 * i] = values[i] & 0xFF
#CRC (Cyclical Redundancy Check) Calculation
CRC = CRCcal(message)
#index11= CRC Lo
message[len(message) - 2] = CRC[0]#CRCLo
#index12 = CRC Hi
message[len(message) - 1] = CRC[1]#CRCHi
if ser.isOpen:
ser.write("".join(chr(h) for h in message))
reading = ser.read(8)
response = [0 for i in range(len(reading))]
for i in range(0, len(reading)):
response[i] = ord(reading[i])
if len(response)==8:
CRCok = CRCvalid(response)
if CRCok & (response[0]==slave) & (response[1]==Function):return True
return False
#Modbus Function Code 03 = Read Holding Registers
def Func03Modbus(slave,start,NumOfPoints):
#Function 3 request is always 8 bytes
message = [0 for i in range(8)]
Slave_Address = slave
Function = 3
Starting_Address = start
Number_of_Points = NumOfPoints
#index0 = Slave Address
message[0] = Slave_Address
#index1 = Function
message[1] = Function
#index2 = Starting Address Hi
message[2] = ((Starting_Address >> 8)& 0xFF)
#index3 = Starting Address Lo
message[3] = (Starting_Address& 0xFF)
#index4 = Number of Points Hi
message[4] = ((Number_of_Points >> 8)& 0xFF)
#index5 = Number of Points Lo
message[5] = (Number_of_Points& 0xFF)
#CRC (Cyclical Redundancy Check) Calculation
CRC = CRCcal(message)
#index6= CRC Lo
message[len(message) - 2] = CRC[0]#CRCLo
#index7 = CRC Hi
message[len(message) - 1] = CRC[1]#CRCHi
if ser.isOpen:
ser.write("".join(chr(h) for h in message))
responseFunc3total = 5 + 2 * Number_of_Points
reading = ser.read(responseFunc3total)
response = [0 for i in range(len(reading))]
for i in range(0, len(reading)):
response[i] = ord(reading[i])
if len(response)==responseFunc3total:
CRCok = CRCvalid(response)
if CRCok & (response[0]==slave) & (response[1]==Function):
#Byte Count in index 3 = responseFunc3[2]
#Number of Registers = byte count / 2 = responseFunc3[2] / 2
registers = ((response[2] / 2)& 0xFF)
values = [0 for i in range(registers)]
for i in range(0, len(values)):
#Data Hi and Registers1 from Index3
values[i] = response[2 * i + 3]
#Move to Hi
values[i] <<= 8
#Data Lo and Registers1 from Index4
values[i] += response[2 * i + 4]
negatif = values[i]>>15
if negatif==1:values[i]=values[i]*-1
return values
return ()
#Main Program
#Serial Port 9600,8,E,1
#Serial Open
try:
ser = serial.Serial(
port = '/dev/ttyAMA0',
baudrate = 9600,
bytesize = serial.EIGHTBITS,
parity = serial.PARITY_EVEN,
stopbits = serial.STOPBITS_ONE,
timeout = 0.2
)
except Exception, e:
raise ValueError(e)
print "START"
while 1:
#Serial Open Check
if not ser.isOpen:ser.open()
#Read of Registers
Func03ArrayValue = Func03Modbus(1,0,2);#slave,start,number of registers
if len(Func03ArrayValue)>0:
for i in range(0, len(Func03ArrayValue)):
print "Read of Registers" + str(i) + " = " + str(Func03ArrayValue[i])
#Fill Random Value for Write
totalvalue=2
val = [0 for i in range(totalvalue)]
for i in range(0, len(val)):
val[i] = random.randrange(-32767,32767) #Random Valiue from -32767 to max 32767
#Write of Registers
WriteValid = Func16Modbus(1,2,val)#slave,start,array value
if WriteValid:
for i in range(0, len(val)):
print "Write of Registers" + str(i) + " = " + str(val[i])
print "#################################"
Download and Testing:
1. Download project file for Python on Raspberry pi, click here
On Raspberry Pi: Copy the file plc-modbus.py into /home/pi
2. Download PLC Ladder Programming for upload to PLC, click here