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. ![]()

