Здравствуйте! Пытыюсь настроить чтение топика с ESP8266. Прошу пояснить механику работы обратного вызова (callback) в протоколе MQTT Как я это понимаю: вот мы подписываемся на топики и тем самым сообщаем серверу, какие обновления топиков мы хотим получать. Сервер их присылает, в функции callback мы прописываем как реагировать на каждую пересылку. По факту так получается, что callback вызывается каждый проход главного цикла. Если при этом есть новые данные в топике, он их читает, а если нет, он всё равно читает! Т.е. функция выолняется, но видит нули С чем это может быть связано? Можете пояснить на пальцах? Спасибо!
Наверно лучше будет попросить помощь более конкретно: На сервере у нас есть топик. В этот топик мы присылаем с мобильного приложения mqtt данные, например число 23,5. Далее мне необходимо чтобы это число попало в ESP8266. Мы сделали подписку на наш топик и с помощью callback пытаемся получить в переменную в ESP8266 наши данные (число 23,5) из топика на сервере. Но у нас ничего не получается. Данные прилетают в ESP8266 только 1 раз нормально, а потом начинают прилетать нули не понятно откуда, а когда мы отправляем данные повторно, то они приходят с очень большой задержкой....несколько минут. Сервер работает нормально, проблема с прошивкой callback ESP8266. Дайте пожалуйста образец кода по которому мы сможем получать данные из топика, и чтобы они приходили без задержки и не было ложных приходов нулей. В результате нам надо получить вот что: На мобильном приложении mqtt мы вводим параметр, этот параметр через сервер приходит в переменную в ESP8266 и остается там до тех пор пока мы не введем новый параметр. Помогите пожалуйста!!!
Выкладывайте код, который не работает, а то так обсуждать сложно. Вместе с библиотеками обычно есть примеры, вы их смотрели? Вот один из примеров: Код (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); } }
Дело в том, что я на этом примере свой код и развивал: Код (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'а отрабатывает правильно, просто почему-то вызывается не только в тот момент, когда есть для него данные, но и в каждом проходе, я не понимаю, откуда он вообще вызывается каждый раз! кроме того, если с телефона или другого клиента записать в топик цифру, он через десятки секунд вызовет дополнительное срабатывание (на самом деле именно то, которое мы ожидаем) где-то между теми срабатываниями. и вот оно будет правильным, отдаст цифры, только эти цифры тут же затрутся нулями. как-то так
без этого вообще не работает у меня ещё 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 не запускается, иначе я бы хоть раз писк услышал
И в setup() он не нужен, у вас же еще нет подключения по mqtt в этот момент. Подписываться нужно один раз после подключения к mqtt брокеру. А зачем вам бипер? Есть же монитор порта туда и выводите данные в период отладки.
Если кратко, порт занят пересылкой принятых данных на другое устройство. Может быть потом переделаю на программный уарт, чтобы железный для мониторинга освободить
Еее, работает! Спасибо огромное! передвинул подписку вот сюда Код (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); }
Лучше так: Код (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
Нужно сначала отладить работу устройства с сетью, с использованием сериала, а потом можно уже и с другим устройством соединять.
В примерах есть пример mqtt_reconnect_nonblocking посмотрите, там в не подвисает код в реконнекте, если нет связи.
Это не проблема, ESPшка больше ни для чего не используется, кроме связи. В любом случае, спасибо за совет, пригодится не здесь, так где-то ещё