Led lampeggiante richiamando una funzione

Buonasera a tutti,

Ho realizzato questo sketch per una centralina antifurto e tutto funziona regolarmente tranne il “led_di_avviso” che vorrei far lampeggiare invece che tenerlo acceso fisso.

Per ottenere questa modalità, ho creato la funzione “blink_led_avviso” che viene invocata quando scatta l’allarme, evitando il comando “delay” che mi avrebbe bloccato il loop, ma purtroppo come risultato ottengo solo che il led di avviso si accende quando scatta l’allarme e si spegne al successivo azionamento dell’allarme. Potete per favore verificare che la funzione sia corretta e dove potrei aver sbagliato?

Grazie e buona serata a tutti voi.

/*
Centralina antifurto 5 ingressi
Lorenzo Stecconi
V3.2 del 10_10_2015
 */

// include the library code:
#include <LiquidCrystal.h>
#include <EEPROM.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

int sensore_1 = 7;
int sensore_2 = 8;
int sensore_3 = 9;
int sensore_4 = A0;
int sensore_5 = A1;
int ill_disp = 10;
int uscita = 13;
int puls_reset = A2;
int led_di_avviso = A3;
const int avviso_eeprom_offset = 5;
boolean state = true;
unsigned long time;
int verifica_garage = 0;
int verifica_cantina = 0;
int verifica_terrazzo = 0;
int verifica_mansarda = 0;
int verifica_casa = 0;

void setup() {
  Serial.begin(9600);
  pinMode(sensore_1, INPUT);
  pinMode(sensore_2, INPUT);
  pinMode(sensore_3, INPUT);
  pinMode(sensore_4, INPUT);
  pinMode(sensore_5, INPUT);
  pinMode(ill_disp, OUTPUT);
  pinMode(uscita, OUTPUT);
  pinMode(puls_reset, INPUT_PULLUP);
  pinMode(led_di_avviso, OUTPUT);
  digitalWrite (led_di_avviso, EEPROM.read (avviso_eeprom_offset));
  time = millis();

  // set up the LCD's number of columns and rows:
  lcd.begin(20, 4);
}

//funzione per far lampeggiare il led di avviso
void blink_led_avviso(){
digitalWrite(led_di_avviso, state);
if((time+300)<millis())//Se sono trascorsi 500 millisecondi
{                   //
state = !state;   //Cambia lo stato del led
time = millis(); // azzera il conteggio del tempo
}

}  

void loop() {

  int sensore_1 = digitalRead(9);
  int sensore_2 = digitalRead(8);
  int sensore_3 = digitalRead(7);
  int sensore_4 = digitalRead(A0);
  int sensore_5 = digitalRead(A1);

  if (digitalRead(puls_reset) == LOW) {
    tone(6, 2000, 100);
    delay(150);
    tone(6, 2500, 100);
    EEPROM.write (avviso_eeprom_offset, 0);
    digitalWrite(led_di_avviso, LOW);
    digitalWrite(ill_disp, HIGH);
    lcd.setCursor(0, 0);
    lcd.clear();
    lcd.print("Tutto bene");
    lcd.setCursor(0, 1);
    lcd.print("Sensori non attivi");
    lcd.setCursor(0, 3);
    lcd.print("Zone:");
    lcd.setCursor(6, 3);
    lcd.print(sensore_1);
    lcd.setCursor(9, 3);
    lcd.print(sensore_2);
    lcd.setCursor(12, 3);
    lcd.print(sensore_3);
    lcd.setCursor(15, 3);
    lcd.print(sensore_4);
    lcd.setCursor(18, 3);
    lcd.print(sensore_5);
    verifica_garage = 0;
    verifica_cantina = 0;
    verifica_terrazzo = 0;
    verifica_mansarda = 0;
    verifica_casa = 0;
    delay(2000);
    digitalWrite(ill_disp, LOW);
  }

  if (sensore_1 == HIGH  && sensore_2 == HIGH && sensore_3 == HIGH && sensore_4 == HIGH && sensore_5 == HIGH) {
    digitalWrite(uscita, LOW);
    digitalWrite(ill_disp, LOW);
    noTone(6);
    delay(10);
  }
  else
  {
    EEPROM.write (avviso_eeprom_offset, 1);
    digitalWrite(uscita, HIGH);
    blink_led_avviso();
    digitalWrite(ill_disp, HIGH);
    lcd.clear();
    lcd.setCursor(2, 0);
    lcd.print("Allarme sensore!");

    //mostra le zone attivate

    lcd.setCursor(0, 3);

    if (sensore_1 == 0) {
      verifica_garage=verifica_garage+1;
      lcd.print( "GARAGE " );
    }
    else
      lcd.print("");

    if (sensore_2 == 0) {
      verifica_cantina=verifica_cantina+1;
      lcd.print( "CANTINA " );
    }
    else
      lcd.print("");

    if (sensore_3 == 0) {
      verifica_terrazzo=verifica_terrazzo+1;
      lcd.print( "TERRAZZO " );
    }
    else
      lcd.print("");

    if (sensore_4 == 0) {
      verifica_mansarda=verifica_mansarda+1;
      lcd.print( "MANSARDA " );
    }
    else
      lcd.print("");

    if (sensore_5 == 0) {
      //if (verifica_casa == 0) verifica_casa = 1;
      verifica_casa=verifica_casa+1;
      lcd.print( "CASA " );
    }
    else
      lcd.print("");

    // Indicazione permanente allarmi attivati

    if (verifica_garage >= 1) {
      lcd.setCursor(1, 2);
      lcd.print("Gar");
      lcd.setCursor(2, 1);
      lcd.print(verifica_garage);
    }

    if (verifica_cantina >= 1) {
      lcd.setCursor(5, 2);
      lcd.print("Can");
      lcd.setCursor(6, 1);
      lcd.print(verifica_cantina);
    }

    if (verifica_terrazzo >= 1) {
      lcd.setCursor(9, 2);
      lcd.print("Ter");
      lcd.setCursor(10, 1);
      lcd.print(verifica_terrazzo);
    }

    if (verifica_mansarda >= 1) {
      lcd.setCursor(13, 2);
      lcd.print("Man");
      lcd.setCursor(14, 1);
      lcd.print(verifica_mansarda);
    }

    if (verifica_casa >= 1) {
      lcd.setCursor(17, 2);
      lcd.print("Cas");
      lcd.setCursor(18, 1);
      lcd.print(verifica_casa);
    }

    //avvio cicalino
    
    tone(6, 2000, 100);
    delay(200);
    tone(6, 2000, 100);
    delay(200);
    tone(6, 2000, 100);
    delay(600);

    tone(6, 2000, 100);
    delay(200);
    tone(6, 2000, 100);
    delay(200);
    tone(6, 2000, 100);
    delay(600);
    
    tone(6, 2000, 100);
    delay(200);
    tone(6, 2000, 100);
    delay(200);
    tone(6, 2000, 100);
    delay(600);
   
    delay(2000);
  
 
    }
}

Guarda questo esempio? https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay

non potrebbe comunque lampeggiare ogni 500 ms con quei delay che hai nel loop o meglio nell’else +/- 5000

hai confuso la funzione millis come una funzione indipendente che agisce da sola, purtroppo non è così, deve essere costantemente e piuttosto rapidamente verificata, tu invece la richiami dal loop ogni 5 secondi

void loop() {
...
...
...

//avvio cicalino
   
    tone(6, 2000, 100);
    delay(200);
    tone(6, 2000, 100);
    delay(200);
    tone(6, 2000, 100);
    delay(600);

    tone(6, 2000, 100);
    delay(200);
    tone(6, 2000, 100);
    delay(200);
    tone(6, 2000, 100);
    delay(600);
   
    tone(6, 2000, 100);
    delay(200);
    tone(6, 2000, 100);
    delay(200);
    tone(6, 2000, 100);
    delay(600);
   
    delay(2000);

Inoltre con questa formula if((time+300)<millis()) cadi nell’errore dell’overflow, il led non funzionerà perchè time sarà maggiore del millis() corrente per 52 giorni

ciao

Hai notato che la variabile time è evidenziata (di colore arancione)?
Non credo tu possa usarla: prova a cambiare nome.

ciao
pippo72

Grazie, in effetti i delay inseriti per far suonare il cicalino non vanno d’accordo con il “millis” ed ho anche rinominato la variabile “time”.
Ho provato a spostare il tutto al termine della segnalazione sonora (a valle dei delay) utilizzando un comando “while” che verifica che almeno un allarme è entrato in funzione. Ora il led lampeggia, ma il loop rimane “intrappolato” nel while e tutto si blocca anche premendo il pulsante di reset (puls_reset)… :o

void loop() {
...
...
...

//avvio cicalino
  
    tone(6, 2000, 100);
    delay(200);
    tone(6, 2000, 100);
    delay(200);
    tone(6, 2000, 100);
    delay(600);

    tone(6, 2000, 100);
    delay(200);
    tone(6, 2000, 100);
    delay(200);
    tone(6, 2000, 100);
    delay(600);
  
    tone(6, 2000, 100);
    delay(200);
    tone(6, 2000, 100);
    delay(200);
    tone(6, 2000, 100);
    delay(600);
  
    delay(2000);


 //lampeggio led di avviso
while(verifica_garage + verifica_cantina + verifica_terrazzo + verifica_mansarda + verifica_casa >=1){

digitalWrite(led_di_avviso, state);
if((tempo_adesso+300)<millis())//Se sono trascorsi 300 millisecondi
{                   //
state = !state;   //Cambia lo stato del led
tempo_adesso = millis(); // azzera il conteggio del tempo
}

}

ciao

Se all’interno del While non vai a leggere gli ingressi dubito che ne potrai uscire…

ciao
pippo72

Solo un suggerimento, lo so che ci sono modi piu "eleganti", ma usare un'if direttamente nel loop e condizionarlo allo stato di una variabile, ad esempio chiamata "blinkled" ?

Cioe' ... si usa un semplice ciclo if per fare il lampeggio, ma lo si mette all'interno di un secondo ciclo if che lo esegue solo se "blinkled" e' TRUE ... in questo modo, per fare iniziare il lampeggio si mette TRUE la variabile blinkled, e per farlo fermare la si mette FALSE ...

Niente da fare ragazzi… sto led non vuole lampeggiare.
Ma mi chiedo più in generale: è valido il principio secondo il quale è possibile richiamare una funzione in qualsiasi parte dello skatch e farla girare in modo autonomo?
Nel mio caso, ad allarme scattato, invoco questa funzione blink_led_avviso():

//funzione per far lampeggiare il led di avviso
void blink_led_avviso(){
digitalWrite(led_di_avviso, state);
if((time+300)<millis())//Se sono trascorsi 300 millisecondi
{                   //
state = !state;   //Cambia lo stato del led
time = millis(); // azzera il conteggio del tempo
}

Se inclusa in un ciclo “if”, non dovrebbe funzionare fin quando le condizioni dell’if sono valide indipendentemente dai vari delay?

lorenzo_1971:
Ma mi chiedo più in generale: è valido il principio secondo il quale è possibile richiamare una funzione in qualsiasi parte dello skatch e farla girare in modo autonomo?

Che intendi con "farla girare in modo autonomo" ? ? ?

Non sei mica su un sistema multitask che attivi un task e quello gira in modo autonomo ...
... tu, dove vuoi, chiami la funzione, il programma chiamate si ferma entra nella funzione, la esegue fino alla fine, esce e torna da dove hai chiamato la funzione e da li riprende, né più né meno.

Guglielmo

Ok, grazie Guglielmo. Pensavo che la funzione, una volta richiamata dal programma, potesse girare per conto ed il programma proseguire nel suo loop fin quando un nuovo comando la faccia interrompere.

Prova inserendola in un'if e facendo dipendere l'if da una variabile ... secondo me dovrebbe funzionare ... (l'if ovviamente va nel loop principale, non deve essere una funzione separata, a meno che il loop non la richiami in continuazione, ma a quel punto non avrebbe senso farci una funzione, e perderebbe tempo per nulla)

una cosa del genere: (fatta al volo, devi adattarla al tuo sketch))

if (ledblink==TRUE) {
   if ((millis()-tempo) > 500) {
      stato = !stato;
      digitalWrite(led, stato)
      tempo = millis();
   }
}

nel loop ... poi quando vuoi far partire il lampeggio, fai tempo=millis() e ledblink=TRUE (entrambe le istruzioni una volta sola, cosi setta il tempo e fa partire l'eseuzione dell'if) ... e quando vuoi fermarlo fai ledblink=FALSE e digitalWrite(led, LOW) (cosi interrompi l'esecuzione dell'if e spegni il led nel caso in quel momento fosse acceso) ... Per sicurezza dichiara stato come HIGH all'inizio dello sketch ...

lorenzo_1971:
Ok, grazie Guglielmo. Pensavo che la funzione, una volta richiamata dal programma, potesse girare per conto ed il programma proseguire nel suo loop fin quando un nuovo comando la faccia interrompere.

No, il trucco è quello di usare il loop() in modo intelligente, un po come ti sta suggerendo Etem ...

Ti consiglio di considerare il loop() come il processo che, girando in continuazione, può attivare a tempo / condizionate da flag, delle funzionalità.

Se entri in questa logica, vedrai che il loop() diventerà il tuo scheduler e tutte le varie cose che devi fare saranno in funzioni richiamate ciclicamente, quando necessario, dal loop() :wink:

Guglielmo

Funziona!!! Ho seguito entrambi i consigli.
Con un IF (quando almeno un sensore è scattato) avvio il lampeggio:

//lampeggio led di avviso
if (led_blink == true){
if ((millis()-tempo_adesso) > 300) {//Se sono trascorsi 500 millisecondi
state = !state;   //Cambia lo stato del led
digitalWrite(led_di_avviso, state);
tempo_adesso = millis(); // azzera il conteggio del tempo
}

}

Il cui codice, però, l'ho spostato dalla fine del loop al punto in cui il programma è in "attesa" dei rilevamenti dai sensori (a quel punto, se un allarme è precedentemente scattato, led_blink sarà in true e il led lampeggia).

Grazie Etem, grazie Guglielmo, grazie a tutti.

Buona serata!

Oltee al sistema dei flag, in questi casi viene utile il concetto di macchina a stati