Asincronismo di Due o + Led

Buona sera,
sto cercando di far alternare in maniera asincrona di 2 led, ma per ora riesco solo nel far lampeggiare i led in maniera alternata.
Come si potrebbe definire lo sketch per far si che i led lampeggiassero in maniera asincrona?
Il progetto detto cosi sembra semplice ma mi sa che è tutto tranne semplice da scrivere.

Devi studiarti come si usa la funzione millis(), prima QUI, poi QUI e QUI e QUI e tutti gli articoli che sono in QUESTA pagina ... vedrai che poi ti sarà tutto più chiaro :wink:

Guglielmo

C'è anche questo :slight_smile:

Come al solito è solo questione di metodo (bloccante o non bloccante, e formalizzare in modo chiaro i passaggi):

SE scaduto_tempo_led1:
    inverti_led1
    riavvia_tempo_led1
  
SE scaduto_tempo_led2:
    inverti_led2
    riavvia_tempo_led2

Stavo guardando lo sketch dei 2 led alternati per poterlo aggiornare in lampeggio asincrono ma non ne vengo fuori.
Non so tipo;
Led1 acceso
si inverte il lampeggio del led1
e si riavvia il tempo del led1

e poi si fa lo stesso non il led2

... e NON ne verrai fuori se prima non ti studi quanto ti ho già indicato al post #2 :roll_eyes:

Guglielmo

Per il calcolo del tempo trascorso, e della ricarica del tempo, vale sempre >>questo post<< (che hai scritto tu).

Per l'inversione dello stato di un LED si può scrivere sull'uscita la lettura negata della stessa uscita:

digitalWrite(PIN_LED, !digitalRead(PIN_LED));

Non ho mai approfondito la questione, ma su alcune piattaforme a volte questa "modalità" non funziona correttamente ed è necessario usare una variabile di appoggio.

Ad esempio mi è capitato tempo fa con ESP32.
Anche se magari nel frattempo qualcosa è cambiato...

Se non posti qui il tuo codice è difficile poterti dire dove sbagli...

Sempre che lo sketch abbia un senso non capisco dove va posizionata questa scritta.

digitalWrite(PIN_LED, !digitalRead(PIN_LED));

unsigned long previousMillis_a = 0;
const unsigned long interval_a = 1000;
const byte pinLed_1 = 2;
byte stateLed_1 = 0;

unsigned long previousMillis_b = 0;
const unsigned long interval_b = 1000;
const byte pinLed_2 = 4;
byte stateLed_2 = 0;

void setup() {

  pinMode(pinLed_1, OUTPUT);
  pinMode(pinLed_2, OUTPUT);
}

void loop() {

  unsigned long currentMillis = millis();
  // Led_1
  if (currentMillis - previousMillis_a >= interval_a) {
    if (stateLed_1 == LOW) {
      stateLed_1 = HIGH;
    } else {
      stateLed_1 = LOW;
    }
    
    previousMillis_a = currentMillis;
  }
  digitalWrite(pinLed_1, stateLed_1);
  // Stringa 1
   // digitalWrite(pinLed_1, !digitalRead(pinLed_1));
  if (stateLed_2 == stateLed_1) {
    // Led_2
    if (currentMillis - previousMillis_b >= interval_b) {
      if (stateLed_2 == LOW) {
        stateLed_2 = HIGH;
      } else {
        stateLed_2 = LOW;
      }
      previousMillis_b = currentMillis;
      digitalWrite(pinLed_2, stateLed_2);
      // Stringa 2 
      //digitalWrite(pinLed_2, !digitalRead(pinLed_2));
    }
  }
}

Da nessuna parte. Visto che per gestire lo stato dei LED usi le variabili stateLed_ bastano quelle. Era una possibile alternativa al loro uso.

Il codice sarebbe giusto, a parte la logica che vorresti creare con la condizione if (stateLed_2 == stateLed_1) che mi risulta totalmente oscura. Tradotta a parole significa: gestisci il LED2 solo quando ha lo stesso stato di LED1. Non devono essere indipendenti?

bool stateLed_1 = LOW;


  if (currentMillis-previousMillis_a >= interval_a) {
    previousMillis_a = currentMillis;
    stateLed_1 = !stateLed_1;
    digitalWrite(pinLed_1, stateLed_1);
  }
  

Lo sketch non è il massimo, comunque quando hai a che fare con 2 o più elementi tutti dello stesso tipo è meglio se utilizzi gli array.
Guarda qui come ho trasformato il tuo codice, e quanto è diventato più semplice:

// Parametrizziamo per eventuali espansioni future...
#define NUMLED 2
// Adeguare di conseguenza l'inizializzazione dei valori...
unsigned long prevMillis[NUMLED] = {0, 0};
unsigned long interval[NUMLED] = {1000, 1200};
byte pinLed[NUMLED] = {2, 4};

void setup() {
  for(int led = 0; led < NUMLED; ++led) {
    pinMode(pinLed[led], OUTPUT);
    digitalWrite(pinLed[led], LOW);
  }
}

void loop() {
  unsigned long currentMillis = millis();
  for(int led = 0; led < NUMLED; ++led) {
    if (currentMillis - prevMillis[led] >= interval[led]) {
      digitalWrite(pinLed[led], !digitalRead(pinLed[led]));
      prevMillis[led] = currentMillis;
    }
  }
}

Qui i led vengono attivati con intervalli differenti tra loro (ho messo 1000 e 1200) impostati all'inizio nekll'array, quindi sono completamente asincroni.
Ovviamente se hai domande sul codice come l'ho modificato, chiedi pure.

Ciao docdoc,
ti ringrazio per la risposta, vedo bene come si è ridotto lo sketch diventando più snello e leggibile a vista d'occhio
Dal momento che non conosco tanto la programmazione e credo che sarebbe più istruttivo vedere lo sketch in tutti i suoi passaggi nel metodo in cui lo avevo postato anche se meno elegante e più lungo da leggere, cosi da vedere esattamente ogni componente come si comporta.
Per voi che masticate la programmazione tutti i giorni può sembrare lungo e noioso .
Lo trascrivererei io ma alcuni passaggi non li conosco, e non li saprei convertire.

Tu prova quel codice, e cerca di capire come funziona (anche perché è più lineare e semplice da capire), se hai dubbi o curiosità, scrivi e ti spiego. Perché oltre a millis(), gli array e come gestirli, fidati, sono concetti che ti saranno utili quando affronterai programmi più complessi di questo, e prima impari a padroneggiarli meglio ti troverai... :wink:

Cioè, selezioni da // led1 e // end led1, copi premendo CTRL+c, sposti
il cursore sotto // end led2 e premi CTRL+v per incollare.
In ciò che hai incollato, sostituisce 1 con 3 e a con c.
PS: CTRL + v sono solo 2 tasti da premere, CTRL e v.

Non resta che dichiarare e inizializzare le nuove variabili, e ovviamente io uso copia ed incolla e modifica 1 con 3 e a con c.

Didatticamente ti mostra la duplicazione di codice, che può essere evitata grazie alle strutture dati come gli array (o vettore), oppure le strutture dati non omogenee come struct.

Domanda:
Stai seguendo un corso su arduino?

Ciao.

No nessun corso quel poco che faccio è grazie a voi .

Emmmm ... nel REGOLAMENTO, al punto 16.1, sono elencati sia dei .pdf che un ottimo libro ... dedicare un po' di tempo a studiare no ??? :wink:

Guglielmo

pdf introduttivo

In questo documento introduttivo trovi sintetizzate molte informazioni di base. Il problema con queste risorse è che sono estremamente sintetizzate e leggerle senza il supporto di un insegnante si corre il rischio di prendere fischi per fiaschi.

Ad esempio a pagina 11, c'è:

Le parentesi { } racchiudono il blocco di codice relative al setup( ) che in questo caso specifica che il pin definito da ledPin è impostato come output.

Un blocco di codice è una entità, più entità possono essere innestate una dentro l'altra. Questa entità viene usata associata alla definizione di funzione. Possiamo dire che una funzione è un blocco di codice a cui assegniamo una impronta composta da: tipo restituito nome(lista di argomenti). Due funzioni devono essere definite obbligatoriamente: setup() e loop().

L'impronta della funzione setup è:

void setup(void) {
  1    2     3
}
  1. tipo di dato restituito dalla funzione
  2. nome della funzione
  3. lista degli argomenti in ingresso alla funzione.

void si traduce con nulla.
ecc ecc.

Un blocco di codice può essere associato o legato ad una parola chiave del linguaggio, esempio:

if (espressione condizionale) {
}

Questa if è un comando strutturato, come pure for, while, switch.
ecc ecc.

Purtroppo in rete non si trova nulla di simile per giunta gratuito, e per mia esperienza nemmeno su un libro, cioè ne servono più di uno di libri. Cioè l'argomento essendo complesso non può essere contenuto in un solo libro sul linguaggio C/C++, per cui l'autore deve per forza sintetizzare in qualche punto.

Sugli array
Sintetizzando, un array o vettore è un contenitore di dati omogeneo indicizzato.

unsigned long interval[] = {1000, 1200};

interval è il nome del vettore di tipo unsigned long, contenente questi due valori {1000, 1200}. La dimensione del vettore viene specificata all'interno delle parentesi quadre, che in questo caso sono vuote poiché il compilatore è in grado di dedurre la dimensione da ciò che è specificato dentro le graffe.

Si accede al vettore sia in lettura che in scrittura con:

Serial.println(interval[0]);  // primo elemento 
Serial.println(interval[1]); // secondo elemento

stiamo accedendo in lettura, per cui dovrebbe stamparti i due valori.
ecc, come vedi sintetizzo anche io e non potrei fare diversamente.

Ciao.

#define pin_led1 2 // il carattere '2' viene sostituito prima della compilazione
// a ogni "pin_led1". Nota: alla fine dei #define non va messo il ';', altrimenti
// verrebbe sostituito insieme al 2!
#define pin_led2 4
#define periodo_led1 700 // 700 viene sostituito prima della compilazione a ogni
// "periodo_led1".
#define periodo_led2 1000

bool stato_led1 = LOW; // Variabile booleana, che può assumere solo i valori
// LOW e HIGH.
bool stato_led2 = LOW;

uint32_t t_led1; // Variabile unsigned (solo positiva) intera a 32 bit (=unsigned
// long in Arduino Uno) usata per memorizzare millis() per il conteggio del tempo
// del led 1.
uint32_t t_led2;

void setup()
{
pinMode(pin_led1, OUTPUT); // Impostazione del pin come uscita per il led 1.
// Automaticamente viene messo a livello basso.
pinMode(pin_led2, OUTPUT);
}

void loop()
{
if (millis()-t_led1 >= periodo_led1) // Se è trascorso ALMENO il periodo_led1
// dal t_led1 salvato precedentemente. Si usa >= perché nel preciso millisecondo
// in cui l'uguaglianza è vera l'esecuzione del programma potrebbe non passare
// in questo punto e non verrebbe eseguito quanto richiesto.
  {
  t_led1 = millis(); // Copia il valore di millis().
  stato_led1 = !stato_led1; // Inverte lo stato della variabile.
  digitalWrite(pin_led1, stato_led1); // Pilota l'uscita.
  }
if (millis()-t_led2 >= periodo_led2)
  {
  t_led2 = millis();
  stato_led2 = !stato_led2;
  digitalWrite(pin_led2, stato_led2);
} // Fine loop