Semaforo Complesso OK-Flip Flop Semplice ( e non ci salto fuori).

secondo me vuoi fare il passo più lungo della gamba, inizia a far lampeggiare due led con tempi diversi con millis(), poi intercetta la pressione di un pulsante e fai fare qualcosa di conseguenza ai due led, che so che rimangano accesi per 2 secondi.... una volta che hai le idee ben chiare su come gestire millis() e leggere un input lo espandi per realizzare tutto il semaforo

mi sa che hai ragione ...è meglio che ricomincio con qualcosa di più semplice....anche perche non so programmare ripeto sono alle prime armi.

anche se il semaforo mi sembrava semplice.

Puso:
mi da errore....ho fatto copia incolla

Molti esempi danno per scontato che determinati nomi presenti nel codice sono variabili o costanti che vanno definite da altre parti. In particolare previousMillis e ledState sono due variabili globali da definire all'inizio del programma, e ledPin è una costante, che va sempre definita in precedenza. Gli errori del compilatore dovrebbero permettere di capire cosa si è scritto in modo errato e in quale riga.

Quella è comunque la via giusta, il loop cicla continuamente alla massima velocità e testa continuamente il verificarsi di qualche condizione. Solo nell'istante in cui una condizione risulta vera allora si esegue quanto contenuto nell'if (e le operazioni da eseguire a loro volta non devono bloccare o durare molto tempo).

Fare uno schema a relè/temporizzatori/pulsanti "classici", tradurlo in funzioni logiche equivalenti potrei anche riusirci,resta il fatto che il LOOP,legge le righe in fila ed una volta che è partito non posso aggiungere o toglier niente.

Non ho capito cosa intendi con aggiungere/togliere. Lo schema seguente è un classico marcia/arresto, con in più un timer per l'arresto dopo tot (facciamo 5 minuti):

Questo è un esempio di istruzioni da mettere nella funzione loop, che rappresentano pari pari il circuito:

byte STA = digitalRead(STA_PIN);
byte STO = digitalRead(STO_PIN);
A = (A | STA) & !T1out & !STO;
if (!A) { T1time=millis();  T1out=0; } else if (millis() - T1time >= 300000) T1out = 1;
digitalWrite(OUT_PIN, A);

NOTA: si assume che i pulsanti premuti diano un 1 in lettura, e che la luce si accenda con un 1 in scrittura

NOTA2: STA_PIN, STO_PIN, OUT_PIN, sono costanti che indicano i pin usati, mentre A, T1time, T1out, sono variabili di lavoro definite in precedenza:

#define STA_PIN  2
#define STO_PIN  3
#define OUT_PIN  4
byte A = 0;
byte T1out = 0;
unsigned long T1time = millis();

Nonostante il tempo del processo timer sia di cinque minuti (300mila ms), il loop gira alla massima velocità e i pulsanti vengono letti continuamente (ad occhio almeno diecimila volte al secondo).

NOTA3: in un circuito reale con pulsanti/contatti meccanici bisogna anche effettuare il debounce (filtro antirimbalzo) degli ingressi.

intendo che :
lo schema elettromeccanico funziona
probabilmente ance come lo hai riportato in funzione logica funziona

pero mettiamo per ipotesi che durante il tempo in cui il timer tiene eccitato il relè, arriva un che vuole accendere una lampadina

a livello elettrotecnico baterebbe mettere in parallelo un interuttore con la lampadina e se arriva uno che la vuole accendere basta che schiaccia l'interruttore indipendentemente da cosa sta facendo il timer (non gli interessa)

a livello logico invece dovrebbe aspettare che il timer abbia finito il suo lavoro.

non so se mi sono spiegato

sorry

-pulsante-

ecco secondo me ci correbbero dei LOOP in parallelo.....mentre adesso mi sembra che ci sia un unico LOOP che funziona in serie

spero di sbagliarmi

Puso:
a livello elettrotecnico baterebbe mettere in parallelo un interuttore con la lampadina e se arriva uno che la vuole accendere basta che schiaccia l'interruttore indipendentemente da cosa sta facendo il timer (non gli interessa)

a livello logico invece dovrebbe aspettare che il timer abbia finito il suo lavoro.

No, visto che l'intera serie di istruzioni è sempre valutata/eseguita migliaia di volte al secondo, basta aggiungere una o due righe per leggere il nuovo interruttore e scrivere la nuova uscita (lo schema si può complicare a piacere con quanti elementi e timer indipendenti si vuole).

secondo me ci correbbero dei LOOP in parallelo.....mentre adesso mi sembra che ci sia un unico LOOP che funziona in serie

Quello che mi sembra sfugga è il fatto che l'esecuzione di quella serie di istruzioni dura in ogni caso pochi microsecondi (una quindicina), quindi ogni singolo componente dello schema viene virtualmente "eseguito" in parallelo a tutti gli altri ogni pochi microsecondi. La logica e i tempi del processo complessivo sono formati da innumerevoli cicli del loop principale.

NOTA: ad ogni nuovo ciclo i dati di lavoro sono rappresentati dalle letture attuali degli ingressi, e dalle variabili impostate nei cicli precedenti (stato).

anche se il semaforo mi sembrava semplice

Il semaforo monotask si, ma come in pratica in tutti i progetti "reali" ci si è trovati subito ad avere bisogno del multitasking (cooperativo), che richiede un diverso approccio/design. È abbastanza carne al fuoco tutta in una volta :wink:

Ciao Puso,

forse... e dico forse...per dimostrare la vera differenza che c'è tra usare delay() e millis() prova ad eseguire questi due sketch che ad ogni secondo scrivono su monitor quanti cicli il loop() ha eseguito in un secondo.

con delay()

unsigned long ciclo = 0;

void setup() {
  Serial.begin(9600);
}

void loop() {
  delay(1000);
  Serial.println(ciclo);
  ciclo++;
}

con millis():

unsigned long ciclo = 0;
unsigned long tempo;

void setup() {
  Serial.begin(9600);
  tempo = millis();
}

void loop() {
  if (millis() - tempo == 1000) {
    Serial.println(ciclo);
    tempo=millis();
  }
  ciclo++;
}

...se poi vuoi comunque che il pulsante premuto abbia precedenza su tutto...si deve andare di interrupt...

Puso:
mi da errore....ho fatto copia incolla ed ho solo corretto il setup

Il codice pubblicato non è completo perché lo trovi già pronto tra gli esempi dell'IDE: Blink without delay

Un pochino di masturbazione intellettuale lo vedo, ben spalmata....

Se vogliamo replicare il funzionamento di un circuito a relè e timer elettromeccanici si può fare, ma non è così che si programma arduino

Io sono quello che ne sa meno di tutti, qui, ma vedo che la strada non è quella giusta

Torno a dire che un test su millis, che legge da un array di strutture ed esegue le istruzioni codificate è un approccio modulare, semplice, espandibile

astrobeed,

nonostante tutto quello che si è scritto, attendiamo volentieri il Tuo PDF.

grazie
E:B:

Quello che ho intenzione di realizzare, e devo trovare il tempo per farlo, non è una soluzione al tuo problema, è una guida generale su come si deve scrivere un programma per mcu/micro.

se l'ignoranza avesse le ali......io guiderei uno stormo di somari

orso
me lo riscrivi per favore la differenza tra delay e millis.....utulizzando le uscite digitali.....se no leggo solo numeri e non capisco cosa fanno

docsavage:
Se vogliamo replicare il funzionamento di un circuito a relè e timer elettromeccanici si può fare, ma non è così che si programma arduino

Io sono quello che ne sa meno di tutti, qui, ma vedo che la strada non è quella giusta

Torno a dire che un test su millis, che legge da un array di strutture ed esegue le istruzioni codificate è un approccio modulare, semplice, espandibile

L' OP ha detto di essere alle prime armi con la programmazione e di conoscere un po' di elettrotecnica. Gli array di strutture sono cose che secondo me arrivano tra molti mesi. D'altra parte in questo thread si è proposto di tutto, i relé simulati sono solo una delle varie possibilità, che ho usato come spunto "familiare" in quanto il punto esame in questo momento mi sembra che non sia tanto come fare qualcosa, ma come fare diverse cose contemporaneamente.

Puso:
orso
me lo riscrivi per favore la differenza tra delay e millis.....utulizzando le uscite digitali.....se no leggo solo numeri e non capisco cosa fanno

Quei numeri indicano quante volte è girato il loop principale ogni secondo trascorso. Nel primo caso la parte che scrive sulla seriale blocca tutto con delay, nel secondo no e nel loop principale si potrebbero fare altre cose "in parallelo".

Un esempio di tre LED che lampeggiano ciascuno per i fatti propri:

#define LED1_PIN  2
#define LED2_PIN  3
#define LED3_PIN  4
#define TEMPO1    500
#define TEMPO2    240
#define TEMPO3    1300


unsigned long t1 = millis();
unsigned long t2 = millis();
unsigned long t3 = millis();
unsigned long actualTime;


void setup() {
    pinMode(LED1_PIN, OUTPUT);
    pinMode(LED2_PIN, OUTPUT);
    pinMode(LED3_PIN, OUTPUT);
}


void loop() {
    actualTime = millis();
    
    if (actualTime - t1 >= TEMPO1) {
        t1 += TEMPO1;
        digitalWrite(LED1_PIN, !digitalRead(LED1_PIN));
    }

    if (actualTime - t2 >= TEMPO2) {
        t2 += TEMPO2;
        digitalWrite(LED2_PIN, !digitalRead(LED2_PIN));
    }

    if (actualTime - t3 >= TEMPO3) {
        t3 += TEMPO3;
        digitalWrite(LED3_PIN, !digitalRead(LED3_PIN));
    }
}

ciao...ci provo.

utilizzando LED integrato dell'arduino, se hai una UNO dovrebbe essere il PIN 13 e che puoi chiamare LED_BUILTIN, ed il LED della comunicazione su monitor seriale...il primo lo vedrai sempre cambiare di stato ogni mezzo secondo...l'altro, a seconda se usi millis() o delay() varia ad ogni esecuzione ciclo...quindi con delay() lo vedrai lampeggiare lento con millis() ti sembrerà sempre acceso da quante volte esegue il ciclo.

con delay():

unsigned long ciclo = 0;

void setup() {
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
}

void loop() {
  Serial.println(ciclo);
  digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  delay(500);
  ciclo++;
}

con milli():

unsigned long ciclo = 0;
unsigned long tempo;

void setup() {
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
  tempo = millis();
}

void loop() {
  Serial.println(ciclo);
  if (millis() - tempo >= 500) {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    tempo = millis();
  }
  ciclo++;
}

io vedo alcune considerazioni:

il semaforo è chiaramente un oggetto che "chiama" gli array
mi spiego:
è chiaramente composto da alcune paline tra loro tutte uguali, che si comportano in modo simile ma sfasate nel tempo

quindi "descrivere" un semaforo con un array è scelta naturale

inoltre la necessità di escludere le delay dal ciclo, associata a una serie pressocche infinita di tempi di attesa fa facilmete pensare a una tabella di tempi da far rispettare alla millis

da qui leggere da una tabella una riga di configurazione del semaforo mi sembra un passo logico
riga che comprenda anche il tempo di durata della configurzione del semaforo

non è difficile, basta leggersi un poco di documentazione sugli array
gli array più semplici da capire sono le stringhe di c
e se si usano solo le lettere si può anche pensare di usare codici mnemonici

e fare qualcosa del tipo:
tutto maiuscolo
prima lettera prima palina (e terza) a fare la via uno
seconda lettera seconda palina (e quarta) a fare la via due
R rosso
V verde
G giallo (col verde acceso)
P verde pedonale (rosso pedonale è semplicemente l'opposto)
B verde pedonale con buzzer acceso

terza lettera tempo di durata
A 5 secondi (sovrapposizione dei rossi)
B 15 secondi (giallo)
C 1 minuto verde sulla via a minore priorità
D 2 minuti verde sulla via a maggiore priorità

quarta lettera numero (cifra)
indica quale riga di luci sarà la prossima

in questa maniera il semoforo si puo' descrivere circa così:

0RRA1 che significa sovrapposizione rossi, 5 secondi, prossima è la riga 1
1RVC2 che significa Rosso in uno verde in due, durata un minuto, prossima la 2
2RGB3 che significa Rosso in 1, giallo in 2, tra 15 secondi cambia in 3
3RRA4 sovrapposizione dei rossi, tra 5 secondi cambia in 4
4VRD5 verde in due rosso in uno, tra minuti cambia in 5
5GRB0 giallo in uno, rosso in due tra 15 secondi cambia in 0 (ricomincia)

sembra un casino, invece ha due grandi vantaggi:

uno: si può scrivere una funzione che interpreti la stringa,
quindi la loop si limita a mandare in esecuzione la stringa
comodo in debug

due:

facile intercettare punti topici per fare cose in più

ovvero basta scrivere delle sequenze "particolari" per mandarle in esecuzione

il passaggio pedoni diventa

6PPC7 Verde pedone su due vie, rosso ovunque, tra un minuto evolve in 7
7BBB0 Verde pedone su due paline, rosso ovunque, buzzer acceso, tra 15 secondi evolve in 0
siccome zero fa parte della sequenza principale non ho bisogno d'altro per far proseguire normalmente il semaforo

anche per far intervenire il pedonale non devo fare altro che mandare in esecuzione la riga 6

e a dimostrare quello che intendobasta guardare questo esempio di loop

è corta, sono più i commenti che le righe di codice

che fa tutto il lavoro è la luci()
che imposta anche il tempo, letto dalla stringa in ingresso

Void loop() {


  // testo se abbiamo gia' raggiunto il tempo
  if (tempo > millis()) {
    // cambio stato semaforo
    luci(sequenza[stato]);
  }

  // testo se è premuto un pulsante di pedone
  if (digitalRead(pedone)) {
    // pulsante di pedone premuto
    // lo memo rizzo
    chiamata = 1;
  }
  // se chiamata eseguita e doppio rosso
  if ( chiamata && digitalRead(P1R) && digitalRead(P2R)) {

    // salvo la sequenza prossima
    stato1 = stato;
    // metto come prossima sequenza il passaggio pedoni
    stato = 6;
    // quindi allo scadere del tempo per il doppio rosso passa in pedoni, invece che in quello che doveva fare
    // considero soddisfatta la chiamata
    chiamata = 0;
    // adesso se non fcessi niente, dopo il tempo di cicalino
    // il semaforo ripartirebbe dall'inizio della sua sequenza
    // questo è garantito dal fatto che il passo 7 BBA punta al passo due
    // invece voglio che riparta da dove si era fermato prima del pedone
    // al momento non ho idee....
  }

// controllo se c'e' o no inserito il lampeggio
// non ho idee adesso
}

notare che io non interrompo il normale ciclo del semaforo

mi limito ad attendere la condizione di doppio rosso e mettere in lista di esecuzione non la normale riga successiva, ma la prima riga della sequenza pedonale

la sequenza pedonale a sua volta termina richiamando la prima riga della sequenza principale

il difficile è tutto nella luci()

Puso:
... la differenza tra delay e millis.....

Quello che secondo me dovresti cercare di capire, e' la differenza fra COSA SONO delay e millis ... perche' cosi poi la potrai sfruttare anche per altre cose, non solo per un'esempio di sketch ...

Premesso che io non sono un programmatore, cerco di darti un paio di spunti ...

Delay, e' un'istruzione (o funzione, chiamala come vuoi), a cui tu passi un certo tempo quando la invochi, e lei "aspetta" per il tempo che gli passi ... ma non fa niente altro, nel frattempo ... se ne sta' li imbambolata ad aspettare, senza leggere ingressi, senza pilotare uscite, senza incrementare cicli ne eseguire calcoli o istruzioni, aspetta e basta ... per cui, in pratica blocca completamente il funzionamento dello sketch e della MCU per tutto il tempo in cui aspetta (immagina di rimanere fermo a fare nulla con occhi ed orecchie chiuse per tutto il tempo, fregandotene di tutto quello che ti capita intorno ... quello e' un delay) ... credo che solo un'interrupt possa essere letto all'interno di un ciclo di delay ...

Millis invece di per se non "aspetta" nulla, la puoi immaginare come una variabile che contiene, ogni volta che viene "letta", il numero di millisecondi trascorsi dall'accensione della MCU fino a quel momento ... pero' nonostante non sia una funzione di attesa, la puoi usare per controllare e confrontare intervalli di tempo, senza bloccare il resto del programma, oltre che per farci altre cose che richiedono il confronto fra due o piu valori di tempo trascorso ...

be mi fa piacere che almeno per essere alle prime armi non ho richiesto uno dei soliti aiuti per avere tutto senza studiare.

vi ringrazio tutti per avermi dato un parere.

ora sto tentando di capire bene il codice mills....che se non ho capito male è un valore di 50 giorni (ovviamemte espresso in millesimi) che parte all'avvio dell LOOP.

Quindi dandogli delle condizioni puramente aritmetiche, posso fargli eseguire dei comandi per circa 50 giorni( ovviamente espresso in millesimi) oppure lo resetto non ho ancora capito come ,ma ci sto studiando e lo faccio ripartire.

Questo al contrario del DELAY che ferma il LOOP per (X) millisecondi, mi darebbe un LooP veloce ne quale posso inserire il PULSANTE DEL VECCHIETTO.

Il tutto solo stando attento che la condizione matematica di Mills non sia pù veloce dell'esecuzione di tutto il cilo di semafori.
.....GIUSTO????????.......

vi prego ditemi che è giusto se significa che non ci ho ancora capito niente.

Per il momento grazie a tutti per l'aiuto che mi state dando.