One section of code constantly causes Blynk to disconnect

Hello all, everytime my code reaches a certain line of codes, the Wemos disconnects and this is causing me much anxiety. Below is the situation:

This code is used in my auto brewing code. I use the terminal widget to input Hop addition times. Then during the boil, when the elapsed time hit the hop add time it sends info to terminal and a notification.

Here is how I initialize the arrays holding the hop add time, and the information that is diaplyed in the terminal. I initialize a 20 sized array becuase there are usually not more that 4/5 hop adds, so then using the terminal widget I modify the first few indexes of those arrays:

int hopCounter; //Number of hop adds that I input, based upon my terminal input
int HopAddtime;
int HOPTIME[20]; // Hop times
String HOPINFO[20];  // HOPINFO;

Next is how I input the data to modify HOPTIME and HOPINFO arrays. I use the first letter T or I to decide which array to modify, then using comma delimited type I add in what should be in each index of the array. This step seems to work as the output to terminal confirms my entries.

BLYNK_WRITE(V14)  // Terminal for HOPTIME and HOPINFO input
{
  String fromTerminal = param.asStr();
  
    
  if (fromTerminal.endsWith("?")) {
   terminalHops.println("Comma delimited, and first letter followed by '!' resets previous inputs: HOPTIMES:start w/ 'T,'//HOPINFO: starts w/ 'I,'");
   terminalHops.flush(); 
      }
  
  if (fromTerminal.startsWith("T") ) { //Hoptimes input
      int lastIndex;
      
      if (fromTerminal.endsWith("!")){ //In case I need to reset everything and re input hop additions
       hopCounter = 0;
       lastIndex = 0;
      }
      
      fromTerminal.remove(0,1);
      for (int i = 0; i < fromTerminal.length(); i++) {
        if (fromTerminal.substring(i, i+1) == ",") {
          HOPTIME[hopCounter] = (fromTerminal.substring(lastIndex, i)).toInt();
          lastIndex = i + 1;
          hopCounter++;
        }
        if (i == fromTerminal.length() - 1) {  // If we're at the end of the string (no more commas to stop us)
          HOPTIME[hopCounter] = (fromTerminal.substring(lastIndex, i)).toInt();
        }
      }   
      terminalHops.print(HOPTIME[1]);
      terminalHops.print(" from ArrayHOPTIME ");
      terminalHops.print(hopCounter);
      terminalHops.println(" total inputs");
      terminalHops.flush();
      }

 
  
  if (fromTerminal.startsWith("I")){ //Hopinfo input
      int counter;
      int lastIndex;
      
      if (fromTerminal.endsWith("!")){ //In case I need to reset everything and re input hop additions
       counter = 0;
       lastIndex = 0;
      }
      
      fromTerminal.remove(0,1);
      for (int i = 0; i < fromTerminal.length(); i++) {
        if (fromTerminal.substring(i, i+1) == ",") {
          HOPINFO[counter] = fromTerminal.substring(lastIndex, i);
          lastIndex = i + 1;
          counter++;
        }
        if (i == fromTerminal.length() - 1) {  // If we're at the end of the string (no more commas to stop us)
          HOPINFO[counter] = fromTerminal.substring(lastIndex, i);
        }
      }   
      terminalHops.print(HOPINFO[1]);
      terminalHops.print(" from CharArrayHOPINFO ");
      terminalHops.print(counter);
      terminalHops.println(" total inputs");
      terminalHops.flush();        
      }
 }

This next part is where I get the disconnection error. This is during the boil phase, it should access the HOPINFO and HOPTIME arrays and then display what hops I need to add when the hop time is up. However every time my code switches to the boil phase, there are lines of “Add:” that display on the terminal and then the Wemos disconnects.

//Hop Additions Code
case 9:
  switchState =0;
  for (int h=0 ; h=hopCounter; h++) {
       HopAddtime = HOPTIME[h];
       if (ElapsedBoilTime >= (HopAddtime - 1.0) && ElapsedBoilTime <= (HopAddtime + 1.0)) { //BE SURE THAT THE +/- of the elapsedboil time doesnt result in overlay of two hop additions or else it could be confusing
        tone(BUZZER,500,2000);
        terminal.print("Add: ");
        terminal.println(HOPINFO[h]);
        terminal.flush();
        Blynk.notify(HOPINFO[h]);
        tone(BUZZER,500,2000);
       }
      }
break;

Below is my full code for reference:

#include 
#include 
#include 
#define ONE_WIRE_BUS D1 //Sensor location
OneWire oneWire(ONE_WIRE_BUS); 
DallasTemperature sensor(&oneWire);

//////////////////////BLYNK CODE
//#define BLYNK_PRINT Serial  
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <SimpleTimer.h>
SimpleTimer timer;
const char* ssid = "";
const char* password = "";
char auth[] = "";
WidgetTerminal terminal(V0);
WidgetTerminal terminalHops(V14);

////////Setting up variables to be declared by Blynk app
int MASHTEMP; // Desired Mash Temp(deg F)
int MASHTIME; //Desired Mash Time(milli sec.)
int BOILTIME; //Desired Boil Time(milli sec.)
int CHILLTIME; //Desired time to pump all wort through the wort chiller (milli sec.)
int hopCounter; //Number of hop adds that I input, based upon my terminal input
int HopAddtime;
int HOPTIME[20]; // Hop times
String HOPINFO[20];  // HOPINFO;

////////////Assign counters for independent and pause control buttons
int noButtonCounter;
int pacounter;
int pucounter;
int h1counter;
int h2counter;

////////////////////////Assign variables
float tanktemp; //Declaring temperature variables
unsigned long btstart;
unsigned long btstart1;
unsigned long btstart2;
unsigned long pausestart; //Time when pause button was pushed
unsigned long elapsedpausetime;
unsigned long elapsedpumptime;
unsigned long elapsedh1time;
unsigned long elapsedh2time;
unsigned long totalpausem;
unsigned long totalpauseb;
unsigned long totalpausec;
unsigned long startingtime; //overall starting time
unsigned long ElapsedPreMashTime;
unsigned long MashStartTime; //used for MASH TIME
unsigned long ElapsedMashTime;
unsigned long BoilStartTime; //used for BOIL TIME
unsigned long ElapsedBoilTime;
unsigned long ChillStartTime; //used for BOIL TIME
unsigned long ElapsedChillTime;


//Setup all states
int switchState;
boolean autostart = true;
boolean premash;
boolean mashPID;
boolean reheatpremash;
boolean mashrun;
boolean mashreheat;
boolean mashend;
boolean boilrun;
boolean reboil;
boolean boilend;
boolean chillrun;
boolean rechill;
boolean chillend;

//Pin Inputs/Outputs to Arduino:
const int BUZZER = D0;
const int HEATER1 = D5;
const int HEATER2 = D6;
const int PUMP = D7;

//Status variables for buttons
int val_start; 
int val_pause;
int val_pump;
int val_heater1;
int val_heater2;


void brewingstuff(){
  
//No button pressed nothing is on (Also works so that when Auto button is on and then turned off, everything shuts down
if (val_start == LOW && val_pause == LOW && val_pump == LOW && val_heater1 == LOW && val_heater2 == LOW){
      switch (noButtonCounter){
      case 0:
         digitalWrite(HEATER1, LOW);
         digitalWrite(HEATER2, LOW);
         digitalWrite(PUMP, LOW);
         terminal.println("No Button Pressed, to pause must press pause button!");
         terminal.flush();
         noButtonCounter = 1;
         pausestart = (millis()/60000);
      break;
      case 1:
       elapsedpausetime = ((millis()/60000) - pausestart);
       terminal.print("No button pressed for:  ");
       terminal.println(elapsedpausetime);
       terminal.flush();
      break;} 
  }

//If anything is pressed right after no button was pressed, reset no button counter, set re heat states if needed, and increase "pause" time
if ((val_start == HIGH || val_pause == HIGH || val_pump == HIGH || val_heater1 == HIGH || val_heater2 == HIGH) && noButtonCounter == 1){
        noButtonCounter = 0; 
        if (premash == true){
          reheatpremash = true; 
        }
        else if (mashrun == true){
          totalpausem = totalpausem + elapsedpausetime;
          terminal.print("Time Added to Mash: ");
          terminal.println(elapsedpausetime); 
        }
       else if (boilrun == true){
          reboil = true;
          totalpauseb = totalpauseb + elapsedpausetime;
          terminal.print("Time Added to Boil: ");
          terminal.println(elapsedpausetime);  
       }
       else if (chillrun == true){
          rechill = true;
          totalpausec = totalpausec + elapsedpausetime; 
          terminal.print("Time Added to Chill: ");
          terminal.println(elapsedpausetime); 
          
       }  
   elapsedpausetime=0;
  }
  
//pause stops everything  
if (val_pause == HIGH){
       switch (pacounter){
       case 0:
        Blynk.virtualWrite(V4,LOW);
        Blynk.virtualWrite(V6,LOW);
        Blynk.virtualWrite(V7,LOW);
        Blynk.virtualWrite(V8,LOW);
        val_start=LOW; 
        val_pump=LOW;
        val_heater1=LOW;
        val_heater2=LOW;
        pucounter=0;
        h1counter=0;
        h2counter=0;
        pausestart=(millis()/60000);
        pacounter=1;
        digitalWrite(HEATER1, LOW);
        digitalWrite(HEATER2, LOW);
        digitalWrite(PUMP, LOW);
        terminal.println("PAUSE WORKING");
      break;
      case 1:
       elapsedpausetime = ((millis()/60000) - pausestart);
       terminal.print("Manual Pause for: ");
       terminal.println(elapsedpausetime);
       terminal.flush();
      break;} 
  }
///Pause button on and then turned off it resets counter, re heats if needed, and increased pause time
if (pacounter == 1 && val_pause == LOW){
       pacounter = 0;
        if (premash == true){
          reheatpremash = true; 
        }
        else if (mashrun == true){
          totalpausem = totalpausem + elapsedpausetime; 
          terminal.print("Time Added to Mash: ");
          terminal.println(elapsedpausetime); 
        }
       else if (boilrun == true){
          reboil = true;
          totalpauseb = totalpauseb + elapsedpausetime;
          terminal.print("Time Added to Boil: ");
          terminal.println(elapsedpausetime);  
       }
       else if (chillrun == true){
          rechill = true;
          totalpausec = totalpausec + elapsedpausetime; 
          terminal.print("Time Added to Chill: ");
          terminal.println(elapsedpausetime); 
       }  
     elapsedpausetime=0;     
    }

//Button pressed on pump starts
if (val_start == LOW && val_pause == LOW && val_pump == HIGH){
      switch (pucounter){
       case 0:
        terminal.println("Pump starting, need to open ball valve!!");
        terminal.flush();
        tone(BUZZER,500,5000);
        digitalWrite(PUMP, HIGH);
        btstart=(millis()/60000);
        pucounter=1;
       break;
       case 1:
        elapsedpumptime = ((millis()/60000) - btstart);
        terminal.print("Manual Pump On for: ");
        terminal.println(elapsedpumptime);
        terminal.flush(); 
       break;
      }  

  }
///Pump button on and then turned off it stops pump
if (pucounter == 1 && val_pump == LOW){
    pucounter = 0;
    digitalWrite(PUMP, LOW);
        if (premash == true){
          reheatpremash = true; 
        }
        else if (mashrun == true){
          totalpausem = totalpausem + elapsedpumptime;
          terminal.print("Time Added to Mash: ");
          terminal.println(elapsedpausetime);  
        }
       else if (boilrun == true){
          reboil = true;
          totalpauseb = totalpauseb + elapsedpumptime;
          terminal.print("Time Added to Boil: ");
          terminal.println(elapsedpausetime);  
       }
       else if (chillrun == true){
          rechill = true;
          totalpausec = totalpausec + elapsedpumptime;
          terminal.print("Time Added to Chill: ");
          terminal.println(elapsedpausetime);  
       }
    elapsedpumptime = 0;  
  }

//Heater 1 button pressed on heater starts
if (val_start == LOW && val_pause == LOW && val_heater1 == HIGH){
      switch (h1counter) {
      case 0: 
        btstart1=(millis()/60000);
        tone(BUZZER,500,5000);
        terminal.println("Heat 1 starting, need cover w water!!");
        terminal.flush();
        digitalWrite(HEATER1, HIGH);
        h1counter=1;
      break;
      case 1: 
       elapsedh1time = ((millis()/60000) - btstart1);
       terminal.print("Manual Heater 1 On for: ");
       terminal.println(elapsedh1time);
       terminal.flush();
      break; } 
  } 
///When Heater1 button on and then turned off it stops one heater
if (h1counter == 1 && val_heater1 == LOW){
        h1counter = 0;
        digitalWrite(HEATER1, LOW);
        if (premash == true){
          reheatpremash = true; 
        }
        else if (mashrun == true){
          totalpausem = totalpausem + elapsedh1time;
          terminal.print("Time Added to Mash: ");
          terminal.println(elapsedpausetime);  
        }
       else if (boilrun == true){
          reboil = true;
          totalpauseb = totalpauseb + elapsedh1time; 
          terminal.print("Time Added to Boil: ");
          terminal.println(elapsedpausetime); 
       }
       else if (chillrun == true){
          rechill = true;
          totalpausec = totalpausec + elapsedh1time;
          terminal.print("Time Added to Chill: ");
          terminal.println(elapsedpausetime);  
       }  
    elapsedh1time=0; 
  }

  
if (val_start == LOW && val_pause == LOW && val_heater2 == HIGH){
      switch (h2counter){
      case 0:
        btstart2=(millis()/60000);
        tone(BUZZER,500,5000);
        terminal.println("Heat 2 starting, need cover w water!!");
        terminal.flush();
        digitalWrite(HEATER2, HIGH);
        h2counter=1;
      break;
      case 1:
       elapsedh2time = ((millis()/60000) - btstart2);
       terminal.print("Manual Heater 2 On for: ");
       terminal.println(elapsedh2time);
       terminal.flush();
      break;} 
  } 
///When Heater2 button on and then turned off it stops two heater
if (h2counter == 1 && val_heater2 == LOW){
        h2counter = 0;
        digitalWrite(HEATER2, LOW);
        if (premash == true){
          reheatpremash = true; 
        }
        else if (mashrun == true){
          totalpausem = totalpausem + elapsedh2time; 
          terminal.print("Time Added to Mash: ");
          terminal.println(elapsedpausetime); 
        }
       else if (boilrun == true){
          reboil = true;
          totalpauseb = totalpauseb + elapsedh2time;
          terminal.print("Time Added to Boil: ");
          terminal.println(elapsedpausetime);  
       }
       else if (chillrun == true){
          rechill = true;
          totalpausec = totalpausec + elapsedh2time;
          terminal.print("Time Added to Chill: ");
          terminal.println(elapsedpausetime);  
       }  
    elapsedh2time=0;
  }

//Auto Brewing State decision upfront
ElapsedPreMashTime = (millis()/60000) - startingtime;
ElapsedMashTime = (millis()/60000) - MashStartTime - totalpausem;
ElapsedBoilTime = (millis()/60000) - BoilStartTime - totalpauseb;
ElapsedChillTime = (millis()/60000) - ChillStartTime - totalpausec;

if (tanktemp < MASHTEMP && autostart == true && switchState ==0) {
  switchState=1;
}

else if (tanktemp >= MASHTEMP - 5.0 && tanktemp < MASHTEMP - 2.0 && premash == true && mashPID == false && switchState ==0) { 
  switchState=2;
}

else if (tanktemp >= MASHTEMP - 2.0 && tanktemp <= MASHTEMP + 2.0 && premash == true && switchState ==0) { 
  switchState=3;
}

else if (tanktemp <= MASHTEMP - 8.0 && mashrun == true && mashreheat == false && switchState ==0){ 
  switchState=4;
}

else if (tanktemp >= MASHTEMP && mashreheat == true && switchState ==0){
  switchState=5;
}

else if (ElapsedMashTime >= MASHTIME && mashrun == true && switchState ==0) { 
  switchState=6;
}

else if (tanktemp >= 200 && mashend == true && switchState ==0) { 
  switchState=7;
}

else if (reboil == true && switchState ==0){ 
  switchState=8;
}

else if (ElapsedBoilTime < BOILTIME + 1 && boilrun == true && switchState ==0) {
  switchState=9;
  if (ElapsedBoilTime >= BOILTIME - 8 && ElapsedBoilTime <= BOILTIME - 7){
    terminal.println("Boil Almost Done: Add Immersion Chiller & begin yeast starter");
    terminal.flush();
  }
}

else if (ElapsedBoilTime >= BOILTIME && boilrun == true && switchState ==0) { 
  switchState=10;
}

else if (tanktemp <= 190 && boilend == true && switchState ==0) {
  switchState=11;
}

else if (rechill == true && switchState ==0){ 
  switchState=12;
}

else if (ElapsedChillTime >= CHILLTIME && chillrun == true && switchState ==0) { 
  switchState=13;
}

else if (reheatpremash == true && switchState ==0) { 
  switchState=14;
}


//When Auto Brewing Start button pressed 
if (val_start == HIGH && val_pause == LOW && val_pump == LOW && val_heater1 == LOW && val_heater2 == LOW){

switch (switchState){
//premash = Heater 1 & 2 on, pump off
case 1: 
      switchState = 0;
      startingtime = (millis()/60000);
      terminal.println(startingtime);
      premash=true;
      autostart = false;
      tone(BUZZER,500,5000);
      terminal.println("Heat starting, need to cover with water!!");
      terminal.flush();
      digitalWrite(HEATER1, HIGH);
      digitalWrite(HEATER2, HIGH);
      terminal.println("Heaters On");
      terminal.flush();    
break;


//reheatpremash: turn heaters back on after a pause during premash stage
case 14:
    switchState = 0;
    reheatpremash = false;
    digitalWrite(HEATER1, HIGH);
    digitalWrite(HEATER2, HIGH);
    terminal.println("Back from pause, premash heaters back on");
    terminal.flush(); 
break;


//close to mash temp = only one heater stay on (MY VERSION OF A PID)
case 2: 
    Blynk.notify("Close to MashTemp, ready to insert grain");
    switchState = 0;
    mashPID = true;
    digitalWrite(HEATER2, HIGH);
    tone(BUZZER,500,2000);
    terminal.println("Temp close to Mash, one heater on");
    terminal.flush();
break;

//mashrun = heaters off, pump on
case 3:
    switchState = 0;
    mashrun = true;
    premash=false;
    MashStartTime = (millis()/60000);   
    digitalWrite(HEATER1, LOW);
    digitalWrite(HEATER2, LOW);
    tone(BUZZER,500,2000);
    terminal.println("Mash temp hit, mash begin");
    terminal.flush();
    digitalWrite(PUMP, HIGH);
break;

//Mash Reheat Adds function so that if paused or a lot of heat loss the heater keeps us at mash temp
case 4:
  switchState = 0;
  mashreheat = true;
  digitalWrite(HEATER1, HIGH);
  digitalWrite(HEATER2, HIGH);
  terminal.println("Heat loss, heaters back on");
  terminal.flush();
break;

//Mash Reheat Complete
case 5: 
  switchState = 0;
  mashreheat = false;
  digitalWrite(HEATER1, LOW);
  digitalWrite(HEATER2, LOW);
  terminal.println("Mash back up to temp, heaters back off");
  terminal.flush();
break;

//mashend = heaters both on, pump off
case 6:
    switchState = 0;
    mashend = true;
    mashrun = false;
    digitalWrite(HEATER1, HIGH);
    digitalWrite(HEATER2, HIGH);
    digitalWrite(PUMP, LOW);
    tone(BUZZER,500,2000);
    terminal.println("Mash Complete, heat on, pump off, pre boil stage");
    Blynk.notify("REMOVE GRAIN BAG: Mash Complete, heat on, pump off, pre boil stage");
    terminal.flush(); 
break;  

//boilrun
case 7:
    switchState = 0;
    boilrun = true;
    mashend= false;
    BoilStartTime = (millis()/60000);
    tone(BUZZER,500,2000);
    terminal.println("Boiling start, heaters on, pump off, look for hop adds");
    terminal.flush();
break;  

//reboil: turn heaters back on after a pause during boilrun stage
case 8:
    switchState = 0;
    reboil = false;
    digitalWrite(HEATER1, HIGH);
    digitalWrite(HEATER2, HIGH);
    terminal.println("Back from pause, boil started again");
    terminal.flush(); 
break;

//Hop Additions Code
case 9:
  switchState =0;
  for (int h=0 ; h=hopCounter; h++) {
       HopAddtime = HOPTIME[h];
       if (ElapsedBoilTime >= (HopAddtime - 1.0) && ElapsedBoilTime <= (HopAddtime + 1.0)) { //BE SURE THAT THE +/- of the elapsedboil time doesnt result in overlay of two hop additions or else it could be confusing
        tone(BUZZER,500,2000);
        terminal.print("Add: ");
        terminal.println(HOPINFO[h]);
        terminal.flush();
        Blynk.notify(HOPINFO[h]);
        tone(BUZZER,500,2000);
       }
      }
break;
  
//boilend = heat off , pump off
case 10:
    switchState = 0;
    boilend = true;
    boilrun = false;
    digitalWrite(HEATER1, LOW);
    digitalWrite(HEATER2, LOW);
    digitalWrite(PUMP, LOW);
    terminal.println("Boil Complete");
    Blynk.notify("INSERT IMMERSION CHILLER IMMEDIATELY, CONNECT ALL CHILL TUBING, MAKE SURE FERMENTER IS READY:Boil Complete");
    terminal.flush();
break;

//chill run: Turns pump on once temp is low enough that going through plate chiller will work: Maybe bump up this temperature
case 11:
    switchState = 0;
    chillrun = true;
    boilend = false;
    ChillStartTime = (millis()/60000);
    terminal.println("Pump starting, need to open ball valve!!");
    terminal.flush();
    tone(BUZZER,500,5000);
    digitalWrite(PUMP, HIGH);    
break;

//rechill: turn pump back on after a pause during chillrun stage
case 12:
    switchState = 0;
    rechill = false;
    digitalWrite(PUMP, HIGH);
    terminal.println("Back from pause, chill pumping started again");
    terminal.flush(); 
break;

//chillend == turn pump off once estimated chill time is over
case 13:
    switchState = 0;
    chillend = true;
    chillrun = false;
    tone(BUZZER,500,5000);
    digitalWrite(PUMP, LOW); 
    terminal.println("Auto Brewing Complete");
    Blynk.notify("Auto Brewing Complete");  
    terminal.flush();  
break;
  }
 }
}


/////////BLYNK CODE: DISPLAYS AND INPUTS
///////BLYNK DASHBOARD DISPLAYS
//Send tanktemp continuously
BLYNK_READ (V9)
{
  Blynk.virtualWrite(V9,tanktemp);
}

//Send Elapsed Time to BLYNK Dashboard Display
BLYNK_READ(V11)
 {
  if (premash == true)
    Blynk.virtualWrite(V11, ElapsedPreMashTime);
  else if (mashrun == true)
    Blynk.virtualWrite(V11, ElapsedMashTime);
  else if (boilrun == true)
    Blynk.virtualWrite(V11, ElapsedBoilTime);
  else if (chillrun == true)
    Blynk.virtualWrite(V11, ElapsedChillTime);
  else 
    Blynk.virtualWrite(V11, 0);
  }

//Display Phase which corresponds with the elapsed time above
BLYNK_READ(V10)
 {
  if (premash == true){
    Blynk.virtualWrite(V10, "preM");}
  else if (mashrun == true){
    Blynk.virtualWrite(V10, "MASH");}
  else if (mashend == true){
    Blynk.virtualWrite(V10, "preB");}  
  else if (boilrun == true) {
    Blynk.virtualWrite(V10, "BOIL");}
  else if (chillrun == true){
    Blynk.virtualWrite(V10, "CHILL");}
  else {
    Blynk.virtualWrite(V10, "N/A");}
  } 

//////////////////////USER INPUTS: Mash Time, Mash Temp, Boil Time, Chill Time, Hop Times, Hop Info, Manual State Change
BLYNK_WRITE(V15)
{
  switch (param.asInt()){
       case 1 : 
        terminal.println("Variables Reset");
        terminal.flush(); 
        autostart = false;
        premash = false;
        mashPID = false;
        reheatpremash = false;
        mashrun = false;
        mashreheat = false;
        mashend = false;
        boilrun = false;
        reboil = false;
        boilend = false;
        chillrun = false;
        rechill = false;
        chillend = false;
        terminal.println("Reverted Back to Pre-Mash Stage");
        terminal.flush(); 
        switchState = 1;
        break;
       case 2 : 
        terminal.println("Variables Reset");
        terminal.flush(); 
        autostart = false;
        premash = false;
        mashPID = false;
        reheatpremash = false;
        mashrun = false;
        mashreheat = false;
        mashend = false;
        boilrun = false;
        reboil = false;
        boilend = false;
        chillrun = false;
        rechill = false;
        chillend = false;
        terminal.println("Reverted Back to Mash Stage");
        terminal.flush(); 
        switchState = 3;
        break;
       case 3 : 
        terminal.println("Variables Reset");
        terminal.flush(); 
        autostart = false;
        premash = false;
        mashPID = false;
        reheatpremash = false;
        mashrun = false;
        mashreheat = false;
        mashend = false;
        boilrun = false;
        reboil = false;
        boilend = false;
        chillrun = false;
        rechill = false;
        chillend = false;
        terminal.println("Reverted back to Boil Stage");
        terminal.flush(); 
        switchState = 6;
        break;
   }
}


BLYNK_WRITE(V1)
{
  MASHTEMP= param.asInt();
  terminalHops.print("MASHTEMP SET AS: ");
  terminalHops.println(MASHTEMP);
  terminalHops.flush(); 
}

BLYNK_WRITE(V2)
{
  MASHTIME= param.asInt();
  terminalHops.print("MASHTIME SET AS: ");
  terminalHops.println(MASHTIME);
  terminalHops.flush(); 
}

BLYNK_WRITE(V3)
{
  BOILTIME= param.asInt();
  terminalHops.print("BOILTIME SET AS: ");
  terminalHops.println(BOILTIME);
  terminalHops.flush(); 
}
  
BLYNK_WRITE(V13)
{
  CHILLTIME= param.asInt();
  terminalHops.print("CHILLTIME SET AS: ");
  terminalHops.println(CHILLTIME);
  terminalHops.flush(); 
}

BLYNK_WRITE(V14)  // Terminal for HOPTIME and HOPINFO input
{
  String fromTerminal = param.asStr();
  
    
  if (fromTerminal.endsWith("?")) {
   terminalHops.println("Comma delimited, and first letter followed by '!' resets previous inputs: HOPTIMES:start w/ 'T,'//HOPINFO: starts w/ 'I,'");
   terminalHops.flush(); 
      }
  
  if (fromTerminal.startsWith("T") ) { //Hoptimes input
      int lastIndex;
      
      if (fromTerminal.endsWith("!")){ //In case I need to reset everything and re input hop additions
       hopCounter = 0;
       lastIndex = 0;
      }
      
      fromTerminal.remove(0,1);
      for (int i = 0; i < fromTerminal.length(); i++) {
        if (fromTerminal.substring(i, i+1) == ",") {
          HOPTIME[hopCounter] = (fromTerminal.substring(lastIndex, i)).toInt();
          lastIndex = i + 1;
          hopCounter++;
        }
        if (i == fromTerminal.length() - 1) {  // If we're at the end of the string (no more commas to stop us)
          HOPTIME[hopCounter] = (fromTerminal.substring(lastIndex, i)).toInt();
        }
      }   
      terminalHops.print(HOPTIME[1]);
      terminalHops.print(" from ArrayHOPTIME ");
      terminalHops.print(hopCounter);
      terminalHops.println(" total inputs");
      terminalHops.flush();
      }

 
  
  if (fromTerminal.startsWith("I")){ //Hopinfo input
      int counter;
      int lastIndex;
      
      if (fromTerminal.endsWith("!")){ //In case I need to reset everything and re input hop additions
       counter = 0;
       lastIndex = 0;
      }
      
      fromTerminal.remove(0,1);
      for (int i = 0; i < fromTerminal.length(); i++) {
        if (fromTerminal.substring(i, i+1) == ",") {
          HOPINFO[counter] = fromTerminal.substring(lastIndex, i);
          lastIndex = i + 1;
          counter++;
        }
        if (i == fromTerminal.length() - 1) {  // If we're at the end of the string (no more commas to stop us)
          HOPINFO[counter] = fromTerminal.substring(lastIndex, i);
        }
      }   
      terminalHops.print(HOPINFO[1]);
      terminalHops.print(" from CharArrayHOPINFO ");
      terminalHops.print(counter);
      terminalHops.println(" total inputs");
      terminalHops.flush();        
      }
 } 

////Manual Controls
BLYNK_WRITE(V4) //Start Button
{
  val_start= param.asInt();
}

BLYNK_WRITE(V5) //Pause Button
{
  val_pause= param.asInt();
}

BLYNK_WRITE(V6) //Pump Button
{
  val_pump= param.asInt();
}

BLYNK_WRITE(V7) //Heater 1 Button
{
  val_heater1= param.asInt();
}

BLYNK_WRITE(V8) //Heater 2 Button
{
  val_heater2= param.asInt();
}


void setup() 
{
  Serial.begin(115200);
  Blynk.begin(auth,ssid,password);
  timer.setInterval(1000, brewingstuff); //Check my brewing code every 1s
  sensor.begin();
  sensor.setResolution(9); //Resolution of 9 means 0.5degC increments.
  pinMode(PUMP, OUTPUT); //initialize pin locations as output or input
  pinMode(HEATER1, OUTPUT);
  pinMode(HEATER2, OUTPUT);
  pinMode(BUZZER, OUTPUT);
  terminal.println("Setup done, Phil's beer is on the way, Ready?");
  terminal.flush();
}

void loop() 
{
  Blynk.run();
  timer.run();
  sensor.requestTemperatures(); //Get TEMP reading continuously
  tanktemp = sensor.getTempFByIndex(0);
}

By using the terminal widget on my Blynk, it is reading back to me the variables and they seem normal; ie if I put in Cascade, then terminal spits back : Cascade as the variable so it seems like it is setting variables correctly.

I am thinking it is an issue with using an array and how it moves through the array in the For loop but I am not sure what the issue is. Is it possible that the array is to big and thus as the For loop runs through all 20 indexes; it creates a delay that causes the Blynk to disconnect? 20 as the size of the array didnt seem that big but maybe it is.

Try adding Blynk.run(); in your for loop. So it will be:

//Hop Additions Code
case 9:
  switchState =0;
  for (int h=0 ; h=hopCounter; h++) {
       Blynk.run();
       HopAddtime = HOPTIME[h];
       if (ElapsedBoilTime >= (HopAddtime - 1.0) && ElapsedBoilTime <= (HopAddtime + 1.0)) { //BE SURE THAT THE +/- of the elapsedboil time doesnt result in overlay of two hop additions or else it could be confusing
        tone(BUZZER,500,2000);
        terminal.print("Add: ");
        terminal.println(HOPINFO[h]);
        terminal.flush();
        Blynk.notify(HOPINFO[h]);
        tone(BUZZER,500,2000);
       }
      }
break;

@philmurp

Are the first 3 lines of code:

#include <OneWire.h>
#include <DallasTemperature.h>
#include <LiquidCrystal_I2C.h>

You STILL have temperature reading in loop() despite the previous recommendations to put it in a timer. You also have 1 sec timer which seems overkill.

Your code is very hard to follow.
For V2, V3 and V13 are they sliders?

Relating to V2 (MASHTIME) you have:
int MASHTIME; //Desired Mash Time(milli sec.)

Is the scale for V2, V3, V13 integer minutes?

I have been through case 9 and case 10 but not quite clear when to add HOPINFO and can I just enter something like “I,stuff”?

@philmurp please confirm I need to terminate both T’s and I’s with a comma, so:

T,1,
I,stuff,

A few weeks ago I advised you to send all your variables to Serial Monitor (that is what it is there for).

It is not the same as sending to Blynk’s Terminal as you have found out i.e. if you are not connected you will not see anything in the Terminal window.

So case 9 needs to have:

Serial.print("Add: ");
Serial.println(HOPINFO[h]);
terminal.print("Add: ");
terminal.println(HOPINFO[h]);

To debug I have added a further 50 Serial.print statements to the sketch.

If you type T,1, and then I,stuff, (or vice versa) in Terminal you will see a permanent loop like this in Serial Monitor:

Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add: 
Add:

It goes on and on and disconnects you from Blynk.

I haven’t worked out why it loops yet, but this is the process you must go through to find your bugs.

I’ll let you know if I find a fix.

  1. I am at work so no access to my Arduino and so I copied old code to put it in; I have taken the temp sensor out of the loop

  2. First three lines of code are (once again my mistake of copying old code)

    #include <OneWire.h>
    #include <DallasTemperature.h>
    #include <LiquidCrystal_I2C.h>

  3. V2: Mashtime Slider
    V3: Boil Time Slider
    V13: Chill Time Slider

The scale is interger minutes as in my code I divide millis() to convert it into minutes when looking at elapsed time

  1. Yes to add HOPINFO: “I,Stuff,Stuff2,Stuff3”
    HOPTIME: “T,TIME1,TIME2”

  2. I will be moving the serial print codes into my code tonight.

I worked out that I and T are not terminated with a comma but for me if I put in Cascade it returns only Cascad. Same with any variable for I,so somewhere you have lost a character.

So you have to add a comma after the last character of both info and time to stop it from looping like that?

potentially your losing a character because the for loop when inputting goes from i=0 ; i< from Terminal.length(), i++)

Maybe it should be i=0 ; i=fromTerminal.length(),i++

I’m not home unless I would try myself.

No it doesn’t need to end with a comma, I just thought that was how you had terminated it.

Still looking for the fix.

In V14 you have:

    if (i == fromTerminal.length() - 1) {  // If we're at the end of the string (no more commas to stop us)
      HOPINFO[counter] = fromTerminal.substring(lastIndex, i); 
    }

If you remove the , i from the last line it will show the full Cascade but the sketch is still crashing the ESP.

In case 9 you have:

for (int h=0 ; h=hopCounter; h++) {

If you change the h=hopCounter to h < hopCounter it will stop the crashing but I’m not sure if that still processes all the required hops etc (perhaps you can look at this).

Also I would comment out the Blynk.notify(HOPINFO[h]) in this section until it is running smoothly because of the one Push message per minute limit. Even with the change above it is still looping round saying Add: Cascade every few seconds so it will exceed the one push per minute (but at least it isn’t dropping the connection).

You will have to review the change from h=hopCounter" part to see where and when the second and subsequent hops should appear.

I am looking at the code, I have the if statement true if the elapsed time is +/- 1 minute from the hop time. That would make it be true for two minutes so perhaps that’s why it still is repeating.

I made it that way because I’m not sure how timing works if I say elapsed time = hop time would it have to be exactly that time and if the loop doesn’t happen to run that time it wouldn’t trigger. I could make a counter type to make the hop trigger only once. Thoughts?

In V14 you have:

terminalHops.print(HOPTIME[1]);

and

terminalHops.print(HOPINFO[1]);

Should these 1’s be i’s and should they be included within the For loop()?

If I have 2 Hops (Cascade and Stuff) the modified (< from =) For loop in case 9 shows:

Add:
Add: Cascade

So one of the hops is missing.
I’m not sure I follow how the hops are added. If I set T,1,1 and I,Cascade,Stuff does this mean show a message for 1 minute to add Cascade and then a minute later add Stuff or are both hops added at the same time, if so what is the T format for this?

I will take a look at the +/- 1 minute but to be honest I think all of the stages would have been handled better with SimpleTimer and the code split up into much smaller sections. So pre mash, mash, boil and chill.

SimpleTimer is dynamic and can take user input from the sliders etc. So you enable pre mash timer based on user input, then move on to mash etc.

1 Like

The remarks on the SimpleTimer are very good I think. I’ve been reading up on brewing because I wanted to take it up too, but it’s too costly in time and I’m not really patient, LOL.

But you can enable/disable timers based on inputs, as Costas says. It’ll save a lot of hassle and possible programming space, but it’ll be some job to get things done.

How I would start, is by making a flowchart to document the process. It’s not to be taken lightly because there are a lot of factors involved in beer brewing (I’m hoping Phil can make his fall beer in time :wink: ).

1 Like

@Lichtsignaal if you study the code you will see how messy it currently is (bit like most of my code).

The brewing process seems quite involved but it is really just one stage following the previous one. @philmurp has some fall back positions included in his code so it is not always A -> B -> C -> D but again SimpleTimer can handle all this.

Flowchart essential.

1 Like

@philmurp regarding the + / - 1 minute for the hop time:

To be able to test your sketch I didn’t want to have to wait a week for a “brew” to finish and I didn’t want to hook up temperature sensors and heaters. So I did some “simulation” and also skipped lots of conditions in the sketch.

So where you had the attempt to read the temperature a thousand times a second in loop() I removed this and with a 3 second timer called Fahrenheit() did this:

void Fahrenheit(){
  tanktemp = tanktemp + 0.1;  // Starting from 200F,   also tried +1
  Serial.println(tanktemp);
  Blynk.virtualWrite(V9,tanktemp);
  if (premash == true){
    Blynk.virtualWrite(V11, ElapsedPreMashTime);
    Blynk.virtualWrite(V10, "preM");
  }
  else if (mashrun == true){
    Blynk.virtualWrite(V11, ElapsedMashTime);
    Blynk.virtualWrite(V10, "MASH");
  }
  else if (boilrun == true){
    Blynk.virtualWrite(V11, ElapsedBoilTime);
    Blynk.virtualWrite(V10, "BOIL");
  }
  else if (chillrun == true){
    Blynk.virtualWrite(V11, ElapsedChillTime);
    Blynk.virtualWrite(V10, "CHILL");
  }
  else {
    Blynk.virtualWrite(V11, 0);
    Blynk.virtualWrite(V10, "N/A");
  }      

  if (mashend == true){
    Blynk.virtualWrite(V10, "preB");
  } 
}

To be honest I wasn’t very successful and vary rarely did I see entries for V10 and V11 in the app (temperature in Fahrenheit for V9 was fine). I saw them a few times but I couldn’t easily replicate the correct process of pushing buttons, moving sliders and adding hops to get V10 and V11 data.

Nonetheless I was able to hack the sketch to move through various processes and you already knew where the problem area was.

One of the hacks was to comment out your + / - 1 minute hop time and change your 1 second timer to 3 seconds. In a live system I am guessing this timer could be a minute and would need to be if you are going to have Blynk.notify commands within the timed process.

I generally use counters to ensure things are only done once (or X times) rather than using time +/- a minute. However I notice that your sketch doesn’t include the text “sync” at any point. You have the brew stage and elapsed time on V10 and V11 Display widgets which is fine but I would be syncing these (and other) values if the ESP does any form of reset or loses the connection to the server etc.

for the:
terminalHops.print(HOPTIME[1]);

I used this as a way to display if the variables were being implemented properly; so it was my version of a serial output; which I now know via several discussions I should have just used serial output. I only have it showing the first of the array because I wanted to show it was working and figured if one worked the others would as well.

The theory is that when the elapsed time == Hoptime that the terminal & blynk.notify will send an alert that I need to add hops. So there should be only one notification of each hop addition. So if Cascade needs to be added during the first minute of the boil; it would be T,1 & I,Cascade which should display once.

I think what I currently have is that once it hits the elapsed time==Hoptime since I have no counter it is repeatedly displaying to add it for the +/-1 minutes from that time. Still trying to think how that counter should be done since I need it to be triggered for each hopadd separatley (i.e. each hopaddition can only be displayed once)

So here is my to do list:

  1. Add in serial reads to debug

  2. change to stop the crashing
    for (int h=0 ; h=hopCounter; h++) {
    to
    for (int h=0 ; h<=hopCounter; h++) {

  3. Change timer to run only once a minute so that Blynk.notify will work

  4. Create counter on hopadd for loop so that hopadditions are only displayed once. Here is where I could use any best practices you guys have seen to create counters in a for loop that would achieve this goal.

In response to the SimpleTimer and breaking down the code; my purpose was to ideally be able to have the brewing automated so that I hit go and don’t touch it (for hop additions I would have to do physical work for now, but in the future state I would have motors running hardware to dump hops but that’s a different story)

With the simpletimers, wouldnt I have to trigger each stage manually then?

As far as workflow, I have drawn out some basic sketches previously to get to this point. I can whip something more formal up in visio or something like that, @Costas your right it is sequential with a few re-work off shoots in case temperatures fall below a certain limit (you need your mash temp to stay consistent as possible). Adding in complexity is also the coding I created to manually run the setup. So I have the auto brewing code; but then separate I have buttons to be able to manually turn on and off heaters & pump which would be for the purpose of other random chores like cleaning or if I wanted to experiment with complex mash phases that I just didnt think were time efficient for me to build into the autobrewing code (some complex brews have step mashes where the mash temperature is held a several different temperatures for some length of time during the mash ie. 150 for 10minutes, 165 for 10 minutes etc)

I think you should give us a real world example of a brew, just a simple one with the H & I data and times and temperatures for each of the stages.

But for sure SimpleTimer doesn’t need any manual intervention unless to you want to provide any.

Using your current if statements based on time and temperature each of the 4 stages are triggered through SimpleTimer.

See extract of how I would lay it out (it compiles but doesn’t do anything).

// extract for Brewery - breweryextract.ino Costas 25/8/16
// see http://playground.arduino.cc/Code/SimpleTimer
// normal libraries here


#include <SimpleTimer.h>

SimpleTimer timer;

int premashtimer = 1;   // timers just numered 1 to 5, required for enable / disable feature
long premashinterval = 10; // general but can be changed during execution
int mashtimer = 2;
long mashinterval = 20;    // general but can be changed during execution
int boiltimer = 3;
long boilinterval = 80;    // general but can be changed during execution
int chilltimer = 4;
long chillinterval = 6;    // general but can be changed during execution

int oneminutetimer = 5;

void setup() {
  // normal Blynk setup here
  
  // these first 4 timers are defind but disabled on bootup
  premashtimer = timer.setInterval(premashinterval, premashstuff); 
  timer.disable(premashtimer);
  mashtimer = timer.setInterval(mashinterval, mashstuff); 
  timer.disable(mashtimer);
  boiltimer = timer.setInterval(boilinterval, boilstuff); 
  timer.disable(boiltimer);
  chilltimer = timer.setInterval(chillinterval, chillstuff); 
  timer.disable(chilltimer);

  // this timer is automatically enabled on bootup
  oneminutetimer = timer.setInterval(60000L, oneminutestuff); // check things every minute
      
  
  // if all is ok enable premashtimer, maybe check is done in oneminutetimer
  timer.enable(premashtimer);
}

void premashstuff(){
  timer.disable(premashtimer);
  // do premashstuff
  timer.enable(mashtimer);  
}

void mashstuff(){
  timer.disable(mashtimer);
  // do mashstuff
  timer.enable(boiltimer);   
}

void boilstuff(){
  timer.disable(boiltimer);
  // do boilstuff
  timer.enable(chilltimer);   
}

void chillstuff(){
  timer.disable(chilltimer);
  // do chillstuff
  // send push message, beer is ready    
}

void oneminutestuff(){
  // check temperature and time etc
  // maybe change timer intervals from sliders
  // add hopinfo and hoptime
}

void loop() {
  //Blynk.run();  // include this line when Blynk libraries are inserted above
  timer.run();
}

Ok now I am getting it a bit more. The benefit of these timers is that during each phase, the only code running would be within that phase; which is run at an interval I have chosen; instead of where I have it now where it runs through the entire autobrewing code via switch case? (regular blynk code, and then the overall oneminute timer code would also run the whole time)

Second, it would make debugging and looking at the code easier?

Here is an example of brewing parameters I would insert:
Mash Temp: 158
Mash Time: 60
Boil Time: 60
Chill Time: 20
T,1,30,45
I,Cascade2OZ,Colombus2OZ,Mosaic2OZ

Here is the theory of what would run now:
Heaters on to heat water until 158
Heaters off, pump on for 60 minutes
Heaters on until boil temp reached (200F in my case)
Boil Temp reached, boil elapsed time tracked; HERE IS WHERE THE HOP ADDITIONS CODES TRIPS ME UP
Heaters on for 60 minutes during boil
Heaters off, pumps on for 20 minutes for chill time

Currently it all works ok, except for as soon as we hit a hop addition code it disconnects; and when I dont put anything in for hop additions; then boil runs fine; so it really seems like isolated hop additions issue.

1 Like

For sure it would. I have eye strain today from looking at your code yesterday :slight_smile:

You can have up to 10 timers running simultaneously (for each SimpleTimer object) if needed but generally a handful running at the same time is normally enough.