Run 8 Pumps From Blynk

@myggle you are driving me nuts :joy:
Everytime I go back to check your code I find that you modified something of what I gave you and that modification is causing your code to work faulty :cry:

Part 1
Iā€™ll start of with your previous code since I think itā€™s more relevant to your end product

int pumpPin[8] = { 2, 3, 4, 5, 6, 7, 8, 9 };      //Digital pins used
int pumpNumber[9]  = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };  //V0 (1 - 8)

pumpNumber is a new array that you added that is not needed,
first of its an array with the size of 9 for tracking 8 pumps so one is unused, further it does nothing for you other than denote the amount of pumps you have. The only variable you need to track which pump you are accessing is the ā€œindexā€ value passed by your step widget

int pumpIndex = 0;
BLYNK_WRITE(V0) {                 // Step Widget
    pumpIndex = param.asInt() - 1;
}  

Every time you press this widget, with min max it 1-8 it will send a number in that range. Using 1-8 is purely a user interface adaptation of your array range 0-7 because it makes more sense.

By subtracting 1, your variable pumpIndex will be set with theese values
per stepper widget ā€œindexā€
1 2 3 4 5 6 7 8 <-- stepper widget var
0 1 2 3 4 5 6 7 <-- pump index var.

This is needed because your arrays always start with index 0, beeing 8 places long, the range is 0-7.

So perhaps now you see why ā€œint pumpNumber[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; //V0 (1 - 8)ā€ is redundant

Part 2

As I found the same error in your new code ill bring it up again. You want to set pinModes for all your pumps as OUTPUT, and you are going about the wrong way with your for loop.

for(int p = 2; p <= 9; p++)
   {
       pinMode(pumpPin[p], OUTPUT);
   }

Your pins are in the range of 2-9, this is declared in the array pumpPin. But! They are declared at the arrays positions 0-7.
Always, when accessing your arrays values you need to call to correct indexes.

With your array

int pumpPin[8] = { 2, 3, 4, 5, 6, 7, 8, 9 };      //Digital pins used

matching the values to index its like this
2 3 4 5 6 7 8 9 <- Values in array
0 1 2 3 4 5 6 7 <- at corresponding index

So your for loop starts at p = 2 the first element you get
is at position 3 in the array and contains your digital pin 4.
Effectively skipping position 1-2 and never setting their pin modes.
pumpPin[2] = 4
pumpPin[3] = 5
ā€¦
ā€¦
pumpPin[7] = 9
pumpPin[8] = N/A <- once your for loop hits this it will crash your code, or rather it should with an index out of bounds error, there is no such position in your arrays above the index value of 7.

Part 3
I would guess that, why your checkPump was crashing/not working properly boils down to your previous sneaky style implementation of the redundant array called pumpNumber. :slight_smile:

 if (pumpNumber[x] == 1 && pumpRunning[x] != 1) // Pump should run but isn't running = Start pump

for every x in the for loop you were checking the index of pumpNumber and this is what you got
x pumpNumber value
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7

And checking if this was equal to 1, which only happend at x = 1
because it accessed pumpNumber[x = 1] which contained 1
thus allowing the code to proceed.

This was the original code I gave you

if (pumpState[x] == 1 && pumpRunning[x] != 1) // Pump should run but isn't running = Start pump

It was made so that it checks the state of each pump, to determine wether or not they were to be turned on or not.
so if pumpState[x] for a given pump would return 1, it would denote that this pump should run.

Then it checked if it was already running by checking the pumpRunning[x] as to prevent redundant code, because if it is already running we need not do anything with this pump.
Doing so would mess up our pumpStartTime and the pump would never stop working.

Conclusion
Always work your code top to bottom, follow the logic, speak outloud if need be. What is happening in my program at this moment, what do these variables contain prior and after my modification. etc etc.

When given a snippet of code, do not modify it untill it is working and all errors are solved, as to prevent creating new ones, case in point :wink:

About new code
Iā€™ll take a look at your new code a bit later when I get the time :slight_smile:

But at a first glance your new solution will bring you error should you ever change the pump index at runtime, you will loose controll of the running pump.

Also you are doing something weird with your index handling.
At declaration you do not instantiate pumpNumber, I think it defaults to 0.

So pumpNumber = 0
then x = 0 - 1

Your subtraction needs to be handled at BLYNK_WRITE(V0)
because at the moment you are indexing your value as is
within the range of 1-8 and will never get pump at index 0 of your array should you use the pump selector widget.

Iā€™ll try not to create a wall of text for your new code. Iā€™ll just modify the errors I see and you can try it out.
Iā€™ve edited with comments containing 5 stars ***** . This should be runnable, Iā€™m at work so canā€™t check in an IDE.

This code will how ever due to its logic

  1. Bug out if a pump is running and you are changing pump via selector

  2. Wonā€™t allow control of multiple pumps at the same time

  3. Wonā€™t allow the use of different ā€œstepā€ settings per pump

Code
#include <SPI.h>                      //Used by Blynk
#include <Ethernet.h>                 //Used by Blynk
#include <BlynkSimpleEthernet.h>      //Used by Blynk
#include <SimpleTimer.h>
//#define BLYNK_PRINT Serial
char auth[] = "AuthCode";       //Paste code that app emailed you between "quotes"

SimpleTimer timer;
WidgetTerminal terminal(V3);
// from left to right position every array hold values for
// your pumps 1-8, by using index 0-7

int pumpPin[8] = { 2, 3, 4, 5, 6, 7, 8, 9 };         //Digital pins used
uint32_t multiplier[8] =  { 858, 827, 872, 865, 887, 895, 913, 843 }; //ms per ml
uint32_t startPump = 0;
uint32_t stopPump = 0;
uint32_t runCount;
//int pumpNumber;    // Not needed *****
float DOSEml;           //V1 Step Widget (0.25 per step, send step/NO, loop values ON)
int button = 0;         //V2 Button Widget set to Switch
bool pumpRunning = false;
int zero = 0;
int x = 0 			// Edited *****

BLYNK_WRITE(V0) {                 // Choose Pump
  pumpNumber = param.asInt() - 1; // Edited *****
}
BLYNK_WRITE(V1) {                 // Adjust Dosage
  DOSEml = param.asFloat();	
}
BLYNK_WRITE(V2) {                 // Activate Chosen Pump
  button = param.asInt();
}

void setup()
{
  Serial.begin(9600);
  Blynk.begin(auth);
  while (Blynk.connect() == false) {}
  timer.setInterval(500L, checkPump);
  for (int p = 0; p <= 7; p++) // Edited *****
  {
    pinMode(pumpPin[p], OUTPUT);
  }
  Blynk.virtualWrite(V0, 0);
  Blynk.virtualWrite(V1, 0);
  Blynk.virtualWrite(V2, 0);
}

void loop()
{
  Blynk.run();
  timer.run();
}

void checkPump()
{
   // Edited *****
  if (button == 1 && pumpRunning == false)    //if NO, do nothing! If YES, proceed.
  {
    pumpRunning = true;  // pump is now running
    digitalWrite(pumpPin[x], HIGH); // sets selected pin HIGH
    startPump = millis();           // saves start time to calculate from
    runCount = DOSEml * multiplier[x]; // Do you get errors here? Multiplying float with uint32_t, saved to uint32_t? *****	
    stopPump = startPump + runCount;
   } // Edited, closing first if statement. If omitted you will never reach stop because pumpRunning cannot be set to true ***** 
   
    if (millis() - startPump > millis() - stopPump)
    {
      digitalWrite(pumpPin[x], LOW); 
      pumpRunning = false;
      Blynk.virtualWrite(V0, 0);
      Blynk.virtualWrite(V1, 0);
      Blynk.virtualWrite(V2, 0);
    }
	// Edited, bracket removed *****
}
1 Like

I copied your code over my sketch this morning, but didnā€™t want to run it till I better understood what is happening and when. I will get back to it after work today. My concern however is that I canā€™t see how [x] is associated with the incoming value from V0.

As for multiplying the float with the uint, my studies suggest that uint32_t is large enough to hold the sum of the problem. As far as I can tell, the previous version of the sketch achieved this and activated the motor accordingly. I know my explanation of it is lacking, but I do need some way of multiplying the incoming float with the corresponding multiplier (int).ā€‹

Your x is a global variable stored on the hardware which tells it what pump it is to work with,
It is a number, 0-7 which corresponds to the positions of 0-7 in your arrays.

V0 gives you values of 1-8, because it looks nicer in your app interface. When you select pump 1 in your app project you are actually selecting the pump located in your hardwares array of pumps located att position 0.

Therefore you must subtract the V0 value with 1 as to pass the correct parameter to your hardware variable x so that when used it gives the correct pump.

If you still do not understand just let me know

I think I understand, but I also thought I achieved this in my latest sketch where x was made global, to equal pumpNumber - 1;

Your modification omitted this line and deferred the subtraction to the BLYNK_WRITE block, and left x still as a global to equal zero. With this done, I have difficulty identifying where the value passed by V0 is assigned to x.

Now I understand your POV.
Itā€™s rather simple, when defining variables outside of functions, as we did with x. Whatever value you added will be saved to x one time, that is on boot of your device.

If we want to change this variable x we need to use functions. BLYNK_WRITE is a function that is called when you change value in your step widget. When it is called we use the variable param to get the value you set in your app. With param.asInt().
Then we save it to variable x to update its value.
x = param.asInt() - 1;

In conclusion every time you update your widget, by changing its value, x is updated as well.

1 Like

Ok, weā€™re getting much closer! HERE is another youtube video of the pumps misbehaving, but at least I can spot why. After the function is complete, x retains the values it received and the respective pump activates at the next scheduled timer interval. My first attempted fix (I just canā€™t leave stuff alone!:grin:) was to add another boolean, and to switch its state once the button was pressed so that at the next call of the function, the state would be opposite and everything would stop. This worked, but it locked me out as the Arduino was stuck in the last ā€˜ifā€™ statement because the bool never went false again, and doing so would defeat the purpose. I then realized that x was causing me the grief, so after the 3 virtualWrites, I set x back to zero. Now my problem is that pump 1 happens to be in the zero slot, so the previous value is merely applied to pump 1 and thatā€™s where Iā€™m at.

void checkPump()
{
   // Edited *****
  if (button == 1 && pumpRunning == false)   //&& alreadyRunning == false)
  {
    //alreadyRan = true;
    pumpRunning = true;  // pump is now running
    digitalWrite(pumpPin[x], HIGH); // sets selected pin HIGH
    startPump = millis();           // saves start time to calculate from
    runCount = DOSEml * multiplier[x]; // Do you get errors here? Multiplying float with uint32_t, saved to uint32_t? ***** 
    stopPump = startPump + runCount;
   } // Edited, closing first if statement. If omitted you will never reach stop because pumpRunning cannot be set to true ***** 
   
    if (millis() - startPump > millis() - stopPump)
    {
      digitalWrite(pumpPin[x], LOW); 
      pumpRunning = false;
      Blynk.virtualWrite(V0, 0);
      Blynk.virtualWrite(V1, 0);
      Blynk.virtualWrite(V2, 0);
      x = 0;
    }
  // Edited, bracket removed *****
}

Alrighty then :stuck_out_tongue:

Getting somewhere, I was hoping youā€™d spot it yourself eventually since youā€™d learn more that way but check it out.

For this explanation purpose lets just check what happens with your if statement if millis() would go from 1-5 and your pump would check against all of that.
Lets assume your DOSEml * multiplier evaluates to 4.

startPump = millis(); // <- assign 1
stopPump = 1 + 4;

so far so good?
now lets go over the math in your if statement
with # corresponding to millis() giving 1-5

[details=Expand]#1
if (1 - 1 > 1 - 5) // evaluates to true, 0 > -4
stopPump
#2
if (2 - 1 > 2 - 5) // evaluates to true, 1 > -3
stopPump
#3
if (3 - 1 > 3 - 5) // evaluates to true, 2 > -2
stopPump
#4
if (4 - 1 > 4 - 5) // evaluates to true, 3 > -1
stopPump
#5
if (5 - 1 > 5 - 5) // evaluates to true, 4 > 0
stopPump
#6
if (6 - 1 > 6 - 5) // evaluates to true, 5 > 1
stopPump[/details]

Not quite what you wanted?
The error is in on your right side of the ā€˜>ā€™ sign.
millis() - startpump gives you a difference in time, this needs to be bigger than runCount to get the proper logic.

So revert to MY PREVIOUS CODE :wink:

if (millis() - startPump > runCount)

[details=Expand]#1
if (1 - 1 > 4) // evaluates to false, 0 > 4
doNothing
#2
if (2 - 1 > 4) // evaluates to false, 1 > 4
doNothing
#3
if (3 - 1 > 4) // evaluates to false, 2 > 4
doNothing
#4
if (4 - 1 > 4) // evaluates to false, 3 > 4
doNothing
#5
if (5 - 1 > 4) // evaluates to false, 4 > 4
doNothing
#6
if (6 - 1 > 4) // evaluates to true, 5 > 4
stopPump[/details]

Also why are you resetting everything to pump 0?

      Blynk.virtualWrite(V0, 0);
      Blynk.virtualWrite(V1, 0);
      Blynk.virtualWrite(V2, 0);
      x = 0;

You should also toss in a
button = 0 in your second if statement prior to calling virtualWrites
as to ensure that your hardware understands that no more pumping is allowed! :stuck_out_tongue:

1 Like

Now before you go off and do something error prone :wink:
Please just try out editing your second if statement to this

if (millis() - startPump > runCount)
    {
      button = 0;
      digitalWrite(pumpPin[x], LOW); 
      pumpRunning = false;
      Blynk.virtualWrite(V2, LOW);
    }
1 Like

Eureka! Your suggestion works now flawlessly. Thank you Fettkeewl so very much!

I see now my error in thinking was that

startPump = millis();

and

millis() - startPump > ....;

were one and the same. After your final explanation, however, I was able to see that it is not and now Iā€™m a little embarrassed that I ever thought they were.

HERE is the most recent video/screen share. I also removed the pumps from the motors and pushed on papers with a number markered on for ease of view.

Also, here is the code. I tried to remove the boolean variable all throughout as it looks like itā€™s not doing anything of real value, but somehow it made the selected pin go HIGH and stay HIGH, so I added them back in and reuploaded and viola, works flawlessly again. I will reread through function as many times as I need to in order to comprehend why the sketch does not function properly without the boolean.

#include <SPI.h>                      //Used by Blynk
#include <Ethernet.h>                 //Used by Blynk
#include <BlynkSimpleEthernet.h>      //Used by Blynk
#include <SimpleTimer.h>
char auth[] = "AuthCode";       //Paste code that app emailed you between "quotes"

SimpleTimer timer;
WidgetTerminal terminal(V3);

int pumpPin[8] = { 2, 3, 4, 5, 6, 7, 8, 9 };         //Digital pins used
uint32_t multiplier[8] =  { 858, 827, 872, 865, 887, 895, 913, 843 }; //ms per ml
uint32_t startPump = 0;
uint32_t runCount;
float DOSEml;           //V1 Step Widget (0.25 per step, send step/NO, loop values ON)
int button = 0;         //V2 Button Widget set to Switch
bool pumpRunning = false;
int zero = 0;
int x;       // Edited *****

BLYNK_WRITE(V0) {                 // Choose Pump
  x = param.asInt() - 1; // Edited *****
}
BLYNK_WRITE(V1) {                 // Adjust Dosage
  DOSEml = param.asFloat();
}
BLYNK_WRITE(V2) {                 // Activate Chosen Pump
  button = param.asInt();
}

void checkPump()
{
  // Edited *****
  if (button == 1 && pumpRunning == false)   
  {
    Blynk.virtualWrite(V0, 0);
    Blynk.virtualWrite(V1, 0);
    Blynk.virtualWrite(V2, 0);
    pumpRunning = true; 
    digitalWrite(pumpPin[x], HIGH); 
    startPump = millis();           
    runCount = DOSEml * multiplier[x];
    terminal.println("Dosing from Pump:");
    terminal.println(x + 1);
    terminal.println("Milliliters:");
    terminal.println(DOSEml);
    terminal.flush();
  } 
  
  if (millis() - startPump > runCount)
  {
    digitalWrite(pumpPin[x], LOW);
    pumpRunning = false;
    button = 0;
  }
  
}

void setup()
{
  Serial.begin(9600);
  Blynk.begin(auth);
  while (Blynk.connect() == false) {}
  timer.setInterval(1500L, checkPump);
  for (int p = 0; p <= 7; p++) // Edited *****
  {
    pinMode(pumpPin[p], OUTPUT);
  }
  Blynk.virtualWrite(V0, 0);
  Blynk.virtualWrite(V1, 0);
  Blynk.virtualWrite(V2, 0);
}

void loop()
{
  Blynk.run();
  timer.run();
}
1 Like

Iā€™m happy to hear that it works well for you and that you are satisfied :stuck_out_tongue: No worries on the help, its been fun, Iā€™ve actually come to appreciate teaching others recently.

Regarding the bool, if you are reffering to ā€˜pumpRunningā€™
it is definately needed, its independant of blynk and insures that your hardware operates properly.

think about it, your pumps run at various lengths in time, but your checkPump function is called every 0.5s. If you did not have the boolean checkflag in your if statement you would do this code
every 0.5 seconds

    Blynk.virtualWrite(V0, 0);
    Blynk.virtualWrite(V1, 0);
    Blynk.virtualWrite(V2, 0);
    pumpRunning = true; 
    digitalWrite(pumpPin[x], HIGH); 
    startPump = millis();           // <--- no end time when this is updated every 0.5 s 
    runCount = DOSEml * multiplier[x];
    terminal.println("Dosing from Pump:");
    terminal.println(x + 1);
    terminal.println("Milliliters:");
    terminal.println(DOSEml);
    terminal.flush();

which would end up resetting your ā€œstartPumpā€ time value every time, how would it then know when to end?

Just remember as it is now, if you change x WHILE a pump is operating, it wonā€™t turn off, because x will be a
different value, than the pin that is active.

Scenario:
turn on pump 1 for 5 seconds
after 3 seconds change to pump 2
after 2 seconds attempt to turn of pump 1 <-- error here, x is now pump 2.
turn of pump 2 <-- whoaa pump 2, isnā€™t even running, pump 1 still runningā€¦

If you want to avoid this, then you must call a halt to your pump prior to changing x.

BLYNK_WRITE(V0) {                 // Choose Pump
  digitalWrite(pumpPin[x], LOW); // ensure that current pump is turned off before changing X
  x = param.asInt() - 1; // Edited *****
}

also I just saw that you need to add pumpRunning to your second if statement to prevent unneccesary execution of the code while pump is not running.

if (pumpRunning == true && millis() - startPump > runCount)
  {
    digitalWrite(pumpPin[x], LOW);
    pumpRunning = false;
    button = 0;
  }

Edit, just saw that you moved your virtualWrites up to the other if statement

A side questionā€¦ what the hell are you building :joy: It better be an alcoholic beverage dispenser!

1 Like

I am building a COMPLETE hydroponics controller. I am yet to find (or design) two way valves that can handle fluids below 3 psi. I think the code for that (w/Blynk) will be simple. The final phase will be Atlas Scientific PH, E.C., D.O. and temp probes with circuitry. The plan is to move nutrient solutions from a reservoir through a manifold with each probe contained, than with these pumps, add in accordingly. I have an illustration of my plumbing circuit on my PC and I will add it to this thread after work. I will also create a finished project later today with better descriptions of the circuit behind the pumps and links to everything, and of course tag you in it.

1 Like

Sounds good :slight_smile:

Now you really need to implement working multiple pumps at the same time :stuck_out_tongue:

I intend to, but itā€™s very risky running all pumps with only a 2amp power supply.

Also neglected to mention in my previous post that you are an excellent teacher. On other coding forums, it seems ā€œthe teachersā€ meet the class with rudeness and condescension, but you are exceedingly patient and have overlooked my shortcomings while continue to focus on the lessons you were trying to teach, so once again, thank you much!

1 Like

Its not risky at all, if you program it, I can show you some day should you want to expand your code, basically you keep track of every pump, wether or not they are running, Count the amounts of running, and once you count to 2, abort the function untill next call.

int counter = 0;
for (int i = 0; i <= 7; i++) 
   if (counter >= 2)
      return; // exit function
   else
      counter += pumpRunning[i];  // 0 for off, 1 for on. 

Providing proper array setups as in previous code snippets and a little modification to checkPump function

1 Like

Ever since the Blynk community helped me get this project working, Iā€™ve added a bit of functionality. I added another Step widget that allows me to adjust the multiplier, so that I will be able to calibrate each pump when itā€™s motor is paired to a nutrient solution. I now wish to add 8 LED widgets to indicate when a pump is pumping. I am somewhat hesitant how to to approach this. I know I need another array of some sort, but I canā€™t figure out how to put WidgetLED calls into an array. The following is my best guess;

WidgetLED dosingLEDs

dosingLEDs[8]  = { V14, V15, V16, V17, V18, V19, V20, V21 }

Any insights or advice is welcomed!

@myggle only looked through the last few posts in this thread but I see you already have:

pumpPin[x]

Which is an array from 0 to 7, inclusive.

You can use this same array for Blynk LEDā€™s.

Blynk.virtualWrite(pumpPin[x] + 14, 255); // for ON or 0 for OFF

Thanks Costas. How would I correlate each LED widget to an array position? I donā€™t comprehend how the array position associates to any of the virtual pins. Can you expound?

I was going to add this text.

I like to see virtual pins referenced as Vx but the clever Blynk guys allow you to access them without the V prefix i.e. the virtualWrite knows you want to access a virtual pin and not a digital pin.

I believe you can actually do something like this but itā€™s not necessary:

Blynk.virtualWrite( "V" + String(pumpPin[x] + 14), 255); // for ON or 0 for OFF

1 Like