Hi All
I am using an Uno to control 3 solenoid valves and an external trigger. These valves and trigger are being used to create drips of water and trigger a camera to photograph the collisions. The approach I have taken is to set up all the parameters in structures representing each valve and 1 for the trigger.
When it is time to open a valve I open it and set a variable in the appropriate structure dictating when to close it again. I then go off and do other things, checking the time during each iteration of the loop. The timings are short, typically between say 10 and 100ms. when millis() <= the stored off time, then the associated pin for the valve is set low. Similar principle for the trigger. It is conceivable that I may want to turn on 4 valves for say 30ms, 20ms, 80ms & 10ms all within a time frame of 70ms, then turn them off after there allotted open time. Somewhere in there I need to set the trigger pin high too.
This approach seems to work well enough, until the 3rd and 4th valves are introduced, when what was a nice small drip from usually only 1 of the valves seems to turn into a bit of a stream lasting seemingly 3 or 4 times longer than it should. Oddly, this usually effects only the first valve although others may be effected to a lesser extent. My software includes a mechanism for reporting how long the Arduino thinks it has had the valve open for and these figures are pretty much spot on. At least within 3ms or so.
I have a fair amount of programming experience and I cant see why this shouldn't work all the time. However I don't have much experience in programming Arduinos though and Im wondering if Im just asking too much of the chip RTC.
I have banged my head against this for a few months now and represents a fair amount of my time. Before I give up (not really in my nature) Id like to run my problems past you guys in case I just being dim!!
This is the important bit of the code. Sketch files attached if anyone would like to look

I appreciate this is a BIG ask, but I'd be eternally (if not longer!) for your thoughts / suggestions.
Many Many thanks for your time and help
Regards
Philip
void loop()
{
long tmp = 0;
boolean AllDripsPerformed = false ;
boolean Done = true;
int i,j;
ReadSerialPort();
if (MsgComplete == true)
{
ActionMessage();
MsgComplete = false;
}
// check for Open Valve or Do Drips Cmd
// Only run if system is not running
if(SysRunning == 0)
{
if(ValveArray[0]->Drip_active == true){
digitalWrite(ValveArray[0]->Arduino_Pin, HIGH);
delay(DripArray[0]->drip_form_time);
digitalWrite(ValveArray[0]->Arduino_Pin, LOW);
Serial.print(F("Doing Drips on Valve 1\r\n "));
delay(1000);
}
if(ValveArray[1]->Drip_active == true){
digitalWrite(ValveArray[1]->Arduino_Pin, HIGH);
delay(DripArray[1]->drip_form_time);
digitalWrite(ValveArray[1]->Arduino_Pin, LOW);
Serial.print(F("Doing Drips on Valve 2\r\n "));
delay(1000);
}
if(ValveArray[2]->Drip_active == true){
digitalWrite(ValveArray[2]->Arduino_Pin, HIGH);
delay(DripArray[2]->drip_form_time);
digitalWrite(ValveArray[2]->Arduino_Pin, LOW);
Serial.print(F("Doing Drips on Valve 3\r\n "));
delay(1000);
}
}//if
// check for Open Valev or Do Drips Cmd
// Only run if system is not running
if(SysRunning == 0)
{
if(ValveArray[0]->Open_active == true){
digitalWrite(ValveArray[0]->Arduino_Pin, HIGH);
}//if
if(ValveArray[1]->Open_active == true){
digitalWrite(ValveArray[1]->Arduino_Pin, HIGH);
}//if
if(ValveArray[2]->Open_active == true){
digitalWrite(ValveArray[2]->Arduino_Pin, HIGH);
}//if
for(int t = 0; t<=2; t++)
{
if(ValveArray[t]->Was_Open_Active == true){
ValveArray[t]->Open_active = false;
ValveArray[t]->Was_Open_Active = false;
digitalWrite(ValveArray[t]->Arduino_Pin, LOW);
}//if
}//for
}//if SysRunning == 0
if(Test_Trigger == true){
Serial.print(F("Testing Trigger on Pin "));Serial.print(Trigger.Ard_Pin);Serial.print(F("\r\n"));
digitalWrite(Trigger.Ard_Pin, LOW);
delay(Trigger.Trig_Pin_High_time);
digitalWrite(Trigger.Ard_Pin, HIGH);
Test_Trigger = false;
}//if
if(SysRunning == 1 && FirstDripTTG == true)
{
// do first drip,
DripArray[0]->ms_to_off = millis() + DripArray[0]->drip_form_time;
StartDrip(DripArray[0],0,NULL);
DripArray[0]->started = true;
// work out when the other drips need to go relative to the StartDrip
FirstDripFinish = millis();
// drip delays cant be changed once the drip sequ has begun, so set the ms_to_go field now
for(i=1; i<=3; i++){
DripArray[i]->ms_to_go = (DripArray[i]->drip_delay + FirstDripFinish);
}//for
// and the trigger time
if(Trigger.active == true){
Trigger.ms_to_go = Trigger.Trig_delay + FirstDripFinish;
}//if
while(AllDripsPerformed == false){ // Hang around in this loop till all drips and trigger that is active has been started AND stopped again for this sequence of drips
tmp = millis();
// check to see if other drips need stopping, including drip 0 ...
for(j=0; j<=3; j++){
if(DripArray[j]->active == true && DripArray[j]->started == true && DripArray[j]->performed == false){
if(tmp >= DripArray[j]->ms_to_off){
digitalWrite((DripArray[j]->use_valve)->Arduino_Pin,LOW);
DripArray[j]->performed = true;
Serial.print(F("===== DRIP "));Serial.print(j+1);Serial.print(F(" Stopped. Size Delay = "));Serial.print(tmp - (DripArray[j]->form_start_time));Serial.print(F("ms\r\n"));
}//if
}//if
}//for
if(Trigger.active == true && Trigger.Trig_Fired == false){
if(tmp >= Trigger.ms_to_go){
digitalWrite(Trigger.Ard_Pin, HIGH);
Trigger.delay_start_time = tmp;
Trigger.Trig_Fired = true;
Trigger.ms_to_off = tmp + Trigger.Trig_Pin_High_time;
Serial.print(F("Trigger pin set HIGH after "));Serial.print(tmp - FirstDripFinish);Serial.print(F("ms\r\n"));
}//if
}//if
// check to see if other drips need starting ...
for(j=1; j<=3; j++){
if(DripArray[j]->active == true && DripArray[j]->started == false){
if(tmp >= DripArray[j]->ms_to_go){
DripArray[j]->ms_to_off = tmp + DripArray[j]->drip_form_time;
StartDrip(DripArray[j],j,NULL);
DripArray[j]->started = true;
}//if
}//if
}//for
// test to see if the ext trigger should be fired yet. When Fired, re set it at the end so as not to mess up the valve timings with the delay needed to fire the trigger
if(Trigger.active == true && Trigger.Trig_Fired == false){
if(tmp >= Trigger.ms_to_go){
digitalWrite(Trigger.Ard_Pin, HIGH);
Trigger.delay_start_time = tmp;
Trigger.Trig_Fired = true;
Trigger.ms_to_off = tmp + Trigger.Trig_Pin_High_time;
Serial.print(F("Trigger pin set HIGH after "));Serial.print(tmp - FirstDripFinish);Serial.print(F("ms\r\n"));
}//if
}//if
if(Trigger.active == true && Trigger.Trig_Fired == true && Trigger.Trig_performed == false){
if(millis() >= Trigger.ms_to_off){
digitalWrite(Trigger.Ard_Pin, LOW);
Trigger.Trig_performed = true;
Serial.print(F("Trigger pin set LOW after "));Serial.print(Serial.print(tmp - Trigger.delay_start_time));Serial.print(F("ms\r\n"));
}//if
}//if
// right, lets check to see if all active drips are completely finished, and the trigger off course!
j=0;
AllDripsPerformed = true;
while((AllDripsPerformed == true) && (j <=3)){ // check to see if other drips need stopping, including drip 0 ...
if((DripArray[j]->active == true) && (DripArray[j]->performed == false)){
AllDripsPerformed = false;
}//if
j++;
}//while
if(Trigger.active == true && Trigger.Trig_performed == false){
AllDripsPerformed = false;
}//if
}//while
Serial.println(" ");
}//if(SysRunning ==1 && FirstDripTTG == true)
// by now, all drips that are active should have performed, so re-set ms_to_go and performed fields
for(i=0; i<=3; i++){
DripArray[i]->ms_to_go = 0;
DripArray[i]->ms_to_off = 0;
DripArray[i]->performed = false;
DripArray[i]->started = false;
}//for
// re set trigger variables
Trigger.ms_to_go = 0;
Trigger.ms_to_off = 0;
Trigger.Trig_Fired = false;
Trigger.Trig_performed = false;
// re set the sequence ...
FirstDripTTG = false;
AllDripsPerformed = false;
if(millis() >= WaitUntil){
Serial.flush();
Serial.println("tick");
FirstDripTTG = true;
WaitUntil = millis() + Sequence_Delay;
}//if
}//loop