Esp8266 callback нужна помощь

Тема в разделе "ESP8266, ESP32", создана пользователем admitry, 15 дек 2020.

Метки:
  1. admitry

    admitry Нуб

    Здравствуйте!
    Пытыюсь настроить чтение топика с ESP8266. Прошу пояснить механику работы обратного вызова (callback) в протоколе MQTT
    Как я это понимаю: вот мы подписываемся на топики и тем самым сообщаем серверу, какие обновления топиков мы хотим получать.
    Сервер их присылает, в функции callback мы прописываем как реагировать на каждую пересылку.
    По факту так получается, что callback вызывается каждый проход главного цикла. Если при этом есть новые данные в топике, он их читает, а если нет, он всё равно читает!
    Т.е. функция выолняется, но видит нули

    С чем это может быть связано?
    Можете пояснить на пальцах?
    Спасибо!
     
  2. admitry

    admitry Нуб

    Наверно лучше будет попросить помощь более конкретно:

    На сервере у нас есть топик. В этот топик мы присылаем с мобильного приложения mqtt данные, например число 23,5. Далее мне необходимо чтобы это число попало в ESP8266. Мы сделали подписку на наш топик и с помощью callback пытаемся получить в переменную в ESP8266 наши данные (число 23,5) из топика на сервере. Но у нас ничего не получается. Данные прилетают в ESP8266 только 1 раз нормально, а потом начинают прилетать нули не понятно откуда, а когда мы отправляем данные повторно, то они приходят с очень большой задержкой....несколько минут.
    Сервер работает нормально, проблема с прошивкой callback ESP8266.
    Дайте пожалуйста образец кода по которому мы сможем получать данные из топика, и чтобы они приходили без задержки и не было ложных приходов нулей. В результате нам надо получить вот что:
    На мобильном приложении mqtt мы вводим параметр, этот параметр через сервер приходит в переменную в ESP8266 и остается там до тех пор пока мы не введем новый параметр.
    Помогите пожалуйста!!!
     
  3. SergeiL

    SergeiL Оракул Модератор

    Выкладывайте код, который не работает, а то так обсуждать сложно.
    Вместе с библиотеками обычно есть примеры, вы их смотрели?
    Вот один из примеров:
    Код (C++):
    /*
    Basic ESP8266 MQTT example

    This sketch demonstrates the capabilities of the pubsub library in combination
    with the ESP8266 board/library.

    It connects to an MQTT server then:
      - publishes "hello world" to the topic "outTopic" every two seconds
      - subscribes to the topic "inTopic", printing out any messages
        it receives. NB - it assumes the received payloads are strings not binary
      - If the first character of the topic "inTopic" is an 1, switch ON the ESP Led,
        else switch it off

    It will reconnect to the server if the connection is lost using a blocking
    reconnect function. See the 'mqtt_reconnect_nonblocking' example for how to
    achieve the same result without blocking the main loop.

    To install the ESP8266 board, (using Arduino 1.6.4+):
      - Add the following 3rd party board manager under "File -> Preferences -> Additional Boards Manager URLs":
           http://arduino.esp8266.com/stable/package_esp8266com_index.json
      - Open the "Tools -> Board -> Board Manager" and click install for the ESP8266"
      - Select your ESP8266 in "Tools -> Board"

    */


    #include <ESP8266WiFi.h>
    #include <PubSubClient.h>

    // Update these with values suitable for your network.

    const char* ssid = "........";
    const char* password = "........";
    const char* mqtt_server = "broker.mqtt-dashboard.com";

    WiFiClient espClient;
    PubSubClient client(espClient);
    long lastMsg = 0;
    char msg[50];
    int value = 0;

    void setup_wifi() {

      delay(10);
      // We start by connecting to a WiFi network
      Serial.println();
      Serial.print("Connecting to ");
      Serial.println(ssid);

      WiFi.begin(ssid, password);

      while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
      }

      randomSeed(micros());

      Serial.println("");
      Serial.println("WiFi connected");
      Serial.println("IP address: ");
      Serial.println(WiFi.localIP());
    }

    void callback(char* topic, byte* payload, unsigned int length) {
      Serial.print("Message arrived [");
      Serial.print(topic);
      Serial.print("] ");
      for (int i = 0; i < length; i++) {
        Serial.print((char)payload[i]);
      }
      Serial.println();

      // Switch on the LED if an 1 was received as first character
      if ((char)payload[0] == '1') {
        digitalWrite(BUILTIN_LED, LOW);   // Turn the LED on (Note that LOW is the voltage level
        // but actually the LED is on; this is because
        // it is active low on the ESP-01)
      } else {
        digitalWrite(BUILTIN_LED, HIGH);  // Turn the LED off by making the voltage HIGH
      }

    }

    void reconnect() {
      // Loop until we're reconnected
      while (!client.connected()) {
        Serial.print("Attempting MQTT connection...");
        // Create a random client ID
        String clientId = "ESP8266Client-";
        clientId += String(random(0xffff), HEX);
        // Attempt to connect
        if (client.connect(clientId.c_str())) {
          Serial.println("connected");
          // Once connected, publish an announcement...
          client.publish("outTopic", "hello world");
          // ... and resubscribe
          client.subscribe("inTopic");
        } else {
          Serial.print("failed, rc=");
          Serial.print(client.state());
          Serial.println(" try again in 5 seconds");
          // Wait 5 seconds before retrying
          delay(5000);
        }
      }
    }

    void setup() {
      pinMode(BUILTIN_LED, OUTPUT);     // Initialize the BUILTIN_LED pin as an output
      Serial.begin(115200);
      setup_wifi();
      client.setServer(mqtt_server, 1883);
      client.setCallback(callback);
    }

    void loop() {

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

      long now = millis();
      if (now - lastMsg > 2000) {
        lastMsg = now;
        ++value;
        snprintf (msg, 50, "hello world #%ld", value);
        Serial.print("Publish message: ");
        Serial.println(msg);
        client.publish("outTopic", msg);
      }
    }
     
  4. admitry

    admitry Нуб

    Дело в том, что я на этом примере свой код и развивал:


    Код (C++):
    #include <ESP8266WiFi.h>
    #include <PubSubClient.h>

    #define wifi_ssid "*******"
    #define wifi_password "*********"

    #define mqtt_server "*************"
    #define mqtt_port 1883
    #define mqtt_user "**********"
    #define mqtt_password "***********"

    #define in_topic  "light/in"
    #define out_topic "light/out"
    #define out_topic1 "light/temp1"
    #define out_topic2 "light/temp2"
    #define out_topic3 "light/temp3"
    #define out_topic4 "light/temp4"
    #define out_topic5 "light/current"


    #define in_led 2

    // Replace by 2 if you aren't enable to use Serial Monitor... Don't forget to Rewire R1 to GPIO2!
    #define in_led 0

    uint a,  b,  c,  d,  e;
    int  a1, b1, c1, d1;


    uint temp, temp2, temp3, current;




    WiFiClient espClient;
    PubSubClient client;

    void setup() {
      Serial.begin(19200);
      setup_wifi();
      client.setClient(espClient);
      client.setServer(mqtt_server, mqtt_port);
      client.setCallback(callback);
      client.subscribe(in_topic);
     
      // initialize digital pin LED_BUILTIN as an output.
      pinMode(in_led, OUTPUT);
      digitalWrite(in_led, HIGH);

      pinMode(0, OUTPUT);
      digitalWrite(0, HIGH);

     
    }

    void setup_wifi() {
      delay(10);
      // We start by connecting to a WiFi network
      Serial.println();
      Serial.print("Connecting to ");
      Serial.println(wifi_ssid);

      WiFi.begin(wifi_ssid, wifi_password);

      while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
      }

      Serial.println("");
      Serial.println("WiFi connected");
      Serial.println("IP address: ");
      Serial.println(WiFi.localIP());
    }

    void reconnect() {
      // Loop until we're reconnected
      while (!client.connected()) {
        Serial.print("Attempting MQTT connection...");
        // Attempt to connect
        // If you do not want to use a username and password, change next line to
        if (client.connect("ESP8266Client")) {
        //if (client.connect("ESP8266Client", mqtt_user, mqtt_password)) {
          Serial.println("connected");
         } else {
          Serial.print("failed, rc=");
          Serial.print(client.state());
          Serial.println(" try again in 5 seconds");
          // Wait 5 seconds before retrying
          delay(5000);
          client.subscribe(in_topic); //это мы добавили по рекомендации автора библиотеки
        }
      }
    }



    void callback(char* topic, byte* payload, unsigned int length) {

    int input;
    input=0;

    a= payload [0]-48;
    b= payload [1]-48;
    c= payload [2]-48;
    d= payload [3]-48;
    e= payload [4]-48;
    //if (length>1)
    // {
    if( strcmp(topic, "light/in") == 0 )
      {
      digitalWrite(0, LOW);
      delay(100);  
      digitalWrite(0, HIGH);
     


    //  for (int i = 0; i < length; i++) {
    //  char receivedChar = (char)payload[i] -48;
     
      switch (length) {
    case 0:
    digitalWrite(0, LOW);
      digitalWrite(0, HIGH);
    break;
    case 1:  input = a ;
    break;
    case 2:  input = (a * 10) +b ;
    break;
    case 3:  input = (a * 100) + (b*10) + c  ;
    break;
    case 4:  input = (a * 1000) + (b*100) + (c*10) + (d) ;
    break;
    case 5:  input = (a * 10000) + (b*1000) + (c*100) + (d*10)+e  ;            //эта байда собирает число из циферок
    break;
    default:
    digitalWrite(0, LOW);
      delay(500);  
      digitalWrite(0, HIGH);
    //последовательность операторов
      }

    client.publish(out_topic5, String(input).c_str(), true);
     

    }

    }



    void loop() {
      if (!client.connected()) {
        reconnect();
      }
      client.subscribe(in_topic);
      client.loop();
     

     
    // Serial.println(a);
      // client.setCallback(callback);
    //   client.subscribe(in_topic);
    /* if (client.subscribe(in_topic))
      {Serial.println("ok"); } else {Serial.println("error");}  */

      delay(500);  


    if( Serial.available() > 12 ) {
        // есть данные
        a = (Serial.read());
        a = ( a << 8 );
        a = ( a + Serial.read() );
        if (Serial.read()) {a1=0-a;} else {a1=a;}

        b = (Serial.read());
        b = ( b << 8 );
        b = ( b + Serial.read() );
        if (Serial.read()) {b1=0-b;} else {b1=b;}

        c = (Serial.read());
        c = ( c << 8 );
        c = ( c + Serial.read() );
        if (Serial.read()) {c1=0-c;} else {c1=c;}

        d = (Serial.read());
        d = ( d << 8 );
        d = ( d + Serial.read() );
        if (Serial.read()) {d1=0-d;} else {d1=d;}
     
    //   e = (Serial.read());
    }

    temp=123;
    temp2=234;
    temp3=345;
    current=321;

    Serial.write (temp >> 8);
    delay(2);
    Serial.write (temp);
    delay(2);

    Serial.write (temp2 >> 8);
    delay(2);
    Serial.write (temp2);
    delay(2);

    Serial.write (temp3 >> 8);
    delay(2);
    Serial.write (temp3);
    delay(2);

    Serial.write (current >> 8);
    delay(2);
    Serial.write (current);
    delay(2);

      /*
       digitalWrite(0, HIGH);
      delay(100);  
      digitalWrite(0, LOW);
      */

     
    }

    мы нашли, что такая же проблема уже у кого-то была
    https://github.com/knolleary/pubsubclient/issues/722
    человек меняет с телефона цифру, на ЕСПшку она приходит через десятки секунд

    У нас на данный момент всё это выглядит так:
    я припаял на плату пищалку, чтобы она пищала при каждом срабатывании callback'а
    и он без всякого обновления топика равномерно тикает.
    т.е. выходит, что callback срабатывает почему-то вместе с каждым проходом главного цикла, и отдает нули
    сам код callback'а отрабатывает правильно, просто почему-то вызывается не только в тот момент, когда есть для него данные, но и в каждом проходе, я не понимаю, откуда он вообще вызывается каждый раз!

    кроме того, если с телефона или другого клиента записать в топик цифру, он через десятки секунд вызовет дополнительное срабатывание (на самом деле именно то, которое мы ожидаем) где-то между теми срабатываниями. и вот оно будет правильным, отдаст цифры, только эти цифры тут же затрутся нулями.

    как-то так
     
  5. admitry

    admitry Нуб

    Код выше выложил, просто забыл прикрепить в начале сообщения цитату.
     
  6. SergeiL

    SergeiL Оракул Модератор

    А зачем вы делаете
    Код (C++):
    client.subscribe(in_topic);
    в loop()?
     
  7. admitry

    admitry Нуб

    без этого вообще не работает
    у меня ещё subscribe стоит в процедуре настройки
    Код (C++):
    void setup() {
      Serial.begin(19200);
      setup_wifi();
      client.setClient(espClient);
      client.setServer(mqtt_server, mqtt_port);
      client.setCallback(callback);
      client.subscribe(in_topic);
    ........
    попробовал убрать подписку из loop,
    но так вообще ничего не работает
    т.е. просто callback не запускается, иначе я бы хоть раз писк услышал
     
  8. SergeiL

    SergeiL Оракул Модератор

    И в setup() он не нужен, у вас же еще нет подключения по mqtt в этот момент.
    Подписываться нужно один раз после подключения к mqtt брокеру.
    А зачем вам бипер? Есть же монитор порта туда и выводите данные в период отладки.
     
  9. admitry

    admitry Нуб

    Если кратко, порт занят пересылкой принятых данных на другое устройство. Может быть потом переделаю на программный уарт, чтобы железный для мониторинга освободить
     
  10. admitry

    admitry Нуб

    Еее, работает! Спасибо огромное!
    передвинул подписку вот сюда
    Код (C++):

    void reconnect() {
      // Loop until we're reconnected
      while (!client.connected()) {
        Serial.print("Attempting MQTT connection...");
        // Attempt to connect
        // If you do not want to use a username and password, change next line to
        if (client.connect("ESP8266Client")) {
        //if (client.connect("ESP8266Client", mqtt_user, mqtt_password)) {
          Serial.println("connected");
         } else {
          Serial.print("failed, rc=");
          Serial.print(client.state());
          Serial.println(" try again in 5 seconds");
          // Wait 5 seconds before retrying
          delay(5000);
         
        }
      }
      client.subscribe(in_topic);
    }
     
     
  11. SergeiL

    SergeiL Оракул Модератор

    Лучше так:
    Код (C++):
    void reconnect() {
      // Loop until we're reconnected
      while (!client.connected()) {
        Serial.print("Attempting MQTT connection...");
        // Attempt to connect
        // If you do not want to use a username and password, change next line to
        if (client.connect("ESP8266Client")) {
        //if (client.connect("ESP8266Client", mqtt_user, mqtt_password)) {
          Serial.println("connected");
          client.subscribe(in_topic);
         }
         else {
          Serial.print("failed, rc=");
          Serial.print(client.state());
          Serial.println(" try again in 5 seconds");
          // Wait 5 seconds before retrying
          delay(5000);
        }
      }
    }
     
    Но по хорошему нужно убирать while так как если нет коннекта - больше ничего не работает - крутимся в while
     
  12. SergeiL

    SergeiL Оракул Модератор

    Нужно сначала отладить работу устройства с сетью, с использованием сериала, а потом можно уже и с другим устройством соединять.
     
    ИгорьК и b707 нравится это.
  13. SergeiL

    SergeiL Оракул Модератор

    В примерах есть пример mqtt_reconnect_nonblocking посмотрите, там в не подвисает код в реконнекте, если нет связи.
     
  14. admitry

    admitry Нуб

    Это не проблема, ESPшка больше ни для чего не используется, кроме связи. В любом случае, спасибо за совет, пригодится не здесь, так где-то ещё
     
  15. admitry

    admitry Нуб

    Как прилепить кнопку, что проблема решена?
    Потому что проблема решена)