Hi everyone!

Hi everyone.

So I’ve spent the last couple of days glued to my computer reading everything I can about the Arduino and it’s uses.

So, I’ve had a project in mind for the last couple of years, and it seems as if the Arduino is the answer to my questions! Now, I have not yet ordered my Arduino Starter Kit, nor do I know a lot about coding or electronics, but I intend to learn a lot and build this thing on my own!

The forum is quite awesome and I can see that there’s a ton of knowledge and people willing to help, and I would like to thank everyone in advance.

So, on to my project :

In essence it’s a machine that dispenses 25ml of liquid at a time.

Stage 1

Basic Components :
1 x Arduino Uno R3 (the brains)
1 x 12v 500mA Solenoid (releases fluid)
1 x Tip120 Transistor (controls solenoid)
2 x 1k Resistor (pulls switches to ground to prevent bouncing)
1 x 1N4004 Diode (to prevent spikes)
1 x Push Button (For input from user)
1 x Push Button (to determine the presence of a glass)

So this is the core of the machine, but I plan a LOT of expansion on it, including IR sensors, more solenoids, LCD touch screen interface and display, 220v mains supply with battery backup, data logging, wifi/BT communication etc etc…

So I’m basically following this tutorial http://www.instructables.com/id/Controlling-solenoids-with-arduino/?ALLSTEPS to build the circuit for the relay and this also helped Arduino Playground - SolenoidTutorial and http://wiki.bildr.org/index.php/Controlling_a_solenoid_(TIP120_Arduino) .

So, onto my code :

/*
Fluid Dispenser v1.0
written by : Theo Gresse
Date : 15/10/2012
*/

int buttonPress = 1;
int glassSensor = 2;
int relayOut = 13;

void setup()                               //setup
{
  pinMode(buttonPress, INPUT);             //buttonPress defined as an input
  pinMode(glassSensor, INPUT);             //glassSensor defined as an input
  pinMode(relayOut, OUTPUT);               //relayOut defined as an output
}

void loop()                                //start of loop
{
  if (digitalRead(buttonPress) == HIGH     //if the signal from the button is HIGH (or pressed)
     &&                                    //and
     (digitalRead(glassSensor) == HIGH))   //if the signal from the glassSensor is HIGH (or pressed)
  {                                        //then
    digitalWrite(relayOut, HIGH);          //activate the relay
    delay(1250);                           //keep relay activated long enough to dispense certain volume
    digitalWrite(relayOut, LOW);           //deactivate the relay
  }      
  else
  {
    digitalWrite(relayOut, LOW);           //no button pressed, no action taken, relay deactivated
  }
}                                          //end loop

Any comments so far? It compiles fine, but as stated, I haven't ordered the Arduino yet, so can't upload it yet :slight_smile:

I would truly appreciate any and all help with my project!

Any comments so far? It compiles fine, but as stated, I haven't ordered the Arduino yet, so can't upload it yet

Personally, I think you are wasting your time writing code for something you don't even have. There will be plenty of time to work out all that is potentially wrong with that code when you have hardware to test with.

For instance, how do you plan to connect the switches?

What is a glassSensor? Why is Pin missing from the name? You are not really reading from a glassSensor. You are reading from a pin that might have a glass sensor, whatever that is, attached to it.

Connecting a switch to pin 1 is not a good idea. You'll learn why when you start trying to debug your code.

Naming the pin buttonPress is also a big mistake.

  if (digitalRead(buttonPress) == HIGH     //if the signal from the button is HIGH (or pressed)

If you wire the switch the easy way, LOW will mean pressed.

If you don't understand why, look at the switch tutorial (misnamed button).

    delay(1250);                           //keep relay activated long enough to dispense certain volume

I wonder if this should be maybe 1248. Or 1251. Magic numbers belong at the top of the code, with #define's names or variable names, so that they can be changed easily when the magic number turns out to be wrong.

It is likely, though, that you'll need some feedback to determine when that amount of fluid has been dispensed, rather than guessing that opening the solenoid for that long is close enough.

Thanks for the blunt reply PaulS, I see now that taking the effort do make a nice introduction (unlike some other people that only leeches of the forum) was a waste of time.

Personally, I think you are wasting your time writing code for something you don't even have. There will be plenty of time to work out all that is potentially wrong with that code when you have hardware to test with.

I don't expect the code will just magically work because it compiles ok. I know the real issues will start arising when the hardware is active. I'm just anxious to get going and thought of using the time while I wait for the solenoid to start doing some work. At least I'm trying :grin:

For instance, how do you plan to connect the switches?
If you wire the switch the easy way, LOW will mean pressed.

Following this : http://www.arduino.cc/en/Tutorial/button button state will be HIGH if pressed. Or am I wrong?

What is a glassSensor? Why is Pin missing from the name? You are not really reading from a glassSensor. You are reading from a pin that might have a glass sensor, whatever that is, attached to it.

It's just a switch that gets depressed once a glass is in place. I changed the code accordingly. Hope it looks better now?

Connecting a switch to pin 1 is not a good idea. You'll learn why when you start trying to debug your code.

Ok, will keep an eye on that one. I like doing things in an orderly fashion, so thought of keeping the inputs to one side of the arduino and outputs to the other.
EDIT --> I see now that pin 1 is used for serial comms. That will stick in the back of my mind from now on XD (Made the neccesary changes).

I wonder if this should be maybe 1248. Or 1251. Magic numbers belong at the top of the code, with #define's names or variable names, so that they can be changed easily when the magic number turns out to be wrong.

That does make sense. I've added and changed to the code. I hope it is ok? The current magic number is only for an illustrative purpose. The precise number will be determined once the hardware is up and running and the flow through the solenoid can be determined.

Thanks for the pointers PaulS, I hope to hear more from you.

Here's the amended code :

/*
Fluid Dispenser v1.0
written by : Theo Gresse
Date : 15/10/2012
*/

const int buttonPressPin = 2;              //refers to the pin where the button is connected for user input
const int glassSensorPin = 3;              //refers to a button that is depressed by the weight of a glass
const int relayOutPin = 13;                //refers to the pin being used to trigger the relay
const int delayTime = 1250;                //constant value assigned to delayTime, used to keep solenoid valve open

void setup()                               //setup
{
  pinMode(buttonPressPin, INPUT);          //buttonPress defined as an input
  pinMode(glassSensorPin, INPUT);          //glassSensor defined as an input
  pinMode(relayOutPin, OUTPUT);            //relayOut defined as an output
}

void loop()                                //start of loop
{
  if (digitalRead(buttonPressPin) == LOW  //if the signal from the button is HIGH (or pressed)
     &&                                    //and
     (digitalRead(glassSensorPin) == LOW))//if the signal from the glassSensor is HIGH (or pressed)
  {                                        //then
    digitalWrite(relayOutPin, HIGH);       //activate the relay
    delay(delayTime);                      //keep relay activated long enough to dispense certain volume
    digitalWrite(relayOutPin, LOW);        //deactivate the relay
  }      
  else
  {
    digitalWrite(relayOutPin, LOW);        //no button pressed, no action taken, relay deactivated
  }
}                                          //end loop

Are you using IDE 1.0.1? Use the internal pullups to hold the switch pins high until the switch closes to pull it low.
Resistor will not help with debounce - that' a mechanical thing.
Once both are pressed, your delay will take care of the debounce.

  pinMode(buttonPressPin, INPUT_PULLUP);          //buttonPress defined as an input
  pinMode(glassSensorPin, INPUT_PULLUP);          //glassSensor defined as an input

or for IDE <= 1.0
  pinMode(buttonPressPin, INPUT);          //buttonPress defined as an input
digitalWrite (buttonPressPin,  HIGH); // enable internal pullup
  pinMode(glassSensorPin, INPUT);          //glassSensor defined as an input
digitalWrite (glassSensorPin,  HIGH); // enable internal pullup

I don't see a current limit resistor on the TIP120 base input

A 2.7K in series so it is similar to the input of a ULN2003 will keep you from burning out the arduino pin driving the base:

Thanks for the blunt reply PaulS

I did say "Personally, I feel...". If you feel otherwise, I have no problem with that. If I offended you, I apologize.

Following this : http://www.arduino.cc/en/Tutorial/button button state will be HIGH if pressed. Or am I wrong?

You are right. But, that requires an external resistor and more wiring. There are internal pullup resistors in the Arduino. Make use of them, and the wiring gets a lot simpler - one wire from the switch to the pin and one wire from the switch to ground. Then, LOW means pressed.

I changed the code accordingly. Hope it looks better now?

I don't like the name, still, but that's OK. As long as the name means something to you, stick with it.

That will stick in the back of my mind from now on

There is a reminder right on the board. 8)

The precise number will be determined once the hardware is up and running and the flow through the solenoid can be determined.

That's fine. If, and it's a big if, the solenoid opens and closes in a consistent amount of time, and that every time the there is the same delay between triggering the solenoid and turning it off, the exact same amount of material is dispensed.

For things like washing machines dispensing soap, a minor (or even significant) variation is acceptable. If you are dispensing medicine, it is not.

    delay(delayTime);                      //keep relay activated long enough to dispense certain volume

If you are, as you hinted, going to be having the Arduino do other things, delay() is not a good idea. Look at the blink without delay example to see how to avoid it.

The name, delayTime, is not particularly good, either. Since the purpose of the delay is to dispense material, I'd use a name like dispenseTime.

Hi CrossRoads

Are you using IDE 1.0.1? Use the internal pullups to hold the switch pins high until the switch closes to pull it low.
Resistor will not help with debounce - that' a mechanical thing.
Once both are pressed, your delay will take care of the debounce.

Yes, I'm using 1.0.1.
Will definitely do some digging on the internal pull ups. I now remember seeing something like that on a tutorial somewhere.

I don't see a current limit resistor on the TIP120 base input

Ok, must have mixed it up when I added the glassSensor switch. So 1 more 1k resistor for the TIP120, check.

A 2.7K in series so it is similar to the input of a ULN2003 will keep you from burning out the arduino pin driving the base:

So the above mentioned resistor should be 2.7k not 1k ?
Would you suggest I use the ULN2003 in place of the TIP120 to drive more solenoids? Or am I misunderstanding this? (I'm trying to keep up here :smiley: )

Thanks for the informative reply!

@ PaulS

Thanks for the reply.

I did say "Personally, I feel...". If you feel otherwise, I have no problem with that. If I offended you, I apologize.

Nevermind... From CrossRoads' reply I see this is standard practice! XD XD XD XD

You are right. But, that requires an external resistor and more wiring. There are internal pullup resistors in the Arduino. Make use of them, and the wiring gets a lot simpler - one wire from the switch to the pin and one wire from the switch to ground. Then, LOW means pressed.

I do remember seeing something like that somewhere. Thanks for the tip! Will definately look into it!

I don't like the name, still, but that's OK. As long as the name means something to you, stick with it.

I suppose as I learn more, so my programming skills will develop and things will change. For now, let's leave it as such just to keep my head in the game :smiley:

There is a reminder right on the board.

... which I don't have yet, but checked out a diagram and saw it on there :blush:

That's fine. If, and it's a big if, the solenoid opens and closes in a consistent amount of time, and that every time the there is the same delay between triggering the solenoid and turning it off, the exact same amount of material is dispensed.

For things like washing machines dispensing soap, a minor (or even significant) variation is acceptable. If you are dispensing medicine, it is not.

If you are, as you hinted, going to be having the Arduino do other things, delay() is not a good idea. Look at the blink without delay example to see how to avoid it.

The name, delayTime, is not particularly good, either. Since the purpose of the delay is to dispense material, I'd use a name like dispenseTime.

Ok, cool. Will do some digging and come back with another option.

Luckily it's not medicine or anything critical, and a small variation will be acceptable XD

Thanks for the feedback PaulS!

@ PaulS

So, I should create a function that handles the delay of the solenoid valve?

If this is correct, does this have any other effect other than "cleaning" up the code?

So, I should create a function that handles the delay of the solenoid valve?

That would depend on when you want to call it, and what it does.

If this is correct, does this have any other effect other than "cleaning" up the code?

Maybe. Maybe not. Depends on the function. Sometimes, moving code to a function makes the code harder to follow, so creating a function is not always desirable. Usually, yes. Always, no.

You are probably better using a logic level Fet, or even a standard n-channel and totem pole driver, as opposed to a Darlington BJT.
A ULN203A for example is an array of 7 Darlington drivers with bias and current limit resistors built in and is designed to operate from a 5v locic signal (TTL)

Having said that your design premise is not great id you want to accurately dispense a specific quantity of liquid.

  1. The pressure and viscosity of the liquid will alter the flow rate even if the solenoid operation is consistent.
    To be accurate you need some form of positive displacement device, even an optic on a bar works this way.
    The metering container fills when the optic is not depressed and then completely emptys when it is.
    You can only ever get 1 shot assuming that the optic was full to start with. There is control feedback in the system, the operator, who visually checks that the optic is full before starting the sequence.
    vending machines use positive displacement pumps running at a known speed or rotating cups and screews to do the same thing, the ones that don't regularly over-fill or under-fill containers or put in the wrong amount of sugar or milk.

  2. If you leave your finger on the button for the whole dispense time then a second dispense cycle will start as soon as the first one finishes.
    To address this you need to look for edges that is a transition from low to high, a positive or rising edge, or high to low, a negative or falling edge.
    When you see an edge, your button is n longer in the state that it was, you act.
    You can do this in the loop, it doesn't have to be an interrupt or anything complex.
    However this brings its own problems that need to be handled ... see 3.

  3. You need to de-bounce the switches ...
    Mechanical switches are almost never clean, they bounce when pressed or released, turning on and off rapidly for a short transitional period between open and closed.
    If the glass was in place and the dispense switch was released after the delay was done you would be very likely to see a high on the digital read long enough to trigger a second cycle.

I appreciate that this is perhaps a little complex for a first dabble into uP programming but they are core concepts that are best learned soon.
If you have an LED that is lit when you press a switch it is perhaps overkill to worry about some imperceptible flashing but for anything else it is pretty much a prerequisite of using a switch.

There are literally thousands of Arduino tutorials on YouTube but this guy starts with connect it and works up, lesson 2 is buttons and de-bouncing.
It is quite watch-able with video of him writing the code and explaining why and how as he go's.

(No connection to me I just thought as getting started vids they were well done, I am sure that there are many other good presentations.)

@ Dyslexicbloke

First off, thanks for the reply.

Now...

You are probably better using a logic level Fet, or even a standard n-channel and totem pole driver, as opposed to a Darlington BJT.

So it's a no to the ULN203A. The TIP120 is a better option? Sorry if this is a stupid question, I'm still new to all of this and struggling to keep up with all the tech jargon.

  1. The pressure and viscosity of the liquid will alter the flow rate even if the solenoid operation is consistent.
    To be accurate you need some form of positive displacement device, even an optic on a bar works this way.
    The metering container fills when the optic is not depressed and then completely emptys when it is.
    You can only ever get 1 shot assuming that the optic was full to start with. There is control feedback in the system, the operator, who visually checks that the optic is full before starting the sequence.
    vending machines use positive displacement pumps running at a known speed or rotating cups and screews to do the same thing, the ones that don't regularly over-fill or under-fill containers or put in the wrong amount of sugar or milk.

It was quite amusing how quickly you caught on to what I was trying to achieve. Well done.
I have seen the systems you describe, but they involve a lot more on the manufacturing side.
I've seen the "optics" that pre-fill before dispensement, and have actually taken one appart,
but like I said, it involves a whole lot more on the fabrication side.

So basically, I will have to build in some sort of double checking system to determine an accurate volume to dispense...

  1. If you leave your finger on the button for the whole dispense time then a second dispense cycle will start as soon as the first one finishes.
    To address this you need to look for edges that is a transition from low to high, a positive or rising edge, or high to low, a negative or falling edge.
    When you see an edge, your button is n longer in the state that it was, you act.
    You can do this in the loop, it doesn't have to be an interrupt or anything complex.
    However this brings its own problems that need to be handled ... see 3.

This is/was an initial concern, but I was hoping that the loop would handle this in some way or another.

Could you maybe elaborate more on this?

  1. You need to de-bounce the switches ...
    Mechanical switches are almost never clean, they bounce when pressed or released, turning on and off rapidly for a short transitional period between open and closed.
    If the glass was in place and the dispense switch was released after the delay was done you would be very likely to see a high on the digital read long enough to trigger a second cycle.

I am busy looking into the debounce situation. this was also brought up by PaulS and CrossRoads. I don't see this to be a big obstacle.

BUT, will debouncing the switches also sort out the situation where the botton is kept in? Or will this need to be approached on the coding side?

The button I intend to use is a "momentary button" if that makes any sense...

Thanks for the great feedback!

Or will this need to be approached on the coding side?

It needs to be handled in code, but it is really simple. Keep track of the previous state of the switch. Compare that to the current state. If they are different, the switch was either just pressed or just released.

The current state of the switch will tell you which transition occurred. You could make the action occur when the switch is released, so it doesn't matter how long the switch is held.

You can make it happen when the switch is just pressed, to, since the transition from released to pressed can't happen until the switch goes back to the released state.

int currState;
int prevState = HIGH;

void loop()
{
   currState = digitalRead(switchPin);
   if(currState != prevState)
   {
      // A transition occurred
      if(currState == LOW)
      {
         // From released to pressed
      }
      else
      {
         // From pressed to released
      }
   }
   prevState = currState;
}

This assumes the use of pullup resistors. If using pulldown resistors, change LOW to HIGH and HIGH to LOW. If not using any resistor, never mind, since you'll never get a valid reading from the switch.

Hi PaulS

Thanks again for the feedback.

First things first. Is this implementation of the pullup code from CrossRoads correct?

/*
Fluid Dispenser v1.0
written by : Theo Gresse
Date : 15/10/2012
*/

const int buttonPressPin = 2;              //refers to the pin where the button is connected for user input
const int glassSensorPin = 3;              //refers to a button that is depressed by the weight of a glass
const int relayOutPin = 13;                //refers to the pin being used to trigger the relay
const int delayTime = 1250;                //constant value assigned to delayTime, used to keep solenoid valve open

void setup()                               //setup
{
  pinMode(buttonPressPin, INPUT_PULLUP);   //buttonPress defined as an input
  pinMode(glassSensorPin, INPUT_PULLUP);   //glassSensor defined as an input
  pinMode(relayOutPin, OUTPUT);            //relayOut defined as an output
  digitalWrite(buttonPressPin, HIGH);      //enabling pull-up resistor on pin 2
  digitalWrite(glassSensorPin, HIGH);      //enabling pull-up resistor o
}

void loop()                                //start of loop
{
  if (digitalRead(buttonPressPin) == LOW   //if the signal from the button is LOW (or pressed)
     &&                                    //and
     (digitalRead(glassSensorPin) == LOW)) //if the signal from the glassSensor is LOW (or pressed)
  {                                        //then
    digitalWrite(relayOutPin, HIGH);       //activate the relay
    delay(delayTime);                      //keep relay activated long enough to dispense certain volume
    digitalWrite(relayOutPin, LOW);        //deactivate the relay
  }      
  else
  {
    digitalWrite(relayOutPin, LOW);        //no button pressed, no action taken, relay deactivated
  }
}                                          //end loop

Is this implementation of the pullup code from CrossRoads correct?

Crossroad's original suggestion was correct.
Your mangling of it is too verbose.
See reply #3, particularly the word "or".

Whether you use an NPN, darlington, array, or MOSFET, is really dependent on the amount of current you want to control.
500mA is too much for a ULN2003. For that much I would suggest a logic level, low Rds, low gate capacitance N-channel MOSFET. Can be found for ~50 cents at digikey.Power dissipation in the MOSFET would then be minimal:
Power = voltage * current, or current^2 * resistance. 0.5A * 0.5A * .05 ohm (50mOhm) = 12.5mW when turned on full. Use a 100 ohm series resistor to the Gate.

You don't need the two digital writer's for the input pins - INPUT_PULLUP implys that.

The last post fom PaulS shows a method of detecting edges, change of state to de-bounce you need to check several time and act when multiple checks are the same or check once and then wait a bit before checking again.

What you want to achieve is to ignore very short pulses and respond to a steady state or a 'confirmed' change of state.

@ AWOL

AWOL:

Is this implementation of the pullup code from CrossRoads correct?

Crossroad's original suggestion was correct.
Your mangling of it is too verbose.
See reply #3, particularly the word "or".

Sorry about that. It was late and I was busy with a tutorial on the pullups on the second part of CrossRoads' suggestion and didn't notice that I already implemented the first suggestion from him.

/*
Fluid Dispenser v1.0
written by : Theo Gresse
Date : 15/10/2012
*/

const int buttonPressPin = 2;              //refers to the pin where the button is connected for user input
const int glassSensorPin = 3;              //refers to a button that is depressed by the weight of a glass
const int relayOutPin = 13;                //refers to the pin being used to trigger the relay
const int delayTime = 1250;                //constant value assigned to delayTime, used to keep solenoid valve open

void setup()                               //setup
{
  pinMode(buttonPressPin, INPUT_PULLUP);   //buttonPress defined as an input & pullup resistor called
  pinMode(glassSensorPin, INPUT_PULLUP);   //glassSensor defined as an input & pullup resistor called
  pinMode(relayOutPin, OUTPUT);            //relayOut defined as an output
}

void loop()                                //start of loop
{
  if (digitalRead(buttonPressPin) == LOW  //if the signal from the button is LOW (or pressed)
     &&                                    //and
     (digitalRead(glassSensorPin) == LOW))//if the signal from the glassSensor is LOW (or pressed)
  {                                        //then
    digitalWrite(relayOutPin, HIGH);       //activate the relay
    delay(delayTime);                      //keep relay activated long enough to dispense certain volume
    digitalWrite(relayOutPin, LOW);        //deactivate the relay
  }      
  else
  {
    digitalWrite(relayOutPin, LOW);        //no button pressed, no action taken, relay deactivated
  }
}                                          //end loop

Rectified! (I hope) :smiley:

@ PaulS

PaulS:

So, I should create a function that handles the delay of the solenoid valve?

That would depend on when you want to call it, and what it does.

If this is correct, does this have any other effect other than "cleaning" up the code?

Maybe. Maybe not. Depends on the function. Sometimes, moving code to a function makes the code harder to follow, so creating a function is not always desirable. Usually, yes. Always, no.

You should really change your name to "Confucious" :grin: :grin:

But I eventually figured out what you want (I think) so will update the code shortly with the code from the blink without delay example.

So in essence, while the delay is running, no other input can be read, as the whole system is on hold during the delay time. SO no processing can happen while the delay is running! Good work PaulS :smiley:

@ CrossRoads

CrossRoads:
Whether you use an NPN, darlington, array, or MOSFET, is really dependent on the amount of current you want to control.
500mA is too much for a ULN2003. For that much I would suggest a logic level, low Rds, low gate capacitance N-channel MOSFET. Can be found for ~50 cents at digikey.Power dissipation in the MOSFET would then be minimal:
Power = voltage * current, or current^2 * resistance. 0.5A * 0.5A * .05 ohm (50mOhm) = 12.5mW when turned on full. Use a 100 ohm series resistor to the Gate.

Please excuse my limited technical background and knowledge.

Is there a specific mosfet you would suggest?

Thanks again for all the replies!

Find one the way I do:
At Digikey.com, search for
n-channel mosfet
in-stock
FETS - single
start filtering:
logic-level gate
package style (thru hole? surface mount?)

Now sort by price
Start scrolling down to single-part prices that are in stock.
Look for a part with low Rds, low gate capacitance.
This is the first one I see has low Rds, low gate capacitance, and low price: