Macchine a Stati Finiti (Finite State Machines o qualcosa del genere)
Ma è un po' prematuro parlarne.
che appunto ti era già stato detto
io direi per cominciare (perché tu non hai ancora nemmeno comiciato, credimi) di fare quelle due cose che ti sono state suggerite:
rimettere a posto quelle due if(), che sono sbagliate
e poi mettere sotto condizione le lucine e ilmovimento
poi, al termine del movimento attendre, questo si con un while, che si liberino entrambi i sensori
a quel punto puoi rialzare le sbarre e poi spegnere le luci
solo adesso, dopo aver fatto tutto queto ti renderai conto che alcune azioni devono essere asincrone
e allora vedremo tutti i discorsi su millis e macchine a stati finiti
ma per un semplice plastico potrebbe non essere necessario, fidati che per adesso non ti serve di entrare in quel ginepraio
A me?...
Veramente sono io che l'ho detto (post #18)... ![]()
Oh, scusa
ho citato te, ma rispondevo allo OP
troppi dottori per un solo malato
aspetto un po' prima di intervenire di nuovo
comunque lo avevo detto io, giusto per non fare torto a nessuno...
Ma non eri tu che volevi comandare i servo con una PCA?
Mi aspettavo di ricevere notizie di la....
Ciao a tutti ho rivisto finalmente il mio sketch, ed un passo avanti lo fatto ma ora mi trovo in difficoltà per due cose: 1) come mettere il secondo sensore ad ostacoli che funzioni come il primo 2) come far lampeggiare i led fin quando il servo si chiude e fermarli 1 sec dopo accesi fissi.. mi potete dare una mano?
grazie
di seguito il mio sketch
#include <Servo.h>
int led1=2;
int led2=4;
int Ostacolopin1=3;
int Ostacolo = LOW;
int Ostacolopin2=5;
Servo Servo1;
int pos = 0;
void setup(){
pinMode(led1,OUTPUT);
pinMode(led2,OUTPUT);
pinMode(Ostacolopin1, INPUT);
pinMode(Ostacolopin2, INPUT);
Servo1.attach(6);
Serial.begin(9600);
}
void loop (){
Ostacolo = digitalRead(Ostacolopin1);
if(Ostacolo==HIGH) {
Serial.println("TRENO");
digitalWrite(led1,HIGH);
digitalWrite(led2,LOW);
delay(100);
digitalWrite(led1,LOW);
digitalWrite(led2,HIGH);
delay(100);{
delay(500);
Servo1.write(90);
digitalWrite(led1,HIGH);
digitalWrite(led2,HIGH);
}
}
else{
Serial.println("Passaggio a livello libero");
delay(1000);
digitalWrite(led1,LOW);
digitalWrite(led2,LOW);
Servo1.write(0);
}
delay(200);
}
Quindi i link non sono serviti a nulla. Eppure li c'è:
- Un lampeggiante a due led che si alternano. Vedi la funzione
void flashingLight(bool tf). - C'è un servo RC che sale e scende lentamente.
- C'è il pulsante giallo che alza la barra.
- C'è il pulsante verde che simula una fotocellula.
Comunque io inizierei a ragionare su quelle operazioni che devono essere eseguite e quelle da escludere dalla esecuzione. Ad esempio una volta che Ostacolo è HIGH non serve più monitorare il suo stato, oppure è necessario?
void loop() {
switch {state} {
case 0:
Ostacolo = digitalRead(Ostacolopin1);
if(Ostacolo==HIGH) {
state = 1; // prenoto il case 1 per essere eseguito al prossimo ciclo di loop
saveMillis = millis(); // salva il tempo corrente.
}
break;
case 1: //
if (lampeggiante == OFF) {
lampeggiante = BLINK;
}
// usa millis qui per attendere il passare del tempo di 5000ms
if (millis() - saveMillis >= 5000) {
saveMillis = millis(); // salva il tempo
lampeggiante = ON; // led fissi
state = 2; // prenoto il case 2 al prossimo ciclo di loop
}
break;
}
}
Eccetera eccetera.
Ciao.
Ciao Maurotec,
in teoria non è da monitorare, perché se inattivo (LOW) il servo deve tornare a 90° dopo 3 sec che nessuno dei due IR è in HIGH, e devono spegnersi i led.
Poi giusto per precisare ho guardato i link ma già nei link ce scritto che non sono facili da seguire, quindi ho fatto quello che ho potuto. Non sarei qui a chiedervi aiuto ![]()
Ora tu mi hai scritto "break" che è un nuovo comando che non avevo ancora visto.
Quindi comprenderai che per noi neofiti non è cosi semplice, come chiedessi a qualcuno di fare il mio lavoro e non è un ing. meccanico. ![]()
Quindi vi chiedo pazienza e vi ringrazio anticipatamente.
"Without break, the program continues to the next labeled statement, executing the statements until a break or the end of the statement is reached. This continuation may be desirable in some situations."
Quindi mettilo sempre alla fine di ogni case, a meno che tu voglia che vengano eseguiti anche i case successivi a quello vero. Ciò può essere desiderabile, per esempio, in un indicatore di livello a barra: si accende il LED del valore raggiunto, ma anche tutti quelli inferiori.
Solo l'ultimo non è indispensabile, ma per coerenza metti anche quello (è gratis!
).
Break si usa anche per uscire da un for, while o do...while:
Capisco, non pretendo miracoli, però break sarà nuovo per te e compare forse in questo posto per la prima volta, ma nel codice al link si vede invece che è usato alla fine di ogni case. Scommetto (ma mi farebbe piacere sbagliarmi) che non hai visto i link a fine articolo che ti portano al simulatore wokwi in cui c'è il programma, che puoi modificare, studiare, analizzare durante l'esecuzione.
Lo scoglio più grande per il principiante è millis() al posto di delay(), il che significa ragionare al contrario. Il principiante (lo sono stato anche io) ragiona "di delay()" e non ho spiegazione del perché gli venga naturale, mentre ragionare "di millis()" è durissima (lo è stato anche per me).
Ragionare "di delay()" non può funzionare, ad esempio:
void mioLampeggiante(boot tf) {
if (tf == true) {
digitalWrite(PIN_LED_REED, HIGH); // impegna la cpu per circa 10us
digitalWrite(PIN_LED_YEL, LOW); // impegna la cpu per circa 10us
delay(500); // la cpu non esegue altro codice per 500ms
digitalWrite(PIN_LED_REED, LOW); // impegna la cpu per circa 10us
digitalWrite(PIN_LED_YEL, HIGH); // impegna la cpu per circa 10us
delay(500); // la cpu non esegue altro codice per 500ms
} else {
digitalWrite(PIN_LED_REED, LOW); // impegna la cpu per circa 10us
digitalWrite(PIN_LED_YEL, LOW); // impegna la cpu per circa 10us
}
}
Durante l'esecuzione di questa funzione si impegna la cpu per poco più di 1 secondo, durante questo
tempo la cpu non può eseguire altro codice. Evitare sempre le funzioni e in genere il codice che monopolizza l'uso esclusivo della CPU per un tempo troppo alto o comunque incompatibile con la reattività richiesta dall'applicazione.
Allora mentre lampeggia deve eseguire il codice che monitora lo stato del secondo sensore, quello in uscita (credo), se è così durante ogni delay non può eseguire questo codice.
Ok sembra incredibile, ma io non riesco più a ragionare "di delay()".
Ciao.
È un po' come passare dal ragionare nel dominio del tempo, che viene spontaneo, al ragionare nel dominio della frequenza... ![]()
Qui basta che invece di fare una lettura di un sensore ne fai due e le sommi
Un banale + oppure sapendo che stiamo ragionando in boolean basta un semplice or
Per la questione tempi e millis devi ribaltare il programma
per noi neofiti non è cosi semplice, come chiedessi a qualcuno di fare il mio lavoro e non è un ing. meccanico
Il problema nel caso specifico è soprattutto di analisi/metodo/design.
Vediamo le specifiche iniziali ben espresse:
1. Il treno viene rilevato da uno dei sensori IR
2. Inizia il suono della campanella e si accendono i led rossi 2 per lato in modo alternato.
3. Dopo 5 sec. iniziano a scendere le barriere e continua il suono della campanella ed i led restano accesi.
4. A barriere abbassate dopo 5 sec. si ferma la campanella e restano i led accesi fissi.
5. Rimane questa condizione fin quando il treno impegna ancora il secondo IR dello stesso binario.
6. Liberato il secondo IR le sbarre si alzano i led rimango accesi
7. A barriere completamente alzate i led si spengono dopo 2 sec.
Si può essere tentati di combinare le istruzioni una dopo l'altra per realizzare esattamente questi passi, e non è sbagliato, almeno finché non sorge la necessità di gestire più "processi" contemporaneamente. Cosa che qui invece è una specifica già presente dall'inizio (il lampeggio dei LED è un processo parallelo, con tempi indipendenti rispetto alla logica principale). Quindi si parte immediatamente dal caso "più difficile" (che poi è anche il più comune... sono gli esempi di sketch che si trovano in giro ad essere il caso più limitato e meno generale).
Cosa intende allora @clarksavagejr con: "Per la questione tempi e millis devi ribaltare il programma"? Che il metodo di mettere un'istruzione dopo l'altra (comprese le pause con delay) per codificare la descrizione iniziale non è più applicabile.
Quando servono più processi "contemporanei", si deve dedicare a ciascuno una "fettina di attenzione" a rotazione molto rapidamente (dove è presente un sistema operativo questa "attenzione" ai task la realizza il sistema stesso, qui no). Per "ribaltare il programma" la descrizione del funzionamento iniziale va riformulata, domandandosi prima di tutto quali e quanti siano i diversi stati di funzionamento in cui il sistema si può trovare (e permanere fino al verificarsi di qualche condizione).
Attenendosi strettamente alla descrizione informale ne individuo sette, né uno di più, né uno di meno (e nota come questa forma schematica e sintetica può prendere tranquillamente il posto di tutta la descrizione informale):
1. (Attesa treno) né campanella né LED
2. (attesa 5 secondi) con campanella e LED lampeggianti
3. (chiusura barriera) con campanella e LED lampeggianti
4. (attesa 5 secondi) con campanella e LED lampeggianti
5. (attesa allontanamento) con LED fissi
6. (apertura barriera) con LED fissi
7. (attesa 2 secondi) con LED fissi
Poi sono da individuare le condizioni (eventi) attese in ogni fase:
1. (Attesa treno)
se rilevato da uno dei due sensori: ....
2. (attesa 5 secondi)
se timeout: ....
3. (chiusura barriera)
se fine chiusura: ....
4. (attesa 5 secondi)
se timeout: ....
5. (attesa allontanamento treno)
se nessun rilevamento sensori: ....
6. (apertura barriera)
se fine apertura: ....
7. (attesa 2 secondi)
se trascorsi: ....
E per ogni condizione attesa, va specificato cosa vogliamo fare:
1. (Attesa treno)
se rilevato da uno dei due sensori:
avvia lampeggio LED
accendi campanella
registra tempo attuale
passa a fase 2
2. (attesa 5 secondi)
se timeout:
passa a fase 3
3. (chiusura barriera)
se fine abbassa:
registra tempo attuale
passa a fase 4
4. (attesa 5 secondi)
se timeout:
spegni campanella
ferma lempeggo LED
accendi LED fissi
passa a fase 5
5. (attesa allontanamento treno)
se nessun rilevamento sensori:
passa a fase 6
6. (apertura barriera)
se fine apertura:
registra tempo attuale
passa a fase 7
7. (attesa 2 secondi)
se trascorsi:
spegni LED
passa a fase 1
Ora il "design" del procedimento è completamente "ribaltato", ma ripetendo continuamente il ragionamento: "in che fase sono? è vera una condizione attesa? allora faccio qualcosa ed eventualmente cambio fase" si vede che realizza esattamente il procedimento descritto inizialmente.
L'esecuzione di questo "codice" rappresenta una "fettina" (della durata di una ventina di microsecondi) e per la stragrande maggioranza del tempo controlla solo in che fase si è, e se si verifica una condizione. Se si, allora farà qualcosa, altrimenti niente, e si riparte da capo con un altro "giro di loop".
Dal momento che questo processo eseguito "a fettine" non monopolizza il tempo (si dice non bloccante), è possibile aggiungere in sequenza altri N processi (sempre progettati con criterio non bloccante), ad esempio il lampeggio di LED o qualsiasi altra cosa, dando l'impressione di un funzionamento realmente in parallelo.
Questo il metodo in generale. Il resto è l'implementazione/codifica pratica che può variare in molti aspetti.
