---
title: "[아두이노#615] Arduino에 W5500을 연결해서 C#윈폼으로 모드버스(modbus) TCP의 프로토콜에 대해서 이해하고 통신하는 방법 알아보기!(녹칸다의 아두이노 시즌2)"
url: "https://maker.wiznet.io/irina/projects/615-arduino-w5500--c--modbus-tcp---------2/"
markdown_url: "https://maker.wiznet.io/irina/projects/615-arduino-w5500--c--modbus-tcp---------2/md"
type: "UCC: User Created Content"
author: "녹칸다"
author_url: "https://bota.tistory.com/2337"
editor: "WIZnet"
editor_url: "https://maker.wiznet.io/"
original_author: "녹칸다"
original_url: "https://bota.tistory.com/2337"
published: "2025-09-24"
language: "ko"
likes: 0
views: 1091
comments: 0
source: "WIZnet Makers (https://maker.wiznet.io/)"
---

# [아두이노#615] Arduino에 W5500을 연결해서 C#윈폼으로 모드버스(modbus) TCP의 프로토콜에 대해서 이해하고 통신하는 방법 알아보기!(녹칸다의 아두이노 시즌2)

> Arduino + W5500 demonstrates Modbus TCP with FC15, FC06, FC04, and 32-bit float data handling for registers and multiple coil control.

Original author: 녹칸다 (source: https://bota.tistory.com/2337)

## Article

[Embedded media](https://www.youtube.com/embed/DPL3s3Cpyhw)

## 1. Overview

In this post, we go beyond a simple Modbus TCP example and dive into **practical implementation details** using **Arduino UNO + W5500 Ethernet** and a **C# WinForms master application**.

You will learn how to:

- Control multiple coils with **FC15 (Write Multiple Coils)**.

- Update variables with **FC06 (Write Single Register)**.

- Read input registers with **FC04 (Read Input Registers)**.

- Safely exchange **32-bit floating-point values**.

We also explain the **Modbus TCP frame structure**, endianness, word-order pitfalls, and provide **debugging tips from a chip engineer’s perspective**.

---

## 2. Modbus TCP Frame Structure

- **MBAP Header (7 bytes):**
  Transaction ID (2) + Protocol ID (2, always `0x0000`) + Length (2) + Unit ID (1)

- **PDU:** Function Code (1) + Data (N)

### Coils & Registers

- **Coils/Discrete Inputs:** bit-addressable (mapped to Arduino GPIO).

- **Holding/Input Registers:** 16-bit word-based (mapped to variables or sensor values).

👉 Important: TCP **removes CRC** (unlike RTU) and adds MBAP for reliable transport.

---

## 3. Function Codes in Action

### FC15 – Write Multiple Coils

- **Request:** Start Address + Quantity + Byte Count + Coil Bits (LSB = lowest address).

- **Example:** Write 10 coils starting at address 19 with pattern `11001101 00000001`.

- **Response:** Echoes start address and quantity written.

### FC06 – Write Single Register

- Writes one 16-bit register.

- Meaning (signed/unsigned, scaling) must be agreed upon by both master & slave.

### FC04 – Read Input Registers

- **Request:** Start address + quantity (1–125).

- **Response:** Byte count + register data (2 bytes each).

- **Floats:** 1 float = 2 registers (4 bytes). Request 4 registers for 2 floats.

⚠️ Beware of word-order differences (ABCD, BADC, CDAB, DCBA). Always fix one convention.

---

## 4. Endianness & Word Order Pitfalls

- **MBAP Header:** Always big-endian (network order).

- **Registers:** Often big-endian by spec, but Arduino/W5500 code may use little-endian internally.

- **Coil Packing:** LSB of each byte = lowest coil address.

- **Floats:** Must align **word order** + **byte order** consistently between master & slave.

---

## 5. Arduino UNO + W5500 Implementation

1. **Ethernet Init:**
   Configure MAC, IP, and open Modbus TCP port 502.

```plaintext
Ethernet.begin(mac, ip, gateway, subnet);
EthernetServer modbusServer(502);
```

**2. Receive Loop:**

- Read MBAP (7B), then payload according to Length.

- Parse Function Code → handle request.

- Return proper response frame.

**3. Error Handling:**

- Illegal Function (0x01), Illegal Data Address (0x02), Illegal Data Value (0x03).

**4. Performance Notes:**

- W5500 has 8 sockets, 32KB total buffer (default 2KB each).

- RX halts if free size &lt; packet size → keep frames small and predictable.

---

## 6. C# Master (WinForms) Implementation

1. **Option A – Raw Socket:**
   Build MBAP + PDU manually, send via `TcpClient`, parse response.

2. **Option B – NModbus4 Library:**
   Simplifies frame building, parsing, and exception handling.

3. **Tips:**

- Always add timeout & retry logic.

- Parse exception frames (FC | 0x80).

- Display coil/register values in real time.

---

## 7. Example Address Map

- Coil 0 → Arduino D2

- Coil 1 → Arduino D3

- Coil 2 → Arduino D4

- Coil 3 → Arduino D5

- Coil 4 → Arduino D6

- Coil 5 → Arduino D7

👉 Use:

- **FC15** to batch-control LEDs.

- **FC06** to write integer values to Arduino variables.

- **FC04** to read sensor registers.

- **FC04 with 4 registers** to read 32-bit float data.

---

## 8. Extended Examples (from C# WinForms Integration)

- **Write Multiple Coils (FC15):** Toggle multiple LEDs at once.

- **Write Single Register (FC06):** Update Arduino variable remotely.

- **Read Input Registers (FC04):** Fetch 32-bit float sensor values.

💡 Mapping rule: Arduino GPIO pins ↔ Modbus coil addresses (e.g., D2 → Coil0).
💡 Use bit operations for packing/unpacking coil data.

---

## 9. Troubleshooting Checklist

- Wrong endian/word order → garbage float values.

- Length mismatch → master rejects frame.

- Coil packing error → wrong LED toggles.

- W5500 buffer overflow → incomplete frame, fix with chunked reads.

---

## 10. Conclusion

By extending Modbus TCP beyond the basics, you can:

- Control multiple coils at once.

- Write signed/unsigned registers.

- Read input registers in bulk.

- Safely exchange 32-bit floating-point data.

With **Arduino UNO + W5500 (slave)** and **C# WinForms (master)**, this setup becomes a practical solution for:

- Industrial control

- IoT gateways

- Embedded prototyping

---

## ✅ SEO Keywords

Arduino Modbus TCP
W5500 Ethernet module
FC15 multiple coil write
FC06 single register write
FC04 read input registers
32-bit float Modbus data
Modbus RTU vs TCP differences

---

Source: https://maker.wiznet.io/irina/projects/615-arduino-w5500--c--modbus-tcp---------2/
