Arduino nano crashes each time at exactly same moment

Hello,

I'm building a controller for garden irrigation with 8 valves to control 8 different zones for watering plants. To control the valves I bought 5v relay boards with optical isolation. Interference problems have already been fixed with a ferrite ring.

The time eacht zone has to stay active can be set in the eprom via the controls. The whole program is controlled with a rotary encoder which includes a pushbutton. When I for example select 30 minutes, the program will run just as expected untill it reaches 23 minutes in the countdown. At this moment the arduino crashes, displays weird characters on the display and self-resets. This reset turns off all relays and the irrigation stops. So It can only water plants for 6 minutes.

I know the way of programming here is not ideal, but it should work. What am I overseeing, what am I doing wrong?

Code below:

/*
 * MARK VAN VEGGEL
 * ARDUINO LCD MENU V0.1
 * YOUTUBE.COM/AMPSOURCE
 * 
 */

#include <OneButton.h>+
#include <Encoder.h>
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>

LiquidCrystal_I2C  lcd(0x20,2,1,0,4,5,6,7);

// ROTARY ENCODER
long oldPosition  = 0;
int encoderDTpin = 2;
int encoderCLKpin = 3;
int mode = 1;
int timerMin = 0;
int timerSec;
int timerRustSec;
int timerSettingSec;
int timerRustMin = 15;
int timerSettingMin; 
bool pomprust = true; 
int addr1 = 0;
int addr2 = 8;

Encoder myEnc(encoderDTpin, encoderCLKpin);

#define buttonPin 8
#define Valve1 4
#define Valve2 5
#define Valve3 6
#define Valve4 7
#define Valve5 9
#define Valve6 10
#define Valve7 11
#define Valve8 12

OneButton button0 = OneButton(
  buttonPin,  // Input pin for the button
  false,       // Button is active high
  false        // Disable internal pull-up resistor
);


void setup() {
  Serial.begin(9600);

  // Map rotary button to actions single and doubleclick.
  button0.attachClick(singleClick);
  button0.attachLongPressStart(longPressStart);
  button0.attachDoubleClick(doubleClick);

  lcd.begin (16,2);
  lcd.setBacklightPin(3,POSITIVE);
  lcd.setBacklight(HIGH);
  lcd.home();

  // Create the custom character.

  lcd.setCursor (2,0);  
  lcd.print("R. Heleven");
  delay(1000);
  lcd.setCursor (0,0);
  lcd.print("                ");
  lcdUpdate();

  pinMode(Valve1,OUTPUT);
  pinMode(Valve2,OUTPUT);
  pinMode(Valve3,OUTPUT);
  pinMode(Valve4,OUTPUT);
  pinMode(Valve5,OUTPUT);
  pinMode(Valve6,OUTPUT);
  pinMode(Valve7,OUTPUT);
  pinMode(Valve8,OUTPUT);

  digitalWrite(Valve1,HIGH);
  digitalWrite(Valve2,HIGH);
  digitalWrite(Valve3,HIGH);
  digitalWrite(Valve4,HIGH);
  digitalWrite(Valve5,HIGH);
  digitalWrite(Valve6,HIGH);
  digitalWrite(Valve7,HIGH);
  digitalWrite(Valve8,HIGH);
  
  timerSettingMin = EEPROM.read(addr1);
  pomprust = EEPROM.read(addr2);

  timerMin = timerSettingMin;
   timerRustMin = 15;
    timerSettingSec = 60*timerSettingMin;
    timerSec = timerSettingSec;
    timerRustSec = 60*timerRustMin;
  
}

void loop() {
    
    // Listen to button presses.
    button0.tick();

    // Listen if the rotary encoder moves.
    rotary_check();
    
    // Print the LCD menu.
    //lcdUpdate();  

}

///////////////////////////////////////////////////////////////////////////////////////////////////////////:

void singleClick() {

  switch (mode) {
  case 1:
    mode = 11;
    break;
  case 2:
    mode = 31;
    break;
  case 3:
    mode = 51;
    break;
  case 51:
    mode = 71;
    break;
  case 52:
    mode = 72;
    break;
  default:
    // if nothing else matches, do the default
    // default is optional
    break;
  }
lcdUpdate();
Serial.println(mode);
}

void longPressStart() {

 switch (mode) {
  case 11:
    mode = 21;
    break;
  case 12:
    mode = 22;
    break;
  case 13:
    mode = 23;
    break;
  case 14:
    mode = 24;
    break;
  case 15:
    mode = 25;
    break;
  case 16:
    mode = 26;
    break;
  case 17:
    mode = 27;
    break;
  case 18:
    mode = 28;
    break;
  case 31:
    screen41();
    break;
  case 32:
    screen42();
    break;
  case 71:
    mode = 100;
    break;
  case 72:
    mode = 101;
    break;
  default:
    // if nothing else matches, do the default
    // default is optional
    break;
  }
  lcdUpdate();
  
} 

void doubleClick() {

 mode = 1;
 lcdUpdate();
 
}

void rotary_check(){

  // Constantly read the position of the rotary encoder
  long newPosition = myEnc.read() / 4;

  // IF the new position of the encoder is different then the old position
  if (newPosition != oldPosition) {

      if(newPosition > oldPosition){
        if (mode==71){ 
          timerSettingMin++;
          if (timerSettingMin >= 90){timerSettingMin = 90;}
        }
        if (mode==72){
          if (pomprust == true){pomprust=false;} 
        }
        if ((mode!=71)&&(mode!=72)){
          if (((mode<=2)&&(mode>=0))||((mode>=11)&&(mode<=17))||((mode>=21)&&(mode<=23))||((mode==51))||(mode==31)){
          mode++;
          Serial.println(mode);
          }
        }
      }
      
      else if (newPosition < oldPosition){
        if (mode==71){ 
          timerSettingMin--;
          if (timerSettingMin <= 5){timerSettingMin = 5;}
        }
        if (mode==72){
          if (pomprust == false){pomprust=true;}
        }
        if((mode!=71)&&(mode!=72)){
            if (((mode>=2)&&(mode<=3))||((mode>=12)&&(mode<=18))||((mode>=22)&&(mode<=24))||((mode==52))||(mode==32)){
            mode--;
            if (mode <= 1){mode=1;}
            Serial.println(mode);
            }       
         }
      }

      oldPosition = newPosition;
      lcdUpdate(); 
  }
}

void lcdUpdate(){

   switch (mode) {
  case 0:
    screen0();
    break; 
  case 1:
    screen1();
    break;
  case 2:
    screen2();
    break;
  case 3:
    screen3();
    break;
  case 11:
    screen11();
    break;
  case 12:
    screen12();
    break;
  case 13:
    screen13();
    break;
  case 14:
    screen14();
    break;
  case 15:
    screen15();
    break;
  case 16:
    screen16();
    break;
  case 17:
    screen17();
    break;
  case 18:
    screen18();
    break;
  case 21:
    screen21();
    break;
  case 22:
    screen22();
    break;
  case 23:
    screen23();
    break;
  case 24:
    screen24();
    break;
  case 25:
    screen25();
    break;
  case 26:
    screen26();
    break;
  case 27:
    screen27();
    break;
  case 28:
    screen28();
    break;
   case 31:
    screen31();
    break;
   case 32:
    screen32();
    break;
  case 51:
    screen51();
    break;
  case 52:
    screen52();
    break;
  case 71:
    screen71();
    break;
  case 72:
    screen72();
    break;
  case 100:
    screen100();
    break;
  case 101:
    screen101();
    break;
  case 200:
    screen200();
    break;                 
  default:
    // if nothing else matches, do the default
    // default is optional
    break;
}
  lcd.setCursor(0, 2);
  //lcd.print(mode);
}

void wateringZONE1(){
      digitalWrite(Valve1,LOW);
      mode = 21;
      delay(940);
      timerSec--;
      Serial.println(timerSec);
      if ((timerSec <= 0)|(digitalRead(buttonPin)== 1)){
        mode=0;
        digitalWrite(Valve1,HIGH);
      }
      timerMin = timerSec/60;
      lcdUpdate();
}

void wateringZONE2(){
      digitalWrite(Valve2,LOW);
      mode = 22;
      delay(940);
      timerSec--;
      if ((timerSec <= 0)|(digitalRead(buttonPin)== 1)){
        mode=0;
        digitalWrite(Valve2,HIGH);
      }
      timerMin = timerSec/60;
      lcdUpdate();
}

void wateringZONE3(){
      digitalWrite(Valve3,LOW);
      mode = 23;
      delay(940);
      timerSec--;
      if ((timerSec <= 0)|(digitalRead(buttonPin)== 1)){
        mode=0;
        digitalWrite(Valve3,HIGH);
      }
      timerMin = timerSec/60;
      lcdUpdate();
}

void wateringZONE4(){
      digitalWrite(Valve4,LOW);
      mode = 24;
      delay(940);
      timerSec--;
      Serial.println(timerSec);
      if ((timerSec <= 0)|(digitalRead(buttonPin)== 1)){
        mode=0;
        digitalWrite(Valve4,HIGH);
      }
      timerMin = timerSec/60;
      lcdUpdate();
}

void wateringZONE5(){
      digitalWrite(Valve5,LOW);
      mode = 25;
      delay(940);
      timerSec--;
      if ((timerSec <= 0)|(digitalRead(buttonPin)== 1)){
        mode=0;
        digitalWrite(Valve5,HIGH);
      }
      timerMin = timerSec/60;
      lcdUpdate();
}

void wateringZONE6(){
      digitalWrite(Valve6,LOW);
      mode = 26;
      delay(940);
      timerSec--;
      if ((timerSec <= 0)|(digitalRead(buttonPin)== 1)){
        mode=0;
        digitalWrite(Valve6,HIGH);
      }
      timerMin = timerSec/60;
      lcdUpdate();
}

void wateringZONE7(){
      digitalWrite(Valve7,LOW);
      mode = 27;
      delay(940);
      timerSec--;
      if ((timerSec <= 0)|(digitalRead(buttonPin)== 1)){
        mode=0;
        digitalWrite(Valve7,HIGH);
      }
      timerMin = timerSec/60;
      lcdUpdate();
}

void wateringZONE8(){
      digitalWrite(Valve8,LOW);
      mode = 28;
      delay(940);
      timerSec--;
      if ((timerSec <= 0)|(digitalRead(buttonPin)== 1)){
        mode=0;
        digitalWrite(Valve8,HIGH);
      }
      timerMin = timerSec/60;
      lcdUpdate();
}

void wateringAUTO(){
      wateringZONE1();
      lcdUpdate();
      if (pomprust==true){
        pompRust();
      }
      wateringZONE2();
      lcdUpdate();
      if (pomprust==true){
        pompRust();
      }
      wateringZONE3();
      lcdUpdate();
      if (pomprust==true){
        pompRust();
      }
      wateringZONE4();
      lcdUpdate();
}

void wateringAUTO2(){
      wateringZONE5();
      lcdUpdate();
      if (pomprust==true){
        pompRust();
      }
      wateringZONE6();
      lcdUpdate();
      if (pomprust==true){
        pompRust();
      }
      wateringZONE7();
      lcdUpdate();
      if (pomprust==true){
        pompRust();
      }
      wateringZONE8();
      lcdUpdate();
}

void pompRust(){
      mode = 200;
      delay(950);
      timerRustSec--;
      if ((timerRustSec <= 0)|(digitalRead(buttonPin)== 1)){
        mode=0;
      }
      timerRustMin = timerRustSec/60;
      lcdUpdate();
}

void screen0(){
    timerRustMin = 15;
    timerSettingSec = 60*timerSettingMin;
    timerSec = timerSettingSec;
    timerRustSec = 60*timerRustMin;
    lcd.clear();
    lcd.print("Geselecteerd");
    lcd.setCursor(0,1);
    lcd.print("prog voltooid");
}

void screen1(){
    timerRustMin = 15;
    timerSettingSec = 60*timerSettingMin;
    timerSec = timerSettingSec;
    timerRustSec = 60*timerRustMin;
    lcd.clear();
    lcd.print("Manuele");
    lcd.setCursor(0,1);
    lcd.print("modus");
}

void screen2(){
    lcd.clear();
    lcd.print("Automatische");
    lcd.setCursor(0,1);
    lcd.print("modus");
}

void screen3(){
    lcd.clear();
    lcd.print("Instellingen");
}

void screen11(){
    lcd.clear();
    lcd.print("ZONE 1");
    lcd.setCursor(0,1);
    lcd.print("> Houd ingedrukt");
}

void screen12(){
    lcd.clear();
    lcd.print("ZONE 2");
    lcd.setCursor(0,1);
    lcd.print("> Houd ingedrukt");
}

void screen13(){
    lcd.clear();
    lcd.print("ZONE 3");
    lcd.setCursor(0,1);
    lcd.print("> Houd ingedrukt");
}

void screen14(){
    lcd.clear();
    lcd.print("ZONE 4");
    lcd.setCursor(0,1);
    lcd.print("> Houd ingedrukt");
}

void screen15(){
    lcd.clear();
    lcd.print("ZONE 5");
    lcd.setCursor(0,1);
    lcd.print("> Houd ingedrukt");
}

void screen16(){
    lcd.clear();
    lcd.print("ZONE 6");
    lcd.setCursor(0,1);
    lcd.print("> Houd ingedrukt");
}

void screen17(){
    lcd.clear();
    lcd.print("ZONE 7");
    lcd.setCursor(0,1);
    lcd.print("> Houd ingedrukt");
}

void screen18(){
    lcd.clear();
    lcd.print("ZONE 8");
    lcd.setCursor(0,1);
    lcd.print("> Houd ingedrukt");
}
void screen21(){
    lcd.clear();
    lcd.print("ZONE 1 Actief");
    lcd.setCursor(0,1);
    lcd.print("Resterend:");
    lcd.setCursor(11,1);
    lcd.print(timerMin);
    lcd.setCursor(13,1);
    lcd.print("min");

    wateringZONE1();
}

void screen22(){
    lcd.clear();
    lcd.print("ZONE 2 Actief");
    lcd.setCursor(0,1);
    lcd.print("Resterend:");
    lcd.setCursor(11,1);
    lcd.print(timerMin);
    lcd.setCursor(13,1);
    lcd.print("min");

    wateringZONE2();
}

void screen23(){
    lcd.clear();
    lcd.print("ZONE 3 Actief");
    lcd.setCursor(0,1);
    lcd.print("Resterend:");
    lcd.setCursor(11,1);
    lcd.print(timerMin);
    lcd.setCursor(13,1);
    lcd.print("min");

    wateringZONE3();
}

void screen24(){
    lcd.clear();
    lcd.print("ZONE 4 Actief");
    lcd.setCursor(0,1);
    lcd.print("Resterend:");
    lcd.setCursor(11,1);
    lcd.print(timerMin);
    lcd.setCursor(13,1);
    lcd.print("min");

wateringZONE4();
}

void screen25(){
    lcd.clear();
    lcd.print("ZONE 5 Actief");
    lcd.setCursor(0,1);
    lcd.print("Resterend:");
    lcd.setCursor(11,1);
    lcd.print(timerMin);
    lcd.setCursor(13,1);
    lcd.print("min");

wateringZONE5();
}

void screen26(){
    lcd.clear();
    lcd.print("ZONE 6 Actief");
    lcd.setCursor(0,1);
    lcd.print("Resterend:");
    lcd.setCursor(11,1);
    lcd.print(timerMin);
    lcd.setCursor(13,1);
    lcd.print("min");

wateringZONE6();
}

void screen27(){
    lcd.clear();
    lcd.print("ZONE 7 Actief");
    lcd.setCursor(0,1);
    lcd.print("Resterend:");
    lcd.setCursor(11,1);
    lcd.print(timerMin);
    lcd.setCursor(13,1);
    lcd.print("min");

wateringZONE7();
}

void screen28(){
    lcd.clear();
    lcd.print("ZONE 8 Actief");
    lcd.setCursor(0,1);
    lcd.print("Resterend:");
    lcd.setCursor(11,1);
    lcd.print(timerMin);
    lcd.setCursor(13,1);
    lcd.print("min");

wateringZONE8();
}

void screen31(){
    lcd.clear();
    lcd.print("Auto ZONE 1234");
    lcd.setCursor(0,1);
    lcd.print("> Houd ingedrukt");
}

void screen32(){
    lcd.clear();
    lcd.print("Auto ZONE 5678");
    lcd.setCursor(0,1);
    lcd.print("> Houd ingedrukt");
}

void screen41(){
    lcd.clear();
    lcd.print("Automatische");
    lcd.setCursor(0,1);
    lcd.print("modus gestart");
    delay(2000);
    wateringAUTO();    
}

void screen42(){
    lcd.clear();
    lcd.print("Automatische");
    lcd.setCursor(0,1);
    lcd.print("modus gestart");
    delay(2000);
    wateringAUTO2();    
}

void screen51(){
    lcd.clear();
    lcd.print("Zonetijd");
}

void screen52(){
    lcd.clear();
    lcd.print("Pompinstelling");
}

void screen71(){
    lcd.clear();
    lcd.print("Tijdinstelling:");
    lcd.setCursor(0,1);
    lcd.print(timerSettingMin);
    lcd.setCursor(3,1);
    lcd.print("min");
}

void screen72(){
    lcd.clear();
    lcd.print("Afkoelpauze:");
    lcd.setCursor(0,1);
    if (pomprust==true){lcd.print("AAN");}
    if (pomprust==false){lcd.print("UIT");}
}

void screen100(){

  lcd.clear();
  lcd.print("Instellingen");
  lcd.setCursor(0,1);
  lcd.print("opgeslagen");
  EEPROM.update(addr1,timerSettingMin);
  delay(2000);
  mode = 1;
  lcdUpdate();
}

void screen101(){

  lcd.clear();
  lcd.print("Instellingen");
  lcd.setCursor(0,1);
  lcd.print("opgeslagen");
  EEPROM.update(addr2,pomprust);
  pomprust = EEPROM.read(addr2);
  delay(2000);
  mode = 1;
  lcdUpdate();
}

void screen200(){
    lcd.clear();
    lcd.print("AFKOELPAUZE pomp");
    lcd.setCursor(0,1);
    lcd.print("Resterend:");
    lcd.setCursor(11,1);
    lcd.print(timerRustMin);
    lcd.setCursor(13,1);
    lcd.print("min");

    pompRust();
}

Looks like you have unintentional recursion (which eventually overflows the call return stack and blows everything up). lcdUpdate() calls screen21(), which calls wateringZONE1(), which then calls lcdUpdate(). This is just an example; other recursive calls exist too.

Don't do that. Let the functions (like waterningZONE1) simply return back to their callers. You might also need to redesign your menu system to use a state machine.

1 Like

@robinheleven, your topic has been moved to a more suitable section of the forum.

I've not see it done like this.. I'd do it..

OneButton button0(
  buttonPin,  // Input pin for the button
  false,       // Button is active high
  false        // Disable internal pull-up resistor
);

Just pattern matching above my pay grade:

I see this all time

Adafruit_NeoPixel strip = Adafruit_NeoPixel(XLEN, NPIN, NEO_GRB + NEO_KHZ800);


and I was surprised to see the following compikle, and assume it amounts to the same thing:

Adafruit_NeoPixel strip(XLEN, NPIN, NEO_GRB + NEO_KHZ800);

I've also seen

Adafruit_NeoPixel strip;

external to all functions, and the following in setup()

 strip = Adafruit_NeoPixel(XLEN, NPIN, NEO_GRB + NEO_KHZ800);

so I don't know which one of those three people "expect" to see, nor why…

Here always to learn at least one new thing every day, so.

a7