Modbus(3) Modbus통신을 이용하여 python으로 데이터 받아오기
저번에 modbus 통신 패킷확인과 점검을 해보았는데 이번에는 pc로 받아올 수 있을지 해봐야겠다.
PC와 연결을 위해 아래 485모듈을 USB로 바꿔주는 컨버터를 이용했다. 얼마 안해서 바로 사용할 수 있었다.
처음에 pymodbus같은 모듈이 있다고 하는데 잘 안되서 다른 코드를 사용했다. 아래 코드에서는 minimalmodbus와 pyserial 모듈을 사용했다.
※ 주의해야 할 점 : import serial이지만 serial 모듈을 다운받으면 serial.Serial가 존재하지 않는다는 에러가 발생한다. 이 경우 serial모듈을 삭제하고 pyserial 모듈을 다시 다운해야한다.
import minimalmodbus as minimalmodbus
import serial
if __name__ == '__main__':
instrument = minimalmodbus.Instrument("COM12", 2,'rtu')
instrument.serial.baudrate = 9600 # Baud
instrument.serial.bytesize = 8
instrument.serial.parity = serial.PARITY_NONE
instrument.serial.stopbits = 1
instrument.serial.timeout = 1 # seconds
try:
print(instrument.read_register(2003, functioncode=int('0x03', 16)))
except IOError:
print("Failed to read from instrument")
위와 같이 코드를 구성해 포트번호, slave ID , 통신 방식을 맞추어 주었고, 통신속도, 통신길이, 패리티, 등을 설정값과 같이 설정해주었다.
아래 그림은 저번 시간에 썻던 메뉴얼의 일부이다. PV_1의 값을 읽어오기 위해 2003 번지로 적었다. 또한 레지스터에서 값을 읽어올것이기 때문에 03번 함수의 16진수로 사용했다.
그런데 다른 결과값인 5000 출력이 되었다..
코드에서 잘못된게 있는지 찾아보고 설정값을 찾아봤는데 틀린점은 없다. 통신이 실패되었다는 메세지가 안 떳기 때문에 그냥 무지성으로 주소값을 냅다 1부터 99까지 출력했다.. ㅋㅋㅋㅋㅋ
import minimalmodbus as minimalmodbus
import serial
if __name__ == '__main__':
instrument = minimalmodbus.Instrument("COM12", 2,'rtu')
instrument.serial.baudrate = 9600 # Baud
instrument.serial.bytesize = 8
instrument.serial.parity = serial.PARITY_NONE
instrument.serial.stopbits = 1
instrument.serial.timeout = 1 # seconds
for i in range(2000, 2100):
try:
print(instrument.read_register(i, functioncode=int('0x03', 16)))
except IOError:
print("Failed to read from instrument")
위와 같이 결과가 나왔는데 잘 보니까 2번째 있는 값이 현재 온도값과 같았다. PV2의값도 18번째 값이랑 같았다. 인터넷을 뒤져보니까 이게 프로그래머들은 보통 숫자시작을 1로하고 메뉴얼을 만드는 사람은 0으로 시작하는 사람도 있고 제조사마다 다르게 만들어서 이것때문에 생긴 차이같다. 항상 메뉴얼을 참고하자!
이제 이 값들을 배열에 담아보자. 1초마다 계속해서 값을 읽어와야 하기 때문에 schedule 모듈을 사용하여 매초마다 값을 읽어 오도록 했다.
이외에도 특정 시간마다 돌릴 수 있는 모듈은 apscheduler등 여러 방법들이 있다.
값을 읽어오는 코드를 함수(W_TEMPER1)로 만들어서 ' schedule.every(1).seconds.do(W_TEMPER1) '를 사용하여 만들었다.
import minimalmodbus
import serial
import schedule
import time
def WRITE_TEMP1():
instrument = minimalmodbus.Instrument("COM13", 2, 'rtu')
instrument.serial.baudrate = 9600 # Baud
instrument.serial.bytesize = 8
instrument.serial.parity = serial.PARITY_NONE
instrument.serial.stopbits = 1
instrument.serial.timeout = 1 # seconds
try:
crr_temp1 = instrument.read_register(2002, functioncode=int('0x03', 16))
print(f'Temperature1: {crr_temp1}', end='')
except IOError:
print("Failed to read from instrument")
def WRITE_TEMP2():
instrument = minimalmodbus.Instrument("COM13", 2, 'rtu')
instrument.serial.baudrate = 9600 # Baud
instrument.serial.bytesize = 8
instrument.serial.parity = serial.PARITY_NONE
instrument.serial.stopbits = 1
instrument.serial.timeout = 1 # seconds
try:
crr_temp2 = instrument.read_register(2018, functioncode=int('0x03', 16))
print(f'Temperature2: {crr_temp2}')
except IOError:
print("Failed to read from instrument")
# 1초마다 WRITE_TEMP 함수 실행
schedule.every(1).seconds.do(WRITE_TEMP1)
schedule.every(1).seconds.do(WRITE_TEMP2)
while True:
schedule.run_pending()
time.sleep(1)
위와 같이 정상적으로 온도값이 측정됨을 확인 할 수 있다.
◎ 현재 주피터 노트북을 사용해서 프로그램을 돌리고 있는데 schedule 패키지가 종료되지 않고 계속 함수를 실행하는 문제가 있다. 종료 조건을 넣어주지 않으면 커널을 종료할때까지 내부에서 프로그램이 계속 돌아가는거 같다. 그리고 셀이 계속 실행중이라 다음셀이 동작하지 않는다는 문제가 있다. 나중에 코드가 길어지면 pycham을 이용해서 프로그램을 짜는게 나을 것 같다.