PubSubClient зависает на ESP32C3

Возможно, я сделал некоторые вещи неправильно, но это то, что у меня есть для публикации изображения, которое я прочитал с последовательной камеры, в брокере MQTT:

void capturePicture() {
  if (!cam.takePicture())
    client.publish("info", "Failed to snap!");
  else
    client.publish("info", "Picture taken!");

  char length[21];
  uint32_t jpglen = cam.frameLength();

  sprintf(length, "buffer length: %d", jpglen);
  client.publish("info", length);

  byte wCount = 0;  // Для подсчета количества записей
  client.publish("info", "transfer about to begin");
  client.beginPublish("picture", jpglen, false);
  client.publish("info", "after beginPublish");

  while (jpglen > 0) {
    uint8_t* buffer;
    uint8_t bytesToRead = min((uint32_t)16, jpglen);  // изменяем 32 на 64 для ускорения, но может работать не со всеми настройками!
    buffer = cam.readPicture(bytesToRead);

    client.write(buffer, bytesToRead);

    jpglen -= bytesToRead;
  }

  client.endPublish();
  client.publish("info", "transfer should have ended;");
}

Я знаю, что доска работает, я подключен к брокеру и могу получать сообщения. В цикле я считываю данные с нескольких датчиков и помещаю строку в тему

void loop() {
  delay(1000);

  if (count == 5) capturePicture();

  if (!client.connected()) {
    reconnect();
  }

  int multi = analogRead(A2);
  int odor = analogRead(A1);
  int voc = analogRead(A0);

  char name[21];
  sprintf(name, "%d;%d;%d", multi, odor, voc);

  bool pubScss = client.publish("measurement", name);

  count++;
  client.loop();
}

и все работает нормально.

Что происходит, когда выполняется capturePicture():

Picture taken!
buffer length: 49048
transfer about to begin

--это то, что я вижу на стороне подписчика темы, затем он зависает примерно на 10 секунд, а затем продолжает отправку сообщений из цикла.

Похоже, что он никогда не проходит мимо вызова client.beginPublish("picture", jpglen, false);. Я устанавливаю размер буфера PubSubClient равным client.setBufferSize(10235);

Что здесь может происходить? Что-то, что я могу проверить??

Спасибо за советы!

, 👍0


1 ответ


0

Краткая форма приведенного ниже примера выглядит примерно так: "После вызова .beginPublish() вызовите .endPublish() перед вызовом любого из них. .publish() или .beginPublish().


У нас нет вашего фактического кода. И это усложняет ситуацию. Однако это источник проблем в том, что вы показали:

  client.beginPublish("picture", jpglen, false);
  client.publish("info", "after beginPublish");
  ...
  client.endPublish();

Похоже, что вы планировали вторую публикацию, чтобы помочь вам отладить первоначальную проблему. Но опять же, у нас нет вашего кода. Я не знаю, есть ли это (или что-то подобное) в вашем оригинале. В любом случае, как средство отладки это бесполезно.


Функция .beginPublish()/[.write()-type]/.endPublish() создает сообщение в MQTT протокол при совершении этих вызовов. Вставляя вызов .publish() между ними, вы нарушаете связь. Короче говоря, вы частично встраиваете сообщение MQTT в другое сообщение MQTT.

Что-то вроде высказывания

HowHello. are you today?

вместо

Hello. How are you today?

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


Я протестировал несколько вариантов этого кода:

//#define CAUSE_TROUBLE

void one_shot_message() {
  client.publish(MQTT_TOPIC, "[hi]");
}

void do_test() {
  static const char part1[] = "<Hello";
  static const char part2[] = " World>";
  static const char both_parts_length = // просто очень явно
      (sizeof part1 - 1) // часть1 без нулевого терминатора
    + (sizeof part2 - 1);// part2 без нулевого терминатора
                       // .publish, принимающий строки, не завершается нулем, поэтому это не так.

#ifndef CAUSE_TROUBLE
  one_shot_message(); // хорошо до начала публикации или после окончания публикации
#endif

  client.beginPublish(MQTT_TOPIC, both_parts_length, false);
#ifdef CAUSE_TROUBLE
  one_shot_message(); // не все в порядке между BeginPublish и EndPublish
#endif
  client.print(part1);
  // протестировано с вызовом one_shot_message() (неподходящее местоположение)
  client.print(part2);
  client.endPublish();
}

В случае #define CAUSE_TROUBLE (по крайней мере, с сервером, который я использую, и клиентом, который я написал на ПК), приходит одно сообщение искаженного вида. а затем клиент ESP32 повторно подключается к серверу. Вероятно сервер закрывает соединение. Это происходит с циклом около 6 секунд. Поврежденное сообщение имеет правильную тему, что имеет некоторый смысл, поскольку тема записывается как часть .beginPublish() до того, как появится возможность испортить сообщение. Возможно, это не совсем то, что вы видите, но у меня нет веской причины думать, что я должен видеть именно то, что видите вы; не тот же самый точный код, клиент, сервер. В любом случае это похоже.


Итак, вы можете использовать client.publish("info", "before BeginPublish"); или client.publish("info", "after endPublish"); в этих местах, но не между вызовами Begin и End. Если бы вы хотели попробовать сделать что-то подобное, вы, вероятно, могли бы сделать это, создав второй PubSubClient, чтобы вы могли отправлять эти меньшие сообщения в отдельный поток протокола, находясь в процессе создания/отправки. более крупное сообщение в основном потоке. Однако сообщения могут приходить не по порядку либо из-за особенностей работы библиотеки, либо из-за синхронизации в сети. Поэтому, если вы хотите это сделать, вы также можете отправлять значение счетчика с каждым сообщением, чтобы вы могли восстановить временную шкалу в отправляющем коде.

Отмечено относительно sprintf и snprintf:

  char name[21];
  snprintf(name, sizeof name, "%d;%d;%d", multi, odor, voc);

sprintf() — это то, что существует для поддержки очень старого кода или очень старых компиляторов. В противном случае не существует варианта использования sprintf(), который не был бы лучше использования snprintf()

,