
Modbus RTU 데이터 읽기 (Read Operations) 💾
안녕하세요. 지난 1회차에서는 Modbus RTU와 RS-485의 기본을 알아보고, NModbus 라이브러리를 설치하여 마스터 통신 환경을 준비했습니다.
이번 2회차에서는 실제 산업 현장의 Modbus 장치에서 데이터를 읽어오는(Read) 방법에 대해 집중적으로 다뤄보겠습니다. 데이터를 읽기 위해서는 먼저 Modbus의 독특한 데이터 모델을 이해해야 합니다.
1. Modbus의 4가지 데이터 모델 이해
Modbus 프로토콜은 모든 데이터를 네 가지 종류의 테이블(Table)로 구분하여 관리합니다. 우리가 읽거나 쓸 데이터가 이 네 가지 중 어디에 속하는지를 파악하는 것이 가장 중요합니다.
| 데이터 타입 | 특징 | 기능 코드 (FC) | 읽기 메서드 (NModbus 예시) |
| Coils | 1비트(Bit) 쓰기/읽기 가능. 주로 출력 상태나 제어 플래그 (ON/OFF)에 사용됨. | FC 01, 05, 15 | ReadCoils |
| Discrete Inputs | 1비트(Bit) 읽기 전용. 주로 물리적인 입력 상태 (센서 스위치)에 사용됨. | FC 02 | ReadDiscreteInputs |
| Holding Registers | 16비트(Word) 쓰기/읽기 가능. 가장 많이 사용되며, 설정 값이나 측정된 아날로그 값에 사용됨. | FC 03, 06, 16 | ReadHoldingRegisters |
| Input Registers | 16비트(Word) 읽기 전용. 온도, 압력 등 실시간 측정 값에 사용됨. | FC 04 | ReadInputRegisters |
🔍 핵심 정리: Register와 Coil
- Coil (1비트): Boolean 형태의 데이터 (true 또는 false).
- Register (16비트): 숫자 형태의 데이터 (0 ~ 65535). PLC나 장치 매뉴얼에서는 이 Register의 주소를 번지라고 부릅니다.

2. 데이터 읽기를 위한 Modbus 함수 코드 (Function Code)
Modbus 마스터가 슬레이브에게 '어떤 작업을 해달라'고 명령할 때 사용하는 코드가 **함수 코드(Function Code, FC)**입니다.
읽기 작업에 사용되는 주요 함수 코드는 다음과 같습니다.
| 함수 코드 (FC) | 설명 | 데이터 타입 |
| 01 (Read Coils) | Coils 데이터 읽기 | 1비트 |
| 02 (Read Discrete Inputs) | Discrete Inputs 데이터 읽기 | 1비트 |
| 03 (Read Holding Registers) | Holding Registers 데이터 읽기 | 16비트 |
| 04 (Read Input Registers) | Input Registers 데이터 읽기 | 16비트 |
우리는 NModbus 라이브러리를 사용하므로, 이 함수 코드들은 메서드 이름으로 추상화되어 제공됩니다. (예: FC 03은 ReadHoldingRegisters 메서드)
3. C# NModbus로 데이터 읽기 구현 (FC 03)
가장 흔하게 사용되는 **Holding Register (FC 03)**를 읽는 C# 코드를 작성해 보겠습니다.
📝 예제 코드: Holding Register 값 읽기
지난 1회차에서 초기화한 IModbusMaster master 객체를 사용합니다.
/// <summary>
/// Modbus Holding Register의 값을 읽어오는 함수
/// </summary>
/// <param name="slaveId">데이터를 읽을 슬레이브 장치의 ID (1~247)</param>
/// <param name="startAddress">읽기 시작할 레지스터 주소 (0부터 시작)</param>
/// <param name="numRegisters">읽어올 레지스터의 개수</param>
public ushort[] ReadHoldingRegisters(byte slaveId, ushort startAddress, ushort numRegisters)
{
// 장치 주소, 시작 번지, 개수가 필수 인수로 들어갑니다.
ushort[] data = new ushort[0];
try
{
// 1. Modbus 마스터를 이용해 ReadHoldingRegisters 호출 (FC 03)
// 응답은 16비트 unsigned short 배열 형태로 반환됩니다.
data = master.ReadHoldingRegisters(slaveId, startAddress, numRegisters);
Console.WriteLine($"[성공] Slave ID {slaveId}의 레지스터 {startAddress}부터 {numRegisters}개 읽음.");
// 읽은 값 출력 (디버깅 목적)
for (int i = 0; i < data.Length; i++)
{
Console.WriteLine($"레지스터 주소 {startAddress + i}: 값 = {data[i]}");
}
}
catch (Exception ex)
{
Console.WriteLine($"[오류] Modbus 읽기 실패: {ex.Message}");
}
return data;
}
// 실제 호출 예시:
// ID가 1인 슬레이브 장치의 100번지 레지스터부터 5개의 값을 읽어옴.
// ushort[] results = ReadHoldingRegisters(1, 100, 5);
4. 바이트 순서(Byte Order)와 데이터 타입 변환
Modbus 통신을 통해 레지스터(16비트 ushort)를 읽어왔다고 끝이 아닙니다. 실제 아날로그 값(예: 32비트 Float 온도)을 얻으려면 데이터 변환이 필수적입니다.
예를 들어, 온도 값 $37.5$를 저장하기 위해 32비트 실수(Single/Float)를 사용했다면, 이는 두 개의 16비트 레지스터(Word)에 나뉘어 저장됩니다.
🔧 데이터 변환 (Float 예시)
NModbus는 기본적으로 ushort 배열을 반환합니다. 이를 Float으로 변환하려면 BitConverter를 사용해야 합니다.
// Modbus에서 읽어온 2개의 레지스터 값 (ushort 배열)
ushort[] modbusWords = { word1, word2 }; // 예: { 17180, 16641 }
// 1. ushort 배열을 바이트 배열로 변환
byte[] bytes = new byte[modbusWords.Length * 2];
for (int i = 0; i < modbusWords.Length; i++)
{
// ushort를 바이트로 변환하여 bytes 배열에 저장
// *주의: 장치에 따라 바이트 순서(Endianness)를 뒤집어야 할 수도 있습니다.
byte[] wordBytes = BitConverter.GetBytes(modbusWords[i]);
// 이 예시에서는 기본 Big Endian (Word Swap) 가정
bytes[i * 2 + 0] = wordBytes[0];
bytes[i * 2 + 1] = wordBytes[1];
}
// 2. 바이트 배열을 32비트 Float (Single)으로 변환
float resultFloat = BitConverter.ToSingle(bytes, 0);
Console.WriteLine($"변환된 실수 값: {resultFloat}");
⚠️ 중요: PLC나 장치마다 바이트 순서(Little Endian/Big Endian) 및 워드 순서(Word Swap)가 다르므로, 장치 매뉴얼을 반드시 확인하여 bytes 배열을 구성하는 순서를 결정해야 합니다.
맺음말
이번 2회차에서는 Modbus의 4가지 데이터 모델을 학습하고, NModbus 라이브러리의 ReadHoldingRegisters 메서드를 이용해 데이터를 읽어오는 핵심 코드를 구현했습니다. 또한, 읽어온 16비트 레지스터 값을 실제 32비트 실수 값으로 변환하는 중요한 과정도 살펴보았습니다.
다음 마지막 3회차에서는 장치의 값을 변경하는 데이터 쓰기(Write) 작업과 통신 프로그램의 안정성을 높이는 예외 처리 방법에 대해 알아보겠습니다.
🚀 C#으로 Modbus/RS-485 마스터(Master) 통신 정복하기
🚀 C#으로 Modbus/RS-485 마스터(Master) 통신 정복하기
Modbus와 RS-485, 그리고 C# 환경 준비 ⚙️ 안녕하세요, 산업 자동화와 데이터 수집에 관심 있는 C# 개발자 여러분!이번 연재에서는 C# .NET 환경에서 Modbus RTU 프로토콜을 사용하여 RS-485 통신 기반의
moneygeneration.tistory.com
✍️ C#으로 Modbus/RS-485 마스터(Master) 통신 정복하기 (3/3)
✍️ C#으로 Modbus/RS-485 마스터(Master) 통신 정복하기 (3/3)
Modbus RTU 데이터 쓰기 및 예외 처리 (Write & Error Handling) ✍️안녕하세요. 드디어 C# Modbus RTU 마스터 통신 연재의 마지막 시간입니다.지난 2회차까지 데이터를 읽는(Read) 방법을 완벽히 이해했다면,
moneygeneration.tistory.com
'HW SW 개발' 카테고리의 다른 글
| 📍 이더캣의 내부 구조 완전 해부: 프레임, ESC, DC 메커니즘 (1/3) (0) | 2025.12.05 |
|---|---|
| ✍️ C#으로 Modbus/RS-485 마스터(Master) 통신 정복하기 (3/3) (0) | 2025.12.03 |
| 🚀 C#으로 Modbus/RS-485 마스터(Master) 통신 정복하기 (0) | 2025.12.03 |
| C#에서 Modbus RTU 프로토콜 사용하기 (2) | 2025.01.04 |
| C# Modbus 사용하기 (1) | 2025.01.04 |