Python LS산전 PLC통신
PLC 와 통신하기 위해서는 형식을 잘 갖춰서 요청을 해야한다.
아주 까다로운 녀석이라 조금만 틀려서도 안된다.
LS산전 PLC와 XGT 프로토콜 방식으로 소켓통신을 하려고 한다.
포트는 2004이다.
프레임 구조는 아래와 같다. 공식 메뉴얼에 다 나와있었다.
프레임 구조는 대충 읽어보면될것같고
개별읽기는 아래와 같다.
그렇다면 코드로 어떻게 구현하면 되는가?
전체코드는 아래와 같다.
import socket
# PLC 통신 정보
TCP_IP = '192.168.99.99'
TCP_PORT = 2004
BUFFER_SIZE = 1024
# 레인지 설정
xmin, xmax = 0, 16000 # 원시값 범위
ymin, ymax = 0, 500 # 조정 후 범위
# 레인지 변환 함수
def scale_value(x, x_min, x_max, y_min, y_max):
return ((x - x_min) / (x_max - x_min)) * (y_max - y_min) + y_min
# PLC 메시지 생성 (예: MW00300 주소 읽기)
message = (
b'LSIS-XGT' + b'\x00\x00' + #Company ID
b'\x00\x00' + #PLC Info
b'\xA4' + #CPU Info
b'\x33' + #Source of Frame
b'\x00\x00' + #Invoke ID
b'\x12\x00' + #Length
b'\x02' + #이더넷위치(베이스,슬롯)
b'\x00' + #예약 영역 ----------------------여기까지가 헤더
b'\x54\x00' + #명령어 코드
b'\x02\x00' + #데이터 형
b'\x00\x00' + #예약 영역
b'\x01\x00' + #블록수
b'\x08\x00' + #변수명 길이
'%MW00300'.encode('ascii') #변수명
)
# 소켓 통신
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send(message)
data = s.recv(BUFFER_SIZE)
s.close()
# 응답 처리
raw_value = data[-2:]
x_raw = int.from_bytes(raw_value, byteorder='little')
y_scaled = scale_value(x_raw, xmin, xmax, ymin, ymax)
# 출력
print(f"\n📥 x값(원시값): {x_raw} / 범위: {xmin} ~ {xmax}")
print(f"🎯 → y값(조정값): {y_scaled:.2f} / 범위: {ymin} ~ {ymax}")
우선 통신할떄
\x00 이런식으로 적는걸 리틀엔디안 또는 빅엔디안 라고 부른다.
일단은 리틀엔디안인것같음.
- 리틀엔디안
- 작은 값(하위 바이트) 먼저
- 0x1234 → b'\x34\x12'
- 0x0058 → b'\x58\x00'
이 한 덩어리(\xOO)당 1바이트이다.
예를들어 10진수 88를 표현해보면
88은 16진수로 58이다
\x58 로 표현하면 된다
근데 두 덩어리로 표현해야한다면
\x58\x00 이렇게 적어야한다.
리틀엔디안형식이기때문
30000(10진수) 를 리틀엔디안으로 표현해보면
30000은 16진수로 0x7530 이다
리틀엔디으로는
\x30\x75
뒤에서부터 두개씩 잘라서 앞에다 적어넣으면 된다.
헤더
1) company ID
company ID 는 10바이트 이다.
고로 한글자당 1바이트라고 쳤을때
LSIS_XGT는 8글자. 2글자=2바이트가 남는다
그래서 NULL NULL 로 2바이트를 채워넣어줘야한다.
그래서 b'LSIS-XGT' + b'\x00\x00 이렇게 적은것이다.
2) PLC Info
pc에서 plc로 요청을 보내는것이기 때문에 Don't care 즉, 0값을 보내면 된다.
그런데 2바이트를 채워서 넣어라고 했으므로 b'\x00\x00 (두덩어어리) 이렇게 보내면된다.
3) CPU Info
자신의 CPU 정보에 해당하는 값을 넣어주면된다.
4) Source of Frame
어디서 프레임을 보내는 것이냐
내컴퓨터에서 PLC로 보내는것이기때문에
0x33 즉 b'\x33'이다.
5) Invoke ID
요청 프레임이기때문에 이부분은 0으로 채워서 보낸다.
그리고 2바이트를 요구했으므로
b'\x00\x00'이다.
6) Length
이부분은 있다가 다시 설명하겠다.
7) 예약영역
예약영역이라 00을 보내준다
b'\x00'
명령 요구 프레임
이제 헤더는 끝났다. 이어서 그밑에 쭉 적으면 된다.
1) 명령어코드
읽기 요구 이니까 0054이다.
작성은 아래와 같이 작성한다.
b'\x54\x00'
두덩어리니까 2바이트다
2) 데이터 형
데이터형은 좀 순서가 바뀌어 표현되어있는것같아서 LS산전에다 문의해보니 메뉴얼 표기가 잘못되어있는거라고 확인함.
표에서 정상표기했다면 h' 0000 0001 0002 0003 0004 0014 이런식으로 표기했어야했다.
암튼
WORD 는 파이썬으로 코드작성시 'b\x02\x00' 이 맞다.
3) 예약 영역
2바이트 0으로 채워넣음 된다
b'\x00\x00'
4) 데이터 영역(블록수, 변수명길이, 변수명)
[ 변수명길이 변수명 ]
이게 한 블록이다.
그래서 한개만 요청할거기 때문에 블록수는 1로한다
2바이트로 넣어야하기때문에
b'\x01\x00' 로 표시
변수명의 길이는 변수명의 길이를 써주면된다.
변수명은 '%MW00100' 이것은 PLC 어드레스이다.
글자수는 세어보니 8글자이고 크기는 2바이트를 요구하니
변수명 길이는 b'\x08\x00' 으로 하면된다
변수명은 2바이트. 최대 16자까지 작성할수있다.
그리고 변수명은 아스키코드로 인코딩해줘야한다.
'%MD00211'.encode('ascii') 이런식으로 해주면된다.