Using ESP8266 to Control Modbus RTU Relay via RS485 - Step by Step
Using ESP8266 to Control Modbus RTU Relay via RS485 - Step by Step
This guide walks you through using ESP8266 to control a Modbus RTU 1-Way Relay Module RS485/TTL 12V. We’ll cover wiring, Modbus command structure with Hex codes, and working Arduino code with CRC validation.
Why Use Modbus RTU over RS485
Modbus RTU via RS485 is a widely used industrial protocol because it requires only 2 signal wires (A+ and B-) to control hundreds of relay units on a single bus, each with its own unique address.
Key advantages of this approach:
- Saves I/O pins - Uses just 2 UART pins (TX/RX) to control dozens of relays
- Long transmission distance - RS485 can carry data hundreds of meters without a repeater
- High noise immunity - Differential signaling resists interference from motors, pumps, and high-voltage equipment
- Suitable for Smart Farm / Home Automation - Easy to wire across large buildings or farms
Required Components
| Component | Qty |
|---|---|
| NodeMCU ESP8266 V2 (CP2102) | 1 |
| MAX485 RS485 to TTL Module | 1 |
| Modbus RTU 1-Way Relay Module RS485/TTL 12V | 1 |
| Power Adapter 12V 5A + DC Jack 5.5x2.5mm | 1 set |
| Power Adapter Micro USB 5V 2A | 1 |
| Breadboard MB-102 830 Point | 1 |
| Jumper wires M-M / M-F / F-F 20cm | 40 each |
| Micro USB cable | 1 |
Wiring Guide
1. Connect ESP8266 to MAX485 Module
| ESP8266 | MAX485 Module |
|---|---|
| Vin (5V) | VCC |
| GND | GND |
| D7 (GPIO13) | RO (RX Output) |
| D4 (GPIO2) | DI (TX Input) |
| D1 (GPIO5) | DE + RE (jumpered together) |
Note: DE and RE must be jumpered together and connected to ESP8266 D1. MAX485 needs this pin to switch between Receive mode (LOW) and Transmit mode (HIGH).
2. Connect MAX485 to Modbus Relay
| MAX485 | Modbus Relay |
|---|---|
| A+ | A+ |
| B- | B- |
3. Connect Power and Load
- 12V DC from Adapter 5A to Modbus Relay’s + / - terminals
- NO (Normally Open) terminal to N terminal of load power supply
- COM terminal to N terminal of the load
- L terminal of load power supply connected directly to load’s L terminal
Modbus RTU Command Structure
Modbus RTU uses Hex codes for communication. Every command frame includes a 16-bit CRC at the end for data integrity checking.
Turn Relay ON (Function Code 0x05 - Force Single Coil)
[ID] [0x05] [0x00] [0x00] [0xFF] [0x00] [CRC Low] [CRC High]
| Field | Value | Meaning |
|---|---|---|
| ID | 0x01 | Module address |
| Function Code | 0x05 | Force Single Coil |
| Coil Address High | 0x00 | Relay #1 |
| Coil Address Low | 0x00 | |
| Force Data High | 0xFF | Command ON |
| Force Data Low | 0x00 | |
| CRC | - | Calculated from first 6 bytes |
Turn Relay OFF
[ID] [0x05] [0x00] [0x00] [0x00] [0x00] [CRC Low] [CRC High]
Change Force Data from 0xFF 0x00 to 0x00 0x00 to turn OFF.
Read Relay Status (Function Code 0x01 - Read Coils)
[ID] [0x01] [0x00] [0x00] [0x00] [0x01] [CRC Low] [CRC High]
| Field | Value | Meaning |
|---|---|---|
| ID | 0x01 | Module address |
| Function Code | 0x01 | Read Coils |
| Starting Address High | 0x00 | Start from Coil 0 |
| Starting Address Low | 0x00 | |
| Quantity High | 0x00 | Number of coils |
| Quantity Low | 0x01 | Read 1 coil |
Arduino Code for ESP8266
#include <SoftwareSerial.h>
// RS485 pin definitions
#define RS485_TX_PIN D4 // GPIO2 - Data Output (DI of MAX485)
#define RS485_RX_PIN D7 // GPIO13 - Data Input (RO of MAX485)
#define RS485_CONTROL_PIN D1 // GPIO5 - TX/RX mode switch (DE/RE of MAX485)
// Modbus parameters
#define MODBUS_ID 0x01 // Relay module address
#define BAUD_RATE 9600 // Standard baud rate
// SoftwareSerial for RS485 interface
SoftwareSerial rs485(RS485_RX_PIN, RS485_TX_PIN);
void setup() {
Serial.begin(9600);
rs485.begin(BAUD_RATE);
pinMode(RS485_CONTROL_PIN, OUTPUT);
digitalWrite(RS485_CONTROL_PIN, LOW); // Start in receive mode
Serial.println("ESP8266 Modbus RTU Relay Ready");
}
// Switch MAX485 between Send and Receive mode
void setRs485Mode(bool send) {
digitalWrite(RS485_CONTROL_PIN, send ? HIGH : LOW);
if (send) {
delay(1); // Allow signal to stabilize
}
}
// Modbus CRC16 calculation (CRC-16/MODBUS polynomial)
unsigned int calculateCRC(byte *data, byte length) {
unsigned int crc = 0xFFFF;
for (byte i = 0; i < length; i++) {
crc ^= data[i];
for (byte j = 0; j < 8; j++) {
if (crc & 0x0001) {
crc = (crc >> 1) ^ 0xA001;
} else {
crc >>= 1;
}
}
}
return crc;
}
// Send Modbus command and read response
bool sendModbusCommand(byte *cmd, byte cmdLength, byte *response, byte respLength) {
// Transmit command
setRs485Mode(true); // Switch to transmit mode
rs485.write(cmd, cmdLength);
rs485.flush(); // Wait for transmission to complete
setRs485Mode(false); // Switch back to receive mode
// Wait for response
unsigned long startTime = millis();
byte receivedBytes = 0;
while (millis() - startTime < 500) {
if (rs485.available()) {
response[receivedBytes++] = rs485.read();
if (receivedBytes >= respLength) break;
}
}
return receivedBytes == respLength;
}
// Turn relay ON
void relayOn() {
byte cmd[] = { MODBUS_ID, 0x05, 0x00, 0x00, 0xFF, 0x00 };
unsigned int crc = calculateCRC(cmd, 6);
cmd[6] = crc & 0xFF; // CRC Low
cmd[7] = (crc >> 8) & 0xFF; // CRC High
byte response[8];
if (sendModbusCommand(cmd, 8, response, 8)) {
Serial.println("Relay ON - Response: OK");
printHex(response, 8);
} else {
Serial.println("Relay ON - No Response");
}
}
// Turn relay OFF
void relayOff() {
byte cmd[] = { MODBUS_ID, 0x05, 0x00, 0x00, 0x00, 0x00 };
unsigned int crc = calculateCRC(cmd, 6);
cmd[6] = crc & 0xFF;
cmd[7] = (crc >> 8) & 0xFF;
byte response[8];
if (sendModbusCommand(cmd, 8, response, 8)) {
Serial.println("Relay OFF - Response: OK");
printHex(response, 8);
} else {
Serial.println("Relay OFF - No Response");
}
}
// Read relay status
bool readRelayStatus() {
byte cmd[] = { MODBUS_ID, 0x01, 0x00, 0x00, 0x00, 0x01 };
unsigned int crc = calculateCRC(cmd, 6);
cmd[6] = crc & 0xFF;
cmd[7] = (crc >> 8) & 0xFF;
byte response[7];
if (sendModbusCommand(cmd, 8, response, 7)) {
Serial.println("Read Status - Response: OK");
printHex(response, 7);
// Check status from byte index 3
bool isOn = (response[3] & 0x01) != 0;
Serial.print("Relay Status: ");
Serial.println(isOn ? "ON" : "OFF");
return isOn;
} else {
Serial.println("Read Status - No Response");
return false;
}
}
// Debug helper - print hex values
void printHex(byte *data, byte length) {
Serial.print("Hex: ");
for (byte i = 0; i < length; i++) {
if (data[i] < 0x10) Serial.print("0");
Serial.print(data[i], HEX);
Serial.print(" ");
}
Serial.println();
}
void loop() {
relayOn();
delay(2000);
relayOff();
delay(2000);
}
Points to Adjust for Your Specific Module
- Address (ID): If your module has a different address, change
MODBUS_IDto match - Baud Rate: Default is usually 9600. If your module uses 4800 or 19200, update
BAUD_RATE - Pin assignments: Different ESP8266 boards may use different pin numbers - check your board’s pinout
How to Change Module Address and Baud Rate
To operate multiple relay modules on the same bus, each must have a unique address:
- Connect only the module you want to reconfigure to ESP8266 (disconnect others)
- Send the address change command per your module’s datasheet (usually Function Code 0x06 to write a Holding Register)
- Verify the change by reading back the status
Reference Video
Quick Setup Summary
- Wire ESP8266 → MAX485 → Modbus Relay according to the pin tables above
- Power Modbus Relay with 12V and MAX485 with 5V from ESP8266
- Upload the code to ESP8266
- Open Serial Monitor to check operation status
- The relay will toggle ON/OFF every 2 seconds - you can now connect a load device
อยากทำโปรเจคแบบนี้?
รับทำโปรเจค Arduino / IoT จบงานไว ส่งงานครบ พร้อมสอน
If you need Arduino project service or urgent IoT development, see full service details on the home page
จ้างทำโปรเจคเลย