Using a pot to change multiple variables independantly

Hi there,
I am currently working on a project to monitor moisture levels in soil, and turn on a motor when it gets too low. The code below works fine for 2 potentiometers controlling 2 values, but not 2 with 4 values. I am aware that the simplest fix would be to add 2 more pots but I would like to know if it is possible to effectively store, then recall a value.

The circuit is an I2C LCD connected to the Arduino and 2 pots as voltage dividers linked to A0 and A1. A button also changes the “page” or what I hope is the variables the pots control.

Any feedback greatly appreciated!

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd1(0x3f, 20, 4);
LiquidCrystal_I2C lcd2(0x27, 16, 2);

const int buttonPin = 2;

int buttonPushCounter = 1;
int buttonState = 0;
int lastButtonState = 0;

void setup() {
  pinMode(buttonPin, INPUT);
  Serial.begin(9600);
    
  lcd1.init();                               //Initialise the LCD Screen
  lcd2.init();
  lcd1.backlight();                          //Start the LCD Backlight
  lcd2.backlight();
   
  lcd1.clear();
  lcd2.clear();

  lcd2.print("< Simulated Lvl");
  lcd2.setCursor(0, 1);
  lcd2.print("Activation Lvl >");
}

void loop() {
  buttonState = digitalRead(buttonPin);
  
  if (buttonState != lastButtonState) {
    if (buttonState == HIGH) {
      buttonPushCounter++;
    } else {}
    delay (50);
  }

  lastButtonState = buttonState;

  if (buttonPushCounter == 3) {
    buttonPushCounter = 1;
  }
  
  lcd1.setCursor(0, 0);
  lcd1.print("Monitoring Station");
  lcd1.setCursor(18, 3);
  lcd1.print("P");
  lcd1.print(buttonPushCounter);
  
  if (buttonPushCounter == 1)  {
    int minLevelReadA = ((analogRead(A0) / 1020.0) * 100); //Defining the minimum activation level for the motor to turn from the read on pin A0, and using that value as a percentage        

    lcd1.setCursor(0, 1);
    lcd1.write(2);
    lcd1.print("Act Level A:");
    if (minLevelReadA < 100) {                  //If the minimun activation level is less than 10 ->
    lcd1.setCursor(13, 1);                //Move the cursor to column 11, row 2
    lcd1.print("     ");                    //Write "  ", or effectively clear the three characters after the set position
  }
    lcd1.setCursor(13, 1);                   //Move the cursor to column 11, row 2
    lcd1.print(minLevelReadA);                 //Print the minimum activation level value (as a percent, defined above)
    lcd1.print("%");                         //Add "%" to the end of the line

  
    int simLevelA = ((analogRead(A1) / 1015.0) * 100);  //Defining the simulated level of moisture in the soil from the read on pin A1, and using that value as a percentage
    lcd1.setCursor(0, 2);
    lcd1.write(2);
    lcd1.print("Sim Level A:");
    if (simLevelA < 100) {                     //If the simulated level is less than 10 ->
    lcd1.setCursor(13, 2);                //Move the cursor to column 11, row 3
    lcd1.print("    ");                    //Write "  ", or effectively clear the three characters after the set position
  }
    lcd1.setCursor(13, 2);                   //Move the cursor to column 11, row 3
    lcd1.print(simLevelA);                    //Print the simulated level value (as a percent, defined above)
    lcd1.print("%");                         //Add "%" to the end of the line


    const int motorA = 13;
    lcd1.setCursor(0, 3);
    lcd1.write(2);
    lcd1.print("Motor A:");
    if (simLevelA < minLevelReadA) {             //If the simulated value is less than the minimum activation value
      digitalWrite(motorA, HIGH);                       //Turn on the motor
      lcd1.setCursor(9, 3);
      lcd1.print("On ");
      }
    if (simLevelA >= minLevelReadA + 10) {     //If it's below the minimum activation value plus 10 to avoid chattering (hysteresis)
      digitalWrite(motorA, LOW);                        //Turn off the motor
      lcd1.setCursor(9, 3);
      lcd1.print("Off");
    } 
    delay(50);
  }

  if (buttonPushCounter == 2)  {
    int minLevelReadB = ((analogRead(A0) / 1020.0) * 100); //Defining the minimum activation level for the motor to turn from the read on pin A0, and using that value as a percentage        
    
    lcd1.setCursor(0, 1);
    lcd1.write(2);
    lcd1.print("Act Level B:");
    if (minLevelReadB < 100) {                  //If the minimun activation level is less than 10 ->
    lcd1.setCursor(13, 1);                //Move the cursor to column 11, row 2
    lcd1.print("    ");                    //Write "  ", or effectively clear the three characters after the set position
  }
    lcd1.setCursor(13, 1);                   //Move the cursor to column 11, row 2
    lcd1.print(minLevelReadB);                 //Print the minimum activation level value (as a percent, defined above)
    lcd1.print("%");                         //Add "%" to the end of the line

  
    int simLevelB = ((analogRead(A1) / 1015.0) * 100);  //Defining the simulated level of moisture in the soil from the read on pin A1, and using that value as a percentage
    lcd1.setCursor(0, 2);
    lcd1.write(2);
    lcd1.print("Sim Level B:");
    if (simLevelB < 100) {                     //If the simulated level is less than 10 ->
    lcd1.setCursor(13, 2);                //Move the cursor to column 11, row 3
    lcd1.print("    ");                    //Write "  ", or effectively clear the three characters after the set position
  }
    lcd1.setCursor(13, 2);                   //Move the cursor to column 11, row 3
    lcd1.print(simLevelB);                    //Print the simulated level value (as a percent, defined above)
    lcd1.print("%");                         //Add "%" to the end of the line


    const int motorB = 13;
    lcd1.setCursor(0, 3);
    lcd1.write(2);
    lcd1.print("Motor B:");
    if (simLevelB < minLevelReadB) {             //If the simulated value is less than the minimum activation value
      digitalWrite(motorB, HIGH);                       //Turn on the motor
      lcd1.setCursor(9, 3);
      lcd1.print("On ");
      }
    if (simLevelB >= minLevelReadB + 10) {     //If it's below the minimum activation value plus 10 to avoid chattering (hysteresis)
      digitalWrite(motorB, LOW);                        //Turn off the motor
      lcd1.setCursor(9, 3);
      lcd1.print("Off");
    }    
    delay(50);
  }
}

Welcome to the forums. Congratulations on using code tags with your first post!

As for your code, you control motor A or B depending on the buttonPushCounter variable. You will need to extend your code for the states of buttonPushCounter == 3 and buttonPushCounter == 4 if you want to control more valves.

I'm not exactly sure what you mean by "effectively store, then recall a value"

HackerJames: I would like to know if it is possible to effectively store, then recall a value.

Yes. Computers are pretty good at that. It's basically half of the entire point of their existence (the other half being performing computations).

You can even make 1 potentiometer set those 4 values. All you need to change in your code is instead of reading the potentiometer when you need one of those values, you read the storage location (most likely a variable in RAM, though you can also store it in EEPROM).

The major thing that you need to figure out "how should I update those values? What sequence of actions on the UI do I want to use to select a value and update it?" Once you have that design laid out then you start thinking about how to code it. Don't just hack away at your code without a plan.

blh64: As for your code, you control motor A or B depending on the buttonPushCounter variable. You will need to extend your code for the states of buttonPushCounter == 3 and buttonPushCounter == 4 if you want to control more valves.

I'm not exactly sure what you mean by "effectively store, then recall a value"

Yes, I am aware I need to add more variables to control more motors. The problem I have is that I would like set the value of, for example, "minLevelA" using one potentiometer, then store that value before I exit "buttonPushCounter = 1" and switch to setting the value for "minLevelB" with the potentiometer and store that before I exit "buttonPushCount = 2", and repeat this for all values of the counter.

Then, once the buttonPushCounter returns to 1 again, and the pot is either too low or too high for the value that I previously stored, print "DWN" or "UP" accordingly. Once the read of the pot is at the previous value, start to change the value once again in accordance with the read of the pot

Is there any way to do that inside an IF statement?

Jiggy-Ninja: Yes. Computers are pretty good at that. It's basically half of the entire point of their existence (the other half being performing computations).

You can even make 1 potentiometer set those 4 values. All you need to change in your code is instead of reading the potentiometer when you need one of those values, you read the storage location (most likely a variable in RAM, though you can also store it in EEPROM).

The major thing that you need to figure out "how should I update those values? What sequence of actions on the UI do I want to use to select a value and update it?" Once you have that design laid out then you start thinking about how to code it. Don't just hack away at your code without a plan.

That is actually exactly what I need! So instead of reading the values when I need them, store them outside the if statement and call on the values as needed. So now I just need to figure out how to call those values into the statement I need them in?

How would I go about doing that?

Let’s take this step by step

To save the values in an array initialise a buttonPress counter to zero and declare an array with 4 elements

byte buttonCounter = 0;
int storedValues[4];

Move the pot to the required position that is to be saved then press the button
When the button becomes pressed read the pot and save the value in the array at the place currently indicated by the value of buttonPress

storedValues[buttonPress] = analogRead(potPin);

then increment buttonPress
Do this until buttonPress is 4 and you will have saved 4 values

Now you will have an array of 4 values with an element number corresponding to each button press. The elements will be numbered 0 to 3

To access the values you use element number
Let’s say that the array is named storedValues, then the values will be in variables named storedValues[0], storedValues[1], storedValues[2] and storedValues[3]
You can use these values just like any other variable, ie

Serial.println(storedValues[2]);
if (storedValues[1] > threshold)
  {
    //do something
  }

Being in an array you can also do things like this

for (int x = 0; x < 4; x++)
  {
    Serial.println(storedValues[x]);
  }

As a refinement to @UKHeliBob's post you could add a second PB. Use the first, as above, to cycle through all the sensors and display current sensor reading, current setpoint, and pot value.

Code the second PB to trigger storing the pot value to the current sensor's setpoint.

If you don't want to modify the hardware there are button libraries which provide a 'long press' feature. So, for example, a short press to cycle through the sensors, a long press to store setpoints.

HackerJames: Then, once the buttonPushCounter returns to 1 again, and the pot is either too low or too high for the value that I previously stored, print "DWN" or "UP" accordingly. Once the read of the pot is at the previous value, start to change the value once again in accordance with the read of the pot

Is there any way to do that inside an IF statement?

This is a lot more complex than just one if statement. Instead of buttonPushCounter just cycling through 4 states, you are adding a lot more states. Maybe add a new variable called potIsReady. Each button push sets that to false. Then you do the up/dwn trick until you get a valid reading on the pot.

If the button is pushed while the pot is not ready then just advance to the next count without storing the value.

Of course it would be better to use an encoder knob which can rotate forever but if you only have the pot you should not try to change.

Damn guys.

That solves all of my problems in 3 posts as far as I know. I'll try those things out and get back to ye!

Thank you so much! :)

Okay. I have managed to create the array and store the values using 2 pots. The problem I have now is I can’t seem to find a way to do as MorganS suggested and do this:

Maybe add a new variable called potIsReady. Each button push sets that to false. Then you do the up/dwn trick until you get a valid reading on the pot.

I have a variable called potPinRead that takes the analogRead of the potPins and I have an array called potIsReady with 4 values in the array. I’ll paste the relevant code below. I am aware I haven’t added anything to change the state of the array potIsReady, I can’t figure it out

int potIsReady [4];
int storedValues [4];
void loop() {
  buttonState = digitalRead(buttonPin);
  if (buttonState != lastButtonState) {
    if (buttonState == HIGH) {
      buttonPushCounter++;
    } else {}
    delay (50);
  }
  lastButtonState = buttonState;
  if (buttonPushCounter == 6) {
    buttonPushCounter = 1;
  }             //Resets counter back to 1

  int potPinRead = ((analogRead(A0) / 1020.0) * 100);

  if (buttonPushCounter == 1)  {
    if (potIsReady[0] == 1)  {
      lcd2.setCursor(0, 0);
      lcd2.write(2);
      lcd2.print("Act Level A:");
      if (storedValues[0] < 100) {                  //If the minimun activation level is less than 10 ->
        lcd2.setCursor(13, 0);                //Move the cursor to column 11, row 2
        lcd2.print("     ");                    //Write "  ", or effectively clear the three characters after the set position
      }
      lcd2.setCursor(13, 0);
      lcd2.print(storedValues[0]);
      lcd2.print("%");
    } else  {
      if (potPinRead < storedValues[0]) {
        lcd2.setCursor(17, 0);
        lcd2.print("UP ");
      }
      if (potPinRead > storedValues[0]) {
        lcd2.setCursor(17, 0);
        lcd2.print("DWN");
      }
    }
  }

As you can see I left out buttonPushCounter == 2, 3 and 4 and also left out the setup. If possible could you explain it to me as UKHeliBob did above?
Thanks

Okay, I figured it out.

For the future confused people (aka me), once the buttonPushCounter reached 2, inside that if statement I set the potIsReady value for [0.] to 0. Then keep that going till you reach the end of your counter and start again

Thanks for all your help guys!

~ A grateful human