Gestione uscite multiple

Ciao a tutti.
Ho la necessità di gestire con un arduino nano 8 relè collegati ad altrettante uscite digitali in maniera indipendente.

La cosa è piuttosto facile se non che non vorrei gestire ogni singola uscita ogni volta, esempio:

Ho 8 variabili (Stato1...Stato8) che in base al valore 0 o 1 che hanno deve accendersi la relativa uscita relè e mantenere spente o spegnere quelle accese non necessarie.

se (Stato1...Stato8)=0                          //tutte le uscite spente;
se Stato1=1 e (Stato2...Stato8)=0               //accendi 1 e spegni 2,3,4,5,6,7,8;
se Stato2=1 e (Stato1 e Stato3...Stato8)=0      //accendi 2 e spegni 1,3,4,5,6,7,8;
ecc...

come posso fare per gestire le uscite con gli array o con librerie più furbe che possano semplificare il codice, soprattutto come dimensione occupata in RAM e Mem Programma?

Al momento il mio codice di test, che gestisce anche altro oltre ai relè, occupa già l'82% di entrambe le locazioni di memoria... solo che è tutt'altro che finito, anzi...

P.S: non vorrei utilizzare shift register al momento, ma direttamente le uscite dell'MCU

Ciao, non capisco come deve essere il funzionamento, ovvero un solo relè attivo alla volta? o anche più di uno?
Se i relè sono indipendenti di basta utilizzare le una digitalWrite per ogni variabile, es:

digitalWrite(PIN_OUT1, Stato1);
digitalWrite(PIN_OUT2, Stato2);
...

Per ottimizzare puoi crearti un array dove memorizzi gli stati, Es.

byte stati[7]
for(byte i=0; i<8;i++){
  stati[i] = digitalRead(i); //Ipotizzando che le porte utilizzate sia da 0 a 7, altimenti adattare la digitalread
}

e poi,

for(byte i=0; i<8;i++){
  digitalWrite(i, stati[i]); //Stessa cosa detta per digitalread
}

Se invece la logica dei relè deve essere differente spiega meglio ciò che ti serve…
P.S = Il codice non è statio provato (ovviamente) è solo un esempio

Faccio un paio di esempi, sono stralci del codice che voglio aggiornare ad 8 uscite:

  // Test uscite
  lcd.setCursor(0, 2);
  lcd.print("|  Test Uscita: A  |");
  digitalWrite(POMPAA , HIGH);
  delay(1000);
  digitalWrite(POMPAA , LOW);
  delay(500);
  lcd.setCursor(0, 2);
  lcd.print("|  Test Uscita: B  |");
  digitalWrite(POMPAB , HIGH);
  delay(1000);
  digitalWrite(POMPAB , LOW);

In questo caso fa il test delle uscite ad intervalli di 1s. Volendo fare il test di 8 uscite indipendenti, a livello di codice sono diverse righe ed occupano spazio in memoria, magari una gestione ad array è più semplice da comprendere, più compatta ed occupa meno.

    // Aggiorna il display ed il funzionamento secondo il modo scelto

    int tempStato = LOW;
    if (FlagAllarme == 0 && ((CUmid == 0) || (CUmid == 1 && StopMin == 0 && StopMax == 0))) tempStato = HIGH;

    if (Modo == 2) {
      StatoA = 0;  // OFF
      digitalWrite(POMPAA, LOW);
      StatoB = 0;
      digitalWrite(POMPAB, LOW);
    }
    if (Modo == 3) {
      StatoA = 1;  // ON-A
      digitalWrite(POMPAA, tempStato);
      StatoB = 0;
      digitalWrite(POMPAB, LOW);
    }
    if (Modo == 4) {
      StatoA = 0;  // ON-B
      digitalWrite(POMPAA, LOW);
      StatoB = 1;
      digitalWrite(POMPAB, tempStato);

    }

In questo caso a seconda del Modo selezionato attiva la funzionalità AUTO, Uscita 1 ON, Uscita 2 ON.
Volendo espandere ad 8, potrei usare lo Switch() per il Modo e gestire un array di 0 ed 1 per attivare la giusta uscita in funzione del modo selezionato.

      // Controlla qual'e' il prossimo programma *del giorno* da eseguire, che e' il primo successivo al tempo attuale
      // che deve essere eseguito nel giorno attuale, oppure quello in corso di esecuzione
      // Funziona anche programmi sovrapposti sulle linee A e B (anche sovrapposte A1-A2 / B1/B2)
      // ed anche se gli orari non sono in ordine cronologico
      StatoA = 0;
      StatoB = 0;

      for (byte i = 0; i <= 3; i++)
      {
        // se il T attuale e' compreso in uno dei programmi, attiva le pompe se livello acqua e umidita' sono rispettati
        if ( GiornoInn[i][Giorno] == 1 && Durata[i] > 0 &&
             (hour() * 60 + minute()) >= (OrePrg[i] * 60 + MinutiPrg[i]) && (hour() * 60 + minute()) < (OrePrg[i] * 60 + MinutiPrg[i] + Durata[i]))
        {
          // Innaffia se il ctrl umidita'  e' OFF oppure e' ON e l'umidita'  rilevata e' nel range impostato
          // e rispetta le eventuali soglie d'isteresi
          if  ( FlagAllarme == 0 && ( (CUmid == 0) || (CUmid == 1 && StopMin == 0 && StopMax == 0) ) )
          {
            if (i < 2)  StatoA = 1;  else StatoB = 1;
          }
        }
      }

Questo è per il modo = 1 con 2 possibilità per ciascuna delle due uscite disponibili (array di 4 elementi, 2 per ciascuna uscita).
In questo caso vorrei avere una possibilità per ciascuna delle 8 uscite, quindi a parte modificare la gestione dell’array per 8 invece che 4:

for (byte i = 0; i <= 7; i++)

Come potrei gestire il terzo if con 8 uscite?

if (i < 2)  StatoA = 1;  else StatoB = 1;

Chiedo scusa per le molte domande, ma queste sezioni di codice non sono farina del mio sacco, per cui al momento le uso così come sono ma mi serve espandere il sistema (ed imparare anche come gestire in maniera furba le uscite).

usa i registri, il nano ha il micro 328 o il micro 168 che non hai specificato.

pablos:
usa i registri, il nano ha il micro 328 o il micro 168 che non hai specificato.
Arduino Reference - Arduino Reference

Ha il micro 328... guardo il link.

Grazie mille!

Ho provato la strada del Port Manipulation.
La soluzione sarebbe ideale (ho provato ad accendere 8 led in sequenza a seconda della pressione di un tasto e funziona alla grande), tuttavia ho un quesito da porvi:

se ad esempio ho le uscite sparse tra Port B e Port D, ma su D ho anche altre cose collegate e non spostabili (bus 1-Wire su D3, lettura frequenza su D5 e generazione ECHO SR-04 su D2) e su B pure (Lettura HC-04 su D8), come gestisco le uscite senza toccare questi pin (visto che sono da dichiarare nel setup tramite DDRx se sono IN o OUT)?

Alla fine sono riuscito a risolvere con un array:

#define MODOESC    17   // Pulsante Modalita  o Esc
#define TRUE		true
#define	FALSE		false

int Modo = 1;
int uscite[] = {4, 6, 7, 9, 10, 11, 12, 13}; //array pin uscite EV
int uscite_size = 8; //Dimensione array uscite

void setup()
{
  pinMode(MODOESC ,  INPUT); digitalWrite (MODOESC , HIGH);
  for (int EV = 0; EV < uscite_size; EV++) {
    pinMode(uscite[EV], OUTPUT);
  }
}

void loop()
{
  if (Check(MODOESC)) // Pressione tasto MODO: cambio modalita  funzionamento
  {
    Modo++;
    if (Modo > 9)
      Modo = 1;

    switch (Modo)
    {
      case 1: //All OFF
        resetta_uscite();
        break;
      case 2: //Uscita 1 (PD4)
        resetta_uscite();
        digitalWrite(uscite[0], HIGH);
        break;
      case 3: //Uscita 2 (PD6)
        resetta_uscite();
        digitalWrite(uscite[1], HIGH);
        break;
      case 4: //Uscita 3 (PD7)
        resetta_uscite();
        digitalWrite(uscite[2], HIGH);
        break;
      case 5: //Uscita 4 (PB1)
        resetta_uscite();
        digitalWrite(uscite[3], HIGH);
        break;
      case 6: //Uscita 5 (PB2)
        resetta_uscite();
        digitalWrite(uscite[4], HIGH);
        break;
      case 7: //Uscita 6 (PB3)
        resetta_uscite();
        digitalWrite(uscite[5], HIGH);
        break;
      case 8: //Uscita 7 (PB4)
        resetta_uscite();
        digitalWrite(uscite[6], HIGH);
        break;
      case 9: //Uscita 8 (PB5)
        resetta_uscite();
        digitalWrite(uscite[7], HIGH);
        break;
    }

  }

}

void resetta_uscite()
{
  for (int EV = 0; EV < uscite_size; EV++)
    digitalWrite(uscite[EV], LOW);
}

così lo spazio in memoria è leggermente superiore al Port Manipulation, ma mi lascia la flessibilità delle uscite:

Port Manipulation:

Lo sketch usa 1.344 byte (4%) dello spazio disponibile per i programmi. Il massimo è 32.256 byte.
Le variabili globali usano 11 byte (0%) di memoria dinamica, lasciando altri 2.037 byte liberi
per le variabili locali. Il massimo è 2.048 byte.

Array:

Lo sketch usa 1.506 byte (4%) dello spazio disponibile per i programmi. Il massimo è 32.256 byte.
Le variabili globali usano 29 byte (1%) di memoria dinamica, lasciando altri 2.019 byte liberi
per le variabili locali. Il massimo è 2.048 byte.

A questo punto chiedo:
c’è un modo per non impiegare lo switch() ed ottenere lo stesso risultato, al fine di alleggerire ancora lo spazio occupato?

#define MODOESC    17   // Pulsante Modalita  o Esc
#define TRUE		true
#define	FALSE		false

int Modo = 8;

int uscite[] = {4, 6, 7, 9, 10, 11, 12, 13}; //array pin uscite EV
int uscite_size = 8; //Dimensione array uscite

void setup()
{
  pinMode(MODOESC ,  INPUT); digitalWrite (MODOESC , HIGH);
  for (int EV = 0; EV < uscite_size; EV++) {
    pinMode(uscite[EV], OUTPUT);
  }
}

void loop()
{
  if (Check(MODOESC))
  {
    Modo++;
    if (Modo >= 9) Modo = 0;

    resetta_uscite();

    if (modo == 8 ) return;
        
    digitalWrite(uscite[Modo], HIGH);

  }

}

void resetta_uscite()
{
  for (int EV = 0; EV < uscite_size; EV++)
    digitalWrite(uscite[EV], LOW);
}