1부에서 무선이 뚫렸습니다. 이제 센서를 물어야 하는데, 문제가 하나 있었습니다. 센서 실물이 현장에 있었습니다.

노드 펌웨어는 센서를 RS485로 폴링해서 그 값을 LoRa로 올립니다. 그런데 그 폴링 로직을 개발하고 검증하려면 대답해 줄 센서가 있어야 합니다. 현장에 가서 개발할 수는 없습니다.

그래서 센서인 척하는 프로그램을 만들었습니다. 시리얼 포트를 열고 슬레이브로 대기하다가, 마스터가 읽기·쓰기를 요청하면 실제 센서처럼 응답합니다.

센서 시뮬레이터 전체 화면

연결 설정, 슬레이브 목록, 레지스터 표, 빠른 조작, 통신 로그. 한 화면에서 센서 한 대가 할 일을 전부 합니다.

한 포트에서 국번이 다른 Modbus 슬레이브 여러 대

RS485 한 포트의 다중 국번이 현장 구성입니다. 노드 하나가 센서 여러 대를 국번으로 갈라 부릅니다. 그래서 시뮬레이터도 한 포트에서 국번이 다른 센서 여러 대를 동시에 연기해야 했습니다.

슬레이브 목록과 빠른 조작

국번 1~5가 각각 다른 측정값을 들고 있습니다. 오른쪽에서 RUN/STOP, 에러 상태, 측정값을 즉시 바꿀 수 있고, 사인·램프·랜덤으로 자동 변동도 겁니다.

이게 왜 필요했냐면, 마스터 쪽 예외 처리는 정상 응답으로는 검증이 안 되기 때문입니다. 센서 한 대가 응답을 멈췄을 때 나머지 네 대는 계속 도는지, 에러 상태를 던졌을 때 상위가 어떻게 처리하는지 — 실기기로는 재현하기 번거로운 것들을 여기서는 클릭 한 번에 만들 수 있습니다.

레지스터는 문서 주소표 그대로

레지스터 표

주소·이름·읽기쓰기 권한·RAW·HEX·해석된 값·범위. RAW 칸을 직접 편집하면 즉시 반영됩니다.

레지스터 직접 편집을 허용한 것은 의도적입니다. 정상 경로로는 못 만드는 상태를 강제로 만들어 놓고 마스터가 어떻게 읽는지 보기 위해서입니다.

프로토콜 문서가 스스로 어긋난 네 군데

프로토콜 문서는 제조사가 준 것을 그대로 보고 구현했습니다. 그런데 구현하다 보니 같은 문서 안에서 합계와 필드 표가 어긋나는 곳이 넷 나왔습니다.

예를 들어 읽기 정상응답 길이가 본문에는 6 + 2N 으로 적혀 있는데, 바로 아래 필드 표를 그대로 더하면 5 + 2N 이 됩니다. 어느 쪽이 맞을까요.

저는 전부 필드 표를 채택했습니다. 이유는 하나입니다 — 합계는 한 군데만 틀리면 그만이지만, 필드 표는 다른 응답들과 서로 교차검증이 되기 때문입니다. 실제로 같은 문서의 다른 명령 응답이 필드 표 계산과 일치했습니다.

다만 하나는 문서를 그대로 따랐습니다. 부정 응답 코드가 표준과 다른 값으로 적혀 있었는데, 이건 표준이 아니라 실기기를 재현하는 게 목적이라 문서를 우선했습니다. 대신 체크박스로 표준값 전환을 열어두고, 확인이 필요한 항목으로 남겼습니다.

실기기가 문서와 다르게 동작하면, 상위 프로그램은 시뮬레이터에서는 통과하고 현장에서 깨집니다. 시뮬레이터가 거짓말을 하면 안 되는 이유입니다. 그래서 확신이 없는 항목은 숨기지 않고 스위치로 꺼내 놨습니다.

시리얼 무음 판정이 정상 프레임을 둘로 쪼갠 버그

시리얼 프레임 경계는 따로 없습니다. 어디까지가 한 요청인지는 일정 시간 조용하면 끝이라는 규칙으로 자릅니다. 이 무음 판정 시간을 처음에 너무 짧게 잡았습니다.

그랬더니 USB 변환기 지연 때문에 한 프레임이 두 덩어리로 도착하는 사이에 타이머가 끼어들어, 정상 요청 하나가 깨진 프레임 두 개로 둔갑했습니다. 로그에는 앞쪽이 CRC 오류로, 뒤쪽이 엉뚱한 국번으로 찍혔습니다.

로그만 보면 회선이 이상한 것처럼 보입니다. 실제로는 제 타이머가 만든 유령이었습니다.

고치면서 길이 힌트를 3상태로 바꿨습니다.

양수이 프레임의 길이는 확정
0아직 판단 보류
음수판단 불가

“모른다”와 “판단할 수 없다”를 하나로 뭉뚱그리면, 프레임의 첫 1바이트만 먼저 도착했을 때 그것을 통째로 잘라 버립니다. 두 상태를 나눠야 했습니다.

RS485 회선 문제와 내 코드 문제 분리하기

첫 바이트가 깨지거나 사라지는 현상이 한동안 있었습니다. 시뮬레이터 쪽 문제인지 회선 문제인지부터 갈라야 했습니다.

방법은 단순했습니다. 시뮬레이터를 아예 빼고 PowerShell로 포트를 직접 열어 읽었습니다. 같은 증상이 나왔습니다. 그러면 그건 제 코드가 아닙니다.

원인은 RS485의 송수신 방향 전환 지연으로 보고 있습니다. 중요한 것은 원인 그 자체보다, “내 코드 아님”을 증명하는 데 5분이면 된다는 것이었습니다. 그 5분을 안 쓰면 남의 문제를 내 코드에서 몇 시간씩 찾습니다.

회귀 테스트로 못 박기

회귀 테스트로 위의 두 버그를 고정해 뒀습니다. 프레임이 쪼개져 도착해도 안 잘리는지, 잡음 한 바이트가 다음 요청에 들러붙지 않는지, 국번별로 값이 격리되는지. 테스트가 100개를 넘습니다.

시뮬레이터는 결국 다른 코드를 믿기 위한 도구입니다. 그 도구가 조용히 틀리면 검증 전체가 무의미해집니다. 그래서 여기만큼은 테스트를 아끼지 않았습니다.

창 없이 도는 진단 모드

진단 모드는 배선이나 속도, 국번이 맞는지만 확인할 때를 위한 것입니다. 그럴 때 GUI를 띄우는 것도 일이라, 창 없이 포트를 열어 정해진 시간 동안 응답만 하고 로그를 남깁니다. 종료 코드로 결과를 구분합니다 — 응답함, 무수신, 포트 열기 실패.

현장에서 노트북 하나 놓고 “이 선이 살아 있나”를 확인할 때 이게 제일 빨랐습니다.

시뮬레이터는 다른 코드를 믿기 위한 도구라서, 그 도구가 조용히 틀리면 검증 전체가 무의미해집니다.


▶️ 다음 편 — 기다리지 않는 폴러: 센서가 준비되자 이번엔 “몇 대까지 붙일 수 있나”가 문제였습니다. 응답을 기다리는 방식으로는 30대에 42초가 걸렸습니다.

🔧 이전 편: CRC 에러조차 안 뜨는 무수신

문의