Как заставить библиотеки MQTT и ArduinoModbus работать на одном порту Ethernet?

У меня есть Arduino PLC на базе SAMD51P20 с Ethernet-шилдом на базе WIZnet W5500. Я использую следующие библиотеки:

#include <Ethernet.h>
#include <MQTT.h>
#include <ArduinoModbus.h>

Клиенты MQTT и Modbus-TCP работают нормально по отдельности, но их одновременный запуск создает проблемы для обоих.

Есть ли программное решение этой проблемы, например, использование отдельных сокетов для MQTT и Modbus-TCP. WIZnet W5500 имеет 8 доступных сокетов, но я не вижу API для выбора сокета для клиента MQTT и Modbus-TCP.

Если нет программного обеспечения, есть ли аппаратное решение?


Вот фрагмент кода:

#include <fmt.h>
#include <P1AM.h>
#include <Ethernet.h>
#include <MQTT.h>
#include <ArduinoModbus.h>

using namespace std;

#define HOST_NAME "WIZnet"
uint8_t macPLC[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
uint8_t ipPLC[] = {192, 168, 0, 41};  
uint8_t ipModbus[] = {192, 168, 0, 43};  
char    ipMQTT[] = "192.168.0.42";
int     unitID = 98;

EthernetClient ethernet;
MQTTClient mqtt;
ModbusTCPClient modbus(ethernet);
int n = 0;

void messageReceived(MQTTClient *client, char topic[], char bytes[], int length) {
  if (length == 8) {
    int32_t commandID;
    float   commandArg;

    memcpy(&commandID, &bytes[0], sizeof(commandID));
    memcpy(&commandArg, &bytes[4], sizeof(commandArg));

    Serial.println(fmt::format("{} {}", commandID, commandArg).c_str());
  }
  else
    Serial.println("wrong length!!!");
}


void servoSetup() {
  modbus.coilWrite(unitID, 0x00C, 1);
}


void setup(){ // процедура настройки выполняется один раз:
  P1.configWD(10000, 1);
  P1.startWD();
  Serial.begin(115200);  //инициализация последовательной связи на скорости 115200 бит в секунду

  while (!P1.init()) 
    delay(1); //Ждать, пока модули подпишутся
  
  Ethernet.begin(macPLC, ipPLC);
  mqtt.begin(ipMQTT, ethernet);
  modbus.setTimeout(500);

  if (!modbus.begin(ipModbus, 502)) 
    Serial.println("Failed to start Modbus TCP client!");
  else
    Serial.println("Connected to Modbus TCP client.");

  P1.petWD();

  while (!mqtt.connect(ipMQTT)) {
    Serial.print(".");
    delay(1000);
  }

  Serial.println("\nConnected to MQTT broker.");
  mqtt.subscribe("plc", 2);
  mqtt.onMessageAdvanced(messageReceived);
  servoSetup();

  delay(2000);
}


void loop() {
  P1.petWD();
  Serial.println(mqtt.loop());
  Serial.println(n++);
  int addr = 0x2407;

  if (!modbus.holdingRegisterWrite(unitID, addr, n)) 
    Serial.println(fmt::format("Modbus write failed: {}", modbus.lastError()).c_str());

  delay(1000);
  if (auto result = modbus.holdingRegisterRead(unitID, addr); result < 0) 
    Serial.println(fmt::format("Modbus read failed: {}", modbus.lastError()).c_str());
  else
    Serial.println(fmt::format("holdingRegisterRead: {}", result).c_str());
  delay(1000);
}

В этом случае записи и чтения Modbus-TCP не имеют никакого эффекта, mqtt.loop() возвращает 0 и обратный вызов messageReceived() не вызывается. Если я закомментирую клиент MQTT или Modbus-TCP, код ведет себя так, как и ожидалось.

, 👍0

Обсуждение

Что за проблема? Три строки кода нам ничего не говорят. Вам нужно подробнее рассказать, как вы создаете клиента для каждого соединения?, @hcheung

@hcheung Я опубликовал полный тестовый код., @Paul Jurczak

ModbusTCPClient modbus(ethernet); и mqtt.begin(ipMQTT, ethernet);, вы используете один и тот же клиент Ethernet для двух служб. Создайте отдельный экземпляр клиента для каждой службы., @hcheung

@hcheung Спасибо, что обратили на это внимание., @Paul Jurczak


1 ответ


Лучший ответ:

1

API для установки количества сокетов, используемых в библиотеке Ethernet, отсутствует.

MAX_SOCK_NUM устанавливается в Ethernet.h на основе доступной оперативной памяти.

#if defined(RAMEND) && defined(RAMSTART) && ((RAMEND - RAMSTART) <= 2048)
#define MAX_SOCK_NUM 4
#else
#define MAX_SOCK_NUM 8
#endif

Итак, на SAMD51 у вас есть 8 доступных сокетов. Ваша проблема с MQTT и Modbus TCP в другом месте.

Кстати, W5100 может работать только с 4 сокетами, но это ограничение на время выполнения.


EDIT: В вашем скетче вы используете один и тот же EthernetClient для обеих библиотек. Используйте два отдельных объекта EthernetClient.

EthernetClient ethernet;
EthernetClient modbusClient;
MQTTClient mqtt;
ModbusTCPClient modbus(modbusClient);
,