PWM di un LED con tasto di spegnimento temporizzato

Ciao a tutti,

ho creato un PWM per dimmerare un LED accensione e deve rimanere acceso
--fin qui tutto OK--
quando premo un tasto il LED si deve spegnere e rilasciando il tasto deve ripartire il dimmer in accensione rimando acceso
--anche fin qui tutto OK--

Il problema nasce quando voglio gestire un ritardo della ripartenza dell'accensione dimmer del LED dopo la pressione del tasto

Il primo skecth come dicevo funziona:

const int led = 9; // PWM pin del LED
const int tasto = 7; // ingresso per tasto spegni
unsigned long FADE_PEDIOD = 6000; // imposta il tempo del fade time

unsigned long fadeStartTime;

unsigned long waiting = 1500;             // imposta il timeout prima della riaccensione del LED
unsigned long t1 = millis();
unsigned long dt;
bool RUN = false;

int tastoValue;
int tastoLight;

// the setup routine
void setup() {
  pinMode(led, OUTPUT); // LED
  pinMode(7, INPUT_PULLUP); //tasto
  Serial.begin(9600);


  fadeStartTime = millis();
}

// fade-in in loop, and restart after finishing
void loop() {
  unsigned long progress = millis() - fadeStartTime;

  tastoValue = digitalRead (tasto);


  if (tastoValue == HIGH)  {
    tastoLight = 1;
    Serial.print ("HIGH");
  }

  if (tastoValue == LOW) {

    tastoLight = 0;
    digitalWrite (led, 0);
    fadeStartTime = millis(); // restart fade again
    Serial.print ("LOW");
  }

  if ((progress <= FADE_PEDIOD) && (tastoLight == 1)) {
    long brightness = map(progress, 0, FADE_PEDIOD, 0, 255);
    analogWrite(led, brightness);
    Serial.print ("FADE");

  }
}


Ma nella versione con il tasto temporizzato ..... purtroppo non va:


const int led = 9; // PWM pin del LED
const int tasto = 7; // ingresso per tasto spegni
unsigned long FADE_PEDIOD = 6000; // imposta il tempo del fade time

unsigned long fadeStartTime;

unsigned long waiting = 1500;   // imposta il timeout prima della riaccensione del LED
unsigned long t1 = millis();
unsigned long dt;


int tastoValue;
int tastoLight;

// the setup routine
void setup() {
  pinMode(led, OUTPUT); //LED
  pinMode(7, INPUT_PULLUP); //tasto
  Serial.begin(9600);
  
  fadeStartTime = millis();
}

// fade-in 
void loop() {
  unsigned long progress = millis() - fadeStartTime;

  tastoValue = digitalRead (tasto);


  if (tastoValue == HIGH)  {
    tastoLight = 1;
    Serial.print ("HIGH");
  }

  if (tastoValue == LOW){

    tastoLight = 0;
    digitalWrite (led, 0);
    t1 = millis();
  }

   if ((tastoLight == 0) && (dt >= waiting)) {
    fadeStartTime = millis(); // restart fade again
    Serial.print ("LOW");
  }
  
  if ((progress <= FADE_PEDIOD) && (tastoLight == 1)) {
    long brightness = map(progress, 0, FADE_PEDIOD, 0, 255);
    analogWrite(led, brightness);
    Serial.print ("FADE");
    
     }
  }   

Qualcucno mi può dare una dritta?

Grazie in anticipo

@cri_stiano : In conformità al REGOLAMENTO, fosse anche per una sola riga, il codice va racchiuso negli appositi tag ... :roll_eyes:

... cortesemente, quindi, edita il tuo post più sopra (quindi NON scrivendo un nuovo post, ma utilizzando il bottone a forma di piccola matita :pencil2: che si trova in basso del tuo post), seleziona la parte di codice e premi l'icona </> nella barra degli strumenti per contrassegnarla come codice.

Inoltre, così com'è, non è molto leggibile ... assicurati di averlo correttamente indentato nell'IDE prima di inserirlo (questo lo si fa premendo ctrlT su un PC o cmd T su un Mac all'intero del IDE). Grazie. :slight_smile:

Guglielmo

@cri_stiano : Ho cancellato il tuo post duplicato ... ho chiaramente scritto di EDITARE il vecchio post, NON di duplicarlo, cortesemente leggi con attenzione quanto ho indicato di fare. Grazie mille :slight_smile:

Guglielmo

In realtà cerco di essere sempre preciso, é insito nella mia natura, ma quando premevo l'icona matita in alto a destra del mio post non mi dava la barra degli strumenti ed ara cmq un problema di Firefox.

Poi con Chrome ho visto che c'è la matita in basso al post e sono riuscito ad editarlo correttamente

Ora spero di ricevere supporto

Grazie ancora
Cristiano

Magari sono "cecato" io, ma a me sembra che i due programmi che hai messo siano identici ... :roll_eyes:

Guglielmo

No no, ci vedi benissimo

tra i vari copia e incolla ho cancellato lo sketch che non va, ma ho potuto editare solo ora nuovamente il mio primo post, correggendo, perche ero fuori per lavoro

Spero ci sia qualcosa che mi possa aiutare

Grazie
Cristiano

Buonasera,

sono riuscito a fare questa modifica allo sketch:


const int led = 9; // PWM pin del LED
const int tasto = 7; // ingresso per tasto spegni
unsigned long FADE_PEDIOD = 7000; // imposta il tempo del fade time

unsigned long fadeStartTime;

unsigned long waiting = 1500;   // imposta il timeout prima della riaccensione del LED
unsigned long t1 = millis();
unsigned long dt;


int tastoValue;
int tastoLight;

// the setup routine
void setup() {
  pinMode(led, OUTPUT); //LED
  pinMode(7, INPUT_PULLUP); //tasto
  Serial.begin(9600);

  fadeStartTime = millis();
}

// fade-in
void loop() {
  unsigned long progress = millis() - fadeStartTime;
  dt = millis() - t1;
  tastoValue = digitalRead (tasto);


  if ((tastoValue == HIGH)  && (dt >= waiting)) {
    tastoLight = 1;
    Serial.print ("HIGH");
  }

  if (tastoValue == LOW)  {

    tastoLight = 0;
    digitalWrite (led, 0);
    t1 = millis();
    fadeStartTime = millis(); // restart fade again
    Serial.print ("LOW");
  }

  if ((progress <= FADE_PEDIOD) && (tastoLight == 1)) {
    long brightness = map(progress, 0, FADE_PEDIOD, 0, 255);
    analogWrite(led, brightness);
    Serial.print ("FADE");

  }
}


Ora un uno spegnimento temporizzato ma quando riparte il l'accensione in dimmer perdo il tempo del waiting ed il led si accende già da una condizione luce già alta :thinking:

Il problema è che la variabile 'fadeStartTime' dovrebbe essere "resettata a millis" alla fine della pausa, e non al rilascio del pulsante.

Il secondo problema è che risolvere il primo problema è oggettivamente complicato se il codice non è progettato in un certo modo fin dall'inizio (e ogni eventuale altra modifica diventa esponenzialmente sempre più difficile).

E questo perché si cerca di ottenere un certo funzionamento "modificando le istruzioni", invece che usare le istruzioni semplicemente per tradurre un disegno logico già funzionante su carta.

E qual è questo disegno logico? È implicito (nascosto) nella descrizione iniziale in italiano, ed emerge, sempre implicitamente, dall'ordine di esecuzione delle istruzioni presentate.

Qual è invece il disegno logico esplicito? Il seguente, con le tre fasi di funzionamento descritte, che qui diventano invece esplicite e fondamentali, con indicati gli "eventi" che portano dall'una all'altra (raggiunto limite di tempo LIM, pressione P o rilascio /P del pulsante):

52786241781

Da cui si ricava la sequenza di operazioni, regolate da una struttura if/else if (il rettangolo rosso), da una variabile di stato/situazione 'S' e da una variabile temporale 'T':

17867232462786786

Se la procedura del disegno a mano funziona, allora (e solo) a questo punto si può tradurre in istruzioni, praticamente uno a uno col disegno:

if ((FADE == s) && keyPress())        { ledOff();      s = ATTR; }

else if (FADE == s)
{
    unsigned long progress = millis() - t;
    if (progress > FADE_PERIOD) { progress = FADE_PERIOD; }
    unsigned long brightness = map(progress, 0, FADE_PERIOD, 0, 255);
    analogWrite(led, (byte)brightness);
    if (progress >= FADE_PERIOD) { s = ON; }
}

else if ((ON == s) && keyPress())     { ledOff();      s = ATTR; }

else if ((ATTR == s) && keyRelease()) { t = millis();  s = FADE; }

Il problema del thread è: aggiungere un'ulteriore "situazione di pausa" tra lo stato ATTR (attesa rilascio) e FADE. Si parte con il modificare il primo disegno in modo che rappresenti in modo esplicito il comportamento che si vuole:

456189526165

Il resto è tutta discesa :wink:

Caspiterina Claudio, dove mi sono andato ad impelagare......

ti ringrazio per avermi dedicato questa dettagliata spiegazione

Appena rientro ci metto su le mani e vedo cosa riesco a combinare

Grazie
Cristiano

Ciao Claudio, credo di essermi incasinato parecchio

io purtroppo sono alle prime armi e quindi faccio un pò di domande per vedere se ci ho capito qualcosa:

S= variabile di stato quindi penso che sia una INT?
Inoltre il valore di partenza di S da inserire nel setup va bene = ON?

T= variabile temporale

ATTR _ é una variabile INT? e quale é il suo valore iniziale?

FADE sarebbe FADE_PERIOD oppure é una altra variabile?

keyPress e keyRelease con le parentesi () vuol dire che legge il cambio di stato associato al digitalRead sul pin del tasto?

ON é una altra variabile ed é una INT?

ledOff é il comando che avevo messo io e cioé "digitalWrite (led, 0);" ?

Ti ringrazio in anticipo per le risposte

Cristiano

io purtroppo sono alle prime armi e quindi faccio un pò di domande per vedere se ci ho capito qualcosa:

Premetto che quello che ho proposto non è obbligatorio, è solo un metodo tra i vari possibili, per incasinarsi il meno possibile in qualsiasi progetto si affronti, metodo che in sostanza si traduce nel progettare il programma sotto forma di tante “frasi”:

SE sono nello stato X
   SE succede Y
      faccio Z (ed eventualmente imposto nuovo stato)

Il problema specifico come detto si risolve facendo iniziare il tempo da controllare dalla fine della pausa, e non dal momento in cui il pulsante non è più premuto. E sempre nel tuo caso specifico si potrebbe risolvere aggiungendo almeno un’altra variabile flag per indicare il verificarsi di tale condizione. Ma… sarebbe una soluzione complicata e valida solo per lo specifico problema, e non una soluzione generale in grado di facilitare qualsiasi altro progetto. E questo per tenere conto della frase “Vorrei imparare di più per sviluppare progetti in autonomia” nella presentazione :wink:

S= variabile di stato quindi penso che sia una INT?

Si, int va bene, o anche byte che tanto fino a 256 stati basterebbe.

Inoltre il valore di partenza di S da inserire nel setup va bene = ON?

Giusta domanda, lo stato iniziale lo decidi tu in base a come vuoi far partire il sistema.

T= variabile temporale

di tipo unsigned long o uint32_t che è la stessa cosa

ATTR _ é una variabile INT? e quale é il suo valore iniziale?
FADE sarebbe FADE_PERIOD oppure é una altra variabile?

ON, ATTR e FADE sono gli stati, rappresentati con nomi di comodo, sono i valori da dare alla variabile S. Li puoi sostituire semplicemente con 1 2 3 (o qualsiasi altro valore, non necessariamente in sequenza) in tutti i punti del programma, oppure puoi creare all’inizio delle costanti

const byte FADE = 1;
const byte ON   = 2;
const byte ATTR = 3;

o ancora si possono usare delle define:

#define FADE 1
#define ON   2
#define ATTR 3

o ancora si può usare una enumerazione che associa automaticamente dei valori numerici ai nomi:

enum Stati { FADE, ON, ATTR };

Stati s = ON; definisce una variabile tipo Stati che
              può assumere i "valori etichetta" FADE,ON,ATTR

...

if (s == ON) { .... }

keyPress e keyRelease con le parentesi () vuol dire che legge il cambio di stato associato al digitalRead sul pin del tasto?

No, sono delle funzioni da scriversi per leggere il pulsante, più comode da leggere nel programma rispetto a scrivere in quei punti direttamente i digitalRead.

ledOff é il comando che avevo messo io e cioé "digitalWrite (led, 0);" ?

Si, anche qui ho usato un nome di comodo per rendere più chiaro cosa si sta facendo in quel punto. Basta scrivere, prima o dopo le funzioni setup e loop, una funzione di nome ledOff che svolga il lavoro richiesto:

void ledOff()
{
    digitalWrite (led, 0);
}

Usando delle funzioni in questo modo si può praticamente scrivere la logica in italiano (il tutto sempre nell'ottica di incasinarsi il meno possibile e rendere le modifiche più semplici possibili).

Claudio i tuoi consigli sono stati davvero preziosi e per questo ti ringrazio

Alla fine ho scelto la strada piu semplice aggiungendo una altra variabile di stato per controllare il restart del FADE al termine della pausa come da tuo suggerimento

  void loop() {
    unsigned long progress = millis() - fadeStartTime;
    dt = millis() - t1;
    tastoValue = digitalRead (tasto);
    tastoStato = 0;
  
  
    if ((tastoValue == HIGH)  && (tastoStato == 0)) { 
      tastoLight = 1;
     
    }
  
    if (tastoValue == LOW)  {
  
      tastoLight = 0;
      digitalWrite (led, 0);
      t1 = millis();
      tastoStato = 1;
  
    }
  
    if (dt <= waiting) {
           
      tastoStato = 0;
      fadeStartTime = millis(); // restart fade again
      
    }
  
    if ((progress <= FADE_PEDIOD) && (tastoLight == 1)) {
      long brightness = map(progress, 0, FADE_PEDIOD, 0, 255);
      analogWrite(led, brightness);


anche se non ho capito come mai ho dovuto nel terzo "if" mettere (dt <= waiting) io nel mio ragionamento lo intendevo >=

Ad ogni modo pare che funziona

Inoltre tutta la tue chiare indicazioni in merito al metodo di sicuro mi saranno utili per il futuro

Per il momento grazie ancora

Cristiano

Ho dato un'occhiata perché mi sembrava troppo complicato, e infatti i flag 'tastoStato' e 'tastoLight' di fatto non fanno niente, si possono tranquillamente togliere :wink:

Alla fine il codice che hai scritto, privato delle parti che non fanno niente, diventa questo:

    unsigned long progress = millis() - fadeStartTime;
    dt = millis() - t1;
    tastoValue = digitalRead (tasto);

    if (tastoValue == 0)  // se premuto
    {
      digitalWrite (led, 0);
      t1 = millis();
    }

  
    if (dt <= waiting) { fadeStartTime = millis(); }  // restart fade again

  
    if ((progress <= FADE_PEDIOD) && (tastoValue == 1)) 
    {
      long brightness = map(progress, 0, FADE_PEDIOD, 0, 255);
      analogWrite(led, brightness);
    }

Quando premi "resetti" 't1' a millis, e quindi 'dt' vale 0, quando rilasci 'dt' inizia a salire fino a raggiungere (e poi superare) 'waiting'. Finché 'dt' rimane sotto 'waiting', 'fadeStartTime' continua a essere "resettato" a millis e quindi 'progress' rimane a zero. Progress inizia a crescere quando 'dt' ha raggiunto 'inwaiting', cioè alla fine della pausa dopo il rilascio.

NOTA: il codice soffre dell'overflow di millis, perché dopo un certo numero di giorni (circa 1192 ore) i valori 'progress' e 'dt' torneranno a zero anche senza premere nulla. Se il tutto deve restare acceso permanentemente il codice va corretto (con il sistema che avevo suggerito questo tipo di problema non si verificherebbe).

Eh già .... mi sono complicato un pò la vita ..... :upside_down_face:

Si il discorso dell'overflow é noto ma al massimo farà 8 / 10 h quindi sono coperto

Grazie
Cristiano