MODBUS

Python으로 Modbus 통신

기하 2023. 1. 31. 11:58

https://theprismdata.medium.com/python%EC%9C%BC%EB%A1%9C-modbus-%ED%86%B5%EC%8B%A0-8faae3351be8

 

Python으로 Modbus 통신

Python을 이용하여 Serial Interface를 이용해 공장 설비를 직접 제어, 모니터가 가능하다. 아직 많은 FA개발자들은 주로 VB, VC를 쓰겠지만, MS계열 운영체제에 최적화되어 Linux 유사 OS나 MacOS같은 시스템

theprismdata.medium.com

 

Python을 이용하여 Serial Interface를 이용해 공장 설비를 직접 제어, 모니터가 가능하다.

아직 많은 FA개발자들은 주로 VB, VC를 쓰겠지만,

MS계열 운영체제에 최적화되어 Linux 유사 OS나 MacOS같은 시스템에서는 동작하지 않는다.

 

이러한 운영체제 의존성을 벗어나고,

설비 제어 프로그램을 넘어서 데이터 분석이나 AI기술도 적용하기에는

사실상 Python이 가장 적합한 듯 하다.

 

Python언어에 대해서는 다른 문서를 찾아보는 것을 권장하는 바이고,

일단 Serial Interface에 접근하는 방법 중 하나는

minimalmodbus 라이브러리를 사용하는 것이다.

 

내가 연결한 제어기는

아래의 Autonics사의 TK4S모델로 온도 제어기이며,

센서는 T타입 온도 센서를 연결하였고,

제어기와 컴퓨터와의 연결에 사용한 RS485 변환기는

Coms사의 USB TO 485/422 컨버터이다.

 

컨버터는 윈도우에서는 어지간하면 대부분 연결되고,

맥북에서는 조금 다를 수 있을 거라 생각해서 지원하는 운영체제를 보고 선택하였다.

아래의 Python 코드는 USB Serial 컨버터에서 현재 센서 값을 읽어온다.

import minimalmodbus as minimalmodbus
import serial

if __name__ == '__main__':
    instrument = minimalmodbus.Instrument("/dev/tty.usbserial-AQ00WOQH", 1,'rtu')
    instrument.serial.baudrate = 19200  # Baud
    instrument.serial.bytesize = 8
    instrument.serial.parity = serial.PARITY_NONE
    instrument.serial.stopbits = 2
    instrument.serial.timeout = 1  # seconds

    try:
        print(instrument.read_register(1000, 0, functioncode=int('0x04', 16)))
    except IOError:
        print("Failed to read from instrument")

 

코드를 살펴보자.

 

minimalmodbus.Instrument(“/dev/tty.usbserial-AQ00WOQH”, 1,’rtu’)에서

 

첫번째 /dev/tty.usbserial-AQ00WOQH는 현재 맥북에 연결된 USB 포트 이름을 의미한다.

만약 윈도우라면 COM1, COM2,..형식으로 연결될 것이다.

 

두번째 1 은 제어기 자체의 ID이다.

RS485에 연결된 제어기는 1,2,3의 정수 값 형태로 자체 ID를 가지며,

서로 다른 제어기가 동일한 ID를 가질 수 없으며,

각 제어기를 ID로 구분하여 데이터를 주소 받는다.

 

세번째 rtu는 데이터 타입으로 rtu와 ASCII를 사용할 수 있다.

ASCII는 문자형태로 명령 및 데이터를 주고 받으나

rtu는 바이너리 바이스 통신으로 데이터를 주고 받는다.

 

제어기에 대한 설정이 끝났으면, 일반 직렬 통신에 대한 파라미터이다.

 

baudrate = 19200 는 통신 속도를 의미한다.

현재 설정은 1초에 19200 비트를 읽는다는 설정인데

보통 9600, 19200, 38400, 57600, 115200이 많이 쓰인다.

 

나머지 bytesize, parity, stop bits, timeout은

직렬 통신 설정 값으로 위의 baudrate를 제외하면 바뀌지 않는다.

 

다음 가장 중요한 것은 제어기 내부의 현재 상태 값이다.

제어기는 센서 현재 값(PV), 목표 값(SV) 및 PID제어를 위한 다양한 파라미터를 메모리에 가지고 있다.

여기서 다른 값 들도 읽을 수 있으나,

우선은 간단하게 현재 값을 가져오기 위해

제어기의 메모리 위치 1000에 데이터 읽기 함수 코드 0x04로 설정하여

현재 값을 수신하며 이 함수는 읽기와 쓰기가 가능하다.

 

참고로 읽기 전용 영역은 0x03으로 설정한다.

만약 목표 값을 읽거나 설정하려 싶다면, 해당 메모리 번지에 읽기/쓰기 코드를 설정하면 된다.

참고로 예제에 있는 주소는 TK Series와 다르게 보일 것이다.

아마도 라이브러리에서 16진수 값을 변환하여 전송하는 듯 하나,

값을 정확하게 가져오는 것을 보면 틀린 주소는 아닌 듯 하다.

 

이때 참고 할 것은 메모리 위치는 제어기가 다르므로,

반드시 제작사에서 제공하는 개발자용 매뉴얼을 참조해야 한다.

이와 같이 설정하고 코드를 수행하면, 아래와 같이 현재의 온도 값을 가져올 수 있다.

 

다음은 제조사에서 제공하는 메모리 맵 값을 16진수로 바꾸어 전송한 것이다.

이에 실험적으로 제조사에 매뉴얼 따른 메모리 주소를 16진수로 바꾸어 전 저송하였다.

결과는 성공인 듯하다.

 

Autonics 사에서 제공하는 매모리 맵

 

주소를 16진수로 바꾸었고, 뒤에 0은 소수점 자리수 설정이다.

import minimalmodbus as minimalmodbus
import serial

if __name__ == '__main__':
    instrument = minimalmodbus.Instrument("/dev/tty.usbserial-AQ00WOQH", 1,'rtu')
    instrument.serial.baudrate = 19200  # Baud
    instrument.serial.bytesize = 8
    instrument.serial.parity = serial.PARITY_NONE
    instrument.serial.stopbits = 2
    instrument.serial.timeout = 1  # seconds

    try:
        print(instrument.read_register(0x03E8, 0,  functioncode=int('0x04', 16)))
    except IOError:
        print("Failed to read from instrument")

 

제어 계측 분야에서는 주소 표현에 있어 10진수보다 16진수를 더 많이 이용하니 이 방법이 더 쉬울 듯 하다.