라즈베리파이로 PLC 만들기 — ZPi Controller 소개
왜 또 PLC 인가
산업 현장에서 PLC 는 흔하다. 미쓰비시, 지멘스, 로크웰 — 다 안정적이고 검증된 제품이다. 그런데 소규모 제어용으로 PLC 한 대 사려면 30만원. 부속 케이블, 통신모듈, 프로그래밍 소프트웨어까지 더하면 50만원이 쉽게 넘어간다.
소형 자동화 / 교육용 / R&D 시제품 / 메이커 프로젝트에는 너무 과한 가격이고 너무 닫힌 생태계다. Modbus 연동 하나 하려면 RTU/TCP 슬레이브 모듈을 추가로 사야 하고, 클라우드로 데이터 보내려면 게이트웨이를 따로 달아야 한다.
ZPi Controller 는 그래서 만들었다. 라즈베리파이 Zero 2 W + 4입력/4릴레이 보드 + .NET 9 + Blazor 조합으로:
- 하드웨어: 5만원대 (Pi Zero 2 W + ZPi-IO08R 보드)
- 소프트웨어: 오픈
- 통신: Modbus TCP/RTU 슬레이브 + 마스터 + (예정) MQTT
- 프로그래밍: 웹 브라우저만 있으면 됨
- UI: AI 가 만들어주는 풀스크린 HMI (다음 글 참조)

구성
[Raspberry Pi Zero 2 W]
│
├─ ZPi-IO08R 보드 (4DI 광 절연 + 4DO 릴레이)
├─ 0.96" OLED (IP + 시간 + IO 표시, 옵션)
└─ I2C / SPI / UART (확장)
[.NET 9 서비스 (systemd)]
├─ Blazor WebAssembly 관리 UI (포트 5000)
├─ Modbus TCP 슬레이브 (포트 502)
├─ Modbus RTU 마스터/슬레이브 (RS-485)
├─ 룰 엔진 (50ms tick) — 매트릭스 + 자연어 카드 + Python 코드
└─ MCP 서버 (Claude Desktop / Code 통합)
핵심 기능 한눈에
| 페이지 | 기능 |
|---|---|
| 모니터 | 입력/출력/메모리/타이머/카운터 라이브 표시 |
| 룰 에디터 | 입력→출력 매트릭스 + 추가 룰(자연어) + 고급 Python 코드(Tick()/OnInput()) — 셋이 같은 변수 풀 공유 |
| Modbus | TCP 슬레이브 / 마스터 (외부 슬레이브 폴링) / RTU 슬레이브 / 마스터 통합 설정 |
| 네트워크 | WiFi 스캔/접속/자동복원, 호스트명 변경 (mDNS) |
| Programs | 사용자 .NET / Python / 네이티브 프로그램 업로드 + 실행 + 자동시작 |
| 시스템 | OS/메모리/온도/IP, 비밀번호 변경, MCP 한 번 클릭 설치 |
| ZView (이번 세션 추가) | 오퍼레이터용 풀스크린 HMI — AI 가 자연어로 생성 |
룰 에디터 — 한 화면에 3가지 방식 (v2)

룰을 짜는 방식이 3가지인데, 셋 다 같은 변수 풀(IN/OUT/M/T/C/D)을 공유하고 매 스캔(50ms) 마지막에 함께 실행된다.
- 입력 → 출력 매트릭스 — 셀을 클릭하면
없음 → 직접(IN→OUT) → 반전(IN→!OUT)으로 순환. 단순 직결은 코드 한 줄 없이 끝. - 추가 룰 (자연어) — 매트릭스로 표현 안 되는 동작(메모리·타이머·카운터·주기·Modbus write 등)을 ”+ 룰 추가”로 카드처럼 쌓는다.
- 고급 (Python 코드) — 그래도 부족하면 Python 으로 직접.
Tick()(매 50ms) /OnInput(i, on)(입력 변경 시) 진입점에 로직을 채운다.
Python 코드에서 바로 쓰는 것들:
- 변수:
In[i]·Out[i]·M[i]·D[i]·C[i]·T[i].Elapsed·T[i].Running - 헬퍼:
StartTimer(i, ms)·StopTimer(i)·IncCounter(i)·ResetCounter(i)·Log(...) - OLED:
Oled.SetLine(0..2, "텍스트")·Oled.ClearLine(i)·Oled.Clear()
예를 들어 “IN0 켜지면 OUT0 ON, 1초 뒤 OUT1 도 ON” 은 이렇게:
void Tick()
{
Out[0] = In[0];
if (In[0] && !T[0].Running) StartTimer(0, 1000);
Out[1] = T[0].Elapsed >= 1000;
Oled.SetLine(0, $"IN0={In[0]} OUT1={Out[1]}");
}
매트릭스로 80%를 클릭만으로 끝내고, 나머지 복잡한 것만 카드·Python 으로 — 그게 v2 룰 에디터의 방향이다.
통신

- TCP 슬레이브 (포트 502) — 외부 SCADA / HMI 가 본 보드를 폴링
- TCP 마스터 — 본 보드가 다른 Modbus TCP 슬레이브를 폴링해서
D[]레지스터에 매핑 - RTU 슬레이브 (RS-485) — 같은 메모리 맵 RS-485 노출
- RTU 마스터 (RS-485) — 외부 RTU 슬레이브 폴링
메모리 맵:
P(Coil) — 출력M(Coil) — 내부 비트T(Holding) — 타이머 ElapsedMs/100C(Holding) — 카운터D(Holding) — 데이터 레지스터Wp/Wm— 비트들을 워드로 묶음
네트워크 자동복구

라즈베리파이에서 WiFi 가 끊기면 진짜 짜증 난다. NetworkManager 프로필이 사라지거나 (특히 SD 카드 복제 후), DHCP 가 새 IP 를 줘서 mDNS 가 혼란스러워지거나, 부팅 시간이 1분 30초로 늘어나거나.
이번에 다 잡았다 — 자세한 건 5편.
Programs — 사용자 코드 호스팅

LauncherTouch 스타일로 .NET 자체포함 exe / Python / 네이티브 바이너리를 zip 으로 업로드하면 systemd 보조 서비스처럼 돌려준다. 자동 재시작, 자동 시작, 로그 보기 다 지원. GPIO 점유는 EngineGate 가 조율 (룰엔진 ↔ 사용자 프로그램 자동 핸드오프).
시스템 페이지 + Claude 연동

시스템 페이지에서 “MCP 설치 (PC연동)” 버튼 한 번이면 Windows BAT 가 다운로드되고, 더블클릭하면 Claude Desktop / Code 양쪽에 자동 등록된다.
참고: 보드는 operator 기본 비밀번호
going, admin 기본 비밀번호admin으로 출고된다. 운영 전 반드시 변경하세요.
이게 가장 재밌는 부분. AI 가 PLC 를 직접 제어할 수 있고, 더 나아가 오퍼레이터용 HMI 페이지를 만들어서 배포까지 할 수 있다. 다음 글에서 자세히 다룬다.
댓글
로그인 없이 이름만 적고 남기실 수 있어요. 남긴 댓글은 바로 게시됩니다.
댓글을 불러오는 중…