Wemos D1 mini clone WDT reset when GPIO 5 aca D1

Hello all,
I need some help on my project used to read epever and manage x3 opto driven relay boards connected to D3, D2 & D1 respectively. The epever part and relays connected to D3 & D2 are working fine, however when D1 is turned ON via the BLYNK_WRITE(vPin_in_3), the Wemos will timeout with a WDT reset after few seconds from activation. Same code is used for the other outputs and relay board was replaced without any improvement.

ets Jan  8 2013,rst cause:4, boot mode:(3,0)
wdt reset
load 0x4010f000, len 3460, room 16 
tail 4
chksum 0xcc
load 0x3fff20b8, len 40, room 4 
tail 4
chksum 0xc9
csum 0xc9
v000689f0
~ld

The board is driven from a separate 5V supply while the relays are being powered from a separate 3.3V supply.

/*************************************************************

  Epever Solar Tracer
 *************************************************************/
#include <FS.h>
#include "config.h"
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
#include <ESP8266HTTPUpdateServer.h>
#include <BlynkSimpleEsp8266.h>
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
#include <ModbusMaster.h>
#include <Ticker.h>
#include <uptime_formatter.h>
#include <DoubleResetDetector.h>
#include <ArduinoJson.h>          //https://github.com/bblanchon/ArduinoJson
#include <PubSubClient.h>

char blynk_template_id[] = BLYNK_TEMPLATE_ID;
char blynk_device_name[] = BLYNK_DEVICE_NAME;
char blynk_auth_token[] = BLYNK_AUTH_TOKEN;
String overTheAirURL = "";
char mqtt_server[40];
char mqtt_port[6] = MQTT_DEFAULT_PORT;
char mqtt_username[40];
char mqtt_password[40];
char time_2_wifi[5] = TIME_TO_CON_WIFI;

bool is_MQTT_trigger_Rly = false;

//flag for saving data
bool shouldSaveConfig = false;

//Real-time data (read only) input register
struct Epever_RTS{
  String param_name;
  unsigned long param_address;
  double result_value;
  int blynk_Vpin;
}
Epever_RTS[] ={
  {"PV array voltage",              0x00, 0,  vPin_VPV},
  {"PV array current",              0x01, 0,  vPin_IPV},
  {"PV array power",                0x02, 0,  vPin_PPV},
  {"Battery voltage",               0x04, 0,  vPin_VBatt},
  {"Battery charging current",      0x05, 0,  vPin_IBatt},
  {"Battery charging power",        0x06, 0,  vPin_PBatt},
  {"Load current",                  0x0D, 0,  vPin_ILoad},
  {"Load power",                    0x0E, 0,  vPin_PLoad},
  {"Battery Temperature",           0x10, 0,  vPin_BTemp},
  {"Temperature inside case",       0x11, 0,  vPin_CTemp}
};

//Statistical parameter (read only) input register
struct Epever_Stat{
  String param_name;
  unsigned long param_address;
  double result_value;
  int blynk_Vpin;
}
Epever_Stat[] ={
  {"Maximum input volt (PV) today",              0x00, 0,  vPin_VPVMax},
  {"Minimum input volt (PV) today",              0x01, 0,  vPin_VPVMin},
  {"Maximum battery volt today",                 0x02, 0,  vPin_VBMax},
  {"Minimum battery volt today",                 0x03, 0,  vPin_VBMin},
  {"Consumed energy today",                      0x04, 0,  vPin_LPTod},
  {"Consumed energy this month",                 0x06, 0,  vPin_LPMon},
  {"Consumed energy this year",                  0x08, 0,  vPin_LPYr},
  {"Generated energy today",                     0x0C, 0,  vPin_PVGenTod},
  {"Generated energy this month",                0x0E, 0,  vPin_PVGenMon},
  {"Generated energy this year",                 0x10, 0,  vPin_PVGenYr}
};

//Initialise blynker time
BlynkTimer timer;

//Initialise modbus
ModbusMaster node;

//Initialise Ticker
Ticker ticker;

//Initailise double reset
DoubleResetDetector drd(DRD_TIMEOUT, DRD_ADDRESS);

//Initialise MQTT
WiFiClient espClient;
PubSubClient client(espClient);
unsigned long lastMsg = 0;
#define MSG_BUFFER_SIZE	(50)
char msg[MSG_BUFFER_SIZE];
int value = 0;
long lastReconnectAttempt = 0;

/* Blynk Functions  */
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
// This function is called every time the Virtual Pin vPin_in_1 state changes
BLYNK_WRITE(vPin_in_1)
{
  // Set incoming value from pin vPin_in_1 to a variable
  int value = param.asInt();
  changeRlyOutState(vPin_in_1,vPin_out_1,OUT_D3_1,value);
  is_MQTT_trigger_Rly = false;  
}

// This function is called every time the Virtual Pin vPin_in_2 state changes
BLYNK_WRITE(vPin_in_2)
{
  // Set incoming value from pin vPin_in_2 to a variable
  int value = param.asInt();
  changeRlyOutState(vPin_in_2,vPin_out_2,OUT_D2_2,value);
  is_MQTT_trigger_Rly = false;
}

// This function is called every time the Virtual Pin vPin_in_3 state changes
BLYNK_WRITE(vPin_in_3)
{
    // Set incoming value from pin vPin_in_3 to a variable
  int value = param.asInt();
  changeRlyOutState(vPin_in_3, vPin_out_3,OUT_D1_3,value);
  is_MQTT_trigger_Rly = false;
}

void changeRlyOutState(int VpinIn, int VpinOut, int PpinOut, int value ){
  //VpinOut = Blynk Virtual Pin
  //PpinOut = ESP Physical Pin
  //value = pin state i.e. 1 / 0
  // start ticker with 0.4 because we received some data
  ticker.attach(0.4, tick);

  // Update Blynk state  
  Blynk.virtualWrite(VpinOut, value);
  //let's see who send the request
  //if MQTT then update the Blynk switch status for visual purpose only
  if(is_MQTT_trigger_Rly){
    Blynk.virtualWrite(VpinIn, value);
  }
  //Let's sync to the server
  Blynk.syncAll();
  //Publish the status to MQTT
  if(VpinOut == vPin_out_1){
    client.publish("SolarTracer/inTopic/relay/1/state_topic", (value == 1)? "ON" : "OFF");
  }
  else if(VpinOut == vPin_out_2){
    client.publish("SolarTracer/inTopic/relay/2/state_topic", (value == 1)? "ON" : "OFF");
  }
  else if(VpinOut == vPin_out_3){
    client.publish("SolarTracer/inTopic/relay/3/state_topic", (value == 1)? "ON" : "OFF");
  }
  // set the RELAY with the virtual switch of the variable:
  digitalWrite(PpinOut, (value == 1)? LOW : HIGH);
  Serial.print("[Blynk/MQTT]: Relay connected to IO " + String(PpinOut) + " value is: ");
  Serial.print((value == 1)? "ON" : "OFF");
  Serial.println();

  ticker.detach();
  //keep LED off
  digitalWrite(LED, HIGH);
}

// This function is called every time the device is connected to the Blynk.Cloud
BLYNK_CONNECTED()
{
  //Lets send log to USB & LOG to Blynk server
  Serial.println(F("[Blynk]: We are now connected to Blynk cloud server."));
  Blynk.logEvent("event_code", String("[Blynk]: We are now connected to Blynk cloud server."));
  //Update virtual pin
  Blynk.virtualWrite(vPin_blynk_status, WiFi.SSID() + " " + WiFi.localIP().toString());
  //Send the WIFI details as logs
  Blynk.logEvent("event_code", String("[WiFi Manager] Connected to: ") + WiFi.SSID() + " with IP address " + WiFi.localIP().toString());
  //Let's sync to the server
  Blynk.syncAll();
}

// This function sends Arduino's uptime every second to Virtual Pin 2.
void myTimerEvent()
{
  String value_str;
  char valuestring[50];

  if(Blynk.connected() && WiFi.isConnected()){
    // You can send any value at any time.
    // Please don't send more that 10 values per second.
    Blynk.virtualWrite(vPin_uptime, "up " + uptime_formatter::getUptime());
  }
  //Send data in various formats to MQTT Server
  if(client.connected() && WiFi.isConnected()){
    value_str = uptime_formatter::getUptime();
    value_str.toCharArray(valuestring, value_str.length() + 1);
    client.publish("SolarTracer/outTopic/uptime", valuestring);
  }   

}

// This function sends EPEVER values
void timerSendRealtime()
{
  uint8_t result;
  uint16_t data[6];
  
  String value_str;
  char valuestring[5];
  String outTopic_Str;
  char paramOutTopic[50];

  // Read all 18 realtime registers starting at 0x3100)
  node.clearResponseBuffer();
  result = node.readInputRegisters(0x3100, 18);
  if (result == node.ku8MBSuccess)
  {
    //Lets loop through each realtime struct value
    for (byte i = 0; i < sizeof(Epever_RTS) / sizeof(Epever_RTS[0]); i++) {
      //Check if we are going to request power registers
      if(Epever_RTS[i].param_address == 0x02 || Epever_RTS[i].param_address == 0x06 
      || Epever_RTS[i].param_address == 0x0E)
      {
        Epever_RTS[i].result_value = (long)(node.getResponseBuffer(Epever_RTS[i].param_address) |
                    node.getResponseBuffer(Epever_RTS[i].param_address+1) << 16)/100.0f;        
      }
      else {
          Epever_RTS[i].result_value = (long)node.getResponseBuffer(Epever_RTS[i].param_address)/100.0f;
      }
      //Print the output to serial debug
      Serial.print(F("[EPEVER]: "));
      Serial.print(Epever_RTS[i].param_name);
      Serial.print(" ");
      Serial.println(Epever_RTS[i].result_value);
      //Send data in various formats to Blynk Virtual Pins
      if(Blynk.connected() && WiFi.isConnected()){
        Blynk.virtualWrite(Epever_RTS[i].blynk_Vpin,Epever_RTS[i].result_value);
      }
      //Send data in various formats to MQTT Server
      if(client.connected() && WiFi.isConnected()){
        value_str = String(Epever_RTS[i].result_value,2);
        value_str.toCharArray(valuestring, value_str.length() + 1);

        outTopic_Str = String("SolarTracer/outTopic/" + Epever_RTS[i].param_name);
        outTopic_Str.replace(" ","_");
        //publish to server
        outTopic_Str.toCharArray(paramOutTopic, outTopic_Str.length() + 1);

        client.publish(paramOutTopic, valuestring);
      }     
    }   
  }
  else
  {
    //Read attempt failed thus send error to serial monitor
    modbusMaster_PrintError("0x3100",result);
    
  }

  // Let's Obtain the Battery SOC status from the 0x311A register
  node.clearResponseBuffer();
  result = node.readInputRegisters(0x311A, 1);
  if (result == node.ku8MBSuccess)
  {
    //Send data in various formats to Blynk Virtual Pins
    if(Blynk.connected() && WiFi.isConnected()){
      Blynk.virtualWrite(vPin_BSOC,(long)node.getResponseBuffer(0)/1.0f);
    }
    //Send data in various formats to MQTT Server
    if(client.connected() && WiFi.isConnected()){
      value_str = String(node.getResponseBuffer(0),2);
      value_str.toCharArray(valuestring, value_str.length() + 1);

      outTopic_Str = String("SolarTracer/outTopic/Battery SOC");
      outTopic_Str.replace(" ","_");
      //publish to server
      outTopic_Str.toCharArray(paramOutTopic, outTopic_Str.length() + 1);
      client.publish(paramOutTopic, valuestring);
    }
    //Print the output to serial debug
    Serial.print(F("[EPEVER]: "));
    Serial.print(F("Battery SOC"));
    Serial.print(" ");
    Serial.println(node.getResponseBuffer(0));  
  }
  else
  {
    //Read attempt failed thus send error to serial monitor
    modbusMaster_PrintError("0x311A",result);
  }

  // Let's Obtain the day status from the 0x2000 register
  /*node.clearResponseBuffer();
  result = node.readDiscreteInputs(0x200C, 1);
  if (result == node.ku8MBSuccess)
  {
    //Send data in various formats to Blynk Virtual Pins
    if(Blynk.connected() && WiFi.isConnected()){
      ((node.getResponseBuffer(0) == 1) ? Blynk.virtualWrite(vPin_SunStat,"Night") : Blynk.virtualWrite(vPin_SunStat,"Day"));
    }
    //Send data in various formats to MQTT Server
    if(client.connected() && WiFi.isConnected()){
      outTopic_Str = String("SolarTracer/outTopic/Day Status");
      outTopic_Str.replace(" ","_");
      //publish to server
      outTopic_Str.toCharArray(paramOutTopic, outTopic_Str.length() + 1);
      ((node.getResponseBuffer(0) == 1) ? client.publish(paramOutTopic, "Night") : client.publish(paramOutTopic, "Day"));
    }
    //Print the output to serial debug
    Serial.print(F("[EPEVER]: "));
    Serial.print(F("Day Status"));
    Serial.print(" ");
    Serial.println(node.getResponseBuffer(0));  
  }
  else
  {
    //Read attempt failed thus send error to serial monitor
    modbusMaster_PrintError("0x220C",result);
  }*/
  //toggle LED status
  tick();
}

// This function sends further EPEVER values.
void timerSendStatistics()
{
  uint8_t result;
  uint16_t data[6];

  String value_str;
  char valuestring[5];
  String outTopic_Str;
  char paramOutTopic[50];

  // Read 20 registers starting at 0x3300)
  node.clearResponseBuffer();
  result = node.readInputRegisters(0x3300, 20);
  if (result == node.ku8MBSuccess)
  {
    //Lets loop through each struct value
    for (byte i = 0; i < sizeof(Epever_Stat) / sizeof(Epever_Stat[0]); i++) {
      //Check if we are going to request power registers
      if(Epever_Stat[i].param_address == 0x04 || Epever_Stat[i].param_address == 0x06
      || Epever_Stat[i].param_address == 0x08 || Epever_Stat[i].param_address == 0x0C
      || Epever_Stat[i].param_address == 0x0E || Epever_Stat[i].param_address == 0x10)
      {
        Epever_Stat[i].result_value = (long)(node.getResponseBuffer(Epever_Stat[i].param_address) |
                    node.getResponseBuffer(Epever_Stat[i].param_address+1) << 16)/100.0f;        
      }
      else {
          Epever_Stat[i].result_value = (long)node.getResponseBuffer(Epever_Stat[i].param_address)/100.0f;
      }
      //Print the output to serial debug
      Serial.print(F("[EPEVER Stat]: "));
      Serial.print(Epever_Stat[i].param_name);
      Serial.print(" ");
      Serial.println(Epever_Stat[i].result_value);      
      //Send data in various formats to Blynk Virtual Pins
      if(Blynk.connected() && WiFi.isConnected()){
        Blynk.virtualWrite(Epever_Stat[i].blynk_Vpin,Epever_Stat[i].result_value);
      }
      //Send data in various formats to MQTT Server
      if(client.connected() && WiFi.isConnected()){
        value_str = String(Epever_Stat[i].result_value,2);
        value_str.toCharArray(valuestring, value_str.length() + 1);

        outTopic_Str = String("SolarTracer/outTopic/" + Epever_Stat[i].param_name);
        outTopic_Str.replace(" ","_");
        //publish to server
        outTopic_Str.toCharArray(paramOutTopic, outTopic_Str.length() + 1);
        
        client.publish(paramOutTopic, valuestring);
      } 
    }   
  }
  else
  {
    //Read attempt failed thus send error to serial monitor
    modbusMaster_PrintError("0x3300",result);
  }
  //toggle LED status
  tick();
}

//Blynk remote device reboot
BLYNK_WRITE(InternalPinDBG) {
  if (String(param.asStr()) == "reboot") {
    Serial.println(F("[Blynk]: Rebooting your device..."));

    // turn off peripherals, write state to EEPROM, etc.
    rebootDevice();    
  }
}

void rebootDevice(){
  //keep relay pins at high thus OFF
  setPins2StateHIGH();
  delay(2000);
  //Let's sync to the server
  Blynk.syncAll();
  //disconnect from server
  Blynk.disconnect();
  //Let's reboot now
  ESP.restart();
}

BLYNK_WRITE(InternalPinOTA) {
  Serial.println(F("[Blynk]: OTA Started"));
  overTheAirURL = param.asString();
  Serial.print(F("[Blynk]: overTheAirURL = "));  
  Serial.println(overTheAirURL);  
  
  WiFiClient my_wifi_client;
  HTTPClient http;
  
  http.begin(my_wifi_client, overTheAirURL);
  
  t_httpUpdate_return ret = ESPhttpUpdate.update(my_wifi_client, overTheAirURL);
  switch(ret) {
    case HTTP_UPDATE_FAILED:
        Serial.println(F("[Blynk] Update failed."));
        break;
    case HTTP_UPDATE_NO_UPDATES:
        Serial.println(F("[Blynk] Update no Update."));
        break;
    case HTTP_UPDATE_OK:
        Serial.println(F("[Blynk] Update ok.")); // may not be called since we reboot the ESP
        break;
  }
  Serial.println(F("[Blynk] Rebooting now!"));
  reboot();
}

void reboot()
{
 Serial.println(F("[Blynk]: Rebooting after OTA Update...")); 
#if defined(ARDUINO_ARCH_MEGAAVR)
  wdt_enable(WDT_PERIOD_8CLK_gc);
#elif defined(__AVR__)
  wdt_enable(WDTO_15MS);
#elif defined(__arm__)
  NVIC_SystemReset();
#elif defined(ESP8266) || defined(ESP32)
  ESP.restart();
#else
  #error "[Blynk]: MCU reset procedure not implemented"
#endif
  for (;;) {}
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
/* END Blynk functions */

void preTransmission()
{
  digitalWrite(RS485_CTR, 1);
}

void postTransmission()
{
  digitalWrite(RS485_CTR, 0);
}


void tick()
{
  //toggle state
  digitalWrite(LED, !digitalRead(LED));     // set pin to the opposite state
}

//callback notifying us of the need to save config
void saveConfigCallback () {
  Serial.println(F("[WiFi Manager]: Should save config"));
  shouldSaveConfig = true;
}

void configModeCallback (WiFiManager *myWiFiManager) {
  Serial.println(F("[WiFi Manager]: Entered config mode"));
  Serial.println(WiFi.softAPIP());

  drd.stop();
}

//Function to set pins as output
void setPins2Output(){
  pinMode(LED, OUTPUT);
  pinMode(OUT_D3_1, OUTPUT);
  pinMode(OUT_D2_2, OUTPUT);
  pinMode(OUT_D1_3, OUTPUT);
  pinMode(RS485_CTR, OUTPUT);
}

//function to set pins to a known state
void setPins2StateHIGH(){
  changeRlyOutState(vPin_in_1, vPin_out_1,OUT_D3_1,0);
  changeRlyOutState(vPin_in_2, vPin_out_2,OUT_D2_2,0);
  changeRlyOutState(vPin_in_3, vPin_out_3,OUT_D1_3,0);
  is_MQTT_trigger_Rly = true;
}

//function to report MODBUS errors to serial.
void modbusMaster_PrintError(String registerID , uint8_t result){
  Serial.print("[EPEVER]: Modbus node failed to read register "+ registerID +" with result (");
  Serial.print(result, HEX);
  switch (result)
  {
    case 0xE0 :
      Serial.println(F(") -> invalid response slave ID exception"));
      break;
    case 0xE1 :
      Serial.println(F(") -> invalid response function exception"));
      break;
    case 0xE2 :
      Serial.println(F(") -> response timed out exception"));
      break;
    case 0xE3 :
      Serial.println(F(") -> invalid response CRC exception"));
      break;
    default : break;;
  }
}
/* MQTT Functions  */
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
void MQTTcallback(char* topic, byte* payload, unsigned int length) {
  // handle message arrived
  String inMSG = "";
  
  Serial.print(F("[MQTT] Message received ["));
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) { 
    Serial.print((char)payload[i]);
    inMSG += (char)payload[i];
  }
  ;
  Serial.println();
  if(strcmp(topic, "SolarTracer/inTopic/relay/1") == 0){
    if(inMSG=="ON"){
      changeRlyOutState(vPin_in_1,vPin_out_1,OUT_D3_1,1);
    }
    else if(inMSG=="OFF"){
      changeRlyOutState(vPin_in_1,vPin_out_1,OUT_D3_1,0);
    }
    is_MQTT_trigger_Rly = true;
  }
  else if(strcmp(topic, "SolarTracer/inTopic/relay/2") == 0){
    if(inMSG=="ON"){
      changeRlyOutState(vPin_in_2,vPin_out_2,OUT_D2_2,1);
    }
    else if(inMSG=="OFF"){
      changeRlyOutState(vPin_in_2,vPin_out_2,OUT_D2_2,0);
    }
    is_MQTT_trigger_Rly = true;
  }
  else if(strcmp(topic, "SolarTracer/inTopic/relay/3") == 0){
    if(inMSG=="ON"){
      changeRlyOutState(vPin_in_3,vPin_out_3,OUT_D1_3,1);
    }
    else if(inMSG=="OFF"){
      changeRlyOutState(vPin_in_3,vPin_out_3,OUT_D1_3,0);
    }
    is_MQTT_trigger_Rly = true;
  }
  else if(strcmp(topic, "SolarTracer/inTopic/cmd") == 0){
    if(inMSG=="reboot"){
      rebootDevice();
    }
  }
}

//MQTT reconect function
boolean reconnect() {

  Serial.print(F("[MQTT] Attempting MQTT connection..."));
  // Create a random client ID
  String clientId = "ESP8266Client-";
  clientId += String(random(0xffff), HEX);

  // Attempt to connect
  if (client.connect(clientId.c_str(),mqtt_username,mqtt_password)) {
      Serial.println(F("[MQTT] connected"));
      // Once connected, publish an announcement...
      client.publish("SolarTracer/outTopic/", "hello world");
      // ... and resubscribe
      client.subscribe("SolarTracer/inTopic/relay/1");
      client.subscribe("SolarTracer/inTopic/relay/2");
      client.subscribe("SolarTracer/inTopic/relay/3");
      client.subscribe("SolarTracer/inTopic/reset");
  } 
  else {
      Serial.print(F("[MQTT] failed, rc="));
      Serial.print(String(client.state()));
      Serial.println(F(" try again soon"));
    }
  return client.connected();
}
//""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
/* END Blynk functions */

void setup()
{
  // set the digital pin as output:
  setPins2Output();  
  //keep relay pins at high thus OFF
  setPins2StateHIGH();
  //Keep modbus control pin low
  digitalWrite(RS485_CTR, LOW);
  // start ticker with 0.5 because we start in AP mode and try to connect
  ticker.attach(0.5, tick);

  // explicitly set mode, esp defaults to STA+AP
  WiFi.mode(WIFI_STA);

  // Initialise Debug console
  delay(2000);
  Serial.begin(HW_SERIAL_BAUD_RATE);

  //print the banner and firmware version
  Serial.println(F("                                                              "));
  Serial.println(F("   _____       __              ______                         "));
  Serial.println(F("  / ___/____  / /___ ______   /_  __/________ _________  _____"));
  Serial.println(F("  \__ \/ __ \/ / __ `/ ___/    / / / ___/ __ `/ ___/ _ \/ ___/"));
  Serial.println(F(" ___/ / /_/ / / /_/ / /       / / / /  / /_/ / /__/  __/ /    "));
  Serial.println(F("/____/\____/_/\__,_/_/       /_/ /_/   \__,_/\___/\___/_/     "));
  Serial.println(F("                                                              "));
  Serial.println(F("                                                              "));
  Serial.println("[ESP8266]: Firmware Version: " + String(BLYNK_FIRMWARE_VERSION));
  Serial.println(F("[ESP8266]: Author: Christmark Chircop"));
  Serial.println(F("[ESP8266]: Supporting Plug-in: MQTT & Blynk"));

  //read configuration from FS json
  Serial.println(F("[SPIFFS]: mounting FS..."));

  if (SPIFFS.begin()) {
    Serial.println(F("[SPIFFS]: mounted file system"));
    if (SPIFFS.exists("/config.json")) {
      //file exists, reading and loading
      Serial.println(F("[SPIFFS]: reading config file"));
      File configFile = SPIFFS.open("/config.json", "r");
      if (configFile) {
        Serial.println(F("[SPIFFS]: opened config file"));
        size_t size = configFile.size();
        // Allocate a buffer to store contents of the file.
        std::unique_ptr<char[]> buf(new char[size]);

        configFile.readBytes(buf.get(), size);

 #if defined(ARDUINOJSON_VERSION_MAJOR) && ARDUINOJSON_VERSION_MAJOR >= 6
        DynamicJsonDocument json(1024);
        auto deserializeError = deserializeJson(json, buf.get());
        serializeJson(json, Serial);
        if ( ! deserializeError ) {
#else
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject(buf.get());
        json.printTo(Serial);
        if (json.success()) {
#endif
          Serial.println(F("\n[SPIFFS]: parsed json"));
          strcpy(blynk_template_id, json["blynk_template_id"]);
          strcpy(blynk_device_name, json["blynk_device_name"]);
          strcpy(blynk_auth_token, json["blynk_auth_token"]);
          strcpy(mqtt_server, json["mqtt_server"]);
          strcpy(mqtt_port, json["mqtt_port"]);
          strcpy(mqtt_username, json["mqtt_username"]);
          strcpy(mqtt_password, json["mqtt_password"]);
          strcpy(time_2_wifi, json["time_2_wifi"]);
        } else {
          Serial.println(F("[SPIFFS] failed to load json config"));
        }
        configFile.close();
      }
    }
  } else {
    Serial.println(F("[SPIFFS] failed to mount FS"));
  }
  //end read

  // The extra parameters to be configured (can be either global or just in the setup)
  // After connecting, parameter.getValue() will get you the configured value
  // id/name placeholder/prompt default length
  WiFiManagerParameter custom_blynk_template_id("blynk_template_id", "Blynk Template ID", blynk_template_id, 34);
  WiFiManagerParameter custom_blynk_device_name("blynk_device_name", "Blynk Device Name", blynk_device_name, 34);
  WiFiManagerParameter custom_blynk_auth_token("blynk_auth_token", "Blynk API token", blynk_auth_token, 34);
  WiFiManagerParameter custom_mqtt_server("mqtt_server", "MQTT Server", mqtt_server, 40);
  WiFiManagerParameter custom_mqtt_port("mqtt_port", "MQTT Port Number", mqtt_port, 6);
  WiFiManagerParameter custom_mqtt_username("mqtt_username", "MQTT Username", mqtt_username, 40);
  WiFiManagerParameter custom_mqtt_password("mqtt_password", "MQTT Password", mqtt_password, 40);
  WiFiManagerParameter custom_time_2_wifi("time_2_wifi", "Time (min) to connect to WIFI (0 = disable)", time_2_wifi, 5);
  //WiFiManager
  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wm;  

  //set config notify callback
  wm.setAPCallback(configModeCallback);
  wm.setSaveConfigCallback(saveConfigCallback);  

  //Add some custom parameters
  wm.addParameter(&custom_blynk_template_id);
  wm.addParameter(&custom_blynk_device_name);
  wm.addParameter(&custom_blynk_auth_token);
  wm.addParameter(&custom_mqtt_server);
  wm.addParameter(&custom_mqtt_port);
  wm.addParameter(&custom_mqtt_username);
  wm.addParameter(&custom_mqtt_password);
  wm.addParameter(&custom_time_2_wifi);
  //Set the portal to dark mode 
  wm.setClass("invert");

  if (drd.detectDoubleReset()) {
    Serial.println("[Double Reset]: We have detected double reset hence entering config mode.");
    wm.startConfigPortal(BLYNK_DEVICE_NAME,WIFI_AP_DEFAULT_PASSWORD);
  } 
  else {
    ticker.detach();
    // start ticker with 0.3
    ticker.attach(0.3, tick);
    //Wait here for some time to ensure wifi is available 
    if(atoi(time_2_wifi)>0){
      delay(atoi(time_2_wifi) * 60 * 1000);
    }    

    //automatically connect using saved credentials if they exist
    //If connection fails it starts an access point with the specified name
    if(wm.autoConnect(BLYNK_DEVICE_NAME,WIFI_AP_DEFAULT_PASSWORD)){
      Serial.println(F("[WiFi Manager]: We have connected to WIFI"));
      //Keep modbus CTR OFF pin HIGH
      digitalWrite(RS485_CTR, HIGH);
    }
    else {
      Serial.println(F("[WiFi Manager]: Entering config portal."));
    }
  }

  //read updated parameters
  strcpy(blynk_template_id, custom_blynk_template_id.getValue());
  strcpy(blynk_device_name, custom_blynk_device_name.getValue());
  strcpy(blynk_auth_token, custom_blynk_auth_token.getValue());
  strcpy(mqtt_server, custom_mqtt_server.getValue());
  strcpy(mqtt_port, custom_mqtt_port.getValue());
  strcpy(mqtt_username, custom_mqtt_username.getValue());
  strcpy(mqtt_password, custom_mqtt_password.getValue());
  strcpy(time_2_wifi, custom_time_2_wifi.getValue());

  //save the custom parameters to FS
  if (shouldSaveConfig) {
    Serial.println(F("[SPIFFS] saving config"));
 #if defined(ARDUINOJSON_VERSION_MAJOR) && ARDUINOJSON_VERSION_MAJOR >= 6
    DynamicJsonDocument json(1024);
#else
    DynamicJsonBuffer jsonBuffer;
    JsonObject& json = jsonBuffer.createObject();
#endif
    json["blynk_template_id"] = blynk_template_id;
    json["blynk_device_name"] = blynk_device_name;
    json["blynk_auth_token"] = blynk_auth_token;
    json["mqtt_server"] = mqtt_server;
    json["mqtt_port"] = mqtt_port;
    json["mqtt_username"] = mqtt_username;
    json["mqtt_password"] = mqtt_password;
    json["time_2_wifi"] = time_2_wifi;

    File configFile = SPIFFS.open("/config.json", "w");
    if (!configFile) {
      Serial.println(F("[SPIFFS] failed to open config file for writing"));
    }

#if defined(ARDUINOJSON_VERSION_MAJOR) && ARDUINOJSON_VERSION_MAJOR >= 6
    serializeJson(json, Serial);
    serializeJson(json, configFile);
#else
    json.printTo(Serial);
    json.printTo(configFile);
#endif
    configFile.close();
    //end save
  }
  Serial.println(F("[SPIFFS] The values in the file are: "));
  Serial.println("\blynk_template_id : " + String(blynk_template_id));
  Serial.println("\blynk_device_name : " + String(blynk_device_name));
  Serial.println("\tblynk_auth_token : " + String(blynk_auth_token));
  Serial.println("\tmqtt_server : " + String(mqtt_server));
  Serial.println("\tmqtt_port : " + String(mqtt_port));
  Serial.println("\tmqtt_username : " + String(mqtt_username));
  Serial.println("\ttime_2_wifi : " + String(time_2_wifi));
  
  //Configure Blynk
  Blynk.config(blynk_auth_token);

  //Configure MQTT
  client.setServer(mqtt_server,atoi(mqtt_port));  //Set the MQTT server as per parameter in config.h
  client.setCallback(MQTTcallback); //Callback function
  
  // Modbus slave ID 1
  node.begin(1, Serial);
  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);

  // Setup a function to be called every second
  timer.setInterval(1000L, myTimerEvent);
  // Setup a function to be called every 4 second
  timer.setInterval(4000L, timerSendRealtime);
  // Setup a function to be called every 90 seconds
  timer.setInterval(90000L, timerSendStatistics);

  ticker.detach();
  //keep LED off
  digitalWrite(LED, HIGH); 
  
  // You can also call drd.stop() when you wish to no longer
  // consider the next reset as a double reset.
  drd.stop();
}

void loop()
{
  //local variables
  static int blynk_connect_attempts = 0;

  // Call the double reset detector loop method every so often,
  // so that it can recognise when the timeout expires.
  drd.loop();

  //Check if we connected to blynk if not try again only if we are connected to wifi
  if(Blynk.connected() && WiFi.isConnected()){  
    Blynk.run();
  } 
  else if(!Blynk.connected() && WiFi.isConnected())
  {
    Serial.print(F("[Blynk]: We are trying to connect to Blynk cloud server. Attempt: "));
    Serial.print(String(blynk_connect_attempts));
    Serial.println();
    Blynk.connect();
    blynk_connect_attempts++;
  }
  timer.run();

  if (!client.connected() && WiFi.isConnected()) {
    long now = millis();
    if (now - lastReconnectAttempt > (MQTT_TIMEOUT*1000)) {
      lastReconnectAttempt = now;
      // Attempt to reconnect
      if (reconnect()) {
        lastReconnectAttempt = 0;
      }
    }
  }
  else if(client.connected() && WiFi.isConnected()){
    client.loop();
  }
  
}

Thanks