Работа с RS485 интерфейсом с ESP32

В прошлой статье я привел пример работы с RS485 модулем XY-485 при работе с ESP8266. Рассмотрю как работать с RS485 модулями с микроконтроллера ESP32.

Тестовое «железо»:

Лучше приобретать 38-ми пиновую плату ESP32, а не 30-ти. Разница в цене около 100 руб, но выводов побольше, а они лишними не бывают. Тем более, что габариты платы остаются такими-же. Отличие платы в том, что ножки на гребенке идут до самого низа платы.


30-ти ножечная плата ESP32
38-ми ножечная плата 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 functionGPIO
UART 0 TX1
UART 0 RX3
UART 1 TX10
UART 1 RX9
UART 2 TX17
UART 2 RX16

Если посмотреть pinout на 30-ти pin-овую ESP32 DevKit V1, то можно отметить, что порт UART1 не выведен ножки и доступен только в 38 пиновой плате.

ESP32 DevKit V1 pinout

Однако, в ESP32 можно перенести последовательный порт UART1 на другие GPIO при инициализации порта.

Схема соединения ESP32 c RS485

В ESP32 используем аппаратные UART-ы. В прошлой статье я уже упоминал о том, что

маркировка RX/TX на платах XY-485 и XY-017 перепутана местами!!!

ESP32XY-485
Rx (PIN RX2, GPIO16)RXD
Tx (PIN TX2, GPIO17)TXD
VCC (Блок питания +3,3V)
GNDGND
XY-017
Rx (PIN RX1, GPIO15) RXD
Tx (PIN TX1, GPIO4) TXD
VCC (Блок питания +3,3V)
GNDGND

Блок питания для модулей RS485 ДОЛЖЕН БЫТЬ на 3,3V,
иначе можно сжечь GPIO ESP32!!! 
Либо используйте конвертер TTL  уровней

И схема подключения модулей XY-485/XY-017 к двум термодатчикам по RS485:

Термодатчик 1XY-485
A+A+
B-B-
GNDGND
GND (Блок питания -)
VCC (Блок питания +5V)
Термодатчик 2XY-017
A+A+
B-B-
GNDGND
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;
}
Spread the love
Запись опубликована в рубрике IT рецепты с метками , , , , , . Добавьте в закладки постоянную ссылку.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *