all in all you are asking for a pretty complex functionality.
The code to do all this requires:
-
a lot of programming-knowledge and a lot of programming-experience
if you want to have the code in the most compact and most elegant way -
a lot of lines of code
if you want to have the code written in a way that is easy to understand
I have thrown out the struct because I'm using long but self-explaining names
With the long variable-names adding
mySolenoids.
in front of each variable makes things much less readable = much less understandable
It would only become "readable" again with shorter names.
But as long as you only have a little programming-experience the sjort names make it hard to understand the code
conclusion: => not using a struct
For easier understanding I still added a lot of comments
So here is a first rough sketch of how it can be coded with this approach
I have to do other work now
If you want to learn more about non-blocking timing, whic is used here a lot
read this tutorial
and to learn about step-chains ( state-machines) you can read here
#define NUM_SOLENOIDS 10 // using one more array-element to start indexes with 1 instead of zero
const byte BTN_PIN = 10;
const unsigned long PULSE_LENGTH = 50;
// declaring the timing-variables
// assigning values is MISSING and must be added
unsigned long PulsingStarts[NUM_SOLENOIDS]; // The time this solenoid is first active
unsigned long PulsingStops[NUM_SOLENOIDS]; // The time this solenoid becomes inactive
unsigned long PulsingStartsPausing[NUM_SOLENOIDS]; // The time this solenoid shall START the pausing
unsigned long PulsingResumes[NUM_SOLENOIDS]; // The time this solenoid shall resume pulsing again
unsigned long minWaitTimeBeforeNextPulse[NUM_SOLENOIDS]; // The minimum time the next pulse is generated
unsigned long maxWaitTimeBeforeNextPulse[NUM_SOLENOIDS]; // The maximum time the next pulse is generated
unsigned long WaitingIntervalUntilNextPulse[NUM_SOLENOIDS]; // The time that has to pass by until next pulse is created
unsigned long ShortPulseTimer[NUM_SOLENOIDS]; // variable used for creating the short pulses with non-blocking timing
unsigned long NextPulseTimer[NUM_SOLENOIDS]; // variable used for non-blocking timing of when the next short-pulse shall be created
uint8_t pin[NUM_SOLENOIDS];
const byte sc_idling = 0;
const byte sc_Sol1_pulsing = 1;
const byte sc_Sol12_pulsing = 2;
const byte sc_Sol1To3_pulsing = 3;
const byte sc_Sol1To4_pulsing = 4;
const byte sc_Sol1To5_pulsing = 5;
const byte sc_Sol1To6_pulsing = 6;
const byte sc_Sol1To7_pulsing = 7;
const byte sc_Sol1To8_pulsing = 8;
const byte sc_Sol1To9_pulsing = 9;
byte stepNo = sc_idling;
unsigned long SequenceStarted;
unsigned long PulsingStarted[NUM_SOLENOIDS];
unsigned long PausingStarted[NUM_SOLENOIDS];
unsigned long PulsingResumes[NUM_SOLENOIDS];
unsigned long PulsingStops[NUM_SOLENOIDS];
unsigned long TimeToStartOperationMode[NUM_SOLENOIDS];
// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - startOfPeriod >= TimePeriod ) {
// more time than TimePeriod has elapsed since last time if-condition was true
startOfPeriod = currentMillis; // a new period starts right here so set new starttime
return true;
}
else return false; // actual TimePeriod is NOT yet over
}
void mySequenceStepChain() {
static unsigned long CurrentMillis = millis();
switch (StepNo) {
case sc_idling:
if (digitalRead(BTN_PIN) == pressed) {
SequenceStarted = CurrentMillis; // store snapshot of time when sequence BEGINS
PulsingStarts[1] = CurrentMillis; // store snapshot of time when pulsing of solenoid 1 starts
NextPulseTimer[1] = CurrentMillis; // initialise Timer-variable that is used for non-blocking timing to create the short pulses with actual time
digitalWrite(pin[1], HIGH); //
ShortPulseTimer[1] = CurrentMillis; // store snapshot of time when IO-pin is switched to HIGH
// create randomised waiting time
WaitingIntervalUntilNextPulse[1] = random(minWaitTimeBeforeNextPulse[1], maxWaitTimeBeforeNextPulse[1]);
StepNo = sc_Sol1_pulsing;
}
break; // immidiately jump down to END-OF-SWITCH
case sc_Sol1_pulsing:
if ( TimePeriodIsOver(NextPulseTimer[1], WaitingIntervalUntilNextPulse[1]) ) {
// if that amount of milliseconds HAVE passed by that shall be waited until
// the next short-pulse shall be created
digitalWrite(pin[1], HIGH); // create next short-pulse
ShortPulseTimer[1] = CurrentMillis; // store snapshot of time when IO-pin is switched to HIGH
WaitingIntervalUntilNextPulse[1] = random(minWaitTimeBeforeNextPulse[1], maxWaitTimeBeforeNextPulse[1]);
}
if ( TimePeriodIsOver(ShortPulseTimer[1], PULSE_LENGTH) ) { // check if it is time to finish the short pulse
// if it is time to finish short-pulse
digitalWrite(mySolenoids[1].pin, LOW); // switch IO-pin to LOW
// create next WaitingInterval that shall be waited until the next short pulse is created
WaitingIntervalUntilNextPulse[1] = random(minWaitTimeBeforeNextPulse[1], maxWaitTimeBeforeNextPulse[1]);
}
// check if it is time to switch to the next mode of operation
if ( CurrentMillis - SequenceStarted >= TimeToStartOperationMode[2] ) {
StepNo = sc_Sol12_pulsing;
}
break; // immidiately jump down to END-OF-SWITCH
case :
break; // immidiately jump down to END-OF-SWITCH
case :
break; // immidiately jump down to END-OF-SWITCH
case :
break; // immidiately jump down to END-OF-SWITCH
case :
break; // immidiately jump down to END-OF-SWITCH
case :
break; // immidiately jump down to END-OF-SWITCH
case :
break; // immidiately jump down to END-OF-SWITCH
case :
break; // immidiately jump down to END-OF-SWITCH
case :
break; // immidiately jump down to END-OF-SWITCH
}
}
void setup() {
Serial.begin(115200);
pinMode( BTN_PIN, INPUT_PULLUP );
for (uint8_t i = 0; i < NUM_SOLENOIDS; i++) {
pinMode(mySolenoids[i].pin, OUTPUT);
}
}
void loop() {
mySequenceStepChain();
}
best regards Stefan