Funzione effetto dissolvenza LED

Buongiorno.

Avendo una strip LED che non veniva impiegata in altri progetti, ho deciso di provare a scrivere un codice per simulare l'effetto dissolvenza ( in crescendo) con Arduino.

Il codice che ho scritto si presenta così:

const byte pwmLED = 5;

int valoreFinale = 127;

int valoreCorrente = 0;

long durataDissolvenza = 5000;//5 secs

unsigned long precMillis = 0;

int intervalloDissolvenza = durataDissolvenza / (valoreFinale - valoreCorrente);

void setup() {

  analogWrite(pwmLED, valoreCorrente);
  Serial.begin(9600);
}

void effettoDissolvenza() {

  if (millis() - precMillis >= intervalloDissolvenza) {
    if (valoreCorrente != valoreFinale) {
      valoreCorrente++;
    }

    analogWrite(pwmLED, valoreCorrente);
    Serial.println(valoreCorrente);
    precMillis = millis();
  }
}


void loop() {
  effettoDissolvenza();
}

Esso funziona correttamente, come ho potuto verificare anche dai Serial.print che ho sparso nelle sezioni più interessanti, a fine funzionale, del codice.

Quello che mi piacerebbe fare ora è trasformare tutto questo in una funzione più compatta, con variabili locali, in modo da poter utilizzare questo codice anche in altri progetti (ad esempio, soft start di ventole da PC).
Quindi, ho elaborato il seguente codice:

bool funzioneDissolvenza(int valPartenza, int valArrivo, long durataTransizione)
{
  int intervallo = durataTransizione / (valArrivo - valPartenza);

  if (millis() - precMillis >= intervallo) {
    if (valPartenza != valArrivo) {
      valPartenza++;
    }
    else {
      return 1;
    }

    analogWrite(pwmLED, valPartenza);
    Serial.println(valPartenza);

    precMillis = millis();
  }
}

La dichiarazione della funzione di tipo booleano mi permette di capire quando il regime di transizione giunge al termine.
Tuttavia, c'è un errore che non riesco a raggirare: nella funzione sopra riportata, siccome valPartenza aumenta col tempo, l'intervallo di refresh tra i valori tende ad aumentare, che non è ciò che vorrei (nel codice riportato all'inizio, infatti, il tempo che trascorre tra i vari aggiornamenti risulta costante). Di conseguenza la durata totale della transizione (es. 5 secondi) non viene rispettata.

Chiedo gentilmente una mano nella riscrittura della funzione, in modo tale che funzioni correttamente.
Grazie

Non capisco bene il problema, ma non basta copiare la variabile in un'altra che poi non viene incrementata?

Sì, basterebbe quello per farlo funzionare.
Però, come potrei fare? Idealmente vorrei avere tutte variabili locali. Potrei farlo passando una variabile per valore alla funzione? Scusa ma non sono molto pratico del passaggio di variabili a funzioni in C

Mica è sempre condannabile l'uso di variabili globali! :slight_smile:

Più che altro potrei dover chiamare quella funzione più volte. Ad esempio, potrei voler fare il fase da 0 PWM a un valore tipo 64, poi in seguito riprendere da 128 a 255. Se usassi una variabile esterna, dovrei assegnare ogni volta che richiamo la funzione il valore che voglio che essa abbia, mentre se avessi una funzione solo con variabili all'interno non avrei questo problema

Ogni volta, prima di chiamare la funzione effettoDissolvenza(), se non è in corso (finito) devi azzerare il conteggio del tempo scrivendo:
precMillis = millis();

void loop()
{
Fai_tutto_cio_che_vuoi();
...
if(finito) precMillis=millis();
effettoDissolvenza(0, 255, 3000);
}


bool effettoDissolvenza(int valPartenza, int valArrivo, long durataTransizione)
  {
  byte finito=0;
  int intervallo = durataTransizione / (valArrivo - valPartenza);
  if (millis() - precMillis >= intervallo)
    {
    if (valPartenza != valArrivo)
      {
      valPartenza++;
      }
    else 
      {
      finito=1; return 1;
      }
    analogWrite(pwmLED, valPartenza);
    Serial.println(valPartenza);
    }
  }

Scusa ma non sono molto pratico del passaggio di variabili a funzioni in C

Intanto il problema è quello che ha specificato Datman.

Poi se vuoi generalizzare l'uso di una funzione, potresti creare una struttura dati, dichiarare la variabile di tipo struct e poi passarla come puntatore alla funzione, una cosa di questo tipo:

struct MyData  {
    unsigned long time;
    unsigned int pwmValue;
    
};

void myGenFunc(struct MyData *data) {
    data->pwmValue = 0; // solo per mostrare come accedere ai membri
    data->time = 0;
}

void setup {
     struct MyData myData;  // crei la variabile myData
     myGenFunc(&myData); // chiami la funzione passando il puntatore a myData
}

Chiaramente i membri della struttura sono scelti a caso, e solo un esempio.

Ciao.

Datman:

void loop()

{
Fai_tutto_cio_che_vuoi();
...
if(finito) precMillis=millis();
effettoDissolvenza(0, 255, 3000);
}

bool effettoDissolvenza(int valPartenza, int valArrivo, long durataTransizione)
  {
  byte finito=0;
  int intervallo = durataTransizione / (valArrivo - valPartenza);
  if (millis() - precMillis >= intervallo)
    {
    if (valPartenza != valArrivo)
      {
      valPartenza++;
      }
    else
      {
      finito=1; return 1;
      }
    analogWrite(pwmLED, valPartenza);
    Serial.println(valPartenza);
    }
  }

Scusa ma non mi torna proprio come codice. Non dovrebbe esserci precMillis=millis() dopo il Serial.print? Sennò non raggiungi mai finito=1 e soprattutto non vai avanti nel codice

Maurotec:

struct MyData  {

unsigned long time;
    unsigned int pwmValue;
   
};

void myGenFunc(struct MyData *data) {
    data->pwmValue = 0; // solo per mostrare come accedere ai membri
    data->time = 0;
}

void setup {
    struct MyData myData;  // crei la variabile myData
    myGenFunc(&myData); // chiami la funzione passando il puntatore a myData
}

Ti ringrazio ma mi sembra un pò esagerato per ciò che devo fare.

Domanda: potrei modificare il codice della funzione di partenza nel seguente modo? Sfrutterei il fatto che una variabile di tipo static viene dichiarata una sola volta all'inizio della funzione e poi non più toccata finchè non esco dalla funzione e poi rientro, giusto?

bool funzioneDissolvenza(int valPartenza, int valArrivo, long durataTransizione)
{
  static int valInferiore = valPartenza; // modificato
  int intervallo = durataTransizione / (valArrivo - valInferiore);

  if (millis() - precMillis >= intervallo) {
    if (valPartenza != valArrivo) {
      valPartenza++;
    }
    else {
      return 1;
    }

    analogWrite(pwmLED, valPartenza);
    Serial.println(valPartenza);

    precMillis = millis();
  }
}

finito divenuta 1 quando valPartenza raggiunge valArrivo.

Mattia9914:
Idealmente vorrei avere tutte variabili locali.

Allora si usano le locali statiche (static), che sono locali in quanto dichiarate/definite all'interno della funzione, ma mantengono il valore come le globali.

void fn()
{
    static int a = 10;  // inizializzata solo la prima volta
}

Claudio_FF:
Allora si usano le locali statiche (static), che sono locali in quanto dichiarate/definite all'interno della funzione, ma mantengono il valore come le globali.

void fn()

{
    static int a = 10;  // inizializzata solo la prima volta
}

Ti ringrazio. Volendo è possibile fare un assegnamento del tipo riportato nel mio messaggio precedente tra una static e una che variabile il cui valore cambia nel corso del programma?

Datman:
finito divenuta 1 quando valPartenza raggiunge valArrivo.

Però nell'esempio che hai riportato non viene aggiornato precMillis()=millis() quando aumento valoreIniziale, quindi quella parte dell'if verrà eseguita una sola volta e basta

Mattia9914:
è possibile fare un assegnamento del tipo riportato nel mio messaggio precedente tra una static e una che variabile il cui valore cambia nel corso del programma?

Se ti riferisci all'ultimo codice del post #8 si, quell'assegnamento si può fare, ma le variabili non sono usate correttamente... valPartenza (in quanto parametro in ingresso alla funzione) è una variabile locale che ha ogni volta il valore passato alla funzione... in sostanza non può mai incrementare mantenendo il valore tra una chiamata e l'altra. La variabile che deve incrementare tra il minimo e il massimo specificato dai parametri è quella statica.

Claudio_FF:
Se ti riferisci all’ultimo codice del post #8 si, quell’assegnamento si può fare, ma le variabili non sono usate correttamente… valPartenza (in quanto parametro in ingresso alla funzione) è una variabile locale che ha ogni volta il valore passato alla funzione… in sostanza non può mai incrementare mantenendo il valore tra una chiamata e l’altra. La variabile che deve incrementare tra il minimo e il massimo specificato dai parametri è quella statica.

Ti ringrazio. Ho modificato il codice secondo le tue indicazioni e ora funziona correttamente.
Ora si presenta così:

bool funzioneDissolvenza(int valPartenza, int valArrivo, long durataTransizione)
{
  static int valInferiore = valPartenza; // modificato
  int intervallo = durataTransizione / (valArrivo - valPartenza);

  if (millis() - precMillis >= intervallo) {
    if (valInferiore != valArrivo) {
      valInferiore++;
    }
    else {
      return 1;
    }

    analogWrite(pwmLED, valInferiore);
    Serial.println(valInferiore);

    precMillis = millis();
  }
}

Ti ho aggiunto anche un punto karma :wink:

Visto che c'è un codice funzionante, propongo una variante contenente qualche "trucchetto", che tra l'altro si presta bene ad essere "resettata", basta riportare a 1 il flag 'primoGiro' ad esempio tramite l'informazione contenuta in un parametro aggiuntivo. E tutte le variabili di lavoro sono entrocontenute.

bool funzioneDissolvenza(int valPartenza, int valArrivo, long durataTransizione)
{
    static bool primoGiro = 1;
    static bool finito;
    static int val;
    static uint32_t precMillis;
    static long intervallo;

    if (primoGiro)
    {
        val = valPartenza;
        intervallo = durataTransizione / (valArrivo - valPartenza);
        analogWrite(pwmLED, val);
        precMillis = millis();
        finito = 0;
        primoGiro = 0;
    }

    if (!finito  &&  (millis()-precMillis >= intervallo)) 
    {
        precMillis += intervallo;
        analogWrite(pwmLED, ++val);
        finito = (val == valArrivo);
    }

    return finito;
}

Grazie. Non sapevo del fatto che si potessero comparare due valori mettendoli tra parentesi () per vedere se venisse restituito true o false