Button Mode Selector

Hi Guys and Girls.

I am pretty new with arduino programming, and was hoping someone could help me with this.

In a nutshell, I am building a garden controller.
There are 2 “modes” that I want to be able to select from using a push button.
Push button once, and it runs ‘mode 1’. Push button again, and it runs ‘mode 2’.
Each mode has an LED indicator to show when that mode is running.

int ledSoil= 1; // Led indicating mode selection = soil
int ledHydro= 2; // Led indicating mode selection = hydro
int button= 3; // Button as mode selector
int pumpPin= 4; // Relay that turns the water pump pn/off
int soil= A5; // Soil moisture sensor - analog
int pumpState = LOW; // initial state = pump OFF
int timer = 200; // The higher the number, the slower the timing.
unsigned long previousMillis = 0; // will store last time relay was updated
long OnTime = 30000UL; // milliseconds of on-time
long OffTime = 3.6e+6; // milliseconds of off-time
int buttonPushCounter = 0; // counter for the number of button presses
int lastButtonState = 0; // previous state of the button
int buttonState =0; //current state of the button

void setup() {
pinMode(soil,INPUT);
pinMode(pumpPin,OUTPUT);
pinMode(button, INPUT);
pinMode(ledHydro, OUTPUT);
pinMode(ledSoil, OUTPUT);

}

void loop() {
buttonState = digitalRead(button);
if (buttonState != lastButtonState) { //check state of button
if(buttonState == HIGH){ //Reads if button was pushed
buttonPushCounter++;
delay(timer);
} else {
delay(timer);
}
lastButtonState = buttonState;

if (buttonPushCounter % 2 == 0) {
soilSetting();
} else {
hydroSetting();
}

}

}

void soilSetting(){ // This runs the soil moisture program
digitalWrite(ledSoil, HIGH); //LED indicating soilSetting turns on
digitalWrite(ledHydro, LOW); //LED indicating hydroSetting turns off
int value=analogRead(soil);
if(value<500){
digitalWrite(pumpPin,HIGH);

}
else digitalWrite(pumpPin,LOW);
delay(timer);
}

void hydroSetting(){ //This runs a timed relay program
digitalWrite(ledSoil, LOW); //LED indicating soilSetting turns off
digitalWrite(ledHydro, HIGH);//LED indicating hydroSetting turns on
unsigned long currentMillis = millis();
if((pumpState == HIGH) && (currentMillis - previousMillis >= OnTime))
{
pumpState = LOW; // Turn it off
previousMillis = currentMillis; // Remember the time
digitalWrite(pumpPin, pumpState); // Update the actual relay
}
else if ((pumpState == LOW) && (currentMillis - previousMillis >= OffTime))
{
pumpState = HIGH; // turn it on
previousMillis = currentMillis; // Remember the time
digitalWrite(pumpPin, pumpState); // Update the actual
}
}

If anyone can tell me where I have gone wrong, or perhaps where I can improve on the code, it would be greatly appreciated.

If anyone can tell me where I have gone wrong,

It would be more helpful of the code were in code tags rather than quote tags.

What happens when you run the program ?

Your code formatting makes me dizzy. Try using control-T in the IDE to auto-format it before posting. There are a lot of useless blank lines, too. You wouldn't have these problems if you had read the sticky posts at the top of the forum.

Sorry, I haven’t posted anything on the forum before. Normally I just read through it and try figure things out.

At a dead end now.

int ledSoil = 1; // Led indicating mode selection = soil
int ledHydro = 2; // Led indicating mode selection = hydro
int button = 3; //  Button as mode selector
int pumpPin = 4; // Relay that turns the water pump pn/off
int soil = A5; //  Soil moisture sensor - analog
int pumpState = LOW; // initial state = pump OFF
int timer = 200;           // The higher the number, the slower the timing.
unsigned long previousMillis = 0; // will store last time relay was updated
long OnTime = 30000UL; // milliseconds of on-time
long OffTime = 3.6e+6;  // milliseconds of off-time
int buttonPushCounter = 0;   // counter for the number of button presses
int lastButtonState = 0;     // previous state of the button
int buttonState  = 0;       //current state of the button

void setup() {
  pinMode(soil, INPUT);
  pinMode(pumpPin, OUTPUT);
  pinMode(button, INPUT);
  pinMode(ledHydro, OUTPUT);
  pinMode(ledSoil, OUTPUT);

}

void loop() {
  buttonState = digitalRead(button);
  if (buttonState != lastButtonState) { //check state of button
    if (buttonState == HIGH) { //Reads if button was pushed
      buttonPushCounter++;
      delay(timer);
    } else {
      delay(timer);
    }
    lastButtonState = buttonState;

    if (buttonPushCounter % 2 == 0) {
      soilSetting();
    } else {
      hydroSetting();
    }

  }

}

void soilSetting() { // This runs the soil moisture program
  digitalWrite(ledSoil, HIGH); //LED indicating soilSetting turns on
  digitalWrite(ledHydro, LOW); //LED indicating hydroSetting turns off
  int value = analogRead(soil);
  if (value < 500) {
    digitalWrite(pumpPin, HIGH);

  }
  else digitalWrite(pumpPin, LOW);
  delay(timer);
}


void hydroSetting() { //This runs a timed relay program
  digitalWrite(ledSoil, LOW); //LED indicating soilSetting turns off
  digitalWrite(ledHydro, HIGH);//LED indicating hydroSetting turns on
  unsigned long currentMillis = millis();
  if ((pumpState == HIGH) && (currentMillis - previousMillis >= OnTime))
  {
    pumpState = LOW;  // Turn it off
    previousMillis = currentMillis;  // Remember the time
    digitalWrite(pumpPin, pumpState);  // Update the actual relay
  }
  else if ((pumpState == LOW) && (currentMillis - previousMillis >= OffTime))
  {
    pumpState = HIGH;  // turn it on
    previousMillis = currentMillis;   // Remember the time
    digitalWrite(pumpPin, pumpState);    // Update the actual
  }
}

Apologies for any dizzying code formats etc, but I am new to Arduino programming and posting on the forum.

I assume that you have your button between 5V and the pin. Did you connect a pull-down resistor between the pin and the GND?

If not, your pin is floating and can have any value at any time. In that case, connect a pull-down resistor (10k will do) from the button pin to GND. If you have the option to change the hardware, connect the button between the pin and GND and use

pinMode(button, INPUT_PULLUP);

instead of

pinMode(button, INPUT);

You need to reverse your logic; when a button is pushed, it will be LOW and not HIGH.

Currently your approach reacts on both press and release. You need to change that by waiting till it goes the other way again (HIGH to LOW for your code, LOW to HIGH in the below code.

The below is a stripped down version that prints on the serial monitor. button on pin 4 using internal pullup and hence LOW equals when the button is pushed.

int button = 4;

int timer = 200;
int buttonPushCounter = 0;   // counter for the number of button presses
int lastButtonState = 1;     // previous state of the button
int buttonState  = 0;       //current state of the button

void setup() {
  Serial.begin(9600);
  pinMode(button, INPUT_PULLUP);

}

void loop() {
  buttonState = digitalRead(button);
  if (buttonState == LOW && buttonState != lastButtonState)
  {
    buttonPushCounter++;
  }
  lastButtonState = buttonState;

    if (buttonPushCounter % 2 == 0)
    {
      Serial.println("Hydro");
    }
    else
    {
      Serial.println("Soil");
    }
}

You have to change your code not to use pins 0 and 1 if you want to use Serial,print / Serial.println.

It's anyway advisable as the Serial Monitor is a valuable debugging tool to see the what is going in your code.

// Note: no debouncing used; up to you to implement.

Thanks sterretje.

I will only be able to test it out in a couple of hours though. Currently at work and do not have the hardware with me.

Really appreciate the help.

It's get mad confusing and I must have changed the code a thousand times. Getting there..hopefully.

Ill post an update once I have made the changes and run the test.

(oh, the button switch is actual a pre-wired/soldered module segment that came with a starter kit.)

Hi Guys

So I made the changes.

It doesn’t seem to change at all.
It keeps the “Soil” loop running, even when the button is pushed, it just keeps running that Function.
Any recommendations to facilitate the change on button push?

int ledSoil = 2; // Led indicating mode selection = soil
int ledHydro = 9; // Led indicating mode selection = hydro
int button = 3; //  Button as mode selector
int pumpPin = 4; // Relay that turns the water pump pn/off
int soil = A5; //  Soil moisture sensor - analog
int pumpState = LOW; // initial state = pump OFF
int timer = 200;           // The higher the number, the slower the timing.
unsigned long previousMillis = 0; // will store last time relay was updated
long OnTime = 30000UL; // milliseconds of on-time
long OffTime = 3.6e+6;  // milliseconds of off-time
int buttonPushCounter = 0;   // counter for the number of button presses
int lastButtonState = 1;     // previous state of the button
int buttonState  = 0;       //current state of the button

void setup() {
  Serial.begin(9600);
  pinMode(soil, INPUT);
  pinMode(pumpPin, OUTPUT);
  pinMode(button, INPUT_PULLUP);
  pinMode(ledHydro, OUTPUT);
  pinMode(ledSoil, OUTPUT);

}

void loop() {
  if (buttonState == LOW && buttonState != lastButtonState)
  {
    buttonPushCounter++;
  }
  lastButtonState = buttonState;

  if (buttonPushCounter % 2 == 0)
  {
    Serial.println("Hydro");
    hydroSetting();
  }
  else
  {
    Serial.println("Soil");
    soilSetting();
  }
}

void soilSetting() { // This runs the soil moisture program
  digitalWrite(ledSoil, HIGH); //LED indicating soilSetting turns on
  digitalWrite(ledHydro, LOW); //LED indicating hydroSetting turns off
  int value = analogRead(soil);
  if (value < 500) {
    digitalWrite(pumpPin, HIGH);

  }
  else digitalWrite(pumpPin, LOW);
  delay(timer);
}


void hydroSetting() { //This runs a timed relay program
  digitalWrite(ledSoil, LOW); //LED indicating soilSetting turns off
  digitalWrite(ledHydro, HIGH);//LED indicating hydroSetting turns on
  unsigned long currentMillis = millis();
  if ((pumpState == HIGH) && (currentMillis - previousMillis >= OnTime))
  {
    pumpState = LOW;  // Turn it off
    previousMillis = currentMillis;  // Remember the time
    digitalWrite(pumpPin, pumpState);  // Update the actual relay
  }
  else if ((pumpState == LOW) && (currentMillis - previousMillis >= OffTime))
  {
    pumpState = HIGH;  // turn it on
    previousMillis = currentMillis;   // Remember the time
    digitalWrite(pumpPin, pumpState);    // Update the actual
  }
}

Urbanjungles: Hi Guys

So I made the changes.

It doesn't seem to change at all. It keeps the "Soil" loop running, even when the button is pushed, it just keeps running that Function. Any recommendations to facilitate the change on button push?

I know it keeps on running; I could see that on my serial monitor :) But you never mentioned what exactly your problem was ;)

If I understand it, you only want to execute the selected function once. If so, keep a variable that is set when a press on the occurs and is reset once the function has been executed. Not able to test the below

int button = 4;

int timer = 200;
int buttonPushCounter = 0;   // counter for the number of button presses
int lastButtonState = 1;     // previous state of the button
int buttonState  = 0;       //current state of the button

void setup() {
  Serial.begin(9600);
  pinMode(button, INPUT_PULLUP);

}

void loop() {
  // keep track of change in requested function
  static bool hasFunctionChanged = false;

  buttonState = digitalRead(button);

  // if button presssed and buttonstate changed
  if (buttonState == LOW && buttonState != lastButtonState)
  {
    buttonPushCounter++;
    // indiate we requested the other function
    hasFunctionChanged = true;
  }
  lastButtonState = buttonState;

  // if requested function has changed
  if (hasFunctionChanged == true)
  {
    if (buttonPushCounter % 2 == 0)
    {
      Serial.println("Hydro");
    }
    else
    {
      Serial.println("Soil");
    }

    // done with our function; indicate that the function has not changed
    hasFunctionChanged == false;
  }
}

You are completely correct. I never actually explained what I wanted to acheive here.

I am building a garden controller (dubbed A.L.F.A), using a adafruit trinket 5v.
I want the controller to have 2 functions, so it can be used in 2 different environments.

Function one(mode 1) - soil based controller, using a soil moisture sensor to control a relay (water pump)
Function two(mode 2) - hydroponic based controller that turns the relay on/off based on a timer.

I want to be able to switch between these 2 functions by pressing the same button.
I want the one controller to be able to be used for soil based systems, or Hydroponic based systems.

Ideally, I want the scenario to be as follows:

System gets powered on
Initial function will be the soil based program. An LED will indicate the system is in soil mode.
If you bush the button, it will change the mode to the hydroponic mode. The soil LED will go off, the hydro LED will come on, and the hydroponic function will now be running (continously until the button is pressed again). If the button is pushed again, the functions switch again.

Soil Mode

void soilSetting() { // This runs the soil moisture program
  digitalWrite(ledSoil, HIGH); //LED indicating soilSetting turns on
  digitalWrite(ledHydro, LOW); //LED indicating hydroSetting turns off
  int value = analogRead(soil);
  if (value < 500) {
    digitalWrite(pumpPin, HIGH);

  }
  else digitalWrite(pumpPin, LOW);
  delay(timer);
}

Hydro Mode

void hydroSetting() { //This runs a timed relay program
  digitalWrite(ledSoil, LOW); //LED indicating soilSetting turns off
  digitalWrite(ledHydro, HIGH);//LED indicating hydroSetting turns on
  unsigned long currentMillis = millis();
  if ((pumpState == HIGH) && (currentMillis - previousMillis >= OnTime))
  {
    pumpState = LOW;  // Turn it off
    previousMillis = currentMillis;  // Remember the time
    digitalWrite(pumpPin, pumpState);  // Update the actual relay
  }
  else if ((pumpState == LOW) && (currentMillis - previousMillis >= OffTime))
  {
    pumpState = HIGH;  // turn it on
    previousMillis = currentMillis;   // Remember the time
    digitalWrite(pumpPin, pumpState);    // Update the actual
  }
}

So basically, I want either of the functions to run, depending on when the button is pressed.
The functions (either soil or hydro modes) must run continuously until the button changes the mode.

I hope this makes sense.

It would be nice to add a third function, that when the button is held in, it overrides/Interrupts the current function (soil or hydro) to put the relay in high state (to flush). When the button is released, it returns to whatever mode it was before the button was held in.

Did you try the sample code in my latest reply to see if it does what it needs to do based on your initial requirement? I usually start with serial print statements to get the basic framework in place and later add the real functionality.

I only checked the reply now, and once again don't have my hardware with me. I will try after work, and then see how to implament it into my sketch.

Would I just replace the

Serial.println("Hydro");

with the

hydroSetting();

in my sketch, if the serial print indicates the button "modes" function is working?

I am using an UNO to test the sketch with the Serial Print function, before I transfer the sketch to the Adafruit Trinket (The Adafruit trinket doesn't support the serial monitor)

I basically am using the UNO to test the sketches, with the serial monitor, and once the code seems to be working, I remove the serial monitoring functions and upload the sketch the trinket.

I'm not sure if leaving the serial monitoring function will actually have any negative effect. If it wont, I would rather leave the serial monitor function in the sketch, even if it doesn't do anything.

Hi Guys

The good news is - I have got the sketch working. I changed the button counter to:

void loop() {
  if (digitalRead(button) == LOW)  // check if button was pressed
  {
    buttonPresses++;                  // increment buttonPresses count
    delay(250);                       // debounce switch
  }
  if (buttonPresses == 2) buttonPresses = 0;     //resets button count to zero on second push    
  if (lastPressCount != buttonPresses)              //reads last button count



    if (buttonPresses == 0)
    {
      ;
      soilSetting();
    }
    else

      hydroSetting();

}

Is there a way to have the relayState LOW when the code changes, but just quickly and not repeating. Currently, the relayState stays HIGH or LOW depending on what the state it was at the time of the change (via the button).

Look, thats not actually the issue. It work well enough, just begins with the hydroSetting's relayState at whatever the last soilSetting's relayState was. Im not that OCD, but it might bug me in the future.

The REAL Issue I have is that the sketch doesn't run on the adafruit trinket 5v. I'm not sure why. I ran it on the UNO and it works fine.

Does anyone know if different coding is required?