Innovate With Data

데이터로 더 큰 가치를 제공합니다.

공장 자동화/통신

PLC 데이터 읽고 쓰기 (3) - Siemens S7 PLC (feat. python-snap7)

데이터위자드 2022. 1. 26. 22:56
반응형

PLC 데이터 읽고 쓰기 (3) - Siemens S7 PLC (feat. python-snap7)

PLC를 PC와 연결하여 데이터 읽고 쓰기 3편 - 지멘스 Siemens S7 PLC 연결

 

 안녕하세요? 데이터 위자드 시모입니다.

이번 시간에는 PLC 데이터 읽고 쓰기 3편 지멘스 Siemens S7 PLC에 Python 프로그램으로 PC와 연결하여 데이터를 읽고 쓰는 방법에 대해서 알아보겠습니다.

 

* PLC 데이터 읽고 쓰기 1/2편은 아래 링크를 참조하시기 바랍니다.

 

PLC 데이터 읽고 쓰기 (1) - 미쓰비시 MELSEC PLC (Python pymcprotocol)

 

PLC 데이터 읽고 쓰기 (1) - 미쓰비시 MELSEC PLC (Python pymcprotocol)

PLC를 PC와 연결하여 데이터를 수집하는 방법  안녕하세요? 데이터 위자드 시모입니다. 이번 포스팅에서는 생산현장에서 사용하는 PLC에 PC를 연결하여 데이터를 읽고 쓰거나, 데이터 수집을 하는

datawizard.co.kr

 PLC 데이터 읽고 쓰기 (2) - Rockwell A-B PLC (feat. pylogix)

 

PLC 데이터 읽고 쓰기 (2) - Rockwell A-B PLC (feat. pylogix)

PLC를 PC와 연결하여 데이터 읽고 쓰기 2편 - Rockwell Automation ControlLogix PLC 연결  안녕하세요? 데이터 위자드 시모입니다. 지난 시간에 알아본 PC와 미쯔비시 MELSEC PLC를 python 프로그램을 이용해..

datawizard.co.kr

 

앞서 1편과 2편에서 PC와 PLC 연결에 대한 내용을 이미 설명한 부분이 있으니, 거두절미하고 본론으로 바로 넘어가겠습니다. (관련 내용은 이전 포스팅 내용을 참조하여 주시기 바랍니다.)

 

* 본 포스팅에서 언급된 Python 패키지와 프로그램 예제는 본 포스팅 내용을 부연 설명하기 위해 예시로 든 것이며 연결하고자 하는 PLC나 디바이스와의 호환성 및 적합성 등은 사용자가 반드시 확인해야 합니다. 부적절하게 사용하여 발생되는 오류나 부주의로 발생되는 문제는 사용자 본인의 책임이며, 언급된 모듈이나 PLC 제조사는 필자와 무관함을 알립니다.

 

이미 OPC UA Server/Client가 잘 나와 있는데 지원하는데 굳이 직접 개발할 필요가 있을까?

 

 최근 OPC UA server 및 Client들이 PLC 제조사와 더불어 Third party에 다양하게 출시되어 있습니다. 물론, 제조 현장에 자원이 충분하다면 잘 만들어진 기성품을 가져다 쓰는 것이 완성도 측면이나 투입되는 시간과 노력을 절감하는 측면에서 더 나은 결과를 얻을 수 있을 것입니다.

 

 하지만, 마른 수건도 짜야하는 환경에서 최소한의 비용으로 극소수의 Critical equipment에 적용하고 한다면???

 

당연히 엔지니어의 수고가 동반되어야 할 것입니다. 충분히 성능과 효과성을 검증 후에 라이선스와 인프라 도입을 검토하는 것도 방법이라 하겠습니다.

 

때론 마른 수건도 쥐어짜야할 때도 있는 법입니다. @Pixabay

 

 잡설이 길어졌는데요. 넘어가 파이썬 모듈의 설치와 지멘스 PLC와 연결하는 예제를 알아보도록 하겠습니다.

 

Python python-snap7 설치하고 PLC와 PC를 이더넷으로 연결합니다.

 

먼저 PLC와 PC를 연결합니다.

 

 

  1. 다이렉트로 LAN 케이블을 이용하여 연결하거나 유무선 공유기를 이용하여 연결합니다.

  2. 미리 PLC의 IP 주소와 PLC 설정을 확인합니다.

 

다음으로 PC에서 python-snap7 모듈을 다운로드하여 설치합니다.


Python 관련 내용이 궁금하신 분들은 이전 포스팅 '맨땅에 Python' 내용을 참조하여 주시기 바랍니다.

 

https://attack.tistory.com/category/%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D%28feat.%20%ED%8C%8C%EC%9D%B4%EC%8D%AC%29/%EB%A7%A8%EB%95%85%EC%97%90%20Python

 

'머신러닝(feat. 파이썬)/맨땅에 Python' 카테고리의 글 목록

현장 자동화 라인의 공정개선 / 유지보수 / 프로젝트 관리 업무에 도움을 드립니다. 📧 simo@datawizard.co.kr

attack.tistory.com

 

 

python-snap7 패키지에 대한 자세한 설명은 링크된 pypi 홈페이지를 확인하시기 바랍니다.

 

https://pypi.org/project/python-snap7/

 

python-snap7

Python wrapper for the snap7 library

pypi.org

 

이제 엔지니어가 필요한 프로그램을 코딩합니다.

 

 패키지 설치까지 완료하셨다면 이제 파이썬 IDE를 여시고 프로그램을 코딩하시면 되겠습니다.

 

해당 패키지 제작자가 공유한 '멀티 변수 읽고 쓰기'에 대한 내용을 아래에 첨부하오니, 참조하시어 원하는 기능을 작성하시면 되겠습니다.

 

* PLC Multi 변수 읽기 예제

 

"""
Example ussage of the read_multi_vars function
This was tested against a S7-319 CPU
"""

import ctypes

import snap7
from snap7.common import check_error
from snap7.types import S7DataItem, S7AreaDB, S7WLByte

client = snap7.client.Client()
client.connect('10.100.5.2', 0, 2)

data_items = (S7DataItem * 3)()

data_items[0].Area = ctypes.c_int32(S7AreaDB)
data_items[0].WordLen = ctypes.c_int32(S7WLByte)
data_items[0].Result = ctypes.c_int32(0)
data_items[0].DBNumber = ctypes.c_int32(200)
data_items[0].Start = ctypes.c_int32(16)
data_items[0].Amount = ctypes.c_int32(4)  # reading a REAL, 4 bytes

data_items[1].Area = ctypes.c_int32(S7AreaDB)
data_items[1].WordLen = ctypes.c_int32(S7WLByte)
data_items[1].Result = ctypes.c_int32(0)
data_items[1].DBNumber = ctypes.c_int32(200)
data_items[1].Start = ctypes.c_int32(12)
data_items[1].Amount = ctypes.c_int32(4)  # reading a REAL, 4 bytes

data_items[2].Area = ctypes.c_int32(S7AreaDB)
data_items[2].WordLen = ctypes.c_int32(S7WLByte)
data_items[2].Result = ctypes.c_int32(0)
data_items[2].DBNumber = ctypes.c_int32(200)
data_items[2].Start = ctypes.c_int32(2)
data_items[2].Amount = ctypes.c_int32(2)  # reading an INT, 2 bytes

# create buffers to receive the data
# use the Amount attribute on each item to size the buffer
for di in data_items:
    # create the buffer
    buffer = ctypes.create_string_buffer(di.Amount)

    # cast the pointer to the buffer to the required type
    pBuffer = ctypes.cast(ctypes.pointer(buffer),
                          ctypes.POINTER(ctypes.c_uint8))
    di.pData = pBuffer

result, data_items = client.read_multi_vars(data_items)

for di in data_items:
    check_error(di.Result)

result_values = []
# function to cast bytes to match data_types[] above
byte_to_value = [util.get_real, util.get_real, util.get_int]

# unpack and test the result of each read
for i in range(0, len(data_items)):
    btv = byte_to_value[i]
    di = data_items[i]
    value = btv(di.pData, 0)
    result_values.append(value)
print(result_values)

client.disconnect()
client.destroy()

 

 

* PLC Multi 변수 쓰기 예제

 

import ctypes
import snap7
from snap7.types import S7WLByte, S7DataItem, S7WLWord, S7WLReal, S7WLTimer
from snap7.types import areas, wordlen_to_ctypes
from snap7.util import set_int, set_real, set_word, get_int, get_real, get_s5time


client = snap7.client.Client()
client.connect('192.168.100.100', 0, 2)

items = []


def set_data_item(area, word_len, db_number: int, start: int, amount: int, data: bytearray) -> S7DataItem:
    item = S7DataItem()
    item.Area = ctypes.c_int32(area)
    item.WordLen = ctypes.c_int32(word_len)
    item.DBNumber = ctypes.c_int32(db_number)
    item.Start = ctypes.c_int32(start)
    item.Amount = ctypes.c_int32(amount)
    array_class = ctypes.c_uint8 * len(data)
    cdata = array_class.from_buffer_copy(data)
    item.pData = ctypes.cast(cdata, ctypes.POINTER(array_class)).contents
    return item


int_values = [10, 20, 30, 40]
ints = bytearray(len(int_values) * 2)
for i, value in enumerate(int_values):
    set_int(ints, i * 2, value)

real = bytearray(4)
set_real(real, 0, 42.5)

counters = 0x2999.to_bytes(2, 'big') + 0x1111.to_bytes(2, 'big')

item1 = set_data_item(area=areas.DB, word_len=S7WLWord, db_number=1, start=0, amount=4, data=ints)
item2 = set_data_item(area=areas.DB, word_len=S7WLReal, db_number=1, start=8, amount=1, data=real)
item3 = set_data_item(area=areas.TM, word_len=S7WLTimer, db_number=0, start=2, amount=2, data=counters)

items.append(item1)
items.append(item2)
items.append(item3)

client.write_multi_vars(items)

db_int = client.db_read(1, 0, 8)
db_real = client.db_read(1, 8, 12)
db_counters = client.ct_read(2, 2)

print(f'int values: {[get_int(db_int, i * 2) for i in range(4)]}')
print(f'real value: {get_real(db_real, 0)}')
print(f'counters: {get_s5time(counters, 0)}, {get_s5time(counters, 2)}')

 

관련해서 문의사항은 댓글이나 메일로 남겨 주시면 도움드릴 수 있도록 하겠습니다. 😀

 

마치며,

 

 이번 포스팅에서는 python-snap7을 이용해서 지멘스 S7 PLC와 PC를 연결하고 PC 측에서 데이터를 가져오고 쓰는 예제에 대해서 알아봤습니다.

 

 물론, 이러한 과정이 제조사에서 제공하는 프로그램이나 상용 OPC UA server 프로그램보다 좋다고 할 수는 없을 것이지만, 오늘 소개한 내용을 가지고 현업에서 어떠한 상황을 해결하기 위한 또 하나의 방법이 될 수 있기 때문에 알아두면 도움이 될 것이라 생각됩니다. (엔지니어 본인이 직접 입맛에 맞게 프로그램을 수정할 수도 있겠네요.)

 

 다소 난해한 부분이 많을 것이라 예상됩니다. 메일이나 댓글로 문의하시면 도움드릴 수 있도록 하겠습니다.

 

생각보다 쉽진 않습니다. 하지만 활용은 무궁무진합니다.

 

* 본 포스팅에서 언급된 Python 패키지와 프로그램 예제는 본 포스팅 내용을 부연 설명하기 위해 예시로 든 것이며 연결하고자 하는 PLC나 디바이스와의 호환성 및 적합성 등은 사용자가 반드시 확인해야 합니다. 부적절하게 사용하여 발생되는 오류나 부주의로 발생되는 문제는 사용자 본인의 책임이며, 언급된 모듈이나 제조사와 필자는 무관함을 알립니다.

 

 

'💗' 도움이 되셨다면 공감 클릭 부탁드립니다. 감사합니다.

반응형