Issue with Deep Sleep on ESP 8266

NodeMCU 8266 over WiFi, Blynk on iOS using Blynk Server. Ver. 0.6.7

I have followed the recommendation that I could find and have only Blynk.run and Time.run in the loop function. However after pushing value to Blynk server with an interval >5sec before going to deep sleep, I do have random updates?

// cyclic event logics
void cyclicEvent()
{
  fanCycle++;

  readSensor();

  if (Blynk.connected())
  {
    Blynk.virtualWrite(V2, t);
    Blynk.virtualWrite(V3, h);
    Blynk.run();
  }

  if (!fanState)
  {
    Blynk.run();
    // Store the fan cycle in RTC memory before deep sleep
    ESP.rtcUserMemoryWrite(0, (uint32_t *)&fanCycle, sizeof(fanCycle));
    // Sleep for 5 mins
    ESP.deepSleep(30000000);
  }
}

I don’t agree with the idea of using timer.run in the void loop with deep sleep.
In fact, you usually don’t need any code in the void loop at all.

However, the snippet of code that you’ve posted is insufficient to figure-out what exactly it is that yore doing, and where you are going wrong.

Pete.

Please find the entire code here:

#include <Arduino.h>

#include <EEPROM.h>

#include <ESP8266WiFi.h>

#include <ESP8266HTTPClient.h>

#include <ESP8266httpUpdate.h>

#include <WiFiUdp.h>

#include <BlynkSimpleEsp8266.h>

#include "DHT.h"

#define DEBUG_INFO

// Define the digital pin connected to the DHT sensor and type

#define DHTPIN D2

#define DHTTYPE DHT22

// Firmware revision, firmware server url and project unique identifier

constexpr char projectID[] = "FanControl_B01"; // ESP8266 on NodeLCU simulating

constexpr int32_t FW_VERSION = 113;            // 0.01.13

constexpr char fwWebServer[] = "MASTERYODA";   //"192.168.1.47";

constexpr char fwLocation[] = "/iothome/firmwares/";

// Blynk token

constexpr char auth[] = "xxxx";

BlynkTimer timer;

// retry attempt to connect to WiFi

const int retryConnectWiFi = 25;

// Initialize DHT sensor.

DHT dht(DHTPIN, DHTTYPE);

// inititate global WiFi client

WiFiClient wifiClient;

// Global sesnsor data

float t = 0.0;

float h = 0.0;

// Humidity threshold

static float humThreshold;

// timers counter

millis_time_t tSensorCycle = 0;

// fan state 0=off, 1=on

volatile uint8_t fanState = 0;

// fan cycle & fan threshold flag

volatile uint32_t fanCycle = 0;

constexpr uint8_t fanMaxCycle = 11;

volatile uint8_t humAboveThreshold = 0;

// Read the data from the DHT22 sensor

bool readSensor()

{

  // Reading temperature or humidity takes about 250 milliseconds!

  h = dht.readHumidity();

  // Read temperature as Celsius (the default)

  t = dht.readTemperature();

  // Check if any reads failed and exit early (to try again).

  if (isnan(h) || isnan(t))

  {

#ifdef DEBUG_INFO //Debug info

    Serial.println(F("Failed to read from DHT sensor!"));

#endif

    return false;

  }

  else

  {

#ifdef DEBUG_INFO //Debug info

    Serial.print(F("Humidity: "));

    Serial.print(h);

    Serial.print(F("%  Temperature: "));

    Serial.print(t);

    Serial.print(F("°C \n"));

#endif

    return true;

  }

}

// Turn the fan on and off

void FanControl(byte fanOnOff)

{

  if (fanOnOff)

  {

    fanState = 1;

    digitalWrite(D1, HIGH);

  }

  else

  {

    fanState = 0;

    digitalWrite(D1, LOW);

  }

  if (Blynk.connected())

  {

    Blynk.virtualWrite(V1, fanState);

    Blynk.run();

  }

}

// cyclic event logics

void cyclicEvent()

{

  fanCycle++;

#ifdef DEBUG_INFO //Debug info

  Serial.println("Reading sensor.");

#endif

  readSensor();

#ifdef DEBUG_INFO //Debug info

  Serial.printf("Fan cycle value %d.\n", fanCycle);

  Serial.printf("Humididty Threshold = %f.\n", humThreshold);

#endif

  if (Blynk.connected())

  {

#ifdef DEBUG_INFO //Debug info

    Serial.println("Sending telemetry to Blynk.");

#endif

    Blynk.virtualWrite(V2, t);

    Blynk.virtualWrite(V3, h);

    Blynk.run();

  }

  

  // humidity above threshold

  if (h >= humThreshold)

  {

    if (!fanState)

    {

      FanControl(1);

    }

    fanCycle = 0;

    humAboveThreshold = true;

#ifdef DEBUG_INFO //Debug info

    Serial.println("Humidity above threshold.");

#endif

  }

  else

  {

    // transition from above to below humidity threshold

    if ((fanCycle < fanMaxCycle) && humAboveThreshold)

    {

      humAboveThreshold = false;

      FanControl(0);

    }

    // reached time cycle

    if (fanCycle == fanMaxCycle)

    {

      FanControl(1);

#ifdef DEBUG_INFO //Debug info

      Serial.println("Fan cycle reached.");

#endif

    }

    // after time cycle

    if (fanCycle > fanMaxCycle)

    {

      FanControl(0);

      fanCycle = 0;

#ifdef DEBUG_INFO //Debug info

      Serial.printf("Fan cycle reset %d.\n", fanCycle);

#endif

    }

  }

  if (!fanState)

  {

#ifdef DEBUG_INFO //Debug info

    Serial.println("Going to sleep.");

#endif

    Blynk.run();

    // Store the fan cycle in RTC memory before deep sleep

    ESP.rtcUserMemoryWrite(0, (uint32_t *)&fanCycle, sizeof(fanCycle));

    // Sleep for 5 mins

    ESP.deepSleep(30000000);

  }

}

// the setup function runs once when you press reset or power the board

void setup()

{

#ifdef DEBUG_INFO //Debug info

  // Get the serial output started */

  Serial.begin(115200);

  Serial.printf("\nBooting chip id %d with SDK %s and firmware %d\n", ESP.getChipId(), ESP.getSdkVersion(), FW_VERSION);

  Serial.println(ESP.getFullVersion());

#endif

  // inititate the DHT22 sensor

  dht.begin();

  // Set the pin that control the relay

  pinMode(D1, OUTPUT);

  // read the fan cycle valur from the RTC memory

  ESP.rtcUserMemoryRead(0, (uint32_t *)&fanCycle, sizeof(fanCycle));

  // locate the humidity threshold from the flask memory (0)

  EEPROM.begin(4);

  // Setup a function to be called every second

  timer.setInterval(30000L, cyclicEvent);

  // local variables

  uint8_t counter = 0;

  // set WiFi mode to Station only

  WiFi.mode(WIFI_STA);

  if (WiFi.SSID().length() == 0)

  {

#ifdef DEBUG_INFO //Debug info

    Serial.print("Please use WPS on your router to connect this device.\n");

#endif

    WiFi.beginWPSConfig();

  }

  // attempt to connect to the access point (AP), retry and if unsuccesfull print diagnostics

  if (WiFi.SSID().length() != 0)

  {

    WiFi.begin();

#ifdef DEBUG_INFO //Debug info

    Serial.print("Connecting to Wifi.");

#endif

    while (WiFi.status() != WL_CONNECTED && counter <= retryConnectWiFi)

    {

#ifdef DEBUG_INFO //Debug info

      Serial.print(".");

#endif

      delay(1000);

      counter++;

    }

#ifdef DEBUG_INFO //Debug info

    Serial.println();

#endif

  }

  else

  {

#ifdef DEBUG_INFO //Debug info

    Serial.println("Authentication timeout.");

#endif

  }

  if (WiFi.status() == WL_CONNECTED)

  {

    // get the WiFi info

#ifdef DEBUG_INFO

    WiFi.printDiag(Serial);

    Serial.print("IP address: ");

    Serial.println(WiFi.localIP());

#endif

    // connect to Blynk

    Blynk.config(auth);

    Blynk.connect(10000);

    Blynk.syncVirtual(V1);

    Blynk.syncVirtual(V4);

    // WiFi.disconnect();

    // Build the url for the version file

    String fwVersionUrl = "http://";

    fwVersionUrl = fwVersionUrl + fwWebServer + fwLocation + projectID + ".version";

    // access the version file and get the string inside

    HTTPClient httpClient;

    httpClient.begin(wifiClient, fwVersionUrl);

    int httpCode = httpClient.GET();

#ifdef DEBUG_INFO //Debug info

    Serial.print(fwVersionUrl);

    Serial.printf("\nhttp return %d\n", httpCode);

#endif

    if (httpCode == 200)

    {

      String versionStr = httpClient.getString();

#ifdef DEBUG_INFO //Debug info

      Serial.print("Available firmware version: ");

      Serial.print(versionStr);

      Serial.println();

#endif

      long srvFwVersion = versionStr.toInt();

      // if the server firware version number is bigger than the one of the current firmwre, update the current firmware

      if (srvFwVersion > FW_VERSION)

      {

        String fwBinUrl = fwLocation;

        fwBinUrl = fwBinUrl + projectID + '_' + srvFwVersion + ".bin";

#ifdef DEBUG_INFO //Debug info

        Serial.printf("Updating to firmware %ld from ", srvFwVersion);

        Serial.print(fwBinUrl);

        Serial.println();

#endif

        t_httpUpdate_return ret = ESPhttpUpdate.update(wifiClient, fwWebServer, 80, fwBinUrl);

#ifdef DEBUG_INFO //Debug info

        Serial.print("HTTP return code ");

        Serial.print(ret);

        Serial.println();

#endif

      }

    }

    httpClient.end();

  }

}

// Action on Virtual Pin 1 - On/Off

BLYNK_WRITE(V1)

{

  int vpValue1 = param.asInt();

  FanControl(vpValue1);

  if (vpValue1)

  {

    fanCycle = 0;

  }

}

// Humidity Threshold adjustment

BLYNK_WRITE(V4)

{

  int vpValue1 = param.asInt();

  humThreshold = (float)vpValue1;

  EEPROM.write(0, vpValue1);

  EEPROM.commit();

}

void loop()

{

  Blynk.run();

  timer.run();
}

Why are you using deep sleep in this project?

Are you actually using 30000000 as your deep sleep duration (8.3 hours)?

Pete.

Because most of the time the system will be in not operational mode and hence do not need to to be powered. The deepsleep is in microsecond, so actually 300sec = 5 mins. The project works just fine except for the update to blynk before deepsleep.

Of course, I’d forgotten that!

If it was me, I’d leave the board running constantly, as it’s then responsive to Blynk commands whenever you want it to be.

If you want to go with deep sleep then I’d suggest that you take a look at this project for information on the best way to structure your code…

Pete.

Comparing my code strategy with the one from Christophe, I could only spot a delay of 100ms after the Blynk.run(). So I did the same although I thought that the delay being a blocking function would not benefits the update however, since the addition of the delay before going to sleep seems to have remove the issue of random update. Feel free to comment if you think that this makes any sense.

The delay seems to allow the send process to complete effectively.
In a similar way, with the ESP8266, you often see a delay after the deep sleep command. This ensures that the device has time to go to sleep before starting the code loop again - which somehow stops deep sleep happening.

Interestingly, I’m doing some deep sleep with an ESP32 TTGO T-Call using GSM connection at the moment. It’s behaving differently to anything I’ve observed with an ESP8266 connected via WiFi, which I think is down to the longer ping time increasing the latency and producing some odd results with Blynk.
I’ve come-up with a solution by structuring my code totally differently and I’ll share the results here soon.

Pete.

Okay, as a follow-up to my previous post I was going to include my code, but I think it might be easier if I simply explained what my project is supposed to do, and how I’ve structured my code (and why).

The project is designed to wake-up every few hours, establish a GSM connection to the Blynk cloud server then check the status of an app button connected to pin V1.
If the button is on then an action is taken (a relay is toggled to reboot a device), and the app button is set to off again. A table widget is updated with either a “no action” or “rebooted” message and a timestamp.
The device then goes back to sleep for another few hours.

I ended-up structuring my code as follows…

void setup

  • Connect to Blynk via GSM, using Blynk.config/Blynk.connect so it’s non-blocking

void loop

  • contains Blynk.run
  • increments a counter to count how many times the void loop is executed
  • performs an if check against the counter which will make the device deepsleep if the counter reaches a present limit

BLYNK_CONNECTED

  • performs an rtc.begin to get the current time from the server (for the timestamp in the table)
  • performs a Blynk.syncVirtual(V1) to get the value of the switch widget

BLYNK_WRITE(V1)

Contains the main code which:

  • Gets the value of the switch widget attached to V1
  • Toggles the relay if necessary
  • Sets the switch widget attached to V1 to Off if necessary
  • Writes the results out to a table widget along with a timestamp
  • loops through a number of Blynk.run and delay(100) iterations
  • Initiates deepsleep again

This is what happens when the device boots up, or wakes from deepsleep (assuming that all goes according to plan)…

  1. void.setup executes and establishes a GSM connection and connects to the Blynk server.

  2. The BLYNK_CONNECTED function is then called and processed.

  3. You’d expect that the BLYNK_WRITE(V1) would then be called almost immediately, or possibly after one or two Blynk.runs being executed. However, that’s not what happens in practice - this maybe because of the slow ping times (550 - 60ms in testing) associated with the GSM connection. Instead, the void loop begins to execute, and goes through over 1000 iterations before the BLYNK_WRITE(V1) function actually executes.

  4. The main code contained within BLYNK_WRITE(V1) is executed. The relay is toggled if necessary then the V1 button widget is set to off (if necessary) and the table widget is updated. A number of Blynk.run commands, interspersed with 100ms delays are then needed to ensure that the V1 widget and tale are updated before going back to sleep. I use 6 Blynk.runs/delays in a for loop to achieve the desired results then initiate deepsleep.

Total wake time (assuming the relay doesn’t need to be toggled) is around 27 seconds, but most of this time is associated with establishing the GSM connection. The actual GSM connected time is around 9 seconds.

The counter in the void loop, and the associated if test is to deal with a situation where a connection is established with the GSM network, but the Blynk.connect is unsuccessful and times-out - because the Blynk server is unreachable.
In this situation, after the Blynk.connect times-out the void loop will start to execute. If no connection is established by the time the loop limit is reached then the device will go to sleep until next time. I use a shorter deepsleep time this situation, so I don’t have to wait as long before the device tries again.

Pete.

.