Rilevamento del cambiamento di stato

Buongiorno, dovrei far svolgere la seguente funzione:
all'accensione il microcontrollore, legge lo stato di un pin, se è basso non fa nulla.
Se è alto attiva un relè per 2 secondi, poi finché resta alto non fa nulla.
Quando rileva che il pin è basso accende di nuovo il relè per 2 secondi
e cosi via, cioè ad ogni cambiamento di stato eccita il relè per 2 secondi.
Negli esempi ho trovato uno sketch che sembra fare al caso mio, ma vedo che interviene dopo 4 volte che viene premuto il pulsante. Lo dovrei modificare, basta solo mettere 1 al posto del 4?
Grazie.

/*
  State change detection (edge detection)

  Often, you don't need to know the state of a digital input all the time, but
  you just need to know when the input changes from one state to another.
  For example, you want to know when a button goes from OFF to ON. This is called
  state change detection, or edge detection.

  This example shows how to detect when a button or button changes from off to on
  and on to off.

  The circuit:
  - pushbutton attached to pin 2 from +5V
  - 10 kilohm resistor attached to pin 2 from ground
  - LED attached from pin 13 to ground (or use the built-in LED on most
    Arduino boards)

  created  27 Sep 2005
  modified 30 Aug 2011
  by Tom Igoe

  This example code is in the public domain.

  http://www.arduino.cc/en/Tutorial/ButtonStateChange
*/

// this constant won't change:
const int  buttonPin = 2;    // the pin that the pushbutton is attached to
const int ledPin = 13;       // the pin that the LED is attached to

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

void setup() {
  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT);
  // initialize the LED as an output:
  pinMode(ledPin, OUTPUT);
  // initialize serial communication:
  Serial.begin(9600);
}


void loop() {
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == HIGH) {
      // if the current state is HIGH then the button went from off to on:
      buttonPushCounter++;
      Serial.println("on");
      Serial.print("number of button pushes: ");
      Serial.println(buttonPushCounter);
    } else {
      // if the current state is LOW then the button went from on to off:
      Serial.println("off");
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state, for next time through the loop
  lastButtonState = buttonState;


  // turns on the LED every four button pushes by checking the modulo of the
  // button push counter. the modulo function gives you the remainder of the
  // division of two numbers:
  if (buttonPushCounter % 4 == 0) {
    digitalWrite(ledPin, HIGH);
  } else {
    digitalWrite(ledPin, LOW);
  }

}

Questo codice interviene ogni quattro pressioni (la % è il modulo del valore diviso 4) a te basta verificare se il valore è uguale a 1, dentro l'if lo resetti a zero e fai quel che devi.

Grazie per la risposta, ora non ho l'hardware per provare, lo farò domani.
Ma non ho capito come resettare a zero dentro l' if :confused:

Assegni il valore zero alla variabile

Bene :slight_smile:

Sto provando, all'avvio e senza modificare nulla, il led è acceso, ma voglio spento.
E naturalmente funziona dopo 4 colpi di pulsante.
Poi ho messo a 0 fisso la variabile buttonPushCounter, ma non va più nulla.

:confused:

Alla fine l'ho fatto ex novo, sarà "maccheronico" ma funziona. :slight_smile:

int button = 4;
int led = 3;
int buttonState = 0;
void setup() {
  pinMode(button, INPUT);
  pinMode(led, OUTPUT);
}

void loop() {
  buttonState = digitalRead(button);
  if (buttonState == 0) {
    loop();
  }
  else {
    digitalWrite(led, HIGH);
    delay(2000);
    digitalWrite(led, LOW);
    due();
  }
}
void due() {
  buttonState = digitalRead(button);
  if (buttonState == 1) {
    due();
  }
  else {
    digitalWrite(led, HIGH);
    delay(2000);
    digitalWrite(led, LOW);
    loop();
  }
}

Non capisco come possa funzionare, stai usando la ricorsione (una funzione che chiama se stessa) senza alcuna condizione di terminazione, quindi lo stack dovrebbe crashare immediatamente.

E qualcosa di molto più semplice?

Schermata da 2021-02-13 10-33-03.png

Schermata da 2021-02-13 10-33-03.png

Nel video puoi vedere che all'inizio do alimentazione (led rosso) e il verde si accende per 2s perché
il pin in ingresso è già a +5.
Poi vedi tutti gli step, e alla fine riaccendo con il pin a zero e il led non si accende .Ed è così che volevo.

https://streamable.com/8fb9hc

Non metto in dubbio che per qualche oscuro motivo (cioè per caso) funzioni. Il problema non è che sia maccheronico, è che stai usando le chiamate a funzione come se fossero dei GOTO, il che non è solo concettualmente sbagliato, ma manda in crash qualsiasi sistema in pochi istanti. Perché qui non accada è la cosa incomprensibile :o

Il GOTO è un "salto a", mentre una call (chiamata a funzione) occupa spazio sullo stack ad ogni chiamata e lo libera ad ogni ritorno. Se continui a chiamare e basta lo stack SI DEVE riempire corrompendo i dati in memoria.

Strutturando la logica a salti come hai fatto, almeno avresti dovuto usare i veri GOTO e non chiamate a funzione:

void loop() 
{
uno:
  if (digitalRead(button) == 0) goto uno;
  digitalWrite(led, HIGH);
  delay(2000);
  digitalWrite(led, LOW);

due:
  if (digitalRead(button) == 1) goto due;
  digitalWrite(led, HIGH);
  delay(2000);
  digitalWrite(led, LOW);
}

Che comunque sono inutili perché bastavano due while di attesa:

void loop() 
{
  while (digitalRead(button) == 0) {}
  digitalWrite(led, HIGH);
  delay(2000);
  digitalWrite(led, LOW);

  while (digitalRead(button) == 1) {}
  digitalWrite(led, HIGH);
  delay(2000);
  digitalWrite(led, LOW);
}

Ma la domanda rimane: come è possibile che non vada in crash? Lo specifico micro usato ha uno stack hardware, che anche quando va in overflow non sovrascrive altre parti di memoria?

Veramente bastava un solo if...

Non volevo chiedere la pappa bella è pronta, ho cercato di sforzarmi con i miei mezzi, ma visto
che la "pappa" me la fornite va bene così.
Poi ci torno su e vedo come va con i vs suggerimenti.
Comunque per ora funziona. :slight_smile:
Non ho preso in considerazione i goto perchè so che sono molto sconsigliati in tale ambiente.
Anch'io come già anticipato ho capito che non è uno sketch fatto in modo ortodosso.
Ho usato un Attiny 85, ciao.

Dato che ti interessano solo il fronte di salita e il fronte di discesa, a me pare più "pulito" così...

void loop() {

  statoIngresso = (digitalRead (button));

  if (statoPrecedenteIngresso == 0 && statoIngresso == 1) {
    statoPrecedenteIngresso = statoIngresso;

    digitalWrite (led, HIGH);
    delay (2000);
    digitalWrite (led, LOW);
  }

  if (statoPrecedenteIngresso == 1 && statoIngresso == 0) {
    statoPrecedenteIngresso = statoIngresso;

    digitalWrite (led, HIGH);
    delay (2000);
    digitalWrite (led, LOW);
  }
}

... che poi, semplificando, è così:

void loop () {

  statoIngresso = (digitalRead (button));

  if (statoPrecedenteIngresso == 0 && statoIngresso == 1 || statoPrecedenteIngresso == 1 && statoIngresso == 0) {
    statoPrecedenteIngresso = statoIngresso;

    digitalWrite (led, HIGH);
    delay (2000);
    digitalWrite (led, LOW);
  }
}

.... e qui scatta la gara a chi lo scrive più CONCISO :slight_smile: :slight_smile: :slight_smile: :slight_smile:

void loop () {

  statoIngresso = (digitalRead (button));

  if (statoPrecedenteIngresso != statoIngresso) {
    statoPrecedenteIngresso = statoIngresso;

    digitalWrite (led, HIGH);
    delay (2000);
    digitalWrite (led, LOW);
  }
}

questo intendevo quando dicevo un solo if.

Scusa, e' una mia idea, oppure ti esegue le istruzioni nell'if sia quando premi che quando rilasci, fatto cosi ?

Tipo invece cosi ?

void loop() {
  // diciamo che chiudi a massa, e statoPrec la dichiari 1 nelle assegnazioni

  if ((digitalRead (button) != statoPrec) && (statoPrec == 1)) {
    statoPrec = 0;
    digitalWrite (led, HIGH);
    delay (2000);
    digitalWrite (led, LOW);
  }
  else if ((digitalRead (button) != statoPrec) && (statoPrec == 0)) statoPrec = 1;
}

Se poi invece del delay fai una funzione con millis, non sarebbe bloccante ...

Io ho capito che deve farlo sul cambio

Il GOTO è un "salto a", mentre una call (chiamata a funzione) occupa spazio sullo stack ad ogni chiamata e lo libera ad ogni ritorno. Se continui a chiamare e basta lo stack SI DEVE riempire corrompendo i dati in memoria.

In effetti 2+2 fa 4, però stranamente tanto tempo addietro indagammo assieme a leo72 e astro e altri ma non ricordo più con precisione e ho perso tanti file. Io ricordo che lavorava e nello stack non veniva spinto nulla.

Ora non potendo compilare e disassemblare non possiamo dire altro, fatto sta che se funziona e per lungo tempo nel codice non ci deve essere alcuna istruzione push, se c'è ne almeno una o prima o poi lo stack sconfina e patatrac.

Se nella funzione ci sono molte variabili locali (inclusi argomenti) ci devono essere tanti push che eseguiti ricorsivamente rapidamente riempiono la ram fino a oltre il limite e patatrac, ma non essendoci variabili locali cosa viene spinto nello stack?

Io penso ci debba essere il push per salvare l'indirizzo del punto di ritorno e quindi solo 2 byte.

Esatto, almeno i push degli indirizzi di ritorno (anche se non vengono usati) dovrebbero essere effettuati, e la RAM si riempirebbe in pochi millisecondi corrompendo tutte le variabili presenti (e, forse, i registri SFR?). Sto ipotizzando anche che quel programma (post #7) non usando gli indirizzi di ritorno lavori permanentemente in crash dati. All'accensione funzionerebbe correttamente, ma la rilevazione delle successive aperture/chiusure invece non dovrebbe avvenire, perché almeno le variabili 'button' e 'led' dovrebbero corrompersi. O c'è qualche particolarità dello stack dell'ATTiny85 che non conosco (tipo essere un segmento ridotto dell'intera memoria disponibile usato in modo circolare).