Проблема с использованием 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.

Я очень новичок в этой теме, обсуждаемой в этом вопросе в целом, поэтому, возможно, есть какой-то механизм, о котором я не знаю. Заранее спасибо за ответы.

, 👍1


1 ответ


2

Из документации библиотеки PubSubClient:

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

,

Привет, это имеет смысл. Спасибо. Любые идеи о том, почему светодиод не включается и не выключается?, @J. C. Stark

Я не знаком с платой XC4411, но подозреваю, что светодиод ESP8266 не находится на GPIO 1, и в любом случае может быть невозможно одновременно использовать последовательный порт и встроенный светодиод., @Bra1n