Device become offline randomly after some time

Blynk app shows Device offline after approx 10 min of operation. is any problem with my code?
The code is simple to turn on relay after one operation to another to automate washing.
Kindly help @Gunner

#define BLYNK_PRINT Serial
#include <BlynkSimpleEsp8266.h>

#define BLYNK_PRINT Serial
#include <ESP8266WiFi.h>
#include <SPI.h>
WidgetLCD lcd(V1);

BlynkTimer timer;




// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "XXXXXXX";

// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "XXX";
char pass[] = "XXX";

//-------- Relay State Setups -------//
#define CLOCKWISE HIGH
#define ANTICLOCKWISE LOW
#define RELAY_ON LOW
#define RELAY_OFF HIGH
//----------------------------------//
//int onoff;
// PORTS //
#define MotorPowerPort D1
#define MototrDirectionPort D2
#define WaterFillValvePort D5
#define WaterDrainValvePort D6
#define button1Pin D7

// GLOBAL PROCESS VARIABLES //
unsigned long process_start = 0L;
unsigned long process_duration = 0L;
int motor_status = CLOCKWISE;
int process = 0;
String process_name = "";
int process_max = 13;
long millis_padding = 0;
// ------------------------ //


void setup() {
  Serial.begin(115200);
 
  Blynk.begin(auth, ssid, pass);
  lcd.clear(); //Use it to clear the LCD Widget
  lcd.print(4, 0, "Washing"); // use: (position X: 0-15, position Y: 0-1, "Message you want to print")
  pinMode (button1Pin, INPUT);
  digitalWrite(button1Pin, HIGH);
  delay(1000);
  processInit();

 
}

void loop() {
  // put your main code here, to run repeatedly:
 Blynk.run();
  
  timer.run();

while (digitalRead(button1Pin) == LOW)
{if (doProcess())
  {
    processReset();
    
    lcd.print(4, 0, "Washing");
    lcd.print(4, 1, "Complete");
    delay(3000);
    
  }
}
}
BLYNK_WRITE(V10)
{
  Serial.println("WebHook data:");
  Serial.println(param.asStr());
}

  



//------------------PROCESS AND OPERATIONS ---------------------------------//


boolean doProcess() //Return is Completed !
{

  if (process == 0)drainWater(1);
  else if (process == 1)fillWater(3);
  else if (process == 2)doWash(4000, 1, "SOAKING");
  else if (process == 3)doSoak(15);
  else if (process == 4)doWash(4000, 10, "WASHING");
  else if (process == 5)drainWater(2);
  else if (process == 6)fillWater(2);
  else if (process == 7)doWash(5000, 8, "RINSING");
  else if (process == 8)drainWater(5);
  else if (process == 9)fillWater(3);
  else if (process == 10)doWash(6000, 7, "RINSING");
   else if (process == 11)Blynk.virtualWrite(V10, "https://maker.ifttt.com/trigger/hi/with/key/fkozy2KdfBixxxxxxxxxxxVjPmd1x");
   else if (process == 12)drainWater(8);
  else if (process == 13)Blynk.virtualWrite(V10, "https://maker.ifttt.com/trigger/hi/with/key/fxxxxxxxxxxxxxxxxxxxd1x");

  else return true;

  processUpdate();
  return false;
}




double process_pivot = 0;
void processUpdate()
{

  double process_current = millis_padded();


  if ((process_current - process_start) >= process_duration)
  {
    process++;
    processReset();
    delay(1000);
  }
  else
  {
    if ((process_current - process_pivot) > 1000) //If past a minimum of one sec
    {
      //double remain=(process_remain/1000)/60;
      process_pivot = process_current;


     lcd.clear();
      lcd.print(4,1 ,process_name);
      


      process_current = (process_current - process_start);

      process_current = process_current / 1000;


      if (process_current >= 60)
      {
        process_current = process_current / 60;
        lcd.print(0, 0,(int)process_current);
        lcd.print(3, 0, "Min");
      }
      else
      {
        lcd.print(0, 0,(int)process_current);
        lcd.print(3, 0,"Sec");
      }

      lcd.print(6, 0,"/");

      process_current = process_duration / 1000;
      if (process_current >= 60)
      {
        process_current = process_current / 60;
        lcd.print(8, 0,(int)process_current);
        lcd.print(10, 0,"Min");
      }
      else
      {
        lcd.print(8, 0,(int)process_current);
        lcd.print(10, 0,"Sec");
      }


    }
  }
}

void processInit()
{
  pinMode(MotorPowerPort, OUTPUT);
  pinMode(MototrDirectionPort, OUTPUT);
  pinMode(WaterFillValvePort, OUTPUT);
  pinMode(WaterDrainValvePort, OUTPUT);

  pinMode(button1Pin, INPUT_PULLUP);


  process_duration = 0;
  process = 0;
  process_start = millis_padded();


  motor_status = CLOCKWISE;
  digitalWrite(MotorPowerPort, RELAY_OFF);
  digitalWrite(MototrDirectionPort, CLOCKWISE);
  digitalWrite(WaterFillValvePort, RELAY_OFF);
  digitalWrite(WaterDrainValvePort, RELAY_OFF);
}

void processReset()
{
 
  process_start = millis_padded();
  process_duration = 0;
  motor_status = CLOCKWISE;
  digitalWrite(MotorPowerPort, RELAY_OFF);
  digitalWrite(MototrDirectionPort, CLOCKWISE);
  digitalWrite(WaterFillValvePort, RELAY_OFF);
  digitalWrite(WaterDrainValvePort, RELAY_OFF);
}


void setProcessDuration(int mins)
{
  process_duration = mins2millis(mins);
}

//---------------------Process In Detail --------------------------------------//
void doWash(int cycle_delay, int time_mins, String title)
{
  process_name = title;
  setProcessDuration(time_mins);
  if (motor_status == CLOCKWISE)motor_status = ANTICLOCKWISE;
  else motor_status = CLOCKWISE;
  digitalWrite(MotorPowerPort, RELAY_OFF); //TURN OFF MAIN POWER TO MOTOR
  delay(1000);
  digitalWrite(MototrDirectionPort, motor_status); //SET MOTOR DIRECTION
  delay(1000);
  digitalWrite(MotorPowerPort, RELAY_ON); //TURN ON MAIN POWER TO MOTOR
  delay(cycle_delay);
}


void fillWater(int time_mins)
{
  process_name = "FILLING";
  setProcessDuration(time_mins);
  digitalWrite(WaterFillValvePort, RELAY_ON);
}


void drainWater(int time_mins)
{
  process_name = "DRAINING";
  setProcessDuration(time_mins);
  digitalWrite(WaterDrainValvePort, RELAY_ON);
}

void doSpin(int time_mins, int DIR)
{
  process_name = "DRY";
  setProcessDuration(time_mins);
  digitalWrite(WaterDrainValvePort, RELAY_ON);
  delay(3000);
  digitalWrite(MototrDirectionPort, CLOCKWISE);
  delay(1000);
  digitalWrite(MotorPowerPort, RELAY_ON);
}

void doSoak(int time_mins)
{
  process_name = "SOAKING";
  setProcessDuration(time_mins);
  delay(1000);
  //JUST WAIT :)
}

//---------------------------------------------------------------------------------------//


unsigned long mins2millis(int mins)
{
  return (((unsigned long)mins) * 60 * 1000);
}


double millis_padded()
{
  return (millis() + millis_padding);
}

Having a While loop in your void loop is never a good ideas.

Pete.

I m newbie in both blynk and arduino.
In my case, condition has to be check every time to start from process =1 to process = 14.
Any solution to remove from loop.

Use an interrupt on the physical button (probably with some debounce code too) or you could poll the switch with a timer, but that’s rather messy and you’ll probably need to delete the timer later.

Pete.

one more doubt, i m unable to call a function like

BLYNK_WRITE(V1)
{
int onoff = param.asInt();
  if (onoff == 1) {
    fillWater(3);
    
  }  
}

Any specific reason?

Add some debugging to see if BLYNK_WRITE(V1) is actually being triggered.

Pete.

i added widget led and that works.
even changing to digitalwrite(pin,HIGH) also works.

I don’t really understand your answer.

Pete.

V1 actually triggered when using like this

BLYNK_WRITE(V15) {
  int onoff = param.asInt();
  if (onoff == 1) {
    digitalWrite(charger, LOW);
    
  }
}

but unable to call any function

Add some debugging at the start of your function to check if it is actually being executed.

Pete.

I m unable to understand. Please give a example.

No, place something (a serial print, virtual write, digital write etc} at the beginning of the function that you’re calling, to see if it is actually called.

Pete.

ok. got it.

I will check and come back.
And thanks for your help.

Removed while loop from void loop. Is it correct?@pete

#include <ESP8266WiFi.h>
#define BLYNK_PRINT Serial
#include <BlynkSimpleEsp8266.h>

#define BLYNK_PRINT Serial
#include <ESP8266WiFi.h>
#include <SPI.h>
WidgetLCD lcd(V1);

BlynkTimer timer;




// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "GhvvvvvvvvvvvvvvvvvvvvvbywkMx74";

// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "TP-vvh4A";
char pass[] = "8ggh7";

//-------- Relay State Setups -------//
#define CLOCKWISE HIGH
#define ANTICLOCKWISE LOW
#define RELAY_ON LOW
#define RELAY_OFF HIGH
//----------------------------------//
//int onoff;
// PORTS //
#define MotorPowerPort D1
#define MototrDirectionPort D2
#define WaterFillValvePort D5
#define WaterDrainValvePort D6
#define button1Pin D7

// GLOBAL PROCESS VARIABLES //
unsigned long process_start = 0L;
unsigned long process_duration = 0L;
int motor_status = CLOCKWISE;
int process = 0;
String process_name = "";
int process_max = 13;
long millis_padding = 0;
// ------------------------ //


void setup() {
  Serial.begin(115200);
 **timer.setInterval(1000L, startbuttonRead);**
  Blynk.begin(auth, ssid, pass);
  lcd.clear(); //Use it to clear the LCD Widget
  lcd.print(4, 0, "Washing"); // use: (position X: 0-15, position Y: 0-1, "Message you want to print")
  pinMode (button1Pin, INPUT);
  digitalWrite(button1Pin, HIGH);
  delay(1000);
  processInit();

 
}

void loop() {
  // put your main code here, to run repeatedly:
 Blynk.run();
  
  timer.run();

}
void startbuttonRead() {
  if (!Blynk.connected()) {
    return;
  }
**while (digitalRead(button1Pin) == LOW)**
**{if (doProcess())**
**  {**
**    processReset();**
**    **
**    lcd.print(4, 0, "Washing");**
**    lcd.print(4, 1, "Complete");**
**    delay(3000);**
**    **
**  }**
BLYNK_WRITE(V10)
{
  Serial.println("WebHook data:");
  Serial.println(param.asStr());
}


 


//------------------PROCESS AND OPERATIONS ---------------------------------//


boolean doProcess() //Return is Completed !
{

  if (process == 0)drainWater(1);
  else if (process == 1)fillWater(3);
  else if (process == 2)doWash(4000, 1, "SOAKING");
  else if (process == 3)doSoak(15);
  else if (process == 4)doWash(4000, 10, "WASHING");
  else if (process == 5)drainWater(2);
  else if (process == 6)fillWater(2);
  else if (process == 7)doWash(5000, 8, "RINSING");
  else if (process == 8)drainWater(5);
  else if (process == 9)fillWater(3);
  else if (process == 10)doWash(6000, 7, "RINSING");
   else if (process == 11)Blynk.virtualWrite(V10, "https://maker.ifttt.com/trigger/hi/with/key/fkozy2KdfBrdghiFdzLstyjytjytjbuscDnP2FNVjPmd1x");
   else if (process == 12)drainWater(8);
  else if (process == 13)Blynk.virtualWrite(V10, "https://maker.ifttt.com/trigger/hi/with/key/fkozy2KdfBiFduyjknP2FNVjPmd1x");

  else return true;

  processUpdate();
  return false;
}




double process_pivot = 0;
void processUpdate()
{

  double process_current = millis_padded();


  if ((process_current - process_start) >= process_duration)
  {
    process++;
    processReset();
    delay(1000);
  }
  else
  {
    if ((process_current - process_pivot) > 1000) //If past a minimum of one sec
    {
      //double remain=(process_remain/1000)/60;
      process_pivot = process_current;


     lcd.clear();
      lcd.print(4,1 ,process_name);
      


      process_current = (process_current - process_start);

      process_current = process_current / 1000;


      if (process_current >= 60)
      {
        process_current = process_current / 60;
        lcd.print(0, 0,(int)process_current);
        lcd.print(3, 0, "Min");
      }
      else
      {
        lcd.print(0, 0,(int)process_current);
        lcd.print(3, 0,"Sec");
      }

      lcd.print(6, 0,"/");

      process_current = process_duration / 1000;
      if (process_current >= 60)
      {
        process_current = process_current / 60;
        lcd.print(8, 0,(int)process_current);
        lcd.print(10, 0,"Min");
      }
      else
      {
        lcd.print(8, 0,(int)process_current);
        lcd.print(10, 0,"Sec");
      }


    }
  }
}

void processInit()
{
  pinMode(MotorPowerPort, OUTPUT);
  pinMode(MototrDirectionPort, OUTPUT);
  pinMode(WaterFillValvePort, OUTPUT);
  pinMode(WaterDrainValvePort, OUTPUT);

  pinMode(button1Pin, INPUT_PULLUP);


  process_duration = 0;
  process = 0;
  process_start = millis_padded();


  motor_status = CLOCKWISE;
  digitalWrite(MotorPowerPort, RELAY_OFF);
  digitalWrite(MototrDirectionPort, CLOCKWISE);
  digitalWrite(WaterFillValvePort, RELAY_OFF);
  digitalWrite(WaterDrainValvePort, RELAY_OFF);
}

void processReset()
{
 
  process_start = millis_padded();
  process_duration = 0;
  motor_status = CLOCKWISE;
  digitalWrite(MotorPowerPort, RELAY_OFF);
  digitalWrite(MototrDirectionPort, CLOCKWISE);
  digitalWrite(WaterFillValvePort, RELAY_OFF);
  digitalWrite(WaterDrainValvePort, RELAY_OFF);
}


void setProcessDuration(int mins)
{
  process_duration = mins2millis(mins);
}

//---------------------Process In Detail --------------------------------------//
void doWash(int cycle_delay, int time_mins, String title)
{
  process_name = title;
  setProcessDuration(time_mins);
  if (motor_status == CLOCKWISE)motor_status = ANTICLOCKWISE;
  else motor_status = CLOCKWISE;
  digitalWrite(MotorPowerPort, RELAY_OFF); //TURN OFF MAIN POWER TO MOTOR
  delay(1000);
  digitalWrite(MototrDirectionPort, motor_status); //SET MOTOR DIRECTION
  delay(1000);
  digitalWrite(MotorPowerPort, RELAY_ON); //TURN ON MAIN POWER TO MOTOR
  delay(cycle_delay);
}


void fillWater(int time_mins)
{
  process_name = "FILLING";
  setProcessDuration(time_mins);
  digitalWrite(WaterFillValvePort, RELAY_ON);
}


void drainWater(int time_mins)
{
  process_name = "DRAINING";
  setProcessDuration(time_mins);
  digitalWrite(WaterDrainValvePort, RELAY_ON);
}

void doSpin(int time_mins, int DIR)
{
  process_name = "DRY";
  setProcessDuration(time_mins);
  digitalWrite(WaterDrainValvePort, RELAY_ON);
  delay(3000);
  digitalWrite(MototrDirectionPort, CLOCKWISE);
  delay(1000);
  digitalWrite(MotorPowerPort, RELAY_ON);
}

void doSoak(int time_mins)
{
  process_name = "SOAKING";
  setProcessDuration(time_mins);
  delay(1000);
  //JUST WAIT :)
}

//---------------------------------------------------------------------------------------//


unsigned long mins2millis(int mins)
{
  return (((unsigned long)mins) * 60 * 1000);
}


double millis_padded()
{
  return (millis() + millis_padding);
}

}
  
}

I see many problems in your code, such as many long delay() calls in the loop(), especially the while loop @PeteKnight mentioned.
Those unwanted and long delay() calls will create erratic / random problems for Blynk connection / internal management / watchdog as well as timers as you have experienced (Offline randomly).

To eliminate the while in loop() to read the status of a Switch, the best approach is using Interrupt. But another good way is to use pseudo-timer such as the following code, embedded in a function in the loop() :

#define DEBOUNCING_TIME      5000L
//Debounce PIN_MANUAL_START
bool isManualSWPressed()
{
  static unsigned long previousPressed = 0;

  // Debounce / not allow repetitive
  // Only once per 5s
  if ( (previousPressed == 0) || (millis() - previousPressed) > DEBOUNCING_TIME )
  { 
    if (digitalRead(PIN_MANUAL_START) == LOW)
    {
      previousPressed = millis();
      return true;
    }
  }
  return false;
}

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

  if (isManualSWPressed())
     doSomething();
}

For the unwanted delay() calls, you can try to replace with timer.setTimeout() placing in strategic location.

For example, in doWash(), instead of

void doWash(int cycle_delay, int time_mins, String title)
{
  process_name = title;
  setProcessDuration(time_mins);
  if (motor_status == CLOCKWISE)motor_status = ANTICLOCKWISE;
  else motor_status = CLOCKWISE;
  digitalWrite(MotorPowerPort, RELAY_OFF); //TURN OFF MAIN POWER TO MOTOR
  delay(1000);
  digitalWrite(MototrDirectionPort, motor_status); //SET MOTOR DIRECTION
  delay(1000);
  digitalWrite(MotorPowerPort, RELAY_ON); //TURN ON MAIN POWER TO MOTOR
  delay(cycle_delay);
}

you can try something similar to this

void runWashingMotor()
{
  digitalWrite(MotorDirectionPort, motor_status); //SET MOTOR DIRECTION
  digitalWrite(MotorPowerPort, RELAY_ON); //TURN ON MAIN POWER TO MOTOR  
  #if DEBUG_LOOP
  Serial.println("Motor started @ " + String(millis()/1000) + "s");
  #endif
}

void doWash(int time_mins, String title)
{
  #define DELAY_RUNNING_MOTOR_MS      2000L
  process_name = title;
  setProcessDuration(time_mins);
  inProcess = true;
  // Reverse direction
  motor_status = !motor_status;
   
  digitalWrite(MotorPowerPort, RELAY_OFF); //TURN OFF MAIN POWER TO MOTOR

  timer.setTimeout(DELAY_RUNNING_MOTOR_MS, runWashingMotor);
  #if DEBUG_LOOP
    Serial.println(String(DELAY_RUNNING_MOTOR_MS/1000) + "s Motor timer started @ " + String(millis()/1000) + "s");
  #endif

  lcd.clear();
  lcd.print(0, 0, processName);  
}

It certainly will take some more of your time to have a better code.

1 Like

Thanks for your suggestion. I will go ahead as you mentioned.

what is this for? @khoih

For the unwanted delay() calls, you can try to replace with timer.setTimeout() placing in strategic location.

For example, in doWash(), instead of

void doWash(int cycle_delay, int time_mins, String title)
{
  process_name = title;
  setProcessDuration(time_mins);
  if (motor_status == CLOCKWISE)motor_status = ANTICLOCKWISE;
  else motor_status = CLOCKWISE;
  digitalWrite(MotorPowerPort, RELAY_OFF); //TURN OFF MAIN POWER TO MOTOR
  delay(1000);
  digitalWrite(MototrDirectionPort, motor_status); //SET MOTOR DIRECTION
  delay(1000);
  digitalWrite(MotorPowerPort, RELAY_ON); //TURN ON MAIN POWER TO MOTOR
  delay(cycle_delay);
}

you can try something similar to this

void runWashingMotor()
{
  digitalWrite(MotorDirectionPort, motor_status); //SET MOTOR DIRECTION
  digitalWrite(MotorPowerPort, RELAY_ON); //TURN ON MAIN POWER TO MOTOR  
  #if DEBUG_LOOP
  Serial.println("Motor started @ " + String(millis()/1000) + "s");
  #endif
}

void doWash(int time_mins, String title)
{
  #define DELAY_RUNNING_MOTOR_MS      2000L
  process_name = title;
  setProcessDuration(time_mins);
  inProcess = true;
  // Reverse direction
  motor_status = !motor_status;
   
  digitalWrite(MotorPowerPort, RELAY_OFF); //TURN OFF MAIN POWER TO MOTOR

  timer.setTimeout(DELAY_RUNNING_MOTOR_MS, runWashingMotor);
  #if DEBUG_LOOP
    Serial.println(String(DELAY_RUNNING_MOTOR_MS/1000) + "s Motor timer started @ " + String(millis()/1000) + "s");
  #endif

  lcd.clear();
  lcd.print(0, 0, processName);  
}

It certainly will take some more of your time to have a better code.
[/quote]

Is this not correct?

void motorleft()
{ 
  
  digitalWrite(MotorDirectionPort, HIGH); //SET MOTOR DIRECTION
  digitalWrite(MotorPowerPort, RELAY_ON); //TURN ON MAIN POWER TO MOTOR  
  
  #if DEBUG_LOOP
  Serial.println("Motor started @ " + String(millis()/1000) + "s");
  #endif
}

void motorright()
{ 
  
  digitalWrite(MotorDirectionPort, LOW); //SET MOTOR DIRECTION
  digitalWrite(MotorPowerPort, RELAY_ON); //TURN ON MAIN POWER TO MOTOR  
  
  #if DEBUG_LOOP
  Serial.println("Motor started @ " + String(millis()/1000) + "s");
  #endif
}
void cycledelay()
{ 
  
  digitalWrite(MotorDirectionPort, LOW); //SET MOTOR DIRECTION
  digitalWrite(MotorPowerPort, RELAY_OFF); //TURN ON MAIN POWER TO MOTOR  
  
  #if DEBUG_LOOP
  Serial.println("Motor started @ " + String(millis()/1000) + "s");
  #endif
}

void doWash(int time_mins, String title)
{
  #define DELAY_RUNNING_MOTOR_MS      4000L
  process_name = title;
  setProcessDuration(time_mins);
  
  // Reverse direction
  timer.setTimeout(4000L, motorright);
  timer.setTimeout(1000L, cycledelay);
   timer.setTimeout(4000L, motorleft);
   timer.setTimeout(1000L, cycledelay);
  #if DEBUG_LOOP
  Serial.println(String(DELAY_RUNNING_MOTOR_MS/1000) + "s Motor timer started @ " + String(millis()/1000) + "s");
  #endif
}

I want to add some time diffrence between motordirection from clockwise to anticlockwise or High to Low. But unable to do so.

That’s not correct. The timer.setTimeout() function will start the functions motorright(), etc, after certain time after you call. With your code, motorright() will be called after 4s, cycledelay() after 1s, and so on. That’s not what you want.
You like to have cycledelay() called after (4+1)s, and motorleft() called after (4+1+4)s, etc.

So your doWash() will be

void doWash(int time_mins, String title)
{
  #define DELAY_RUNNING_MOTOR_MS      4000L
  process_name = title;
  setProcessDuration(time_mins);
  
  // Reverse direction
  timer.setTimeout(4000L, motorright);
  timer.setTimeout(5000L, cycledelay);
   timer.setTimeout(9000L, motorleft);
   timer.setTimeout(10000L, cycledelay);
  #if DEBUG_LOOP
  Serial.println(String(DELAY_RUNNING_MOTOR_MS/1000) + "s Motor timer started @ " + String(millis()/1000) + "s");
  #endif
}

The inProcess variable was can be created to avoid calling those doWash(), doFill(), etc. functions repeatedly and unnecessarily in loop().

boolean doProcess() //Return is Completed !
{
  if (!inProcess)
  {
     if (process == 0)drainWater(1);
     else if (process == 1)fillWater(3);
     else if (process == 2)doWash(4000, 1, "SOAKING");
     .....
  }

thanks for your clarification.