Need Help, avoid writing duplicate code for multiple same type sensors

Hello to everyone!

I am not a deep programmer, i am not interstead to go deeper in my age.

I have mutiple same type soil sensors each one interfacing in diffrent pins.
I want to avoid writing duplicate codes for the same reading function. How can i change the following code and make it smaller and simpliest and read data every 6 hours using rtc withour overflow problems? CAN SOMEONE HELP ME ?

Any help it will appreciate

#include <SoftwareSerial.h>
#include <Wire.h>
#include "RTClib.h"

RTC_DS1307 rtc;
char daysOfTheWeek[7][12] = {"Sunday","Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};


int sensorPin = A0;    // select the input pin for the Soil moisture sensor
int sensorValue = A0;  // variable to store the value coming from the sensor
int sensor1Pin = A1;    // select the input pin for the Soil moisture sensor
int sensor1Value = A1;  // variable to store the value coming from the sensor
int sensor2Pin = A2;    // select the input pin for the Soil moisture sensor
int sensor2Value = A2;  // variable to store the value coming from the sensor
int sensor3Pin = A3;    // select the input pin for the Soil moisture sensor
int sensor3Value = A3;  // variable to store the value coming from the sensor
int sensor4Pin = A4;    // select the input pin for the Soil moisture sensor
int sensor4Value = A4;  // variable to store the value coming from the sensor

void setup()  {
  Serial.begin(9600);
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }
  if (! rtc.isrunning()) {
    Serial.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
    // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    rtc.adjust(DateTime(2018, 11, 11, 04, 45, 0));   // <----------------------SET TIME AND DATE: YYYY,MM,DD,HH,MM,SS
  }
delay(100);
}


void loop() {

{
    DateTime now = rtc.now();
    Serial.print(now.day(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.year(), DEC);
    Serial.print(" (");
    Serial.print(daysOfTheWeek[now.dayOfTheWeek()]);
    Serial.print(") ");
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.println();
    
    delay(3000); //Print date and time every 3 sec
}
    
  sensorValue = analogRead(A0);    // read the value from the sensor1:
  delay(1000);
  Serial.print("sensor = " );
  Serial.println(sensorValue);

  delay(20000);

  sensor1Value = analogRead(A1);   // read the value from the sensor2
  delay(1000);
  Serial.print("senso1r = " );
  Serial.println(sensor1Value);


  delay(20000);
  sensor2Value = analogRead(A2);    // read the value from the sensor1:
  delay(1000);
  Serial.print("sensor2 = " );
  Serial.println(sensor2Value);

  delay(20000);
  sensor3Value = analogRead(A3);    // read the value from the sensor1:
  delay(1000);
  Serial.print("sensor3 = " );
  Serial.println(sensor3Value);

  delay(20000);
  sensor4Value = analogRead(A4);    // read the value from the sensor1:
  delay(1000);
  Serial.print("sensor4 = " );
  Serial.println(sensor4Value);
}

Read up on arrays[] - and then an array [] of struct{}
One array to reference all operations on any sensor.

... and lose the delay() before it causes other problems in the future.

Can you give me a small example ?

I did something earlier for a different problem, but it shows exactly how to use these constructs...

https://forum.arduino.cc/index.php?topic=578541.msg3940643#msg3940643

Look at the OPs original code, and my (later) rewrite using an array of structs.

TRAIST:
I am not a deep programmer, i am not interstead to go deeper in my age.

Then why are you here? This really encourages me to spend my time trying to help. If you don't want to learn, then what you're looking for is simply somebody to do it for you. At my age, I think NOT.

I wanted to give the OP a chance... I’d guess i’m probably older than they, and only started with Arduino a couple of years ago.
Sure, I’ve always worked in tech, and related skills - but it’s NEVER too late to learn.

I'm probably older than both of you. I've been doing this for 50 years and I still enjoy it, so I have little time for those who aren't 'interested'.

FOR mr DKWatson.

First learn how to speak polite , and we shall discusse about spending time. Please spent your time for somewere else and dont spent mine. Please do have a nice day and dont answer me YOU SPENT MY TIME

i will check OPs original code and How toggle the output of shift register individually with buttons? - #14 by lastchancename - Project Guidance - Arduino Forum

.Thank you for the reply.

At 60, I'm possibly the youngest of all of you 8)

@TRAIST
You will have to learn. If you want somebody to write it for you, head over to the gigs and collaboration section and be prepared to pay.

To help you with the learning, you can start by writing a function that takes a pin number.

void readSoilSensor(byte pinNo)
{

}

In that function, you read the sensor that is connect to that pin and take action based on the value (in your case the serial prints and delays.

/*
  read and display soil sensor
  In:
    pin number for soil sensor
*/
void doSoilSensor(byte pinNo)
{
  int sensorValue = analogRead(pinNo);
  delay(1000);
  Serial.print("sensor = " );
  Serial.println(sensorValue);

  delay(20000);
}

You can place the above at the end of your code so the framework for your code looks like

...
...

void setup()
{
  ...
  ...
}

void loop()
{
  ...
  ...
}

void doSoilSensor()
{
  ...
  ...
}

Now in loop(), you can call the function using the pin number

void loop()
  // your rtc stuff here
  ...
  ...

  // read the sensors and display
  doSoilSensor(sensorPin);
  doSoilSensor(sensor1Pin);
  doSoilSensor(sensor2Pin);
  doSoilSensor(sensor3Pin);
}

The above will do what you asked for. I however have my doubts that printing to serial monitor is all you want to do. You might want to control a relay if a certain sensorValue is reached so you open a valve and give your plant some water.

Before we get there, let met question your current code.
1)
Why do you set your sensorValues to A0, A1 etc? If you use

int sensorValue;

sensorValue will be initialised with zero (because it's a global variable). You approach costs CPU cycles as well as memory.
2)
Why the delay between reading the sensor and displaying the result?

OK, as mentioned by @lastchancename you can use arrays for your pin numbers and your values. I've added an array for relay pins.

const byte sensorPins[] = {A0, A1, A2, A3};
const byte relayPins[] = {7, 8, 9, 10};

You can iterate through the sensorPins array in loop()

void loop()
{
  // your rtc stuff here
  ...
  ...

  // read the sensors and display
  for (uint8_t cnt = 0; cnt < sizeof(sensorPins); cnt++)
  {
    doSoilSensor(sensorPins[cnt]);
  }
}

In the next step, doSoilSensor is modified so it does not take the pin number but the index in the array.

/*
  read and display soil sensor
  In:
    zero based sensor number
*/
void doSoilSensor(byte sensorNo)
{
  int sensorValue = analogRead(sensorPins[sensorNo]);
  delay(1000);
  Serial.print("sensor = " );
  Serial.println(sensorValue);

  delay(20000);
}

And loop() changes to

void loop()
{
  // your rtc stuff here
  ...
  ...

  // read the sensors and display
  for (uint8_t cnt = 0; cnt < sizeof(sensorPins); cnt++)
  {
    doSoilSensor(cnt);
  }
}

And now you can also use the sensor number to e.g. control the associated array in doSoilSensor(). Note that for this step sensorPins[0] is associated with relayPins[0] etc.

/*
  read, action and display soil sensor
  In:
    zero based sensor number
*/
void doSoilSensor(byte sensorNo)
{
  int sensorValue = analogRead(sensorPins[sensorNo]);

  // if too dry
  if (sensorValue < 500)
  {
    digitalWrite(relayPins[sensorNo], HIGH);
  }
  // if wet enough
  else if (sensorValue > 800)
  {
    digitalWrite(relayPins[sensorNo], LOW);
  }
  else
  {
    // keep status quo
  }

  Serial.print("sensor = " );
  Serial.println(sensorValue);

  delay(20000);
}

In the next step, you can use a struct. This is like an entry in a phone book where a name is linked with a phone number. In the above code, the sensor pin number and the relay pin number will be combined.

#include <SoftwareSerial.h>
#include <Wire.h>
#include "RTClib.h"

RTC_DS1307 rtc;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

struct PIN
{
  const byte sensorPin;
  const byte relayPin;
};

PIN pins[] = 
{
  {A0, 7},
  {A1, 8},
  {A2, 9},
  {A3, 10},
};

void setup()
{
  ...
  ...
  
  for(
}

void loop()
{
[code]void loop()
{
  // your rtc stuff here
  ...
  ...

  for (uint8_t cnt = 0; cnt < sizeof(pins) / sizeof(pins[0]); cnt++)
  {
    doSoilSensor(cnt);
  }
}

Note that, because an element in the array no longer has the size of one byte, the for loop now differs. sizeof(pins) / sizeof(pins[0]) gives you the number of elements, regardsless of te size of the first element.

And the doSoilSensor needsto change to use the PIN struct

/*
  read, action and display soil sensor
  In:
    zero based sensor number
*/
void doSoilSensor(byte sensorNo)
{
  int sensorValue = analogRead(pins[sensorNo].sensorPin);

  // if too dry
  if (sensorValue < 500)
  {
    digitalWrite(pins[sensorNo].relayPin, HIGH);
  }
  // if wet enough
  else if (sensorValue > 800)
  {
    digitalWrite(pins[sensorNo].relayPin, LOW);
  }
  else
  {
    // keep status quo
  }

  Serial.print("sensor = " );
  Serial.println(sensorValue);

  delay(20000);
}

Now the above uses hard-coded values for the limits. You can either define those near the top of the code or if the limits for the soil sensors differ, you can add them to the struct. E.g.

struct PIN
{
  const byte sensorPin;
  const byte relayPin;
  int lowLimit;
  int highLimit;
};

PIN pins[] = 
{
  {A0, 7, 500, 800},
  {A1, 8, 600, 700},
  {A2, 9, 300, 1000},
  {A3, 10, 500, 800},
};

and you can use highLimit and lowLimit in the doSoilSensor() function.

Question
Which Arduino are you using? On an Uno, A4 is part of the I2C bus and you can not use it for an analogRead if you also use I2C; same for A5.

Thank you for you reply and your time.
. Ι will check i.

Ι Appreciate !!

I assume that the reason that you use the delay(20000) is that you don't want a serial monitor flooded with information. It however results in an application that is not responsive. If you want e.g. to be able to manually override the relays using buttons, you will have to get rid of the delay.

You can send data to serial monitor if the sensor measurement changes. For that you need to remember the last value of the reading and you can add a field to the struct

struct SENSOR
{
  const byte sensorPin;
  const byte relayPin;
  int lowLimit;
  int highLimit;
  int lastValue;
};

SENSOR sensors[] = 
{
  {A0, 7, 500, 800, -1},
  {A1, 8, 600, 700, -1},
  {A2, 9, 300, 1000, -1},
  {A3, 10, 500, 800, -1},
};

I've renamed the struct to SENSOR as it no longer only contains pins. You will have to replace pins in the earlier code by sensors.

In doSoilSensor, you can remember the setting

/*
  read, action and display soil sensor
  In:
    zero based sensor number
*/
void doSoilSensor(byte sensorNo)
{
  int sensorValue = analogRead(sensors[sensorNo].sensorPin);

  // if too dry
  if (sensorValue < 500)
  {
    digitalWrite(sensors[sensorNo].relayPin, HIGH);
  }
  // if wet enough
  else if (sensorValue > 800)
  {
    digitalWrite(sensors[sensorNo].relayPin, LOW);
  }
  else
  {
    // keep status quo
  }

  // only display sensor data on change or at the first run (lastValue = -1)
  if (sensorValue != sensors[sensorNo].lastValue || sensors[sensorNo].lastValue == -1)
  {
    // remember sensorValue on change
    sensors[sensorNo].lastValue = sensorValue;
    
    Serial.print("sensor "); Serial.print(sensorNo + 1);
    Serial.print(": "); Serial.println(sensorValue);
  }
}

Because there is always some fluctuation in the analog readings that can still result in continuous updates, you add a small guard (e.g. +/- 3). I think that the below will do the trick

 // only display sensor data on change or at the first run (lastValue = -1)
  if ((sensorValue - sensors[sensorNo].lastValue > 3) ||
      (sensorValue - sensors[sensorNo].lastValue < -3) ||
      sensors[sensorNo].lastValue == -1)
  {
    // remember sensorValue on change
    sensors[sensorNo].lastValue = sensorValue;

    Serial.print("sensor "); Serial.print(sensorNo + 1);
    Serial.print(": "); Serial.println(sensorValue);
  }

I will act with greater care based on what you wrote to me. Your help is valuable.!

Thank very much!

P/S please see my pm that i sen you.