Aiuto Input controllato da rele' - Necessita Debouncing

Salve a tutti ragazzi...
Avrei bisogno di un piccolo aiuto per sistemare un problema che ogni tanto si verifica.

Ho un Mega2560 dove gira un programma che controlla il processo di miscelazione dei chicci di caffe' prima della tostatura.

Ora, il processo parte quando l' INPUT1 viene messo in stato HIGH dalla chiusura di un rele' azionato da una macchina esterna. Alla chisura del rele' INPUT1 viene spostato da NO to GRD.

Essendo un rele' meccanico a volte il processo di miscelazione parte senza che il rele' viene alimentato.
Sospetto fortemente che vibrazioni possano causare delle micro chiusure dei contatti e quindi far partire il processo.

Non sono un super esperto e quindi ho bisogno di un piccolo aiuto se qualcuno e' disposto a dare un occhiata al codice.

Grazie in anticipo
coffee_batch_control_05_05_21.zip (10.4 KB)

Scusa ma se il problema pare qualcosa di hardware, perchè chiedi di vedere il codice ?
Dubito che puoi risolvere via software se gli input che arrivano possono essere sbagliati.

Dubito che una vibrazione esterna possa fare chiudere il contatto di un relè, ma nel caso sia cosi il relè è da sostituire perché non è un comportamento normale. Hai provato a simulare dando dei colpetti leggeri al relè?

Per quanto riguarda il rimedio, intanto aggiungerei il classico passa-basso RC a tutti gli ingressi (cosa che andrebbe fatta a prescindere), se non dovesse bastare ci puoi mettere una pezza anche via software.
L'ingresso incriminato qual'è?

Io sospetto che sia un problema di interferenza elettromagnetica e non i contatti del relé che si chiudono per le vibrazioni, quindi applicherei in prima battuta quanto detto da @cotestatnt e se, come immagino, il problema verrà mitigato ma non risolto avrai la certezza che è un problema di interferenze. A quel punto torna in gioco il fatto che Arduino così com'è non è utilizzabile in ambito operativo proprio perché non ha le caratteristiche da renderlo certificato in ambito civile/industriale.
A quel punto opterei per sostituirlo con una versione certificata (Controllino, sprodotti sferalabs, ecc.) e vedrai che il problema sparirà

Grazie mille per le vostre risposte
Vi aggiorno qui:

  1. Chiedo qui perche volevo mettere la funzione deboucing all' INPUT1

  2. Il rele' usato e' tipo questo: https://au.element14.com/finder/38-51-0-024-0060/relay-spdt-250vac-6a/dp/1169353
    Ho provato e dando dei colpettini infatti i contatti all'interno si toccano e fanno partire il processo.
    Il filtro passa basso, non ho capito bene a che cosa servirebbe e dove applicarlo

  3. Posso confermarti che l'elettomagnetico non puo' essere perche il tutto e' racchiuso all'interno di una scatola in plastica. Inoltre non ci sono campi elettromagnetici vicini

Senza offendere nessuna, a me servivi una mado per inserire nel codice la funzione debouncing e quindi fare delle prove.

Grazie

Nessun problema. Ma secondo me con un debounce software non risolvi. Parere mio.

1 Like

Questa cosa non ha alcun senso... Per attenuare i disturbi indotti per via elettromagnetica, la scatola dovrebbe essere metallica e messa a terra.

Comunque se con il colpetto il ciclo parte, possiamo ragionevolmente escludere questa ipotesi e continuo ad essere dell'idea che dovresti cambiare il relè.

Nel software che hai allegato c'è già un meccanismo di debounce che però non ti mette al riparo da questo malfunzionamento (riga 34 del file handleTimer.ino) perché semplicemente aspetta un po' prima di cambiare stato.

Potresti provare cosi e vedere se è efficace come metodo:

  case WAIT_ROASTER_START:                                // Wait until INPUT1 goes low - just in case.
      if (digitalRead(INPUT1) == LOW) {
        dischargeTimerState = WAIT_DEBOUNCE;
        isDischargedTime = millis();
      }
      break;

    case WAIT_DEBOUNCE:                                     // Relay contacts may bounce.
      if (millis() - isDischargedTime > 100  {              // After some delay time,
        if(digitalRead(INPUT1) == LOW) {
          dischargeTimerState = WAIT_ROASTER_READY;         // start waiting for the roaster to finish (and INPUT1 to go high again).4
        } 
        else {
          dischargeTimerState = WAIT_ROASTER_START;         // Falsa lettura del contatto, ritorna in attesa
        }
      }
      break;

... che NON ha alcun effetto sui disturbi elettromagnetici! Per poter schermare il contenitore deve essere metallico e connesso a terra.

Guglielmo

Grazie della risposta
La parta di codice che hai postato interessa un altro INPUT2 che controlla anche lo stato dell 'INPUT1.
Io penso che il Debouncing deve essere inserito qui ma non so come e dove di preciso:

case STANDBY:                                           // Waiting for INPUT1 to go HIGH.
  if (digitalRead(INPUT1) == HIGH) {                    // INPUT1 HIGH: can start the batch.
    fillingBin = 0;                                     // Start filling the first bin.
    startWeight = scaleWeight;                          // The weight at the start of this process.
    for (uint8_t i = 0; i < NBINS; i++) {               // Reset the bin weights for proper display.
      binWeight[i] = 0;
    }
    setState(FILLING_BIN);                              // We start filling the bin.
  }
  checkWDT();
  break;

case FILLING_BIN:                                       // Open the valves one by one; add material.
  binWeight[fillingBin] = scaleWeight - startWeight;    // Record the weight added to this bin.
  if (binWeight[fillingBin] >= binTargetWeight[fillingBin]) { // This one is complete.
    lastFillCompleteTime = millis();                    // Record when we completed this one.
    setState(FILLING_PAUSE);                            // Take a break for the scale to stabilise.
  }
  checkWDT();
  break;

Prova cosi:

   case STANDBY:                                           // Waiting for INPUT1 to go HIGH.
      static uint32_t bounceTime;
      if (digitalRead(INPUT1) == HIGH) {            
        bounceTime= millis();
      }

      // Segnale ancora alto e sono passati pi√Ļ di 200ms (se il rel√® √® farlocco meglio stare larghi con il debounce)
      if (digitalRead(INPUT1) && millis() - bounceTime > 200) {  // INPUT1 HIGH: can start the batch.
        fillingBin = 0;                                     // Start filling the first bin.
        startWeight = scaleWeight;                          // The weight at the start of this process.
        for (uint8_t i = 0; i < NBINS; i++) {               // Reset the bin weights for proper display.
          binWeight[i] = 0;
        }
        setState(FILLING_BIN);                              // We start filling the bin.
      }
      checkWDT();
      break;

Grazie mille
Domani faccio l'upload e vedo come va.
Ti faccio sapere il risultato il prima possibile.

Ciao Cotestatnt

Ho inserito la parte di codice ma non funziona.
Adesso se il rele' collegato all'INPUT1 viene azionato il sistema non parte proprio.

Magari c'e' altro che ci sfugge?

Se faccio l'upload del codice originale, tutto torana a funzionare.

Grazie in anticipo

Ops.. Ho scritto un po'di corsa ed in effetti è normale che faccia così.
Se l'ingresso è alto il valore di bounceTime viene aggiornato continuamente a millis() e quindi l'if successivo non è mai vero.
√ą necessario salvare il valore di millis() solo sul fronte di salita del segnale.

Ora sono da smartphone e mi risulta un po' scomodo. Pi√Ļ tardi correggo.

Fai con calma....
Ti faccio solo presente che l' INPUT1 e collegato al GRD costanatemenete. Il rele' quindi apre i contatti per far partire il processo.
Nel caso cambia qualcosa

Grazie mille

Allora si spiega anche il perché dell'avvio picchiettando il relé. Il contatto normalmente chiuso non fa bene contatto. Di solito serve una corrente minima circolante affinché un contatto chiuso non formi ossido superficiale, ed è indicata nel datasheet dello specifico relé. Quindi va collegata una adeguata resistenza di pull-up esterna (tra ingresso e il positivo di alimentazione della logica) che faccia circolare almeno una decina di mA nel contatto chiuso.

E tra l'altro non mi sembra per niente sicuro un impianto che si avvia con un'interruzione :thinking:

Abbastanza strano per una attivazione, no? Come mai non hai fatto come i comuni mortali cioè collegando il contatto Normalmente Aperto.

I contatti NC sono per l’INPUT1 dell’arduino.
Ci passano pochissimi mA. Non posso essere usurati.

√ą stato progettato cos√¨

Condivido quanto detto da @Claudio_FF
Il circuito cosi com'è non è molto sicuro, potresti verificare se hai la possibilità di aggiungere un interblocco elettromeccanico.
Non ho ben chiaro il processo che devi controllare, ma quando deve partire il ciclo in questione, immagino ci siano delle "condizioni" della macchina a monte che puoi sfruttare a tuo vantaggio per una maggior sicurezza intrinseca.

Ciò detto per filtrare il tuo segnale sul pin INPUT1 potresti provare in questo modo.
Ho aggiunto una variabile static bool per memorizzare il fronte di salita del segnale ed una temporanea per il segnale visto che viene letto pi√Ļ volte.

  case STANDBY:                                       // Waiting for INPUT1 to go HIGH.
	{ 
	  /* 
	  * Le parentesi sono necessarie perché usiamo delle variabili locali all'interno del blocco.
	  * La direttiva static fa in modo che la variabile locale venga memorizzata come se fosse globale.
	  * Il valore quindi rimane quindi "preservato" in memoria pur mantenendo lo scope a livello locale
	  */      
	  static uint32_t bounceTime;						// Variabile per memorizzare il "momento" in cui il segnale va alto
	  static bool risingEdge = false;				    // Flag booleano per memorizzare il fronte di saluta
	  bool signalInput = digitalRead(INPUT1);			// Assegniamo a signalInput il valore di INPUT1
	  
	  // Fronte di salita del segnale di input
      if (signalInput && !risingEdge) {     
		// Queste istruzioni saranno eseguite solo una volta alla transizione LOW->HIGH di INPUT1
        bounceTime= millis();
		risingEdge = true;
      }

      // Vero solo quando sono passati pi√Ļ di 200ms (se il rel√® √® farlocco meglio stare larghi con il tempo di debounce)
      if (risingEdge && millis() - bounceTime > 200) {    
		/* 
		* Se il segnale è ancora a livello alto, avvio il ciclo. In caso contrario si è trattato
		* di una falsa lettura e quindi non facciamo nulla a parte resettare risingEdge
		*/
		if (signalInput) {		   		        // INPUT1 HIGH: can start the batch.
		  fillingBin = 0;                                 // Start filling the first bin.
		  startWeight = scaleWeight;                      // The weight at the start of this process.
		  for (uint8_t i = 0; i < NBINS; i++) {           // Reset the bin weights for proper display.
		    binWeight[i] = 0;
		  }
		  setState(FILLING_BIN);                          // We start filling the bin.
		}
		
		// Reset di risingEdge per il prossimo fronte di salita
		risingEdge = false;
      }

      checkWDT();
      break;
	}

Ciao cotestant
Ho caricato il programma sul mio setup per prove e sembra che funzioni.
Devo aspettare meta' settimana per metterlo in produzione e vedere se tutto funziona e se nessun altro processo si e' alterato con l'aggiunta della tua parte di codice.

Come e' collegato il sistema e' abbastanza sicuro ma come hai detto tu un interblocco mi darebbe la massima sicurezza.
Ci stavo gia' pensando da tempo.....
Ho 6 motori controllati da un inverter a azionati tramite contattori con le relative termiche. Stavo pensano di metter l'interblocco collegato in serie a tutte le termiche cosi' se un motore si guastava, mi stoppava il sistema in modo celere.

Non voglio abusare della tua pazienza e del tuo tempo....
Ci vorrebbe molto ad integrare nel codice questa funzione?

In pratica un INPUT che deve essere collegato sempre al GRD altrimenti il processo non parte. Se uno dei motori si guasta e la termica apre il circuito, il processo si stoppa e un altro INPUT viene portato su HIGH (magari ci collego un led cosi' capisco il perche' il processo non continua)

Fammi sapere.
Grazie ancora