3 buttons - 1. value + / 2. value - / 3. change to different value

BTW you don't need a FET to power the soilsensor, you can power it from an Arduino pin.

PaulS:
The analogRead() function can not fail to return some value.

said it wrong, I get a few normal values (currently around 700) and then a few "bad" ones (1023).

I get a reading of 1023 when there is no "fork" attached (like in attachment 1, compared to attachment 2 with fork "plugged into soil"), so "as if the pin were disconnected" but i do not touch the system between "good" and "bad" readings.

the conclusion that I get out of 700 and then 1023 is, that something with the timing might be wrong.
As if with the 1023 readings, the sensor is not ready
stray voltage... mmm. with the same setup but different program I've no "bad" readings at all.

will reduce the time between power on and read and read and power off. and play with those values.

Whandall:
BTW you don't need a FET to power the soilsensor, you can power it from an Arduino pin.

ok, thought this way would be easier, as I will connect 4-5 soil sensors to the same FET and power them together.
Or does the arduino have enough power for several sensors simultaneously?

the question overall, which way is adviced?

SensorOn(); int time_between_reads = 10000;
SensorRead(); time_between_reads + 500)
SensorOff(); time_between_reads + 2000)

or

SensorOn(); int time_between_reads = 10000;
SensorRead(); 20 after SensorOn();
SensorOff(); 50after SensorRead();

kundun:
SensorOff(); 50after SensorRead();

Why do you want to leave the sensor powered for 50mS after it is read? Does that serve any useful purpose?

kundun:
Because the soil moisture sensors are in contact with water and soil and are under current, they deteriorate very quickly.

you wouldn't believe how quickly they go bad when powered on all the time

kundun:
you wouldn't believe how quickly they go bad when powered on all the time

especially with DC voltage

OldSteve:
Why do you want to leave the sensor powered for 50mS after it is read? Does that serve any useful purpose?

kundun:
you wouldn't believe how quickly they go bad when powered on all the time

That's my point exactly. Why power it for even 50ms longer than is necessary?

you mean that the next line after "reading" can be "turn off", as I have the readout already?

kundun:
you mean that the next line after "reading" can be "turn off", as I have the readout already?

Yep. There's nothing to gain by keeping the sensor powered for a further 50ms.

I can not test the buttons, my small OLED Nano has none.

I used Bounce2 for the button handling, two small statemachines for the sensors and the pump.
Like your code this looks only at the moisture of the first plant.

#include <Bounce2.h> // https://github.com/thomasfredericks/Bounce2
#include "U8glib.h"

const byte plusKeyPin = 5;
const byte minusKeyPin = 6;
const byte plantsKeyPin = 7;

const byte sensorFetPin = 2;
const byte pumpFetPin = 11;

enum pStates {
  pOff,
  pRunning,
  pBlocked,
};

byte pumpState;
unsigned long lastPumpEvent;
const unsigned long pumpOnDuration = 3000;
const unsigned long pumpBlockTime = 30000;

enum sStates {
  sOff,
  sPower,
  sMeasure,
};

byte sensorState;
unsigned long lastSensorEvent;
const unsigned long measureInterval = 10000;
const unsigned long sensorPrePower = 20;

Bounce plusKey;
Bounce minusKey;
Bounce plantsKey;

const byte moistureSensor[] = { A0, A1, A2 };
int  moistureThreshold[] = { 700, 200, 300};
int  moistureValue[] = { 0, 0, 0};

int plantNumber = 0;

unsigned long lastPrint;
unsigned long topLoop;

U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);

void setup() {
  pinMode(pumpFetPin, OUTPUT);
  pinMode(sensorFetPin, OUTPUT);
  plusKey.attach(plusKeyPin, INPUT_PULLUP);
  minusKey.attach(minusKeyPin, INPUT_PULLUP);
  plantsKey.attach(plantsKeyPin, INPUT_PULLUP);
  Serial.begin(115200);
  Serial.println(F("plantMenu"));
  u8g.setFont(u8g_font_unifont);
  u8g.setColorIndex(1); // draw with ON.
  setPumpState(pOff);
  setSensorState(sPower);
}

void loop() {
  topLoop = millis();
  plusKey.update();
  minusKey.update();
  plantsKey.update();

  if (plusKey.fell()) {
    moistureThreshold[plantNumber] += 5;
  }
  if (minusKey.fell()) {
    moistureThreshold[plantNumber] -= 5;
  }
  if (plantsKey.fell()) {
    if (++plantNumber >= (sizeof(moistureValue) / sizeof(moistureValue[0]))) {
      plantNumber = 0;
    }
  }

  if (pumpState == pRunning) {
    if (topLoop - lastPumpEvent >= pumpOnDuration) {
      setPumpState(pBlocked);
    }
  } else if (pumpState == pBlocked) {
    if (topLoop - lastPumpEvent >= pumpBlockTime) {
      setPumpState(pOff);
    }
  }

  if (sensorState == sOff) {
    if (topLoop - lastSensorEvent >= measureInterval) {
      setSensorState(sPower);
    }
  } else if (sensorState == sPower) {
    if (topLoop - lastSensorEvent >= sensorPrePower) {
      setSensorState(sMeasure);
    }
  }

  if (pumpState == pOff) {
    // only look at plant 1 for now
    if (moistureValue[0] > moistureThreshold[0]) {
      setPumpState(pRunning);
    }
  }

  u8g.firstPage();
  do {
    draw_lcd();
  }
  while ( u8g.nextPage() );

  if (topLoop - lastPrint >= 6000) {
    lastPrint = topLoop;
    for (byte pIdx = 0; pIdx < sizeof(moistureValue) / sizeof(moistureValue[0]); pIdx++) {
      Serial.print(F("Plant "));
      Serial.print(pIdx + 1);
      Serial.print(F(" Threshold "));
      Serial.print(moistureThreshold [pIdx]);
      Serial.print(F(" Reading "));
      Serial.println(moistureValue[pIdx]);
    }
  }
}

void setPumpState(byte newState) {
  Serial.print(F("Pump "));
  Serial.print(pumpState);
  Serial.print(F(" to "));
  Serial.println(newState);
  pumpState = newState;
  lastPumpEvent = topLoop;
  switch (pumpState) {
    case pOff:
      digitalWrite(pumpFetPin, LOW);
      break;
    case pRunning:
      digitalWrite(pumpFetPin, HIGH);
      break;
    case pBlocked:
      digitalWrite(pumpFetPin, LOW);
      break;
  }
}

void setSensorState(byte newState) {
  Serial.print(F("Sensor "));
  Serial.print(sensorState);
  Serial.print(F(" to "));
  Serial.println(newState);
  lastSensorEvent = topLoop;
  sensorState = newState;
  switch (sensorState) {
    case sOff:
      digitalWrite(sensorFetPin, LOW);
      break;
    case sPower:
      digitalWrite(sensorFetPin, HIGH);
      break;
    case sMeasure:
      for (byte pIdx = 0; pIdx < sizeof(moistureValue) / sizeof(moistureValue[0]); pIdx++) {
        int junk = analogRead(moistureSensor[pIdx]);
        moistureValue[pIdx] = analogRead(moistureSensor[pIdx]);
      }
      digitalWrite(sensorFetPin, LOW);
      setSensorState(sOff);
      break;
  }
}

void draw_lcd() {

  u8g.drawStr( 0, 10, "1.");
  u8g.setPrintPos(20, 10);
  u8g.print(moistureThreshold[0]);
  if (plantNumber == 0)
    u8g.drawStr( 52, 10, "-");
  u8g.setPrintPos(64, 10);
  u8g.print(moistureValue[0]);

  u8g.drawStr( 0, 22, "2.");
  u8g.setPrintPos(20, 22);
  u8g.print(moistureThreshold[1]);
  if (plantNumber == 1)
    u8g.drawStr( 52, 22, "-");
  u8g.setPrintPos(64, 22);
  u8g.print(moistureValue[1]);

  u8g.drawStr( 0, 34, "3.");
  u8g.setPrintPos(20, 34);
  u8g.print(moistureThreshold[2]);
  if (plantNumber == 2)
    u8g.drawStr( 52, 34, "-");
  u8g.setPrintPos(64, 34);
  u8g.print(moistureValue[2]);

  // u8g.drawStr( 0, 46, "4.");
  // u8g.drawStr( 0, 58, "5.");
}
plantMenu
Pump 0 to 0
Sensor 0 to 1
Sensor 1 to 2
Sensor 2 to 0
Plant 1 Threshold 700 Reading 64
Plant 2 Threshold 200 Reading 202
Plant 3 Threshold 300 Reading 252
Sensor 0 to 1
Sensor 1 to 2
Sensor 2 to 0
Plant 1 Threshold 700 Reading 67
Plant 2 Threshold 200 Reading 175
Plant 3 Threshold 300 Reading 227
Plant 1 Threshold 700 Reading 67
Plant 2 Threshold 200 Reading 175
Plant 3 Threshold 300 Reading 227

or maybe even a loss, with "bad" readings . thanks

Whandall, thanks a lot. there are so many new features in your code, that will take me a while (if even possible) to comprehend.

Just have a look.

I tried to find better names and to isolate the functionalities.
Using Bounce2 hides all the debouncing and statehandling of the keys, which is quite nice IMHO.

I don't want to discourage you or your efforts.

it does look quite nice.

I do like challenges, otherwise I'd given up a while ago. Sometimes, it's just a mater of having time or not. or "honey, I'm going to bed, are you coming too?"

have not had much time, but just couldnt leave my fingers from a little testing.

Whandall, I've thrown your code at my board. I get a reading but the buttons have not worked. But as I said, had no time to see why not. I'll give it a go on the weekend. I like your code as it is much cleaner (an has new different approach).

but then I couldn't leave my fingers from the original code. Readings follow in the next post. Interestingly, the SensorOff() with 2000 seems to have been the problem. After getting rid of it, the readings became (at least in those short sequences) stable.

then I had to play with the time where the sensor is powered on.
the very last column shows the readings of a sensor that is always powered on and I'd say that could be taken as a reference point.

now looking at the other readings, is clear that they are quite a bit off from the reference point. It seems that the sensor needs to be powered on for about 500mS to get a reliable reading.

About the table
SensorOn() = 6000 --> int time_between_reads = 6000;
SensorRead() = 6000 + 500 --> time_between_reads + 500

SensorOn(); 6'000 10'000 10'000 10'000 10'000 10'000 10'000 6'000 always
SensorRead(); 6'000 + 500 10'000 + 500 + 20 + 50 + 100 + 300 + 500 + 20 every 2000
SensorOff(); 6'000 + 2000 10'000 + 2000 A A A A A A
799 757 720 719 685 739 752 86 744
A 1023 757 720 719 685 739 752 35 755
= right after 1023 766 717 732 714 749 756 33 763
sensor off 1023 766 711 732 714 749 756 34 770
1023 781 711 738 725 754 757 31 777
1023 798 706 736 733 758 761 32 783
776 798 706 736 733 758 761 34 787
791 801 702 734 737 759 763 33 791
797 801 700 733 737 759 763 31 793
800 1023 700 733 739 761 768 32 795
1023 1023 696 738 743 764 768 31 795
798 1023 696 738 743 764 771 32 797
800 798 695 736 744 767 775 29 798
800 798 693 736 744 767 775 27 798
1023 800 693 736 746 770 781 30 798
1023 800 691 735 747 770 781 24 801
800 803 691 735 747 772 790 29 800
801 803 690 735 749 774 790 26 800
801 802 690 736 749 774 795 28 800
803 690 750 778 798 29 801
689 751 778 798 26 800
689 751 783 802 29 803
753 802 801

My code assumes an INPUT_PULLUP without external resistors closing to GND.
If you have external pull downs and closing to 5V you should use
.attach(key) and .rose() in place of .attach(key, INPUT_PULLUP) and .fell().

About the table: I think +50 mS is close enough.

Works like a charm!
regards Whandall

next step would be putting the pumpFetPin into an array, right?

Yes, if you want to control more than one pump, an array could be useful. :wink: