Gestire una condizione di allarme

Lo scopo e’ quello di gestire una segnalazione di allarme, ma quello che ho realizzato mi sembra inutilmente complicato, mi date un parere ?
Quando ho la condizione di allarme da contatto esterno:

  1. accendo LED e suona un Cicalino
  2. se premo il pulsante di tacitazione
  3. il cicalino smette di suonare
  4. il LED si spegne solo se la causa dell’allarme non e’ presente, altrimenti rimane acceso fino a quando e’ presente la causa dell’allarme.

Per fare questo ho utilizzato una variabile numerica e lavorato sullo stato di pari o dispari

/*
Scopo: Memorizzazione ingresso e azzeramento/tacitazione
quando ingresso cambia stato, si accende il LED e suona il cicalino
se ingresso ritorna normale, il LED e cicalino sono acceso
alla pressione del tasto di azzeramento, il cicalino cessa di suonare, il
LED si spegne a meno che la causa di allarme sia ancora presente
*/
// this constant won't change:
const int AlrmPin = 2;         // ingresso allarme
const int AlrmOutPin = 13;	   // uscita al LED
const int AlrmMOutPin = 12;    // uscita al Cicalino
const int AckPin = 3;           // pulsante tacitazione

// Variables will change:
int AlrmState = 0;             // attuale stato dell'ingresso
int lastAlrmState = 0;         // precedente stato dell'ingresso
int Alrm = 2;
int Ack = 0;
int AckState = 0;
int lastAckState = 0;

//============================================
void setup() {
   pinMode(AlrmPin, INPUT);      // ingresso contatto allarme, normalmente Alto, Basso=Allarme
   pinMode(AckPin, INPUT);       // pulsante tacitazione
   pinMode(AlrmOutPin, OUTPUT);  // uscita LED
   pinMode(AlrmMOutPin, OUTPUT); // uscita cicalino
}

//============================================
void loop() {
  AlrmState = digitalRead(AlrmPin);    // legge stato ingresso
  if (AlrmState != lastAlrmState) {    // rileva cambio di stato ingresso
    if (AlrmState == LOW) {
     digitalWrite(AlrmOutPin, HIGH);    // se condizione allarme accende il LED
      if (!(Alrm % 2)) Alrm++;			// se non in allarme, memorizza
  }
    if (AlrmState == HIGH) digitalWrite(AlrmOutPin, LOW);
  }
  lastAlrmState = AlrmState;
  
  if (Alrm % 2) digitalWrite(AlrmMOutPin, HIGH); // se Alrm=dispari suona cicalino
  else digitalWrite(AlrmMOutPin, LOW);

  if (Alrm >= 9) Alrm = 2;// riporta Alrm entro la prima decina

  AckState = digitalRead(AckPin);
   if (AckState != lastAckState){
     if ((AckState == LOW) && (AlrmState == HIGH)) Alrm-- ; // se in allarme, azzera
   }   
    lastAckState = AckState;

}

AlarmMng.ino (1.91 KB)

Certo tutto quel codice nel loop nella previsione che ti serva inserire altro codice nel porta il loop ad essere poco leggibile dal programmatore e difficile da seguire. Stabiliamo che l'importante per adesso è fallo funzionare come non importa. La successiva fase potrebbe essere quella di spostare quel codice dentro una o più funzioni di modo che nel loop ci sia qualcosa di simile a:

checkAlarm();
/* //oppure
if (checkAlarm()) {

}
*/

Nel secondo caso checkAlarm ritorna true o false, quindi:

boolean checkAlarm() {
    AlrmState = digitalRead(AlrmPin);    // legge stato ingresso
    if (AlrmState != lastAlrmState) {   
    // ecc
}

Durante la creazioni di una o più funzioni per snellire il loop() chiediti se alcune variabili possono essere locali alle varie funzioni e non tutte globali. Es AlrmState potrebbe essere dichiarata dentro checkAlarm e quindi locale, tanto di serve solo per conservare lo stato della lettura digitalRead, se ti serve conservare lo stato della condizioni di allarme puoi usare una variabile globale numerica dove 1 == allarme presente non tacitato, 2 == allarme presente tacitato, 3 == altro.

Fai bene a chiederti come semplificare il codice e il primo passo e quello di distribuire il codice in una o più funzioni che prendono e/o ritornano parametri se necessario. Questo ti aiuta a scrivere un codice più comprensibile, ed è anche possibile che durante questo lavoro tu trovi un modo per ridurre il codice.

PS: Di norma i nomi di classi o enum o in genere i tipi di dato utente sono scritti così: AlrmState il che fa pensare che si tratti di una classe o struct o altro tipo, per evitare fraintendimenti le variabili iniziano con la minuscola: alrmState. Non abbreviare per ridurre la dimensione del codice perché non serve a nulla, alarmState o alrmState ai fini della occupazioni dello spazio sono la stessa cosa in quanto quando il tuo codice si trasforma in firmware, tutti gli identificativi (nomi di variabili, classi, oggetti ecc) vengono rimossi e trasformati in indirizzi di memoria.

Gli identificativi o simboli dichiarati e non usati (non referenziati) vengono rimossi dal compilatore grazie al parametro gc-section usato da arduino.

Ciao.

per MauroTec penso di aver (forse) capito a cosa ti riferivi, ho cambiato il codice per fare solo il Set//Reset, non mi da errore alla compilazione, ma non va'. Domani lo ricontrollo! Ciao!

/*
Scopo: Memorizzazione ingresso e azzeramento/tacitazione
quando ingresso cambia stato, si accende il LED e suona il cicalino
se ingresso ritorna normale, il LED e cicalino sono acceso
alla pressione del tasto di azzeramento, il cicalino cessa di suonare, il
LED si spegne a meno che la causa di allarme sia ancora presente
*/
// variabili par checkInputChange
boolean inputState, lastInputState, risultato; 

boolean checkInputChange (int inPin)      // funzione controlla cambio stato ingresso
{
 inputState = digitalRead(inPin);
 if (inputState != lastInputState) {
   lastInputState = inputState;
   risultato = HIGH; }
   else risultato = LOW;
   return risultato; 
}

// costanti
const int AlarmPin = 2;         // ingresso allarme
const int AlarmLedPin = 13; // uscita al LED
const int AlarmBuzzerPin = 12;    // uscita al Cicalino
const int AckPin = 3;           // pulsante tacitazione

// variabili 
int alarmState = 0;             // attuale stato dell'ingresso
int lastAlarmState = 0;         // precedente stato dell'ingresso
int alarm = 2;
int ack = 0;
int ackState = 0;
int lastAckState = 0;

//============================================
void setup() {
   pinMode(AlarmPin, INPUT);      // ingresso contatto allarme, normalmente Alto, Basso=Allarme
   pinMode(AckPin, INPUT);       // pulsante tacitazione
   pinMode(AlarmLedPin, OUTPUT);  // uscita LED
   pinMode(AlarmBuzzerPin, OUTPUT); // uscita cicalino
}

//============================================
void loop() {

 if (checkInputChange (AlarmPin) && (!(alarm % 2))) { 
   alarm++;
    if (alarm % 2) digitalWrite(AlarmLedPin, HIGH); // se Alrm=dispari suona cicalino
    else digitalWrite(AlarmLedPin, LOW);
    }
  if (alarm >= 9) alarm = 2;// riporta alarm entro la prima decina

  if (checkInputChange (AckPin) && (!(alarm % 2)))
 { 
   alarm-- ; // se in allarme, azzera
 }
}

ridotto ancora e risparmiato 14byte XD

/*
Scopo: Memorizzazione ingresso e azzeramento/tacitazione
quando ingresso cambia stato, si accende il LED e suona il cicalino
se ingresso ritorna normale, il LED e cicalino sono acceso
alla pressione del tasto di azzeramento, il cicalino cessa di suonare, il
LED si spegne a meno che la causa di allarme sia ancora presente
*/
// variabili par checkInputChange
boolean inputState, lastInputState, risultato; 

boolean checkInputChange (int inPin)      // funzione controlla cambio stato ingresso
{
 inputState = digitalRead(inPin);
 if (inputState != lastInputState) {
   lastInputState = inputState;
   risultato = 1; }
   else risultato = 0;
   return risultato; 
}

// costanti
//const int AlarmPin = 2;         // ingresso allarme
//const int AlarmLedPin = 13;   // uscita al LED
//const int AlarmBuzzerPin = 12;    // uscita al Cicalino
//const int AckPin = 3;           // pulsante tacitazione

// variabili 
byte alarmState = 0;             // attuale stato dell'ingresso
byte lastAlarmState = 0;         // precedente stato dell'ingresso
byte alarm = 2;
byte ack = 0;
byte ackState = 0;
byte lastAckState = 0;

//============================================
void setup() {
   pinMode(2, INPUT);      // ingresso contatto allarme, normalmente Alto, Basso=Allarme
   pinMode(3, INPUT);       // pulsante tacitazione
   pinMode(13, OUTPUT);  // uscita LED
   pinMode(12, OUTPUT); // uscita cicalino
}

//============================================
void loop() {

 if (checkInputChange (2) && (!(alarm % 2))) { 
   alarm++;
    if (alarm % 2) digitalWrite(13, 1); // se Alrm=dispari suona cicalino
    else digitalWrite(13, 0);
    }
  if (alarm >= 9) alarm = 2;// riporta alarm entro la prima decina

  if (checkInputChange (3) && (!(alarm % 2)))
 { 
   alarm-- ; // se in allarme, azzera
 }
}

Come vedi, dividere il codice in funzioni permette di concentrarsi sui dettaglio isolando il resto del codice.

boolean checkInputChange (int inPin)      // funzione controlla cambio stato ingresso
{
    inputState = digitalRead(inPin);
    if (inputState != lastInputState) {
        lastInputState = inputState;
        return HIGH; 
    } 
    return LOW; 
}

Non serve salvare lo stato “result” quindi si risparmia una variabile globale, che qui poteva anche essere locale.
lastInputState invece occorre che conservi il valore tra una chiamata e l’altra e questo lo si può fare usando variabili globali o static local.

boolean checkInputChange (byte inPin)      // funzione controlla cambio stato ingresso
{
    static boolean lastInputState = LOW;      // lastInputState now is static local variable
    boolean inputState = digitalRead(inPin); // now inputState is local variable

    if (inputState != lastInputState) {
        lastInputState = inputState;
        return HIGH; 
    } 
    return LOW; 
}

(int inPin) o (byte inPin) ??
Non esiste un pin negativo (-5) per cui, è il tipo di dato più adatto a contenere il numero del pin.

In un solo colpo abbiamo rimosso due variabili globali e ridotto la quantità di codice sorgente e questo
migliora anche la leggibilità del codice sorgente. Inoltre lastInputState e inputState sono dichiarate vicino
a dove vengono impiegate e non è necessario andare a cercare tra le globali per vedere di che tipo sono,
per cui osservando la funzione li dentro ci sono tutte le info per capire cosa fa la funzione.

Ciao.

per MauroTec, Ho provato a modificare la funzione "checkInputChange" come da te suggerito, ma il risultato non e' equivalente. Ma qui mi manca la confidenza con il codice che mi sarebbe di aiuto, in ogni caso, qui sotto c'e' l'ultima versione funzionante come descritto, e occupa 1.190bytes. Ho riscritto anche la descrizione dello sketch.

/*
Scopo: Gestione stato e memorizzazione ingresso allarme per malfunzionamento.
Ingresso un contatto Allarme Nomalmente Alto
Ingresso un contatto Tacitazione normalmente Alto
Una uscita Digitale (LED)
Una uscita digitale (Cicalino)
Quando contatto malfunzionamento diviene Basso, si accende il LED e suona il cicalino.
 ( il contatto si chiude per un attimo, oppure rimane chiuso fino alla fine della causa che lo ha attivato)
Se ingresso ritorna normale, il LED e cicalino sono ALTI
Alla pressione del tasto di Tacitazione, il cicalino cessa di suonare, il
LED rimane ALTO fino a quando la condizione di malfunzionamento e' presente.
*/
// variabili per funzione checkInputChange
boolean risultato; 
boolean checkInputChange(int inPin)      // funzione controlla cambio stato ingresso
{
 static boolean lastInputState = LOW;  
 boolean inputState = digitalRead(inPin);
   if (inputState != lastInputState) {
      if (inputState == LOW) risultato = HIGH;  // rilevo solo lo stato LOW, altrimenti ho allarme anche quando ritorna alto 
      else risultato = LOW; }
   lastInputState = inputState;
   return risultato;
 }

// costanti
const byte AlarmPin = 2;         // ingresso allarme
const byte AlarmLedPin = 13;    // uscita al LED
const byte AlarmBuzzerPin = 12;    // uscita al Cicalino
const byte AckPin = 3;           // pulsante tacitazione

// variabili 
byte memoriaUno = 0;
byte memoriaDue = 0;

//============================================
void setup() {
   pinMode(AlarmPin, INPUT);      // ingresso contatto allarme, normalmente Alto, Basso=Allarme
   pinMode(AckPin, INPUT);       // pulsante tacitazione, normalmente Alto,
   pinMode(AlarmLedPin, OUTPUT);  // uscita LED
   pinMode(AlarmBuzzerPin, OUTPUT); // uscita cicalino
}
//============================================
void loop() {

 if (checkInputChange(AlarmPin) && memoriaUno == 0) { 
     digitalWrite(AlarmBuzzerPin, HIGH);
     digitalWrite(AlarmLedPin, HIGH);
     memoriaUno = 1;
    }
 if (checkInputChange(AckPin)) digitalWrite(AlarmBuzzerPin, LOW), memoriaDue = 1;

 if (memoriaDue == 1 && (digitalRead(AlarmPin) == HIGH)) {
   digitalWrite(AlarmLedPin, LOW);
   memoriaUno = 0;
   memoriaDue = 0;
 }
}

Ciao e grazie

Ora la descrizione di cosa deve fare il codice mi risulta più chiara.

Tuttavia c'è necessità di riconferma.

Quando contatto malfunzionamento diviene Basso, si accende il LED e suona il cicalino. ( il contatto si chiude per un attimo, oppure rimane chiuso fino alla fine della causa che lo ha attivato) Se ingresso ritorna normale, il LED e cicalino sono ALTI

Questa parte deve essere chiarita con precisione. Io potrei scrivere qualcosa di simile a:

If (alarmState == LOW) alarmState = !digitaleRead(AlarmPin);

Il quale legge lo stato del pin alarm solo se l'applicazione non è in alarm. Visto che la condizioni di presenza alarm è LOW, ma lo stato di anomalia di solito equivale a alarm = HIGH, per questo inverto ciò che leggo e lo salvo in alarmState.

alarmState: uguale LOW Non è in allarme, uguale HIGH è in allarme Nota tra parentesi lo stato invertito grazie a ! prima di digitalRead(AlarmPin) digitaleRead(AlarmPin): restituisce HIGH (LOW) se non in allarme, LOW (HIGH) se è in allarme.

A questo codice non importa se la condizioni di allarme sul pin è rientrata nelle condizioni normali e una volta che alarmState == HIGH non esegue più la lettura del pin, quindi ciò che comanda lo stato di alarmState. Come dire se l'applicazione è in allarme prima di ricontrollare se permane la condizione di allarme devo spegnere l'allarme con la pressione di una combinazione di tasti.

Potrebbe essere questo il comportamento voluto oppure no, perché magari il controller deve fare altro di utile anche quando è in condizione di allarme.

PS: sembra banale ma non lo è. Devi stabilire la logica e esprimerla in modo non fraintendibile.

Ciao.

Riprovo descrivendo un esempio pratico (ma il mio utilizzo e' diverso) Supponiamo di avere un serbatoio dove ho un sistema che ne controlla il livello (non incluso in questa logica) Nella eventualita' di un possibile malfunzionamento del sistema di controllo del livello, ho un sistema ausiliario e indipendente, costituito dalla nostro codice, e da un interruttore di livello che viene azionato al superamento di una soglia. Con il mio codice vorrei: 1) quando il livellostato cambia stato (impulso o permanente), deve a) azionare un cicalino e accendere un LED 2) sento il cicalino, mi avvicino e vedo quale LED e' acceso. 3) premo il pulsante di Tacitazione, b) il cicalino smette di suonare c) il LED si spegne se si e' trattato di un problema momentaneo d) il LED rimane acceso fino a quando la condizione di allarme persiste.

Tieni presente che con il codice ultimo, funziona come descritto, ma non escludo passaggi inutili o tortuosi. Ciao

Io userei AND (&) e OR (|) per i controlli piuttosto che una variabile pari e dispari, che poi +/- sono la stessa cosa, non penso venga meno tortuoso alla fine

quando il livellostato cambia stato

mmm... dal codice precedente non ti serve tracciare il cambio di stato ma il passaggio di stato da HIGH -> LOW.

boolean alarmState = LOW;
boolean buzzerState = HIGH

void setup()
{
}

void loop() 
{
If (alarmState == LOW) {
     //alarmState = !digitaleRead(AlarmPin);
     buzzerState = alarmState = !digitaleRead(AlarmPin);;
} else  {
    // se è in allarme

    if (digitalRead(AckPin) == LOW) {
            buzzerState = LOW;                                       // spegne il buzzer 
            alarmState = !digitaleRead(AlarmPin);         // legge il pin alarmPin 
            // nota che se alarmState è LOW al prossimo ciclo non entra nell'else ma nell'if
    }
    digitalWrite(AlarmBuzzerPin, buzzerState);                         // buzzer on  
    digitalWrite(AlarmLedPin, alarmState);                               // alarm led on if alarmState is HIGH or led off se alarmState is LOW

}

Penso che così dovrebbe funzionare, se si non rimane che trasformarlo in funzioni e visto che tutte le variabili sono globali tutto il codice può essere spostato in una funzione che non prende e non restituisce nulla. Es:

void checkAlarmState()  // controlla lo stato allarmi
{

}

ciao.

per pablotec, Con della logica cablata, avrei utilizzato dei flip-flop bistabili, e questo e' un tentativo per simularli.

per MarcoTec Se alla fine sara' una funzione, mi va benissimo, perche' ho due allarmi distinti da gestire.

Ho provato il tuo (ultimo) codice, e il comportamento e' un poco diverso. "alarmState" viene letto nell' IF iniziale, e poi solo quando AckPin=LOW (nell' IF dopo ELSE) conseguentemente e' necessario un secondo AckPin=LOW per aggiornare "alarmState" e spegnere il LED.

La condizione di Allarme contiene due informazioni: cambio di stato,e condizione nel tempo. Lo stato temporaneo di AckPin=LOW deve essere memorizzata per consentire al LED di spegnersi autonomamente. [piu' tardi ci riprovo] Ciao

La logica io l'ho realizzata in base a come ho interpretato la tua spiegazione.

Ragionandoci, mi è sembrata funzionale e l'ho implementata.

Il funzionamento dovrebbe essere il seguente: Se si presenta LOW all'ingresso allarme, (sia che ritorni HIGH o permanga nello stato di LOW) si passa all'else e la prima if non viene eseguita. Ti presenti davanti la centralina e vedi il led acceso e il buzzer suona, taciti il buzzer, e viene letto lo stato di allarme che accende o spegne il led ed inoltre se alarmState == LOW si passa al prossimo ciclo ad eseguire la prima if.

Lo stato temporaneo di AckPin=LOW deve essere memorizzata per consentire al LED di spegnersi autonomamente.

Mi spiace mi sfugge la logica, il led credevo dovesse rimanere acceso anche se il buzzer viene tacitato e il led si spegne solo se il pin alarm è LOW.

Poi certo risolta la causa che tiene il led acceso devi azzerare l'allarme che in sostanza equivale a premere una combinazioni di tasti o entrare in un menu e confermare la segnalazione di allarme. Ora mi sono accorto che ci sono commenti errati nel codice, ora vedo e correggo.

Ciao.

MauroTec: La logica io l'ho realizzata in base a come ho interpretato la tua spiegazione.

Ragionandoci, mi è sembrata funzionale e l'ho implementata.

Il funzionamento dovrebbe essere il seguente: Se si presenta LOW all'ingresso allarme, (sia che ritorni HIGH o permanga nello stato di LOW) si passa all'else e la prima if non viene eseguita. Ti presenti davanti la centralina e vedi il led acceso e il buzzer suona, taciti il buzzer, e viene letto lo stato di allarme che accende o spegne il led ...

Dino: corretto, ma lo stato dell'allarme viene letto solo quando si tacita, conseguentemente se lo stato dell'allarme cabia dopo la tacitazione, e' necessario tacitare un'altra volta per aggiornare lo stato del LED

......ed inoltre se alarmState == LOW si passa al prossimo ciclo ad eseguire la prima if.

Dino: Per questo motivo penso sia necessario memorizzare la tacitazione, etc.

Lo stato temporaneo di AckPin=LOW deve essere memorizzata per consentire al LED di spegnersi autonomamente.

Mi spiace mi sfugge la logica, il led credevo dovesse rimanere acceso anche se il buzzer viene tacitato e il led si spegne solo se il pin alarm è LOW.

Dino: corretto

Poi certo risolta la causa che tiene il led acceso devi azzerare l'allarme che in sostanza equivale a premere una combinazioni di tasti o entrare in un menu e confermare la segnalazione di allarme.

Dino: Questa parte mi sfugge

Ora mi sono accorto che ci sono commenti errati nel codice, ora vedo e correggo.

Ciao.

Dino: non sono molto pratico con questo forum, e spero di aver commentato senza fare pasticci

edita il messaggio e sistemalo che così non si capisce nulla :astonished:

per pablotec,

chi è? carino come nick ora lo cambio :) :)

Quote per pablotec, chi è? carino come nick ora lo cambio smiley smiley

Io allora sono Maurolos. :P

Ho come l'impressione che al pin dell'allarme c'è collegato il meccanismo che si monta nell'avvolgibile delle tapparelle. Però se fosse così qualunque cambio di stato indifferentemente dallo stato di permanenza equivale ad un allarme, mentre il tuo codice controllo il cambio di stato ma l'allarme è presente solo se lo stato dell'ingresso che rileva l'allarme è LOW.

Puoi fare un prova per me? Prendi il codice che dici funziona e metti a LOW l'ingresso allarme, togli l'alimentazione al circuito per poi fornirla nuovamente. Ho il dubbio che se l'alimentazione viene fornita mentre l'ingresso allarme è LOW non viene segnalata la condizione di allarme, la cosa potrebbe essere non desiderata o forse si.

Ciao.

Se premere Reset, [Arduino-Uno] equivale a togliere tensione, si comporta cosi: - con contatto Allarme e ACK= HIGH ottengo Led e buzzer LOW, - con ACK =LOW e Allarme= HIGH, ottengo Led e buzzer LOW, - con contatto allarme= LOW e ACK =HIGH, ottengo Led e Buzzer = HIGH,

Una eventuale condizione iniziale (power up) non voluta, si dovrebbe poter "azzerare" con Ack. Ma questa eventuale condizione di funzionamento, introduce una ulteriore condizione, che potrebbe essere: utile / ininfluente / non-voluto. Ritengo sia meglio non introdurre in questo momento questa ulteriore condizione.

....l'allarme è presente solo se lo stato dell'ingresso che rileva l'allarme è LOW.

Avevo impostato la rilevazione dell'allarme in condizione LOW, perche' con il codice iniziale (dove si fa il controllo dello stato dell'ingresso) ottenevo una segnalazione di allarme sia quando avevo LOW, che quando avevo HIGH al ritorno della condizione "normale" dell'allarme.