Uscire da un ciclo for durante un fade di un led rgb

Ciao gente! Sto cercando di venire a capo con questo problema che mi da filo da torcere. Ho un led RGB e un sensore HC-SR04. Praticamente il led RGB deve fare il fade sul colore blu di continuo, e nel momento in cui mi avvicino al sensore HC-SR04 a meno di 30 cm, il led dovrebbe smettere di lampeggiare blu immediatamente e diventare subito rosso. Il mio problema è che il led rosso non scatta come dovrebbe subito quando metto la mano davanti al sensore, ma al termine del for (int i=0; i<=255; i+=5) all’interno della funzione fadeBlu(). Vi chiedo se potete aiutarmi! Grazie mille in anticipo!

#include <HCSR04.h>

int red = 13;
int green = 12;
int blue = 11;
int trigger = 9;
int echo = 8;
int halt;

void setup(){
  pinMode(red, OUTPUT);
  pinMode(green, OUTPUT);
  pinMode(blue, OUTPUT);
  pinMode(trigger, OUTPUT);
  pinMode(echo, INPUT);
  Serial.begin(9600);
}

void redLed(){
  rgb(255,0,0);
  delay(200);
}

void rgb(int r, int g, int b){
  analogWrite(red, r);
  analogWrite(green, g);
  analogWrite(blue, b);
}

void fadeBlu(){
  
    for (int i=0; i<=255; i+=5){
    rgb(0,0,i);
    delay(40);
    }
    
    for(int i=255; i>=0; i-=5) {
      rgb(0,0,i);
      delay(40);

    } 
}

void human() {
  
  UltraSonicDistanceSensor distanceSensor(trigger, echo);
  double distance = distanceSensor.measureDistanceCm();

  // with obstacles
  if (distance<=30) {

    redLed();
  }
  else{
  // if no obstacles 
    fadeBlu();
  }
}

void loop(){
  human();
}

ma al termine del for (int i=0; i<=255; i+=5) all’interno della funzione fadeBlu().

Esattamente, per ogni ciclo del loop 40ms moltiplicati 255 cicli = 10200ms (10,2s)

Poiché i cicli sono due, entrambe durano 20,4 secondi.

Devi trovare il modo di accendere i led uscire da void fadeBlu() per poi rientrarci, grazie a ciò il controllo passa per il loop ed esegue double distance = distanceSensor.measureDistanceCm();
molto spesso.

void fadeBlu() {
    static byte index = 0;
    rgb(0,0,i);
    index++;
    delay(40);
}

Questa funzione richiamata continuamente si comporta come un for, ma attenzione che quando
index == 255 il risultato di index++ sarà 0 (zero), perché va in overflow.

Per fare il saliscendi di index puoi usare switch case, ma questo viene dopo l’importante che
comprendi il problema e come aggirarlo.

Anche il delay(200); in redLed() impedisce per 200ms di eseguire double distance = distanceSensor.measureDistanceCm();

Ciao.

Per intanto il ciclo fa solo 50 passi, ovvero 2 secondi a salire e altrettanto a scendere

Per il resto concordo, quasi

Io direi di togliere la dichiarazione dell’oggetto sensore da una funzione e dichiararlo globale

Poi eliminerei la dannosa fadeblu

Per fare i calcoli dentro la loop usando millis e non delay

Sto provando con un approccio diverso:

void fadeBlu()
{
    fadeBlueCycle(0, 255, 5);
    fadeBlueCycle(255, 0, -5);
}

void fadeBlueCycle(uint8_t start, uint8_t end, int8_t step)
{
    uint8_t i = start;
    bool condition = true;
     
    while (condition) // 
    {
        
        rgb(0, 0, i);
        delay(40);

    //    Serial.print("halt in step");
    //    Serial.print(step);
    //    Serial.println(halt);

        condition = (distance<=30);
        i += step;

    }
}

In pratica ho impostato start ed end per il fade e uno step di valore 5 per cambiarre la luminosità del led.
Poi ho impostato la condizione true prima del while e faccio il controllo. Ora “funziona” però adesso il led non fa più il fade ma lampeggia stile volante della polizia. Ho controllato con le println che vedete commentate e dal monitor seriale ottengo questo ciclo infinito:

halt in step 50
halt in step -50
halt in step 50
halt in step -50
halt in step 50
halt in step -50
halt in step 50
halt in step -50
halt in step 50
halt in step -50
halt in step 50
halt in step -50
halt in step 50
halt in step -50
halt in step 50
halt in step -50
halt in step 50
halt in step -50
halt in step 50
halt in step -50
halt in step 50
halt in step -50
halt in step 50
.....

Molto strano perchè parte subito da 50 e -50, quando in pratica dovrebbe fare tutto il ciclo da 0 a 255…

Mi aggancio ai suggerimenti di maurotec e standardoil per chiarirti alcune cose anche io e “riassumerti” come dovrebbe essere il codice.

Prima alcuni piccoli consigli:

int red = 13;
int green = 12;
int blue = 11;
int trigger = 9;
int echo = 8;

I pin sono valori interi tra 0 e, su una UNO, al massimo 13, quindi inutile sprecare spazio con gli “int”, basta “byte”. Inoltre sono costanti, quindi inutile usare delle variabili, meglio dei simboli (che per convenzione vanno in maiuscolo per distinguerli dalle variabili). Inoltre le configurazioni dei pin in genere io li evidenzio con un prefisso “P_”, quindi:

#define P_RED 13
#define P_GREEN 12
#define P_BLUE 11
#define P_TRIG 9
#define P_ECHO 8

e quindi adatta di conseguenza il resto del codice. Ignoro la “int halt;” perché la dichiari ma non l’hai (ancora?) usata, ma se fa quello che immagino, potresti dichiararla “bool”.

Quindi passiamo al codice vero e proprio.

Come dice standardoil inutile creare un oggetto ogni volta che devi misurare, crealo una sola volta all’inizio e poi usa sempliemente quello (a parte che sta libreria HCSR04 non mi piace molto, è “logorroica”…;)) per leggere il valore. Stessa cosa per la variabile “distance”.

Poi come dice Maurotec per fare il fade devi prevedere “un passo per volta” perché altrimenti il codice resta bloccato su quell’effetto e non passa per la lettura della distanza se non una volta ogn 20 secondi.
Ora quindi il problema è come fare questo, ed in parte te l’ha indicato Maurotec ossia devi sfruttare la loop() e fare “un passo” del fade alternandolo alla lettura della distanza o, meglio, leggere la distanza diciamo 5 volte al secondo (più che sufficiente) e per fare questo usi millis(). Per gestire il fade up/down puoi inglobare tutto nella fadeBlu().

Insomma, vedi come ti ho modificato il codice, provalo e vedi come è stato impostato. Non ti assicuro che non ci siano problemi, visto che non l’ho provato (e non mi piace dare “pappa pronta”;)), quindi nel caso devi provare intanto tu a fare debug… :wink:

#include <HCSR04.h>

#define P_RED 13
#define P_GREEN 12
#define P_BLUE 11
#define P_TRIG 9
#define P_ECHO 8
//int halt;

UltraSonicDistanceSensor distanceSensor(P_TRIG, P_ECHO);
double distance;

// Per fadeBlu() serve per il valore attuale e per capire la 
// direzione del prossimo step
byte fadeVal = 0;
bool fadeUp = true;
// Per usare millis() al posto dei delay()
unsigned long tmrfade = 0;
unsigned long tmrDist = 0;
// Intervalli di tempo per lettura distanza e fade
#define TMR_DIST 200
#define TMR_FADE 40

void setup(){
  Serial.begin(9600);
  Serial.print("setup...");
  pinMode(P_RED, OUTPUT);
  pinMode(P_GREEN, OUTPUT);
  pinMode(P_BLUE, OUTPUT);
  pinMode(P_TRIG, OUTPUT);
  pinMode(P_ECHO, INPUT);
  Serial.println("OK!");
}

void redLed(){
  rgb(255,0,0);
  delay(200);
}

void rgb(int r, int g, int b){
  analogWrite(P_RED, r);
  analogWrite(P_GREEN, g);
  analogWrite(P_BLUE, b);
}

void fadeBlu(){
  // Controllo i limiti
  if ( fadeVal == 255 && fadeUp )
    fadeUp = false;
  else if ( fedeVal == 0 && !fadeUp )
    fadeUp = true;
  if ( fadeUp )
    ++fadeVal;
  else
    --fadeVal;
  rgb(0,0,fadeVal);
}

void human() {
  if ( millis()-tmrDist >= TMR_DIST ) {
    distTimer = millis();
    distance = distanceSensor.measureDistanceCm();
  }
  // Gestione led in base alla presenza di ostacoli
  if (distance<=30) {
    redLed();
    // resetta il fade
    fadeVal = 0;
    fadeUp = true;
  } else {
    if ( millis()-tmrFade >= TMR_FADE )
      fadeBlu();
  }
}

void loop(){
  human();
}

hyuma:
Sto provando con un approccio diverso:

...

Hai solo rimescolato le carte, non hai cambiato approccio

Adesso hai variabili globali (che prima erano locali) che interroghi durante una funzione, ma non aggiorni se non a funzione terminata

Ergo : hai gli stessi problemi ti prima e non te ne rendo conto, perché sei partito senza capire i suggerimenti

Suggerimenti che ti sono stati dati corretti, e che quindi considero inutile ripetere

Si ma non ti scaldare... sto solo provando cose diverse.

void human() {
  if ( millis()-tmrDist >= TMR_DIST ) {
    distTimer = millis();
    distance = distanceSensor.measureDistanceCm();
  }
  // Gestione led in base alla presenza di ostacoli
  if (distance<=30) {
    redLed();
    // resetta il fade
    fadeVal = 0;
    fadeUp = true;
  } else {
    if ( millis()-tmrFade >= TMR_FADE )
      fadeBlu();
  }
}

void loop(){
  human();
}

ciao, grazie, con questo funziona, hai dimenticato un tmrFade++ dopo il fadeBlu(); però voglio riuscire a far andare il mio codice e capire dove sto sbagliando. Intanto grazie per i tuoi suggerimenti :wink:

Cosa ne diresti di metterlo tutto, il codice?

Così vediamo quanto vale halt

Edit

Mi riferivo al post di mezza mattina

Standardoil:
Cosa ne diresti di metterlo tutto, il codice?

Così vediamo quanto vale halt

Edit

Mi riferivo al post di mezza mattina

No, adesso provo, quando funziona posto il codice, non mi va di avere pagine di flame e di robe dette e stradette.

Auguri

hyuma:
hai dimenticato un tmrFade++ dopo il fadeBlu();

No, non ho dimenticato nulla, ci sono alcuni errorini che ho inserito per non darti la "pappa pronta".. :wink: La cosa deve servirti anche per capire come impostare più correttamente un codice, e come fare debug.

però voglio riuscire a far andare il mio codice e capire dove sto sbagliando.

Beh il "dove sto sbagliando" te l'abbiamo detto in tre 8) ma per sistemare il codice di fatto devi usare millis() e quindi inutile rimettere mano al tuo listato originario, usa quello che ti ho suggerito (con le correzioni e modifiche che riuscirai ad apportare :wink: ).

hyuma:
ciao, grazie, con questo funziona, hai dimenticato un tmrFade++ dopo il fadeBlu(); però voglio riuscire a far andare il mio codice e capire dove sto sbagliando. Intanto grazie per i tuoi suggerimenti :wink:

Stai semplicemente sbagliando approccio.
Stai pensando che tutto il fade debba essere inserito in un singolo giro di loop, invece l’approccio corretto è di distribuirlo in più giri di loop.
In questo modo non si blocca nulla e puoi fare altre operazioni “in parallelo”.
Fai tutti i tuoi test, così ti rendi meglio conto della differenza di approccio e potrai entrare consapevolmente nel club dei NO_DELAY.

… se non una volta ogn 20 secondi…

Socio, è passata in cavalleria la tua indicazione sui 50 passi e quindi sulla durata di 2s.
Continua a sfuggire che l’incremento nel for sia di 5 e non di 1 ;D
Cambia nulla, però…

docdoc:
Beh il “dove sto sbagliando” te l’abbiamo detto in tre 8)

E il quarto vien da se :smiley:

Maurizio

Ragione tieni

Anche l'indicazione (molto) velata da dove arrivi lo zero finale nella stampa è stata ignorata

Dimmi socio: tu hai fatto informatica all'università?

maubarzi:
Socio, è passata in cavalleria la tua indicazione sui 50 passi e quindi sulla durata di 2s.

Non ho capito se tu stia dicendo a me, visto che hai quotato una mia frase, ma non ho mai citato "50 passi", e non sono "socio".. :wink:

Ma è vero, chiaramente non sono 20 secondi ma 4 dato l'incremento di 5, comunque è una precisazione che cambia ben poco la sostanza (che è: "non si gestiscono così quelle cose" :D)

PS: comunque se fai capire meglio a chi stai parlando si fa tutti prima, soprattutto se quoti una persona ma quello che scrivi sotto è rivolta ad un'altra... :wink:

A tutti: Possiamo evitare tutte queste battutine e frecciatine o devo … veramente cominciare a bannarvi?

SOLO RISPOSTE PURAMENTE TECNICHE, NESSUNA BATTUTA O APPREZZAMENTO SUGLI UTENTI E SE NON SEGUONO QUELLO CHE DITE … PEGGIO PER LORO, E’ L’ULTIMA VOLTA CHE VE LO RICORDO.

Guglielmo

Standardoil:
...hai fatto informatica all'università?

yess

Maurizio

Allora c’è un errore nel mio codice ma è intuitivo risolverlo, comunque correggo.

void fadeBlu() {
    static byte index = 0;

    rgb(0, 0, index);
    index++;
    delay(40);
}

questa funzione impegna in modo esclusivo la CPU per poco più di 40ms, poi torna al chiamante.

Per chiarire chi è il chiamante e il punto di ritorno.

void human() {
 
  UltraSonicDistanceSensor distanceSensor(trigger, echo);
  double distance = distanceSensor.measureDistanceCm();

  // with obstacles
  if (distance<=30) {

    redLed();
  }
  else{
    // if no obstacles
    fadeBlu(); // --> entra in fadeBlu e ritorna dopo circa 40ms
    // <-- punto di ritorno 
  } // termina la if 
}   // esce da questa funzione e ritorna al chiamante che è la funzione loop

Con questa implementazione questa funzione double distance = distanceSensor.measureDistanceCm();
viene eseguita circa ogni 40ms, stessa sorte tocca a tutte le altre istruzioni.

Un tempo di reattività di 40ms è accettabile per molte applicazioni.
Ok doc doc ti ha proposto delle correzioni al codice molto utili, mi riferisco alle macro tramite #define ecc.
Inoltre ha pure introdotto l’uso di millis() che richiede un certo impegno ed è da affrontare in modo isolato
dal tuo problema attuale, così da prendere confidenza.

La sua funzione fadeBlue non contiene alcun delay e impegna la cpu per pochissimo tempo diciamo
1ms per poi ritornare il controllo al chiamante.

void fadeBlu(){
  // Controllo i limiti
  if ( fadeVal == 255 && fadeUp )
    fadeUp = false;
  else if ( fedeVal == 0 && !fadeUp )
    fadeUp = true;
  if ( fadeUp )
    ++fadeVal;
  else
    --fadeVal;
  rgb(0,0,fadeVal);
}

Quindi i tempi di reazione sono molto rapidi ed è quello che ti serve.

PS: a volere essere pignoli fadeBlu dovrebbe essere scritta fadeBlue, ma comunque il senso si capisce.

Ciao.

Io farei semplicemente così:
Se distanza<=30:

  • digitalWrite(P_BLU,0)
  • ogni volta che millis()-t>39:
    – t=millis()
    – i+=5; if (i>510) i=0
    – if (i<256) analogWrite(P_ROSSO, i)
    – else analogWrite(P_ROSSO, 510-i)

Else:

  • analogWrite(P_ROSSO,0)
  • digitalWrite(P_BLU,1)

Sì, ci sono i Write inutilmente ripetuti a ogni giro. Con delle variabili e degli if è possibile evitarlo. Alla fine, però, credo che sia una complicazione inutile.

Datman:
Io farei semplicemente così:

Ma perché, la mia soluzione (vedi la funzione fadeBlui nel post #4) che ha che non va?