C'e modo di "Sincronizzare" l'avvio dei programmi LOOP di 3 Arduino Uno??

Ciao a tutti,
Con TRE diversi Arduino UNO, ho realizzato le sequenze di giochi di luce per un presepe della chiesa. :slight_smile:
Arduino "A" Gestisce stiscia LED digitale WS2812 x simulare sole su alba/tramonto.
Arduino "B" Gestisce con le sue uscite digitali, 11 RELE per comandare lampadine singole a 12Vcc
Arduino "C" Gestisce 5 MOSFET IRF520 in PWM per fare solo il fading IN/OUT della luce BIANCA GIORNO e BLU NOTTE su Strip Led singolo colore.

Singolarmente i tre arduini fanno il loro dovere!
Per NON impostare i vari tempi e ritardi di intervento con i DELAY!!! volevo "sincronizzarli".
Con I2C mi sembra molto complesso, oltre che aver letto che il cavo deve essere mooolto corto!!!) Distanza dei miei:DUE vicini 30cm max, e il terzo a 1,5 metri.

DOMANDA nr.1:
Se usassi il "Delay" nei tre vari programmi, dovrei riuscire a fare compiere le azioni al momento giusto, MA C'E' un modo di fare partire i 3 arduini insieme, inteso come tempo di partenza dei tre LOOP?
Non mi è chiaro se il tempo di avvio di Arduino e quindi del loop, dipende da quanto è lungo il SETUP / settings preliminari e/o lunghezza programma???

I Tre Arduino sono accesi insieme, dal pulsante che da corrente ad un rele/contatore (Da eettricista per la "luce Scale" ) che da corrente al trasformatore 220V-12Vcc che alimenta tutti gli arduini e i vari dispositivi...

DOMANDA nr.2:
Con un ARDUINO MEGA, riuscirei a gestire tutti le uscite (11 rele + 2 strisce WS2812 + 5 MOSFET)
Come potrei, in questo, caso mischiare i vari tempi di interveno? cioè accendere i rele MENTRE sta facendo il fading via Mosfet PWM o MENTRE sta inviando dati alla striscia digitale???

Grazi1000 in anticipo.

ciao...

se non ho capito male nei tuoi tre programmi singoli hai usato delay()...bene (male) mi sa che dovrai rivedere parte di questi programmi in quanto, per come la vedo io, quando hai più arduino da coordinare devi implementare uno "scambio" di informazioni...puoi farlo tramite seriale o tramite I/O fisici...di solito uno farà da Master, cioè da il tempo, mentre gli altri da slave, cioè eseguiranno quanto comandato dal master...e quindi da evitare il delay() in quanto si deve rispondere ad "eventi" nel giusto tempo.

se invece vuoi fare tutto con un unico arduino anche qui devi rimuovere i delay() e gestire il tutto tramite millis()

Dovresti allegare almeno uno degli sketch per capire se si può con poche modifiche e suggerimenti permetterti di raggiungere il tuo obbiettivo. Quasi certamente c'è il modo di usare una sola scheda, poi con la MEGA ovviamente si hanno più pin a disposizione. Per evitare i delay solitamente si usa una macchina a stati finiti (per semplicità basata sul "comando" switch (stato) { case n }. Purtroppo le macchine a stati non sono così intuitive, per cui ti dovrai impegnare un poco. Puoi leggere qualcosa qui, ma anche cercando nel forum o su google "macchina a stati finiti".

Andiamo alle domande, sono due, ma ne vedo di più.

Si possono sincronizzare tre arduino UNO? Si si può
Tuttavia ci deve essere un motivo tecnicamente valido che giustifichi l'impiego di tre arduino anziché uno solo.
Una centralina effetti giorno notte non richiede più di uno arduino, se arduino UNO non basta si può passare alla MEGA, oppure impiegare dei circuiti chiamati expander per aumentare il numero di pin. La stessa espansione si può fare anche con la MEGA, qualora fosse necessario.

MA C'E' un modo di fare partire i 3 arduini insieme, inteso come tempo di partenza dei tre LOOP?

C'è più di un modo, ma come dici tu, è complesso, sia che scegli I2C, seriale SPI o seriale USART ti dovrai scontrare con la complessità, se proprio è necessario la si affronta e in qualche modo si risolve. Per il momento la vedo come una complicazione.

Non mi è chiaro se il tempo di avvio di Arduino e quindi del loop, dipende da quanto è lungo il SETUP / settings preliminari e/o lunghezza programma???

Il dubbio ti è venuto però, per toglierti il dubbio puoi provare a stampare il valore restituito da millis(), il quale rappresenta il tempo espresso in millesimi di secondo da quando arduino è stato alimentato (o "resettato").
Vedrai che più cose metti nel setup (ma in genere anche nel loop) maggiore sara il valore restituito da millis().

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  // commenta o decomenta le Serial.println
  // Serial.println("xxxxxxxxxxxxxxxx"); // comando commentato
  // Serial.println("xxxxxxxxxxxxxxxx"); //          "
  Serial.println("xxxxxxxxxxxxxxxx");
  Serial.println("xxxxxxxxxxxxxxxx");
  Serial.println(millis());
}

void loop() {
  // put your main code here, to run repeatedly:

}

Con un ARDUINO MEGA, riuscirei a gestire tutti le uscite (11 rele + 2 strisce WS2812 + 5 MOSFET)

Potresti anche con la UNO, usando un expander, ma ovviamente con la MEGA avrai maggiori possibilità di espansione future. Ora suppongo che la MEGA tu non l'hai, ma hai tre uno e penso che bastino e avanzino.

Come potrei, in questo, caso mischiare i vari tempi di interveno? cioè accendere i rele MENTRE sta facendo il fading via Mosfet PWM o MENTRE sta inviando dati alla striscia digitale???

Io userei una macchina a stati finiti e le temporizzazione li gestisci con millis().

Ok, la sparo grossa, 3 arduino li hai per cui vediamo gli sketch e anziché sincronizzare i loop, facciamo che arduino A è il MASTER connesso agli altri due che sono gli SLAVE. Gli SLAVE non possono impegnare (chiamare il master) il bus dati (qualunque esso sia), gli SLAVE possono rispondere al MASTER se da questo sono interrogati. In sostanza chi dirige è il master. I2C è il canale fisico da scartare per i motivi che hai brevemente elencato (la lunghezza). SPI potrebbe anche farcela, ma la scarto, USART (la Serial.print in sostanza) c'è la fa ed è anche possibile creare una rete multimaster e c'è anche una libreria che potrebbe aiutare, tuttavia la distanza è un nemico di questi mezzi di comunicazione si potrebbe scoprire che non è abbastanza robusto e in tal caso c'è anche una alternativa (anche più), ad esempio cerca RS485 arduino, troverai dei transceiver che ti permetto di collegare in rete più device con sicurezza anche oltre i cento metri di distanza.

Tutte queste considerazione senza avere visto un pezzo di codice non ci permettono di fare una valutazione completa, l'obbiettivo dovrebbe essere quello di ottenere il risultato desiderato con il minor esborso possibile e con l'impegno commisurato alle tue competenze attuali, o poco più. Quindi capirai che mi sono sbilanciato molto nel risponderti.

Ciao.

Anch'io credo che basti un arduino solo e al massimo un paio di shift register

Però capisco che tu avendo già cablato il tutto sia restio a ripartire da zero

Viene facile sincronizzare le 'partenze' dei vari effetti se un solo arduino fa da master

Ad ogni start il master alza una uscita, e tutti gli slave fanno partire un 'passo'

Passo propedeutico è certamente partire da una macchina a stati finiti

Se (e solo se) cominci a mettere qui codice tento di darti una mano

Ciao,
RINGRAZIO TUTTI per le risposte!!!

Vi allego i 3 programmi.

Programma "ARDUINO-A_ALBA-TRAMONTO" è il più complesso, con molti "void XXXX()" per gestire la striscia digitale WS2811. Fa alba e tramonto (Dal blu al giallo tenue, poi al giallo forte e viceversa, ma led per led..)

Programma "ARDUINO-B_MOSFET" per esempio, mentre "A" sta facendo salire giallo forte, B deve fare FADE IN Salita striscia BIANCO PURO e FADE OUT Striscia BLU puro... ecc ecc. nelle varie fasi alba//tramonto/notte.

Programma "ARDUINO-C_RELE" ancora in BOZZA!, gestisce solo le luci a 5Vcc dei lampioni, case, faretti 12V per stelle ecc...("C" NON ha piu pin disponibili!)

Come funzionano i 3 programmi ora:

  1. Inizialmente regolo i tempi di alba/giorno/tramonto dal programma "ARDUINO A" e faccio stampare a seriale i tempi!
  2. Inserisco i tempi misurati da "A" su "B", per usarli per calcolare i suoi ritardi dei Mosfet.
  3. stessa cosa dovrò fare con "C" ma essendo rele è più facile!!

NON è ancora tutto sistemato, sono a 60% direi, e non è ancora installato nulla... quindi SONO DISPONIBILE A MODIFICHE, ma per ottenere quello che vorrei nel modo più SEMPLICE possibile... :confused:

  1. MASTER "C" che comanda ed invia QUANDO devono fare le cose A e B??
    oppure
  2. Se mi dite invece che con ARDUINO MEGA è più facile e mi semplifica, lo prendo!!! Metterò insieme i 3 programmi, ma come faccio a gestire le "contemporaneita" di alcuni azionamenti, cicli FOR, VOID, FADE su un solo LOOP?? :frowning: :frowning:

Grazie 1000 ancora!

ARDUINO-A_ALBA-TRAMONTO_WS2811.ino (11.7 KB)

ARDUINO-B_MOSFET.ino (3.71 KB)

ARDUINO-C_RELE.ino (2.97 KB)

Prima di pensare ad unire i vari programmi occorre una serie mi modifiche perché anche se presi uno per uno i tuoi programmi hanno problemi.
Come prima cosa ti consiglio di attivare nelle impostazioni i messaggi di output dettagliato sia in compilazione che in caricamento (sono due flag nelle impostazioni dell'IDE)
Perché in compilazione hai dei warning

warning: invalid conversion from 'const char*' to 'int' [-fpermissive]

     FASE = "PARTI_AlbaBLU_GialloTENUE";

            ^~~~~~~~~~~~~~~~~~~~~~~~~~~

Questo eprchè tenti di confrontare una stringa con una varibile che hai dichiarato come intero.
Visto che stai gestendo cose multiple ti consiglio sin da subito di ottimizzare al massimo, quindi NO all'uso di Stringe (S maiuscola intesa come classe String) che attualmente tu non stai usando e il consiglio è quello di restare su questa strada
secondo la varibile FASE l'hai definita int, supponendo che non avrai più di 255 fasi ti consiglio di modificarne il tipo in byte (risparmi un byte).
Arriviamo al dunque, se tu volessi confrontare la varibile FASe con qualcosa di comprensibile ad occhio umano allora dovresti definirti alcune "define" per fare coò che vuoi senza avere errori/warnig ovvero:

   #define PARTI_NOTTE_BLU 1
   #define PARTI_AlbaBLU_GialloTENUE 2
   ..ecc..
   byte FASE =0;

poi quando vai a confrontare o assegnare fai

  if(PARTI_NOTTE_BLU==FASE)
  {
    ..omissis...
  }
  ...
  FASE=PARTI_NOTTE_BLU;

Noterai che nell'if ho "girato" la varibile con il valore che voglio confrontare, questo ti è utile perché se sbagli e scrivi:

  if(FASE=PARTI_NOTTE_BLU)
  {
    ..omissis...
  }

non ti viene riportato errore ma entrerebbe sempre nell'if in quanto non stai facendo un confronto ma un assegnazione che restituisce sempoe il valore vero.
E queste sono le cose "facili", la parte difficile è che sebbene tu usi millis() in realtà lo usi solo come sistema per notificarti il tempo trascorso ma l'attesa la gestisci con i delay.
Per unire i programmi e gestire più cose "contemporaneamente" occorre rivedere tutti i passaggi e NON avere nessun delay nella procedura, per capire le basi con cui si usa millis() consiglio prima la lettura di QUESTO post esplicativo, dopo di che lo studio di come si usa la funzione millis(), prima QUI, poi QUI e QUI e QUI e tutti gli articoli che sono in QUESTA pagina ... alla fine il tutto dovrebbe essere più chiaro :slight_smile:
Capisco che è un percorso difficile da intraprendere e che sconvolga la struttura del programma ma se vuoi unire e/o sincronizzare i vari programmi con i delay la vedo dura, molto difficilmente riuscirai a tenere sincronizzato il tutto.
Comunque partrei con sistemare i warnig
ultima cosa prima di postare i programmi nell'IDE permi Ctrl+T che fa la formattazione del codice, rende la vita di chi deve leggerlo più semplice :slight_smile:

bello

con mio fratello Massimo (salvorhardin) stiamo facendo la stessa cosa

ma stiamo partendo a rovescio rispetto allo OP

prima ci annotiamo le fasi, con le ore (fasulle) di start

06.00 avvio alba
06.15 basta luce blu
06.30 piena luce a est
06.45 stop luci lampioni
06.46 avvio luci case
e via così

poi vorremmo girare l'array per far eseguire il ciclo delle luci

come era stato consigliato a quello del robot che scriveva, pensavamo di usare un vettore di puntatori a funzione, invece che uno switch

io invece sto pensando a come consigliare lo OP

se hai un pin libero su tutti gli Arduino puoi usarlo per collegarli tra di loro usando una uscita su uno e l'ingresso sugli altri

uno, magari quello dei rele, che è più scarico, fa da clock per gli altri

ogni volta che alzi e poi abbassi l'uscita gli altri se ne accorgono e passano alla fase successiva

però assolutamente devi trasformare i tuoi 3 arduini in macchine a stato finito

a quel punto forse vale la spesa di usare una sola mega2560, se non sei molto avanti col cablaggio, visto che comunque i programmi sono da rifare per intero

che mosfet hai usato? noi non abbiamo ancora deciso

Salvorhardin:
uno, magari quello dei rele, che è più scarico, fa da clock per gli altri
...
però assolutamente devi trasformare i tuoi 3 arduini in macchine a stato finito

D'accordo sulla necessità della macchina a stati ma il problema del clock non è tanto chi lo genera (non è una cosa così gravosa per la MCU) ma per chi deve riceverlo e sincronizzarsi. Considerando che quando si sta aggiornando lo stato delle WS2811 non si può fare altro perché in quelo momento si è in una situazione time critical dove tempistiche strette non possono essere interrotte per nessun motivo, pena il fallimento dell'update dei led. Quindi anche ipotizzando di ricevere il clock tramite interrupt ci sono buone possibilità che questo portai al mal funzionamento delle strisce led indirizzabili.
Alla fine concordo anche sul fatto che inglobando tutto in una mega o anche in una UNO rivedendo le uscite usando dei multiplexer o degli I/O extender su I2C alla fine il programma girerà senza intoppi o problematiche di sincronia.
Al solito se ancora non si è provveduto all'acquisto come spesso consiglia un altro "utente" del forum evitare i WS2811 come la peste e preferire gli APA102 che danno solite possibilità garantendo però libertà in fase di aggiornamento dei led

Altro piccolo pensiero mio, purtroppo quando ci si trova in una situazione tipo che il programma è pronto al 60% "buttare" tutto per cambiare rotta è mentalmente difficile e scoraggiante. In realtà ripartendo da zero con le nuove specifiche di funzionamento implementando via via le funzionalità solo quando quelle precedentemente sviluppate funzionano a dovere (in questo caso macchina a stati e no delay) alla fine si risparmia tempo e si ottiene un risultato migliore. E, senza volre sminuire nessuno sia ben chiaro, più si è inesperti e più questa metodologia è vincente.

io stavo pensando una cosa del genere:

nel master

ipotizzando il pin due come pin di sincronismo

digitalWrite(2,HIGH);
delay(20);
digitalWrite(2,LOW);
// impulso di sincronismo lanciato
digitalWrite(2,HIGH);
delay(100);
digitalWrite(2,LOW);
// impulso di reset lanciato

nei 2 slave

void loop(){
   if digitalRead(2){
      tempo=millis();
      while(!digitalRead(2));
      tempo=millis()-tempo;
      // durata impulso
   }

   if (tempo>5){
   fase=fase+1;
   // impulso OK, 
   }
   if (tempo>50){
   fase=0;
   // impulso di reset
   // richiamo la procedura di reset 
   }
tempo=0;

// e da qui tutta la macchina a stati

}

altrimenti serve una software serial con 2 pin liberi connettendo in daisy chain i vari arduino

il primo trasmette al secondo, il secondo riceve e per dare conferma trasmette al terzo, il terzo per dare conferma ritrasmette al primo

quando il primo riceve indietro il comando cha ha lanciato è contento così e si va avanti
altrimenti ripete la trasmissione

però ammetto che non saprei risolvere il problema dei led indirizzabili WS2812

un pensiero, intanto che aspettavo i 5 minuti:

non è un problema, dato che per definizione all'inizio di una fase la precedente è terminata
non si tratta di comandare un'azione
di tratta di tenere sincronizzati dei device che fanno azioni di loro sponte
e far partire la prossima azione contemporaneamnete, non abortire un'azione già intrapresa

fabpolli:
(in questo caso macchina a stati e no delay) alla fine si risparmia tempo e si ottiene un risultato migliore.

ah guarda, io ti do pienamente ragione

nell'industria meccanica abbiamo un detto:"non c'è mai tempo per farlo, c'è sempre tempo per rifarlo"

che significa: se parti sbagliando per la fretta e non ti raddrizzi subito, ti toccherà poi chiedere scusa e altro tempo per ri-fare il lavoro

Direi di partire a risolvere i problemi elencati da @fabpolli al post #5

Aggiungo che le convenzioni esistono per permettere a tutti di leggere e comprendere il codice, se si usano proprie convenzioni o non se ne usano affatto è difficile fare progressi.

Cosa si intende per convenzioni:
Una variabile che non muta durante il corso del programma è una costante è va dichiarata come tale, es:

// preferibile 
const byte pinLed1 = 1;
// o anche, ma senza mischiarle nello stesso sketch
const byte pin_led_1 = 1;

Esistono altre costanti il cui valore viene stabilito con #define COSTANTE valore, per convenzione il nome della costante è tutto in maiuscolo, può anche essere ad es. COSTANTE _1.

C'è una certa libertà, quindi si può stabilire che tutte le costanti abbiano il nome in maiuscolo, quindi es:

const byte PINLED_1 = 1;   
#define PINLED_2 = 2

Si possono anche inventare le convenzioni, es:

const byte c_pinLed1 = 1;   // tutte i nomi che iniziano con c_ sono variabili dichiarate constanti.

In questo caso per distinguerle dalle #define i nomi di queste sono tutte in maiuscolo.
Viene da se che STRISCIA1 non è una costante ma una istanza di classe e anche per queste si deve stabilire
quali convenzioni usare.

Tutto sto pappardello si può sintetizzare in: Scrivi il codice in modo che possa essere condiviso e compreso da tutti quelli con cui lo condividi.

un pensiero, intanto che aspettavo i 5 minuti:

non è un problema, dato che per definizione all'inizio di una fase la precedente è terminata
non si tratta di comandare un'azione

Credo ti stai riferendo a quello che possiamo chiamare macchina a stati finiti sequenziali. Banalmente

switch (stato) {
      case 0:
            while (1) {
                 ....
            } 
      stato = 1;
      break; 

      case 1:
            while (1) {
                 ....
            } 
      stato = 2;
      break; 
    
}

Dove nessuna parte di codice nel case 1 può essere eseguita durante l'esecuzione del codice presente nel case 0.
Si potrebbe anche eliminare lo switch e i case e funzionerebbe uguale, tuttavia fuori dallo switch (ma dentro al loop) tramite if (condizione) { stato = n; } possiamo scegliere da quale case partire.

Ciao.

Salvorhardin:
io stavo pensando una cosa del genere:

nel master

ipotizzando il pin due come pin di sincronismo

Cosa intendevi è chiaro ma quello che ti sta sfuggendo è che tu nel loop non è detto che arrivi a leggere il pin nel momento esatto in cui questo viene posto a HIGH, quindi se arrivi dopo 40ms dall'attivazione interpreti un reset per un avvio. Queste cose vanno gestite con gli interrupt che ti danno la certezza di "catturare" il cambio di pin nel momento esatto in cui avviene, come detto prima però questo collide con la necessità assoluta delle tempistiche d'aggiornamento delle WS2811 che NON deve essere interrotto, credo spengano anche gli interrupt durante l'aggiornamento ma non ne sono sicuro, se così fosse anche questa soluzione non sarebbe percorribile. Ad ogni modo cercare un sincronismo tra apparati esterni non è mai una passeggiata di salute.
E concordo con @Maurotec (che concorda con me :slight_smile: ) che prima occorre risolvere i problemi che ho evidenziato, che sono sicuramente prioritari

volendo si può anche fare senza un master che da il clock

serve sempre almeno un pin libero, ma senza un master

fabpolli:
... credo spengano anche gli interrupt durante l'aggiornamento ma non ne sono sicuro...

Confermo, per esperienza recente :frowning: (da leggere)

Federico

scusate ancora per l'errore
siccome abitiamo nello stesso palazzo usiamo connessione internet in comune e abbiamo il laboratorio nel mio garage

spero che non ci capiti più

fabpolli:
Cosa intendevi è chiaro ma quello che ti sta sfuggendo è che tu nel loop non è detto che arrivi a leggere il pin nel momento esatto in cui questo viene posto a HIGH, quindi se arrivi dopo 40ms dall'attivazione interpreti un reset per un avvio.

ma no

è ovvio che ci deve essere un lasco, un tempo morto, significativo

pensa all'alba: non è che se spegniamo i lampioni un secondo dopo la fine dell'alba muore qualcuno

tu aumenti progressivamente la luce in ad esempio 40 secondi +1 -0.5 (tollerato all'europea)

e la corrispondente fase del master dura anche lei 40 secondi +1 -0.5, la facciamo durare 1 secondo in più

alla fine dell'incremento della luminosità lo slave si pone in ascolto (anzi, come macchina a stati spesso è comunque in ascolto)

quando il master ha atteso il tempo sufficiente da il clock, e non muore nessuno se per 2 secondi (+2 -1)
le luci dei lampioni sono ancora accese

e poi basta comunque far fare da master all'arduino che ha le tempistiche più critiche, e il problema è risolto

alla fine dell'alba (senza alcun tempo e senza alcuna tolleranza)
l'arduino che gestisce i WS da il clock
e i due slave, che sono in attesa
(accendere le luci dei lampioni non occupa molto tempo e lo hanno già fatto da mo', adesso sono in attesa)
i due slave passano alla fase dopo

ecco, ho messo a posto
scusate ancora

Ciao,
RINGRAZIO TUTTI per le risposte!!!

Come da vostri suggerimenti ho sistemato e ripulito il codice del Programma "A" ( Quello delle strisce Digitali)
Ho messo a posto la visualizzazione Seriale dei millis di ogni fase.

Premuto il pulsante di avvio,devo SOLO FARE 3 CICLI, poi viene tolta corrente a tutto, via RELE TEMPORIZZATO! Per riavviare devono ripremere il pulsante che ridà corrente a tutti i trasfomatori!

A questo punto, non è piu facile, semplicemente usare i tempi Millis() visualizzati da "A" per usarli in "B e "C" con:
Quando millis()=xxxx allora fai xxx.
Quando millis()=yyyy allora fai yyy.

Il problema è ce non mi è chiara questa cosa di Arduino:
Se uso Millis() per dire quando iniziare un azione, ma questa azione FINISCE DOPO i millis che il programma gli dice di iniziare una altra azione, che succede?
Vedi esempio programma sotto:

void loop() {

if (millis()==40000); //# Impiega 15 secondi!!!!!!!!!
for ( int i=0; i<255; i++)
{ analogWrite(BIANCOFRONTALE, i);
delay( VelocitaBianco );
}

if (millis()==45000); //# CHE ACCADE A QUESTO PUNTO???
for ( int i=0; i<255; i++)
{ analogWrite(CHIESA, i);
delay( VelocitaChiesa );
}
...... ecc

inoltre,

Potrei "AZZERARE" i millis() di "B" e "C" con un segnale di "A" che va HIGH/LOW finito il SETUP??
Se si come sarebbe il codice dei "B" e "C" per aspettare il segnale HIGH (esempio PIN13 libero su "C") quindi azzerare millis() e PARTIRE con i loop LOOP??