[SOLVED] Single pot, three values with array - but cannot get 'live' values.

Hello there good people of Arduino land! :slight_smile:

This is my first post and I would firstly like to say hello to everyone, and wish you all well, and also I hope you're all keeping safe what with the current situation re. that corona thing!
Secondly, I should apologise for my lack of knowledge and my use of incorrect nomenclature.
I've been tinkering with Arduinos for only a short while, so if I appear a complete rookie, then that's because I am! :grinning:

The project:

I wish to build a water drop controller which will dispense three timed drops, each from three independently controlled valves, and trigger a camera to capture the collision event.
I have already built two successful devices, which were much, much simpler:

  • A single valve/double drop controller
  • A double valve/single drop (each) controller

My plan is to use an Arduino Nano as an I2C master since it has 8 analog pins, to gather the necessary timing data.
I will need 6 analog pins to obtain the required timing data:

- pot1 - Flash delay (valve one) / initial delay (valve two and three)
- pot2 - Drop One size (All three valves)
- pot3 - Drop One delay (All three valves)
- pot4 - Drop Two size (All three valves)
- pot5 - Drop Two delay (All three valves)
- pot6 - Drop Three size (All three valves)

(The first entry above - pot1 - will have two operations - for valve one it will be the flash delay for the camera trigger, and for valves two and three it will be a delay before the first drop - since I may wish to drop a single drop from each valve using different coloured water, and therefore will need a pause between the drops)

Pins A4 and A5 will be for the usual SDA and SCL I2C bus to connect to 3 AtMega328P-PU chips as slaves which will control the individual valves with the data sent from the master.
The selected valve will be chosen via a simple button, one corresponding to each valve.

Each valve will have its own 'select' button - which will be marked with a '<' on the lcd.

All the data will be displayed on a single 20X4 lcd screen.
I realise that I will have to keep each time value to under 99 milliseconds in order to get all the data to fit on the 20X4 lcd.
Hopefully it will look something like this:

I will then trigger the event by sending a single pulse from the master to the same digital pin on each of the slaves.

The master (Nano) will then fire the flash at the pre-determined time, by calculating the longest elapsed time (for the complete drop event) from whichever valve that may be.

The problem:

The test circuit I have built to test the potentiometer array readings seems to work, but it only updates the time data when the button is pressed, and of course due to the 'mechanical memory' of the pot, the same value is 'carried' over to the next pot value.

(This may not be a big problem however, and I've thought about rotary encoders, but I would need six of them, and I haven't yet found a reliable way to use them without interrupts, so I'm stuck with pots...which is fine - I will be using precision 10-turn pots for the final build)

It is a very simple circuit, so I didn't post a schematic, but simply put:

  • Arduino Nano clone (Elegoo with UNO bootloader)
  • 3 momentary push buttons (pins D2, D3, D4)
  • single 10K potentiometer (pin A7)
  • 16x2 lcd screen

I have tried to find out how to address this problem by searching the internet and indeed these forums, but I haven't been able to find an answer which I understand!
I know this is probably a very well known issue, but I'm still a beginner!

What I would like is for each value to be able to be adjusted 'live' so to speak, so that I can see what value is being set.
At the moment, I can turn the pot, and then when I press the button, it updates....so I have to guess what the value might be....which is no good clearly!

I plan to simply duplicate the (working) sketch six times, to have the correct number of parameters...which is maybe easier said than done naturally!

Here is the test sketch:

#include <digitalIOPerformance.h> //----Old library which is supposed to enable faster performance.
#include <Wire.h>
#include <LiquidCrystal.h> //----Open LiquidCrystal library.
LiquidCrystal lcd(12, 11, 10, 9, 8, 7); //----Create the lcd instance.

//----Define button and pot pins
#define button1 2
#define button2 3
#define button3 4
#define pot A7

//----Start an array with 3 fields, one for each time value.
int potValues[3]; 

//----set button states to HIGH.
int button1state = HIGH;
int button2state = HIGH;
int button3state = HIGH;

void setup() {
  
//----start serial comms.
  Serial.begin(9600); 

//----set pinModes.
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  pinMode(button3, INPUT_PULLUP);
  pinMode(pot, INPUT);

//----initialize the lcd and print static info.
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(1, 0);
  lcd.print("Val1");
  lcd.setCursor(6, 0);
  lcd.print("Val2");
  lcd.setCursor(11, 0);
  lcd.print("Val3");
}

void loop() {

  button1state = digitalRead(button1); //----Read button1.
  if (button1state == LOW) {           //----if button1 is pressed...
    
//----Read pot and map the first time value to field '0'
    potValues[0] = map(analogRead(pot), 0, 1023, 1, 5); 
    
//----Set lcd to print time value.
    lcd.setCursor(1, 1); 
    lcd.print("   ");
    lcd.setCursor(1, 1);
    lcd.print(potValues[0]);
    delay(10);
    
//----Set lcd position markers, and overwrite the previous ones.
    lcd.setCursor(0, 0); 
    lcd.print(">");
    lcd.setCursor(5, 0);
    lcd.print("<");
    lcd.setCursor(10, 0);
    lcd.print(" ");
    lcd.setCursor(15, 0);
    lcd.print(" ");
  }

//----Duplicate of 'button1' but changed button#, array field and cursor positions etc.

  button2state = digitalRead(button2);
  if (button2state == LOW) {

    potValues[1] = map(analogRead(pot), 0, 1023, 1, 5);

    lcd.setCursor(6, 1);
    lcd.print("   ");
    lcd.setCursor(6, 1);
    lcd.print(potValues[1]);
    delay(10);

    lcd.setCursor(5, 0);
    lcd.print(">");
    lcd.setCursor(10, 0);
    lcd.print("<");
    lcd.setCursor(0, 0);
    lcd.print(" ");
    lcd.setCursor(15, 0);
    lcd.print(" ");
  }

//----Duplicate of 'button1' but changed button#, array field and cursor positions etc.

  button3state = digitalRead(button3);
  if (button3state == LOW) {

    potValues[2] = map(analogRead(pot), 0, 1023, 1, 5);

    lcd.setCursor(12, 1);
    lcd.print("   ");
    lcd.setCursor(12, 1);
    lcd.print(potValues[2]);
    delay(10);

    lcd.setCursor(10, 0);
    lcd.print(">");
    lcd.setCursor(15, 0);
    lcd.print("<");
    lcd.setCursor(0, 0);
    lcd.print(" ");
    lcd.setCursor(5, 0);
    lcd.print(" ");
  }
}

I've been testing some I2C master/slave stuff, so I think I've got a good handle on that when the time comes, but this issue is really holding things up!

I will be eternally grateful if someone would kindly nudge me in the right direction.
And sorry I've rambled on a bit! :-[

Thank you! :slight_smile:

First of All, what you Do is, when Button is Pressed, do all the Things. If nothing is Pressed, just skip everytjing and idle.

loop() {
  button1state = digitalRead(button1); //----Read button1.
  if (button1state == LOW) {           //----if button1 is pressed...
    
//----Read pot and map the first time value to field '0'
    potValues[0] = map(analogRead(pot), 0, 1023, 1, 5); 
}
// now Show everything on LCD at once. 
}

Sorry mate, I am on mobile atm. Cant write more Code but you seem like a clever guy. At least you know how to ask for help properly. I am sure, you will fix that!

Have a great one

  pinMode(pot, INPUT);

Setting the mode of a pin being used as an analog pin is pointless. Analog pins can only be input pins.

Why do you only read the potentiometer pin if a switch is pressed? Reading the value and showing it on the LCD should happen on every pass through loop. USING the value should happen only when the appropriate switch is pressed.

@dr-o

Thanks for the quick response, most appreciated!

That hadn't occurred to me to do that....I guess I was getting hung up on keeping the readings separate.
I'll rewrite it (tomorrow now) and hopefully it'll work.

Cheers mate!

@PaulS

Thanks for the response Paul.
Sorry for the pinMode rookie mistake!

I think what I thought was that I needed to read the pot pin when I pressed the switch, I didn't realise that I could continually read the pot pin, and then act on that when the switch is pressed.

So would I write something like this:

void loop() {

  potValues[0] = map(analogRead(pot), 0, 1023, 1, 5);
  potValues[1] = map(analogRead(pot), 0, 1023, 1, 5);
  potValues[2] = map(analogRead(pot), 0, 1023, 1, 5);  

//----then continue with switch-press conditions....

I appreciate the time taken to reply, Paul....I think I'm chasing the problem down.

So would I write something like this:

You could, but that would be rather pointless. 3 consecutive calls to analogRead() are almost always going to return the same value. Storing three copies of the same value is not really necessary, is it?

All can be done in one line.

if (!button1state) potvalues[0] = map(analogRead(pot), 0, 1023, 1, 5); // if button reads low, read the pot and map it

Leo..

Thanks again PaulS, I really do appreciate your patient responses to my naive questions.

Storing three copies of the same value is not really necessary, is it?

True indeed...makes sense.
I'm still not sure how to implement it, but I am turning in now (2:30am here!)
I'll dive straight in to it again tomorrow, when I'm at the bench...and when my brains are refreshed!!

@Wawa

Thank you Leo, I think I can understand that.

So I can replace this:

  if (button1state == LOW) { 
    potValues[0] = map(analogRead(pot), 0, 1023, 1, 5);
}

with this?

if (!button1state) potvalues[0] = map(analogRead(pot), 0, 1023, 1, 5);

Or in other words (!button1state) is equivalent to button1state == LOW
That's certainly neater and more efficient, thanks.

Thanks to everyone who responded, I'll be back on again tomorrow to bother you all again!! :grinning:
Goodnight all.

bongobreak:
Thanks again PaulS, I really do appreciate your patient responses to my naive questions.

True indeed...makes sense.
I'm still not sure how to implement it, but I am turning in now (2:30am here!)
I'll dive straight in to it again tomorrow, when I'm at the bench...and when my brains are refreshed!!

@Wawa

Thank you Leo, I think I can understand that.

So I can replace this:

  if (button1state == LOW) { 

potValues[0] = map(analogRead(pot), 0, 1023, 1, 5);
}




with this?


if (!button1state) potvalues[0] = map(analogRead(pot), 0, 1023, 1, 5);




Or in other words **(!button1state)** is equivalent to **button1state == LOW**
That's certainly neater and more efficient, thanks.

Thanks to everyone who responded, I'll be back on again tomorrow to bother you all again!! :grinning: 
Goodnight all.

Hey,

! means "not". So a value will logically inverted. Which means, if a Button is LOW (State 0 and therefor false) it gets a true and you get in loop.

I would rather want you not do the suggested thing though. You seem like pretty new to software engineering. And one liner querries is a huge source of mistakes because you forgot that the second line is not included into this. So for the sake of clean code, stay with

if (button1state == LOW) { 
  potValues[0] = map(analogRead(pot), 0, 1023, 1, 5);
}

This is not the source of your original problem though bongobreak. Like already mentioned in this thread, pull out the LCD Part out of button pressed querries and you are good to go. Please focus on this instead of doing fancy programmer stuff.

Have a great one.

Hello dr-o

Thanks for your reply, most appreciated.

I've been fiddling around with moving the lcd stuff out of the button press statements, and I think I'm slowly getting somewhere.

First I tried moving all of the lcd print statements out, but that didn't seem to work - my 'cursor' (position markers) are all showing, and so don't reflect the actual position.

So I moved just the bit relating to potValues [] read section, and now it will update the pot value in the right place...but only while the button is continually pressed.

Here is the loop() code (everything else is unchanged):

void loop() {

  lcd.setCursor(1, 1);
  lcd.print("   ");
  lcd.setCursor(1, 1);
  lcd.print(potValues[0]);
  delay(50);

  lcd.setCursor(6, 1);
  lcd.print("   ");
  lcd.setCursor(6, 1);
  lcd.print(potValues[1]);
  delay(50);

  lcd.setCursor(11, 1);
  lcd.print("   ");
  lcd.setCursor(11, 1);
  lcd.print(potValues[2]);
  delay(50);

  button1state = digitalRead(button1);
  if (button1state == LOW) {

    potValues[0] = map(analogRead(pot), 0, 1023, 1, 5);

    lcd.setCursor(0, 0);
    lcd.print(">");
    lcd.setCursor(5, 0);
    lcd.print("<");
    lcd.setCursor(10, 0);
    lcd.print(" ");
    lcd.setCursor(15, 0);
    lcd.print(" ");
  }

  button2state = digitalRead(button2);
  if (button2state == LOW) {

    potValues[1] = map(analogRead(pot), 0, 1023, 1, 5);

    lcd.setCursor(5, 0);
    lcd.print(">");
    lcd.setCursor(10, 0);
    lcd.print("<");
    lcd.setCursor(0, 0);
    lcd.print(" ");
    lcd.setCursor(15, 0);
    lcd.print(" ");
  }

  button3state = digitalRead(button3);
  if (button3state == LOW) {

    potValues[2] = map(analogRead(pot), 0, 1023, 1, 5);

    lcd.setCursor(10, 0);
    lcd.print(">");
    lcd.setCursor(15, 0);
    lcd.print("<");
    lcd.setCursor(0, 0);
    lcd.print(" ");
    lcd.setCursor(5, 0);
    lcd.print(" ");
  }
}

Progress I think, but I still can't work out how to get the 'live' reading without keeping my finger on the button.
It seems obvious that this should be happening of course now that I read it back to myself...I'm telling Arduino to only do something when I'm pressing a button.
So naturally it stops doing it when I'm not pressing the button!

I've just tried to implement an LED toggle to show which field is active, and perhaps use that as a way to get it to display a live reading, but I got myself into a bit of twist!

You seem like pretty new to software engineering.

How'd you guess?! :grinning:

Nevertheless, I'm grateful for the helpful nudges dr-o....I think I'm getting somewhere!

Suppose that someone asks you what the temperature and humidity are, at irregular intervals. When the person asks, you could say:

  1. Hang on, let me run outside and check...

  2. The temperature is x degrees and the humidity is y percent.

The second response certainly looks more intelligent. It, of course, requires that you check the humidity and temperature regularly, even though the person will only ask irregularly.

On the Arduino, "regularly" means on every pass through loop().

You are NOT reading the pot ON EVERY PASS THROUGH LOOP, despite being told several times that you need to. Why not?

Thanks for the reply PaulS.

I can sense your frustration, and for that I apologise.
I assure you I'm not being thick on purpose, I am trying to get a handle on this...I'm very inexperienced, but I am keen to learn.

So, I understand now that I should read the pot on every loop() pass.

Should I have a separate variable for the pot read only, right at the top of the loop()?

Perhaps like this?

void loop() {

int potVal = map(analogRead(pot), 0, 1023, 1, 5);

...

...and then take whatever 'potVal' is at any given time, and put it into the array?

Not sure how to do this!

Would I then write this within the 'button press' if statement:

  button1state = digitalRead(button1);
  if (button1state == LOW) {

potValues[0] = potVal

....

This makes sense, if it's what you mean.

Thanks again.

OK, I seem to have stumbled upon a kind of 'workaround'.

You're probably not going to like it though PaulS :frowning:

I have placed the pot read right at the top of the loop() as you suggest, and now when I press the button to vary a particular field, I simply 'hold' the button LOW with 'digitalWrite' statements within the button if statements, so that the value updates live.

Here is the main loop - everything else is unchanged:

void loop() {

  int potVal = map(analogRead(pot), 0, 1023, 1, 5);

  lcd.setCursor(1, 1);
  lcd.print("   ");
  lcd.setCursor(1, 1);
  lcd.print(potValues[0]);
  delay(50);

  lcd.setCursor(6, 1);
  lcd.print("   ");
  lcd.setCursor(6, 1);
  lcd.print(potValues[1]);
  delay(50);

  lcd.setCursor(11, 1);
  lcd.print("   ");
  lcd.setCursor(11, 1);
  lcd.print(potValues[2]);
  delay(50);

  button1state = digitalRead(button1);
  if (button1state == LOW) {

    digitalWrite(button1, LOW);
    digitalWrite(button2, HIGH);
    digitalWrite(button3, HIGH);

    potValues[0] = potVal;

    lcd.setCursor(0, 0);
    lcd.print(">");
    lcd.setCursor(5, 0);
    lcd.print("<");
    lcd.setCursor(10, 0);
    lcd.print(" ");
    lcd.setCursor(15, 0);
    lcd.print(" ");
  }

  button2state = digitalRead(button2);
  if (button2state == LOW) {

    digitalWrite(button2, LOW);
    digitalWrite(button1, HIGH);
    digitalWrite(button3, HIGH);

    potValues[1] = potVal;

    lcd.setCursor(5, 0);
    lcd.print(">");
    lcd.setCursor(10, 0);
    lcd.print("<");
    lcd.setCursor(0, 0);
    lcd.print(" ");
    lcd.setCursor(15, 0);
    lcd.print(" ");
  }

  button3state = digitalRead(button3);
  if (button3state == LOW) {

    digitalWrite(button3, LOW);
    digitalWrite(button2, HIGH);
    digitalWrite(button1, HIGH);

    potValues[2] = potVal;

    lcd.setCursor(10, 0);
    lcd.print(">");
    lcd.setCursor(15, 0);
    lcd.print("<");
    lcd.setCursor(0, 0);
    lcd.print(" ");
    lcd.setCursor(5, 0);
    lcd.print(" ");

  }
}

I realise that this looks cumbersome, and I'm certain it's not pretty for experienced programmers to look at, but it works.
(Although I still have the old potentiometer 'mechanical-memory' problem....which is mildly irritating....hopefully I'll solve that issue soon though)

I'm trying to learn, and sorry to those to whom it is easier...it must be tedious to 'baby-step' us thickos through this!! :grinning:

Should I have a separate variable for the pot read only, right at the top of the loop()?

Yes.

...and then take whatever 'potVal' is at any given time, and put it into the array?

Exactly.

Not sure how to do this!

But then you wrote code to do just that (except for the missing semicolon). So, now I'm the one that's confused.

This makes sense, if it's what you mean.

It is exactly where I expected you to get to. I sort of expected you to get there a bit sooner, but slow is fine.

You're probably not going to like it though PaulS

Why not? It's what I expected you to do.

Thanks PaulS!

...(except for the missing semicolon).

Oops...sorry about that!

So, now I'm the one that's confused.

You don't seem like the type of chap that is easily confused ...sorry it had to be me that caused it! :-[

...but slow is fine.

That is indeed fortunate...at present I only seem to have one speed!

So can I say that you have 'signed-off' on my code so far?

I know I have a long way to go, but I really think I have got somewhere today!

Thanks again!

And thank you dr-o too!

Glad you solved your problem mate! Not to be rude against others but you have potential and commitment to finish your project. Not everyone in this forum has that!

dr-o:
Glad you solved your problem mate! Not to be rude against others but you have potential and commitment to finish your project. Not everyone in this forum has that!

Thanks dr-o, I really appreciate the time taken to nudge me in the right direction!

However, I am now getting a curious issue when I power it from an external source - in this case I've tried using both a 9V battery and my bench power supply (set to 9VDC) - both wired to Vin on the Nano.

It works flawlessly when powered through the USB, but when I power it from the power supply or the battery, the first value (relating to button1 in the sketch) is very intermittent...and sometimes will not update at all.

Even more curiously, it works fine when I hold the button down.

This only happens with 'button1'....the other buttons work well whether powered by the power supply, battery or USB.

I have checked (and checked again) for continuity all the breadboard connections/grounds etc., and they seem fine.

I've been through the code too, and the 'button' statements are mostly duplicated, so I can't see where the problem is.

I hope this is not a breach in protocol in posting this here, although I suspect it's a hardware issue, since the code works well when powered with USB.

I can understand that this problem will be difficult for others to reproduce without building the circuit.

It seems almost like 'button1' is floating and isn't registering LOW all the time, and so only intermittently updates the value.

I have scoped the button pin (with my laughable, beginners DSO Shell single trace scope!), and the pin does indeed stay LOW, so I'm not sure what's going on there.

Any ideas?

Edit

I should also mention that I have not debounced any switches yet. When I come to the final build, I will be using Schmitt triggers....this may have something to do with me not knowing how to perform software debouncing!!

Here are some scope pics of the button1 pin.

First, is after pressing the button, and releasing - whilst powered through the USB cable.

That looks pretty solid to me...a nice LOW reading with no ripple.

Here is the same scenario, but powered with a 9V battery...

Why is that horrible ripple there?
It's a DC battery!
That looks like nearly 1V ripple there...no wonder it's malfunctioning!

Here is the same setup (9V battery) but with me holding the button down...
(apologies for the blurry picture...I was holding my camera with one hand whilst taking the image)

That looks pretty clean to me too.

What could be happening here?

I can only assume it's got something to do with the 'digitalWrite' parts where I'm attempting to hold the pins LOW, but it only seems to be a 'clean' LOW when powered via the USB cable, or when I physically hold the switch down.

By the way, I get the same results on the other pins too when powered by the 9V battery, but for some reason, they seem to work fine, despite the ripple on LOW.

Is it something to do with the pins I have chosen?

Can't see that being it...but I know very little...as we've established! :smiley:

Hope someone can shed some light on this perplexing (to me) problem.

Thanks everyone.

This is a more electrotechnical question than software dev kind. But I will still try to reflect my own experiences, maybe it helps.

USB Input has a built in 5V stabilizer. So does the External Input on Mega and Uno. Vin pins don't have this. So if you don't build an external voltage stabilizer, best case is stutter, worst case is you burn your CPU. How do you plug your arduino to your 9v Battery?

This can be the reason of your issue, I am not sure though... It requires at least 0.7V to misread the LOW, since this is the passby voltage for a transistor.

I was just about to post when your message dropped in dr-o!

Again, sorry if this is not the right place for this.

I am wiring it straight into the Vin pin, and then everything else is powered via the 5V pin on the power rails of the breadboard.

I thought this would be fine since it can be powered with up to 12VDC on the Vin...or am I doing something horrendous?

I've been doing this all the time so far, but I won't continue if there's a risk I might blow something....thanks for the heads up.

I plan to use a 12VDC power supply (my valves are 12VDC) and build a 5VDC power supply using the ubiquitous 7805, to power all the electronics.

Would this be reasonable?

By the way, I've just rebuilt the complete circuit on a brand new breadboard, and guess what....it works perfectly now!!
Even on the battery!

Who'd have thunk it eh?

A dodgy breadboard?!

What are the odds?! :smiley:

Ahh well, I've had my mini-success today, time for me to cobble together a rudimentary, noodle based snack!!

Thanks again dr-o, it's reassuring to have you cast your expert eye over my disasters efforts! :smiley: