Arduino Uno stops in Sketch

Hello everyone,

I have a problem. I don't know right now if it is the code or the hardware that is the problem.
My Arduino Uno controls my watering system. It has done that well for the last few years. Two months ago I added a thermometer and a Wemos D1 which sends me the serial data via Pushbullet.
Yesterday, the Uno just stopped. The LEDs were all inconspicuous. After a reset everything works again. Could someone look over the code if I have built in an error? Please :wink:
Thanks a lot.

//define soil moisture in the next line
int SoilTomato = 650;        // Summer 2021 Soil 610 hot / 630 normal
int SoilGround = 680;        // Summer 2021 Soil 650 hot / 680 normal
int WaterLevel = 800;         // Waterlevel, water tank greater than 800 then empty
int tomatoRead;              //Cache the measurement value.
int groundRead;              //Cache the measurement value.
int waterRead;               //Cache the measurement value.


//int Sensor = A0;            //Senor pin plant
const byte SensorPower = 7;   //Power pin plant - use digital I/O pin
const byte SensorGnd = 8;     //Gnd pin plant - use digital I/O pin
const byte Sensor = A0;       //Sensor pin plant

//int Sensor2 = A1;           //Senor pin water lever
const byte SensorPower2 = 12; //Power pin water level - use digital I/O pin
const byte SensorGnd2 = 13;   //Gnd pin water level - use digital I/O pin
const byte Sensor2 = A1;      //Sensor pin water level

//int Sensor3 = A2;           //Senor pin plant - ground
const byte SensorPower3 = 9;  //Power pin water level - use digital I/O pin
const byte SensorGnd3 = 10;   //Gnd pin water level - use digital I/O pin
const byte Sensor3 = A2;      //Sensor pin water level

//relay solenoid valve
int pumpPin = 4;              //1.
//int pumpPin2 = 3;             //2. Tomato - Solenoid valve
pin,solenoid valve
int pumpPin2 = A4;             //2. Tomato - Solenoid valve
pin,solenoidvalve
int pumpPin3 = 6;             //3. Ground - Solenoid valve pin,solenoidvalve
int pumpPin4 = 5;             //4. Ground2 - Solenoid valve
pin,solenoidvalve

 // SSR pump
//int relayPin = 2;   // set pin 2 for relay output
int relayPin = A3;   // set pin 2 for relay output


//Serial Data for Wemos D1 mini
#include <SoftwareSerial.h>
SoftwareSerial espSerial(2, 3); //RX, TX

//Temp LM35
float tempC;
int tempRead;
int tempPin = A5; // Define to which pin of the Arduino the output of
the LM35 is connected:


void setup() {

  digitalWrite(pumpPin, HIGH);     // Relais wird nichtgestartet,Prevents relays from starting up engaged
  digitalWrite(pumpPin2, HIGH);    // Relais wird nichtgestartet,Prevents relays from starting up engaged
  digitalWrite(pumpPin3, HIGH);    // Relais wird nichtgestartet,Prevents relays from starting up engaged
  digitalWrite(pumpPin4, HIGH);    // Relais wird nichtgestartet,Prevents relays from starting up engaged

  pinMode(pumpPin, OUTPUT);
  pinMode(pumpPin2, OUTPUT);
  pinMode(pumpPin3, OUTPUT);
  pinMode(pumpPin4, OUTPUT);

  pinMode(relayPin, OUTPUT);


  Serial.begin(9600);
  Serial.println("Watering-System is online");
  espSerial.begin(9600);  //ESP-Data
  delay(10000);
  espSerial.println("<Watering-System is online>");
}

void loop() {

//********* Round 1 ***************
//check

  Serial.println(F("***Start measuring plant in 60s! warmuup Sensor***"));

  pinMode(SensorPower, OUTPUT);
  pinMode(SensorPower2, OUTPUT);
  pinMode(SensorPower3, OUTPUT);
  pinMode(SensorGnd, OUTPUT);
  pinMode(SensorGnd2, OUTPUT);
  pinMode(SensorGnd3, OUTPUT);
  digitalWrite(SensorPower, HIGH);
  digitalWrite(SensorPower2, HIGH);
  digitalWrite(SensorPower3, HIGH);
  digitalWrite(SensorGnd, LOW);
  digitalWrite(SensorGnd2, LOW);
  digitalWrite(SensorGnd3, LOW);

  delay(60000);                       //warmup Time



// temp reading with warumup
//***************
  tempRead = analogRead(tempPin);
  delay(1000);
  tempRead = analogRead(tempPin);
  delay(1000);

  float voltage = tempRead * (5000 / 1024.0); // Convert the reading into voltage:
  float tempC = voltage / 10;    // Convert the voltage into the temperature in degree Celsius:


 // Print the temperature in the Serial Monitor:
  Serial.print("temperature: ");
  Serial.print(tempC, 1);
  Serial.print(" \xC2\xB0"); // shows degree symbol
  Serial.println("C");
  delay(1000);

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

  int tomatoRead =  analogRead(A0); //cache the measurement value.
  int groundRead =  analogRead(A2); //cache the measurement value.
  int waterRead = analogRead(A1); //cache the measurement value.

  //Serial.print("plant-tomato:");
  //Serial.println(analogRead(A0));
  Serial.println("plant-tomato: " + String(tomatoRead));
  delay(500);
  //Serial.print("plant-ground:");
  //Serial.println(analogRead(A2));
  Serial.println("plant-ground: " + String(groundRead));
  delay(500);
  Serial.print("water level:");
  Serial.println(analogRead(A1));
  delay(500);




// 1. sensor and pump Tomato:



  if((tomatoRead < SoilTomato) && (analogRead(A1) < WaterLevel)) {
    Serial.println("Soil Tomato still moist: " + String(tomatoRead) + ", Water tank filled.");
    //espSerial.println("<Tomato moist: " + String(tomatoRead) + ", tank filled.>");
    }

  if((analogRead(A0) < SoilTomato) && (analogRead(A1) > WaterLevel)) {
    Serial.println("Soil Tomato still moist: " + String(tomatoRead) + ", empty water tank!!!");
    //espSerial.println("<Tomato moist: " + String(tomatoRead) + ", empty tank!!!>");
    }

  if((analogRead(A0) > SoilTomato) && (analogRead(A1) > WaterLevel)) {
    Serial.println("Soil Tomato dry: " + String(tomatoRead) + ", empty water tank!!!");
    //espSerial.println("<Tomato dry: " + String(tomatoRead) + ", empty tank!!!>");
    }


  if((analogRead(A0) >SoilTomato) && (analogRead(A1) <WaterLevel)) {

    //int tomatoRead =  analogRead(A0); //cache the measurement value.
    digitalWrite(relayPin, HIGH);
    Serial.println(F("pump-on:"));
    delay(2000);                                          //pump pressure delay 2 sec
    digitalWrite(pumpPin2, LOW);
    Serial.println(F("Tomato-valve-on:"));
    delay(20000); //watering time
    digitalWrite(relayPin, LOW);
    Serial.println(F("pump-off:"));
    digitalWrite(pumpPin2, HIGH);
    Serial.println(F("Tomato-valve-off:"));
    Serial.println(F("watering tomato successfully"));
    espSerial.println("<watering tomato successfully --" + String(tomatoRead) + "-- Temp: " + String(tempC, 1) + " \xC2\xB0" + "C>");
    }


// 1. sensor and pump Ground:

  if((groundRead <SoilGround) && (analogRead(A1) <WaterLevel)) {
    Serial.println(("Soil Ground still moist: " + String(groundRead) + ", Water tank filled."));
    //espSerial.println("<Ground moist: " + String(groundRead) + ", tank filled.>");
    }

  if((groundRead <SoilGround) && (analogRead(A1) >WaterLevel)) {
    Serial.println(("Soil Ground still moist: " + String(groundRead) +
", empty water tank!!!"));
    //espSerial.println("<Ground moist: " + String(groundRead) + ", empty tank!!!>");
    }

  if((groundRead >SoilGround) && (analogRead(A1) >WaterLevel)) {
    Serial.println(("Soil Ground dry: " + String(groundRead) + ", empty water tank!!!"));
    //espSerial.println("<Ground dry: " + String(groundRead) + ", empty tank!!!>");
    }

     if((groundRead >SoilGround) && (analogRead(A1) <WaterLevel)) {
    Serial.println(("Soil Ground dry: " + String(groundRead) + ", Water tank filled."));
    }

  if((groundRead >SoilGround) && (analogRead(A1) <WaterLevel)) {

    //int groundRead =  analogRead(A2); //cache the measurement value.
    digitalWrite(relayPin, HIGH);
    Serial.println(F("pump-on:"));
    delay(2000); //pumppressure delay 2 Sec
    digitalWrite(pumpPin3, LOW);
    Serial.println(F("Ground1-valve-on:"));
    delay(25000); //watering time
    digitalWrite(pumpPin3, HIGH);
    Serial.println(F("Ground1-valve-off:"));

    delay(2000);                                         //Break 2 Sec

    digitalWrite(pumpPin4, LOW);
    Serial.println(F("Ground2-valve-on:"));
    delay(25000); //watering time
    digitalWrite(relayPin, LOW);
    Serial.println(F("pump-off:"));
    digitalWrite(pumpPin4, HIGH);
    Serial.println(F("ground2-valve-off:"));

    Serial.println(F("watering ground successfully"));
    espSerial.println("<watering ground successfully --" + String(groundRead) + "-- Temp: " + String(tempC, 1) + " \xC2\xB0" + "C>");
    }

int waterReadEnd = analogRead(A1); //cache the measurement value.


//poweroff
 { Serial.println(F("***Measurement done. Disconnecting sensor.***"));
  pinMode(SensorPower, INPUT);
  pinMode(SensorGnd, INPUT);
  pinMode(SensorPower2, INPUT);
  pinMode(SensorGnd2, INPUT);
  pinMode(SensorPower3, INPUT);
  pinMode(SensorGnd3, INPUT);
  }

Serial.print("waterlevel:");
Serial.println(waterReadEnd);

if(waterReadEnd > WaterLevel) {
   Serial.println("no water");
   Serial.println("delay 12h");
    espSerial.println("<no water>");
  delay(12UL * 60UL * 60UL * 1000UL); //12hour, x hours each of 60 minutes each of 60 seconds each of 1000 milliseconds all unsigned longs
}else {
  Serial.println("delay 1h");
  delay(1UL * 60UL * 60UL * 1000UL); //1hour
  }

 // delay(3600000); // 1hour
//  delay(7200000); // 2hour


} 

Do your serial prints offer any clue to where it stops?

What is the last thing printed?

Rather than taking reading in the if conditions, do the analogReads at the top of loop, and store the reading s in variables.

It'll make debugging much less stressful.

It was all inconspicuous. "watering ground successfully "
After that, nothing more happened until I did the reset. Normally I get a message after 6 hours at the latest, then the soil is too dry. This time, however, not. As I said that was the first time ever that this happened. I am now trying to figure out what the error is. I am also not a good coder. :wink:
The next question is if it is not the code, how can I check this in the future. I am often not at home for a reset. Should I install a watchdog or rather a wlan socket so I can reset the Uno remotely if this happens again.

That's right. I always had different readings at the beginning because they were measured at different times. It was easier for me to work with a "global" variable. So that this is always the same.
But as said I am not a coder. I read and learned all this myself. But I am very grateful for tips. Also to make the code better readable.
Refactoring is not my strength.

@stereo007, your topic has been moved to a more suitable location on the forum.

I assume that the posted code is the actual code running on the UNO and not on the ESP, if that is the case, remove all references to the "String()" class / method. Here is an example of how to do that:

//Current code:
Serial.println("plant-tomato: " + String(tomatoRead));

//Better code:
Serial.print(F("plant-tomato: "));
Serial.println(tomatoRead);

In fact, this approach has been used elsewhere in the code, so..

Also for espSerial.println?

Yes, exterminate everything "String()" in your UNO sketch.

Okay, I will.
Yes, I had it that way before. I do the same with ESP. Did not know that the Uno does not work so. Had read and written nicer. :wink:

UNO has a very crude memory management, it does not handle the "String" class very well. ESP has much better memory management and the usage of String will (most likely) cause no issues on the ESP / FreeRTOS.

Is there any other fundamental error in the sketch that I should definitely change?

You could rationalise the pin numbers to be all "const byte"

I'm not clear why some pinModes.are in loop()

UNO will not crash using Strings due to the way it allocates memory that is actually safer than the ESP (see my Taming Arduino Strings for the details)
If you happen to run out of memory, your String results will just be empty and you should be able to see that from you Serial.print( ) and what is printed to the ESP

The String + operator allocates more memory than just doing the individual prints but you have no other allocations going and all your String usage is recovered at the end of each loop() so there should be no problems there.
Adding just one { } blocks will force recovery of all your String memory as soon as it is finished with.

  {   // block this to recover String memory
  //Serial.print("plant-tomato:");
  //Serial.println(analogRead(A0));
  Serial.println("plant-tomato: " + String(tomatoRead));
  delay(500);
  //Serial.print("plant-ground:");
  //Serial.println(analogRead(A2));
  Serial.println("plant-ground: " + String(groundRead));
  delay(500);
  Serial.print("water level:");
  Serial.println(analogRead(A1));
  delay(500);
  }

All other String usage is already in { } and so any memory allocated is recovered directly and reused.

Having said that ALL your use of Strings are un-necessary and can be easily replaces with consecutive prints( ) as noted above by @Danois90 #7
E.g.
Serial.println("plant-tomato: " + String(tomatoRead));
becomes
Serial.print(F("plant-tomato: ")); Serial.println(tomatoRead);
// not the F( ) to save more RAM

I am running your code here with much shorter delay 2sec instead of hrs I got this compile msg

C:\Users\matthew\Documents\Arduino\WaterSystem\WaterSystem.ino:116:7: warning: unused variable 'waterRead' [-Wunused-variable]
   int waterRead = analogRead(A1); //cache the measurement value.

Is that important to your logic

Here is the output I am getting (with added memory freeList output at the end of loop())
No memory allocated at all!!
So looks like some other problem.

09:28:28.781 -> Watering-System is online
09:28:38.826 -> ***Start measuring plant in 60s! warmuup Sensor***
09:28:42.828 -> temperature: 247.6 °C
09:28:43.811 -> plant-tomato: 423
09:28:44.319 -> plant-ground: 382
09:28:44.829 -> water level:387
09:28:45.305 -> Soil Tomato still moist: 423, Water tank filled.
09:28:45.373 -> Soil Ground still moist: 382, Water tank filled.
09:28:45.407 -> ***Measurement done. Disconnecting sensor.***
09:28:45.475 -> waterlevel:381
09:28:45.475 -> delay 1h
09:28:47.444 -> end of loop() totalFreeList:0 totalAllocatedFragements:0 topFreeHeap:1330 freeList+topFreeHeap:1330
09:28:47.545 -> ***Start measuring plant in 60s! warmuup Sensor***
09:28:51.515 -> temperature: 173.3 °C
09:28:52.534 -> plant-tomato: 374
09:28:53.042 -> plant-ground: 350
09:28:53.517 -> water level:382
09:28:54.025 -> Soil Tomato still moist: 374, Water tank filled.
09:28:54.092 -> Soil Ground still moist: 350, Water tank filled.
09:28:54.126 -> ***Measurement done. Disconnecting sensor.***
09:28:54.194 -> waterlevel:369
09:28:54.194 -> delay 1h
09:28:56.126 -> end of loop() totalFreeList:0 totalAllocatedFragements:0 topFreeHeap:1330 freeList+topFreeHeap:1330

Edit --
I have been running the original code (with the Strings) here for a few hours, using delays of a few secs instead of hours and no problems observed and still no memory used

end of loop() totalFreeList:0 totalAllocatedFragements:0 topFreeHeap:1330 freeList+topFreeHeap:1330

SO definitely NOT a String problem

@drmpf In this topic you have posted 3 replies, none of which is helpful to OP and your "pro String" crusade does not bite on me. You keep posting the same arguments and a link to a "taming the String" guide which IMHO is a waste of time and space. "String" is used by most beginners as a simple string handler, no beginner wants to read an exhausting and boring manual in order to use "String".

When a sketch, which uses the "String" class, works perfectly but after some time stops responding, "String" is the most likely culprit - whether you like it or not. I believe that this is a common opinion amongst most of the frequent users around here.

1 Like

Yes I am surprise by how may miss-informed users there are. If you have a sketch on a UNO where Strings stops it I would be interested in seeing it. So far no one has been able to provide one. The two examples put forward so far turned out to be due out to be other issues.

But I look forward to your example. Always happy to be proved wrong.

Do not argue :wink: Please.
I have removed all String(). What can I do in the future if the sketch again simply stops.
I have now bought a wifi socket. So I can at least restart the Uno when I'm not at home and I no longer get push messages.
Can I do anything else? A watchdog or the like does not work with my Sketch or?

If it still causes issues, I would try to periodically "blink" the LED_BUILTIN (or another status LED) on the Arduino UNO (and the Wemos) - this would require much of the code to be rewritten because you are using delay quite a lot. By periodically blinking a LED, you can determine whether it is actually the UNO or something else that stops responding. An electrical issue may cause the serial connection to the Wemos to stop working, the Wemos may disconnect from WiFi and require a manual re-connect.

Another tip that has not been given is to use conditional defines to remove all your non-ESP related serial communication for the non-debugging version of the build:

//Comment out the next line to remove serial debugging
#define DEBUG

#ifdef DEBUG
  #define s_print(what) Serial.print(what)
  #define s_println(what) Serial.println(what)
#else
  #define s_print(what)
  #define s_println(what)
#endif

void setup()
{
#ifdef DEBUG
  Serial.begin(9600);
#endif
}

void loop()
{
  int tomatoState = analogRead(XY);
  s_print(F("The tomato state: "));
  s_println(tomatoState);
}