ESP32 + RS-485(Mudbus RTU) = работает

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Приветствую.

Собственно столкнулся с задачей, надо считать при помощи ESP32 данные из промышленного контроллера OWEN МВ110-8А(использую MAX485).

Пока что не одна библиотека даже не заставила мигнуть индикатор RS-485 на Овене. 

Пробовал считать при помощи Arduino Nano - без проблем, библиотека SimpleModbusMaster справляется на ура, под ESP32 компилится, но переводит состояние ножки RE/DE раз в 20 медленее.

Может кто-то сталкивался с подобными делами, пусть даже на ESP8266?

Чуть позже сделаю фото осциллограм, что происходит.

Araris
Offline
Зарегистрирован: 09.11.2012

OFFTOP: Привіт, друже, рад видеть ))).

По теме сказать нечего, моя ESP32 где-то в дороге ещё...

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Araris - здоровенькі були.

А если конкретнее - к Овену подключен промышленный термодатчик, и стоит задача получить от Овена по интерфейсу RS-485 температуру которую она измеряет.

Вот этот код на ардуино нано работает так как мне нужно:

/*
   The example will use packet1 to read a register from address 0 (the adc ch0 value)
   from the arduino slave (id=1). It will then use this value to adjust the brightness
   of an led on pin 9 using PWM.
   It will then use packet2 to write a register (its own adc ch0 value) to address 1
   on the arduino slave (id=1) adjusting the brightness of an led on pin 9 using PWM.
*/


unsigned long previousMillis = 0;        // will store last time LED was updated

// constants won't change:
const long interval = 1;           // interval at which to blink (milliseconds)


#include <SimpleModbusMaster.h>
//////////////////// Port information ///////////////////
#define baud        9600
#define timeout     1000
#define polling     200 // скорость опроса по модбус
#define retry_count 0
#define TxEnablePin 25   // Tx/Rx пин RS485


int16_t val1 = 0;
int16_t val2 = 0;
int16_t val3 = 0;
int16_t val4 = 0;
int32_t val5 = 0;
int32_t val6 = 0;

float VAL = 0.0;

//Общая сумма доступной памяти на ведущем устройстве, чтобы хранить данные
//Локальный регистр это регистры хранения в памяти мастера тоесть масив регистров с адресами от 0 до 29
//ВСЕГО 30 это важно иначе работать не будет всетаки это ардуино а не плк
#define TOTAL_NO_OF_REGISTERS 10


// Это самый простой способ для создания новых пакетов
// Добавить столько, сколько вы хотите. TOTAL_NO_OF_PACKETS
// обновляется автоматически.
enum
{
  PACKET1,
  //  PACKET2,
  //  PACKET3,
  TOTAL_NO_OF_PACKETS // leave this last entry
};

// Масив пакетов модбус
Packet packets[TOTAL_NO_OF_PACKETS];

// Массив хранения содержимого принятых и передающихся регистров

unsigned int regs[TOTAL_NO_OF_REGISTERS];

/*
  float convert_uint_to_float(unsigned int hw, unsigned int lw) {
        union {
          float f;
          unsigned short i[2];
        } convert_float_uint;
        convert_float_uint.i[0] = lw;
        convert_float_uint.i[1] = hw;
        return (convert_float_uint.f);
        }
*/

void setup()
{
  // инициализируем протокол модбус
  //Valid modbus byte formats are:
  //     SERIAL_8N2: 1 start bit, 8 data bits, 2 stop bits
  //     SERIAL_8E1: 1 start bit, 8 data bits, 1 Even parity bit, 1 stop bit
  //     SERIAL_8O1: 1 start bit, 8 data bits, 1 Odd parity bit, 1 stop bit
  modbus_configure(&Serial, baud, SERIAL_8N1, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs);

  // Настраиваем пакеты
  // Шестой параметр - это индекс ячейки в массиве, размещенном в памяти ведущего устройства, в которую будет
  // помещен результат или из которой будут браться данные для передачи в подчиненное устройство. В нашем коде - это массив reg
  // Пакет,SLAVE адрес,функция модбус,адрес регистра,количесво запрашиваемых регистров,локальный адрес регистра.
  //  modbus_construct(&packets[PACKET1], 100, READ_COIL_STATUS, 48, 1, 0);
  //  modbus_construct(&packets[PACKET2], 100, READ_INPUT_STATUS, 48, 1, 1);
  //  modbus_construct(&packets[PACKET3], 100, READ_HOLDING_REGISTERS, 48, 1, 2);
  modbus_construct(&packets[PACKET1], 16, READ_HOLDING_REGISTERS, 0, 6, 0);
  //  modbus_construct(&packets[PACKET2], 50, READ_HOLDING_REGISTERS, 49, 1, 1);
  //  modbus_construct(&packets[PACKET3], 50, READ_HOLDING_REGISTERS, 50, 1, 2);

  //  modbus_construct(&packets[PACKET2], 14, READ_HOLDING_REGISTERS,    11, 1, 1);
  //  modbus_construct(&packets[PACKET3], 15, READ_HOLDING_REGISTERS,    11, 1, 1);
  //  modbus_construct(&packets[PACKET4], 16, READ_HOLDING_REGISTERS,    11, 1, 1);
  //  modbus_construct(&packets[PACKET5], 17, READ_HOLDING_REGISTERS,    11, 1, 1);

  // Пакет,SLAVE адрес,функция модбус,адрес регистра,количесво запрашиваемых регистров,локальный адрес регистра.
  //modbus_construct(&packets[PACKET3], 1, PRESET_MULTIPLE_REGISTERS, 2, 1, 2);
  //modbus_construct(&packets[PACKET4], 1, PRESET_MULTIPLE_REGISTERS, 3, 1, 3);


}

void loop()
{
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;


    modbus_update();
    //val_f_1 = regs[0]; // чтение данных slave-master (slave адрес 4, в регистр 0) (регистр 0 находится на ардуине)
    val1 = regs[0];
    val2 = regs[1];
    val3 = regs[2];
    val4 = regs[3];
    val5 = regs[4];
    val6 = regs[5];

    VAL = val2 / 10.0;

    // Вывод в сериал
    //  val2 = regs[1]; // чтение данных slave-master (slave адрес 100, регистр 1)
    //  val3 = regs[2]; // чтение данных slave-master (slave адрес 100, регистр 2)
    //  int val =  val1 + val2 + val3;
    //  if (val > 0){digitalWrite(LED1, HIGH);}
    //  else{digitalWrite(LED1, LOW);}//если пришло 255 зажигаем светодиод

    //  if (val1 > 0){digitalWrite(LED1, HIGH);}
    //  if (val2 > 0){digitalWrite(LED1, HIGH);}
    //  if (val3 > 0){digitalWrite(LED1, HIGH);}
    //  else{digitalWrite(LED1, LOW);}//если пришло 255 зажигаем светодиод


    //unsigned int temp2;
    //regs[0] = analogRead(0);       // запись данных master-slave
    //analogWrite(LED, regs[0]>>2);  // чтение данных slave-master (ограничеть количесво бит данных числом 255)
    //regs[0] = 255;   // запись данных master-slave (slave адрес 1, регистр 1)


    //temp2 = regs[3]; // чтение данных slave-master (slave адрес 1, регистр 4)

    //if (temp2 == 255){digitalWrite(LED1, HIGH);}else{digitalWrite(LED1, LOW);}
    Serial.print("val1=");
    Serial.println(val1);

    Serial.print("val2=");
    Serial.println(val2);

    Serial.print("val3=");
    Serial.println(val3);

    Serial.print("val4=");
    Serial.println(val4);

    Serial.print("val5=");
    Serial.println(val5);

    Serial.print("val6=");
    Serial.println(val6);

    Serial.print("Temperature = ");
    Serial.println(VAL);
  }
}

 


Синий луч - изменение состояния на RE/DE ножках микросхемы MAX485, жёлтый - TXD микроконтроллера.

Данный код под ESP32 компилится и работает, но ооочень медленно(обратите внимание на временную равёртку):

Мало того, ещё и переключает режим MAX485 раньше чем нужно:
 

 

Подключился к RS-485 через USB переходник и вижу что в шине нули:

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Из-за особенностей реализации Serial.flush() для ESP32 переключение ноги прием-передача происходит раньше. Естественно, никакого вменяемого пакета клиент не получает, поскольку контрольная сумма не совпадает. Нужно изменять реализацию sendPack, в файле SimpleModbusMaster.cpp

Например вот так

void sendPacket(unsigned char bufferSize)
{
  if (TxEnablePin > 1)
    digitalWrite(TxEnablePin, HIGH);

  previousTimeout = millis();  // <=тут изменить
  
  for (unsigned char i = 0; i < bufferSize; i++)
    Serial.write(frame[i]);

  unsigned int timeout=((10000UL * (bufferSize+3))/(35000000/3_5)); // <=тут изменить
  while(millis()-previousTimeout<timeout) delay(1); // <=тут изменить
    
  if (TxEnablePin > 1)
    digitalWrite(TxEnablePin, LOW);
    
  previousTimeout = millis(); // initialize timeout delay 
}

 

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Да, функция Serial.flush() на ESP32 работает некорректно, это факт.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Пожалуйста.

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Вот этот код чётко работает на Arduino Nano:
 

/*
   The example will use packet1 to read a register from address 0 (the adc ch0 value)
   from the arduino slave (id=1). It will then use this value to adjust the brightness
   of an led on pin 9 using PWM.
   It will then use packet2 to write a register (its own adc ch0 value) to address 1
   on the arduino slave (id=1) adjusting the brightness of an led on pin 9 using PWM.
*/
#include <SimpleModbusMaster.h>
//////////////////// Port information ///////////////////
#define baud        9600
#define timeout     1000
#define polling     200 // скорость опроса по модбус
#define retry_count 0
#define TxEnablePin 13  // Tx/Rx пин RS485


int16_t val1 = 0;
int16_t val2 = 0;
int16_t val3 = 0;
int16_t val4 = 0;
int32_t val5 = 0;
int32_t val6 = 0;

float VAL = 0.0;

unsigned long previousMillis = 0;        // will store last time LED was updated
const long interval = 250;           // interval at which to blink (milliseconds)


//Общая сумма доступной памяти на ведущем устройстве, чтобы хранить данные
//Локальный регистр это регистры хранения в памяти мастера тоесть масив регистров с адресами от 0 до 29
//ВСЕГО 30 это важно иначе работать не будет всетаки это ардуино а не плк
#define TOTAL_NO_OF_REGISTERS 10


// Это самый простой способ для создания новых пакетов
// Добавить столько, сколько вы хотите. TOTAL_NO_OF_PACKETS
// обновляется автоматически.
enum
{
  PACKET1,
  //  PACKET2,
  //  PACKET3,
  TOTAL_NO_OF_PACKETS // leave this last entry
};

// Масив пакетов модбус
Packet packets[TOTAL_NO_OF_PACKETS];

// Массив хранения содержимого принятых и передающихся регистров

unsigned int regs[TOTAL_NO_OF_REGISTERS];

void setup()
{
  // инициализируем протокол модбус
  //Valid modbus byte formats are:
  //     SERIAL_8N2: 1 start bit, 8 data bits, 2 stop bits
  //     SERIAL_8E1: 1 start bit, 8 data bits, 1 Even parity bit, 1 stop bit
  //     SERIAL_8O1: 1 start bit, 8 data bits, 1 Odd parity bit, 1 stop bit
  modbus_configure(&Serial, baud, SERIAL_8N1, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs);

  modbus_construct(&packets[PACKET1], 16, READ_HOLDING_REGISTERS, 0, 6, 0);
  /*
     modbus_construct(&packets[PACKET1], a, READ_HOLDING_REGISTERS, b, c, d);
    a - SLAVE адрес
    b - адрес регистра
    c - колличество запрашиваемых регистров
    d - номер ячейки массива куда запишутся данные
  */
}

void loop() {
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    modbus_update();

    val1 = regs[0];
    val2 = regs[1]; // возвращает температуру в формате 123 - 12.3 градуса
    val3 = regs[2]; 
    val4 = regs[3];
    val5 = regs[4];
    val6 = regs[5];

    VAL = val2 / 10.0;
    // Вывод в сериал

        Serial.print("val1 = ");
        Serial.println(val1);
         Serial.print("val2 = ");
        Serial.println(val2);
         Serial.print("val3 = ");
        Serial.println(val3);
         Serial.print("val4 = ");
        Serial.println(val4);
         Serial.print("val5 = ");
        Serial.println(val5);
         Serial.print("val6 = ");
        Serial.println(val6);
  
    Serial.print("Temoerature = ");
    Serial.println(VAL,1);
  }
}

Ссылка на библиотеку SimpleModbusMasterV2rev2:

https://drive.google.com/file/d/1AQggEa6UnQLptpZGrw3t3ZvFZ_RR3wzH/view?usp=sharing

Вот так работа выглядит на осциллографе:

Жёлтый - TX ардуины, синий - RE/DE ножка микросхемы MAX485.

Вот такое наблюдаю в RS-485 шине при помощи переходника с RS-485 на USB.

Сейчас попробую аналогичным образом всё показать как на ESP32 этот же код крутится.

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

На ESP32 приведённая мною выше библиотека работает следующим образом:

В сериале ESP32 полная тишына, если подключить USB - RS-485 переходник то можно в шине увидить следующее:

 

Как видно одни нули.
На осциллографе следующая картина:

Обратите внимание на время через которее изменяется состояние на ножке RE/DE, это секунд 7-8 примерно, что наталкивает на мысль - библиотека работает значительно медленее чем ра Arduino Nano.

b707
Offline
Зарегистрирован: 26.05.2017

имхо, если на нано все работает - надо сделать из атмеги переходник для общения с ОВЕНом и все дела. Если пойдет библиотека, можно под переходник взять не атмегу328, а что-нибудь попроще, типа тини45 или 85

RizONE
Offline
Зарегистрирован: 22.03.2018

HWman пишет:

Пробовал считать при помощи Arduino Nano - без проблем

Схему подключения не скинете?

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

RizONE пишет:
HWman пишет:
Пробовал считать при помощи Arduino Nano - без проблем
Схему подключения не скинете?

Да, вот:

 

Питаю микросхему от 3.3 В в случае с ESP32, и от 5-ти - Arduino.

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

b707 пишет:

имхо, если на нано все работает - надо сделать из атмеги переходник для общения с ОВЕНом и все дела. Если пойдет библиотека, можно под переходник взять не атмегу328, а что-нибудь попроще, типа тини45 или 85


Я думал над этим... Но хз как-то, городить горы микроконтроллеров это вообще как-то не это... Комильфо. Хотя в данный момент ничего не вижу кроме как сделать так.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

На осциллограмме скорее всего шум. Что на шине МодБас, можно посмотреть ?

В "новой" библиотеке так же используется flush, так почему же она должна работать ? Я же дал вам пример исправления библиотеки, чем он вам не подошел ?

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

brokly пишет:

На осциллограмме скорее всего шум. Что на шине МодБас, можно посмотреть ?

В "новой" библиотеке так же используется flush, так почему же она должна работать ? Я же дал вам пример исправления библиотеки, чем он вам не подошел ?


Нет, не шум это, это шлёт ЕСПшка по мудбас, как я и говорил - в шине мудбас нули одни.

Спасибо большое за код, я подставлял его, всё равно ничего корректно не отрабатывало. Скорее всего где-то есть привязка к частоте микроконтроллера, у ардуины 16, а у есп32 240 МГц.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Не, нет никаких привязок. Вы можете в цикле с поднятой ножкой передача просто отправлять что то типа 0xAA  и глянуть осциллографом что на шинах A и B ?

RizONE
Offline
Зарегистрирован: 22.03.2018

HWman пишет:

Да, вот:

Спасибо. Попробую Ваш скетч прикрутить к своей задаче, а то на библиотеке ModbusRtu.h у меня пока не получается.

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

RizONE пишет:

Спасибо. Попробую Ваш скетч прикрутить к своей задаче, а то на библиотеке ModbusRtu.h у меня пока не получается.


Нет, это не мой скетч.

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

brokly пишет:

Не, нет никаких привязок. Вы можете в цикле с поднятой ножкой передача просто отправлять что то типа 0xAA  и глянуть осциллографом что на шинах A и B ?


Без диференциирования интересует осциллограмма?

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Лучше отдельно A-земля и B-земля... 

Andy
Andy аватар
Offline
Зарегистрирован: 01.01.2016

Цитата:
Питаю микросхему от 3.3 В в случае с ESP32, и от 5-ти - Arduino.
Это MAX485 от 3.3 В запитываешь? А почитать datasheet на неё не пробовал?

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Andy пишет:

Цитата:
Питаю микросхему от 3.3 В в случае с ESP32, и от 5-ти - Arduino.
Это MAX485 от 3.3 В запитываешь? А почитать datasheet на неё не пробовал?


Таки да, рекомендуютпитать от 5V.

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

brokly пишет:

Не, нет никаких привязок. Вы можете в цикле с поднятой ножкой передача просто отправлять что то типа 0xAA  и глянуть осциллографом что на шинах A и B ?


Прошил код из этого поста в ESP32, на линии А относительно земли следующая картина(синий луч - А, жёлтый - RE/DE):

На линии В:

Вот такое с Вашей функцией, A:

На линии B:

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Ножка направления явно рано  сбрасывается

Попробуйте 11 строку в моей правке изменить :

unsigned int timeout=((10000UL * (bufferSize+6))/(35000000/3_5)); // 

А по правильности данных - не понятно. Меня вообще эти осциллограммы удивляют. У вас A и B обязаны быть противофазными. А я вижу обратное. Точно знаю что чудес не бывает. А у вас - чудо.

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Вообщем принял решение выгрузить работу с RS-485 на ардуинку, устроил общение посредтвом UART и AT команд.

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Надо же, нашёл библиотеку, которая завелась на ESP32, обзывается она - SimpleModbusMasterV2rev2_DUE
 

Скетч:

/*
   The example will use packet1 to read a register from address 0 (the adc ch0 value)
   from the arduino slave (id=1). It will then use this value to adjust the brightness
   of an led on pin 9 using PWM.
   It will then use packet2 to write a register (its own adc ch0 value) to address 1
   on the arduino slave (id=1) adjusting the brightness of an led on pin 9 using PWM.
*/

#include <SimpleModbusMaster_DUE.h> 
// https://github.com/jecrespo/simple-modbus

//////////////////// Port information ///////////////////
#define baud        9600
#define timeout     1000
#define polling     200 // скорость опроса по модбус
#define retry_count 0
#define TxEnablePin 13 // Tx/Rx пин RS485


int16_t val1 = 0;
int16_t val2 = 0;
int16_t val3 = 0;
int16_t val4 = 0;
int32_t val5 = 0;
int32_t val6 = 0;

float VAL = 0.0;

unsigned long previousMillis = 0;        // will store last time LED was updated
const long interval = 250;           // interval at which to blink (milliseconds)


//Общая сумма доступной памяти на ведущем устройстве, чтобы хранить данные
//Локальный регистр это регистры хранения в памяти мастера тоесть масив регистров с адресами от 0 до 29
//ВСЕГО 30 это важно иначе работать не будет всетаки это ардуино а не плк
#define TOTAL_NO_OF_REGISTERS 10


// Это самый простой способ для создания новых пакетов
// Добавить столько, сколько вы хотите. TOTAL_NO_OF_PACKETS
// обновляется автоматически.
enum
{
  PACKET1,
  //  PACKET2,
  //  PACKET3,
  TOTAL_NO_OF_PACKETS // leave this last entry
};

// Масив пакетов модбус
Packet packets[TOTAL_NO_OF_PACKETS];

// Массив хранения содержимого принятых и передающихся регистров

unsigned int regs[TOTAL_NO_OF_REGISTERS];

void setup()
{
  // инициализируем протокол модбус
  //Valid modbus byte formats are:
  //     SERIAL_8N2: 1 start bit, 8 data bits, 2 stop bits
  //     SERIAL_8E1: 1 start bit, 8 data bits, 1 Even parity bit, 1 stop bit
  //     SERIAL_8O1: 1 start bit, 8 data bits, 1 Odd parity bit, 1 stop bit
  //modbus_configure(&Serial, baud, SERIAL_8N1, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs);
  modbus_configure(&Serial, baud, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs);

  modbus_construct(&packets[PACKET1], 16, READ_HOLDING_REGISTERS, 0, 6, 0);
  /*
     modbus_construct(&packets[PACKET1], a, READ_HOLDING_REGISTERS, b, c, d);
    a - SLAVE адрес Овена
    b - адрес регистра
    c - колличество запрашиваемых регистров
    d - номер ячейки массива куда запишутся данные
  */
}

void loop() {
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    modbus_update();

    val1 = regs[0]; // смещение точки, например 1 - 123= 12.3
    val2 = regs[1]; // возвращает температуру в формате 123 - 12.3 градуса
    val3 = regs[2];
    val4 = regs[3];
    val5 = regs[4];
    val6 = regs[5];

    VAL = val2 / 10.0;
    // Вывод в сериал


    Serial.print("val1 = ");
    Serial.println(val1);
    Serial.print("val2 = ");
    Serial.println(val2);
    Serial.print("val3 = ");
    Serial.println(val3);
    Serial.print("val4 = ");
    Serial.println(val4);
    Serial.print("val5 = ");
    Serial.println(val5);
    Serial.print("val6 = ");
    Serial.println(val6);


    Serial.print("Temp = ");
    //Serial.println(VAL, 1);
    Serial.println(VAL, val1);
  }
}

Крутость этого решения заключается в том, что тут тебе и 2 ядра, и вай фай, и блютуз, АЦП и ЦАП... Я в своих наработках использую одно ядро под RS-485 а второе под простенький вебсервер, и ещё куча процессорного времени есть в запасе. ESP32 можная штука, это факт.

Ирония заключается в том, что когда вообщем всё сделал уже на ардуине в формате АТ-комманд, случайно наткнулся на эту библиотеку, месяц тупежа по сути.

 

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Предлагаю изменить название темы на "ESP32 + RS-485(Mudbus RTU) = работает"

 

Araris: - изменил )) ]

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Подтверждаю работу на ESP32 библиотеки SimpleModbusSlaveV10_DUE(режим Slave) из поста выше

Прикинулся Овеном по ID и отсылал содержимое нужных регистров.

Схема подключения аналогичная как в этом посте, только питаю MAX485 от 5 В и сигнал подаю на RX/TX еэспэшки.

Вообщем огромный простор для полёта фантазии учитывая колличество памяти в ESP32 ;)

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Ха, поспешил я радоваться. примерно через мину​​т 5 еспшка перестаёт отвечать на запросы по RS-485

И лечится всё только перегрузкой платы...

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

 

Похоже всё решилось, слишком часто теребил функцию  modbus_update(); - прибавил делей на 50 мс и всё заработало.

kaustubhawade
Offline
Зарегистрирован: 26.09.2018

HWman пишет:

 

Похоже всё решилось, слишком часто теребил функцию  modbus_update(); - прибавил делей на 50 мс и всё заработало.

 

Hello HWMan,

can you please share the code and modifications done to run this library on ESP32

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

kaustubhawade пишет:

HWman пишет:

 

Похоже всё решилось, слишком часто теребил функцию  modbus_update(); - прибавил делей на 50 мс и всё заработало.

 

Hello HWMan,

can you please share the code and modifications done to run this library on ESP32

 

#include <SimpleModbusSlave_DUE.h> // github.com/jecrespo/simple-modbus

//Debuging settings:
#define DebugToUART false

#define ReDePin 12 // Re/De Pin

int maxNumRegisters = 6; // 6 regs

int RS485Result[] = {4, 8, 15, 16, 23, 42}; // 6

enum
{
  d0,
  d1,
  d2,
  d3,
  d4,
  d5,
  HOLDING_REGS_SIZE // leave this one
};

unsigned int holdingRegs[HOLDING_REGS_SIZE];



void setup() {
  modbus_configure(&Serial, 9600, 10, ReDePin, HOLDING_REGS_SIZE, holdingRegs);
  modbus_update_comms(9600, 10); //  register addres 10
}

void loop() {
  modbus_update();
  holdingRegs[d0] = RS485Result[0];
  holdingRegs[d1] = RS485Result[1];
  holdingRegs[d2] = RS485Result[2];
  holdingRegs[d3] = RS485Result[3];
  holdingRegs[d4] = RS485Result[4];
  holdingRegs[d5] = RS485Result[5];
#if DebugToUART
  debuging();
#endif
}


void debuging() {
  for (int dbg = 0; dbg < maxNumRegisters; dbg++) {
    Serial.print(" ");
    Serial.print(dbg);
    Serial.print(" = ");
    Serial.print(RS485Result[dbg]);
    Serial.print(",");
  }
  Serial.println("");
}

 

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Set
Offline
Зарегистрирован: 07.02.2017

Здравствуйте господа! Может кто знает? Библиотека SimpleModbusMaster_DUE, опрос, а также запись регистров ведется пачкой, и по циклу при помощи modbus_update();, есть ли в этой библиотеке возможность записи отдельно каждого пакета? Смысл в том, что мне необходимо запись производить только один раз, когда мне это нужно, а не в цикле вмеcте с чтением. И если есть, может примеры найдутся? В сети информацию на эту тему найти не получается.

MOP3E
Offline
Зарегистрирован: 19.09.2021

Приведённая в статье библиотека не работает на передачу. Пробовал её использовать в своём проекте - мастер не может корректно прочитать данные, причём на любой скорости. Читает две переменные по два регистра, а третью уже не читает. На приём всё работает.

Для ESP32 используйте лучше вот это:

emelianov/modbus-esp8266: Наиболее полная библиотека Modbus для Arduino. Библиотека, которая позволяет вашей плате Arduino взаимодействовать по протоколу Modbus, выступая в качестве хозяина, ведомого или обоих. Поддержка сетевого транспорта (Modbus TCP) и последовательной линии/RS-485 (Modbus RTU). Поддержка безопасности Modbus TCP для ESP8266/ESP32. (github.com)

Объектно-ориентированный драйвер MODBUS, поддерживает весь стандарт MODBUS, поддерживает многопоточность и события. Можно несколькими потоками работать с общим портом. Можно несколькими портами обращаться к одним и тем же данным. Рекомендую.

З.Ы. Как убрать простыню текста из ссылки я не понял. Куча каких-то мутных настроек, которые ни на что не влияют.

Axex
Offline
Зарегистрирован: 28.09.2021

...

Axex
Offline
Зарегистрирован: 28.09.2021

MOP3E пишет:

Приведённая в статье библиотека не работает на передачу. Пробовал её использовать в своём проекте - мастер не может корректно прочитать данные, причём на любой скорости. Читает две переменные по два регистра, а третью уже не читает. На приём всё работает.

Для ESP32 используйте лучше вот это:

emelianov/modbus-esp8266: Наиболее полная библиотека Modbus для Arduino. Библиотека, которая позволяет вашей плате Arduino взаимодействовать по протоколу Modbus, выступая в качестве хозяина, ведомого или обоих. Поддержка сетевого транспорта (Modbus TCP) и последовательной линии/RS-485 (Modbus RTU). Поддержка безопасности Modbus TCP для ESP8266/ESP32. (github.com)

Объектно-ориентированный драйвер MODBUS, поддерживает весь стандарт MODBUS, поддерживает многопоточность и события. Можно несколькими потоками работать с общим портом. Можно несколькими портами обращаться к одним и тем же данным. Рекомендую.

З.Ы. Как убрать простыню текста из ссылки я не понял. Куча каких-то мутных настроек, которые ни на что не влияют.

 

Здравствуйте. Вопрос к МОРЗЕ.

Поработал с библиотекой, что вы порекомендовали (https://github.com/emelianov/modbus-esp8266)
Возможно у вас есть рабочий пример кода записи с ЕСП32 в ПЛК, или в ПР200. Поделитесь пожалуйста, не могу справиться с библиотекой.
Вот мой код для считывания с ПР200-го. Он рабочий, а вот записать не получается.

#include <ModbusRTU.h>
#define SLAVE_ID 16
#define FIRST_REG 512
#define REG_COUNT 2
ModbusRTU mb;

bool cb(Modbus::ResultCode event, uint16_t transactionId, void* data) {
  if (event != Modbus::EX_SUCCESS) {
    Serial.print("Request result: 0x");
    Serial.print(event, HEX);
  }
  return true;
}

void setup() {
  Serial.begin(115200);
  Serial2.begin(9600, SERIAL_8N1, 19, 18);
  mb.begin(&Serial2, 17);
  mb.master();
}

void loop() {
  uint16_t res[REG_COUNT];
  if (!mb.slave()) {    
    mb.readHreg(SLAVE_ID, FIRST_REG, res, REG_COUNT, cb);    
    while(mb.slave()) { 
      mb.task();
      delay(10);
    }
    Serial.println(res[0]);
    Serial.println(res[1]);
  }
  delay(1000);
}

 

nik182
Offline
Зарегистрирован: 04.05.2015

Записать может только мастер. Слейв может только ответить. Мастер поднимается в 19 строке, но дальше в коде вызова нет. Нужно заполнить так называемую телеграмму и отправить. В примерах работа с мастером есть. 

MOP3E
Offline
Зарегистрирован: 19.09.2021

А вы вообще в курсе, как MODBUS работает? В сегменте сети MODBUS есть один ведущий (он же мастер) и до 32 ведомых. Ведущий по очереди отправляет ведомым сообщения. Ведомые отвечают только тогда, когда у них запрашивают данные. Сами по себе ведомые никогда ничего не отправляют. Говоря компьютерными терминами, мастер является клиентом, а все ведомые - серверами.

Я делал ведомого, работу с ПЛК через обработчики событий реализовал, работал только с регистрами. Но там можно с любой адресной зоной MODBUS работать, смотрите примеры.

1. Делаете ПЛК мастером, а ардуинку ведомым.

2. Настраиваете у мастера в свойствах скорость обмена, адрес ведомого в сети и опрашиваемые регистры.

Вот пример программы:

#include <ModbusRTU.h>

//стартовый регистр MODBUS
#define HOLDING_REGS_START 0
//число регистров MODBUS
#define HOLDING_REGS_SIZE 24

//настройки порта MODBUS
#define MODBUS_SPEED 19200 //115200
#define MODBUS_CONFIG SERIAL_8N1
#define MODBUS_ID 18
#define MODBUS_RX_PIN 22
#define MODBUS_TX_PIN 23
#define MODBUS_TX_ENABLE_PIN 21

//массив регистров MODBUS
unsigned int holdingRegs[HOLDING_REGS_SIZE];

//порт MODBUS
ModbusRTU Port;

//Обработчик события чтения регистра
uint16_t cbPortRead(TRegister* reg, uint16_t val) {
    if(reg->address.isHreg() && reg->address.address - HOLDING_REGS_START >= 0 && reg->address.address - HOLDING_REGS_START < HOLDING_REGS_SIZE)
    {
        val = holdingRegs[reg->address.address - HOLDING_REGS_START];
    }
    return val;
}

//Обработчик события записи регистра
uint16_t cbPortWrite(TRegister* reg, uint16_t val) {
    if(reg->address.isHreg() && reg->address.address - HOLDING_REGS_START >= 0 && reg->address.address - HOLDING_REGS_START < HOLDING_REGS_SIZE)
    {
        holdingRegs[reg->address.address - HOLDING_REGS_START] = val;
    }
    return val;
}

void setup()
{
    //инициализация порта для отладки
    Serial.begin(9600, SERIAL_8N1);
    
    //инициализация регистров MODBUS
    int i;
    for(i = 0; i < HOLDING_REGS_SIZE; i++)
    {
        holdingRegs[i] = 0;
    }

    //инициализация порта MODBUS
    Serial1.begin(MODBUS_SPEED, SERIAL_8N1, MODBUS_RX_PIN, MODBUS_TX_PIN);
    
    //инициализация сервера MODBUS
    Port.begin(&Serial1, MODBUS_TX_ENABLE_PIN);
    Port.setBaudrate(MODBUS_SPEED);
    Port.server(MODBUS_ID);
    Port.cbEnable(true);
    Port.addHreg(HOLDING_REGS_START, 0, HOLDING_REGS_SIZE);
    Port.onGetHreg(HOLDING_REGS_START, cbPortRead, HOLDING_REGS_SIZE);
    Port.onSetHreg(HOLDING_REGS_START, cbPortWrite, HOLDING_REGS_SIZE);
}

void loop()
{
    //выполнить цикл обмена с ведомым порта MODBUS
    Port.task();
    yield();
}

 

b707
Offline
Зарегистрирован: 26.05.2017

строчки 45-50 лишние....

MOP3E
Offline
Зарегистрирован: 19.09.2021

Угу, давайте не будем чистить регистры перед отладкой чтобы любоваться мусором в них. :)

b707
Offline
Зарегистрирован: 26.05.2017

MOP3E пишет:

Угу, давайте не будем чистить регистры перед отладкой чтобы любоваться мусором в них. :)

так вы не регистры чистите, а локальный массив для них. А в нем и так нули после ресета, читайте описание языка

b707
Offline
Зарегистрирован: 26.05.2017

МОРЗЕ если уж вы хотите, чтобы инициализация массива была явной, просто добавте ее в обьявление массива:

unsigned int holdingRegs[HOLDING_REGS_SIZE] = {0};

хотя повторяю. по стандарту языка в этом нет необходимости

 

nik182
Offline
Зарегистрирован: 04.05.2015

Я очень хорошо знаю как работать c MODBUS. Написал кучу мастеров и слейвов и даже на Delphi PCшном. У меня в реальном режиме времени через него графики шестифазного выпрямителя бегают. Вот только проблема в том, что мастера ничего нельзя попросить. Совсем нельзя. Если сделать мастером ПЛК, то надо его программировать на выдачу данных либо через интервалы времени, либо по внешнему событию. Очень интересно узнать, как у Вас реализован обработчик событий.

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Точно мусором ?
Я все время думал, что при объявлении переменных в них не мусор, а инициализация по умолчанию (0)...

Axex
Offline
Зарегистрирован: 28.09.2021

nik182 пишет:

Записать может только мастер. Слейв может только ответить. Мастер поднимается в 19 строке, но дальше в коде вызова нет. Нужно заполнить так называемую телеграмму и отправить. В примерах работа с мастером есть. 

Я знаю что записать может только мастер
У меня ПР-200 будет точно "slave", и ЕСП32 "master", я хочу слать команды с ЕСП32 на овен.
Я сейчас ничего не отписываю, нашел эту тему (http://arduino.ru/forum/programmirovanie/modbus-rtu), перечитываю ее и пытаюсь разобраться.

В данной ветке обсуждается несколько библиотек
1. (https://github.com/emelianov/modbus-esp8266)
#include <ModbusRTU.h>

2. (https://github.com/smarmengol/Modbus-Master-Slave-for-Arduino)
#include <ModbusRtu.h>

Видите слово RTU / Rtu один раз большие а второй раз маленькие буквы, это две разные библиотеки.

Сейчас пытаюсь разобраться с той, где телеграмм (https://github.com/smarmengol/Modbus-Master-Slave-for-Arduino).

nik182
Offline
Зарегистрирован: 04.05.2015

Для ESP святцы рекомендуют только #11

 

 

Axex
Offline
Зарегистрирован: 28.09.2021

nik182 пишет:

Для ESP святцы рекомендуют только #11

Я и начинал с этой библиотеки. И вот на ней получилось получить данные с ОВЕН (конфигарация ESP- master, овен - slave) #34.

Но тогда вы сказали, что нужно слать через телеграмму, а в данной библиотеке я не нашел как это сделать, поэтому и начал пробовать на такой (https://github.com/smarmengol/Modbus-Master-Slave-for-Arduino)

nik182
Offline
Зарегистрирован: 04.05.2015

В этой библиотеке телеграмму спрятали в библиотеку. Упростили до нельзя. Пример тут. Создание запроса строка 38. Печать ответа слэйва 43-44. 

Axex
Offline
Зарегистрирован: 28.09.2021

nik182 пишет:

В этой библиотеке телеграмму спрятали в библиотеку. Упростили до нельзя. Пример тут. Создание запроса строка 38. Печать ответа слэйва 43-44. 

Получилось. Спасибо.

klofelin
Offline
Зарегистрирован: 26.11.2021

Здравствуйте, люди добрые. Помогите, пожалуйста. Что-то я совсем застрял.
Пример из сообщения #47 не выводит в сериал-порт содержимое регистров.
Ведомое устройство (прога на компе) получает запрос и правильно отвечает на него:

Rx:000000-01 03 00 00 00 02 C4 0B
Tx:000001-01 03 04 00 42 7E B2 FB F2

А ESP32(master) с кодом из примера #47 в сериал выводит только:

Request result: 0xE40
0
Request result: 0xE40
0
...

Как я догадываюсь, обработка ответа уже есть в библиотеке modbus-esp8266 и строки 43-44 должны печать ответ слэйва?
Или ESP32 нужен обработчик ответа (как в примерах для слэйва из той же библиотеки)?

Меня смущает, что контакты DE, RE у MAX485 висят в воздухе.

nik182
Offline
Зарегистрирован: 04.05.2015

А ни кто не обещал что с 485 будет работать. Тут только 485 с автоматическим Rx-Tx пользовать. А результат должен быть 0 если всё правильно, иначе печатает код ошибки - Е4,  что мы и видим. Пробуйте без 485, когда всё получится думайте как 485 прикрутить.