Non-Blocking sktech

As I wrote am newbe but I tried several times with no luck. I can do it using delays but no good solution it blocks other command.

      digitalWrite(Relay7, LOW);
      delay(10000);    
      digitalWrite(Relay6, LOW);
      delay(3000);
      digitalWrite(Relay7, HIGH);
      delay(10000);
      digitalWrite(Relay5, LOW);
      delay(3000);
      digitalWrite(Relay6, HIGH);
      delay(10000);
      digitalWrite(Relay4, LOW);
      delay(3000);
      digitalWrite(Relay5, HIGH);
      delay(10000);
      digitalWrite(Relay3, LOW);
      delay(3000);
      digitalWrite(Relay4, HIGH);
      delay(10000);
      digitalWrite(Relay2, LOW);
      delay(3000);
      digitalWrite(Relay3, HIGH);
      delay(10000);
      digitalWrite(Relay1, LOW);
      delay(3000);
      digitalWrite(Relay2, HIGH);
      delay(10000);
      digitalWrite(Relay1, HIGH);

Maybe your help will give me good start learning Multitasking. Teatcher :slight_smile:

Looks to me like your list of changes will...change. This is perhaps not amenable to a state machine, especially since you just want to execute it once, but rather a simple execution list, or step list, or sequence list(I've seen it called all three).
Think of it this way. Make a list of 'steps'. Each step consists of a relay number, a new state for that relay, and a duration to wait. Relays may be numbered 1 to 7, new state can be on or off, and duration can be 0 to whatever. When you need to make two or more relay changes without a delay following, just do them all in a row, but make the delay 0.
Set it up as a 2 D array:
7 0 10 // relay 7, on, wait 10 seconds
6 0 3 // relay 6, on, wait 3 seconds
7 1 10 // relay 7, off, wait 10 seconds
...etc
Now write a simple execution engine that runs through the array, row by row, turning relays on or off and waiting some period of time before the next step.
That way, you can see at a glance what's going on, and modify sequence or timing at will.
Make your last step a special one, make the relay number -1 or some other special flag, to tell you to simply drop into a loop that runs forever.
That's a whole lot easier than iffity iffity iffity, and probably a lot easier than a state machine to both conceptually understand, code, and modify.
If you have other actions to be done during this sequence, consider whether they can be encapsulated in the same way, with "relay" numbers like 100 to flag a different behaviour. That might require that you use a switch with the relay number as the case value, so each case can be made unique, but it's still a powerful way to run a small task. It's basically a simplified form of scripting.
C

So far a part of this code works fine but relay 1 keep turning on and off instead of keeping off , I cant figure this out, my purpose is to turn Relay1 on, wait 3s, turn Relay 8 on wait 10s, turn Relay 7 on wait 3 s turn Relay 8 off... Help guys

int RelayPompe = 2;
int RelayVanne7 = 3;
int RelayVanne6 = 4;
int RelayVanne5 = 5;
int RelayVanne4 = 6;
int RelayVanne3 = 7;
int RelayVanne2 = 8;
int RelayVanne1 = 9;

const unsigned long eventInterval1 = 10000;
const unsigned long eventInterval2 = 3000;
const unsigned long eventInterval3 = 15000;
const unsigned long eventInterval4 = 20000;
unsigned long previousTime1 = 0;
unsigned long previousTime2 = 0;
unsigned long previousTime3 = 0;
unsigned long previousTime4 = 0;

void setup() {
  Serial.begin(9600);
  pinMode(RelayPompe, OUTPUT);
  pinMode(RelayVanne1, OUTPUT);
  pinMode(RelayVanne2, OUTPUT);
  pinMode(RelayVanne3, OUTPUT);
  pinMode(RelayVanne4, OUTPUT);
  pinMode(RelayVanne5, OUTPUT);
  pinMode(RelayVanne6, OUTPUT);
  pinMode(RelayVanne7, OUTPUT);
  digitalWrite(RelayPompe, HIGH);
  digitalWrite(RelayVanne1, HIGH);
  digitalWrite(RelayVanne2, HIGH);
  digitalWrite(RelayVanne3, HIGH);
  digitalWrite(RelayVanne4, HIGH);
  digitalWrite(RelayVanne5, HIGH);
  digitalWrite(RelayVanne6, HIGH);
  digitalWrite(RelayVanne7, HIGH);
}
void loop() {
  unsigned long currentTime = millis();
  if (currentTime - previousTime1 >= eventInterval1) {
    digitalWrite(RelayPompe, LOW);
    previousTime1 = currentTime;
  }
  if (currentTime - previousTime2 >= eventInterval2) {
    digitalWrite(RelayVanne1, LOW);
    previousTime2 = currentTime;
  }
  if (currentTime - previousTime3 >= eventInterval3) {
    digitalWrite(RelayVanne2, LOW);
    previousTime3 = currentTime;
  }
  if (currentTime - previousTime4 >= eventInterval4) {
    digitalWrite(RelayVanne1, HIGH);
    previousTime4 = currentTime;
  }
}

Hello agili

Design an object by using a strucured array containing all relevant data about timing and output pin. A simple time controlled sequencer will operate the data as service.

Have a nice day and enjoy programming in C++ and learning.
Errors and omissions excepted.

Not: Errors and omissions accepted. :thinking:

What you have is a sequence of events. So you can create an array of events. Below code uses a struct to combine all information related to an event. A struct is like a record in a phone book with e.g. last name, first name and phone number.

// structure with information about a relay event
struct RELAY_EVENT  //
{
  const uint8_t pin;        // the pin that the relay is connected to
  const uint8_t pinState;   // the state that you want to set the pin to (LOW or HIGH)
  const uint32_t duration;  // the duration that we need to wait before continuing with the next relay event
  uint8_t smState;          // the state of the relay statemachine (0 = set pin state and start delay timer, 1 = wait for delay timer to time out)
};

The first 3 fields of the struct are constants and can't be changed by the code.

Next we can create an array of events as shown below

// array with relay events
RELAY_EVENT events[] = {
  { 3, HIGH, 10000UL, 0 },  // relay 7
  { 4, LOW, 5000UL, 0 },    // relay 5
  { 6, HIGH, 10000UL, 0 },  // relay 6
};

You will need to complete it; I don't know which pin numbers you use so I just picked a few.

Next you can set the pinMode of the relay pins in setup() by iterating through the events array.

void setup()  //
{
  Serial.begin(115200);
  // loop through the events to set the pin mode; note that they might be multiple times
  for (uint8_t cnt = 0; cnt < NUMELEMENTS(events); cnt++)  //
  {
    pinMode(events[cnt].pin, OUTPUT);
  }
}

NUMELEMENTS is a macro that you place near the top of your code

// macro to calculate the number of elements in any type of array (int, char, struct)
#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))

Note that if a pin occurs multiple times, pinMode() for that pin will be executed multiple times. It's a bit of a lazy approach but was the easier way to implement.

Now you can write a function that iterates through the events and execute two steps

  1. sets the pin to the desired state and starts a timer
  2. waits till the timer has timed out.
/*
  sequencer to control the relay sequence
  Returns:
    true if sequence is in progress
    false if sequence is completed (or aborted)
*/
bool sequencer() {
  static uint8_t activeEventNumber = 0;
  static uint32_t startTime;

  if (events[activeEventNumber].smState == 0)  //
  {
    digitalWrite(events[activeEventNumber].pin, events[activeEventNumber].pinState);
    startTime = millis();

    Serial.print(millis());
    Serial.print(F("\tStep: "));
    Serial.print(activeEventNumber);
    Serial.print(F(", smState = "));
    Serial.println(events[activeEventNumber].smState);

    events[activeEventNumber].smState++;
  }     //
  else  //
  {
    if (millis() - startTime >= events[activeEventNumber].duration)  //
    {
      Serial.print(millis());
      Serial.print(F("\tStep: "));
      Serial.print(activeEventNumber);
      Serial.print(F(", smState = "));
      Serial.print(events[activeEventNumber].smState);
      Serial.println(F(" timed out"));

      events[activeEventNumber].smState = 0;
      activeEventNumber++;
      if (activeEventNumber >= NUMELEMENTS(events)) {
        // if all step of the sequence are executed, return false to indicate that the sequencer is no longer in prograss
        return false;
      }
    }
  }
  // indicate that the sequencer is in progras.
  return true;
}

You have to keep on calling the function till it has done all events; at that point it will return false to indicate that it's no longer in progress.

A sequencer event starts with smState == 0; once that is done, the smState is set to 1 so the next tims that you call the function it will execute the 'delay'. Once the 'delay' is finished, the smState of the current sequencer event is set back to 0 and the active sequencer event is incremented.

Once the last event is finished, you have to reset the sequencer event and next indicate to the caller that the sequencer is no longer in progress.

And in loop() you can call it as shown below

void loop()  //
{
  if (sequencer() == false) {
    Serial.println(F("Sequence complete"));
    // hang forever
    for (;;)  //
    {
    }
  }
}

The below is a complete demonstration including reading a button.

// macro to calculate the number of elements in any type o array (int, char, struct)
#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))

// structure with information about a relay event
struct RELAY_EVENT  //
{
  const uint8_t pin;        // the pin that the relay is connected to
  const uint8_t pinState;   // the state that you want to set the pin to (LOW or HIGH)
  const uint32_t duration;  // the duration that we need to wait before continuing with the next relay event
  uint8_t smState;          // the state of the relay statemachine (0 = set pin state and start delay timer, 1 = wait for delay timer to time out)
};

// array with relay events
RELAY_EVENT events[] = {
  { 3, HIGH, 10000UL, 0 },  // relay 7
  { 4, LOW, 5000UL, 0 },    // relay 5
  { 6, HIGH, 10000UL, 0 },  // relay 6
};

// the button
const uint8_t buttonPin = 2;

void setup()  //
{
  Serial.begin(115200);
  // loop through the events to set the pin mode; note that they might be multiple times
  for (uint8_t cnt = 0; cnt < NUMELEMENTS(events); cnt++)  //
  {
    pinMode(events[cnt].pin, OUTPUT);
  }

  pinMode(buttonPin, INPUT_PULLUP);
}

void loop()  //
{
  if (sequencer() == false) {
    Serial.println(F("Sequence complete"));
    // hang forever
    for (;;)  //
    {
    }
  }

  if (digitalRead(buttonPin) == LOW) //
  {
    Serial.println(F("button is pressed"));
  }
}

/*
  sequencer to control the relay sequence
  Returns:
    true if sequence is in progress
    false if sequence is completed (or aborted)
*/
bool sequencer() {
  static uint8_t activeEventNumber = 0;
  static uint32_t startTime;

  if (events[activeEventNumber].smState == 0)  //
  {
    digitalWrite(events[activeEventNumber].pin, events[activeEventNumber].pinState);
    startTime = millis();

    Serial.print(millis());
    Serial.print(F("\tStep: "));
    Serial.print(activeEventNumber);
    Serial.print(F(", smState = "));
    Serial.println(events[activeEventNumber].smState);

    events[activeEventNumber].smState++;
  }     //
  else  //
  {
    if (millis() - startTime >= events[activeEventNumber].duration)  //
    {
      Serial.print(millis());
      Serial.print(F("\tStep: "));
      Serial.print(activeEventNumber);
      Serial.print(F(", smState = "));
      Serial.print(events[activeEventNumber].smState);
      Serial.println(F(" timed out"));

      events[activeEventNumber].smState = 0;
      activeEventNumber++;
      if (activeEventNumber >= NUMELEMENTS(events)) {
        // if all step of the sequence are executed, return false to indicate that the sequencer is no longer in prograss
        return false;
      }
    }
  }
  // indicate that the sequencer is in progras.
  return true;
}

This should give you the idea how you can implement a non-blocking sequence of events and you can basically build on that.

@ sterretje many thanks I will give it a try.

sterretje
Hopeless, getting tired of it, nothing works.

In an hour or so, I may be able to post a solution for my earlier suggestion, which you ignored. Want it?
C

Is that with the sketch that I provided? I assume that you adjusted the pin numbers to suite your needs and that you have wired a button between pin and GND. You do understand that once the sequence is complete, the demo code hangs forever and there will be no reaction on a button press?

You will have to give a better description than "nothing works". I also suggest that you post your full sketch.

This is the output that I get

0	Step: 0, smState = 0
10000	Step: 0, smState = 1 timed out
10000	Step: 1, smState = 0
15000	Step: 1, smState = 1 timed out
15000	Step: 2, smState = 0
25000	Step: 2, smState = 1 timed out
Sequence complete

And if I use a wire to simulate a button

0	Step: 0, smState = 0
button is pressed
...
...
button is pressed
10000	Step: 0, smState = 1 timed out
10000	Step: 1, smState = 0
15000	Step: 1, smState = 1 timed out
15000	Step: 2, smState = 0
button is pressed
...
...
button is pressed
25000	Step: 2, smState = 1 timed out
Sequence complete

Hello agili

I have found a sequencer in my Sketchbox for used sketches that you can easily adapt to the requirements in your project.

/* BLOCK COMMENT
  ATTENTION: This Sketch contains elements of C++.
  https://www.learncpp.com/cpp-tutorial/
  Many thanks to LarryD
  https://europe1.discourse-cdn.com/arduino/original/4X/7/e/0/7e0ee1e51f1df32e30893550c85f0dd33244fb0e.jpeg
  https://forum.arduino.cc/t/non-blocking-sktech/1013571
  Tested with Arduino: Mega[x] - UNO [ ] - Nano [ ]
*/
#define ProjectName "Non-Blocking sktech"
// VARIABLE DECLARATION AND DEFINITION
unsigned long currentTime;
#define HardwareTest
#define StartMenue
constexpr  unsigned long HardwareTestTime {1000};
enum TimerCtrl {Off, On};
enum Steps {One, Two, Three, Four, Five};
constexpr byte LedPins[] {8, 9, 10, 11, 12};
constexpr  unsigned long StepTime [] {1000, 1000, 1000, 1000, 1000, 1000};
// -- Forward declaration
void dummy(int);
// -- objects -----------------------------------------
struct TIMER
{ // has the following members
  const unsigned long duration;       // memory for interval time
  unsigned long stamp;                // memory for actual time
  int onOff;                          // control for stop/run
};
struct RELAYTIMER
{ // has the following members
  Steps ident;                        // name of step
  const byte pin;                     // port pin
  void (*task)(int);                  // processing
  TIMER time2go;                      // see timer struct
};
RELAYTIMER relayTimers []
{
  {One, LedPins[One], dummy, StepTime[One], 0, false},
  {Two, LedPins[Two], dummy, StepTime[Two], 0, false},
  {Three, LedPins[Three], dummy, StepTime[Three], 0, false},
  {Four, LedPins[Four], dummy, StepTime[Four], 0, false},
  {Five, LedPins[Five], dummy, StepTime[Five], 0, false},
};
constexpr int sizeOfSequencer (sizeof(relayTimers) / sizeof(relayTimers[0]) - 1);
// ------------------- Services --------------------------------------
void startSequencer( int sequenceNumber)
{
  relayTimers[sequenceNumber].time2go.stamp = currentTime;
  relayTimers[sequenceNumber].time2go.onOff = On;
  digitalWrite(relayTimers[sequenceNumber].pin, HIGH);
}
void dummy(int ident)
{
  Serial.print(F("Step=")); Serial.println(ident);
}
// -------------------------------------------------------------------
void setup()
{
  Serial.begin(9600);
#ifdef StartMenue
  Serial.println(F("."));
  Serial.print(F("File   : ")), Serial.println(__FILE__);
  Serial.print(F("Date   : ")), Serial.println(__DATE__);
  Serial.print(F("Time   : ")), Serial.println(__TIME__);
  Serial.print(F("Project: ")), Serial.println(ProjectName);
#endif
  pinMode (LED_BUILTIN, OUTPUT);  // used as heartbeat indicator
  //  https://www.learncpp.com/cpp-tutorial/for-each-loops/
  for (auto relayTimer : relayTimers) pinMode(relayTimer.pin, OUTPUT);
#ifdef HardwareTest
  // check outputs
  Serial.print(F(" hardware test "));
  for (auto relayTimer : relayTimers) digitalWrite(relayTimer.pin, HIGH), Serial.print(F(".")), delay(HardwareTestTime);
  for (auto relayTimer : relayTimers) digitalWrite(relayTimer.pin, LOW), Serial.print(F(".")), delay(HardwareTestTime);
  Serial.println(F(" done"));
#endif
  currentTime = millis();
  startSequencer(One);
}
void loop ()
{
  currentTime = millis();
  digitalWrite(LED_BUILTIN, (currentTime / 500) % 2);
  for (auto &relayTimer : relayTimers)
  {
    if (currentTime - relayTimer.time2go.stamp >= relayTimer.time2go.duration && relayTimer.time2go.onOff)
    {
      relayTimer.time2go.onOff = Off;
      digitalWrite(relayTimer.pin, LOW);
      int nextStep = 1;
      relayTimer.ident == sizeOfSequencer ? nextStep = One : nextStep += relayTimer.ident;
      relayTimers[nextStep].time2go.onOff = On;
      relayTimers[nextStep].time2go.stamp = currentTime;
      digitalWrite(relayTimers[nextStep].pin, HIGH);
      relayTimers[nextStep].task(nextStep);
    }
  }
}

Have a nice day and enjoy programming in C++ and learning.

@ sterretje this is the sketch I tryied but it turns all relays on and then turning off one after other considering defined delays and thats not what I wanted, I tryied regulating order with no luck, I wanted : relay2 on first then relay1 on 10s, relay3 on 3s, relay2 off, 10s relay4 on, 3s relay3 off...until the laste relay8: this is the sketch:

#define NUMELEMENTS(x) (sizeof(x) / sizeof(x[0]))

// structure with information about a relay event
struct RELAY_EVENT  //
{
  const uint8_t pin;        // the pin that the relay is connected to
  const uint8_t pinState;   // the state that you want to set the pin to (LOW or HIGH)
  const uint32_t duration;  // the duration that we need to wait before continuing with the next relay event
  uint8_t smState;          // the state of the relay statemachine (0 = set pin state and start delay timer, 1 = wait for delay timer to time out)
};

// array with relay events
RELAY_EVENT events[] = {   

  { 2, HIGH, 5000UL, 0 },  // relay 7
  { 3, HIGH, 10000UL, 0 },  // relay 6
  { 4, HIGH, 5000UL, 0 },  // relay 5
  { 5, HIGH,  10000UL, 0 },    // relay 4
  { 6, HIGH, 5000UL, 0 },  // relay 3
  { 7, HIGH, 10000UL, 0 },  // relay 2
  { 8, HIGH, 5000UL, 0 },  // relay 1
  { 9, HIGH, 5000UL, 0 },  // relay 1
  
};

// the button
const uint8_t buttonPin = 12;

void setup()  //
{
  Serial.begin(115200);
  // loop through the events to set the pin mode; note that they might be multiple times
  for (uint8_t cnt = 0; cnt < NUMELEMENTS(events); cnt++)  //
  {
    pinMode(events[cnt].pin, OUTPUT);
  }

  pinMode(buttonPin, INPUT_PULLUP);
}

void loop()  //
{
  if (sequencer() == false) {
    Serial.println(F("Sequence complete"));
    // hang forever
    for (;;)  //
    {
    }
  }

  if (digitalRead(buttonPin) == LOW) //
  {
    Serial.println(F("button is pressed"));
    sequencer();
  }
}
bool sequencer() {
  static uint8_t activeEventNumber = 0;
  static uint32_t startTime;

  if (events[activeEventNumber].smState == 0)  //
  {
    digitalWrite(events[activeEventNumber].pin, events[activeEventNumber].pinState);
    startTime = millis();

    Serial.print(millis());
    Serial.print(F("\tStep: "));
    Serial.print(activeEventNumber);
    Serial.print(F(", smState = "));
    Serial.println(events[activeEventNumber].smState);

    events[activeEventNumber].smState++;
  }     //
  else  //
  {
    if (millis() - startTime >= events[activeEventNumber].duration)  //
    {
      Serial.print(millis());
      Serial.print(F("\tStep: "));
      Serial.print(activeEventNumber);
      Serial.print(F(", smState = "));
      Serial.print(events[activeEventNumber].smState);
      Serial.println(F(" timed out"));

      events[activeEventNumber].smState = 0;
      activeEventNumber++;
      if (activeEventNumber >= NUMELEMENTS(events)) {
        // if all step of the sequence are executed, return false to indicate that the sequencer is no longer in prograss
        return false;
      }
    }
  }
  // indicate that the sequencer is in progras.
  return true;
}

camsysca, I did not ignore you, I dont ignore people spending time and attention to help me bro I appreciate all kind of help in this great forum.

@agili
...but you don't want it? Ok. You've enough help from others. No problem.
C

@ camsysca
ofcourse I want it.

ok. I will provide later this morning, when I get to the machine it was created on. Last night would have been easy, today isn't, but I'll try to get to it.
C

camsysca ok great I will be waiting. Thanks

paulpaulson thank you your sketch does turn on and off relays regularly but is not what I Need. My project should do as following: |R2 ON| 3s| R1 ON| 10s| R3 ON| 3s| R2 OFF| 10s| R4 On| 3s| R3 OFF| 10s| R5 ON| 3s| R4 OFF| 10s| R6 ON| 3s| R5 OFF| 10s| R7 ON| 3s| R6 OFF|10s| R8 ON|3s| R7 OFF| 10s| R1 OFF| 3s| R8 OFF|. No need for loop just only one button is pushed.

@ sterretje
My project should do as following: |R2 ON| 3s| R1 ON| 10s| R3 ON| 3s| R2 OFF| 10s| R4 On| 3s| R3 OFF| 10s| R5 ON| 3s| R4 OFF| 10s| R6 ON| 3s| R5 OFF| 10s| R7 ON| 3s| R6 OFF|10s| R8 ON|3s| R7 OFF| 10s| R1 OFF| 3s| R8 OFF|. No need for loop just only one button is pushed.

You seem extremely desperate. I think the sketch in post #32 is made to repeat the timing cycle with this line

relayTimer.ident == sizeOfSequencer ? nextStep = One : nextStep += relayTimer.ident;

Try commenting it out.