Valves connected to Arduino digital pins not working. Please help.

Hello all,

I'm working on a project that requires me to control 8 valves and 4 IR sensors, and send TTLs to the computer. I have posted the links for the valves and IR sensors at the bottom of this post. The main objective of this project is to deliver air and liquid through the valves at specific times, and measure a response from animals by measuring proximity to the IR sensors. 4 of the 8 valves will deliver air, while the other 4 will deliver the liquid. All 4 that deliver air need to be in sync with each other, and all 4 that need to deliver liquid also need to be in sync with each other. The way that I have currently set this system up is that the 4 odor valves are connected to one digital pin (pin 2), the 4 liquid valves are connected to another digital pin (pin 3), and the IR sensors are connected to analog pins A1-A4. Each valve is connected to a 12V 23A external battery supply (link to the battery is also at bottom of the post). The valves are connected to the pins via a transistor (PN2222A, technical details attached.)

The problem with this is that the valves are not working as I need them to. I have set pins 2 and 3 to HIGH when I need them to be on, and LOW when they need to be off. Only some (could be 1,2 or 3 on any given day) of the valves in each group switch on and off correctly. I have tested all the valves many times, and I know that they work when I connect them to the 12V battery supply directly.

All the connections are through a protoboard mounted on the Arduino, and I have checked connectivity through my soldering with a multimeter. I am quite confident that the connections are secure. What other reason could there be for the valves to not work? Is there a limit to how many valves a single pin can switch on/off?

I have attached the circuit diagram for each valve, and the technical details of the valves (operating voltage, etc). The Arduino board that I'm using is the Arduino Mega 2560. Please let me know if I've missed anything and you need more information. The code I am using is as follows:

// This code is to carry out odor training 1 in upto 4 behavioral set-ups.

#include <RBD_Timer.h> // RBD Timer is the package this code is using to set timed events. Credit: https://github.com/alextaujenis/RBD_Timer
RBD::Timer timer;
RBD::Timer dummytimer;

// Lickometers
const int lickometerPin1 = A1; float sensorValue1 = 0;
const int lickometerPin2 = A2; float sensorValue2 = 0;
const int lickometerPin3 = A3; float sensorValue3 = 0;
const int lickometerPin4 = A4; float sensorValue4 = 0;

// Valves 
const int valveOdor = 2; 
const int valveReward = 3;
const int valveDummy = 4;

// TTLs for Cheetah system
const int odorTTL = 28;
const int rewardTTL = 30;
const int lick1TTL = 32;
const int lick2TTL = 36;
const int lick3TTL = 34;
const int lick4TTL = 38;
const int dummyTTL = 40;

// Event-marker variables for CoolTerm/Bonsai/Arduino SDE serial plotter
int odor = 0;
int reward = 0;

// To have variable inter-trial length, we will use random numbers from 10-21. This is an initialization.
long randomNumber1 = 10;

// Setup for time variables
unsigned long delayInterval = 6000;
unsigned long timerValue;
unsigned long dummyTimeValue;



void setup() 
{
  Serial.begin(9600); // For continuous event display/saving for CoolTerm/Bonsai/Arduino SDE serial plotter

  timer.setTimeout(3400); // Set trial length 
  timer.restart(); // Timer resets after trial length

  dummytimer.setTimeout(4000); // Set dummy valve frequency 
  dummytimer.restart();

  pinMode(valveOdor, OUTPUT); 
  pinMode(valveReward, OUTPUT); 
  pinMode(valveDummy, OUTPUT); 
  pinMode(odorTTL, OUTPUT); 
  pinMode(rewardTTL, OUTPUT);
  pinMode(lick1TTL, OUTPUT);
  pinMode(lick2TTL, OUTPUT);
  pinMode(lick3TTL, OUTPUT);
  pinMode(lick4TTL, OUTPUT);
  pinMode(dummyTTL, OUTPUT);
  
  randomSeed(analogRead(A6)); // Seed for random number 1
  

}

void loop() 
{
  sensorValue1 = analogRead(lickometerPin1); 
  sensorValue2 = analogRead(lickometerPin2);
  sensorValue3 = analogRead(lickometerPin3); 
  sensorValue4 = analogRead(lickometerPin4);
  
  Serial.print(odor);
  Serial.print(",");
  Serial.print(reward);
  Serial.print(",");
  Serial.print(sensorValue1);
  Serial.print(",");
  Serial.print(sensorValue2);
  Serial.print(",");
  Serial.print(sensorValue3);
  Serial.print(",");
  Serial.println(sensorValue4);

  sendLickData(60, sensorValue1, lick1TTL); 
  sendLickData(60, sensorValue2, lick2TTL); 
  sendLickData(60, sensorValue3, lick3TTL); 
  sendLickData(60, sensorValue4, lick4TTL); 

  
  dummyTimeValue = dummytimer.getValue();
  if (dummyTimeValue < 400) {
    digitalWrite(valveDummy, HIGH); digitalWrite(dummyTTL, HIGH);
  }
  else if (dummyTimeValue >= 400 && dummyTimeValue <= 700) {
    digitalWrite(valveDummy, LOW); digitalWrite(dummyTTL, LOW);
  }
  else if (dummyTimeValue >= 3500 && dummyTimeValue <= 4000) {
    dummytimer.restart();
  }
  

  timerValue = timer.getValue();

  if (timerValue <= delayInterval) {
    odor = 0; digitalWrite(valveOdor, LOW); digitalWrite(odorTTL, LOW);
    reward = 0; digitalWrite(valveReward, LOW); digitalWrite(rewardTTL, LOW);   
  }
  
  else if (timerValue > delayInterval && timerValue <= delayInterval + 6000) {
    odor = 1; digitalWrite(valveOdor, HIGH); digitalWrite(odorTTL, HIGH);
    reward = 0; digitalWrite(valveReward, LOW); digitalWrite(rewardTTL, LOW);   
  }

  else if (timerValue > delayInterval + 6000 && timerValue <= delayInterval + 6000 + 400) {
    odor = 1; digitalWrite(valveOdor, HIGH); digitalWrite(odorTTL, HIGH);
    reward = 1; digitalWrite(valveReward, HIGH); digitalWrite(rewardTTL, HIGH);   
  }

  if (timer.onRestart()) {
    randomNumber1 = random(10, 21);
    delayInterval = randomNumber1 * 1000;
    timer.setTimeout(delayInterval + 6000 + 400);
    odor = 0; digitalWrite(valveOdor, LOW); digitalWrite(odorTTL, LOW);
    reward = 0; digitalWrite(valveReward, LOW); digitalWrite(rewardTTL, LOW);   
  }

}


void sendLickData (int threshold, int sensorValue, const int lickTTL) 
{
  if (sensorValue > threshold) 
  {
    digitalWrite(lickTTL, HIGH);
  }
  else 
  {
    digitalWrite(lickTTL, LOW);
  }
}

Links:
Battery supply
IR sensors

[Note: I thought that the IR sensors and TTLs are most likely unrelated to the problem, so I have not given more details about those. I would be happy to share details if you think it is relevant.]

Circuit diagram.png

Circuit diagram.png

Valve manual.pdf (98.1 KB)

PN2222-D transistor.PDF (198 KB)

Try putting a diode across the valve coils.
When the transistor turns off, the solenoid in the valve keeps generating current, which can cause a voltage spike when it hits the off-resistance of the transistor. The diode lets the current dissipate back on the power supply coils of the valve.

10K is pretty high for the base resistor. The base resistor should be sized to provide about 10mA base current (100mA collector current / 10). Be careful to not exceed the max current for your Arduino output with all those in parallel.

What Arduino are you using?

Hi,
What is your 12V power supply?
Do you have a DMM?

Try 1K for the transistor base resistor.

Have you got some code that JUST operates the solenoids, NOTHING else in the code.
Lets get your valves working correctly before the other stuff.

Tom... :slight_smile:

As groundFungus suggests, the Hfe of the PN2222A when you need a collector current of 100mA is going to be around 10. This means you need a base current of 10mA. With a 5V pin voltage and a Vbe of 0.7, you'd need ~430-ohms for a base resistor.

CrossRoads:
Try putting a diode across the valve coils.
When the transistor turns off, the solenoid in the valve keeps generating current, which can cause a voltage spike when it hits the off-resistance of the transistor. The diode lets the current dissipate back on the power supply coils of the valve.

That makes sense! Do you have a recommendation for which diode I could use in this case?

groundFungus:
What Arduino are you using?

I am using an Arduino Mega 2560.

TomGeorge:
Hi,
What is your 12V power supply?
Do you have a DMM?
Have you got some code that JUST operates the solenoids, NOTHING else in the code.

I’m using 12V 23A alkaline batteries (link: 12V batteries). I do have a DMM (digital multimeter, right?). I have been using the following code to test the valves alone:

const int transistorPin = 2; // Change to 2 for air, 3 for liquid

 
 void setup() {
   // set  the transistor pin as output:
   pinMode(transistorPin, OUTPUT);
}
 
 void loop() {
   digitalWrite(transistorPin, LOW);
   delay(1000);
   digitalWrite(transistorPin, HIGH);
   delay(1000);

 }

All 4 valves in the group still don’t work whenever I use this code to test them.

Blackfin:
As groundFungus suggests, the Hfe of the PN2222A when you need a collector current of 100mA is going to be around 10. This means you need a base current of 10mA. With a 5V pin voltage and a Vbe of 0.7, you’d need ~430-ohms for a base resistor.

Thank you! I will try this. Will post an update on how it goes on this thread.
Thank you all for your responses so far, I really appreciate it.

I would use a 330 Ohm base resistor with a 5V Mega.

The recommended maximum current from a Mega output is 20mA. If you set the base current at 10mA, only 2 valves can be controlled with 1 output.

Given that the Mega has a bazillion I/Os why connect four NPNs to a single GPIO? Why not just use one pin per?

If you need precise switching you can arrange the I/Os so they all fall on the same port and then use direct port access.

groundFungus:
I would use a 330 Ohm base resistor with a 5V Mega.

The recommended maximum current from a Mega output is 20mA. If you set the base current at 10mA, only 2 valves can be controlled with 1 output.

I see, thanks.

Blackfin:
If you need precise switching you can arrange the I/Os so they all fall on the same port and then use direct port access.

Thank you. Could you point me to instructions on how to do this?
Does anybody have any suggestions on how I might still be able to squeeze in 4 valves to work from a single pin? I've already soldered my board, and I have a time-crunch for the project. It looks to me like there might not be a way, but I thought I would try my luck and ask anyway. :confused:

arduinobaswas:
I see, thanks.
Thank you. Could you point me to instructions on how to do this?

As a "for example":

On the Mega2560 port C is brought out to pins 30 through 37:

Suppose you had your 4 valve NPN transistors connected to the lowest four bits of port C (that is, to pins 37, 36, 35 and 34).

These pins correspond to PC0, PC1, PC2 and PC3 respectively.

You can directly access the direction and data registers like this:

void setup( void )
{
    .
    .
    .
    //       76543210
    DDRC = 0b00001111;  //data direction register; a '1' means that pin is an output, a '0' is input
                        //so here port C 0, 1, 2 and 3 are set to outputs

    PORTC = 0b00000000; //initialize the outputs "low"
    
    .
    .
    .
    
}//setup

void loop( void )
{
    .
    .
    .
    //turn on all valves at once
    //  we use a bit-wise OR (the '|') to set all relay bits (PC3..0) high to turn on the valves
    //  so for 0b00001111
    //           ||||++++---- all these bits are set to '1'
    //           ++++-------- all these bits are left as is
    PORTC |= 0b00001111;

    .
    .
    .
    //turn off all valves at once
    //  we use a bit-wise AND (the '&') to clear all bits where a '0' is in the mask
    //  so for 0b11110000
    //           ||||++++---- all these bits are turned off
    //           ++++-------- all these bits are left as is    
    PORTC &= 0b11110000;
    .
    .
    .
    
}//loop

Does anybody have any suggestions on how I might still be able to squeeze in 4 valves to work from a single pin? I've already soldered my board, and I have a time-crunch for the project. It looks to me like there might not be a way, but I thought I would try my luck and ask anyway. :confused:

If you go with an N-ch MOSFET you (essentially) remove the need for the MCU to supply current. Instead, they rely on a voltage between the gate and the source. (I say essentially because if you want to switch a MOSFET on and off rapidly they do have not-insignificant current requirements to move charge around...)

Anyway, take this one as an example:

https://www.digikey.ca/en/products/detail/infineon-technologies/IRL520NPBF/811770?s=N4IgTCBcDaIJICUAyBWMAGAcgBQEIDEBaTAERAF0BfIA

This is a logic-level MOSFET meaning it doesn't need 10-15V between the gate and source to conduct like standard MOSFETs. They are, literally, designed to work with logic like the MCU. Connect the gate to your MCU pin. Connect the source to ground and tie all the valve "low-sides" together to the drain of the MOSFET.

Make sure you have snubbing diodes across the valve solenoids.

Hi,
Does your test code in post#5 for the valves work?
If not keep using this code and;

Use your DMM to measure

  • The voltage at the output pins of the controller.
  • The voltage at the base of the transistor.
  • Then the voltage at the collector of the transistor.

Is your PN2222A oriented correctly so your connections to E B and C are correct?

Thanks.. Tom.. :slight_smile:

And check your battery voltage. It might drop more with more valves turned-on.

DVDdoug:
And check your battery voltage. It might drop more with more valves turned-on.

What you expect from a "12volt 23A" battery.
Leo..

It has a capacity of around 55 mAh

Blackfin:
As a "for example":

void setup( void )

.
   //turn on all valves at once
   //  we use a bit-wise OR (the '|') to set all relay bits (PC3..0) high to turn on the valves
   //  so for 0b00001111
   //           ||||++++---- all these bits are set to '1'
   //           ++++-------- all these bits are left as is
   PORTC |= 0b00001111;

//turn off all valves at once
   //  we use a bit-wise AND (the '&') to clear all bits where a '0' is in the mask
   //  so for 0b11110000
   //           ||||++++---- all these bits are turned off
   //           ++++-------- all these bits are left as is    
   PORTC &= 0b11110000;
   .
   
}//loop

In the second part of this you have the first 4 bits as "1" and say they are left as is but they were "0" in the first statement.

windoze_killa:
In the second part of this you have the first 4 bits as "1" and say they are left as is but they were "0" in the first statement.

In the 2nd statement, the bitwise operator is '&'. ANDing any bit with '1' results in that bit remaining unchanged:

1 & 1 = 1
1 & 0 = 0

The 1st statement uses the '|' operator, indicating bitwise OR:

1 | 1 = 1
0 | 1 = 1

The operation "&=" means the port is read, the value ANDed with the mask and the value written back to the PORT. Similarly, "|=" is a read/OR/write operation.

If PORTC was 0b10100000 and then ORed with 0b00001111 the result is 0b10101111.

If PORTC was 0b10101111 and then ANDed with 0b11110000 the result is 0b10100000.

The goal of this is to affect only the lower 4 bits of the port.

Did I make a typo in my post? If so, apologies and can you point it out?

Sorry. Been a very long time since I played with logical operators. I should have known that.

groundFungus:
I would use a 330 Ohm base resistor with a 5V Mega.

The recommended maximum current from a Mega output is 20mA. If you set the base current at 10mA, only 2 valves can be controlled with 1 output.

Hi all! I just wanted to let you know that this did the trick! I have now connected two valves to each pin, and they're working well. I ran my code for extended periods of time (30 mins, 1 hour) to see if there would be an appreciable difference in time between 1 pair of valves switching on and the 2nd pair per group of 4 - and this is what I found: In the first 10 minutes or so, the difference in time between the two digital pins switching on was ~300 microseconds. This added up to ~900 microseconds by 30 mins, and at the end of 1 hour the difference was ~1200 microseconds. This is acceptable for my project. In case anyone is interested to have the actual values, please let me know.

Blackfin:
As a "for example":

On the Mega2560 port C is brought out to pins 30 through 37:

Suppose you had your 4 valve NPN transistors connected to the lowest four bits of port C (that is, to pins 37, 36, 35 and 34).

These pins correspond to PC0, PC1, PC2 and PC3 respectively.

You can directly access the direction and data registers like this:

void setup( void )

{
.
.
.
// 76543210
DDRC = 0b00001111; //data direction register; a '1' means that pin is an output, a '0' is input
//so here port C 0, 1, 2 and 3 are set to outputs

PORTC = 0b00000000; //initialize the outputs "low"

.
.
.

}//setup

void loop( void )
{
.
.
.
//turn on all valves at once
// we use a bit-wise OR (the '|') to set all relay bits (PC3..0) high to turn on the valves
// so for 0b00001111
// ||||++++---- all these bits are set to '1'
// ++++-------- all these bits are left as is
PORTC |= 0b00001111;

.
.
.
//turn off all valves at once
//  we use a bit-wise AND (the '&') to clear all bits where a '0' is in the mask
//  so for 0b11110000
//           ||||++++---- all these bits are turned off
//           ++++-------- all these bits are left as is    
PORTC &= 0b11110000;
.
.
.

}//loop






If you go with an N-ch MOSFET you (essentially) remove the need for the MCU to supply current. Instead, they rely on a voltage between the gate and the source. (I say essentially because if you want to switch a MOSFET on and off rapidly they do have not-insignificant current requirements to move charge around...)

Anyway, take this one as an example:

https://www.digikey.ca/en/products/detail/infineon-technologies/IRL520NPBF/811770?s=N4IgTCBcDaIJICUAyBWMAGAcgBQEIDEBaTAERAF0BfIA

This is a logic-level MOSFET meaning it doesn't need 10-15V between the gate and source to conduct like standard MOSFETs. They are, literally, designed to work with logic like the MCU. Connect the gate to your MCU pin. Connect the source to ground and tie all the valve "low-sides" together to the drain of the MOSFET.

Make sure you have snubbing diodes across the valve solenoids.

Huge thanks for your patient and detailed set of instructions. I will keep this bookmarked so that I can come back to it if I ever need it.
Thank you all for your help!