esp32, deep sleep and mqtt

I saw a lot of posts about sending mqtt messages from an esp32/esp8266 with deep sleep between the sending cycles. This works fairly well as long as you keep a some time for the message to transmit before the deep sleep cycle.
However, when you want to receive messages things get more interesting. If you just sent the callback on the esp32 to receive messages during the wake up time the messages almost never got through.
This was a bit puzzeling for me at first, as reliability was one of the main reasons why i choose to use MQTT in the first place.

My first thoughts were that the messages got lost because the esp went to sleep without proper disconnecting and the mosquitto server was trying to send with no response. So I did try sending them in QoS 1 instead (and trying to implement a more graceful shutdown before sleep on the esp side), but the effects were exactly the same.

But then I thought back about queues and topics, and that MQTT topics won’t work like queues. That was exactly the point. MQTT Topics are not retained on default. That means the mosquitto server is only sending the message to the subscribed clients when a message arrives. If the client is sleeping/unsubscribed the message will be lost.

Howerver, if you send the message in “retained” mode, like “mosquitto_pub -t esp32/output -r -m on” the message will stay on the topic in the MQTT server. So, each time a client connects it will get that retained message, for example if a relais should be turned off or on.

/* Project ESP32, MQTT and Deepsleep */
#include <WiFi.h>
#include <PubSubClient.h>

#define wifi_ssid "<ssid>"         //wifi ssid
#define wifi_password "<password>"     //wifi password

#define mqtt_server "<mqtt>"  // server name or IP
#define mqtt_user "username"      // username
#define mqtt_password "password"   // password

#define debug_topic "debug"                   //Topic for debugging

/* definitions for deepsleep */
#define uS_TO_S_FACTOR 1000000        /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 20              /* Time ESP32 will go to sleep for 15 minutes (in seconds) */
#define TIME_TO_SLEEP_ERROR 3600       /* Time to sleep in case of error (1 hour) */

bool debug = true;             //Display log message if True

WiFiClient espClient;
PubSubClient client(espClient);

void setup() {
  Serial.begin(115200);     
  setup_wifi();                           //Connect to Wifi network
   
  client.setServer(mqtt_server, 1883);    // Configure MQTT connection, change port if needed.

  if (!client.connected()) {
    reconnect();
  }
  
  client.loop();
  delay(100);
  client.loop();
  delay(100);
  client.unsubscribe("esp32/output");
  client.disconnect();
  
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); //go to sleep
  Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) + " Seconds");
  Serial.println("Going to sleep as normal now.");
  esp_deep_sleep_start();
}

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

  // Feel free to add more if statements to control more GPIOs with MQTT

  // If a message is received on the topic esp32/output, you check if the message is either "on" or "off". 
  // Changes the output state according to the message
  if (String(topic) == "esp32/output") {
    Serial.print("Changing output to ");
    if(messageTemp == "on"){
      Serial.println("on");
    }
    else if(messageTemp == "off"){
      Serial.println("off");
    }
  }
}


//Setup connection to wifi
void setup_wifi() {
  delay(20);
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(wifi_ssid);

  WiFi.begin(wifi_ssid, wifi_password);

  int count=0;
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
    count++;
    Serial.print(".");
    /* I had some cases where the esp was stuck in connection to wifi mode */
    if (count > 100) {
      Serial.println();
      Serial.println("something bad happened, trying to reset");
      ESP.restart();

    }
  }

 Serial.println("");
 Serial.println("WiFi is OK ");
 Serial.print("=> ESP32 new IP address is: ");
 Serial.print(WiFi.localIP());
 Serial.println("");
}

//Reconnect to wifi if connection is lost
void reconnect() {

  while (!client.connected()) {
    
    Serial.print("Connecting to MQTT broker ...");
    if (client.connect("ESP32Client")) {
      Serial.println("OK");
      client.setCallback(callback);
      client.subscribe("esp32/output");
      client.loop();
      delay(500);
    } else {
      Serial.print("[Error] Not connected: ");
      Serial.print(client.state());
      Serial.println("Wait 5 seconds before retry.");
      delay(5000);
    }
  }
}

void loop() { 
}
This entry was posted in Uncategorized. Bookmark the permalink.