ESP + Wemos D1 get freezes when wifi connection lost

Hi everyone,

I’m totaly new in the “hobby” so my code might affraid some of you…

I’m using the Wemos D1 Mini V3.0.0 (ESP8266) to build my own weather station (largely inspire by this Solar Powered WiFi Weather Station V1.0 : 19 Steps (with Pictures) - Instructables) + I’ve added a oled screen so I can read the temp direclty on the device.

My problem is that when my wifi connection is lost (don’t know why), the device freezes (I guess loop “brokes”) and I have to go outside to restart the device to make it reworks properly.

I have read somewehere that Cayenne Team is working on an “offline” feature, but I’m not sure if this is related to my issue. I guess the issue is more on my side, so I hope you’ll be able to helps me to fixe this and improve my code :slight_smile:

#include <Adafruit_GFX.h> // Icons and lines
#include <Adafruit_SSD1306.h> // Oled display
#include <Adafruit_BME280.h> // Sensor
#include <math.h> // isnan()
//#define CAYENNE_DEBUG
#define CAYENNE_PRINT Serial
#include <CayenneMQTTESP8266.h>

// Cayenne authentication info. This should be obtained from the Cayenne Dashboard.
char username[] = "xxxxxxxxxxxxxxxxx";
char password[] = "xxxxxxxxxxxxxxxxxx";
char clientID[] = "xxxxxxxxxxxxxxxxxxxxxxx";

// WiFi network info.
char ssid[] = "xxxxxxxxxx";
char wifiPassword[] = "xxxxxxxxxxx";

Adafruit_BME280 bme; //I2C
Adafruit_SSD1306 display(-1); //I2C

const unsigned long UPDATE_PERIOD_MS = 5000;
unsigned long lastMillis = 0;

//Oled on/off
bool oledState = true;

// I2C wiring
#define BME_MOSI D2
#define BME_SCK D1

// Temp calibration
const float tempOffset = -1.75;

// Battery voltage
const float VoltageMultiplier = ( 1 / 210.447761 ); //calculed with voltmeter
const float BatteryPercentage = 0;
const float BattMin = 3.3;
const float BattMax = 4.2;

unsigned long lastUpdate = 0;
char buf[64];

//wifi icon size: 20W*15H px
const unsigned char wifi_icon [] PROGMEM = {
0x07, 0xc0, 0x1f, 0xf0, 0x7c, 0x7c, 0x70, 0x1c, 
0xc0, 0x06, 0xc3, 0x86, 0x0f, 0xe0, 0x1c, 0x70,
0x18, 0x30, 0x00, 0x00, 0x03, 0x80, 0x03, 0x80
};

void setup() {
    Serial.begin(9600);
    if (bme.begin()) {
        Serial.println("BME280 sensor connected");
    }
    display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
    display.dim(true);
    display.display(); // show splashscreen
    delay(1000);    
    Cayenne.begin(username, password, clientID, ssid, wifiPassword);
    display.clearDisplay(); // clears the screen and buffer
}

void loop() { 
  
  Cayenne.loop();
        
  if (millis() - lastUpdate >= UPDATE_PERIOD_MS) {
    lastUpdate = millis();
  
    float volt = analogRead(A0)*VoltageMultiplier; // battery voltage

    // Prevent power consumption if voltage it too low
    if( volt < 3.3 ){

      //Do not read neither send an data
      //Erase all screen pixels
      display.clearDisplay(); // clears the screen and buffer
      display.fillRect(0, 0, 124, 68, BLACK );
      display.display();
        
    } else {
      
      Serial.println("____________________________________");

      Cayenne.virtualWrite(1, volt, "voltage", "v");
      Serial.print("Voltage = ");
      Serial.print(volt);
      Serial.println(" V");
  
      float bat = (volt - BattMin)/(BattMax - BattMin) * 100;
      if(bat > 100){
        bat = 100;
      }
      Cayenne.virtualWrite(2, bat, "batt", "p");
      Serial.print("Battery = ");
      Serial.print(bat);
      Serial.println("%");
                
      float temp = bme.readTemperature() + tempOffset; // degrees C
      Cayenne.virtualWrite(3, temp, "temp", "c");
      Serial.print("Temperature = ");
      Serial.print(temp);
      Serial.println(" °C");
        
      float humidity = bme.readHumidity(); // % 
      Cayenne.virtualWrite(4, humidity, "rel_hum", "p");
      Serial.print("Humidity = ");
      Serial.print(humidity);
      Serial.println(" %");
        
      float pressure = (bme.readPressure() / 100.0F); // hPa
      Cayenne.virtualWrite(5, pressure, "bp", "c");
      Serial.print("Pressure = ");
      Serial.print(pressure);
      Serial.println(" hPa");
      
      if( oledState == true ){
        
        // Update button status on Cayenne
        Cayenne.virtualWrite(6, 1, "digital_actuator", "d");

        if (!isnan(temp) && !isnan(humidity) ) {
            
          display.clearDisplay();
          display.setTextSize(1);
          display.setTextColor(WHITE);
    
          if( bat == 100){
            display.fillRect(0, 0, 27, 12, 1 );
            display.setTextColor(BLACK);
            display.setCursor(2,2);
            snprintf(buf, sizeof(buf), "%.1fv", volt);
            display.print(buf);
            display.setTextColor(WHITE);
          } else {
            display.setCursor(5,2);
            snprintf(buf, sizeof(buf), "%.0f%%", bat);
            display.print(buf);
          }
    
          //Battery logo
          display.drawLine(27, 3, 27, 8, 1);
          display.drawLine(28, 3, 28, 8, 1);
          display.drawLine(29, 4, 29, 7, 1);
          display.drawRect(0, 0, 27, 12, 1 );
      
          display.setCursor(0,16);
          display.setTextSize(2);
          snprintf(buf, sizeof(buf), "%.2f ", temp);
          display.print(buf);
          display.print((char)247);
          display.println("C");
    
          snprintf(buf, sizeof(buf), "%.2f %%", humidity);
          display.println(buf);
          
          snprintf(buf, sizeof(buf), "%.1f hPa", pressure);
          display.println(buf);
    
        }
        
        if( WiFi.status() == WL_CONNECTED ){
          // Wifi logo
          display.drawBitmap(113, 0, wifi_icon, 15, 12, 1);
        } else {
          // Remove wifi icone and draw a cross
          display.fillRect(113, 0, 15, 12, BLACK );
          display.drawLine(116, 1, 128, 12, 1);
          display.drawLine(116, 11, 128, 0, 1);  
        }
          
      } else {

         // Update button status on Cayenne
         Cayenne.virtualWrite(6, 0, "digital_actuator", "d");

         //Erase all screen pixels
         display.clearDisplay(); // clears the screen and buffer
         display.fillRect(0, 0, 124, 68, BLACK );
          
       }
  
       display.display();

    }
  }
  
}

// Virtual button to power on/off the oled display
CAYENNE_IN(6){
  // get value sent from dashboard
  int currentValue = getValue.asInt();
  if (currentValue ==1){
    Serial.println("Oled on");
    oledState = true;
  } else {
    Serial.println("Oled off");
    oledState = false;
  }
}

I’ve added a feature to “shutdown” the oled screen (actually just make it black) to prevent over battery discharge + a virtual button to manually turn it on/off.

My if/else statement to update the wifi logo with WiFi.status() == WL_CONNECTED does not work neither, I guess because of when there is no wifi, the conditional test is not called because the loop breaks.

Thanks for your help !
Brice

welcome to the cayenne community @brice.
You got a nice project going on here.

when the wifi connection is lost, the code continuously searches for wifi/Internet connection until it is reconnected. i don’t see the necessity for the device to be restarted when the wifi connection is available again. you can do a test case by uploading the basic esp8266 code from the cayenne library, uncomment #define CAYENNE_DEBUG and open serial monitor. you will see output showing connection okay and publish message. Now disconnect/ power OFF your wifi and check your monitor. You will get a disconnect/ network error message in a continuous loop as the code is trying to continuously look for the internet and failing. Now connect/ power ON your wifi and the output will show connected.

We are working on this feature. In your case, the code looks pretty good enough to work. i cannot find any mistake that can cause a disconnect. can you check whether you are receiving wifi connection properly at the place you have kept the device? You can also try the debug method i mentioned above to check what is the issue in the serial monitor.

the if statement should run properly, the else statement wont run as the code never reaches this as it is in a loop to check for the wifi connection.

Hi @shramik_salgaonkar,
Thanks for your reply.

When the connexion gets lost, the display (oled) is not refreshed because ESP tried to connect and loop is paused (right?). I can see in the serial monitor that it tried to connect as you said, every 30 second I think . But some times it stuks here and can’t reconnect so I had to manually restart the device (made my tests very closed to the Internet box).
Would-it be possible to let the loop continuing whyl trying to connect (and so refresh screen)?

Do you mean that WL_CONNECTED is always true when in the loop? If yes, what would you suggest to update the icon based on the wifi state ?

Thanks for your help :smiley:

yes, you are right.

can you try the same test by uploading the basic esp8266 code from the cayenne library?

have a look at this workaround How can i end the {cayenne.loop()} if there is no connection? - #5 by ahmed.blacky.xaver3

i mean when wifi is disconnected or no internet, the code goes in an infinite loop to search for connection and stops all the other code. So your entire code is never executed when there is no connection.

Hi again,
Well, I just found where the issue is.
When I use Cayenne.virtualWrite(); within the loop it totaly breaks when connection is lost.
Even after connection get back, it stucks (no debug or line updated in the monitor) and I have to manually reboot the board.

My test function:

void setup() {
  Serial.begin(9600);
  Cayenne.begin(username, password, clientID, ssid, wifiPassword);
}

void loop() {
  Cayenne.loop();
  Serial.println("Loop OK");
  //With the above line commented, it works well
  Cayenne.virtualWrite(5, "fakevalue", "bp", "c");

}

CAYENNE_DISCONNECTED() {
  Serial.println("Connection lost");
  bool disconnected = true;
  while (disconnected)
  {
    if( WiFi.status() == WL_CONNECTED ){
      Serial.println("Wifi is back...");
      disconnected = false;
    } else {
      Serial.println("No wifi...");
    }
    delay(2000); 
  }
}

Is it a bug? If no, what can you suggest?
Thanks

Well, I’ve finaly rewrite almost all my code.
What I’ve done is:

  • Do not use Cayenne.virtualWrite(); within the loop and use CAYENNE_OUT_DEFAULT() instead
  • Create a new function to put all the loop content in it
  • Use CAYENNE_CONNECTED() and CAYENNE_DISCONNECTED() to detect connection state change
  • Called my new loop function in CAYENNE_DISCONNECTED() so it still runs event if Cayenne is not connected
  • Remove the delay after the screen init (splashscreen) and before the Cayenne.begin() because some times the wifi failed to connect at startup because of this

My code is now a bit less factored, but it works great. It updates the wifi logo depending of the connection status, never breaks even after connection lost and automaticaly reconnect after wifi is back.

Hope my feedback will helps other :slight_smile:

#include <Adafruit_GFX.h> // Icons and lines
#include <Adafruit_SSD1306.h> // Oled display
#include <Adafruit_BME280.h> // Sensor
#include <math.h> // isnan()
#define CAYENNE_DEBUG
#define CAYENNE_PRINT Serial
#include <CayenneMQTTESP8266.h>

// Cayenne authentication info.
char username[] = "XXXXXXXXXXXXXX";
char password[] = "XXXXXXXXXXXXXX";
char clientID[] = "XXXXXXXXXXXXXX";

// WiFi network info.
char ssid[] = "XXXXXXXXXXXXXX";
char wifiPassword[] = "XXXXXXXXXXXXXX";
bool disconnected = true;

// Init I2C devices
Adafruit_BME280 bme; 
Adafruit_SSD1306 display(-1);

// I2C wiring
#define BME_MOSI D2
#define BME_SCK D1

const unsigned long UPDATE_PERIOD_MS = 2000;
unsigned long lastMillis = 0;

// Oled on/off
bool oledState = true;

// Temp calibration
const float tempOffset = -1.75;

// Battery voltage
const float VoltageMultiplier = ( 1 / 210.447761 ); //calculed with voltmeter
const float BatteryPercentage = 0;
const float BattMin = 3.3;
const float BattMax = 4.2;

unsigned long lastUpdate = 0;
char buf[64];

// Wifi icon size: 20W*15H px
const unsigned char wifi_icon [] PROGMEM = {
0x07, 0xc0, 0x1f, 0xf0, 0x7c, 0x7c, 0x70, 0x1c, 
0xc0, 0x06, 0xc3, 0x86, 0x0f, 0xe0, 0x1c, 0x70,
0x18, 0x30, 0x00, 0x00, 0x03, 0x80, 0x03, 0x80
};

void setup() {
  
    Serial.begin(9600);
    if (bme.begin()) {
        Serial.println("BME280 sensor connected");
    }
    display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
    display.dim(true);
    display.display(); // Show splashscreen during connection
    Cayenne.begin(username, password, clientID, ssid, wifiPassword);
    display.clearDisplay(); // Clears the screen and buffer
    
}

void loop() { 

  Cayenne.loop();
         
  if (millis() - lastUpdate >= UPDATE_PERIOD_MS) {
    LoopFunction();
  }
  
}

double LoopFunction(){
  
  lastUpdate = millis();
  float volt = analogRead(A0)*VoltageMultiplier; // Battery voltage

  // Prevent power consumption if voltage it too low
  if( volt < 3.3 ){

    // Erase all screen pixels
    display.clearDisplay(); // clears the screen and buffer
    display.fillRect(0, 0, 124, 68, BLACK );
    display.display();
      
  } else {
    
    Serial.println("____________________________________");
    Serial.print("Voltage = ");
    Serial.print(volt);
    Serial.println(" V");

    float bat = (volt - BattMin)/(BattMax - BattMin) * 100;
    if(bat > 100){
      bat = 100;
    }
    Serial.print("Battery = ");
    Serial.print(bat);
    Serial.println("%");
              
    float temp = bme.readTemperature() + tempOffset; // Celcius
    Serial.print("Temperature = ");
    Serial.print(temp);
    Serial.println(" °C");
      
    float humidity = bme.readHumidity(); // % 
    Serial.print("Humidity = ");
    Serial.print(humidity);
    Serial.println(" %");
      
    float pressure = (bme.readPressure() / 100.0F); // hPa
    Serial.print("Pressure = ");
    Serial.print(pressure);
    Serial.println(" hPa");
    
    if( oledState == true ){
      
      if (!isnan(temp) && !isnan(humidity) ) {
          
        display.clearDisplay();
        display.setTextSize(1);
        display.setTextColor(WHITE);
  
        if( bat == 100){
          display.fillRect(0, 0, 27, 12, 1 );
          display.setTextColor(BLACK);
          display.setCursor(2,2);
          snprintf(buf, sizeof(buf), "%.1fv", volt);
          display.print(buf);
          display.setTextColor(WHITE);
        } else {
          display.setCursor(5,2);
          snprintf(buf, sizeof(buf), "%.0f%%", bat);
          display.print(buf);
        }
  
        // Battery logo
        display.drawLine(27, 3, 27, 8, 1);
        display.drawLine(28, 3, 28, 8, 1);
        display.drawLine(29, 4, 29, 7, 1);
        display.drawRect(0, 0, 27, 12, 1 );
    
        display.setCursor(0,16);
        display.setTextSize(2);
        snprintf(buf, sizeof(buf), "%.2f ", temp);
        display.print(buf);
        display.print((char)247);
        display.println("C");
  
        snprintf(buf, sizeof(buf), "%.2f %%", humidity);
        display.println(buf);
        
        snprintf(buf, sizeof(buf), "%.1f hPa", pressure);
        display.println(buf);
  
      }
      
      if( !disconnected ){
        // Wifi logo
        display.drawBitmap(113, 0, wifi_icon, 15, 12, 1);
      } else {
        // Remove wifi icone and draw a cross
        display.fillRect(113, 0, 15, 12, BLACK );
        display.drawLine(116, 1, 128, 12, 1);
        display.drawLine(116, 11, 128, 0, 1); 
        display.display();
        delay(2000); 
      }
                
    } else {

        // Erase all screen pixels
        display.clearDisplay(); // Clears the screen and buffer
        display.fillRect(0, 0, 124, 68, BLACK );
        
     }

     display.display();

  }
  
}


CAYENNE_OUT_DEFAULT(){
  
  float volt = analogRead(A0)*VoltageMultiplier; // Battery voltage
  
  // Prevent power consumption if voltage it too low
  // Do not read neither send data
  if( volt > 3.3 ){
      
    float bat = (volt - BattMin)/(BattMax - BattMin) * 100;
    if(bat > 100){
      bat = 100;
    }
    float temp = bme.readTemperature() + tempOffset; // Celcius
    float humidity = bme.readHumidity(); // %         
    float pressure = (bme.readPressure() / 100.0F); // hPa
    
    Cayenne.virtualWrite(1, volt, "voltage", "v");
    Cayenne.virtualWrite(2, bat, "batt", "p");
    Cayenne.virtualWrite(3, temp, "temp", "c");
    Cayenne.virtualWrite(4, humidity, "rel_hum", "p");
    Cayenne.virtualWrite(5, pressure, "bp", "c"); 
  
    if( oledState == true ){
      // Update button status on Cayenne
      Cayenne.virtualWrite(6, 1, "digital_actuator", "d");
    } else {
      // Update button status on Cayenne
      Cayenne.virtualWrite(6, 0, "digital_actuator", "d");    
    }
  
  }
  
}

// Virtual button to power on/off the oled display
CAYENNE_IN(6){
  
  // get value sent from dashboard
  int currentValue = getValue.asInt();
  if (currentValue ==1){
    Serial.println("Oled on");
    oledState = true;
  } else {
    Serial.println("Oled off");
    oledState = false;
  }
  
}

CAYENNE_CONNECTED() {
  
  Serial.println("Connection OK");
  disconnected = false;
  
}

CAYENNE_DISCONNECTED() {
  
  Serial.println("Connection lost");
  disconnected = true;
  
  while (disconnected){
    if( WiFi.status() == WL_CONNECTED ){
      Serial.println("Wifi is back...");
      disconnected = false;
    } else {
      Serial.println("No wifi...");
      //Keep loop active (for display)               
      LoopFunction();
      delay(UPDATE_PERIOD_MS);
    }
  }
  
}

One last question. If there is no wifi when the device start, it stuck at “Connecting to wifi …”. Is there a way to make it tried to connect in the background without blocking the loop startup?

Thanks again for your help!

1 Like

it is not just about Cayenne.virtualWrite(); it is also about Cayenne.loop(); which checks for internet connection when Cayenne.virtualWrite(); is called and blocks the code when there is no connection.

it is one and the same if you create a new function and call it from the main loop.

yup, you are correct here. but only with wifi connection not with internet connection

your code is great for handling wifi connection. what about if the wifi has an active connection or not?
Do a quick test: if your device connected to your wifi and only disconnect the ethernet cable from your wifi router (or any other way in which there is only wifi connection and no internet connection).

use the same method you used to detect wifi connection in void setup and if there is wifi connection add Cayenne.begin(username, password, clientID, ssid, wifiPassword); or else wait for wifi connection. But again, it only checks for wifi connection and not the internet connection.

To solve all this issue we are working on a solution.