브라질 쪽 채널에서 회로 시뮬레이터 영상을 봤습니다. PT100을 변환기에 물리고 0-10V로 뽑는 걸 화면에서 그대로 보여 주더군요. 그걸 보고 든 생각은 하나였습니다. 우리 GO 모듈형 IO로도 저게 되면, 제품을 아직 못 써 본 사람에게 동작을 보여줄 수 있겠다. 제어는 센브릭스(Senbrix) PLC가 맡고, 모듈이 그 IO를 담당합니다.

출시 전 홍보도 되고, 실물 없이 결선과 프로그램을 미리 시험해 볼 수도 있습니다.

센브릭스 시뮬레이터 첫 화면. 아직 아무것도 배포하지 않아 모듈 네 종만 레일에 서 있다

이름은 센브릭스 시뮬레이터(SENBRIX Simulator)로 정했습니다. 센브릭스 에디터에서 배포하는 대상이니 그 이름을 따랐습니다.

흉내 내는 대신 컨트롤러를 통째로 넣었습니다

처음에 떠오른 방법은 웹에서 도는 시뮬레이터였습니다. 브라우저에 모듈을 그리고, IF/THEN 규칙을 몇 개 넣어 동작을 흉내 내는 것. 실제로 그렇게 시작했고 잘 굴러갔습니다.

문제는 그게 진짜가 아니라는 것입니다. 화면 안의 규칙은 우리가 만든 흉내이고, 실제 컨트롤러에서 도는 것은 에디터가 컴파일한 프로그램입니다. 둘이 조금씩 어긋나기 시작하면 시뮬레이터를 믿을 수 없게 됩니다. “시뮬에서는 됐는데요” 만큼 쓸모없는 말이 없습니다.

그래서 방향을 바꿨습니다.

에디터가 배포한 컴파일된 프로그램        ← 진짜
배포된 보드 드라이버                     ← 진짜
  보드와 주고받는 통로 (실물은 CAN 선)   ← 여기만 가상
시뮬레이터 작업면 (센서·스위치·부하·배선)

컨트롤러 런타임을 통째로 시뮬레이터 안에 넣었습니다. 보드 드라이버도 실물이 쓰는 것을 그대로 씁니다. 바꾼 것은 딱 한 군데, 실물에서 CAN 선이 앉던 자리에 가상 버스를 끼운 것뿐입니다.

그래서 배포된 프로그램은 자기가 시뮬레이터 안인지 모릅니다. 스캔 주기도, 보드가 프레임을 주고받는 방식도 실물과 같습니다. 흉내 낼 것이 없으니 어긋날 자리도 없습니다.

배포하면 화면이 알아서 결선됩니다

여기서부터가 재미있는 부분입니다.

에디터에서 프로젝트를 만들면 그 프로젝트는 어떤 보드를 쓰는지 이미 알고 있습니다. 배포물 안에 다 적혀 있으니까요. 그러면 시뮬레이터가 그걸 읽어서 레일을 꾸미면 됩니다.

에디터 연결 창을 열면 목록에 SenbrixSim 이 라즈베리파이와 나란히 뜹니다. 평소 실물에 배포하듯 그대로 누르면 됩니다. 그리고 나면

  • 그 프로그램이 쓰는 모듈이 화면의 레일에 붙습니다. IO 모듈을 두 장 쓰면 두 장이 붙습니다
  • 채널마다 기기가 놓이고 배선까지 깔립니다
  • 입력 채널에는 신호를 만들어 줄 것(스위치·센서)이, 출력 채널에는 결과를 보여 줄 것(램프·계측기)이 붙습니다

프로그램이 실제로 쓰는 채널만 꾸밉니다. 코드에서 직접 만지는 채널과 주소를 묶어 쓰는 채널을 구분해서, 쓰지도 않는 단자에 기기를 달지 않습니다.

배포된 프로그램대로 결선된 작업면. 위는 레일에 선 GO 모듈형 IO 네 장, 아래는 스위치·센서·표시등·계측기

배포 직후의 작업면입니다. 접점 입력 세 개에 스위치, 접점 출력에 표시등, 아날로그 입력에 센서, 아날로그 출력에 계측기가 놓이고 선이 이어졌습니다. 전원과 접지는 선을 긋지 않습니다 — 그 이유는 아래에 있습니다.

인터뷰에서 적은 이름이 그대로 옵니다

에디터는 프로젝트를 만들 때 무엇을 만들 건지 묻습니다. 그 답에 이름이 들어 있습니다 — 시작 버튼, 안전 도어, 공정 온도 같은 것들.

그 이름이 시뮬레이터까지 옵니다.

보드 단자주소이름
CB08 IN0P0시작 버튼
CB08 IN1P1안전 도어
CB08 IN2P2자재 감지
AD04 AI0D0공정 온도 (℃)

화면에 놓인 스위치 이름표가 “접점 스위치” 가 아니라 “시작 버튼” 입니다. ℃ 로 답한 채널의 센서는 ℃ 로 값을 내보냅니다. 결선을 설명할 때 이 차이가 큽니다.

공통선은 그리지 않습니다

처음에는 전원·접지·COM 을 전부 선으로 그렸습니다. 정직하긴 한데 화면이 못 쓰게 됐습니다. 공통선이 작업면을 가득 채우고 그 사이에 신호선이 묻혀서, 무엇이 어디로 가는지 읽을 수가 없었습니다.

실제 배선도가 접지를 기호로만 찍는 이유가 이겁니다.

그래서 단자마다 공통 레일을 두었습니다. 같은 레일에 물린 단자끼리는 선을 긋지 않아도 이어진 것으로 봅니다. 단자 아래에 작은 접지 기호만 찍힙니다.

그리고 그 레일을 사용자가 고를 수 있게 했습니다. 이게 생각보다 중요했습니다.

스위치는 닫는다고 켜지지 않습니다

무전원 접점 스위치를 생각해 봅시다. 한쪽 다리를 24V 에 물리면 닫았을 때 24V 를 통과시킵니다. 0V 에 물리면 닫아도 아무것도 흘리지 않습니다.

한쪽이 어디에 물렸는지가 곧 무엇이 흐르는지입니다. 그리고 이건 실제 결선에서 가장 자주 틀리는 자리이기도 합니다.

그래서 시뮬레이터도 그렇게 동작하게 했습니다. 스위치는 공급 쪽에 실제로 걸린 전압을 통과시킵니다. 검사창에 이렇게 나옵니다.

공통 연결
  1        V+
  2        직접 배선

on        ON
supply    24
passing   ON

supply 가 0 이면 닫아도 passing 은 OFF 입니다. 결선이 틀렸다는 게 화면에 그대로 드러납니다 — 이게 시뮬레이터가 해야 할 일이라고 생각합니다.

스위치 세 개를 켠 화면. 검사창에 on·supply·passing 이 나오고, 출력에 물린 표시등이 켜져 있다

시작 버튼·안전 도어·자재 감지를 차례로 켠 화면입니다. 오른쪽 검사창의 supply 24 가 스위치 한쪽에 실제로 걸린 전압이고, 그래서 passing 이 ON 입니다. PLC 프로그램이 응답한 출력이 표시등에 들어와 있습니다.

직접 결선할 때

배포가 다 해 주지만, 직접 꾸미고 싶을 때도 있습니다. 왼쪽에서 모듈과 기기를 골라 놓고, 단자를 두 번 눌러 이으면 됩니다. 이을 수 없는 조합은 거절합니다.

선이 많아지면 서로 겹치고 부품 위를 지나갑니다. 그래서 선을 잡아 옮길 수 있게 했습니다. 한 가닥은 세 부분입니다.

잡은 구간움직임
양쪽 세로 구간좌우로 비켜난다
가운데 가로 구간위아래로 차선을 바꾼다

세로선은 옆으로밖에 갈 곳이 없고 가로선은 위아래로밖에 갈 곳이 없습니다. 커서 모양이 누르기 전에 어느 쪽인지 알려 줍니다. 양쪽 세로 구간은 따로 놉니다 — 한 값으로 묶어 놨더니 한쪽 겹침을 풀면 반대쪽이 딸려 나왔습니다.

작업면은 휠로 확대하고 바탕을 끌어 이동합니다. 예전에는 화면 폭에 맞춰 통째로 줄였는데, 그러면 기기를 놓을수록 전부 작아집니다. 배선하라고 만든 화면인데 배선할수록 못 하게 되는 구조였습니다.

돌려서 놓고, 이름 붙여 저장합니다

RS485 장비를 넣으면서 배선 길이 하나 더 필요해졌습니다. 그 장비는 메인 모듈 위에 두는 게 자연스러운데, 배선 차선을 레일 아래에만 내 두었던 터라 위에 놓으면 선이 아래로 내려갔다 돌아 올라오는 이상한 길을 탔습니다. 위에도 차선을 내고, 내친김에 모듈과 기기를 돌려서 놓을 수 있게 했습니다. 단자가 어느 쪽을 보느냐에 따라 선이 나가는 방향이 달라지니, 배치를 바꿀 자유가 생깁니다.

돌리니까 이번엔 이름표가 같이 뒤집혔습니다. 180도 돌린 기기의 이름을 거꾸로 읽고 있는 걸 보고 글자만 다시 바로 세웠습니다. 이런 건 돌려 보기 전엔 모릅니다.

그리고 꾸며 둔 작업면은 이름을 붙여 저장하고 다음에 불러옵니다. 제목 옆에 저장과 불러오기가 있습니다. 프로젝트마다 반을 새로 꾸밀 필요가 없어졌습니다.

RS485 장비도 가상으로 물립니다

이번 판에서 가장 오래 걸린 부분입니다.

현장에서 PLC가 RS485로 이야기하는 상대는 대개 인버터입니다. 그런데 인버터는 책상에 올려 두고 시험하기 어려운 물건입니다. 그래서 시뮬레이터에 가상 RS485 장비를 넣었습니다. 메인 모듈의 RS485 단자대에 물리면, 프로그램의 Modbus RTU 마스터가 실물 장비와 통신할 때와 똑같이 주고받습니다.

여기서도 원칙은 같습니다. 통신 마스터는 실물 런타임에 든 그 코드가 돕니다. 마스터는 시리얼 포트를 열었다고 알고 있는데, 그 포트의 반대편에 화면에 놓인 가상 장비가 앉아 있는 겁니다. 컨트롤러 쪽에서 바꾼 것은 CAN 자리와 마찬가지로 시리얼 포트 자리 하나뿐입니다.

메인 모듈 위에 SV-iP5A 인버터를 물린 작업면. 검사창에 Modbus RTU 교신 기록과 레지스터 24칸이 보인다

인버터를 메인 모듈 위에 놓고 A·B 두 가닥을 RS485 단자대에 이은 작업면입니다. 프로그램의 Modbus RTU 마스터가 200 ms 마다 홀딩 레지스터 0x0005 부터 10칸을 읽어 가고, 그 요청과 응답 프레임이 오른쪽 검사창에 그대로 쌓입니다. 아직 운전 지령을 주지 않아 0.00 Hz 정지 상태입니다.

똑같다는 것은 이런 뜻입니다. 각 제조사 매뉴얼의 통신 레지스터 맵을 옮겨 넣었기 때문에, 있는 주소를 읽으면 그 값이 오고 없는 주소를 읽으면 실물처럼 예외로 답합니다. 그리고 지령을 받으면 그 장비처럼 움직입니다. 주파수 지령을 주면 가속 시간에 맞춰 올라가고, 상태 워드의 비트가 매뉴얼에 적힌 순서대로 서고, 트립이 걸리면 리셋 지령을 줘야 다시 섭니다. 프로그램이 폴링을 멈춰도 모터는 계속 돕니다. 실물이 그렇기 때문입니다.

첫 판에 넣은 레지스터 맵은 여섯 종입니다.

장비지령에 대한 동작
LS ELECTRIC SV-iP5A있음
LS ELECTRIC SV-iV5있음
YASKAWA GA500있음
SIEMENS SINAMICS V20있음
미쓰비시 인버터 (QPLC 통신 예제)있음
ADT iMaster-U1레지스터 읽기·쓰기만

여기서 두 번 넘어졌습니다.

첫 번째. 매뉴얼 PDF에서 레지스터 표를 엑셀로 뽑아 변환했는데, 나중에 매뉴얼과 대조해 보니 주소가 절반 넘게 빠져 있었습니다. 다른 기종은 배율이 단위 칸에 밀려 들어가 있었고요. 표가 페이지를 넘어가면서 깨진 겁니다. 결국 PDF를 펴 놓고 손으로 옮겼고, 어느 매뉴얼 몇 절에서 옮겼는지를 장비마다 같이 적어 뒀습니다. 나중에 틀리면 어디를 봐야 하는지 알아야 하니까요.

두 번째. 다섯 기종을 옮기고 나서 알았는데, 운전 지령의 모양이 제조사마다 다 다릅니다. 정방향 비트와 역방향 비트를 따로 두는 것, 운전 비트 하나에 방향 비트가 따로 있는 것, 비트를 내려야 켜지는 것, 지령을 쓰는 주소와 상태를 읽는 주소가 같은 것. 네 가지 모양이 시뮬레이터 안에서 다 돌아야 해서, “인버터 하나 넣으면 나머지는 복사”라는 계획은 첫 기종이 끝나자마자 접었습니다.

자기 장비의 레지스터 맵을 JSON으로 적어 Devices/ 폴더에 넣으면 목록에 늘어납니다. 현장 기기와 같은 방식입니다.

제품 그림을 다시 만들었습니다

여기서 한 번 크게 되돌렸습니다.

모듈 사진은 전에 만들어 둔 스튜디오 렌더를 잘라 쓰고 있었습니다. 그런데 그게 전부 하이앵글 이었습니다. 제품 사진으로는 근사한데, 화면에 눕혀 놓고 배선을 붙이니 커넥터도 로터리 스위치도 단자대도 전부 기울어져 보였습니다. 잘라 온 티가 났습니다.

같은 디자인을 유지하고 시점만 원근 없는 정투영 정면도로 다시 만들었습니다. 케이스 윗면·옆면이 보이지 않고, 다이얼과 나사 머리가 찌그러지지 않은 정원이고, 아래 DIN 클립까지 잘리지 않게.

한 가지는 일부러 그렇게 받았습니다. 표시등 LED 는 전부 꺼진 상태로. 꺼진 렌즈가 곧 OFF 상태이고, 시뮬레이터가 그 위에서 켜면 실제 표시등이 들어오는 것처럼 보입니다. 상태를 그림에 구워 넣으면 실제와 반드시 어긋납니다 — 라벨과 같은 이유입니다.

현장 기기 다섯 종도 같은 규칙으로 만들었습니다. 24V 전원, 접점 스위치, 표시등, 아날로그 센서, 계측기. 전부 윗변에 초록 나사 단자대가 있고, 단자명이 크게 인쇄돼 있습니다. 회색 네모가 아니라 제품처럼 보입니다.

단자와 표시등 좌표는 눈대중으로 찍지 않았습니다. 초록 단자대 안의 나사 구멍을 찾고 하우징 위의 둥근 렌즈를 찾는 식으로 그림에서 직접 읽어, 화면의 단자가 사진의 나사 위에 정확히 앉게 했습니다.

지금까지 된 것

  • 첫 공개판 0.1.0을 올렸고, 기기 이름표가 배선에 가려지던 것을 고친 0.1.1이 최신입니다. Release.Senbrix 릴리스 페이지에 Senbrix-sim-0.1.1-win.zip 으로 올렸습니다. zip 하나 66MB, 풀면 EXE 하나. .NET 설치 없이 더블클릭하면 됩니다
  • 품고 있는 런타임은 0.9.2 입니다. 시뮬레이터는 런타임을 다시 만든 게 아니라 그 버전을 그대로 넣은 것이라, 릴리스마다 어느 런타임이 들어 있는지 밝힙니다. 라즈베리파이와 마찬가지로 에디터가 이보다 새 버전이면 배포를 거부합니다
  • 부품 10종 — 모듈 4, 현장 기기 6. RS485 장비 레지스터 맵 6종. JSON 파일을 하나 넣으면 하나 늘어납니다
  • 테스트 251개. 프레임 규격 시험은 실제 보드 드라이버와 대조하고, RS485 는 실제 Modbus RTU 마스터를 통과시켜 대조합니다 — 우리끼리 적어 두면 드라이버가 바뀌어도 시험은 통과하고 시뮬레이터만 조용히 어긋납니다
  • 켜면 모듈만 진열되어 있고 기기도 배선도 없습니다. 배포해야 놓입니다. 빈 레일은 이 제품군에 무엇이 있는지조차 알려 주지 못해서, 아무것도 배포하지 않았을 때만 카탈로그를 세워 둡니다

아직 아닌 것

아날로그 입력 모듈의 변환 특성이 아직 가정입니다. 단자 전압을 보드 원시값으로 옮기는 곡선인데, 입력 레인지를 정하는 저항 값이 확정되지 않았습니다. 지금은 가정한 곡선을 쓰면서 화면에 “가정한 곡선” 이라고 밝혀 두었습니다. 정해지면 실측으로 갈아야 합니다.

접점을 통과하는 부하 전류도 아직 모델에 없습니다. 램프가 켜지는 건 맞는데, 그 전류가 전원 용량에서 빠지지는 않습니다.

인버터도 매뉴얼에 적힌 것만 매뉴얼대로 합니다. 가속 곡선이나 전류처럼 매뉴얼이 정하지 않는 물리량은 일반적인 인버터 값을 가정해 채웠습니다. 프로그램의 통신 절차와 지령 순서를 확인하는 데는 충분한데, 특정 기종의 세부 특성까지 대신하지는 못합니다. iMaster-U1 은 두 문서 어디에도 통신 지령 영역이 없어 동작 없이 레지스터 맵만 넣었습니다.

그래도 하고 싶었던 것은 됩니다. 에디터에서 배포 버튼을 누르면, 화면에 모듈이 붙고 센서와 스위치가 놓이고 선이 이어지고, 스위치를 켜면 프로그램이 반응합니다. 그리고 이제 인버터에 주파수를 주면 화면 안의 인버터가 가속합니다.

라즈베리파이도 IO 모듈도 인버터도 없는 책상에서, 배포하고 결선하고 돌려 보는 데까지 됩니다. 시뮬레이터에서 맞게 돈 프로그램은 라즈베리파이에서도 맞게 돕니다. 같은 코드니까요. 그게 이걸 만들면서 지킨 유일한 약속입니다.


🔧 GO 모듈형 IO 시리즈: 1부 — 시스템 구성 · 2부 — 메인 모듈 · 3부 — 서브 모듈 3종 · 4부 — 사양과 확장 · 5부 — 시뮬레이터

문의