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