Più lavori con un solo sketch?

Non ho la possibilità di provarlo e trovare gli errori CHE SICURAMENTE CI SONO.
Però ho modificato il codice per farti vedere cosa intendevo, non bloccare il programma quando si aziona il motore, e fare lampeggiare, e la chiusura temporizzata attiva con un flag.

Sicuramente non funziona, ma per mostrarti il concetto e poi puoi correggere gli errori.

// ---- inizio cancello ---- //
//comandi
const int pulsante=5; //pulsante apertura cancello
const int finecorsa1=6; //finecorsa di chiusura
const int finecorsa2=7; //finecorsa d'apertura

//motore passo passo
const int IN1=8;
const int IN2=9;
const int IN3=10;
const int IN4=11;
const char tab1[] =
{
  0x01, 0x03, 0x02, 0x06, 0x04, 0x0c, 0x08, 0x09
};
const char tab2[] =
{
  0x01, 0x09, 0x08, 0x0c, 0x04, 0x06, 0x02, 0x03
};

//lampeggiante
#define Lamp_pin 12
const unsigned long tempo_commutazione = 500; // qui salvo e decido quanto tempo deve restare off/on il lampeggiante
unsigned long tempo = 0; // qui salvo l'istante di tempo nel quale è cambiato lo stato del lampeggiante
byte flag_led=false;
byte stato_led=LOW;
byte flag_chiusuraTemporizzata=1;
unsigned long attesa;
//stato del cancello
enum tipo_stato {CancelloChiuso=0, CancelloAperto=1};
tipo_stato stato=CancelloChiuso;

unsigned long previousMillis1 = 0;

unsigned long tempo_attesa = 2000;

// ---- fine cancello ---- //



void setup() 
{
  delay(1000);
  Serial.begin(9600);
  pinMode(pulsante, INPUT_PULLUP);
  pinMode(finecorsa1, INPUT_PULLUP);
  pinMode(finecorsa2, INPUT_PULLUP);
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
  pinMode(Lamp_pin, OUTPUT); //lampeggiante cancello
}

void loop() {
  GestioneCancello();
}


void GestioneCancello()
{
  if(millis() - previousMillis1 >= 16)// che senso ha attendere 16 millesecondi ?
  {
    previousMillis1 = millis();
    switch(stato)
    {
      case CancelloChiuso:
      if(digitalRead(pulsante)==LOW && digitalRead(finecorsa1)==LOW && digitalRead(finecorsa2)==HIGH)
      {
        stato=CancelloAperto;
        AvvioMotore();
      }
      
      break;

      case CancelloAperto:
      if(digitalRead(finecorsa1)==HIGH && digitalRead(finecorsa2)==LOW)
      {
         
        if(flag_chiusuraTemporizzata){
           attesa=millis();
           flag_chiusuraTemporizzata=0; // Chiamo la prima volta la funzione e acquisisco il tempo di     
        }
        
      }
      break;
    }
    
    
  }
  
  ChiusuraTemporizzata();
  
}

void ChiusuraTemporizzata(){
  if(f(flag_chiusuraTemporizzata) return;
  if(millis() - attesa >= tempo_attesa)//Attendo un intervallo di tempo e eseguo
        {
          //delay(3000);
          stato=CancelloChiuso;
          AvvioMotore();
          flag_chiusuraTemporizzata=1; // una volta chiuso il cancello ricalcolo il tempo 
        }
  
}

void LampCancello()
{
  if(!flag_led) return;
  if (millis() - tempo >= tempo_commutazione)
  {
    tempo = millis();
    digitalWrite(Lamp_pin,!digitalRead(Lamp_pin));
    
  }
}

void AvvioMotore()
{
  for (int j = 0; j < 1 ; j)
  {
    if (stato==0)
    {
      for (int i = 0; i < 8; i++)
      {
        //if(millis() - timer >= 1)
        //{
        digitalWrite(IN1, ((tab1[i] & 0x01) == 0x01 ? true : false));
        digitalWrite(IN2, ((tab1[i] & 0x02) == 0x02 ? true : false));
        digitalWrite(IN3, ((tab1[i] & 0x04) == 0x04 ? true : false));
        digitalWrite(IN4, ((tab1[i] & 0x08) == 0x08 ? true : false));
        GestioneCancello();
        LampCancello()
        if(digitalRead(finecorsa1)==LOW ){ // METTERE IL GIUSTO FINECORSA 
              StopMotore();
              return;
        
        }
        delay(1);
    //timer=millis();
        //}
      }
    }
    else
    {
      for (int i = 0; i < 8; i++)
      {
        //if(millis() - timer2 >= 1)
        //{
        digitalWrite(IN1, ((tab2[i] & 0x01) == 0x01 ? true : false));
        digitalWrite(IN2, ((tab2[i] & 0x02) == 0x02 ? true : false));
        digitalWrite(IN3, ((tab2[i] & 0x04) == 0x04 ? true : false));
        digitalWrite(IN4, ((tab2[i] & 0x08) == 0x08 ? true : false));
        GestioneCancello();
        LampCancello()
        if(digitalRead(finecorsa2)==LOW ){ // METTERE IL GIUSTO FINECORSA
              StopMotore();
              return;
        
        }
        delay(1);
    //timer2=millis();
        //}
      }
    }
    
    if(digitalRead(finecorsa1)==LOW && stato==0)
    {
      StopMotore();
      break;
    }

    if(digitalRead(finecorsa2)==LOW && stato==1)
    {
      StopMotore();
      break;
    }
    
  }
  
}

void StopMotore()
{
  digitalWrite(IN1, 0);
  digitalWrite(IN2, 0);
  digitalWrite(IN3, 0);
  digitalWrite(IN4, 0);
}

torn24:
Non ho la possibilità di provarlo e trovare gli errori CHE SICURAMENTE CI SONO.
Però ho modificato il codice per farti vedere cosa intendevo, non bloccare il programma quando si aziona il motore, e fare lampeggiare, e la chiusura temporizzata attiva con un flag.

Sicuramente non funziona, ma per mostrarti il concetto e poi puoi correggere gli errori.

Ottimo, sono riuscito ad ottenere il risultato da me cercato (sistemando gli errori del tuo codice e provando lo sketch corretto), ringrazio te e tutti voi che avete detto la vostra in questo topic.

Sketch finale:

//Comandi Cancello----------------------------------------------------//
const int pulsante = 5; //pulsante apertura cancello                  //
const int finecorsa1 = 6; //finecorsa di chiusura                     //
const int finecorsa2 = 7; //finecorsa d'apertura                      //
//--------------------------------------------------------------------//

//Motore Passo-Passo Cancello-----------------------------------------//
#define IN1 8                                                         //
#define IN2 9                                                         //
#define IN3 10                                                        //
#define IN4 11                                                        //
const char tab1[] = {0x01, 0x03, 0x02, 0x06, 0x04, 0x0c, 0x08, 0x09}; //
const char tab2[] = {0x01, 0x09, 0x08, 0x0c, 0x04, 0x06, 0x02, 0x03}; //
//--------------------------------------------------------------------//

//Lampeggiante Cancello-----------------------------------------------//
#define Lamp_pin 12 //Pin lampeggiante cancello                       //
#define tempo_commutazione 500 //Tempo di commutazione lampeggiante   //
unsigned long tempo = 0; //Quì salvo l'istante di tempo nel quale è cambiato lo stato del lampeggiante
byte flag_led = false;                                                //
byte stato_led = LOW;                                                 //
unsigned long attesa;                                                 //
//--------------------------------------------------------------------//

//Stato del Cancello--------------------------------------------------//
enum tipo_stato {CancelloChiuso = 0, CancelloAperto = 1};             //
tipo_stato stato = CancelloChiuso;                                    //
byte flag_chiusuraTemporizzata = 1;                                   //
#define tempo_attesa 2000                                             //
//--------------------------------------------------------------------//

void setup() 
{
  delay(1000);
  Serial.begin(9600);
  pinMode(pulsante, INPUT_PULLUP);
  pinMode(finecorsa1, INPUT_PULLUP);
  pinMode(finecorsa2, INPUT_PULLUP);
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
  pinMode(Lamp_pin, OUTPUT);
}

void loop() {
  GestioneCancello();
}

void GestioneCancello()
{
  switch(stato)
  {
    case CancelloChiuso:
    if(digitalRead(pulsante) == LOW && digitalRead(finecorsa1) == LOW && digitalRead(finecorsa2) == HIGH)
    {
      stato = CancelloAperto;
      AvvioMotore();
    }
    break;

    case CancelloAperto:
    if(digitalRead(finecorsa1) == HIGH && digitalRead(finecorsa2) == LOW)
    {
      if(flag_chiusuraTemporizzata)
      {
        attesa = millis();
        flag_chiusuraTemporizzata = 0;
      }
    }
    break;
  }
  ChiusuraTemporizzata();
}

void ChiusuraTemporizzata()
{
  if(flag_chiusuraTemporizzata) return;
  if(millis() - attesa >= tempo_attesa) //Attendo un intervallo di tempo ed eseguo
  {
    stato = CancelloChiuso;
    AvvioMotore();
    flag_chiusuraTemporizzata = 1; //Una volta chiuso il cancello ricalcolo il tempo 
  }
}

void LampCancello()
{
  if(!flag_led) return;
  if(millis() - tempo >= tempo_commutazione)
  {
    tempo = millis();
    digitalWrite(Lamp_pin, !digitalRead(Lamp_pin));
  }
}

void AvvioMotore()
{
  for (int j = 0; j < 1; j)
  {
    if (stato == 0)
    {
      for (int i = 0; i < 8; i++)
      {
        digitalWrite(IN1, ((tab1[i] & 0x01) == 0x01 ? true : false));
        digitalWrite(IN2, ((tab1[i] & 0x02) == 0x02 ? true : false));
        digitalWrite(IN3, ((tab1[i] & 0x04) == 0x04 ? true : false));
        digitalWrite(IN4, ((tab1[i] & 0x08) == 0x08 ? true : false));
        LampCancello();
        flag_led = true;
        if(digitalRead(finecorsa1) == LOW)
        {
          StopMotore();
          return;
        }
        delay(1);
      }
    }
    else
    {
      for (int i = 0; i < 8; i++)
      {
        digitalWrite(IN1, ((tab2[i] & 0x01) == 0x01 ? true : false));
        digitalWrite(IN2, ((tab2[i] & 0x02) == 0x02 ? true : false));
        digitalWrite(IN3, ((tab2[i] & 0x04) == 0x04 ? true : false));
        digitalWrite(IN4, ((tab2[i] & 0x08) == 0x08 ? true : false));
        GestioneCancello();
        LampCancello();
        flag_led = true;
        if(digitalRead(finecorsa2) == LOW)
        {
          StopMotore();
          return;
        }
        delay(1);
      }
    }
  }
}

void StopMotore()
{
  digitalWrite(IN1, 0);
  digitalWrite(IN2, 0);
  digitalWrite(IN3, 0);
  digitalWrite(IN4, 0);
  flag_led = false;
  if(Lamp_pin)
  {
    digitalWrite(Lamp_pin, LOW);
  }
}

Ho utilizzato un motore passo passo 28BYJ-48 (con ULN2003)
Funzionamento:
Apertura cancello scorrevole tramite apposito pulsante di marcia (solo quando il cancello è chiuso), arrivato alla fine della corsa attesa di secondi 2 e quindi chiusura del cancello, integrazione lampeggiante in fase di apertura/chiusura.

Ok, una soluzione è stata trovata. La logica però è piuttosto contorta, nel senso che ogni funzione è troppo vincolata al funzionamento delle altre (accoppiamento stretto), mi spiego meglio: e se adesso volessi comandare un altro cancello e un altro lampeggiante indipendenti dai primi?

Bisognerebbe inserire in ogni parte del programma esistente le chiamate per far funzionare la nuova parte, e nella nuova parte le chiamate per far funzionare la vecchia.

Immagina di comandare tre, quattro, cinque cancelli.... un'esplosione di chiamate a cose che non c'entrano strettamente con la logica locale, si commetterebbero errori, si potrebbero creare interferenze tra le varie parti che dovrebbero invece funzionare in modo parallelo e indipendente.

Il secondo, terzo, quarto cancello dovrebbero invece poter essere implementati senza alcuna modifica al codice del primo cancello/lampeggiante.

Il modo (quasi) giusto è quello del task LampCancello: lo si controlla con un flag, lo si comanda "dall'esterno".

Il modo totalmente giusto sarebbe chiamarlo solo dal mainloop e includere lo spegnimento del LED nel task stesso quando rileva flag_led == 0. A questo punto il lampeggiante diventa un oggetto totalmente indipendente, nessuna funzione si deve preoccupare di "chiamarlo", ma solo di comandarlo impostando il flag opportuno.

Ad esempio, senza fare modifiche troppo profonde LampCancello potrebbe diventare così:

void LampCancello()
{
  if(!flag_led)
  {
    tempo = millis();
    digitalWrite(Lamp_pin, LOW);
    return; 
  }
  if(millis() - tempo >= tempo_commutazione)
  {
    tempo = millis();
    digitalWrite(Lamp_pin, !digitalRead(Lamp_pin));
  }
}

Mettendo la chiamata nel loop principale questa sparirebbe da ogni parte di programma relativa al controllo motore (e cosa più importante dal controllo motore di qualsiasi altro cancello), ad esempio il tuo codice motore diventerebbe così (anche se, come per il lampeggiante, bisognerebbe riscriverlo per essere comandato e non chiamato):

void AvvioMotore()
{
  for (int j = 0; j < 1; j)
  {
    if (stato == 0)
    {
      for (int i = 0; i < 8; i++)
      {
        digitalWrite(IN1, ((tab1[i] & 0x01) == 0x01 ? true : false));
        digitalWrite(IN2, ((tab1[i] & 0x02) == 0x02 ? true : false));
        digitalWrite(IN3, ((tab1[i] & 0x04) == 0x04 ? true : false));
        digitalWrite(IN4, ((tab1[i] & 0x08) == 0x08 ? true : false));
        flag_led = true;
        if(digitalRead(finecorsa1) == LOW)
        {
          StopMotore();
          flag_led = false;
          return;
        }
        delay(1);
      }
    }
    else
    {
      for (int i = 0; i < 8; i++)
      {
        digitalWrite(IN1, ((tab2[i] & 0x01) == 0x01 ? true : false));
        digitalWrite(IN2, ((tab2[i] & 0x02) == 0x02 ? true : false));
        digitalWrite(IN3, ((tab2[i] & 0x04) == 0x04 ? true : false));
        digitalWrite(IN4, ((tab2[i] & 0x08) == 0x08 ? true : false));
        GestioneCancello();
        flag_led = true;
        if(digitalRead(finecorsa2) == LOW)
        {
          StopMotore();
          flag_led = false;
          return;
        }
        delay(1);
      }
    }
  }
}


void StopMotore()
{
  digitalWrite(IN1, 0);
  digitalWrite(IN2, 0);
  digitalWrite(IN3, 0);
  digitalWrite(IN4, 0);
}

Se ogni parte del programma viene scritta in questo modo, chiamata sempre e solo dal loop principale, e comandata tramite flag/variabili, allora ogni parte diventa un processo realmente indipendente dagli altri anche a livello di codice: se aggiungo il cancello N non devo mai andare a modificare ogni volta anche il codice di cancello-1 -2 -3 ecc. e nel codice del nuovo cancello non devo occuparmi del funzionamento degli altri.

Forse non ho capito bene io..
Il lampeggiante deve essere accoppiato con quel cancello, dev'essere tutto il cancello a dipendere da ogni sua funzione, non mi serve "liberare" la funzione del lampeggiante se comunque quel lampeggiante deve lampeggiare solo con quel cancello, ho provato a chiamare solo le funzioni del lampeggiante e gestionecancello direttamente nel loop, eliminando quindi la chiamata del lampeggiante dall'avviomotore, però non lampeggia allego quindi lo sketch in caso abbia indubbiamente sbagliato io

//Comandi Cancello----------------------------------------------------//
const int pulsante = 5; //pulsante apertura cancello                  //
const int finecorsa1 = 6; //finecorsa di chiusura                     //
const int finecorsa2 = 7; //finecorsa d'apertura                      //
//--------------------------------------------------------------------//

//Motore Passo-Passo Cancello-----------------------------------------//
#define IN1 8                                                         //
#define IN2 9                                                         //
#define IN3 10                                                        //
#define IN4 11                                                        //
const char tab1[] = {0x01, 0x03, 0x02, 0x06, 0x04, 0x0c, 0x08, 0x09}; //
const char tab2[] = {0x01, 0x09, 0x08, 0x0c, 0x04, 0x06, 0x02, 0x03}; //
//--------------------------------------------------------------------//

//Lampeggiante Cancello-----------------------------------------------//
#define Lamp_pin 12 //Pin lampeggiante cancello                       //
#define tempo_commutazione 500 //Tempo di commutazione lampeggiante   //
unsigned long tempo = 0; //Quì salvo l'istante di tempo nel quale è cambiato lo stato del lampeggiante
byte flag_led = false;                                                //
byte stato_led = LOW;                                                 //
unsigned long attesa;                                                 //
//--------------------------------------------------------------------//

//Stato del Cancello--------------------------------------------------//
enum tipo_stato {CancelloChiuso = 0, CancelloAperto = 1};             //
tipo_stato stato = CancelloChiuso;                                    //
byte flag_chiusuraTemporizzata = 1;                                   //
#define tempo_attesa 2000                                             //
//--------------------------------------------------------------------//

void setup() 
{
  delay(1000);
  Serial.begin(9600);
  pinMode(pulsante, INPUT_PULLUP);
  pinMode(finecorsa1, INPUT_PULLUP);
  pinMode(finecorsa2, INPUT_PULLUP);
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
  pinMode(Lamp_pin, OUTPUT);
}

void loop() {
  LampCancello();
  GestioneCancello();
}

void GestioneCancello()
{
  switch(stato)
  {
    case CancelloChiuso:
    if(digitalRead(pulsante) == LOW && digitalRead(finecorsa1) == LOW && digitalRead(finecorsa2) == HIGH)
    {
      stato = CancelloAperto;
      AvvioMotore();
    }
    break;

    case CancelloAperto:
    if(digitalRead(finecorsa1) == HIGH && digitalRead(finecorsa2) == LOW)
    {
      if(flag_chiusuraTemporizzata)
      {
        attesa = millis();
        flag_chiusuraTemporizzata = 0;
      }
    }
    break;
  }
  ChiusuraTemporizzata();
}

void ChiusuraTemporizzata()
{
  if(flag_chiusuraTemporizzata) return;
  if(millis() - attesa >= tempo_attesa) //Attendo un intervallo di tempo ed eseguo
  {
    stato = CancelloChiuso;
    AvvioMotore();
    flag_chiusuraTemporizzata = 1; //Una volta chiuso il cancello ricalcolo il tempo 
  }
}

void LampCancello()
{
  if(!flag_led)
  {
    tempo = millis();
    digitalWrite(Lamp_pin, LOW);
    return; 
  }
  if(millis() - tempo >= tempo_commutazione)
  {
    tempo = millis();
    digitalWrite(Lamp_pin, !digitalRead(Lamp_pin));
  }
}

void AvvioMotore()
{
  for (int j = 0; j < 1; j)
  {
    if (stato == 0)
    {
      for (int i = 0; i < 8; i++)
      {
        digitalWrite(IN1, ((tab1[i] & 0x01) == 0x01 ? true : false));
        digitalWrite(IN2, ((tab1[i] & 0x02) == 0x02 ? true : false));
        digitalWrite(IN3, ((tab1[i] & 0x04) == 0x04 ? true : false));
        digitalWrite(IN4, ((tab1[i] & 0x08) == 0x08 ? true : false));
        flag_led = true;
        if(digitalRead(finecorsa1) == LOW)
        {
          StopMotore();
          flag_led = false;
          return;
        }
        delay(1);
      }
    }
    else
    {
      for (int i = 0; i < 8; i++)
      {
        digitalWrite(IN1, ((tab2[i] & 0x01) == 0x01 ? true : false));
        digitalWrite(IN2, ((tab2[i] & 0x02) == 0x02 ? true : false));
        digitalWrite(IN3, ((tab2[i] & 0x04) == 0x04 ? true : false));
        digitalWrite(IN4, ((tab2[i] & 0x08) == 0x08 ? true : false));
        GestioneCancello();
        flag_led = true;
        if(digitalRead(finecorsa2) == LOW)
        {
          StopMotore();
          flag_led = false;
          return;
        }
        delay(1);
      }
    }
  }
}


void StopMotore()
{
  digitalWrite(IN1, 0);
  digitalWrite(IN2, 0);
  digitalWrite(IN3, 0);
  digitalWrite(IN4, 0);
}

Ok, di per sè la funzione LampCancello è a posto, il problema è che non viene più chiamata dal loop principale, perché se non si cambia anche la logica di tutto il resto, il loop si sposta "all'interno" delle funzioni che si richiamano l'un l'altra... mi accorgo solo ora che AvvioMotore, chiamata da GestioneCancello, a sua volta chiama di nuovo GestioneCancello, con il pericolo che si instauri una ricorsione tale da saturare la memoria e bloccare tutto (oltre al refuso del ciclo con la variabile j).

Riguardo al fatto se le sottofunzioni dipendenti dalla gestione cancello abbia senso chiamarle solo quando il cancello "è in funzione", vero, può avere senso, ma non bisogna bloccare il loop principale (che è il requisito fondamentale per portare avanti operazioni in parallelo, ed è il motivo per cui si è parlato di millis).

Comunque provando ad aggiungere il codice per controllare un secondo cancello e un secondo lampeggiante indipendenti dai primi, si capisce subito perché il programma strutturato così non permette alcuna modifica.

La funzione AvviaMotore monopolizza il tempo CPU, durante la durata della funzione il resto del codice non viene eseguito, se questo ti è chiaro puoi passare a cercare una soluzione.

Una soluzione hardware consiste nel l'impiego di un driver motor stepper il quale pensa lui a generare la sequenza di impulsi. Visto che sai già qual'è la sequenza corretta per fare girare il motore in un senso e nell'altro potresti pensare di usare un altro microcontrollore (es. ATtiny85) programmandolo in modo da creare un driver stepper.

Ovviamente impiegare un altro micro per svolgere solo quelle funzioni pare sprecato, allora perché non aggiungere anche la selezione della velocità e perché no anche una curva di velocità da spedire via seriale o I2C. Tuttavia nel tuo caso ti basta potere variare la velocità nelle vicinanze dei fine corsa, per farlo potresti usare un sensore di giri nel motore in questo modo il codice conoscerebbe la posizione del cancello e quando si avvicina ai battenti modifichi la velocità fino a fermarlo dopo tot giri.

Altra soluzione software richiede la conoscenza dei timer interni al micro, questi infatti possono generare delle IRQ (Interrupt Request) quando il contatore va in overflow o quando raggiunge un certo valore, ma è una soluzione complessa da implementare anche perché occorre la manipolazione diretta dei registri.

Potresti tentare di usare le lib di leo per chiamare la funzione che genera gli impulsi, ma questo limita la velocità di rotazione, magari trovi un compromesso con 1ms generi 4 impulsi, poi per poco meno di 1ms esegui il resto del codice nel loop.

Ciao.

In realtà rivedendo e aggiungendo nuove funzioni mi rendo conto che ancora non ho raggiunto lo scopo del mio lavoro, ho fatto il cancello, però non ho fatto eseguire 2 funzioni contemporaneamente nel loop.
Infatti ho provato ad aggiungere un sensore di temperatura nel loop che ogni 500ms (non tramite delay, ma tramite millis) mi restituisce un valore nel monitor seriale, questo valore viene "bloccato" quando il motore è in movimento, quindi colpa del delay(1) nell'AvvioMotore.
Effettivamente credo sia pure meglio richiamare tutte le funzioni (gestione cancello, lampeggiante, avvio motore e future implementazioni per il completamento del plastico) direttamente dal loop principale, però il problema rimane il delay del motore, non ho ancora capito come eliminare o bypassare questa procedura in modo semplice e veloce, ho provato con il millis, ma senza alcun risultato. Help me

... ti rimando al mio post #11.

NON esiste un sistema "semplice e veloce" quando si vuole costruire un sistema pseudo-multi-thread ...
... nel tuo caso, la strada più semplice, come ti ho già detto, è che ti studi BENE le "macchine a stati finiti" e definisci una serie di "stati" per il tuo sistema nonché gli "eventi" che fanno passare da uno stato all'altro.

Altre strade NON che ne sono ... ::slight_smile:

Guglielmo

gpb01:
... ti rimando al mio post #11.

NON esiste un sistema "semplice e veloce" quando si vuole costruire un sistema pseudo-multi-thread ...
... nel tuo caso, la strada più semplice, come ti ho già detto, è che ti studi BENE le "macchine a stati finiti" e definisci una serie di "stati" per il tuo sistema nonché gli "eventi" che fanno passare da uno stato all'altro.

Altre strade NON che ne sono ... ::slight_smile:

Guglielmo

Un esempio di "stati finiti" non c'è già nello sketch? switch(stato)? Se no allora consigliatemi una buona guida per come creare una macchina a stati finiti

Salvatore765:
Un esempio di "stati finiti" non c'è già nello sketch? switch(stato)?

Si, ma poi finisci per chiamare sottofunzioni con una logica non più a stati. Inoltre gli stati di un processo "cancello" sono di più, abbiamo almeno: chiuso, in apertura, aperto in conteggio, in chiusura. Lo si potrebbe anche voler fermare a metà e poi richiuderlo senza farlo arrivare a finecorsa. In un cancello reale si dovrebbe gestire anche una fotocellula e una barra anti collisione e quindi sarebbe normale avere cinque o più stati.

Gli attori principali (macchine a stati/processi) alla fine potrebbero essere solo tre:

                                                            .---->IN1
                      cmd_motor (0,1,2) ------------------  +---->IN2
                                   .-->| CONTROLLO MOTORE |-+---->IN3
               ----------------    |    ------------------  '---->IN4
 finecorsa1-->|                |---'
 finecorsa2-->| GEST. CANCELLO |
  pulsante--->|                |---.
               ----------------    |    ------------------------
                                   '-->| CONTROLLO LAMPEGGIANTE |--->Lamp_pin
                          fag_led (0,1) ------------------------

Ciascun processo:

  • può o deve avere una propria variabile di stato, una di tempo, eventuali contatori
  • ha come ingressi uno o più pin o variabili/flag
  • ha come uscite uno o più pin o variabili/flag
  • viene normalmente chiamato solo dal loop principale e non chiama nessuno
  • ad ogni ciclo del loop principale viene eseguito solo lo stato attivo di ciascun processo
  • lo stato attivo verifica se si è verificata una certa condizione, nel caso effettua un'operazione elementare ed eventualmente imposta un nuovo stato per il giro seguente.

Claudio_F:
Si, ma poi finisci per chiamare sottofunzioni con una logica non più a stati. Inoltre gli stati di un processo "cancello" sono di più, abbiamo almeno: chiuso, in apertura, aperto in conteggio, in chiusura. Lo si potrebbe anche voler fermare a metà e poi richiuderlo senza farlo arrivare a finecorsa. In un cancello reale si dovrebbe gestire anche una fotocellula e una barra anti collisione e quindi sarebbe normale avere cinque o più stati.

Gli attori principali (macchine a stati/processi) alla fine potrebbero essere solo tre:

                                                            .---->IN1

cmd_motor (0,1,2) ------------------  +---->IN2
                                  .-->| CONTROLLO MOTORE |-+---->IN3
              ----------------    |    ------------------  '---->IN4
finecorsa1-->|                |---'
finecorsa2-->| GEST. CANCELLO |
  pulsante--->|                |---.
              ----------------    |    ------------------------
                                  '-->| CONTROLLO LAMPEGGIANTE |--->Lamp_pin
                          fag_led (0,1) ------------------------




Ciascun processo:
- può o deve avere una propria variabile di stato, una di tempo, eventuali contatori
- ha come ingressi uno o più pin o variabili/flag
- ha come uscite uno o più pin o variabili/flag
- viene normalmente chiamato solo dal loop principale e non chiama nessuno
- ad ogni ciclo del loop principale viene eseguito solo lo stato attivo di ciascun processo
- lo stato attivo verifica se si è verificata una certa condizione, nel caso effettua un'operazione elementare ed eventualmente imposta un nuovo stato per il giro seguente.

Credo d'essere arrivato a qual cosa.. Ho metto le mie 4 funzioni tutte nel loop, nessuno chiama nessuno, cambiano semplicemente lo stato del flag, aggiungo quindi lo sketch appena finito e testato, funziona.
Ora.. continuo a sbagliare?

//Comandi Cancello----------------------------------------------------//
const int pulsante = 5; //pulsante apertura cancello                  //
const int finecorsa1 = 6; //finecorsa di chiusura                     //
const int finecorsa2 = 7; //finecorsa d'apertura                      //
//--------------------------------------------------------------------//

//Motore Passo-Passo Cancello-----------------------------------------//
#define IN1 8                                                         //
#define IN2 9                                                         //
#define IN3 10                                                        //
#define IN4 11                                                        //
const char tab1[] = {0x01, 0x03, 0x02, 0x06, 0x04, 0x0c, 0x08, 0x09}; //
const char tab2[] = {0x01, 0x09, 0x08, 0x0c, 0x04, 0x06, 0x02, 0x03}; //
//--------------------------------------------------------------------//

//Lampeggiante Cancello-----------------------------------------------//
#define Lamp_pin 12 //Pin lampeggiante cancello                       //
#define tempo_commutazione 500 //Tempo di commutazione lampeggiante   //
unsigned long tempo; //Quì salvo l'istante di tempo nel quale è cambiato lo stato del lampeggiante
byte flag_led = false;                                                //
byte stato_led = LOW;                                                 //
unsigned long attesa;                                                 //
//--------------------------------------------------------------------//

//Stato del Cancello--------------------------------------------------//
enum tipo_stato {CancelloChiuso = 0, CancelloAperto = 1};             //
tipo_stato stato = CancelloChiuso;                                    //
byte flag_chiusuraTemporizzata = 1;                                   //
byte flag_motore = false;                                             //
#define tempo_attesa 2000                                             //
//--------------------------------------------------------------------//

void setup() 
{
  delay(1000);
  Serial.begin(9600);
  pinMode(pulsante, INPUT_PULLUP);
  pinMode(finecorsa1, INPUT_PULLUP);
  pinMode(finecorsa2, INPUT_PULLUP);
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
  pinMode(Lamp_pin, OUTPUT); 
}

void loop()
{
  LampCancello();
  GestioneCancello();
  ChiusuraTemporizzata();
  AvvioMotore();
}

void GestioneCancello()
{
  switch(stato)
  {
    case CancelloChiuso:
    if(digitalRead(pulsante) == LOW && digitalRead(finecorsa1) == LOW && digitalRead(finecorsa2) == HIGH)
    {
      stato = CancelloAperto;
      flag_motore=true;
    }
    break;

    case CancelloAperto:
    if(digitalRead(finecorsa1) == HIGH && digitalRead(finecorsa2) == LOW)
    {
      if(flag_chiusuraTemporizzata)
      {
        attesa = millis();
        flag_chiusuraTemporizzata = 0;
      }
    }
    break;
  }
}

void ChiusuraTemporizzata()
{
  if(flag_chiusuraTemporizzata) return;
  if(millis() - attesa >= tempo_attesa) //Attendo un intervallo di tempo ed eseguo
  {
    stato = CancelloChiuso;
    flag_motore=true;
    flag_chiusuraTemporizzata = 1; //Una volta chiuso il cancello ricalcolo il tempo 
  }
}

void LampCancello()
{
  if(!flag_led)
  {
    tempo = millis();
    digitalWrite(Lamp_pin, LOW);
    return; 
  }
  if(millis() - tempo >= tempo_commutazione)
  {
    tempo = millis();
    digitalWrite(Lamp_pin, !digitalRead(Lamp_pin));
  }
}

void AvvioMotore()
{
  if(!flag_motore)
  {
    digitalWrite(IN1, 0);
    digitalWrite(IN2, 0);
    digitalWrite(IN3, 0);
    digitalWrite(IN4, 0);
    return;
  }
  if (stato == 0)
  {
    for (int i = 0; i < 8; i++) //Chiusura
    {
      digitalWrite(IN1, ((tab1[i] & 0x01) == 0x01 ? true : false));
      digitalWrite(IN2, ((tab1[i] & 0x02) == 0x02 ? true : false));
      digitalWrite(IN3, ((tab1[i] & 0x04) == 0x04 ? true : false));
      digitalWrite(IN4, ((tab1[i] & 0x08) == 0x08 ? true : false));
      flag_led = true;
      if(digitalRead(finecorsa1) == LOW)
      {
        flag_motore = false;
        flag_led = false;
        return;
      }
      delay(1);
    }
  }
  else
  {
    for (int i = 0; i < 8; i++)
    {
      digitalWrite(IN1, ((tab2[i] & 0x01) == 0x01 ? true : false));
      digitalWrite(IN2, ((tab2[i] & 0x02) == 0x02 ? true : false));
      digitalWrite(IN3, ((tab2[i] & 0x04) == 0x04 ? true : false));
      digitalWrite(IN4, ((tab2[i] & 0x08) == 0x08 ? true : false));
      flag_led = true;
      if(digitalRead(finecorsa2) == LOW)
      {
        flag_motore = false;
        flag_led = false;
        return;
      }
      delay(1);
    }
  }
}

Oooo, ecco, adesso ci siamo sul punto "far fare più cose". Siccome parte tutto dal mainloop è facile aggiungere altri controlli in parallelo senza toccare quanto già scritto.

Quei piccoli loop in AvvioMotore adesso non danno fastidio, potrebbero darlo se quel delay(1) dovesse essere più grande. In tal caso quei loop andrebbero riscritti per eseguirne un solo passo ad ogni chiamata del mainloop.

Il resto da dire riguarderebbe a questo punto solo la "distribuzione della logica" nelle varie parti, quindi non una questione di errori o malfunzionamenti, ma solo di design. Ad esempio la gestione potrebbe essere semplificata in questo modo:

void GestioneCancello()
{
    byte pressed = !digitalRead(pulsante);
    byte fca = !digitalRead(finecorsa2);
    byte fcc = !digitalRead(finecorsa1);

    switch (cancStat) 
    {
        case 0:  // fermo chiuso (stato iniziale)
            if (pressed && !fca) {
                cmd_motor = 1;  // direzione apertura
                flag_led = 1;   // avvia lampeggiante
                cancStat = 1;
            }
        break;    

        case 1:  // in apertura
            if (fca) {
                cmd_motor = 0;  // ferma motore
                cancTime = millis();
                cancStat = 2;
            }
        break;    

        case 2:  // fermo aperto
            if(millis() - cancTime >= TEMPO_ATTESA) {
                cmd_motor = 2;  // direzione chiusura
                cancStat = 3;
            }
        break;    

        case 3:  // in chiusura
            if (fcc) {
                cmd_motor = 0;  // ferma motore
                flag_led = 0;   // ferma lampeggiante
                cancStat = 4;
            }
        break;    

        case 4:  // attesa rilascio pulsante (se ancora premuto)
            if (!pressed)
                cancStat = 0;
        break;    

    }
}

Nota che tutta la logica di lettura dei sensori e comando è gestita qui, anche la funzione ChiusuraTemporizzata non è altro che uno stato del processo. A questo punto LampCancello e AvvioMotore diventano meri esecutori senza logica complessa interna (ovviamente AvvioMotore non deve più controllare lo stato di GestioneCancello ma solo il "comando" che gli viene dato).

Claudio_F:
Oooo, ecco, adesso ci siamo sul punto "far fare più cose". Siccome parte tutto dal mainloop è facile aggiungere altri controlli in parallelo senza toccare quanto già scritto.

Quei piccoli loop in AvvioMotore adesso non danno fastidio, potrebbero darlo se quel delay(1) dovesse essere più grande. In tal caso quei loop andrebbero riscritti per eseguirne un solo passo ad ogni chiamata del mainloop.

Il resto da dire riguarderebbe a questo punto solo la "distribuzione della logica" nelle varie parti, quindi non una questione di errori o malfunzionamenti, ma solo di design. Ad esempio la gestione potrebbe essere semplificata in questo modo:

void GestioneCancello()

{
    byte pressed = !digitalRead(pulsante);
    byte fca = !digitalRead(finecorsa2);
    byte fcc = !digitalRead(finecorsa1);

switch (cancStat)
    {
        case 0:  // fermo chiuso (stato iniziale)
            if (pressed && !fca) {
                cmd_motor = 1;  // direzione apertura
                flag_led = 1;  // avvia lampeggiante
                cancStat = 1;
            }
        break;

case 1:  // in apertura
            if (fca) {
                cmd_motor = 0;  // ferma motore
                cancTime = millis();
                cancStat = 2;
            }
        break;

case 2:  // fermo aperto
            if(millis() - cancTime >= TEMPO_ATTESA) {
                cmd_motor = 2;  // direzione chiusura
                cancStat = 3;
            }
        break;

case 3:  // in chiusura
            if (fcc) {
                cmd_motor = 0;  // ferma motore
                flag_led = 0;  // ferma lampeggiante
                cancStat = 4;
            }
        break;

case 4:  // attesa rilascio pulsante (se ancora premuto)
            if (!pressed)
                cancStat = 0;
        break;

}
}



Nota che tutta la logica di lettura dei sensori e comando è gestita qui, anche la funzione ChiusuraTemporizzata non è altro che uno stato del processo. A questo punto LampCancello e AvvioMotore diventano meri esecutori senza logica complessa interna (ovviamente AvvioMotore non deve più controllare lo stato di GestioneCancello ma solo il "comando" che gli viene dato).

Ottimo, cosi sembra molto più leggibile, chiedo scusa se c'è voluto un pò prima che capissi il meccanismo, ma ora posso sbizzarrirmi ad aggiungere qualsiasi altra parte del plastico.

Grazie a tutti voi che avete risposto, Salvatore