В прошлой статье я привел пример работы с RS485 модулем XY-485 при работе с ESP8266. Рассмотрю как работать с RS485 модулями с микроконтроллера ESP32.
Тестовое «железо»:
- ESP32 — 30-ти пиновая плата ESP32 DevKit v1.
- RS485 — плата XY-485.
- RS485 — плата XY-017.
Лучше приобретать 38-ми пиновую плату ESP32, а не 30-ти. Разница в цене около 100 руб, но выводов побольше, а они лишними не бывают. Тем более, что габариты платы остаются такими-же. Отличие платы в том, что ножки на гребенке идут до самого низа платы.

30-ти ножечная плата ESP32

Последовательный порт ESP32
На ESP32 доступно три UART порта именуемые как U0UXD, U1UXD и U2UXD :
- U0UXD используется чипом преобразователя USB интерфейса платы ESP32 DevKit V1.
- U1UXD можно использовать для своих проектов. Некоторые платы используют этот порт на пине используемом под SPI Flash. Если не перенести на другой GPIO, то при обращении к этому пину программа выпадет в ошибку.
- U2UXD можно использовать для своих проектов.
На большинстве плат с ESP8266, U0UXD как и в случае с ESP32, занят для работы с конвертером USB интерфейса. Поэтому на платах ESP8266 с USB портом, по сути, не свободных аппаратных портов.
Для решения проблемы с отсутствием доступных последовательных портов на ESP8266 есть два варианта:
- Использовать дополнительную плату конвертера I2C в один порт UART с получением дополнительных 8 GPIO портов на чипе SC16IS750. Либо ковертер I2C в два порта UART на чипе SC16IS752.
- Использовать программную эмуляцию последовательного порта с помощью библиотеки ESPSoftwareSerial. Эмуляция работает с модулями RS485 неплохо, но хочется для надежности чисто аппаратной реализации.
В случае с ESP32 есть два свободных аппаратных последовательных порта. По умолчанию RX/TX выведены на следующие пины GPIO:
| Device function | GPIO |
|---|---|
| UART 0 TX | 1 |
| UART 0 RX | 3 |
| UART 1 TX | 10 |
| UART 1 RX | 9 |
| UART 2 TX | 17 |
| UART 2 RX | 16 |
Если посмотреть pinout на 30-ти pin-овую ESP32 DevKit V1, то можно отметить, что порт UART1 не выведен ножки и доступен только в 38 пиновой плате.

Однако, в ESP32 можно перенести последовательный порт UART1 на другие GPIO при инициализации порта.
Схема соединения ESP32 c RS485
В ESP32 используем аппаратные UART-ы. В прошлой статье я уже упоминал о том, что
маркировка RX/TX на платах XY-485 и XY-017 перепутана местами!!!
| ESP32 | XY-485 |
| Rx (PIN RX2, GPIO16) | RXD |
| Tx (PIN TX2, GPIO17) | TXD |
| VCC (Блок питания +3,3V) | |
| GND | GND |
| XY-017 | |
| Rx (PIN RX1, GPIO15) | RXD |
| Tx (PIN TX1, GPIO4) | TXD |
| VCC (Блок питания +3,3V) | |
| GND | GND |
Блок питания для модулей RS485 ДОЛЖЕН БЫТЬ на 3,3V,
иначе можно сжечь GPIO ESP32!!!
Либо используйте конвертер TTL уровней.
И схема подключения модулей XY-485/XY-017 к двум термодатчикам по RS485:
| Термодатчик 1 | XY-485 |
| A+ | A+ |
| B- | B- |
| GND | GND |
| GND (Блок питания -) | |
| VCC (Блок питания +5V) | |
| Термодатчик 2 | XY-017 |
| A+ | A+ |
| B- | B- |
| GND | GND |
| GND (Блок питания -) | |
| VCC (Блок питания +5V) |
Программа для ESP32 для работы с RS485
Понятно, что особого смысла в подключении двух карт RS485 в общем-то нет. Длина линии одного интерфейса RS485 может составлять до 1200 метров с количеством подключенных устройств 32 шт, что достаточно для решения большинства задач.
В данном случае пример больше для демонстрации, что оба доступных UART портов работают нормально.
/*
* There are three serial ports on the ESP known as U0UXD, U1UXD and U2UXD.
*
* U0UXD is used to communicate with the ESP32 for programming and during reset/boot.
* U1UXD is unused and can be used for your projects. Some boards use this port for SPI Flash access though
* U2UXD is unused and can be used for your projects.
*
* Tested on the 30 pin ESP32 DevKit V1 board
*
*/
#include "ModbusMaster.h" //https://github.com/4-20ma/ModbusMaster
#include <HardwareSerial.h>
#define Slave_ID1 2
#define Slave_ID2 1
// instantiate ModbusMaster object
ModbusMaster modbus1;
ModbusMaster modbus2;
//HardwareSerial Serial1(1);
//HardwareSerial Serial2(2); //- there is no any sense to define since it already defined in HardwareSerial.h
#define RXD1 15 //RX1 pin
#define TXD1 4 //TX1 pin
#define RXD2 16 //RXX2 pin
#define TXD2 17 //TX2 pin
void setup()
{
// Modbus communication runs at 9600 baud
Serial.begin(9600, SERIAL_8N1);
Serial1.begin(9600, SERIAL_8N1, RXD1, TXD1);
Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
// Modbus slave ID 1
modbus1.begin(Slave_ID1, Serial1);
modbus2.begin(Slave_ID2, Serial2);
modbus1.idle(yield);
modbus2.idle(yield);
}
long lastMillis = 0;
void loop()
{
long currentMillis = millis();
if (currentMillis - lastMillis > 5000)
{
// Read 2 registers starting at 0x01
uint8_t result1 = modbus1.readInputRegisters(0x01, 2);
if (getResultMsg( & modbus1, result1))
{
Serial.println();
double res_dbl = modbus1.getResponseBuffer(0) / 10;
String res = "Temperature1: " + String(res_dbl) + " C\r\n";
res_dbl = modbus1.getResponseBuffer(1) / 10;
res += "Humidity1: " + String(res_dbl) + " %";
Serial.println(res);
}
uint8_t result2 = modbus2.readInputRegisters(0x01, 2);
if (getResultMsg( & modbus2, result2))
{
Serial.println();
double res_dbl = modbus1.getResponseBuffer(0) / 10;
String res = "Temperature2: " + String(res_dbl) + " C\r\n";
res_dbl = modbus1.getResponseBuffer(1) / 10;
res += "Humidity2: " + String(res_dbl) + " %";
Serial.println(res);
}
lastMillis = currentMillis;
}
}
bool getResultMsg(ModbusMaster *node, uint8_t result)
{
String tmpstr2 = "\r\n";
switch (result)
{
case node->ku8MBSuccess:
return true;
break;
case node->ku8MBIllegalFunction:
tmpstr2 += "Illegal Function";
break;
case node->ku8MBIllegalDataAddress:
tmpstr2 += "Illegal Data Address";
break;
case node->ku8MBIllegalDataValue:
tmpstr2 += "Illegal Data Value";
break;
case node->ku8MBSlaveDeviceFailure:
tmpstr2 += "Slave Device Failure";
break;
case node->ku8MBInvalidSlaveID:
tmpstr2 += "Invalid Slave ID";
break;
case node->ku8MBInvalidFunction:
tmpstr2 += "Invalid Function";
break;
case node->ku8MBResponseTimedOut:
tmpstr2 += "Response Timed Out";
break;
case node->ku8MBInvalidCRC:
tmpstr2 += "Invalid CRC";
break;
default:
tmpstr2 += "Unknown error: " + String(result);
break;
}
Serial.println(tmpstr2);
return false;
}