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

I'm sure, that the information must be on the net or the forum, just couldn't find it.
Any link to a useful site is appreciated

This is an extract from my watering program. It should result in setting the values where the solenoid valve opens and waters each individual plant.

I've wired 3 tactile switches to my nano.

button 1 - increases the threshold value by 5
button 2 - decreases the threshold value by 5
those two are working fine.

The problem lies with button 3, which should change between the different values/plants.
(Value1= plant1; Value2=plant2; Value2=plant3)

In other words,
once pressing button 3, allows me to change "Value2"
next push on button 3, allows me to change "Value3"
next push on button 3, back to "Value1"

int Value1 = 835;
int Value2 = 835;
int Value3 = 735;

void setup() {
  Serial.begin(9600); 
  pinMode(5, INPUT); //Value +5
  pinMode(6, INPUT); //Value -5
  pinMode(7, INPUT); //change Value
}

void loop() {
  int b1 = digitalRead(5);
  int b2 = digitalRead(6);

  if (HIGH == b1) 
      {Value1 += 5;}
  if (HIGH == b2) 
      {Value1 -= 5;}

}

Thanks for hints or links to a relevant page.

What have you tried? Your example doesn't even get as far as reading the third button. You can do better than that!

Hints:
You'll probably need a variable to keep track of which value you are changing.
If you put your values in an array it will be easier to write the code to change the right value.

This is an extract from the answer. The problem is obviously.....

Please post all of your code and not just an extract (snippet).

How can we possibly guess what's going wrong when button 3 is pressed if you don't show that part of your code?

you can try something like this (un-tested):

enum PlantType{
  ROSE,
  FICUS,
  FERN,
  END_OF_LIST
};
PlantType plantType = ROSE;

char* plantString[] = {"Rose", "Ficus", "Fern", "None"};

int Value1 = 835;
//int Value2 = 835;
//int Value3 = 735;

const byte buttonPin[] = {5,6,7};

void setup() 
{
  Serial.begin(9600); 
  for(byte i = 0; i < sizeof(buttonPin); i++)
  {
    pinMode(buttonPin[i], INPUT_PULLUP);
  }
}

void loop() 
{
  int buttonSelected = checkForPress();
  switch(buttonSelected)
  {
    case 1:
      Value1 += 5;
      break;
    case 2:
      Value1 -= 5;
      break;
    case 3:
      plantType = PlantType(int(plantType)+1);
      if(plantType == END_OF_LIST) plantType = ROSE;  //<<< Edited here
      Serial.print(F("New Plant Setected: "));
      Serial.println(plantString[plantType]);
      break;
  }
  delay(30);  //crude de-bounce
}

int checkForPress()
{
  static byte lastState[3] = {HIGH, HIGH, HIGH}; 
  for(int i = 0; i < 3; i++)
  {
    byte state = digitalRead(buttonPin[i]);
    if (state != lastState[i])
    {
      lastState[i] = state;
      if(state == LOW)
      {
        return i + 1; //edit
      }
    }
  }
  return 0;
}

EDIT fixed equality test

so, a few hours later and a few lines further...

thanks bulldog. your code looks great but I dont get all of it and couldnt get it to work. the "const byte buttonpin" and the case looks neat.

as I lack the knowledge, I tried to go further with johns hints and endet up with the following code.
it works in the way, that I can change from plant1 + but
how can I make it jump back to 0?

I assume that the code is ugly as hell, I'm open for critisism

here the code

int Values [] = {100, 200, 300};
int plantnumber = 0;

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

  pinMode(5, INPUT); //Value +5
  pinMode(6, INPUT); //Value -5
  pinMode(7, INPUT); //change Value
}

void loop() {
  int b1 = digitalRead(5);
  int b2 = digitalRead(6);
  int b3 = digitalRead(7);


  if (HIGH == b1) 
      {Values [plantnumber] += 5;}
  if (HIGH == b2) 
      {Values [plantnumber] -= 5;}
  if (HIGH == b3) 
      {plantnumber += 1;}

  Serial.println(Values[0]);
  Serial.println(Values[1]);
  Serial.println(Values[2]);
  Serial.println(plantnumber);
  Serial.println(" ");
delay(5000);
}

(deleted)

Hi,

Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?

In particular, how you have your switched wired, do you have pull-up or pull-down resistors fitted?

Thanks.. Tom.. :slight_smile:

dont know yet if pull up or pull down. will first have read which is which

if i got the concept right, its a pull - down resistor

thanks, spycatcher2k

one thing left is, that I need to keep pushing the button for a long time. the push onto the button reacts very slowly.
how must I alter the code, to have a push = a change?

delay(5000);

I need to keep pushing the button for a long time. the push onto the button reacts very slowly.

I wonder what could be causing the problem ?

how must I alter the code, to have a push = a change?

You need to look into using millis() for timing as in the BlinkWithoutDelay example and Several things at the same time
Save the time an event happens, such as a button press, then each time through loop() check whether the required period has elapsed since the event. If not then go round loop() again reading inputs etc. If the period has elapsed then take the required action.

Another thing worth considering is acting when the buttons become pressed rather than when the are pressed. Look at the StateChangeDetection example in the IDE to see how to do it.

that means that in my current code, a button must me "pressed" by the end of the loop for the change to happen?

thanks for throwing in concepts, that helps choosing the right path

that means that in my current code, a button must me "pressed" by the end of the loop for the change to happen?

What it means is that you must have nothing in the loop() function or any function that it calls that blocks its free running. Written properly the loop() function will execute thousands of time a second so can easily read buttons pressed by humans that many times per second which will make the response practically instantaneous. Combining this with detecting the change of state of the button input rather than its current state is a very powerful technique and can be used in many circumstances.

ok, did take the previous advices in account, spent several hours working on it.

currently I'm having 2 problems,

  1. From the values that I get, I would assume that the millis() function is working improperly. How do you guys see that?

  2. does it make sense how I print to lcd and serial.monitor? Do I understand the concept right (I tried to put it into place, but probably failed...)

  3. Loop
    sensor reads, value is stored
    value is printed to lcd

  4. loop
    (sensor is off)
    "stored" value is printed to lcd

  5. loop
    (sensor is off)
    "stored" value is printed to lcd

...

  1. loop
    sensor reads, value is updated
    updated value is printed to lcd

Otherwise, I'm open for criticism too, it's my first arduino project and I'm trying to make (as good as I can) sense of the advices I get and the information I find.

i've currently only 1 sensor in place, the goal would be to have several and the goal would also be to trigger solenoid valves for bringing the water to the plants.

thanks a lot for help

  #include "U8glib.h"
  
  int Values [] = {700, 200, 300};
  int plantnumber = 0;

  unsigned long previousMillisS=0;        // millis() returns an unsigned long. SerialMonitor.
  unsigned long previousMillisOn=0;        // millis() returns an unsigned long. SensorOn
  unsigned long previousMillisR=0;        // millis() returns an unsigned long. SensorRead
  unsigned long previousMillisOff=0;        // millis() returns an unsigned long. SensorOff
  const int  ValueP = 5;
  const int  ValueM = 6;
  const int  Plants = 7;
  int buttonPushCounterValue = 0;   
  int buttonPushCounterPlants = 0; 
  int buttonState1 = 0;         // current state of the button
  int buttonState2 = 0;
  int buttonState3 = 0;
  int lastButtonState1 = 0; 
  int lastButtonState2 = 0; 
  int lastButtonState3 = 0; 
  int moistureSensorValue1;    // stores the moisture sensor values
  int time_between_reads = 10000;
  int SensorPower = 2;             //digital 2 for powering mosfet that turns on the soil moisture sensor
  int moistureSensor [] = {0, 0, 0};        //analog 0 = Sensor1
  int  moistureSensorValue [] = { 0, 0, 0};
//  int moistureSensor1 = 0;        //analog 0 = Sensor1
  
  U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);
  
  void setup()
  {
    Serial.begin(9600);
    u8g.setFont(u8g_font_unifont);
    u8g.setColorIndex(1); // Instructs the display to draw with a pixel on. 
  }
  
  void loop()
  {

  buttonPushCounterValue = 0;   
  buttonPushCounterPlants = 0; 
  pinMode(SensorPower,OUTPUT);

  
  checkForPress();
  SensorOn();
  SensorRead();
  SensorOff();
  SerialPrint();
  
  u8g.firstPage();
  do {draw_lcd();}
  while( u8g.nextPage() );
  water();
  
  
  }
  
  int checkForPress()
  {
    buttonState1 = digitalRead(ValueP);
    buttonState2 = digitalRead(ValueM);
    buttonState3 = digitalRead(Plants);
                                                 
    if (buttonState1 !=lastButtonState1) 
      {if (buttonState1 == HIGH) 
        {Values [plantnumber] += 5;}
      delay(50);}
      lastButtonState1 = buttonState1;
    
    if (buttonState2 != lastButtonState2) 
       {if (buttonState2 == HIGH) 
         {Values [plantnumber] -= 5;}
      delay(50); }
    lastButtonState2 = buttonState2;
    
    if (buttonState3 != lastButtonState3)
      {if (buttonState3 == HIGH) 
        {plantnumber++;
        if(plantnumber == 3)
          plantnumber = 0;}
      delay(50);}
    lastButtonState3 = buttonState3;
     }

void SensorOn(){
   
    unsigned long currentMillisOn = millis(); // grab current time
    if ((unsigned long)(currentMillisOn - previousMillisOn) >= time_between_reads) {
          previousMillisOn = millis();
      
          digitalWrite(SensorPower, HIGH);
}}

void SensorRead(){
   
    unsigned long currentMillisR = millis(); // grab current time
    if ((unsigned long)(currentMillisR - previousMillisR) >= (time_between_reads + 500) ) {
          previousMillisR = millis();
      
          moistureSensorValue [0] = analogRead(moistureSensor [0]);
   }}

void SensorOff(){

    unsigned long currentMillisOff = millis(); // grab current time
    if ((unsigned long)(currentMillisOff - previousMillisOff) >= (time_between_reads + 2000)) {
          previousMillisOff = millis();
       
          digitalWrite(SensorPower, LOW);
   }}

  int SerialPrint(){
        unsigned long currentMillisS = millis(); // grab current time
    if ((unsigned long)(currentMillisS - previousMillisS) >= 6000) {
          previousMillisS = millis();
  
//          Serial.println(plantnumber); 
//          Serial.println(Values [plantnumber]);
          Serial.println(moistureSensorValue [0]);
//        Serial.println(moistureSensorValue [1]);
//          Serial.println(" ");
         }

  }


void draw_lcd(){


  u8g.drawStr( 0, 10, "1.");
  u8g.setPrintPos(20, 10);
  u8g.print(moistureSensorValue [0]);
  if(plantnumber == 0)
  u8g.drawStr( 52, 10, "-");
  u8g.setPrintPos(60, 10);
  u8g.print(Values[0]);
  
 // u8g.drawStr( 0, 22, "2.");  
  if(plantnumber == 1)
  u8g.drawStr( 52, 22, "-");
  u8g.setPrintPos(60, 22);
 // u8g.print(Values[1]);

 // u8g.drawStr( 0, 34, "3."); 
  if(plantnumber == 2)
  u8g.drawStr( 52, 34, "-");
  u8g.setPrintPos(60, 34);
 // u8g.print(Values[2]);

 // u8g.drawStr( 0, 46, "4."); 
 // u8g.drawStr( 0, 58, "5.");    
  }


void water(){
/*     int Mosfet1 = 11; //digital 11
     pinMode(Mosfet1,OUTPUT);

       if(moistureSensorValue1 > Values[0])
       {digitalWrite(Mosfet1, HIGH);
       delay(3000);
       digitalWrite(Mosfet1,LOW);
       delay(5000);
       }
       else 
       {digitalWrite(Mosfet1,LOW);}       // keep closed solenid
*/
}
  1. From the values that I get, I would assume that the millis() function is working improperly. How do you guys see that?

I see that as very unlikely. What do you see and where, and what do you expect to see ?

I think that your timing problems stem from the way you do your timing for these:-

SensorOn();
SensorRead();
SensorOff();

You either need to:-
Create one function to turn the sensor on, do a read, then turn it off.
OR
Leave the sensor powered at all times, and just use one timed process for the reads.
OR
Turn the sensor on, then (from that function) start a timed interval until you actually read the sensor, then (from that function) start a timed interval until you turn the sensor off.

Also, you should really call this:-pinMode(SensorPower, OUTPUT);from 'setup()', rather than from 'loop()'. It only needs to happen once, not every iteration of 'loop()'.

Another point is that in lines like this one:-if ((unsigned long)(currentMillisOn - previousMillisOn) >= time_between_reads)you don't need to cast the result of the subtraction to 'unsigned long'. Both of those variables are already 'unsigned long' type. (It won't affect your results, but the cast is superfluous.)
All you really need is:-if (currentMillisOn - previousMillisOn >= time_between_reads)

great, thanks a lot.

I would like to go with version 3:
-turn on
-get a reading
-turn off.

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

Ok, you time it differently then

But why doesnt it work at the moment?

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

Going with your idea, I would leave the SensorOn(); in relation to int time_between_reads = 10000;, right?

But adapt SensorRead() to start a little after SensorOn()
And SensorOff() starting after SensorRead()?
How do I do that?

my idea (and if wrong tell me so), would be:

  • powering on the Arduiono

  • power on the sensor

  • get a reading

  • turn off the sensor (and store the reading till its overwritten by the next reading in 15min)

  • use that reading, to water the plants if needed

  • get a reading every 15min

  • use that reading, to water the plants if needed

thanks for the other 2 inputs, will change the code accordingly.

ps. (I'm currently using 10000, just to get readings more often. The final version should be with 15min)

About the wrong readings

  • the "analogRead(moistureSensor [ 0 ]) " doesnt get an answer every time. I've got several readings ok, and then a few with the value 1023, which would mean that there is a reading but no sensor connected right?

  • the readings are at irregular intervals

the "analogRead(moistureSensor [ 0 ]) " doesnt get an answer every time.

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

I've got several readings ok, and then a few with the value 1023, which would mean that there is a reading but no sensor connected right?

Not necessarily.

I would suggest to power the sensor off immediately after being read.
There is no point in keeping it powered longer than neccessary.

Powering the sensor 20 mS before the measurement should be enough,
but you can experiment with that value.

then a few with the value 1023, which would mean that there is a reading but no sensor connected right?

With no sensor connected then all bets are off as to what the reading will be if you do not have a pullup or pulldown resistor in place. A reading of 1023 would normally indicate that the input pin is at 5V or HIGH (same thing). This could happen if the pin were disconnected and was picking up stray voltages.