Issues regarding timers and sending data from Blynk to device

Title: Watchdog Timeouts and Communication Issues with Bidirectional Data Flow on ESP8266

Hello Blynk Community,

I’m a new Plus Blynk user and recently migrated from another IoT cloud service. The transition was smooth, and I’m really impressed with the performance and UI in Blynk. However, I’ve run into some issues when implementing a bidirectional data flow between my Blynk UI and my device.

Project Overview:

  • Device: Custom-built weather station
  • Hardware: Currently using ESP8266 (Wemos D1 mini), planning to upgrade to ESP32
  • Objective: To reset timers for measurements and displays from the Blynk UI, aiming to conserve battery power with ESP deep sleep between measurements.

The Issue:

When I send data from Blynk to the device, I encounter multiple problems:

  1. Watchdog Timeouts
  2. Wi-fi Communication issues
  3. Device crashes and reboots

Interestingly, commenting out the code that sends data from Blynk to the device resolves these issues.

Suspected Cause:

I suspect the issue lies in my misunderstanding of how timers work in the Blynk ecosystem.

Questions:

  1. How do I effectively manage timers to prevent these issues?
  2. Are there any best practices for sending data from Blynk to the device to prevent timeouts and crashes?

I appreciate any help and feedback from the community. Thank you!

Relevant Code:

Here is the relevant portion of my code that handles the Blynk to device communication.

#define BLYNK_TEMPLATE_ID "#######"
#define BLYNK_TEMPLATE_NAME "#######"
#define BLYNK_AUTH_TOKEN "#######"
#define BLYNK_PRINT Serial

#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <LittleFS.h>
#include "DHT.h"
#include <Wire.h>
#include <eloquent.h>
#include <UrlEncode.h>
#include "RunningAverage.h"
#include "SparkFun_AS3935.h"
#include <Adafruit_BMP280.h>
#include <ESP8266HTTPClient.h>
#include <Adafruit_MLX90614.h>

// Global variables
const long datasetinterval = 1 * 60; // minutes
long measureinterval = 1; // minutes
long loopinterval = measureinterval * 60 * 1000; //loop of measureinterval minutes in milliseconds
long sleepinterval = 2 * 60 * 1000; // miliseconds

// Your WiFi credentials.
char ssid[] = "#######";
char pass[] = "#######";

BlynkTimer timer;

// This function is called every time the device is connected to the Blynk.Cloud
BLYNK_CONNECTED()
{
  Blynk.syncVirtual(V0);
}

const long timeIntervals[] = {60000L, 300000L, 900000L, 1800000L, 3600000L}; // 1 min, 5 min, 15 min, 30 min, 1 hour in milliseconds
BLYNK_WRITE(V0)
{
  Serial.println("Blynk.Cloud is writing something...");
  int value = param.asInt();

  if (value >= 0 && value <= 4) {
    Serial.print("Setting interval to ");
    Serial.print(timeIntervals[value] / 60000L);
    Serial.println(" minutes");

    measureinterval = timeIntervals[value] / 60000L; // Convert back to minutes
    loopinterval = timeIntervals[value]; // Already in milliseconds
    sleepinterval = timeIntervals[value]; // Already in milliseconds
    int newBufferSize = datasetinterval / measureinterval;
    initializeRunningAverageBuffers(newBufferSize);  // Resize the running average buffers

    timer.setInterval(timeIntervals[value], timer1);
    timer.setInterval(timeIntervals[value], timer2);
  } else {
    Serial.println("Invalid value");
  }
}

void timer1()
{
  updateSensorReadings(t, p, h, c);
  updateBuffers(t, p, h, c);
  printSensorValues(t, p, h, c);
  makeRainPredictionAndSendMessage(t, p, h, c);
  detectLightning();
  measureBattery();
  //loopCounter++; // Increment the counter
  //if (loopCounter >= n) { // If we've looped n times
    // Put Wemos D1 Mini to sleep (time is in microseconds)
  //  Serial.println("Going to sleep");
  //  ESP.deepSleep(sleepinterval * 1000);
  //}
  //}
}

void timer2()
{
  Blynk.virtualWrite(V1, t);
  Blynk.virtualWrite(V2, p);
  Blynk.virtualWrite(V3, h);
  Blynk.virtualWrite(V4, c);
  Blynk.virtualWrite(V5, ot);
  Blynk.virtualWrite(V6, at);
  Blynk.virtualWrite(V7, bat_percentage);
  Blynk.virtualWrite(V8, prob[0]*100.0);
  Blynk.virtualWrite(V9, prob[1]*100.0);
}

void setup()
{
  // Debug console
  Serial.begin(9600);
  Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);
  timer.setInterval(60000L, timer1);
  timer.setInterval(60000L, timer2);
  initializeSensors();
  initializeRunningAverageBuffers((datasetinterval / measureinterval));
  Serial.println(ESP.getResetInfo());
}

void loop()
{
  timer.run();
  Blynk.run();
}

These are rather vague issues. It would be good to understand EXACTLY what errors occur and in which situations.

The segment of code you’ve posted doesn’t really help, because it’s calling functions that don’t exist, and what happens inside these functions could have an impact on your Blynk connection.

When the device boots it creates an Always On connection to the Blynk server, and there two need to exchange handshake/heartbeat data on a regular basis.
This heartbeat only happens when Blynk.run() is executed
In addition, for the BLYNK_WRITE(vpin) callback function to be recognised and processed, Blynk.run() needs to be executed.

If your timed functions are taking too long to execute then the connection to Blynk can timeout.

Also, you have two timers that are being called at exactly the same frequency. As your MCU can’t multitask, these are effectively executed immediately after on another.

More on timers here…

If you want to implement deepsleep then you’ll need a very different code structure to the one you currently have, and it makes no sense to use timers in that situation. Generally, you’d want to do something like this…

Wake up
Connect to WiFi
Connect to Blynk
Wait until Blynk.syncVirtual(V0) has executed
Take readings
Go to sleep

You’ll notice that I refer to connecting to WiFi and connecting to Blynk as separate actions.
Blynk.begin() is a blocking function, and if it can’t create a connection to either WiFi or the Blynk server then all code execution will stop at that point. This means that your device will never reach the deepsleep code, and stay awake until your battery is exhausted.

A better alternative is to manually manage your WiFi connection then use Blynk.config() and Blynk.connect() instead of Blynk.begin().

Pete.

Thank you @PeteKnight

Yes indeed, one of the crashes where being caused by one of the functions that I didn’t posted, my fault.
I made some modifications to the code, now I am using timerIDs as per documentation and also using the changeInterval method.
Regarding the timeouts, I increased the timer frequency of the Blynk updater to a fixed 10 seconds instead of 60 or more and yes I didn’t thought much about the single core nature of ESP8266 and serialization of timers.
Regarding deepsleep, I am not doing it right now but thanks to pointing me in the right direction.

The only remaining error is the following, during code execution I see these messages on the serial console. It only happens whenever I change the measurement timers from the UI could this be a timeout?

[233617] Heartbeat timeout
[233918] Connecting to blynk.cloud:80
[234335] Ready (ping: 202ms).
[382013] Connecting to blynk.cloud:80
[388014] Connecting to blynk.cloud:8080
[394018] Connecting to blynk.cloud:80
[400020] Connecting to blynk.cloud:8080
[406024] Connecting to blynk.cloud:80
[412026] Connecting to blynk.cloud:8080

--------------- CUT HERE FOR EXCEPTION DECODER ---------------

Exception (29):
epc1=0x4000df64 epc2=0x00000000 epc3=0x00000000 excvaddr=0x00000000 depc=0x00000000

stack>>>

Googling the exception 29 Looks like it is ESP8266 StoreProhibited it appears to be a memory exhaustion. I will continue debugging it may well be my memory management although it is not clear to me why the reconnection events.
Probably migrating to ESP32 might solve this memory problem.

I’m not sure that I follow 100%, or understand how that will help.

Do you mean decreased?
Going to 10 seconds musts makes things worse.

Even dual-core MCUs won’t allow multi-tasking.

When you post errors, or serial output, it needs to have triple backticks the same as when you post code.

Have you tried using the Exception Decoder on your error?

Pete.

It appears that setting timer.setInterval inside BLYNK_WRITE handler was not changing the timer duration, changeInterval is indeed working.

The crash that I was experiencing was a memory leak in my code I was allocating buffers for storing measurements in memory and not properly freeing them when I made changes in the timers duration from the UI. It is working now. That’s why it happened when I changed from the UI.

I am running the update to Blynk every fixed 60 seconds again, no reconnection issues.

Regarding ESP32 I was pondering that it has more memory but the issue was in my code.

Here is the final code:

BlynkTimer timer;
int timerId1, timerId2;

// This function is called every time the device is connected to the Blynk.Cloud
BLYNK_CONNECTED()
{
  Blynk.syncVirtual(V0);
}

const long timeIntervals[] = {60000L, 300000L, 900000L, 1800000L, 3600000L}; // 1 min, 5 min, 15 min, 30 min, 1 hour in milliseconds
BLYNK_WRITE(V0)
{
  Serial.println("Blynk.Cloud is writing something...");
  int value = param.asInt();

  if (value >= 0 && value <= 4) {
    Serial.print("Setting interval to ");
    Serial.print(timeIntervals[value] / 60000L);
    Serial.println(" minutes");

    measureinterval = timeIntervals[value] / 60000L; // Convert back to minutes
    loopinterval = timeIntervals[value]; // Already in milliseconds
    sleepinterval = timeIntervals[value]; // Already in milliseconds
    int newBufferSize = datasetinterval / measureinterval;
    resizeRunningAverageBuffers(newBufferSize);
    timer.changeInterval(timerId2, timeIntervals[value]);
  } else {
    Serial.println("Invalid value");
  }
}

void timer1()
{
  Blynk.virtualWrite(V1, t);
  Blynk.virtualWrite(V2, p);
  Blynk.virtualWrite(V3, h);
  Blynk.virtualWrite(V4, c);
  Blynk.virtualWrite(V5, ot);
  Blynk.virtualWrite(V6, at);
  Blynk.virtualWrite(V7, bat_percentage);
  Blynk.virtualWrite(V8, prob[0]*100.0);
  Blynk.virtualWrite(V9, prob[1]*100.0);
}

void timer2()
{
  updateSensorReadings(t, p, h, c);
  updateBuffers(t, p, h, c);
  printSensorValues(t, p, h, c);
  makeRainPredictionAndSendMessage(t, p, h, c);
  measureBattery();
 }
}

void initializeRunningAverageBuffers(int initialSize) {
  airtemperature_buffer = new RunningAverage(initialSize);
  airpressure_buffer = new RunningAverage(initialSize);
  humidity_buffer = new RunningAverage(initialSize);
  clouds_buffer = new RunningAverage(initialSize);
}

void resizeRunningAverageBuffers(int newSize) {
  delete airtemperature_buffer;
  delete airpressure_buffer;
  delete humidity_buffer;
  delete clouds_buffer;
  airtemperature_buffer = new RunningAverage(newSize);
  airpressure_buffer = new RunningAverage(newSize);
  humidity_buffer = new RunningAverage(newSize);
  clouds_buffer = new RunningAverage(newSize);
}

void setup()
{
  Serial.begin(9600);
  Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);
  timerId1 = timer.setInterval(10000L, timer1);
  timerId2 = timer.setInterval(60000L, timer2);
  initializeSensors();
  initializeRunningAverageBuffers((datasetinterval / measureinterval));
}

void loop()
{
  timer.run();
  Blynk.run();
}

No, I think you’d need to delete the timer and re-create it for that to work.

The only real reason to move t9 an ESP32 would be more GPIO and Analog pins, and the ability to wake on interrupts.

If you’re planning on doing more with your weather station it’s something that I have quite a bit of experience with. There’s a topic here that I’ve shared some code in…

Pete.

Thank you for the links @PeteKnight

1 Like