Проблема с использованием PubSubClient, функция "обратный вызов" работает некорректно
У меня есть программа, которая обменивается данными между esp8266 и atmega328 с помощью Serial, esp8266 будет подключаться к серверу MQTT, работающему в локальной сети, и публиковать/подписываться на некоторые темы. В функции обратного вызова для подписки он отправит инструкцию чипу atmega для переключения светодиода (контакт 13). Я использую плату XC4411, которая представляет собой интегрированную плату 2-в-1 с esp и atmega.
Код выглядит следующим образом: На стороне esp:
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include "credentials.h" // здесь я определяю Wi-Fi ssid, psw, mqtt username/psw и т. д.
#include <Arduino.h>
#define SERIAL_BAUD 115200 //убедитесь, что это то же самое в коде atmega, последовательной связи с чипом atmega
#define ESP8266_LED 1
WiFiClient espWifiClient;
PubSubClient espMQTTClient(espWifiClient);
const char* test_topic = "/test/msg01";
void reconnect();
void callback(char *topic, uint8_t *payload, unsigned int length);
void setup_wifi() {
delay(10);
WiFi.begin(MY_SSID, MY_WIFIPSW);
while (WiFi.status() != WL_CONNECTED) delay(500);
}
void setup_mqtt() {
espMQTTClient.setServer(MY_MQTT_SERVER_ADDRESS, MY_MQTT_SERVER_PORT);
reconnect();
espMQTTClient.subscribe(test_topic);
espMQTTClient.setCallback(callback);
}
void reconnect() {
while(! espMQTTClient.connected()) {
if (espMQTTClient.connect(MY_MQTT_CLIENT_ID, MY_MQTT_USER, MY_MQTT_PSW)) {
espMQTTClient.subscribe(test_topic);
}
else delay(500);
}
}
void callback(char *topic, uint8_t *payload, unsigned int length) {
digitalWrite(ESP8266_LED, !digitalRead(ESP8266_LED));
Serial.println("[toggle]");
// espMQTTClient.publish("/test/esp8266", тема, длина);
espMQTTClient.publish("/test/esp8266", payload, length);
if (strcmp(topic, test_topic) == 0) {
if (length >= 0 && (char) payload[0] == '1') {
espMQTTClient.publish("/test/esp8266", "get payload 1");
} else if (length >= 0 && (char) payload[0] == '0') {
espMQTTClient.publish("/test/esp8266", "get payload 0");
} else {
espMQTTClient.publish("/test/esp8266", "unknown payload for topic /test/msg01");
}
}
}
void setup() {
Serial.begin(SERIAL_BAUD);
setup_wifi();
setup_mqtt();
pinMode(ESP8266_LED, OUTPUT);
}
String data_from_arduino = "";
void loop() {
if (! espMQTTClient.connected()) reconnect();
espMQTTClient.loop();
while (Serial.available()) {
char x = Serial.read();
if (x == '\r')
continue;
data_from_arduino += x;
}
if (data_from_arduino != "") {
espMQTTClient.publish("/test/arduino", data_from_arduino.c_str());
data_from_arduino = "";
}
// espMQTTClient.publish("/test/esp8266", "привет из esp"); // эта строка работает нормально, так что проблем с соединением нет
}
На стороне atmega:
#include <Arduino.h>
#define SERIAL_BAUD 115200
#define LED_PIN 13
String receivedCommand = "";
bool dataIn = false;
void setup()
{
Serial.begin(SERIAL_BAUD); //то же, что и скорость ESP
pinMode(LED_PIN, OUTPUT);
}
String msg_from_esp8266 = "";
bool start_receiving_msg = false;
void loop()
{
while (Serial.available())
{
char c = Serial.read();
if (c == '[')
{
start_receiving_msg = true;
msg_from_esp8266 = "";
} else if (start_receiving_msg && c != ']') {
msg_from_esp8266 += c;
} else if (start_receiving_msg && c == ']') {
start_receiving_msg = false;
if (msg_from_esp8266 == "toggle") {
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
Serial.println("arduino toggle led");
}
}
}
delay(10);
}
В основном, когда вы публикуете что-либо в теме /test/msg01 вручную, он попытается переключить встроенный индикатор esp8266. затем esp8266 опубликует сообщение в /test/esp8266 с точной полезной нагрузкой, которую вы публикуете в /test/msg01. Кроме того, esp8266 также отправит последовательное сообщение "[toggle]" к атмеге. На стороне atmega он проверит, получает ли он "[toggle]" и переключите его встроенный светодиод, затем он отправит сообщение через последовательный порт. на стороне esp в цикле он получит это сообщение от atmega и опубликует его в теме /test/arduino. Эта программа предназначена для проверки связи между другими клиентскими устройствами mqtt и esp8266, а также (связи между другими клиентскими устройствами mqtt) и atmega через esp8266 через последовательный порт. Код в if (strcmp(topic, test_topic) == 0) — это просто дополнительный тестовый код.
Как правило, вы должны ожидать такого поведения: как только вы опубликуете любое сообщение в теме /test/msg01, вы увидите, что это же сообщение ретранслируется в тему /test/esp8266. и esp8266, и atmega будут включать и выключать свой светодиод. И сообщение "arduino toggle led" будет опубликован в теме /test/arduino.
Однако ни один из индикаторов не переключается. И нет сообщения от /test/arduino. в теме /test/esp8266 вместо исходного сообщения я увидел мусорный текст.
Я уже устранил неполадки с подключением, конфигурацией платы и проверил, не сломан ли светодиод. Все они хороши, и я почти уверен, что проблема заключается в том, чтобы сделать в "обратном вызове" функция.
Вот скриншот с сообщением mqtt:
Что я опубликовал в /test/msg01:
Что я получаю от /test/esp8266
И нет проблем с сообщением в /test/msg01, так как я также подписался на тему и получил то же сообщение, что и опубликовал.
Я не получил ни одного сообщения из темы "/test/arduino", поэтому похоже, что серийное сообщение не дошло до atmega.
И снова нет проблем с конфигурацией платы XC4411, так как я включаю кнопки 1, 2 после того, как перепрошил два чипа, и код скомпилировался и был успешно загружен на оба чипа.
Похоже, функция обратного вызова только что испортила мое сообщение, а также каким-то образом пропустила и проигнорировала строку, которая отправляет сообщение о переключении на arduino, и строку, которая переключает светодиод esp8266.
Я очень новичок в этой теме, обсуждаемой в этом вопросе в целом, поэтому, возможно, есть какой-то механизм, о котором я не знаю. Заранее спасибо за ответы.
@J. C. Stark, 👍1
1 ответ
Из документации библиотеки PubSubClient:
Внутренне клиент использует один и тот же буфер как для входящих, так и для исходящих сообщений. После возврата из функции обратного вызова или при вызове публикации или подписки из функции обратного вызова значения темы и полезной нагрузки, переданные в функцию, будут перезаписаны
- Последовательная связь ESP8266 с ATMega328P
- ESP8266 не может подключиться к брокеру MQTT
- ошибка 404 в запросе HTTP GET с Arduino Uno и ESP8266 с использованием AT-команд
- Отправка значений из arduino uno в wemos d1 r1
- Разбор сообщений с сервера MQTT
- Мониторинг двух скоростей передачи
- Arduino + ESP8266 для отправки сообщений MQTT
- Я закирпичил свой Arduino Uno? Проблемы с загрузкой скетчей на плату
Привет, это имеет смысл. Спасибо. Любые идеи о том, почему светодиод не включается и не выключается?, @J. C. Stark
Я не знаком с платой XC4411, но подозреваю, что светодиод ESP8266 не находится на GPIO 1, и в любом случае может быть невозможно одновременно использовать последовательный порт и встроенный светодиод., @Bra1n