1. Environment Setup: Refer to the Arduino IDE Getting Started Tutorial to install the IDE, then install the board manager and required driver libraries for the development board you are using.
2. Required Libraries:
3. Required Hardware:


0x01). Every second, the Client writes an incrementing value to the Server's holding register 0x0000, then reads the register and compares the result.CoreS3 PORT.A uses G1 (RX) and G2 (TX), while PORT.C uses G18 (RX) and G17 (TX). Unit RS485-ISO supports automatic transmit/receive direction control internally, so both DE/RE pins are set to -1.
#include "M5Unified.h"
#include <Wire.h>
#include <ArduinoModbus.h>
#include <ArduinoRS485.h>
constexpr uint32_t MODBUS_BAUDRATE = 115200;
constexpr uint8_t SERVER_ID = 0x01;
constexpr uint16_t REGISTER_ADDR = 0x0000;
constexpr int PORT_A_RX_PIN = 1;
constexpr int PORT_A_TX_PIN = 2;
constexpr int PORT_C_RX_PIN = 18;
constexpr int PORT_C_TX_PIN = 17;
// PORT.A uses Serial2 as the Modbus RTU Client interface.
RS485Class RS485(Serial2, PORT_A_RX_PIN, PORT_A_TX_PIN, -1, -1);
// PORT.C uses Serial1 as the Modbus RTU Server interface.
RS485Class RS485PortC(Serial1, PORT_C_RX_PIN, PORT_C_TX_PIN, -1, -1);
ModbusRTUClientClass portAClient(RS485);
ModbusRTUServerClass portCServer(RS485PortC);
uint16_t transmitValue = 0;
void modbusServerTask(void *parameter);
void showResult(uint16_t sentValue, long receivedValue);
void haltWithError(const char *message);
void setup()
{
M5.begin();
Serial.begin(115200);
delay(2000);
M5.Display.setFont(&fonts::FreeMonoBold12pt7b);
M5.Display.setTextColor(TFT_WHITE, TFT_BLACK);
M5.Display.clear();
M5.Display.println("RS485 initializing");
// Release PORT.A from I2C before using its pins for Serial2.
Wire.end();
if (!portCServer.begin(SERVER_ID, MODBUS_BAUDRATE, SERIAL_8N1)) {
haltWithError("Server init failed");
}
if (!portCServer.configureHoldingRegisters(REGISTER_ADDR, 1)) {
haltWithError("Register init failed");
}
portCServer.holdingRegisterWrite(REGISTER_ADDR, 0);
if (xTaskCreatePinnedToCore(modbusServerTask, "modbus-server", 4096,
nullptr, 2, nullptr, 0) != pdPASS) {
haltWithError("Server task failed");
}
if (!portAClient.begin(MODBUS_BAUDRATE, SERIAL_8N1)) {
haltWithError("Client init failed");
}
portAClient.setTimeout(500);
Serial.println("PORT.A Client and PORT.C Server started");
delay(100);
}
void loop()
{
++transmitValue;
bool writeSuccess = portAClient.holdingRegisterWrite(
SERVER_ID, REGISTER_ADDR, transmitValue);
long receivedValue = -1;
if (writeSuccess) {
receivedValue = portAClient.holdingRegisterRead(
SERVER_ID, REGISTER_ADDR);
}
showResult(transmitValue, receivedValue);
delay(1000);
}
void modbusServerTask(void *parameter)
{
while (true) {
portCServer.poll();
vTaskDelay(1);
}
}
void showResult(uint16_t sentValue, long receivedValue)
{
bool success = receivedValue >= 0 &&
static_cast<uint16_t>(receivedValue) == sentValue;
Serial.printf("PORT.A TX: 0x%04X, PORT.C RX/TX: 0x%04lX, %s\n",
sentValue, receivedValue, success ? "PASS" : "FAIL");
M5.Display.clear();
M5.Display.setCursor(0, 0);
M5.Display.println("Unit RS485-ISO Test");
M5.Display.println("A Client -> C Server");
M5.Display.printf("TX: 0x%04X\n", sentValue);
if (receivedValue >= 0) {
M5.Display.printf("RX: 0x%04lX\n", receivedValue);
} else {
M5.Display.println("RX: ERROR");
}
M5.Display.printf("Status: %s\n", success ? "PASS" : "FAIL");
}
void haltWithError(const char *message)
{
Serial.println(message);
M5.Display.clear();
M5.Display.setCursor(0, 0);
M5.Display.println(message);
while (true) {
delay(1000);
}
}
