knolleary / pubsubclient

A client library for the Arduino Ethernet Shield that provides support for MQTT.
http://pubsubclient.knolleary.net/
MIT License
3.82k stars 1.47k forks source link

ESP32, MQTT Subscribe broken when using deep sleep #634

Open m4dm4n opened 5 years ago

m4dm4n commented 5 years ago

Hardware: Board: Wemos lolin Core Installation/update date: v1.0.3 IDE name: Arduino IDE Flash Frequency: 80Mhz PSRAM enabled: no Upload Speed: ?921600? Computer OS: Windows 10

If uploading the code,not using Deep sleep function, ESP32 subscribes and publishes to some topic succesfully (MQTT Broker is Rpi, all debugging is done in Node-Red + ESP32 serial monitor).

But if I transfer that same code inside Deep Sleep part of the code, ESP32 fails to subscribe (precisely receive message from topic).

I tried as simple code as possible, some messages to get some clearer picture, but I can't pinpoint the problem.

#include <Int64String.h>
#include <WiFi.h>
#include <PubSubClient.h>

uint64_t uS_TO_S_FACTOR = 1000000;  /* Conversion factor for micro seconds to seconds */
uint64_t TIME_TO_SLEEP = 5;        /* Time ESP32 will go to sleep (in seconds) */

RTC_DATA_ATTR int bootCount = 0;

const char* ssid = "x";
const char* password = "x";
const char* mqtt_server = "x";

WiFiClient espClient;
PubSubClient client(espClient);

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();

 }

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");
      delay(1000);
      // Once connected, publish an announcement...
      client.publish("esp32/TestOut", "hello world");
      // ... and resubscribe
      if(client.subscribe("esp32/Test")){
      Serial.println("Subscribed to esp32/Test");  
      } else {
      Serial.println("Failed to subscribe to esp32/Test");    
      }

      } 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()
{
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  Serial.println("Setup ESP32 to sleep for every " + int64String(TIME_TO_SLEEP) + " Seconds");
  delay(500);

  //TRANSFER CODE FROM LOOP TO HERE

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

  //END OF LOOP CODE

  Serial.println("Going to sleep now");
  Serial.flush();
  esp_deep_sleep_start();

}

void loop()
{

}

Serial Monitor Debug message when the code is NOT working (aka using Deep Sleep functionality)

rst:0x5 (DEEPSLEEP_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0018,len:4
load:0x3fff001c,len:928
ho 0 tail 12 room 4
load:0x40078000,len:8424
ho 0 tail 12 room 4
load:0x40080400,len:5868
entry 0x4008069c

Connecting to x
..
WiFi connected
IP address: 
x
Boot number: 2
Setup ESP32 to sleep for every 5 Seconds
Attempting MQTT connection...connected
Subscribed to esp32/Test
Going to sleep now
ets Jun  8 2016 00:22:57

Serial Monitor Debug message when the code IS working (aka using Deep Sleep functionality)

018,len:4
load:0x3fff001c,len:928
ho 0 tail 12 room 4
load:0x40078000,len:8424
ho 0 tail 12 room 4
load:0x40080400,len:5868
entry 0x4008069c

Connecting to isdn
..
WiFi connected
IP address: 
192.168.0.30
Attempting MQTT connection...connected
Subscribed to esp32/Test
Message arrived [esp32/Test] 50

You can CLEARLY see it received the message (which is retained on MQTT broker).

EFWob commented 5 years ago

As I see it your problem is that client.loop() is called only once directly after successfull connect. To make it run for longer (i. e. like 15 seconds) you could try the change below. That should do the trick, though I did not test it.

//TRANSFER CODE FROM LOOP TO HERE uint32_t loopStart = millis(); while (millis() - loopStart < 15000) { if (!client.connected()) { reconnect(); } else client.loop(); } //END OF LOOP CODE

grafoteka commented 4 years ago

Hello @EFWob, I had the same problem and your solution solved it!

Thank you very much and happy christmas!

OneHoopyFrood commented 4 years ago

A bit more detail on this for anyone else who comes along, the problem was caused (for me) by not understanding when client.loop does and when it needs to be called, as pointed out by @EFWob.

I've kept my code in loop, but moved the client.loop() call to the bottom, just before calling deep sleep. It's now working perfectly!

Essentially, you must call client.loop() after your client.publish(), before your deep sleep wipes out the internal buffer. Without deep sleep, that buffer will still be filled on the next loop iteration and will, therefore, be sent. Sorted.

My (simplified) loop function:

void loop(void)
{  
  // Connect to MQTT
  if (!client.connected()) {
    mqtt_reconnect();
  }
  // ** USED TO HAVE client.loop() HERE **

  // Send to MQTT topic
  bool publishSuccess = client.publish(mqtt_topic, "message");

  if (publishSuccess) {
    Serial.println("MQTT Message published");
  }

  // MQTT Loop has to be AFTER message sent when using deep sleep.
  client.loop();

  // Deep sleep for 15 min
  ESP.deepSleep(9e8);
}