Salve a tutti. Premesso che ho cercato in ogni fiume mare e lago ma non ho trovato una soluzione pertanto mi trovo a scrivere questo problema.
Ho un sensore ad ultrasuoni che si dovrebbe spostare a destra e a sinistra per leggere la distanza dall'ostacolo.
Dopo aver letto la distanza con un ritardo (NON BLOCCANTE) mi dovrebbe cambiare di stato con un cambio di case: .
Quello che non capisco è perchè appena entra nel case 0 tutto funziona ed aspetta (nell'esempio 5 secondi) prima di spostarsi sul case 1.
Quando arriva al case 1 nonostante abbia messo ancora un controllo del tempo con i millis mi passa direttamente al case iniziale.
Allego codice di esempio con anche la figura del serial monitor
//test lettura ultrasuoni non bloccante
int stato = 0;
float distanzaAnteriore;
float distanzaSinistra;
float distanzaDestra;
unsigned long tempoUltrasuoniAnteriore;
unsigned long millisPrecedentiDX = 0;
unsigned long millisPrecedentiSX = 0;
const long intervallo = 5000;
#define PIN_TRIG_ANT 5 //pin ultrasuoni anteriore
#define PIN_ECHO_ANT 4 //pin ultrasuoni anteriore
void setup()
{
Serial.begin(9600);
//setPinUltrasuoni
Serial.println("Inizio");
pinMode(PIN_TRIG_ANT, OUTPUT);
pinMode(PIN_ECHO_ANT, INPUT);
digitalWrite(PIN_TRIG_ANT, LOW);
}
void loop()
{
switch(stato)
{
case 0:
{
//inizia qui e quando passano 5 secondi va a stato 1
giraSensDx();
unsigned long millisCorrenti = millis();
if(millisCorrenti - millisPrecedentiDX >= intervallo)
{
millisPrecedentiDX = millisCorrenti;
distanzaDestra = distanzaAnteriore;
Serial.println("stato 0");
Serial.println(distanzaDestra);
stato = 1;
}
}
break;
case 1:
{
giraSensSx();
unsigned long millisCorrenti = millis();
if(millisCorrenti - millisPrecedentiSX >= intervallo)
{
millisPrecedentiSX = millisCorrenti;
distanzaSinistra = distanzaAnteriore;
Serial.println("stato 1");
Serial.println(distanzaSinistra);
stato = 0;
}
}
break;
}
}
void giraSensDx()
{
controlloDistanzaOstacoloAnteriore();
}
void giraSensSx()
{
controlloDistanzaOstacoloAnteriore();
}
void controlloDistanzaOstacoloAnteriore()
{
digitalWrite(PIN_TRIG_ANT, HIGH);
delayMicroseconds(10);
digitalWrite(PIN_TRIG_ANT, LOW);
tempoUltrasuoniAnteriore = pulseIn(PIN_ECHO_ANT, HIGH);
distanzaAnteriore = 0.03438 * tempoUltrasuoniAnteriore/2;
}
Anni fa ho letto di problemi con nomi di variabili lunghi pur se sotto il limite nominale del compilatore
Penso in particolare a quella tempoultrasuoniantariore
Che potrebbe mandare in confusione il compilatore
Ma io per primo ci credo poco
Provare non costa nulla comunque
Un secondo dubbio viene dalle due variabili con lo stesso nome nella stessa funzione
Le due milliscorrenti
Che dichiari separatamente nei due case
Non mi piace per nulla
Io le eliminerei direttamente e userei direttamente milli()
Oppure una sola variabile globale o dichiarata locale una sola volta all'inizio della funzione
Quando vai a valutare il case 1 per la prima volta millisPrecedentiSX è uguale a 0 perché non lo hai ancora assegnato da nessun'altra parte.
Il valore di millis() però è già maggiore di 5000 (e di conseguenza millisCorrenti) perché hai già valutato il case 0 e fatto tutto il resto.
Grazie della info a cui non avevo pensato. Quindi per risolvere il problema dovrei impostare la variabile intervallo a intervallo + millis()? O ancora meglio prevederne una diversa per ogni stato?
Questa è una semplice sottrazione, vogliamo che il risultato sia uguale o maggiore di 5000, giusto?
Sembra assurdo ricordare come si fa la sottrazione, ma ho constatato che basta avere delle variabili al posto dei numeri per mandare in confusione il principiante. Quindi sostituisco alle variabili i numeri:
Nello specifico il case 0 viene eseguito in ciclo per 5000ms, fino al momento in cui la sottrazione vale 5000 (o maggiore) per cui il corpo della if viene eseguito e al suo interno prenoti lo stato 1 per essere eseguito nel successivo ciclo di loop. All'interno del corpo della if allora carichi il timer con il valore attuale di millis(), es:
if(millisCorrenti - millisPrecedentiDX >= intervallo)
{
millisPrecedentiDX = millisCorrenti;
distanzaDestra = distanzaAnteriore;
Serial.println("stato 0");
Serial.println(distanzaDestra);
millisPrecedentiSX = millisCorrenti; // salva il valore corrente di millis()
stato = 1;
}
stessa cosa dovrai fare nel case 1, ma con la variabile millisPrecedentiDX = millisCorrenti.
Nell'ambito della progettazione c'è un acronimo che, per quanto un po' rude, è estremamente significativo: K I S S, ovvero Keep It Simple, Stupid.
Tralasciando l'ultima parola che è decisamente fuori luogo, per quello che ti serve basta una sola variabile condivisa tra gli stati usata per tenere traccia di quando avviene il passaggio di stato. Inoltre, come ti hanno già suggerito, la variabile millisCorrenti è decisamente ridondante.
Prego, purtroppo mi sono accorto solo ora che c'è una assegnazione da eliminare marcata (1). Questa assegnazione si usa quando si vuole ripetere il codice ad intervallo specificato per tutta la durata dell'applicazione. Ma all'interno del case 0 quando scade il tempo passi ad eseguire il case 1, la conseguenza è che il case 0 non viene eseguito. Lo switch case seleziona un solo case per ciclo e i vari case presenti sono attivi in modo mutuamente esclusivo. Detto semplicemente, il case (o stato) attivo disattiva tutti gli altri.
Ad intuito credo che la necessità sia di eseguire 1 case alla volta ogni 5 secondi, allora il suggerimento di @cotestatnt è da esaminare ed approfondire poiché è una soluzione ottimale senza stravolgere il programma attuale.
Hai già il codice per muovere a destra e sinistra il sensore ad ultrasuoni?
Si. Però avevo impostato i delay bloccanti perché iil sensore aveva bisogno di stabilizzarsi un attimo. Finito il blocco leggeva bene la misura e si spostava a sinistra. Con i delay funziona bene ma volevo assolutamente evitarli. Stasera metto in pratica le info ricevute e le provo ad inserire nel programma completo.
... allora, evitiamo di demonizzare la funzione delay() !
Ci sono casi in cui si può tranquillamente usare (dove bloccare completamente il programma non ha effetti collaterali) e dove invece occorre completamente evitarli perché, durante l'intervallo di tempo, si hanno altre cose da fare.
Valuta sempre in che condizioni ti trovi e decidi di conseguenza ...
non si tratta di demonizzare delay(), ma di incoraggiare i principianti ad utilizzare fin da subito le buone prassi di programmazione.
L’uso di delay() su Arduino è un compromesso accettabile solo in sketch estremamente semplici, dove la temporizzazione bloccante non causa problemi o magari in sketch che hanno solo uno scopo dimostrativo.
Tuttavia, negli sketch più complessi, questa funzione introduce ritardi che impediscono la gestione efficiente di eventi concorrenti, rendendo il codice meno reattivo e meno scalabile.
Purtroppo, molti tutorial ed esempi ufficiali di Arduino e librerie abusano di delay(), trasmettendo ai principianti un paradigma di programmazione bloccante che, in progetti più avanzati, diventa immediatamente un ostacolo. Approcci basati su macchine a stati o su millis() sono decisamente più adatti per sviluppare codice robusto e reattivo.
Se dovessi insegnare le basi di Arduino a qualcuno, inizierei con l'uso di millis(); solo dopo aver imparato bene a usarlo fino a farlo diventare spontaneo, aggiungerei che esiste anche delay(), che può essere usato nei casi che lo consentono.
Prima imparare bene il sistema più difficile e di uso generale; poi, dopo averci faticato tanto, scoprire che esiste un sistema più semplice che in alcuni casi è vantaggioso. Mi sembra di ricordare che un insegnante fece così a scuola... Non so se era il guadagno del transistor a doppio carico, che dopo tanti calcoli veniva molto semplicemente approssimato a Rc/Re.
Dipende da come sei abituato a sviluppare, a me capita raramente di avere l'esigenza di aspettare "localmente" e non fare altro.
Nascondere un costrutto dietro un'istruzione dal nome semplice e accattivante non la rende per forza di cose preferibile.
Questa è l'implementazione del delay() nel core AVR di Arduino, nulla di trascendentale (anche se mi sembra fin troppo complessa per quello che deve fare): perché un principiante non dovrebbe imparare a usare fin da subito lo stesso approccio con millis() in tutte le sue sfaccettature e con tutta la flessibilità che questo comporta?
void delay(unsigned long ms)
{
uint32_t start = micros();
while (ms > 0) {
yield();
while ( ms > 0 && (micros() - start) >= 1000) {
ms--;
start += 1000;
}
}
}