kitt knight rider

Ciao a tutti, con un mio amico ci stiamo divertendo a creare il mitico effetto che aveva la macchina di supercar, il famoso kitt…
Ho trovato in rete molti esempi, ma tutti utilizzano delay come tempo di attesa.

void setup(){
for (byte pin=2;pin<14;pin++){
pinMode(pin,OUTPUT);}
}

void loop(){
 byte t=20;
//---------->>> for this side
for (byte i=2;i<14;i++{
     digitalWrite(i,HIGH);
     delay (t);
     digitalWrite(i+1,HIGH);
     delay (t);
     digitalWrite(i+2;HIGH);
     delay (t);
     digitalWrite(i,LOW);
     delay (t);
     digitalWrite(i+1,LOW);
}
//  <<<-------- for this side
for (byte i=13;i>1;i--){
     digitalWrite(i,HIGH);
     delay (t);
     digitalWrite(i-1,HIGH);
     delay (t);
     digitalWrite(i-2;HIGH);
     delay (t);
     digitalWrite(i,LOW);
     delay (t);
     digitalWrite(i-1,LOW);
}
}

Come ben sappiamo delay è bloccante ed io volevo provare a modificare questo scketch funzionante con i millis, in modo da non bloccare arduino, facendogli fare altri controlli
Avete qualche suggerimento da darmi??
Io avevo pensato ad una cose del genere:

unsigned long tempo_pausa;
byte volte=0;
void setup(){
for (byte pin=2;pin<14;pin++){
pinMode(pin,OUTPUT);}
tempo_pausa=millis();
}

void loop(){

//---------->>> for this side
for (byte i=2;i<14;i++{
     digitalWrite(i,HIGH);
     if(millis()-tempo_pausa>=20 && volte==0){digitalWrite(i+1,HIGH);tempo_pausa=millis();volte=1;}
     if(millis()-tempo_pausa>=20 && volte==1){digitalWrite(i+2;HIGH);tempo_pausa=millis();volte=2;}
     if(millis()-tempo_pausa>=20 && volte==2){digitalWrite(i,LOW);volte=3;}
     if(millis()-tempo_pausa>=20 && volte==3){digitalWrite(i+1,LOW);volte=0;}
}

Ma non funziona prorpio bene.
Aiutino??
Grazie

Stai sbagliando l’approccio, ovvero tu dici che vuoi lasciar libero Arduino di fare altri controlli, ma finché racchiudi l’effetto sui led all’interno di un cliclo for sarai legato a fare i controlli dento al for e ripeterli anche nell’altro for che gestirà l’effetto in ordine inverso.
Il consiglio che ti posso dare è di strutturare il programma in modo che ao ogni ciclo di loop se è trascorso il tempo tra uno step e il siccessivo venga eseguito quando necessario ovvero qualcosa del tipo:

void loop
{
  if(millis()-tempo_pausa>=20)
  {
     if(direzione==--->)
     {
        switch(i)
        {
           case 0:
              accendi il led N;
              break;
           case 1:
              accendi il led M;
           ecc.
        }
       i++;
       if(i>14)
       {
          i=0;
          direzione = <--;
       }
       
     }
     else
     {
        ..codice per direzione inversa
     }
     tempo_pausa = millis();
  }
  ...altri controlli...
}

manolomao: Ho trovato in rete molti esempi, ma tutti utilizzano delay come tempo di attesa.

eeeh, non hai cercato bene :P

doppiozero: eeeh, non hai cercato bene :P

Interessante, grazie, utile anche per me, non conoscevo la "SoftPWM"! :)

L'effetto è ottimo, ma voglio provare a giocare un poco sull'implementazione perché il codice non l'avrei scritto proprio così... ;)

docdoc: Interessante... ...perché il codice non l'avrei scritto proprio così... ;)

Concordo su entrambi i punti. A naso direi che ci si potrebbe riuscire riducendo di un fattore 10 la complessità del codice, vediamo se mi confermi l'intuito nasale ;) Io lo avrei impostato con un tipo dato (visto il numero di bit e il fatto che non scorre del tutto fuori schermo potrebbe bastare un byte solo) gestito bit a bit con lo shift a destra o a sinistra di un bit alla volta e una variabile per gestire la dissolvenza agli estremi una il contrario dell'altra, cioè considerando in percentuale se la variabile la facciamo crescente in percentuale, l'accensione sarebbe uguale alla variabile, lo spegnimento a 100-variabile. Un ciclo sarebbe composto da una intera dissolvenza e lo shift di 1 bit in un senso o nell'altro. Al momento non ho molto tempo per giocarci e togliermi il dubbio, ma sono curioso di vedere la tua soluzione, se ci giocherai e vorrai condividere ;) Magari abbiamo avuto un'idea simile :P

docdoc: L'effetto è ottimo, ma voglio provare a giocare un poco sull'implementazione perché il codice non l'avrei scritto proprio così... ;)

sicuramente può essere scritto meglio :D l'ho scritto da super nabbo, oggi lo scriverei solo da quasi nabbo :P

Sei tu l'autore? io non direi meglio o peggio ma diverso e magari più compatto, che non necessariamente significa meglio ;) La potremmo far diventare una gara tra nabbi, perchè mi ci metto pure io su questa definizione :P

Piatto ricco mi ci ficco Se mi permettete. ...

maubarzi: Sei tu l'autore?

si l'ho fatto qualche anno fa. Mi ricordo di aver provato anche con un codice più semplice e consendatori per il fading, non era male, peccato che non ci sia il video

doppiozero:
sicuramente può essere scritto meglio :smiley:

Beh una prima “sgrossata” la farei così (non l’ho provata, ovviamente, ma solo scritta di getto):

#include <SoftPWM.h>
#include <SoftPWM_timer.h>

// Numero totale di LED
#define TOTLEDS 9
// Primo pin corrispondente a led[0] (pin consecutivi)
#define MINLED 2

unsigned long int StartTime = 0;
unsigned long int[TOTLEDS] StartTimeOld;
unsigned long int[TOTLEDS] delay;

byte[9] FadeIn;

byte[9] reset = true;

byte forward = false;

int[9] a;

const int TimeFadeIn = 1; // millis
const int TimeFadeOut = 3; //millis
const byte brightness = 110; // pwm value and speed

void setup() {
  SoftPWMBegin();

  Serial.begin(9600);

  for( int i=0; i<=TOTLEDS; ++i ) 
  {
	StartTimeOld[i] = 0;
	delay[i] = 0;
	FadeIn[i] = true;
	reset[i] = false;
	a[i] = 0;
	pinMode(MINLED+i, OUTPUT);
  }
  reset[0] = true;
}

void loop() {
  StartTime = millis();

  for( int i=0; i<=TOTLEDS; ++i )
    delay[i] = StartTime - StartTimeOld[i];

  for( int i=0; i<=TOTLEDS; ++i ) 
  {
    if (FadeIn[i] && delay[i] > TimeFadeIn && reset[i]) 
	{
    SoftPWMSet(i+MINLED, ++a[i]);
    StartTimeOld[i] = StartTime;
    } 
    else if (!FadeIn[i] && delay[i] > TimeFadeOut) 
    {
      SoftPWMSet(i+MINLED, --a[i]);
      StartTimeOld[i] = StartTime;
    }
    if (a[i] == brightness) 
	{
      FadeIn[i] = false;
      reset[i] = false;
      if (forward && i < TOTLEDS)
        reset[i+1] = true;
      if (!forward && i > 0)
        reset[i-1] = true;
    }
	else if ( a[i] == 0 )
	{
	  FadeIn[i] = true;
	}
  }
}

Non assicuro che funzioni, soprattutto perché ho cercato di “dedurre” il funzionamento dal codice iniziale e carpire le cose che si potevano “vettorializzare” e parametrizzare (ti prego, le variabili a, b, c, d eccetera “nun se ponno vede” :wink: infatti non sapendo bene a cosa servano le ho convertite in un array a) .
Ma può essere un punto di partenza, e soprattutto come vedi è mooooolto più compatto :wink:

doppiozero: si l'ho fatto qualche anno fa.

Beh, che dire, l'effetto è notevole. Da nabbo non avevo pensato al fading, ci sono arrivato solo dopo aver guardato il codice. Anche la soluzione con i condensatori è interessante, vanno calcolate bene le resistenze per dare la giusta durata dell'effetto, il dubbio è che ad un certo punto c'è il gradino quando si scende sotto la tensione minima del led, forse con il pwm, a naso, è più graduale, meno spigoloso.

fabpolli:
Stai sbagliando l’approccio, ovvero tu dici che vuoi lasciar libero Arduino di fare altri controlli, ma finché racchiudi l’effetto sui led all’interno di un cliclo for sarai legato a fare i controlli dento al for e ripeterli anche nell’altro for che gestirà l’effetto in ordine inverso.
Il consiglio che ti posso dare è di strutturare il programma in modo che ao ogni ciclo di loop se è trascorso il tempo tra uno step e il siccessivo venga eseguito quando necessario ovvero qualcosa del tipo:

void loop

{
  if(millis()-tempo_pausa>=20)
  {
    if(direzione==—>)
    {
        switch(i)
        {
          case 0:
              accendi il led N;
              break;
          case 1:
              accendi il led M;
          ecc.
        }
      i++;
      if(i>14)
      {
          i=0;
          direzione = <–;
      }
     
    }
    else
    {
        …codice per direzione inversa
    }
    tempo_pausa = millis();
  }
  …altri controlli…
}

Hai ragione, ragionandoci è davvero un errore madornale…

Grazie a tutti per i consigli... La libreria SoftPWM.h non la conoscevo....ci guarderò.... Ora il mio amico mi chiede se il "giochino" è fattibile farlo cno 16 led, utilizzando il MCP2301. So che è un I/O expander in I2C... Dite che è fattibile farlo???

Io farei diversamente: senza usare la libreria, farei un loop() velocissimo che accende i led in sequenza e, al tempo stesso, accende i due led precedenti (se ci sono, a sinistra all'andata e a destra al ritorno, se vogliamo fare una vera scia) con un duty cycle del 40% e del 10%. Il duty cycle si può realizzare semplicemente contando i "passaggi" con un contatore (ad es. byte A): per il 40% resta acceso per valori di A da 1 a 4 e resta spento per valori da 5 a 10; per il 10% resta acceso se A vale 1 e resta spento per valori da 2 a 10. Se vogliamo fare il 15%, basta contare da 1 a 20 e tenerlo acceso da 1 a 3.

Mi hai letto nel pensiero, stavo ragionando su una cosa analoga anche io senza librerie ma solo loop a manetta opportunamente temporizzato. Per pilotare i led stavo pensando ad uno shift register a 8 bit. La dissolvenza la stavo pensando a step del 20%. Per lo scorrimento stavo pensando ad uno shift di un bit su un byte ogni 5 passi che identifica il bit centrale degli 8 della serie (ho controllato sul film quante luci c'erano :P ) che va da un estremo all'altro. Il led subito a destra o subito a sinistra sarebbe in dissolvenza.

Ho provato a valutare anche l'opzione con dissolvenza fatta con condensatore ma mi sono venuti valori abbastanza grandi, dell'ordine del mF da 100uF a 2mF a seconda della durata della dissolvenza con corrente di picco sul led di 20mA. Un po' tantino, per cui ho accantonato l'idea.

Considera il movimento è veloce, quindi l'occhio non riesce ad apprezzare finezze sullo sfumo! :-) Penso che due led di scia siano più che sufficienti.

Trent'anni fa feci delle luci "Supercar" per un venditore di auto che le montava sulle auto dei clienti. Usavo un contatore (CD4017? o forse contavo in binario e poi decodificavo a 16 uscite?) con le uscite combinate in OR con coppie di diodi che realizzavano un contatore avanti-indietro. Poi c'erano dei transistorini piccoli ma robusti (BC637?...) pilotati tramite una resistenza e un condensatore (per la scia) che accendevano delle lampadine da albero di Natale. :-)

EDIT
doccia fatta, sto per uscire mi accorgo di essermi dimenticato i tag code
Ok, piatto ricco mi ci ficco
non ho la libreria, tanto non ho nemmeno i LED necessari, quindi non ho nemmeno provato a compilare
ma questa mi sembra un’idea interessante
cominciamo col premabolo

// di Nelson "StandardOil"
// Idea da sviluppare: Kitt di supercar

#include <SoftPWM.h>

// Numero totale di LED
#define NULED 8
// Primo pin corrispondente al primo led e così via
#define START 2
// Numero di passi da 0 a 100% luminosità
#define STEP 10
//Tempo tra un passo e l'altro
#define TEMPO
// variabile attuale tempo
unsigned long int tempo;

come vedete imposto il solito cinema
numero dei led, primo piedino, variabile tempo
ma imposto anche il numero di passi da fare per passare da 0 a 255 di luminosità
Non mi vergogno di avere un premabolo simile a quelli già postati, si tratta come ho detto del solito circo…
la setup

void setup(void)
{
    SoftPWMBegin();

    // inizializziamo i led
    for (byte i = 0; i < NULED; i++)
    {
        SoftPWMSet(i + START, 0);
    }

    // tempo di fade controllato dalla loop
    SoftPWMSetFadeTime(ALL, 0, 0)
    // primo passo
    tempo = millis();
}

non mi risulta che con la softpwm serva la pinMode, casomai è una riga
anche qui, il solito cinema, si puo’ dire copiato pari pari dai soliti esempi
la loop

void loop(void)
{
    if (millis() - tempo > TEMPO)
    {
        tempo = millis();
        passo();
    }

    // loop cortissima
    // non bloccante
}

è provate a dirmi che non è chiara…
solo un test su millis e il richiamo di una funzione ad hoc
naturalmente il lavoro sporco lo fa la passo()

void passo(void)
{
    // avanza di un passo nella sequenza
    // indicatore della luminosità raggiunta
    static byte lum = 0;
    // indicatore al led da accendere (come dire puntatore)
    static byte indicatore = 0;
    // indicatore al led da spegnere
    static byte spegnimento = 0 + NULED - ACCESI;
    // alzo la sua luminosità
    lum++;
    SoftPWMSet(indicatore + START, int a = (int)255 * lum / PASSI)

    if (lum >= PASSI)
    {
        // led tutto acceso
        // passo a quelli dopo
        lum = 0;
        indicatore++;
        indicatore = indicatore % NULED;
        spegnimento++;
        spegnimento = spegnimento % NULED;
    }

    // e così ho acceso il led
    // devo spegnere quello acceso prima
    SoftPWMSet((spegnimento - 3 + NULED) % NULED, 255 - a)
}

dove abbiamo una serie di variabili static (per tenere tracca da una chiamata con l’altra
del numero del led da accendere
del passo raggiunto con quel led
e, udite udite:
del numero del led da “spegnere”, alcuno led “indietro” rispetto a quello da accendere

ora devo andare, e quindi non posso finire, manca solo una cosa
in questo momento è monodirezionale, la luce non torna indietro
ma si tratta di una banalità: con un paio di operatori modulo e assoluto si mette a posto
adesso bella barista, auguratemi buona fortuna…

naturalmente non andava, qualche variabile con grafia differente, qualche punto e virgola, cose leggere insomma
peraltro fortunato in amore…
ma siccome vi devo ringraziare per gli auguri di ieri metto qui la versione che almeno compila

// di Nelson "StandardOil"
// Idea da sviluppare: Kitt di supercar

#include <SoftPWM.h>

// Numero totale di LED
#define NULED 8
// Primo pin corrispondente al primo led e così via
#define START 2
// Numero di passi da 0 a 100% luminosità
#define PASSI 10
//Tempo tra un passo e l'altro in millisecondi
#define TEMPO 10
// Numero dei led da tenere accesi contemporanemante
#define ACCESI 2
// variabile attuale tempo
unsigned long int tempo;

void setup(void)
{
    SoftPWMBegin();

    // inizializziamo i led
    for (byte i = 0; i < NULED; i++)
    {
        SoftPWMSet(i + START, 0);
    }

    // tempo di fade controllato dalla loop
    SoftPWMSetFadeTime(ALL, 0, 0);
    // primo passo
    tempo = millis();
}

void loop(void)
{
    if ((millis() - tempo) > TEMPO)
    {
        tempo = millis();
        passo();
    }

    // loop cortissima
    // non bloccante
}

void passo(void)
{
    // avanza di un passo nella sequenza
    // indicatore della luminosità raggiunta
    static byte lum = 0;
    // indicatore al led da accendere (come dire puntatore)
    static byte indicatore = 0;
    // indicatore al led da spegnere
    static byte spegnimento = 0 + NULED - ACCESI;
    // alzo la sua luminosità
    lum++;
    int a = (int)255 * lum / PASSI;
    SoftPWMSet(ritorno(indicatore) + START, a);

    if (lum >= PASSI)
    {
        // led tutto acceso
        // passo a quelli dopo
        lum = 0;
        indicatore++;
        indicatore = indicatore % (NULED * 2);
        spegnimento++;
        spegnimento = spegnimento % (NULED * 2);
    }

    // e così ho acceso il led
    // devo spegnere quello acceso prima
    SoftPWMSet((ritorno(spegnimento) + NULED) % NULED, 255 - a);
}

byte ritorno(byte ingresso)
{
    // fa ritornare indietro se abbiamo superato la metà
    if (ingresso > NULED)
    {
        return NULED * 2 - ingresso;
    }

    return ingresso;
}

invece, ho pensato che se usassi la funzione specifica della libreria che il fade in ed out se le gestisce lei…
avrei questo:

// di Nelson "StandardOil"
// Idea da sviluppare: Kitt di supercar V2

#include <SoftPWM.h>

// Numero totale di LED
#define NULED 8
// Primo pin corrispondente al primo led e così via
#define START 2
// tempo di fade
#define FADE 100 // che ne so quale e' bello all'occhio, serve provare
//Tempo tra un passo e l'altro in millisecondi
#define TEMPO 100
// Numero dei led da tenere accesi contemporanemante
#define ACCESI 2
// variabile attuale tempo
unsigned long int tempo;

void setup(void)
{
    SoftPWMBegin();

    // inizializziamo i led
    for (byte i = 0; i < NULED; i++)
    {
        SoftPWMSet(i + START, 0);
    }

    // tempo di fade controllato dalla loop
    SoftPWMSetFadeTime(ALL, FADE, FADE);
    // primo passo
    tempo = millis();
}

void loop(void)
{
    if ((millis() - tempo) > TEMPO)
    {
        tempo = millis();
        passo();
    }

    // loop cortissima
    // non bloccante
}

void passo(void)
{
    // avanza di un passo nella sequenza
    // indicatore della luminosità raggiunta
    static byte lum = 0;
    // indicatore al led da accendere (come dire puntatore)
    static byte indicatore = 0;
    // indicatore al led da spegnere
    static byte spegnimento = 0 + NULED - ACCESI;
    // lo accendo
    SoftPWMSet(ritorno(indicatore) + START, 255);
    // e così ho acceso il led
    // devo spegnere quello acceso prima
    SoftPWMSet(ritorno(spegnimento) + START, 0);
    // passo a quelli dopo
    indicatore++;
    indicatore = indicatore % (NULED * 2);
    spegnimento++;
    spegnimento = spegnimento % (NULED * 2);
}

byte ritorno(byte ingresso)
{
    // fa ritornare indietro se abbiamo superato la metà
    if (ingresso > NULED)
    {
        return NULED * 2 - ingresso;
    }

    return ingresso;
}

che lascia gestire i tempi di fade alla softpwm
e comunque ci ho ancora trovato un bel bacherozzo nel calcolo del piedino
…niente…
la soluzione migliore come leggibilità sarebbe passare per un array di piedini, che a questo punto potrebbero anche non essere ne contigui ne in ordine, e l’indice dell’array come numero del led
una cosa così:

// di Nelson "StandardOil"
// Idea da sviluppare: Kitt di supercar V3

#include <SoftPWM.h>

// array di pin
byte piedino[]={2,3,4,5,6,7,8,9,10};
// numero dei led
byte NULED=sizeof(piedino)/sizeof(piedino[0]);
// tengo NULED maiuscolo per non alterare il codice rispetto a quando era una define


// tempo di fade
#define FADE 100 // che ne so quale e' bello all'occhio, serve provare
//Tempo tra un passo e l'altro in millisecondi
#define TEMPO 100
// Numero dei led da tenere accesi contemporanemante
#define ACCESI 2
// variabile attuale tempo
unsigned long int tempo;

void setup(void)
{
    SoftPWMBegin();

    // inizializziamo i led
    for (byte i = 0; i < NULED; i++)
    {
        SoftPWMSet(piedino[i] , 0);
    }

    // tempo di fade controllato dalla loop
    SoftPWMSetFadeTime(ALL, FADE, FADE);
    // primo passo
    tempo = millis();
}

void loop(void)
{
    if ((millis() - tempo) > TEMPO)
    {
        tempo = millis();
        passo();
    }

    // loop cortissima
    // non bloccante
}

void passo(void)
{
    // avanza di un passo nella sequenza
    // indicatore al led da accendere (come dire puntatore)
    static byte indicatore = 0;
    // indicatore al led da spegnere
    static byte spegnimento = 0 + NULED - ACCESI;
    // lo accendo
    SoftPWMSet(piedino[ritorno(indicatore)], 255);
    // e così ho acceso il led
    // devo spegnere quello acceso prima
    SoftPWMSet(piedino[ritorno(spegnimento)], 0);
    // passo a quelli dopo
    indicatore++;
    indicatore = indicatore % (NULED * 2);
    spegnimento++;
    spegnimento = spegnimento % (NULED * 2);
}

byte ritorno(byte ingresso)
{
    // fa ritornare indietro se abbiamo superato la metà
    if (ingresso > NULED)
    {
        return NULED * 2 - ingresso;
    }

    return ingresso;
}

bene bene bene ma non benissimo... @doppiozero aveva il fade anche in accensione creando una prescia che dava all'effetto un aspetto migliore ;) però un plauso per il risultato ottenuto:

Standardoil: doccia fatta...

:P :P :P ;)

Va che io ho sempre avuto il fade in accensione, guarda bene