Sistema ridondante Arduino UNO

Buongiorno a tutti mi servirebbe una mano da un programmatore esperto. Il pulsante viene configurato come input con pull-up interno, I pin dei led vengono configurati come output. Il pin di errore viene impostato a LOW. La comunicazione seriale viene inizializzata alla velocità di 9600 baud. Viene attivato il watchdog interno della scheda.
La funzione checkButtonState1() verifica lo stato del pulsante handleButtonPress()
La funzione handleButtonPress() invia un comando alla scheda e attiva un ack (acknowledgement) per indicare comando ricevuto.
La funzione handleWatchdog() controlla il tempo trascorso dall'ultimo reset E' presente anche il bit di parità che viene utilizzato per controllare la correttezza della trasmissione del messaggio.

#include <SoftwareSerial.h>
#include <avr/wdt.h>

#define RX_PIN 0
#define TX_PIN 1
#define BUTTON_PIN 2
#define LED_PIN 3
#define ERROR_LED_PIN 4
#define TIMEOUT 1000
#define WATCHDOG_TIMEOUT 5000
#define DEBOUNCE_TIME 50

SoftwareSerial serial(RX_PIN, TX_PIN);
bool buttonState1 = false;
bool ack1 = false;
bool ack2 = false;
unsigned long lastPress1 = 0;
bool ledState = false;
bool errorState = false;
unsigned long lastWatchdogReset = 0;

void checkButtonState1(unsigned long now);
void checkReceivedMessage();
void toggleLED();
void handleButtonPress();
void handleSerialCommunication();
void handleWatchdog();

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);
  pinMode(ERROR_LED_PIN, OUTPUT);
  digitalWrite(ERROR_LED_PIN, LOW);
  serial.begin(9600);
  wdt_enable(WDTO_8S);
}

void loop() {
  handleSerialCommunication();
  checkReceivedMessage();
  unsigned long now = millis();
  checkButtonState1(now);
  toggleLED();
  handleWatchdog();
  digitalWrite(LED_PIN, ledState);
  digitalWrite(ERROR_LED_PIN, errorState);
}

void checkButtonState1(unsigned long now) {
  static bool buttonState1Prev = false;
  bool reading1 = !digitalRead(BUTTON_PIN);
  if (reading1 != buttonState1Prev) {
    buttonState1Prev = reading1;
    if (buttonState1Prev) {
      lastPress1 = now;
      handleButtonPress();
    }
  }
}

void checkReceivedMessage() {
  if (serial.available()) {
    char message = serial.read();
    bool parityBit = false;
    int bitCount = 0;
    for (int i = 0; i < 7; i++) {
      if (bitRead(message, i) == 1) {
        bitCount++;
      }
    }
    if (bitCount % 2 == 0) {
      parityBit = true;
    }
    if (bitRead(message, 7) == parityBit) {
      if (message == '1') {
        ack1 = true;
      } else if (message == '2') {
        ack2 = true;
      }
    }
  }
}

void toggleLED() {
  if (ack1 && ack2) {
    ledState = !ledState;
    digitalWrite(LED_PIN, ledState);
    ack1 = false;
    ack2 = false;
  }
}

void handleButtonPress() {
  if (!ack1) {
    serial.write('1');
    ack1 = true;
  } else if (!ack2) {
    serial.write('2');
    ack2 = true;
  }
}

void handleSerialCommunication() {
  if (serial.available()) {
    char message = serial.read();
    bool parityBit = false;
    int bitCount = 0;
    for (int i = 0; i < 7; i++) {
      if (bitRead(message, i) == 1) {
        bitCount++;
      }
    }
    if (bitCount % 2 == 0) {
      parityBit = true;
    }
    if (bitRead(message, 7) == parityBit) {
      if (message == '1') {
        ack1 = true;
      } else if (message == '2') {
        ack2 = true;
      }
    }
  }
}

void handleWatchdog() {
  if (millis() - lastWatchdogReset > WATCHDOG_TIMEOUT) {
    wdt_reset();
    lastWatchdogReset = millis();
  }
}

bool checkChecksum

#include <SoftwareSerial.h>
#include <avr/wdt.h>

#define RX_PIN 0
#define TX_PIN 1
#define BUTTON_PIN 2
#define LED_PIN 3
#define ERROR_LED_PIN 4
#define TIMEOUT 1000
#define WATCHDOG_TIMEOUT 5000
#define DEBOUNCE_TIME 50

SoftwareSerial serial(RX_PIN, TX_PIN);
bool buttonState1 = false;
bool ack1 = false;
bool ack2 = false;
unsigned long lastPress1 = 0;
bool ledState = false;
bool errorState = false;
unsigned long lastWatchdogReset = 0;

void checkButtonState1(unsigned long now);
void checkReceivedMessage();
void toggleLED();
void handleButtonPress();
void handleSerialCommunication();
void handleWatchdog();
bool checkChecksum(char message);

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);
  pinMode(ERROR_LED_PIN, OUTPUT);
  digitalWrite(ERROR_LED_PIN, LOW);
  serial.begin(9600);
  wdt_enable(WDTO_8S);
}

void loop() {
  handleSerialCommunication();
  checkReceivedMessage();
  unsigned long now = millis();
  checkButtonState1(now);
  toggleLED();
  handleWatchdog();
  digitalWrite(LED_PIN, ledState);
  digitalWrite(ERROR_LED_PIN, errorState);
}

void checkButtonState1(unsigned long now) {
  static bool buttonState1Prev = false;
  bool reading1 = !digitalRead(BUTTON_PIN);
  if (reading1 != buttonState1Prev) {
    buttonState1Prev = reading1;
    if (buttonState1Prev) {
      lastPress1 = now;
      handleButtonPress();
    }
  }
}

void checkReceivedMessage() {
  if (serial.available()) {
    char message = serial.read();
    if (checkChecksum(message)) {
      if (message == '1') {
        ack1 = true;
      } else if (message == '2') {
        ack2 = true;
      }
    }
  }
}

bool checkChecksum(char message) {
  bool parityBit = false;
  int bitCount = 0;
  for (int i = 0; i < 7; i++) {
    if (bitRead(message, i) == 1) {
      bitCount++;
    }
  }
  if (bitCount % 2 == 0) {
    parityBit = true;
  }
  if (bitRead(message, 7) == parityBit) {
    return true;
  } else {
    return false;
  }
}

void toggleLED() {
  if (ack1 && ack2) {
    ledState = !ledState;
    digitalWrite(LED_PIN, ledState);
    ack1 = false;
    ack2 = false;
  }
}

void handleButtonPress() {
  if (!ack1) {
    serial.write('1');
    ack1 = true;
  } else if (!ack2) {
    serial.write('2');
    ack2 = true;
  }
}

void handleSerialCommunication() {
  if (serial.available()) {
    char message = serial.read();
    if (checkChecksum(message)) {
      if (message == '1') {
        ack1 = true;
      } else if (message == '2') {
        ack2 = true;
      }
    }
  }
}

void handleWatchdog() {
  if (millis() - lastWatchdogReset > WATCHDOG_TIMEOUT) {
    wdt_reset();
    lastWatchdogReset = millis();
  }
}

È un watchdog software??? Se si blocca il programma, si blocca anche lui!

Buongiorno! Il problema... Non funziona nulla😂Quali soluzioni?

No, è un modo per chiamare ad intervalli prefissati, usando millis(), il reset del WDT hardware della MCU wdt_reset(); (Reset the watchdog timer. When the watchdog timer is enabled, a call to this instruction is required before the timer expires, otherwise a watchdog-initiated device reset will occur.).

Se il programma si blocca, li non ci passa più e scatta il WDT hardware.

Guglielmo

1 Like

Grazie singnor Guglielmo della specificazione. Io non sono il tipo da pappa pronta, ma sono in grado di trovare l'errore. È un codice già molto avanzato x le funzioni che utilizza. Sono disponibile a condividere tutto il progetto con relativa relazione, schemi, etc. Prima però deve funzionare. Grazie a chi vorrà metterci mano.

#define RX_PIN 0
#define TX_PIN 1

Su questi pin c'è la seriale hardware, quindi o usi la seriale hardware cioè: Serial.read ecc
Oppure se ti serve la seriale software devi scegliere altri due pin.

Poi per il resto lo si vede dopo. Assicurati che ogni singolo componente funzioni, cioè fai nel setup il test per inviare un byte. Chiama nel setup le singole funzioni per testarle quando possibile.

Ciao.

Buonasera e grazie. Provo a spostare i pin. 0 e 1 liberi, 2 e 3 per la seriale e a seguire il resto. Le faccio sapere. Grazie per la disponibilità.

Buongiorno, ho impostato i pin della seriale 11 e 12... sembra funzionare! I led di errore, se implemento la mancanza di comunicazione seriale rimangono sempre accesi, mentre prima, fortunatamente sempre spenti...

#include <SoftwareSerial.h>
#include <avr/wdt.h>

#define BUTTON_PIN 2
#define LED_PIN 3
#define ERROR_LED_PIN 4
#define TIMEOUT 4000
#define WATCHDOG_TIMEOUT 5000
#define DEBOUNCE_TIME 50
#define RX_PIN 11
#define TX_PIN 12

SoftwareSerial mySerial(RX_PIN, TX_PIN); // RX, TX
bool ack1 = false;
bool ack2 = false;
bool errorState = false;
unsigned long lastPress1 = 0;
unsigned long lastWatchdogReset = 0;
unsigned long lastMessageTime = 0;
bool ledState = false;
bool messageReceived = false;
bool connectionState = false;
bool lastConnectionState = false; // Aggiunta variabile lastConnectionState

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);
  pinMode(ERROR_LED_PIN, OUTPUT);
  digitalWrite(ERROR_LED_PIN, LOW);
  Serial.begin(9600);
  mySerial.begin(9600);
  wdt_enable(WDTO_8S);
}

// Loop principale
void loop() {
  handleSerialCommunication();
  unsigned long now = millis();
  bool buttonState1;
  checkButtonState1(now, &buttonState1);
  toggleLED(&ledState);
  handleWatchdog();
  handleTimeout(now);
  digitalWrite(LED_PIN, ledState);
  if (mySerial.isListening()) {
    digitalWrite(ERROR_LED_PIN, errorState);
  }
  lastConnectionState = connectionState; // Aggiornamento lastConnectionState
}

// Controlla lo stato del pulsante e gestisce l'antirimbalzo
void checkButtonState1(unsigned long now, bool* buttonState1) {
  static bool buttonState1Prev = false;
  bool reading1 = !digitalRead(BUTTON_PIN);
  if (reading1 != buttonState1Prev) {
    buttonState1Prev = reading1;
    if (buttonState1Prev) {
      if (now - lastPress1 > DEBOUNCE_TIME) {
        handleButtonPress();
        lastPress1 = now;
      }
    }
  }
  *buttonState1 = buttonState1Prev;
}

// Gestisce la pressione del pulsante e invia il comando corrispondente
void handleButtonPress() {
  if (!ack1) {
    mySerial.write('1');
    ack1 = true;
  } else if (!ack2) {
    mySerial.write('2');
    ack2 = true;
  }
}

// Legge i messaggi dalla comunicazione seriale e gestisce gli ACK

void handleSerialCommunication() {
static char message = '\0';
if (mySerial.available()) {
message = mySerial.read();
if (checkChecksum(message)) {
messageReceived = true;
if (message == '1') {
ack1 = true;
} else if (message == '2') {
ack2 = true;
}
lastMessageTime = millis();
connectionState = true;
}
} else {
connectionState = false;
}
}

// Verifica il checksum del messaggio ricevuto
bool checkChecksum(char message) {
bool parityBit = false;
int bitCount = 0;
for (int i = 0; i < 7; i++) {
if (bitRead(message, i) == 1) {
bitCount++;
}
}
if (bitCount % 2 == 0) {
parityBit = true;
}
if (bitRead(message, 7) == parityBit) {
return true;
} else {
return false;
}
}

// Alterna lo stato del LED quando entrambi gli ACK sono ricevuti
void toggleLED(bool* ledState) {
if (ack1 && ack2) {
*ledState = !(*ledState);
digitalWrite(LED_PIN, *ledState);
ack1 = false;
ack2 = false;
}
}

// Gestisce il watchdog timer
void handleWatchdog() {
if (millis() - lastWatchdogReset > WATCHDOG_TIMEOUT) {
wdt_reset();
lastWatchdogReset = millis();
}
}

// Gestisce il timeout e controlla lo stato della connessione seriale
void handleTimeout(unsigned long now) {
static bool lastConnectionState = false; // Aggiunta variabile lastConnectionState
if (messageReceived && (now - lastMessageTime > TIMEOUT)) {
errorState = true;
messageReceived = false;
} else {
errorState = !connectionState;
}
if (lastConnectionState != connectionState) { // Controlla se la connessione è cambiata
if (connectionState) {
digitalWrite(ERROR_LED_PIN, LOW); // Spegni il LED di errore quando la connessione è stabilita
} else {
digitalWrite(ERROR_LED_PIN, HIGH); // Accendi il LED di errore quando la connessione è persa
}
lastConnectionState = connectionState;
}
}

Molte cose non mi risultano chiare. Per aiutarti devo prima schiarirmi le idee, a questo scopo ti consiglio di usare la funzione di formattazione automatica Ctrl-t che indenta il codice, ad esempio segue tuo codice indentato manualmente:

// Alterna lo stato del LED quando entrambi gli ACK sono ricevuti
void toggleLED(bool* ledState) {
    if (ack1 && ack2) {
        *ledState = !(*ledState);
        digitalWrite(LED_PIN, *ledState);
        ack1 = false;
        ack2 = false;
    }
}

Per chi è abituato a leggere codice indentato come me, ci impiega mezzo secondo a capire cosa fa e se c'è qualcosa di sbagliato. Mentre ci impiego una eternità quando il codice è come lo hai postato.

Attenzione alle variabili locali

// Loop principale
void loop() {
  handleSerialCommunication();
  unsigned long now = millis();
  bool buttonState1;
  checkButtonState1(now, &buttonState1);

La variabile buttonState1 è locale e ci sono delle conseguenze:

  • La variabile è allocata nella memoria ram gestita come stack (pila di piatti) e nel caso specifico il suo valore non è stato specificato e il suo valore implicito dipende da cosa conteneva in precedenza quella cella di memoria. Per cui occorre inizializzare sempre le variabili locali. Cosa non strettamente necessaria per le variabili globali che assumono un valore di default, solitamente zero.
  • Lo stack viene ripulito quando la funzione restituisce il controllo al chiamante, quindi quando la funzione loop() termina buttonState1 non esiste più. Non è chiaro ma la funzione loop termina e viene nuovamente chiamata e così all'infinito.

Non mi è chiaro

  • Stai usando due schede fisiche? Oppure una sola scheda con due seriali software connesse in loop?
  • Per il bit di parità solitamente si sacrifica il bit 7 (128), ad esempio '1' (49) 3 bit accesi, '2' (50) 3 bit accesi per cui devo accendere il bit 7 per avere un numero pari di bit accesi, cioè 4. Chi riceve deve almeno assicurasi che il numero di bit ricevuti sia pari, se lo è scarta il bit 7 con datoRicevuto = datoRicevuto & 127.

PS: C'è disponibile il simulatore online wokwi che non necessita di iscrizione.
PS1: Concludo sempre con "Ciao" tutti i miei post.

Ciao.

Buonasera, per non incorrere in errori il codice è su pastebin: #include <SoftwareSerial.h>#include <avr/wdt.h>#define BUTTON_PIN 2#defi - Pastebin.com.
Sto usando due schede fisiche Arduino Uno che controllano un pulsante con doppio contatto NA. Il primo contatto è su una scheda e il secondo sull'altra scheda (ridondanza) Entrambe le schede avranno un relè. Entambi i relè si ecciteranno alla pressione del pulsante. In caso una scheda dovesse fallire la seconda scheda porterà a temine l'operazione. Ovviamente è necessario un led su ciascuna scheda che si accenda in caso di errore. Avendo la seriale impegnata non posso collegare il PC con il monitor seriale per avere dei messaggi. Se una scheda si "impasta" deve potersi resettare da sola mandando un avviso che si è riavviata. Così come se ci dovessero essere incongruenze un qualcosa deve avvisare. L'idea è la verifica di una apertura porta molto particolare, quindi il relè deve rimanere alto sino a quando non interviene un fine corsa che dice al relè di diseccitarsi. In egual modo in chiusura. Ovviamente i fine corsa in apertura e in chiusura saranno sempre doppi. L'idea è l'implementazione di un sistema ridondante sia software che hardware. Per la parte hardware non ho alcun problema così come per gli alimentatori in ridondanza. Per concludere, immagina un film di fantascienza. due porte dividono una stanza. La porta di cui parlo apre sull'esterno dello spazio siderale :joy: la prima porta invece ti fa accedere alla stanza. Ci deve essere anche la condizione che se la seconda porta di cui stiamo parlando trova la prima porta aperta non deve aprire la seconda porta. Ciao e grazie
PS per relazione e schemi se interessato invio per mail. La prima parte di relazione è ok, o almeno credo. Lo schema lo disegnerò appena il tutto funzionerà egregiamente.
--- attenzione NON si mette l'eMail nei post ed i contatti avvengono tramite messaggi privati (MP). Basta andare sul nick dell'utente e fare click, apparirà l'apposito bottone per lo scambio di messaggi privati. - gpb01

ok. grazie dell'info. buona serata gpb01

1 Like

Ti posso chiedere come mai vuoi implementare un sistema del genere?
Parlando da "manutentore" (con una discreta esperienza alle spalle), francamente mi sembra un sistema inutilmente complesso... il motto in questi casi è che " tutto quello che non c'è, non si rompe! "

La ridondanza tipicamente viene fatta sui segnali di ingresso/uscita (doppio contatto per i finecorsa e doppio canale per le uscite) e non sui sistemi di controllo.

Ho avuto esperienza con macchine in cui venivano usate due CPU per eseguire lo stesso "task" più o meno come vuoi fare e questa cosa era per lo più fonte di problemi senza alcun valore aggiunto reale.

Buongiorno cotestatnt. Cercherò di essere rapido ed esaustivo. Credo che nella esperienza di ciascuno sarebbe opportuno andare oltre i limiti personali, di promuovere nuove sfide e in questo caso (parlo per me) ci si accorge, nonostante tutto, di come la materia di studio sia infinita e di quanto poco si conosca. Oggi, la ridondanza dei sistemi è essenziale per la sicurezza delle persone, basti pensare ai sistemi avionici. Sono perfettamente d'accordo con lei che tutto quel che c'è non si rompe, non solo, sono un grande estimatore di Occam. Le macchine, da lei citate, sono effettivamente fonte di problemi e sono convinto che suddetti nascano solo da progettazioni malfatte. Un sistema ridondante prima di essere applicato deve necessariamente passare numerosi test che implicano molto tempo e molto denaro. Le aziende che si affidano per la produzione di tali sistemi non colgono l'idea dei costi - benefici. L'utilizzo di un micro come Arduino non è certo il massimo dell'affidabilità, ma per sperimentare è più che ottimo. Resta, poi, l'idea affascinante di un sistema che sia in grado di "auto-ripararsi"; forse un giorno... Spero di aver risposto adeguatamente alla sua domanda e sono disponibile a suggerimenti, critiche e quanto possa essere d'aiuto a crescere.

@Milanodabere se non è un problema, preferirei se ci dessimo del tu. Del resto il forum è un posto molto "amichevole" di base :slightly_smiling_face:

Sono completamente d'accordo sul discorso dei limiti. Io non me ne sono mai posti e continuo a non farlo tutt'oggi. Se un sistema non lo conosco, mi rimbocco le maniche e lo studio fino a quando non ne vengo fuori.
Nel mio lavoro, non mi sono mai posto problemi di fronte a qualcosa di nuovo e sconosciuto, ma quando si parla di "sicurezza" e in particolare delle persone (campo di applicazione a cui viene da pensare quando si parla di sistemi ridondanti) è necessario pensarci non due volte, ma dieci!

Fintanto che si rimane nell'ambito della sperimentazione va tutto bene, ma un prototipo è un sistema che viene testato in un ambiente delimitato e da tecnici che sanno quello che stanno facendo e spero che sia solo questo il caso. Se si "mette in produzione" il prototipo tutto questo discorso va a farsi benedire.

Personalmente non userei mai una scheda di sviluppo per implementare qualcosa che ha a che fare con la sicurezza, ma non per una questione di affidabilità... se si rompe pazienza.

Piuttosto perché non è un dispositivo classificato come adeguato per la funzione che deve svolgere secondo le norme tecniche vigenti ridondanza o meno!
Mi piace dormire sereno la notte, e nel malaugurato caso dovesse succedere qualcosa, il responsabile ne deve rispondere penalmente secondo le attuali leggi.

Certamente! Infatti esistono precise direttive che rispondono a normative CE, RoHS, REACH, EN 61010-1 ...
Attualmente esistono dispositivi certificati basati su STM32, PIC di Microchip, MSP430 di Texas ... E anche l'Arduino Industrial 101 e l'Arduino OPTA. Questo è solo uno studio personale che non avrà alcuna implicazione commerciale. Di notte piace dormire anche a me.

Ecco il codice riscritto senza troppi fronzoli. Funziona. Se servisse a qualcuno c'è anche una relazione... Chiedete!
Grazie!
PS per quanto mi riguarda [CHIUSO :wink:]

/*
Il codice controlla lo stato di tre LED, un relè e due contatti di input. Il primo contatto è un pulsante che deve essere premuto per attivare il relè. Il secondo contatto è un contatto di sicurezza che se aperto, impedisce l'attivazione del relè.
Il debounce viene implementato sui due contatti di input (pin 6 e 7), per evitare falsi contatti dovuti a fluttuazioni di tensione. Se il contatto del pulsante è chiuso (pulsante premuto) e il contatto di sicurezza è chiuso, i LED 2 e 3 si accendono, mentre il LED 1 si spegne e il relè si attiva. Altrimenti, il LED 1 si accende, mentre i LED 2, 3 e il relè si spengono.
Inoltre, viene monitorato lo stato del pin 6, che se aperto, fa accendere il LED 4.

Le variabili utilizzate sono:

    LED_PIN_1, LED_PIN_2, LED_PIN_3, LED_PIN_4: costanti intere che definiscono i numeri dei pin per i quattro LED;
    CONTACT_PIN: costante intera che definisce il numero del pin per il pulsante di attivazione;
    RELE_PIN: costante intera che definisce il numero del pin per il relè;
    SECURITY_PIN: costante intera che definisce il numero del pin per il contatto di sicurezza;
    previousMillis: variabile unsigned long utilizzata per tenere traccia del momento in cui è stato rilevato l'ultimo cambiamento dei contatti di input, necessario per implementare il debounce;
    debounceDelay: costante long che indica la durata del debounce in millisecondi;
    contactState: variabile booleana utilizzata per memorizzare lo stato attuale del pulsante di attivazione dopo il debounce;
    currentState: variabile booleana utilizzata per memorizzare lo stato attuale dei contatti di input durante il debounce;
    currentMillis: variabile unsigned long utilizzata per memorizzare il momento attuale, necessario per implementare il debounce;
    isContactClosed(): funzione booleana che restituisce true se il pulsante di attivazione è chiuso dopo il debounce;
    isRelayOn(): funzione booleana che restituisce true se il relè deve essere acceso;
    setLed1(), setLed2(), setLed3(), setLed4(): funzioni che accendono o spengono i tre LED (setLED4 scorta);
    setRelay(): funzione che accende o spegne il relè.
 */

// Definizione dei pin per i led, il pulsante di attivazione, il relè e il contatto di sicurezza
const int LED_PIN_1 = 2; // Led rosso, segnala la pressione del pulsante sul pin 7
const int LED_PIN_2 = 3; // Led verde, si accende se pin 7 non è a GND
const int LED_PIN_3 = 4; // Segnalazione di riserva (pin 4 alto se pin 7 non è a GND) 
const int LED_PIN_4 = 5; // Led blu, si accende se pin 6 è aperto
const int CONTACT_PIN = 7; // Pulsante di attivazione
const int RELE_PIN = 8; // Relè,inattivo se il pulsante di attivazione non va a GND
const int SECURITY_PIN = 6; // Contatto di sicurezza

// Variabili per la gestione del debounce del pulsante di attivazione
unsigned long previousMillis = 0;
const long debounceDelay = 50;
bool contactState = LOW;

// Funzione che legge lo stato del pulsante di attivazione e gestisce il debounce
bool isContactClosed() {
  bool currentState = digitalRead(CONTACT_PIN);
  if (currentState != contactState) { // Il pulsante è stato premuto o rilasciato
    unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= debounceDelay) { // È passato abbastanza tempo da considerare lo stato del pulsante stabile
      previousMillis = currentMillis;
      contactState = currentState; // Aggiorna lo stato del pulsante
    }
  }
  return !contactState; // Ritorna lo stato del pulsante invertito (premuto = LOW, rilasciato = HIGH)
}

// Funzione che legge lo stato del contatto di sicurezza e del pulsante di attivazione e determina lo stato del relè
bool isRelayOn() {
  if (digitalRead(SECURITY_PIN) == LOW) { // Il contatto di sicurezza è chiuso, quindi il relè dipende dallo stato del pulsante di attivazione
    return isContactClosed();
  } else { // Il contatto di sicurezza è aperto, quindi il relè è sempre disattivato
    return false;
  }
}

// Funzioni per impostare lo stato dei led
void setLed1(bool state) {
  digitalWrite(LED_PIN_1, !state);
}

void setLed2(bool state) {
  digitalWrite(LED_PIN_2, !state);
}

void setLed3(bool state) {
  digitalWrite(LED_PIN_3, !state);
}

void setLed4(bool state) {
  digitalWrite(LED_PIN_4, state);
}

// Funzione per impostare lo stato del relè
void setRelay(bool state) {
  digitalWrite(RELE_PIN, state); // Il relè si attiva con LOW e si disattiva con HIGH
}

// Configurazione dei pin
void setup() {
  pinMode(LED_PIN_1, OUTPUT);
  pinMode(LED_PIN_2, OUTPUT);
  pinMode(LED_PIN_3, OUTPUT);
  pinMode(LED_PIN_4, OUTPUT);
  pinMode (CONTACT_PIN, INPUT_PULLUP); // Il pulsante di attivazione è connesso a GND quando viene premuto, quindi impostiamo la modalità INPUT_PULLUP
pinMode(RELE_PIN, OUTPUT);
pinMode(SECURITY_PIN, INPUT_PULLUP); // Il contatto di sicurezza è connesso a GND quando è chiuso, quindi impostiamo la modalità INPUT_PULLUP

// Inizializza la variabile previousMillis
previousMillis = millis();
}

// Loop principale
void loop() {
// Legge lo stato dei pulsanti
bool contact1Closed = isContactClosed();
bool contact2Closed = digitalRead(SECURITY_PIN) == LOW;
bool pin6Open = digitalRead(6) == HIGH;

// Controlla lo stato dei contatti e accende i LED di conseguenza
if (contact1Closed && contact2Closed) {
// Entrambi i contatti di sicurezza sono chiusi, accende il led 2 (verde), il led 1 (rosso) viene spento
setLed1(false);
setLed2(true);
setLed3(true);
setRelay(true);
} else {
setLed1(true);
setLed2(false);
setLed3(false);
setRelay(false);
}

// Controlla lo stato del pin 6 e accende il LED di conseguenza
if (pin6Open) {
// Il pin 6 è aperto, accende il led 4 (blu)
setLed4(true);
} else {
// Il pin 6 è chiuso, spegne il led 4 (blu)
setLed4(false);
}
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.