Problems with water drop photograph using millis

Hi

I have built a water drop rig and I am using an Arduino Uno to control the valves, flashes etc.
Sadly I’m not a skilled programmer and this is actually my first attempt writing in C++

I started using delays, but as all the others soon run into sync troubles.
After searching around in this forum for some time, I discovered this thread:

where @S77eve having the same challenge as I have. S77eve’s set up was reasonable similar to mine and the help he got from @Grumpy_Mike was a big help to me as well. I managed to put this sketch together whicht actually work very well when doing single drop on three valves

boolean allFiringNotdone;
long int timeNow, sol1delay,sol2delay,sol3delay;
long int sol1OnTime,sol2OnTime,sol3OnTime;
long int fire_Sol1,fire_Sol2,fire_Sol3;
long int release_Sol1,release_Sol2,release_Sol3;
long int flash1_delay,flash2_delay;
long int flash1_off,flash2_off;
long int fire_flash1,fire_flash2,release_flash1,release_flash2;
#define STANDBY 0
#define ACTIVE 1
int flash1 = 2; //Flash 1 on pin 2
int flash2 = 3; //Flash 2 on pin 3
int Sol_One = 4; //Valve 1 on pin 4
int Sol_Two = 5; //Valve 2 on pin 5
int Sol_Three = 6; //Valve 3 on pin 6
int light = 7; //Worklight auto on/off
int cam_focus = 10; // Camera focus on pin 10
int cam_shut = 11; // Camera shutter on pin 11
int BUTTON_PIN = 9; // Trigger button on pin 9
int mode = STANDBY;


void setup(){
   //Flash is output
  pinMode (flash1, OUTPUT);
  pinMode (flash2, OUTPUT);
  //Valves is output
  pinMode (Sol_One, OUTPUT);
  pinMode (Sol_Two, OUTPUT);
  pinMode (Sol_Three, OUTPUT);
  // Worklight is output
  pinMode (light, OUTPUT);
  // Camera focus and shutter is output
  pinMode (cam_focus, OUTPUT);
  pinMode (cam_shut, OUTPUT);
  //Trigger button pin is input
  pinMode (BUTTON_PIN, INPUT);
  
  
  
  // Valve opening and closing times in milliseconds
  // To be edited
  sol1delay = 50; // time delay before solinoide 1 fires
  sol2delay = 60; // time delay before solinoide 2 fires
  sol3delay = 90; // time delay before solinoide 3 fires
  sol1OnTime = 80; // the time this solinoide is on
  sol2OnTime = 250; // the time this solinoide is on
  sol3OnTime = 200; // the time this solinoide is on
  
  // Flash firering times
  flash1_delay = 350; //time before flash 1 fires
  flash2_delay = 750; //time before flash 2 fires
  //No need to edit
  flash1_off = flash1_delay + 10; // Flash pin 1 on time
  flash2_off = flash2_delay + 10; // Flash pin 2 on time

}

void loop() {

  if (digitalRead(BUTTON_PIN) == HIGH)
  {
    mode = ACTIVE;
    // Working light off
    digitalWrite(light, HIGH); // turn the lights off
    delay(500); // to give time for light to go down and settle after button push
    //Activate camera 
    digitalWrite(cam_shut, HIGH); // open the camera shutter
    delay(150); // Give time for shutter to open
    digitalWrite(cam_shut, LOW); // close camera shutter pin. 
    //Actually shutter opening time is set in the camera 
    
// Start water drop sequence
  allFiringNotdone = true;
  timeNow = millis();
  fire_Sol1 = timeNow + sol1delay;
  fire_Sol2 = timeNow + sol2delay;
  fire_Sol2 = timeNow + sol3delay;
  release_Sol1 = timeNow + sol1OnTime;
  release_Sol2 = timeNow + sol2OnTime;
  release_Sol3 = timeNow + sol3OnTime;
  fire_flash1 = timeNow + flash1_delay;
  fire_flash2 = timeNow + flash2_delay;
  release_flash1 = timeNow + flash1_off;
  release_flash2 = timeNow + flash2_off;
  
  
  while(allFiringNotdone){
    timeNow = millis();
    if(timeNow > fire_Sol1 && timeNow < release_Sol1) digitalWrite(Sol_One, HIGH); 
    if(timeNow > release_Sol1) digitalWrite(Sol_One, LOW); 
    if(timeNow > fire_Sol2 && timeNow < release_Sol2) digitalWrite(Sol_Two, HIGH); 
    if(timeNow > release_Sol2) digitalWrite(Sol_Two, LOW); 
    if(timeNow > fire_Sol3 && timeNow < release_Sol3) digitalWrite(Sol_Three, HIGH); 
    if(timeNow > release_Sol3) digitalWrite(Sol_Three, LOW);
    if(timeNow > fire_flash1 && timeNow < release_flash1) digitalWrite(flash1, HIGH); 
    if(timeNow > release_flash1) digitalWrite(flash1, LOW);
    if(timeNow > fire_flash2 && timeNow < release_flash2) digitalWrite(flash2, HIGH); 
    if(timeNow > release_flash2) digitalWrite(flash2, LOW);
    
    if(timeNow > release_Sol1 && timeNow > release_Sol2 && timeNow > release_Sol3 && timeNow > release_flash1 && timeNow > release_flash2) allFiringNotdone = false;
  }

  }
// End water drop sequence

    
    
    mode = STANDBY;
    delay(500);
    digitalWrite(light, LOW); // turn lights back on
  }

This is end of part one

I exceeded the maximum character in this post so here is part to:

My next challenged was to reprogram this sketch to open a single valve up to three times producing a water drop collision using the same valve. I thought that would be an easy task, well it wasn’t. :slight_smile: I obviously got It wrong but I can’t see what it is. I hope anyone of you guys can lead me to the right direction.
This is the sketch I cant get to work. The valve open and close only the first time.

boolean allFiringNotdone;
long int timeNow, sol1delay,sol2delay,sol3delay;
long int sol1OnTime,sol2OnTime,sol3OnTime;
long int fire_Sol1,fire_Sol2,fire_Sol3;
long int release_Sol1,release_Sol2,release_Sol3;
//Define  2 and 3 drop using valve 1
long int sol12ndDropDelay,sol13rdDropDelay;
long int sol12ndOnTime,sol13rdOnTime;
long int fire_Sol1_2nd, fire_Sol1_3rd;
long int release_Sol1_2nd,release_Sol1_3rd;
// Flash
long int flash1_delay,flash2_delay;
long int flash1_off,flash2_off;
long int fire_flash1,fire_flash2,release_flash1,release_flash2;
#define STANDBY 0
#define ACTIVE 1
//Pin set up
int flash1 = 2; //Flash 1 on pin 2
int flash2 = 3; //Flash 2 on pin 3
int Sol_One = 4; //Valve 1 on pin 4
int Sol_Two = 5; //Valve 2 on pin 5
int Sol_Three = 6; //Valve 3 on pin 6
int light = 7; //Worklight auto on/of
int cam_focus = 10; // Camera fokus on pin 10, not used
int cam_shut = 11; // Camera shutter on pin 11
int BUTTON_PIN = 9; // Trigger switch on pin 9
int mode = STANDBY;


void setup(){
  // put in the other stuff in your setup
   //Flash is output
  pinMode (flash1, OUTPUT);
  pinMode (flash2, OUTPUT);
  //Valves are output
  pinMode (Sol_One, OUTPUT);
  pinMode (Sol_Two, OUTPUT);
  pinMode (Sol_Three, OUTPUT);
  // worklight is output
  pinMode (light, OUTPUT);
  // Camera shuttet and fokus is output
  pinMode (cam_focus, OUTPUT);
  pinMode (cam_shut, OUTPUT);
  //Trigger switch is input
  pinMode (BUTTON_PIN, INPUT);
  // put some sensible figures here, time in milliseconds
  // First drop
  sol1delay = 50; // time delay before solinoide 1 fires
  sol1OnTime = 80; // the time this solinoide is on
  //Second drop
  sol12ndDropDelay = 100; // Time before solinoide 1 fires 2nd time
  sol12ndOnTime = 120; //Time solinoide is on 2nd time
  //Third drop
  sol13rdDropDelay = 150; // Time before solinoide 1 fires 3rd time
  sol13rdOnTime = 170; //Time solinoide is on 2nd time
  
  
  
  flash1_delay = 350; //Delay flash 1
  flash2_delay = 750; //Delay flash 2
  flash1_off = flash1_delay + 10;
  flash2_off = flash2_delay + 10;

}

void loop() {

  if (digitalRead(BUTTON_PIN) == HIGH)
  {
    mode = ACTIVE;

   
    digitalWrite(light, HIGH); // turn the lights off
    delay(500); // to give time for light to go down and settle after button push

    
    digitalWrite(cam_shut, HIGH); // open the camera shutte
    delay(250); // Give time for shutter to open
    digitalWrite(cam_shut, LOW); // close camera shutter
    
// Problem is below *******************************************************[/b]
  allFiringNotdone = true;
  timeNow = millis();
  fire_Sol1 = timeNow + sol1delay;
  release_Sol1 = timeNow + sol1OnTime;
  // Second drop
  fire_Sol1_2nd = timeNow + sol12ndDropDelay;
  release_Sol1_2nd = timeNow + sol12ndOnTime;
  //Third drop
  fire_Sol1_3rd = timeNow + sol13rdDropDelay;
  release_Sol1_3rd = timeNow + sol13rdOnTime;
  
 
  
  fire_flash1 = timeNow + flash1_delay;
  fire_flash2 = timeNow + flash2_delay;
  release_flash1 = timeNow + flash1_off;
  release_flash2 = timeNow + flash2_off;
  
  
  while(allFiringNotdone){
    timeNow = millis();
    //Valve one first drop
    if(timeNow > fire_Sol1 && timeNow < release_Sol1) digitalWrite(Sol_One, HIGH); 
    if(timeNow > release_Sol1) digitalWrite(Sol_One, LOW);
    //Valve one second drop
    if(timeNow > fire_Sol1_2nd && timeNow < release_Sol1_2nd) digitalWrite(Sol_One, HIGH); 
    if(timeNow > release_Sol1_2nd) digitalWrite(Sol_One, LOW);
    //Valve one third drop
    if(timeNow > fire_Sol1_3rd && timeNow < release_Sol1_3rd) digitalWrite(Sol_One, HIGH); 
    if(timeNow > release_Sol1_3rd) digitalWrite(Sol_One, LOW);
   
    //Flash
    if(timeNow > fire_flash1 && timeNow < release_flash1) digitalWrite(flash1, HIGH); 
    if(timeNow > release_flash1) digitalWrite(flash1, LOW);
    if(timeNow > fire_flash2 && timeNow < release_flash2) digitalWrite(flash2, HIGH); 
    if(timeNow > release_flash2) digitalWrite(flash2, LOW);
    
    if(timeNow > release_Sol1 && timeNow > release_flash1 && timeNow > release_flash2 && timeNow > release_Sol1_2nd && timeNow > release_Sol1_3rd) allFiringNotdone = false;
  }

  }
// Problem above ***********************************************************[/b]

    
    
    mode = STANDBY;
  
    delay(500);
    digitalWrite(light, LOW); // turn lights back on
  }

The rig and the rest of the hardware is working quite well and I have made a little video when it’s in action.
You can see it here if you like. What you see here is double drop single valve programmed with standard delays.

The code doesn't look very pretty, and I haven't looked into it in detail but I can easily imagine that you have trouble changing the logic. If I understood you correctly you already have something working sending one pulse each to three separate solenoids and now want to send the three pulses to the same solenoid. Given that the first version (presumably) works, couldn't you just define Sol_One, Sol_Two and Sol_Three to be the same pin number so that all three pulses go to the same solenoid? That would seem easier than refactoring the sketch.

Beautiful pictures, by the way.

The way I solved this was to set the camera on its highest shutter speed then add as much light as possible (I used a 1000 watt photo flood) then take photos until you get the one you want. No need for the arduino at all.

Mark

holmes4:
The way I solved this was to set the camera on its highest shutter speed then add as much light as possible (I used a 1000 watt photo flood) then take photos until you get the one you want. No need for the arduino at all.

So how do you manage to get two or three drops colliding?

Hi and thanks for reply :)

I have tried to put sol 2 and 3 also on to pin 4 like this

int flash1 = 2; //Flash 1 on pin 2
int flash2 = 3; //Flash 2 on pin 3
int Sol_One = 4; //Valve 1 on pin 4
int Sol_Two = 4; //Valve 2 on pin 5
int Sol_Three = 4; //Valve 3 on pin 6

But Sol_One open only one time. I retried just now but only single drop i'm afraid.

If anyone will have a look at the code I will be very grateful :)

If you find your self writing code with variables with explicit subscripts, it's time to start thinking about arrays.

You have a couple of issues; it looks like your start times and delays overlap so you're trying to turn on the solenoid for the 2nd drop while it's already on. Worse though, you have this:

    if(timeNow > release_Sol1) 
      digitalWrite(Sol_One, LOW);

So once you're past the first drop timing, that line is going to repeatedly close the solenoid, irrespective of what the other statements are trying to do.

I'd echo Awol's suggesting to look at arrays. Perhaps build an array of structs where the struct has a time, a pin and high or low and a flag to mark the action as done. Work repeatedly through the array and see if anything's come due, set the pin to the specified value and mark it done.

Ok

Thanks for reply.

Being a novice in programming this was a "little Greek" for me but i'll try to have a look at it.

On reflection, an array of unsigned long with times in it would be simpler and could be made to suit your purpose. Just have the array represent the time offsets at which the state of the solenoid needs to change. As the time for each change comes up, flip the state of the solenoid. You'll probably need to set each time to zero as you process it and add code to ignore those zeroes.

This is how I have done it using delays.

//Snip
void setup()  {
  // initialize serial communication:
  Serial.begin(9600); 
  //setter blitser som output
  pinMode (flash1, OUTPUT);
  pinMode (flash2, OUTPUT);
  //Setter ventiler som output
  pinMode (valve1, OUTPUT);
  pinMode (valve2, OUTPUT);
  pinMode (valve3, OUTPUT);
  // Setter arbeidslys som output
  pinMode (light, OUTPUT);
  // Setter kamera fokus og lukker som output
  pinMode (cam_focus, OUTPUT);
  pinMode (cam_shut, OUTPUT);
  //Setter bryter som input
  pinMode (trig_shut, INPUT);
}
void light_off() {
  digitalWrite(light, HIGH); //Rele på og lys av
  delay (100);
}

  void light_on() {
  delay (1000);
  digitalWrite(light, LOW); // Rele av og lys på
  }
  
void cam_fire() {
  digitalWrite(cam_focus, HIGH); //Kamera fokus: aktiv
  delay(100);
  digitalWrite(cam_shut, HIGH); //Kamera lukker: aktiv
  delay(400);
  digitalWrite(cam_focus, LOW); //Kamera fokus av
  digitalWrite(cam_shut, LOW); //Kamera lukker av
  }

 void valve1_open() {
   digitalWrite(valve1, HIGH); //Setter ventil en høy
  delay(drop11); //Dette setter dråpestørrelsen
  digitalWrite(valve1, LOW); //Stenger ventil en
 }

  void valve2_open() {
   digitalWrite(valve2, HIGH); //Setter ventil to høy
  delay(drop21); //Dette setter dråpestørrelsen
  digitalWrite(valve2, LOW); //Stenger ventil to
 }

  void valve3_open() {
   digitalWrite(valve3, HIGH); //Setter ventil tre høy
  delay(drop31); //Dette setter dråpestørrelsen
  digitalWrite(valve3, LOW); //Stenger ventil tre

And then under loop it looks like this

 void loop()  {
   if (Serial.available() > 0) {
    int inByte = Serial.read();
   
     switch (inByte) {

//snip

   case 'c': //Case c  1 ventil 3 dråper
  //Når shutter er høy vil kamera ta bilde, ventil vil åpne og bilts vil fyre
  buttonStateShut = digitalRead(trig_shut);
  if (buttonStateShut == LOW) {
    
    light_off(); //Slå Av Lys
    cam_fire();
    valve1_open();
    delay(interv_1);
    valve1_open();
    delay(interv_2);
    valve1_open();
    flash_fire();
    light_on(); // Slå på Lys etter sekvens
  }
  else {
    digitalWrite(cam_shut, LOW);
   break;

Could this be a way forward, using millis insted of delay? Here I'm using serial monitor and just write the letter c and enter.