Wifi weather station with MQTT and deep sleep

Wasn't sure if this is the right place for this question but here goes.

I have setup my hardware the same as: https://www.instructables.com/id/Solar-Powered-WiFi-Weather-Station-V20/

I changed the code to be my own as I wanted to use MQTT instead of Thingspeak or Blynk.

I am having an issue where the weather station will work once I turn it on but then will stop all of a sudden. Sometimes it stops after 10 minutes, sometimes after 6 hours, or really anytime.

The battery hasn't died because if I go out and turn it off and on (or press the reset button on the wemos) it starts working again for a while.

I can't make it "stop" while it is connected to my laptop when I'm reading serial data, it all looks happy then.

What is the best way to debug something that uses deep sleep?

I didn't have a delay between publishing the data and going into deep sleep, I thought this was my problem but it ran for 6 hours yesterday and has now been "stopped" for ~8 hours.

The wifi and MQTT server are up and fine as I have other wemos inside my house running fine and publishing to the same server.

If I can't connect to wifi after 15 seconds or then connect to the mqtt server after 15 seconds I restart the wemos so that it doesn't get stuck in a loop while not able to connect.

I ran a wifi scanner strength check from the wemos and the strength of the wifi is over 40%.

Here is the full code incase I am doing something silly (I hope I am as its driving me mad now):

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

// configure period between reports
const long interval = 300000;  // 5 minutes

// bme280 bme0(0, false) ; // creates object bme0 of type bme280, base address
Adafruit_BME280 bme;

// Network details
const char ssid[] = "proper-ssid";
const char wifi_password[] = "proper-password";

// MQTT broker
const char mqtt_server[] = "proper-ip";
const char mqtt_topic[] = "proper-topic";
const char mqtt_username[] = "proper-username";
const char mqtt_password[] = "proper-password";
const char mqtt_client_id[] = "proper-id";

WiFiClient wifi_client;
PubSubClient client(mqtt_server, 1883, wifi_client);

double temperature;
double pressure;
double humidity;
float altitude = 0;
float volt;

void setup()
{
    Serial.begin(115200);

    bool bme_status = bme.begin(0x76);  //address either 0x76 or 0x77
    if (!bme_status)
    {
        Serial.println("Could not find a valid BME280 sensor, check wiring!");
    }

    bme.setSampling(Adafruit_BME280::MODE_FORCED,
        Adafruit_BME280::SAMPLING_X1, // temperature
        Adafruit_BME280::SAMPLING_X1, // pressure
        Adafruit_BME280::SAMPLING_X1, // humidity
        Adafruit_BME280::FILTER_OFF);

    get_data();
    connect_to_wifi();
    connect_to_mqtt();
    post_data();
    go_to_sleep();
}

void loop()
{
}

void connect_to_wifi()
{
    WiFi.persistent(false);
    WiFi.disconnect();
    WiFi.mode(WIFI_STA);
    // Connect to the network
    WiFi.begin(ssid, wifi_password);
    Serial.println("Connecting to WiFi");

    int retry_count = 0;

    while (WiFi.status() != WL_CONNECTED && retry_count < 15)
    {
        delay(1000);
        retry_count++;
        Serial.print(".");
    }

    if (WiFi.status() != WL_CONNECTED)
    {
        ESP.restart();
    }

    Serial.println("");
}

void connect_to_mqtt()
{
    int retry_count = 0;

    while (!client.connected() && retry_count < 15)
    {
        // Connect to MQTT broker
        if (client.connect(mqtt_client_id, mqtt_username, mqtt_password))
        {
            Serial.println("Connected to MQTT Broker!");
        }
        else
        {
            Serial.println("Connection to MQTT Broker failed...");
            Serial.print(client.state());
            retry_count++;
            delay(1000);
        }
    }

    if (!client.connected())
    {
        ESP.restart();
    }
}

void get_data()
{
    bme.takeForcedMeasurement();
    temperature = bme.readTemperature();
    humidity = bme.readHumidity();
    pressure = bme.readPressure() / 100.0F;

    Serial.print("Atm press = ");
    Serial.print(pressure, 2);
    Serial.print(" hPa. Temperature = ");
    Serial.print(temperature, 2);
    Serial.print( " deg C. Humidity = ");
    Serial.print(humidity, 2);
    Serial.print( " %RH. Altitude = ");
    Serial.print(altitude, 2);

    // Battery Voltage Monitoring
    // Voltage divider R1 = 220k+100k+220k =540k and R2=100k
    float calib_factor = 5.24; // change this value to calibrate the battery voltage
    unsigned long raw = analogRead(A0);
    volt = raw * calib_factor / 1024;

    Serial.print( " m. \nVoltage = ");
    Serial.print(volt, 2);
    Serial.println(" V");
}

void post_data()
{
    String serialised = String(temperature, 2);
    serialised += "|";
    serialised += String(pressure, 2);
    serialised += "|";
    serialised += String(humidity, 2);
    serialised += "|";
    serialised += String(altitude, 2);
    serialised += "|";
    serialised += String(volt, 2);

    client.publish(mqtt_topic, serialised.c_str(), false);
    client.loop();
}

void go_to_sleep()
{
    // calculate required sleep time and go to sleep
    long sleepTime = interval - millis();
    if (sleepTime < 100)
    {
        sleepTime = 100; // set minimum sleep of 0.1 second
    }

    delay(2000);

    ESP.deepSleep(sleepTime * 1000, WAKE_RF_DEFAULT); // convert to microseconds
}

You should properly disconnect from the MQTT broker and WiFi before deep sleep. Properly disconnecting from the MQTT Broker is nice, especially with out a Last Will and Testament and properly closing the network connection is an OK thing do.

Idahowalker:
You should properly disconnect from the MQTT broker and WiFi before deep sleep. Properly disconnecting from the MQTT Broker is nice, especially with out a Last Will and Testament and properly closing the network connection is an OK thing do.

I was trying to read about this but couldn't get a good answer.
Is it good enough to just call WiFi.disconnect(); and the equivalent for the MQTT client after the delay and before the deep sleep?

First, the ESP8266 is a 32 bit machine why use 64 bit floats, the doubles?

Here's how I put a ESP32 to deep sleep with nearly the same setup as yours:

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include "certs.h"
#include "sdkconfig.h"
#include "esp_system.h" //This inclusion configures the peripherals in the ESP system.
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/event_groups.h"
#include "esp_sleep.h"
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BME680.h"
////
EventGroupHandle_t eg;
#define evtDoBME ( 1 << 2 ) //100
#define evtSetupBME_Complete ( 1 << 3 )
SemaphoreHandle_t sema_ReadBME680;
////
WiFiClientSecure secureClient = WiFiClientSecure();
WiFiClient wifiClient;
PubSubClient client(mqtt_server, 1883, wifiClient); // 1883 is the listener port for the Broker
////
Adafruit_BME680 bme( GPIO_NUM_15); // hardware SPI
///
////
void setup()
{
  eg = xEventGroupCreate();
  SPI.begin();
  sema_ReadBME680 = xSemaphoreCreateBinary();
  xTaskCreatePinnedToCore( fDoBME, "fDoBME", 20000, NULL, 3, NULL, 1 ); // assigned to core
  //start this task last
  xTaskCreatePinnedToCore( fDoTheThing, "fDoTheThing", 40000, NULL, 5, NULL, 1 ); // assigned to core
} // end setup()
////
void mqttCallBack( char* topic, byte* payload, unsigned int length )
{
  if ( client.connected() )
  {
    String s;
    for ( int i = 0; i < length; i++ )
    {
      s += (char)payload[i];
    }
    log_i( "incomming... topic %s payload %s", topic, s);
  }
}
////
void connectToMQTT()
{
  client.setCallback( mqttCallBack );
  if ( client.connect(clientID, mqtt_username, mqtt_password) )
  {
    log_i("Connected to MQTT Broker!");
  } else {
    log_i("Connection to MQTT Broker failed...");
  }
  log_i("MQTT Connected");
}
//
void connectToWiFi()
{
  struct tm timeinfo;
  const char* ntpServer = "pool.ntp.org";
  const long  gmtOffset_sec = -25200;
  const int   daylightOffset_sec = 3600;
  // struct tm timeinfo;
  log_i( "Connecting to WiFi " );
  WiFi.begin( SSID, PWD );
  vTaskDelay( 750);
  while ( WiFi.status() != WL_CONNECTED )
  {
    log_i(".");
    vTaskDelay( 800 );
  }
  log_i( "WiFi Connected - ");
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  // getLocalTime(&timeinfo));
  // log_i( "%A, %B %d %Y %H:%M:%S", &timeinfo );
}
////
void fDoTheThing( void * pvParameters )
{
  xEventGroupWaitBits (eg, evtSetupBME_Complete, pdTRUE, pdTRUE, portMAX_DELAY ); //
  connectToWiFi();
  connectToMQTT();
  while (1)
  {
    xEventGroupSetBits( eg, evtDoBME ); // trigger tasks
    xSemaphoreTake( sema_ReadBME680, portMAX_DELAY ); // wait for task to be done
    log_i( "entering deep sleep" );
    client.disconnect();
    WiFi.disconnect(true);
    WiFi.mode(WIFI_OFF);
    esp_sleep_enable_timer_wakeup( (60000000 * 6) ); // set timer to wake up every 60000000uS (1 minute) * 6
    // esp_sleep_enable_timer_wakeup( (60000000/4.0f) ); // set timer to wake up every 60000000uS (1 minute)
    esp_deep_sleep_start();
  } //while(1)
  vTaskDelete ( NULL );
} // void fDoTheThing( void * pvParameters )
////
void fDoBME ( void *pvParameters )
{
  float hum_score, gas_score;
  while (!bme.begin())
  {
    log_i("Could not find BME680 sensor!");
    vTaskDelay( 10 );
  }
  bme.setTemperatureOversampling(BME680_OS_8X);
  bme.setHumidityOversampling(BME680_OS_2X);
  bme.setPressureOversampling(BME680_OS_4X);
  bme.setIIRFilterSize(BME680_FILTER_SIZE_3);
  bme.setGasHeater(320, 150); // 320*C for 150 ms
  float Temperature = 0.0f;
  float Pressure = 0.0f;
  float Humidity = 0.0f;
  // float gas_resistance = 0.0f; //in K ohms
  float gas_reference = 0.0;
  float hum_reference = 40.0f;
  int   getgasreference_count = 0;
  float gas_lower_limit = 5000.0f;   // Bad air quality limit
  float gas_upper_limit = 50000.0f;  // Good air quality limit 
  float air_quality_score = 0.0f;
  xEventGroupSetBits( eg, evtSetupBME_Complete ); // trigger task to begin
  for ( ;; )
  {
    xEventGroupWaitBits (eg, evtDoBME, pdTRUE, pdTRUE, portMAX_DELAY ); //
    Temperature = (bme.readTemperature() * 1.8f) + 32.0f; // (Celsius x 1.8) + 32
    Pressure = bme.readPressure() / 133.3223684f; //converts to mmHg
    Humidity = bme.readHumidity();
    // gas_resistance = bme.gas_resistance;
    // gas_resistance = bme.readGas();
    
    //Calculate humidity contribution to IAQ index
    gas_reference = GetGasReference(); // get 10 gas readings and average.
    if ( Humidity >= 38.0f && Humidity <= 42.0f )
    {
      hum_score = 0.25f * 100.0f; // Humidity +/-5% around optimum
    } else  { //sub-optimal
      if (Humidity < 38)
      {
        hum_score = 0.25f / hum_reference * Humidity * 100.0f;
      } else {
        hum_score = ((-0.25f / (100.0f - hum_reference) * Humidity) + 0.416666f) * 100.0f;
      }
    }
    gas_score = (0.75 / (gas_upper_limit - gas_lower_limit) * gas_reference - (gas_lower_limit * (0.75 / (gas_upper_limit - gas_lower_limit)))) * 100.0f;
    //Combine results for the final IAQ index value (0-100% where 100% is good quality air)
    air_quality_score = hum_score + gas_score;
    log_i( "Temperature %f C, Pressure %f hPa, Humidity %f, IAQ %f", Temperature, Pressure, Humidity, air_quality_score );
    client.publish( "Home/oTemperature", String(Temperature).c_str() );
    vTaskDelay( 2 ); // gives the Raspberry Pi 4 time to receive the message and process
    client.publish( "Home/oHumidity", String(Humidity).c_str() );
    vTaskDelay( 2 ); // no delay and RPi is still processing previous message
    client.publish( "Home/oGas_Resistance", String(air_quality_score).c_str() );
    vTaskDelay( 2 );
    client.publish( "Home/oPressure", String(Pressure).c_str() );
    xSemaphoreGive ( sema_ReadBME680 );
  } // for loop
  vTaskDelete ( NULL );
} // void fDoBMP ( void *pvParameters )
////
float GetGasReference(){
  // Now run the sensor for a burn-in period, then use combination of relative humidity and gas resistance to estimate indoor air quality as a percentage.
  float gas_reference = 0.0f;
  int readings = 10;
  for (int i = 1; i <= readings; i++){ // read gas for 10 x 0.150mS = 1.5secs
    gas_reference += bme.readGas();
  }
  gas_reference = gas_reference / readings;
  return gas_reference;
}
void loop() {}

Idahowalker:
First, the ESP8266 is a 32 bit machine why use 64 bit floats, the doubles?

Are you saying that I should floats for the BME280 readings and not doubles ?

I added code to disconnect wifi and disconnect the mqtt client but I will also add:

WiFi.mode(WIFI_OFF);

after seeing yours.

Thanks for the help!

Edit:
I see the Adafruit returns float for these readings, I changed to a float, I was using a different library before for the BME280 and it returned double but changed the library to the Adafruit one to see if it fixed my issue but it didn't.

av4625:
I am having an issue where the weather station will work once I turn it on but then will stop all of a sudden. Sometimes it stops after 10 minutes, sometimes after 6 hours, or really anytime.

The battery hasn't died because if I go out and turn it off and on (or press the reset button on the wemos) it starts working again for a while.

I can't make it "stop" while it is connected to my laptop when I'm reading serial data, it all looks happy then.

After looking at your code and rereading your first post, I'd now turn to, from those 2 statements above, to looking at a power issue. A cold solder joint or other bad connection. Power it up on battery and tap, with a wooden stick, around the connections and other power related components. When it stops does a bit of freeze spray zoink it back to life?

Can you poke around the power circuits, on battery, with a DVM? Note readings of before and after stops of the same places. Measure battery voltage before stop and after stop. Any components look like the are over heating?


Good work on figuring out you do not need a double with this use case.

Ever since this morning when I added the wifi and client disconnect it hasnt stopped :fingers-crossed:

I havent yet set the wifi mode to off when disconnecting. I have the code written but was waiting to see if it stopped again before up loading more.

I will also check the connections!

Stopped over night :frowning:

Will try the connections when i have time.

Tapped the connections and it didnt start up again, when i clicked the reset button it started working again. Going to take it out of the box and bring it back inside and see if i can make it stop

Had a go trying to make it fail while connected to my computer and couldn’t do it. I noticed that if i pulled the BME280 sensor of my board that it did hang in the setSampling function and never came out of that function until I plugged the sensor back in.

I thought maybe the sensor had a bad connection or was failing or something, i tried wiggling it in the header on the board and it was fine.

I put some code in that only called the setSampling function if the begin function returned success. If the begin failed i just reports back to the mqtt server with all zeros for the measurements so that I could tell if it ever failed.

It stopped again :frowning: and never reported zeros.

My last ditch attempt is to put very basic code on tomorrow that only, connects to wifi, connects to mqtt, and sends zeros then deep sleep. The server will record the times that data is published so i can tell if it stops publishing.

If it still stops then... I’m all out

At this point I'm all out thanks for taking the time to help @Idahowalker.

So running the bare minimum code also didn't work. It stopped quite quick.

Last night I ran the wifi scanner code again on the wemos d1 mini pro. I noticed it wasn't all that strong. I then ran the scanner code on wemos d1 mini. It was about twice as strong. So here I blame myself for not doing a good enough job when moving the tiny resistor on the board so I could use the external antenna.

So I setup my wemos d1 mini with the weather station code and it ran for about 2 hours and stopped as well :frowning:

This morning I plugged it in to my laptop and all I get from the serial monitor is:

 ets Jan  8 2013,rst cause:2, boot mode:(7,6)

waiting for host

From rst cause 2 I think that is rst pin, that pin is connected to GPIO for deep sleep but I get that error on boot before it does anything if rst is connect to GPIO 0 or not

I only get this error when the wemos is connected to the rest of the circuit, if i pull it off the board it seems to be fine. This never used to happen but happens all the time now. This also doesnt happen if the wemos is not connected to my circuit and I put a jumper between RST and GPIO0 so that isn't the problem.

Must be the circuit