Python/PySerial, PyModbus

PySerial을 이용한 RS485, modbus-RTU 통신

Juhyuck 2023. 4. 13. 17:47
728x90

1. 모드버스

Modbus는 산업용 자동화 및 프로세스 제어 시스템에서 널리 사용되는 통신 프로토콜이다. 1979년 Modicon에서 개발되었으며, 간단하고 안정적인 구조로 인해 여러 장비 및 시스템 간 통신에 이상적이다. Modbus 프로토콜은 주로 마스터/슬레이브 구조로 동작하며, 마스터는 데이터 요청을 보내고 슬레이브는 응답을 제공한다.

Modbus 프로토콜에는 두 가지 주요 변형이 있다.

 

  1. Modbus RTU (Remote Terminal Unit): 이진 형식의 프레임을 사용하는 시리얼 통신 프로토콜로, 주로 RS-232 또는 RS-485 인터페이스를 통해 통신
  2. Modbus TCP/IP: 이더넷 기반의 통신 프로토콜로, Modbus RTU의 데이터 패킷을 TCP/IP 패킷으로 캡슐화하여 이더넷 네트워크를 통해 전송

 

Modbus 프로토콜은 다양한 데이터 유형을 지원한다:

  1. Coils: 이진 출력 값으로, 읽기 및 쓰기가 가능
  2. Discrete Inputs: 이진 입력 값으로, 읽기 전용
  3. Holding Registers: 16비트 레지스터 값으로, 읽기 및 쓰기가 가능
  4. Input Registers: 16비트 레지스터 값으로, 읽기 전용

기능 코드를 사용하여 이러한 데이터 유형에 액세스하고, Modbus 클라이언트와 서버 간에 데이터를 교환한다. 간단하고 견고한 특성으로 인해 Modbus는 산업용 자동화, 빌딩 자동화, 스마트 그리드, 원격 모니터링 등 다양한 분야에서 사용되고 있다.

 

PySerial 이용해서 RS485 인터페이스로 Modbus통신 해보기

import serial
import crcmod

# Set up the serial communication with the Modbus device
ser = serial.Serial(port='COM5', baudrate=9600, parity='N', stopbits=1, bytesize=8)

# Initiate the Modbus RTU request message to read holding register 0x0000
tx = b'\x01\x03\x00\x00\x00\x01' # Slave ID (1), Function code (3), Starting address (0x0000), Quantity of registers (1)

# Calculate the CRC16 checksum of the message
crc16 = crcmod.predefined.Crc('modbus')
crc16.update(tx)
crc_bytes = crc16.digest()

# Reverse the byte order of the CRC16 bytes (Modbus uses little-endian byte order)
crc_bytes_reversed = crc_bytes[::-1]

# Append the CRC16 checksum to the Modbus RTU request message
tx += crc_bytes_reversed

# Send the Modbus RTU request message to the slave device
ser.write(tx)

# Wait for a response from the slave device
res = ser.read(7) # Modbus RTU response messages are always 7 bytes long (including the slave ID and function code)

# Print the response message (which should contain the requested data)
print(res)

import struct

# Unpack the response message
slave_id, function_code, data_bytes, crc = struct.unpack('>BBBHB', res)

# Print the extracted values
print("Slave ID: {}".format(slave_id))
print("Function code: {}".format(function_code))
print("Data bytes: {}".format(data_bytes))
print("CRC: {}".format(crc))

struct 모듈을 불러와 받은 값을 unpack 할 수 있는데, 이건 통신대상 기기의 매뉴얼에 있는 프로토콜에 따라서 달라질 수 있다. 예를 들어, Modbus RTU 프로토콜에서는 바이트 순서가 빅 엔디안(big-endian) 방식을 사용한다. 따라서 struct.unpack() 함수에서도 빅 엔디안 순서를 사용하도록 지정해야 한다.

 

또한, Modbus RTU 프로토콜에서는 정수 데이터를 16비트 크기로 전송하며, 부호 있는 정수(short)와 부호 없는 정수(unsigned short)를 구분하여 처리해야 한다. 이러한 프로토콜 규격을 고려하여 형식 문자열을 작성해야 한다.

따라서 통신하는 기기의 메뉴얼이나 프로토콜 규격을 참고하여 struct.unpack() 함수에서 사용하는 형식 문자열을 수정해야 한다. 메뉴얼이나 규격에 따라서 사용하는 바이트 순서나 데이터 형식이 다를 수 있기 때문에, 이를 고려하여 형식 문자열을 작성해야 한다.