본문으로 바로가기
homeimage
  1. Home
  2. 컴퓨터/프로그래밍
  3. 모드버스 프로토콜 작성 방법 및 주의사항

모드버스 프로토콜 작성 방법 및 주의사항

· 댓글개 · 바다야크

이 글은 모드버스 프로토콜을 처음 접하면서 프로그램을 구현해야 하는 분을 위한 글입니다. 모드버스 질의에 대한 응답을 어떻게 구현하는지에 대한 설명이며, 코드를 작성하기 전에 미리 알고 있으면 헷갈리거나 헛수고를 줄일 수 있을 것으로 생각합니다. 아울러 모드버스 프로그램 작성에 도움이 되는 팁을 소개합니다.

모드버스 프로토콜 작성 시 주의 사항

모든 모드버스 Function Code를 작성해야 하나요?

결론부터 말씀드리면 아닙니다. 만일 내 장비가 온도계이고 어떤 설정도 없이 현재 온도 값만을 제공한다면 읽기 Function code 4만 작성해도 됩니다.

또는 내 장비가 조명 시스템이고 10개의 조명이 달려 있는데, 번호에 따라 On/Off를 할 수 있다면 Function code 6만 작성해도 됩니다. 한 번에 여러 개를 On/Off 할 수 있게 하려면 code 16까지 제공하면 더욱 좋고요. 그러나 조명이 많지 않으면 code 6만 작성해도 문제없습니다. 대신에 불평하는 소리를 들을 수는 있겠죠.

여기에 몇 번 조명이 켜 있는지 알 수 있도록 읽기 기능을 제공한다면 Function code 3을 추가합니다.

어? 앞에서 읽기는 Function code 4라고 하지 않았나?

맞습니다. 모드버스 Function code 4도 읽기 기능 코드입니다. 그럼에도 code 3이 따로 있는 것은 Function code별로 접근하는 모드버스 테이블의 주소가 다르기 때문입니다.

모드버스 메모리 맵
모드버스 메모리 맵

모드버스는 Function code에 따라 접근할 수 있는 주소 영역이 다릅니다. Function code 4가 접근하는 모드버스 주소 영역은 다른 Function code로는 접근이 안 됩니다. 오로지 Function code 4로만 값을 요청할 수 있습니다.

Function code 6이 접근 가능한 모드버스 주소 영역은 Function code 3과 code 16이 접근 가능합니다. 즉, 이 영역은 code 6과 16으로 쓰기를 할 수 있고 code 3으로 값을 읽을 수 있습니다.

이렇게 Function code를 나눈 이유는 모드버스가 PLC 장비를 다루기 위해 생긴 프로토콜이어서 그렇다고 하네요. 그래서 한 가지 더 주목하실 것은 모드버스의 기본 데이터 크기는 bit와 2 byte의 word입니다. bit 단위는 코일을, word는 레지스터로 분류되는데요, 코일은 스위치를, 레지스터는 정수 값을 갖는 변수라고 생각하시면 됩니다.

모드버스 메모리 맵을 어떻게 정리해야 하나요?

위에 언급된 모드버스 메모리 맵을 보듯이 Function code 4는 30001번부터 39999번까지, Function code 3·6·16은 40001번부터 49999번까지로 되어 있어서 다른 개발자에게 30001번을 사용한다고 얘기만 해도 그분은 읽기 전용 장비이구만 하고 단번에 알게 됩니다. 40001번부터 기능을 차례로 정리해서 주면 읽거나 쓰기가 가능하고 Function code 3으로 읽을 수 있고 code 6으로 쓸 수 있겠네 하고 알려 주지 않아도 알게 됩니다.

그런데 여기서 주의하셔야 할 것이 있습니다. 30001번의 레지스터를 읽는다고 주소에 30001이라고 넣을까요? 아닙니다. 메모리 맵의 주소 영역에 따라 Function code를 나누었으므로 Function code 4를 사용하면 당연히 30001번부터 이니 0번이 30001이 됩니다. Function code 3은 40001번부터이므로 0번은 주소 40001이 되고요.

즉, 모드버스에서 메모리의 주소를 지정할 때 30001, 30002, 30003, ... 이렇게 지정하지 않고 0, 1, 2, ... 이렇게 지정합니다.

그런데(또 그런데) 주의해야 할 것이 또 있습니다. 경험으로 알게 된 것인데요, 대부분의 장비가 30001번이나 40001번을 지정할 때 0부터 시작하는데, 1인 장비가 있습니다. 즉, 30001번을 읽으려고 address를 0으로 요청했는데 30002번 값이 수신되는 것이죠.

그래서 상대에게 모드버스 메모리 맵을 제공할 때는 수고스럽더라도 실제 주소 번호를 함께 명시해 주는 것이 좋습니다.

모드버스 메모리 맵 작성 예시
모드버스 메모리 맵 작성 예시

위와 같이 제품의 기능에 따라서 코일·레지스터 번호와 함께 실제 주소를 함께 표시해서 제공한다면 상대방이 매우 고마워할 것입니다. 이렇게 깔끔하게 정리해서 보내 주다니 꼼꼼한 분이구만 칭찬하면서 말이죠.

MODBUS-RTU 프로토콜 주의 사항

바이트 순서 주의

어떤 깊은 뜻이 있는지 모르지만, UART로 통신하는 MODBUS-RTU에서는 바이트 순서에 주의해야 합니다.

모드버스 바이트 순서

모드버스 통신 패킷을 구성할 때 2 byte의 word 데이터는 빅 엔디안 순서를 따릅니다. 16진수 값으로 0x1234 라면 0x12 다음에 0x34를 전송합니다. 그러나 통신 패킷의 구성 요소인 CRC는 리틀 엔디안입니다. CRC가 계속 틀린다 싶으면 바이트 순서를 확인해 보세요.

질의와 응답 Count 값은 다르다?

모드버스 4번 함수 질의 요청
레지스터 1개 데이터 요청

Function code 4로 30011 레지스터의 값을 요청하는 질의 패킷 구성입니다. 주소로 0x000A에 길이는 1로 한 개의 레지스터 값을 요청했습니다.

모드버스 함수 4번 요청에 대한 응답 예
함수 4번 요청에 대한 응답 예

질의에 대한 응답을 보면 길이가 2입니다. 질의할 때는 1인데 응답은 2입니다. 값이 서로 다른 이유는 질의에서는 레지스터 개수이지만, 응답에서는 데이터 길이이기 때문입니다. 또 한 가지 주이하실 점은 질의할 때 레지스터 개수는 2 바이트이지만, 응답 데이터 길이는 1 바이트입니다. 즉, 255 이상 큰 수로 응답할 수 없습니다. 그러므로 질의할 때 레지스터 개수를 127 개 이상 요청하면 안 됩니다.

수신 데이터를 처리하기 곤란할 경우 처리 방법은?

Function code 4로 특정 레지스터 값을 요청받았는데, 해당 레지스터가 없다거나 Function code 4만 지원하는 장비인데 Function code 3으로 질의한다면 에러 코드로 반환해서 예외 상황임을 알려 주어야 합니다.

모드버스 예외 코드

모드버스의 예외 코드입니다. 수신한 Function code의 MSB bit를 1로 set 하고 예외 코드를 넣어서 응답합니다. 아래는 Function code 4로 특정 레지스터 값을 요청했지만, 해당 레지스터가 없어서 예외 코드를 반환하는 예제입니다.

마스터>> 01 04 00 0A 00 01 11 C8
01: 슬레이브 번호
04: Function Code
00 0A : 요청 주소 0x000A
00 01 : 요청 데이터 개수
0x0001 11 C8 : CRC 0xC811

1번 장치에 Function Code 4로 30011번 주소의 1개 레지스터 값을 요청했습니다. 그러나 1번 장치에는 30011번 레지스터를 사용하지 않습니다. 그래서 마스터에게 예외 상황을 알리려면 아래와 같이 응답합니다.

슬레이브>> 01 84 02 C1 C2
01 : 슬레이브 번호
84 : 요청한 Function Code의 MSB를 1로 세팅 0x04 + 0x80
02 : Illegal Data Address
C1 C2 : CRC 0xC2C1

모드버스의 통신 패킷 오류에 대한 자세한 내용은 여기 글을 참고하세요.

수신 데이터에 CRC가 오류가 발생했다면

수신한 데이터의 CRC 값이 틀린 경우에도 마스터에게 예외 상황으로 오류임을 알려야 할까요? 아닙니다. CRC가 틀린 경우에는 무시하고 응답해서는 안 됩니다. 이유는 장치 번호에 해당하는 국번 번호가 올바르지 않을 수 있기 때문입니다. 즉, 나에게 보낸 전문이 아닐 수 있기 때문으로 자세한 내용은 여기 글을 참고하세요.

모드버스 프로토콜 구현을 위한 팁

Modbus-Poll 프로그램 강력 추천

프로그램을 작성했다면 디버깅을 해야 할 텐데요, 완벽히 작성했다고 하더라도 상대방의 시스템과 원활히 통신이 안 되면 분쟁이 발생할 수 있습니다. 지금껏 통신이 안 된 적이 단 한 번도 없다는 답답한 소리만 반복하는 인간을 만나면 너무 피곤한데요, Modbus-Poll 프로그램으로 디버깅해서 이상이 없다는 것을 확인했다면, 답답한 상대방과 싸울 필요가 없습니다. Modbus-Poll로 확인했다고 하면 됩니다. 그만큼 Modbus-Poll은 역사가 오래되었고 많은 개발자가 사용하고 있으며 성능을 인정하기 때문입니다. Modbus-Poll에 대한 자세한 내용은 여기를 참고하세요.

libModbus 라이브러리 추천

모드버스는 구현하기 어려운 프로토콜은 아닙니다. 앞서 언급했듯이 모드버스를 지원하는 장비라고 모든 Function code를 제공할 필요가 없고 제품 기능에 따라 Function code에 응답하는 코드를 작성하면 됩니다. 그러나 잘 만들어진 라이브러리가 있다면 애써 고생해서 만들고 디버깅할 필요가 없습니다. 그래서 libModbus 라이브러리를 추천합니다.

모드버스에는 MODBUS-RTU 말고도 TCP/IP를 이용하는 MODBUS-TCP도 있습니다. 같은 모드버스 프로토콜이지만, MODBUS-RTU와 MODBUS-TCP는 패킷 구성부터 다릅니다. 스펙이 변경될 때마다 새로 작성하는 것보다 학습이 필요하더라도 libModbus 라이브러리로 구현하는 것이 좋습니다.

로그 파일 반드시 생성

모드버스 프로토콜이 아니더라도 모든 통신 프로그램은 수신과 응답에 대한 꼼꼼한 로그 파일을 반드시 작성해야 합니다. 갑갑한 개발자가 만든 장비와 통신한다면 더욱 필요합니다. 현장에서 경험한 내용을 바탕으로 로그 파일의 중요성을 언급한 글을 소개합니다.

모드버스 프로토콜 관련 글 모음

모드버스와 관련하여 작성한 글 모음입니다. 본문 중에 소개된 글도 있습니다만, 모쪼록 모드버스 프로토콜을 구현하는데 도움이 되었으면 좋겠습니다.

SNS 공유하기
💬 댓글 개
최근글
이모티콘창 닫기
울음
안녕
감사해요
당황
피폐

이모티콘을 클릭하면 댓글창에 입력됩니다.