attiny45 e controllo sensore - analisi del firmware

Finalmente ho potuto fare un po' di prove con i miei nantetti montati nei cassonetti e mi sono reso conto di un bug nel debouncing del sensore. Il sensore è un elemento rotante meccanico, ruotando assieme alla tapparella genera un mare di rimbalzi impossibile da gestire senza debounce; il sensore gestisce un contatto aperto/chiuso e può stare in una posizione qualsiasi; con l'interrupt attivo il controllo ma con lo sketch davo per scontato che ad ogni movimento passasse da chiuso ad aperto, con un delay(200) superavo (confermato dall'oscilloscopio) la fase di rimbalzo e sembrava tutto ok. In realtà mi sono reso conto che aprendo una tapparella di colpo, in 200ms si alza anche di 1 metro :astonished: e se a fine movimento per caso (e capita spesso) il sensore si ritrova nuovamente su chiuso, il contatore non si incrementa. Sono riuscito (a c...) ad aprire una tapparella di balcone in due alzate senza far scattare l'allarme; ovvio che così non può andare! All'"epoca" ignoravo la libreria debounce, né l'ho studiata ora, ma prima di metterci mano voglio essere sicuro che non usi il delay, poi cercherò di studiare una logica software migliore. Vi allego lo spezzone di sketch:

void loop()
{
  // Lettura del sensore
  valueSensore=digitalRead(sensore); //leggo lo stato del sensore
  if (valueSensore==HIGH) // se il sensore (NC) è su HIGH significa che è attivo
    {
      delay(200); //ritardo antirimbalzo
      valueSensore=digitalRead(sensore); //rileggo lo stato del sensore
      if (valueSensore==LOW) // mi serve perché a volte il sensore rotativo resta su NA
        {
          contatore++; //incremento il contatore degli impulsi
          ritardo=0; //azzero il conteggio del ritardo a partire dall'ultimo impulso del sensore
        }
      else
        {
          counthigh++; //incremento un altro contatore se il sensore resta bloccato su attivo
        }
    }

La Debounce è datata, è stata sostituita dalla Bounce. Usa i millis() internamente.

Non ha senso eliminare il problema alla radice e usare un contatto reed e relativo magnete al posto del sensore esistente. Ciao Uwe

Sì l’avevo letto nella ricerca, c’è la nota proprio sulla pagina della libreria, ma avevo dimenticato il nome della nuova :blush:.
Il vantaggio è quindi che non blocco l’mcu, ma ora devo ragionare nuovamente sulla logica sw, non avevo previsto la variabile NCprima-NCdopo e questo è un handicap, visto che la probabilità che il sensore si ritrovi nella stessa posizione è del 50%, decisamente un’esagerazione!

@ Uwe:
devo controllare la tapparella a prescindere dalla sua posizione originale, non è detto che sia sempre tutta chiusa, quindi posso usare o il sensore a cordina (che è scomodissimo) o questo sensore rotativo fisso, con una rotella sempre a contatto con la tapparella, che gira con essa e mi dà il contatto; purtroppo non ho suluzione alternativa per il sensore, o almeno così credo.

A me pare che blocchi il codice, ad una prima occhiata. D'altronde esegue dei controlli con i millis, non con gli interrupt.

leo72: A me pare che blocchi il codice, ad una prima occhiata. D'altronde esegue dei controlli con i millis, non con gli interrupt.

non ho capito, ti riferisci al mio delay o alla libreria? Aggiungo che il sensore lo uso solo per svegliare il micro con il pin interrupt, invece a questo punto mi sa che devo rivedere tutto, ma tanto sono davvero quattro righe.

menniti: @ Uwe: devo controllare la tapparella a prescindere dalla sua posizione originale, non è detto che sia sempre tutta chiusa, quindi posso usare o il sensore a cordina (che è scomodissimo) o questo sensore rotativo fisso, con una rotella sempre a contatto con la tapparella, che gira con essa e mi dà il contatto; purtroppo non ho suluzione alternativa per il sensore, o almeno così credo.

Dico di sostituire la combinazione ruota-contatto con ruota - magnetino e reed (non di mettere un reed di taparella chiusa. Se ne monti piú magnetini e 2 reed puoi avere una risoluzione maggiore e leggere la direzione del movimento.

Ciao Uwe

@Mike: mi riferivo alla libreria Bounce. A me pare che blocchi il codice. Non usa il delay ma il ciclo che fa presuppone comunque l'uscita solo al termine del tempo preimpostato nel momento della dichiarazione della libreria. In pratica quindi si comporta come un delay.

@ Uwe: e come faccio? il sensore mica l’ho costruito io, ma forse non capisco io cosa intendi dire. D’altra parte dopo tre mesi sono ancora qui a fare test, se mi metto a modificare i sensori non ne esco più :fearful: In fondo devo solo superare un problema di logica, per il resto funziona tutto bene.

@ Leo: appunto, come temevo, ecco perché ho chiesto consiglio prima di stare lì a perdere tempo; a questo punto devo gestire tutto con l’interrupt, lasciando il delay, ma in questo momento (a quest’ora poi…) non ho idea di cosa posso fare.

Vista l'ora e visto il fatto che sono sveglio da circa 20 ore :sleeping: :sleeping:, non ho più neanche io il cervello per cercarti una soluzione...

scusami ma non capisco esattamente cosa vuoi fare.

Se l'allarme è inserito, come può riconoscere se la tapparella la alzi tu o qualcun'altro? comunque:

allora il debounce io lo farei così:

//variabili gloabali
boolean debounce=false;
boolean lastStatusSensore;
unsigned long lastStatusTime;

//nel setup
lastStatusSensore = digitalRead(sensore);
lastStatusTime = millis(); //da gestire l'overflow

//nel loop
valueSensore=digitalRead(sensore);
if (valueSensore!=lastStatusSensore){
  if(millis()-lastStatusTime>DEBOUNCE_TIME){
    //il debounce è terminato, se succede qualcosa quì dentro è perchè hai aperto o chiuso la tapparella dopo un bel pò!
  }
  //aggiorna le variabili di supporto
  lastStatusTime=millis();
  lastStatusSensore=valueSensore;
}

l'unica cosa da settare è il valore, in millisecondi, di DEBOUNCE_TIME. volendo si può anche calcolare la velocità con cui stai alzando la tapparella, ovvero gli on-off del sensore al secondo, in modo da modificare DEBOUNCE_TIME dinamicamente, ma mi sembra una complicazione inutile.

menniti:
In realtà mi sono reso conto che aprendo una tapparella di colpo, in 200ms si alza anche di 1 metro :astonished: e se a fine movimento per caso (e capita spesso) il sensore si ritrova nuovamente su chiuso, il contatore non si incrementa.

In questo caso hai un errore di logica nel tuo software, se il sensore cambia stato vuol dire che la tapparella si è mossa quindi devi, in tutti i casi, considerarlo come una condizione di allarme indipendentemente dal fatto che alla fine del debounce il sensore si trovi nello stesso stato in cui era prima del delay per il debouncing.

@ menniti Un interrupt peggiora le cose se hai problemi con il bounce perché devi fare il debounce prima eletronicamente. Come dice astrobeed se Ti serve solo sapere se la taparella é stato mossa puoi ignorare il bounce. Solo se vuoi sapere la posizione della taparella devi gestire di quanto é stata mossa. Hai controllato che un forte vento che fa muovere la taparella non fa girare il sensore e Ti da dei falsi alarmi? Ciao Uwe

@Mike:
vediamo se ho capito come funziona quel sensore. Il braccetto è appoggiato alla tapparella con una rotellina che scorre su di essa.
Quando la tapparella sale oltre il sensore, il braccetto si chiude tutto e scatta l’allarme. Riscendendo, la tapparella risposta il braccetto, che disattiva il sensore.

E’ così oppure no?

Grazie a tutti, il problema è proprio quello del dubbio finale di Uwe: Prima di mettere i nanetti, un qualsiasi minimo evento mi faceva scattale l'allarme, se il sensore si trovava giusto in prosimità di aprire o chiudere il contatto. Ho dovuto prevedere i nanetti per creare questa logica: 1 - alimento il nanetto, dopo 20 secondi lo metto a dormire 2 - al primo impulso il nanetto si sveglia, lo conteggia e si mette in ascolto, se nei successivi 20 secondi arriva un nuovo impulso non può essere un falso (verificato con certezza mediante prove) e allora mi manda l'impulso di allarme, poi si mette a dormire. 3 - se nei successivi 20 secondi non succede nulla lo interpreta come falso allarme, azzera il contatore e si rimette a nanna. 4 - Il ciclo ricomincia Quindi devo semplicemente ignorare il primo impulso che arriva, o ogni impulso che arriva sporadicamente (so che c'è una parte di rischio ma è calcolata, se volete poi vi spiego la cosa). Il vero problema è che prima e dopo il debounce potrei trovarmi con la condizione HIGH e quindi è come se non si fosse mosso; ma dall'intervento di Astrobeed mi rendo conto che in realtà io sto usando l'interrupt solo per svegliare il nanetto, quindi lo sketch riparte dal punto in cui avevo spento il micro; e dall'intervento di UWE mi rendo conto che il debounce è indispensabile. In conclusione penso che mi basti mettere la mia routine nella void di risveglio, significa che l'impulso è arrivato, quindi non controllo più lo stato post-debounce e procedo col conteggio. Semmai voglio capire se mi può essere utile il codice postato da lesto, al posto del delay. E' chiara la situazione ora? avete suggerimenti diretti?

@ leo: il braccetto serve solo per mantenere sempre la rotella a contatto con la tapparella; è la rotella che, girando, scorre su un sistema meccanico interno che fa due contatti ogni giro completo.

Allora se tirandolo su di scatto non si attiva il sensore forse vuol dire che la rotellina salta sulla tapparella e quindi non legge il brusco movimento?

Non perde un colpo, quel braccetto ha una molla potentissima che mantiene la rotella incollata alla tapparella; il problema è proprio nella logica del mio sketch :blush: Rispetto a quando l'ho realizzato, con la scusa della barriera infrarossi, ho imparato abbastanza sull'uso degli interrupt ed ora mi sto rendendo conto che è proprio sballata l'impostazione. Tutto nasce dal fatto che allora avevo la sballata convinzione che un interrupt funzionasse come un reset facendo ripartire il loop dall'inizio; ora invece so che non è così e mi spiego alcuni problemi che avevo sporadicamente. :~

se li metti in sleep l' impulso per sveglarli non fa partire il programma in un ben determinata posizione? Ciao Uwe

allora va bene il codice che ti ho scritto prima, devi solo cambiare

if(millis()-lastStatusTime>DEBOUNCE_TIME){
    //il debounce è terminato, se succede qualcosa quì dentro è perchè hai aperto o chiuso la tapparella dopo un bel pò!
  }

in

if(millis()-lastStatusTime<DEBOUNCE_TIME){
    //suona l'allarme
  }

(un maggiore in minore)
il valore di DEBOUNCE_TIME è 20 secondi, ma devi pensare in millesecondi, quindi DEBOUNCE_TIME=20000

se non capisci il codice chiedi pure.

@ Uwe: io pensavo che una volta in sleep al risveglio il micro iniziasse da 0 come un reset, mentre in realtà continua dal punto in cui si è fermato; nel mio caso ci sono almeno due punti in cui il micro viene mandato in sleep, quindi in base a quele dei due è stato eseguito il software si comporta di conseguenza; invece ora mi conviene spostare parte del codice nella void eseguita automaticamente dall'interrupt (cioè al risveglio) così non devo controllare se il sensore si è "mosso", visto che si è mosso di sicuro se attiva l'interrupt; non c'è niente da fare, la logica software è sbagliata, devo rivederla per bene ragionando con le nuove conoscenze.

@ Lesto: purtroppo sai che non sono una cima in programmazione, quindi mi mette fuori strada il commento "il debounce è terminato, se succede qualcosa qui dentro è perchè hai aperto o chiuso la tapparella dopo un bel pò!"; a me il debounce serve solo per far vedere al micro un impulso secco invece delle decine di impulsi provocati dai rimbalzi, quindi ad ogni contatto meccanico prelevo il primo impulso e poi blocco la lettura del sensore con un delay(200). Ad ogni giro completo della rotella in uscita ottengo (sui due fili del contatto): chiuso-aperto-chiuso-aperto. A me interessa percepire sia il passaggio da chiuso ad aperto che viceversa, perché NON SO mai in che posizione (essendo un cerchio) si ferma il contatto; se è in posizione chiuso devo percepire il passaggio ad aperto, se è in posizione aperto devo percepire il passaggio a chiuso; questa cosa mi è garantita dalla funziona "change" dell'interrupt, il delay mi serve per contare un solo impulso ad ogni passaggio (invece dei molti generati dai rimbalzi); i 20 secondi li conteggio a partire da ogni impulso, se nel frattempo non ne arriva un secondo metto il micro in sleep, se invece arriva scatta l'allarme. Attualmente in realtà non uso una funzione temporale per i 20 secondi ma un semplice contatore di cicli loop (il cui numero è calcolato empiricamente); il delay non posso usarlo per ovvvie ragioni e sul millis leggevo la storia dei 49 e passa giorni per l'overflow, non sapendolo gestire ho usato questo accrocco rozzo ma funzionale :D