• Уважаемые посетители сайта esp8266.ru!
    Мы отказались от размещения рекламы на страницах форума для большего комфорта пользователей.
    Вы можете оказать посильную поддержку администрации форума. Данные средства пойдут на оплату услуг облачных провайдеров для сайта esp8266.ru
  • Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

ESP8266 как Modbus Master

Alexandr.Seleznev

New member
Всем привет.
Подключаю esp8266 NodeMCU как мастера к слэйву. При помощи библиотеки хочу прочитать значения регистров слэйва. Не понимаю почему не выводит значения регистров.
Настройки подключения(скорость, четность, стоп биты) подключены правильно. ESP подключаю преобразователем UART -> RS485 на MAX485.
Слэйвом выступает счетчик энергии map3et.

C++:
/*
  Modbus Library for Arduino Example - Modbus RTU Client
  Read Holding Registers from Modbus RTU Server in blocking way
  ESP8266 Example
 
  (c)2020 Alexander Emelianov (a.m.emelianov@gmail.com)
  https://github.com/emelianov/modbus-esp8266
*/

#include <ModbusRTU.h>
#include <SoftwareSerial.h>

#define SLAVE_ID 39
#define FIRST_REG 128
#define REG_COUNT 2
// MODBUSRTU_REDE_SWITCH_US 1200
#define DE_RE 12 //D6  For MAX485 chip
#define RX 3      // RX Node MCU
#define TX 1      // TX Node MCU

SoftwareSerial S(RX, TX);
ModbusRTU mb;

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

void setup() {
  Serial.begin(115200);
  S.begin(9600, SWSERIAL_8N2, RX, TX);
  mb.begin(&S, DE_RE);
  mb.master();
}

//uint16_t response = 0;
uint16_t regs[REG_COUNT];

void loop() {
  // uint16_t regs;
  if (!mb.slave()) {    // Check if no transaction in progress
    mb.readHreg(SLAVE_ID, FIRST_REG, regs, REG_COUNT, cb); // Send Read Hreg from Modbus Server
    while(mb.slave()) { // Check if transaction is active
      mb.task();
      yield();
      //delay(20);
    }
    //delay(1000);
    //Serial.print("response = "); Serial.println(response);
    Serial.print("regs = "); Serial.println(regs[0]);
  }
  delay(1000);
  //mb.task();
}
 

Вложения

pvvx

Активный участник сообщества
1. Следует проверить полярность подключения проводков. На шине RS-485 два провода, один типа минус, другой плюс.
Желательно осциллографом или другим логгером, приемником RS-485 и т.д., сразу убедиться в правильной работе преобразователя и используемой Ардуино-детской библиотеке для игрушек.
2. Настроить Modbus в счетчике приложенной к нему программой.
 

pvvx

Активный участник сообщества
Далее, если будут получены хоть каткие-то результаты, требуется произвести анализ кода используемой библиотеки на соблюдение стандартов Modbus RTU по задержкам и переключениям направления шины, отработке межсимвольных пауз и интервалов между фреймами при включенном WiFi.
Т.к. они не будут соблюдаться, поиграться и выкинуть данное решение, заменив чем-то не игрушечным.
 

pvvx

Активный участник сообщества
Подключаю esp8266 NodeMCU как мастера к слэйву. При помощи библиотеки хочу прочитать значения регистров слэйва.
В указанной вами библиотеке не найдена установка регистра тишины на линии у UART ESP8266 (для вычисления пауз по спецификации Modbus RTU).
Так же не найдены никакие таймеры для анализа пауз между транзакциями в функциях приема и между приемом-передачей.
Вам придется это вписать самому или надеяться что имеющееся как-то иногда будет работать...
 

pvvx

Активный участник сообщества
У map3et мне не удалось найти полную спецификацию по работе устройства в Modbus RTU. А так-же проверок или сертификации шины Modbus RTU RS-485.

А для работы с устройством на шине Modbus RTU требуется значение максимального времени до ответа устройством.

Иначе послав запрос, придется ждать вечно и вся шина будет занята в вечном ожидании ответа? А если повторить запрос, через придуманную на обум паузу, то имеется большая вероятность вхождения в вечный цикл коллизии:

Запросили, ждем, пауза вышла, производим следующий запрос, а устройство в это время отвечает на прошлый. И так бесконечно, надеясь что драйверы шины RS-485 не перегреются работая друг на друга и другое устройство не словит в этом бардаке похожую для неё команду, т.к. CRC16 не панацея.

По этому в документации должно быть объявлена максимальная пауза до ответа устройством. И если пауза выходит, а ответ у устройства ещё не готов, то посылается короткий стандартный Modbus RTU ответ c идентификатором “Устройство занято”.
 

Alexandr.Seleznev

New member
Т.к. они не будут соблюдаться, поиграться и выкинуть данное решение, заменив чем-то не игрушечным.
Можете порекомендовать что-то не игрушечное? Вы имеете в виду заменить ESP или заменить библиотеку? Или возможно дописать настройки в текущую библиотеку?
И так бесконечно, надеясь что драйверы шины RS-485 не перегреются работая друг на друга и другое устройство не словит в этом бардаке похожую для неё команду, т.к. CRC16 не панацея.
Уже перегревалось.

Спасибо большое за столь развернутый ответ, Вы очень помогли!
 

pvvx

Активный участник сообщества
Это всего предупреждение. Как говорится - предупрежден значит вооружен.
У вас же не сертификация изделия, а игрушка на Arduino - для быстрого анализа возможностей и продумывания алгоритмов, демонстрации концепта - сгодится.
А для пром. применения или применения в других сферах, типа учета - не годится.
 

pvvx

Активный участник сообщества
Можете порекомендовать что-то не игрушечное?
Это сложно и обычно дорого. Не думаю что вам это надо.
Вы имеете в виду заменить ESP или заменить библиотеку? Или возможно дописать настройки в текущую библиотеку?
Там как не меняй, всё равно будут недочеты.
Основные, которые придется учесть - использование С++. Т.к. чип не имеет MMU, то придется отказаться от типа памяти "Heap" - динамического распределения кучи. Только статические буфера. Иначе дефрагментация и падение системы, перезагрузка.
А при использовании статических буферов во всех драйверах, включая WiFi - у вас не хватит памяти для поддержки по стандарту даже простого TCP сокета.
Придется анализировать все вызовы функций с запросом памяти и строить так, чтобы не возникло фрагментации. Для этого обычно сразу отказываются от C++ и строят всё на СИ.
И т.д.... типа перезагрузки всей системы для очистки памяти после выполнения пары функций...
 

pvvx

Активный участник сообщества
Другой метод, если используется динамическая память и нет аппаратного MMU - иметь объем памяти в несколько раз превышающий выполняемые задачи. Но это не имеет смысла, т.к. тогда проще распределить память статически.
По этому пример в Arduino с миганием светодиода не падает. А всё что посложнее не имеет никакой гарантии в стабильности работы.
 

pvvx

Активный участник сообщества
Вместо HEAP часто используют стек. Тогда каждый последующий вложенный вызов в некой функции не вызывает дефрагментации, т.к. когда произойдет выход из начальной функций, то память будет освобождена. Но это требует изменения всех алгоритмов в системе и распределения на поочередный вызов таких функций. Т.е. никакой мультизадачности.
Соответственно С++ и тут не нужен. Если только аккуратно применять его синтаксис, без использования памяти-зависимых функций. Но потребует большего внимания.
В итоге и рекомендуют использовать ESP-IDF с чиcтыми СИ функциями....
 

pvvx

Активный участник сообщества
Для ухода от проблем дефрагментации памяти много-много лет назад был придуман и создан MMU. Он есть во всех архитектурах современных процессоров и многих микроконтроллерах. Т.к. MMU транслирует память для CPU мелкими блоками, то всегда есть возможность набрать большой непрерывный по адресации для CPU фрагмент из кусочков, собранных из малых фрагментиков в памяти находящихся по разным физическим адресам. И как таковая дефрагментация становится невозможна. Это и использовано в С++.
 

pvvx

Активный участник сообщества
Начните c переписывания стека TCP/IP в ESP на соответствие стандарту, чтобы работать хотя-бы с одним TCP сокетом :)

Тогда вам придется содержать в памяти структуры TCP с состоянием TIME_WAIT. И каждые несколько ms сравнивать – вышел или нет таймаут у конкретной структуры в 4 минуты (это нормы TCP/IP в RFC).

За 4-ре минуты при имеющейся скорости WiFi у ESP таких структур может быть создано к 10 тысячам за секунду. Соответственно требуется объем памяти для их содержания на 4 минуты – это 2400000 таких структур. В Lwip одна такая структура использует от сотни байт. Далее считать необходимый объем памяти для поддержки TCP/IP по стандарту для ESP бессмысленно.

Это только одна мелкая частичка стандарта TCP/IP… вырезанная в Arduino и приводящая к приколам, а так-же глюкам у устройств типа роутеров и т.д. по всему пути соединений.
 

pvvx

Активный участник сообщества
Какие могут быть методы уменьшить вероятность глюков в Arduino ESP.

Для TCP/IP соединений необходимо запретить открытие соединений чаще раз в несколько секунд.

Не использовать соединений с эксплорерами, т.к. при открытии страницы они открывают сразу от 4-х соединений для ускорения, а ESP не может обслужить более десятка при подготовке и переписывании кода ESP-IDF. Любой тест сервера HTTP на ESP показывает ошибки при множественных соединениях.

Так же запретить переподключение к внешней WiFi АP с паузой менее 4-х минут.

Этим частично уменьшится вероятность возникновения TIME_WAIT, но не спасет от других глюков.
 
Сверху Снизу