MEGA 2560 e led BUILT_IN

Vorrei fare lampeggia il led presente sul MEGA 2560 utilizzando la funzione millisecondo().
Dovrebbe accendere il led per 5 secondi e poi spegnerlo per 2 secondi, ma mi rimane sempre acceso.
Lo sketch è il seguente:

/*
 * 18 settembre 2024
 * 
 * Arduino Mega 2650
 */

unsigned long precedenteMillis = 0;
unsigned long correnteMillis = 0;
const long intervalloON = 5000;
const long intervalloOFF = 2000;

void setup() {
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
    correnteMillis = millis(); 
     if(correnteMillis - precedenteMillis > intervalloON){     
        precedenteMillis = correnteMillis;
        digitalWrite(LED_BUILTIN, LOW); // accende
    }
   else{
      correnteMillis = millis(); 
     if(correnteMillis - precedenteMillis > intervalloOFF){     
        precedenteMillis = correnteMillis;
        digitalWrite(LED_BUILTIN, HIGH);    // spegne
      }
    }
 }

Dove sbaglio?

EzioGi

Perché tu non mantieni traccia dello stato del led e quindi facendo nel loop continuamente:

    correnteMillis = millis(); 
     if(correnteMillis - precedenteMillis > intervalloON){     

appena sono passati "intervalloON" millisecondi entra nuovamente nella if() e accende nuovamente.
In prima battuta potresti modificare così il tuo programma, per tenere in conto dello stato corrente del led:

unsigned long precedenteMillis = 0;
const long intervalloON = 5000;
const long intervalloOFF = 2000;

void setup() {
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);    // spegne
}

void loop() {
  // Questa è la tua variabile di stato
  bool LedOFF = (digitalRead(LED_BUILTIN) == HIGH);
  if ( LedOFF) { 
    if (millis() - precedenteMillis > intervalloON) {
      precedenteMillis = millis();
      digitalWrite(LED_BUILTIN, LOW); // accende
    }
  } else {
    // Qui il led sarà quindi ON
    if (millis() - precedenteMillis > intervalloOFF) {
      precedenteMillis = millis();
      digitalWrite(LED_BUILTIN, HIGH);    // spegne
    }
  }
 }

Sostituisci alle variabili i valori assunti per ogni ciclo di loop, esempio:
La variabile correnteMillis assume valore 0 io scrivo così:
correnteMillis(0) = millis();

LOOP 1 {
     correnteMillis(0) = millis(); 
     if (0 - 0 > 5000) {
           // non esegue il codice
     }
}

Vediamo LOOP n che valore assume la variabile correnteMillis.

LOOP n {
     correnteMillis(5001) = millis(); 
     if (5001 - 0 > 5000) {
           // esegue il codice
     }
}

Purtroppo prima di arrivare a 5001 ci sarà un ciclo in cui correnteMillis vale 2001 e la seguente condizione risulta vera:

if (correnteMillis - precedenteMillis > intervalloOFF) {

Una semplificazione estrema viene usando le funzioni, sapendole creare ovviamente.

Credo che il codice seguente funzioni ma non l'ho provato:

// devi dichiarare globale le variabile intervallo e ledState
void loop() {
     correnteMillis = millis(); 
     if (correnteMillis - precedenteMillis > intervallo) {    
         if (ledState == LOW) {
             intervallo = 2000;
         }  else {
              intervallo = 5000;
        }
        precedenteMillis = correnteMillis;
        digitalWrite(LED_BUILTIN, ledState); // accende
        ledState = !ledState; // inverte il valore di ledState
    }
  }

Ciao.

Per testare il codice ho usato uno sketch su cui sto lavorando al simulatore, tra una cosa e l'altra mi sono fatto prendere la mano e il codice funziona ma non è destinato ad essere compreso da @eziogi (troppo complesso)

Lo lascio qui perché devo rimuoverlo dal mio progetto in sviluppo.

struct Blink {
    uint8_t pin;
    uint32_t timer;
    uint16_t interval;
    uint16_t stayOn;
    uint16_t stayOff;
};
// crea e inizializza istanza blink13 di struttura Blink
Blink blink13 = { 
  .pin = 13,
  .timer = 0,
  .interval = 0,
  .stayOn = 5000,
  .stayOff = 2000 
};

void blink(Blink &b) {
    if (millis() - b.timer >= b.interval) {
        b.timer = millis();
        bool sPin = digitalRead(b.pin);
        if (sPin) {
            b.interval = b.stayOff;
        } else {
            b.interval = b.stayOn;
        }
        digitalWrite(b.pin, !sPin);
    }
}

void setup() {
    pinMode(13, OUTPUT);
}

void loop() {
    blink(blink13);  // chiama funzione blink con argomento blink13
}

Puoi creare inizializzare più istanze e nel loop chiamare:

void loop() {
    blink(inst0);
    blink(inst1);
    blink(inst2);
}

Ciao.

Grazie Maurotec dell'aiuto.
Per adesso mi metto a capire come funzionano i due esempi che mi hai mandato.
Nel fine settimana poi lo sperimento.

EzioGi

Grazie del consiglio.
Nel fine settimana lo provo sul Mega 2560 e poi faccio sapere.

EzioGi

Ok, io comunque l'ho già provato su Wokwi, e funziona. :wink:
Ovviamente poi partendo da qui potrai iniziare ad estenderlo per le tue esigenze, non credo che ti basti far lampeggiare quel led, immagino che tu abbia in mente di fare anche altro per cui facci sapere se hai bisogno. :sunglasses:

Che obbrobrio:
bool LedOFF = (digitalRead(LED_BUILTIN));

Così è molto più chiara

Allora leva anche le parentesi, che mi sembrano inutili.

Ciao, Ale.

Che poi tutti i commenti accende/spegne già dal primo post sono invertiti.
Il LED builtin è spento quando LOW. Quindi sarebbe:

bool LedOFF = !digitalRead(LED_BUILTIN);

A questo punto risulterebbe più semplice ragionare in logica positiva:

bool LedON = digitalRead(LED_BUILTIN);

Già che sono state date altre soluzioni, aggiungo anche quella ad oggetti. Un blinker nel mondo reale è un oggetto con un'uscita, delle regolazioni, ed eventualmente un ingresso X di abilitazione:

87687645762534

Il programma è brevissimo:

#define SECOND  1000UL

Blinker  blk;

void setup() {
    pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
    blk.run(1, 5*SECOND, 2*SECOND);
    digitalWrite(LED_BUILTIN, blk.q);
}

Basta aggiungere una classe (lasciata qui a titolo di esempio/studio) che descrive il comportamento di un blinker (da cui ottenere tutti i blinker reali che servono, in questo esempio solo blk). La logica di funzionamento (anche se implementata in modo diverso) è la stessa degli esempi dei post precedenti, con l'aggiunta dell'abilitazione, in questo caso tenuta sempre a 1.

class Blinker {
    uint32_t t;
    bool s = 0;
  public:
    bool q = 0;
    bool run(bool x, uint32_t th, uint32_t tl) {
        if (!s && x) { 
            q = s = 1;  
            t = millis(); 
        }
        else if (s && !x) 
            q = s = 0;
        else if (s && (millis()-t >= (q ? th : tl))) { 
            t += q ? th : tl;  
            q = !q; 
        }
        return q;
    }
};

Per qualcuno che ha iniziato da non molto a programmare no, non è più chiara per niente.
Ma perché aggiungere commenti dichiarando "obbrobrio" una istruzione che, al contrario, è assolutamente logica (prende un valore booleano in una variabile booleana) oltre che comprensibile (non ci sono cast impliciti), invece di dire sempicemente "Si ma si può scrivere anche così"?...
Tra l'altro come invece ha fatto molto più correttamente Claudio_FF, che senza schifare nulla ha dato semplicemente informazioni molto più complete ed utili del tuo commento "obbrobrio".

Effettivamente sono pochi anni che ho conosciuto Arduino e quindi sto ancora imparando.
Come mi è stato consigliato è meglio se sperimento le singole funzioni prima di scrivere il programma principale.
Brevemente, il programma dovrebbe presentare casualmente sul LCD uno di tre numeri e rimanere presente per 10 secondi.
Contemporaneamente nei 10 secondi dovrei acquisire e leggere uno o due ingressi ADC e salvare il valore su una eeprom, per poi leggere i dati con comodo.
La cosa andrebbe ripetuta per una decina di volte.
Sono partito con la funziona Millis(), per capirne il funzionamento e come utilizzarlo dentro i 10 secondi.
Qui ho trovato un po' gli esempi che mi avete consigliato e che metterò in pratica.
Per il momento chiuderei questo post.
Se trovo difficoltà nella stesura del programma completo aprirò un nuovo post.
Grazie ancora.

EzioGi

Per quale motivo utilizzare una EEPROM? Devi conservare i valori anche se togli l'alimentazione ad Arduino?

Ricorda che le EEPROM hanno vita limitata e NON puoi scriverci in continuazione ... :roll_eyes:

Guglielmo

Si, devo conservare i valori per poterli analizzare in un secondo momento.
Volevo utilizzare una eeprom che conosco come fare per scrivere e leggere nella sua memoria.
Anche perché non so se è possibile salvare i miei dati in qualche memoria del dispositivo Arduino e sopratutto come fare, se c'é qualche libreria da utilizzare.

EzioGi

Ok, allora ricordati che, comunque, ogni singolo byte della EEPROM ha una durata di vita garantita di 100'000 scritture che ... possono essere tantissime come pochissime ... dipende dalla frequenza con cui si scrive.

Ci sono inoltre tecniche di "rotazione" per far si di non usare sempre gli stessi bytes (ricorda che il limite di 100'000 è per singolo byte) ed allungare ancora di più la vita.

Tieni conto di tutte queste cose e fai bene i tuoi calcoli.

Guglielmo

Intanto puoi imparare da qua':

https://docs.arduino.cc/learn/built-in-libraries/eeprom/

Beh se ti serve per sperimentare, questa prima versione di sketch puoi realizzarla anche usando un emulatore, così puoi "pasticciare" e fare test in modo più semplice e rapido rispetto ad usare il circuito "fisico", in particolare concentrati su una cosa alla volta: fai prima uno sketch che genera e mostra il numero casuale per 10 secondi (senza usare delay ma solo mllis, magari sperimentando come gestire la cosa come "macchina a stati finiti", o FSM); poi quando funziona aggiungi anche la lettura di un ingresso analogico durante la "pausa"; e alla fine puoi pensare alla crittura su EEPROM (anche se io ti consiglierei di vedere di usare una scheda SD).

Ovviamente nell'emulatore userai un pin analogico scollegato per calcolare il "seed" e da questo generare i numeri casuali che userai per mostrare il numero, e come output potresti usare o la seriale oppure già collegare un LCD (non so quale sia quello che devi usare tu, ma qui per fare esperimenti puoi usarne uno con I2C che è più semplice da collegare e gestire). Quando funziona, passi alla lettura analogica, usando uno dei pin analogici per acquisire il dato (ci collegherai magari un trimmer per poter stabilire un valore "stabile" di ingresso). E quando tutto è ok, puoi provare a scrivere in EEPROM (simulata, per ora).

Per ognuna di queste tre fasi se hai necessità di una mano, ti basta condividere il link del tuo progetto su Wokwi così anche noi possiamo vedere il funzionamento e darti i consigli su cosa correggere (è una delle cose utili degli emulatori).

Ed alla fine non solo avrai sperimentato bene questi aspetti (che sono comunissimi in questo ambito) ma avrai uno schema di base già pronto, ed uno sketch che basta adeguarlo all'hardware fisico che intendi usare.

Grazie, terrò presente.

EzioGi

Ottimo documento, grazie.

EzioGi

Provo a cimentarmi anche con questo nuovo applicativo, visto che dovrebbe semplificare la creazione dello sketch che ho intenzione di provare.
Saluti

EzioGi