Il suffit de ne pas rendormir l’arduino tant que le bouton n’est pas relâché. Il faut donc rendre la loop un peu plus intelligente (si le bouton est appuyé ne pas faire sleep )
et comment ça marche avec cette librairie ?
Dans ce dernier code, à la sortie de veille, les fonctions s'exécutent entièrement et retour en veille automatiquement quand l'action est terminée.
Ça fonctionne sans le detachInterrupt.
Si je mets RISING, l'action 1 se déchenche au relaché du bouton, mais toujours pas l'action2 ?
si le bouton est câblé PIN 2 --- BOUTON --- GND alors le bouton au repos est HIGH et lors de l'appui du bouton on passe à LOW. C'est donc FALLING qui devrait déclencher l'interruption?
ensuite si vous voulez attendre avant de dormir que le bouton soit relâché il suffit de tester la condition dans la loop
if (digitalRead(interruptPin) == LOW) {
// ici utiliser des variables pour voir si vous venez d'appuyer ou si
// la loop est en train de tourner dans ce cas mesurer le temps qui passe
// pour faire autre chose au bout de 2 secondes d'appui
...
} else {
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF); // on s'endort
// ici on se réveille, préparer les variables utiles (raz du compteur de 2 secondes etc)
...
}
Cette partie de code ne mesure pas le temps d'appui sur le bouton tel que tu le voudrais.
Lorsque le CPU se réveille sur appui bouton tu arrives directement sur ce if, le temps écoulé et évidemment inférieur à 2 secondes donc tu en sors immédiatement et repars en veille.
Il faudrait faire une boucle while(digitalRead(interruptPin)) et
soit tester si les 2 secondes sont écoulées et forcer une sortie de la boucle,
soit tester le temps écoulé en sortant de la boucle.
disons que mettre un while dans la loop c'est un peu contre productif puisque la loop() boucle déjà. c'est pour cela qu'on vous proposait plutôt un if() {} else {} afin de laisser la boucle tourner (si vous voulez faire autre chose en attendant le relâchement du bouton par exemple)
C'est vrai qu'un while est bloquant . Dans ce cas, ce n'est pas gênant, le retour en veille se fait dès que le bouton est relaché. 15 mA/ 5µA
Comment faire alors pour empêcher le retour en veille ?
Ça ne fonctionne pas. Peut-être qu'avec if (millis() - chrono > 2000)
le temps d'inaction est trop long, retour en veille et du coups, perte de l'info millis() ?
Bonjour,
Merci pour ta persévérance,
Je ne doute pas qu'il y ait une solution dans ce sens, mais ne suis pas assez féru ...
Je n'ai pas de soucis avec un rebond et l'interruption.
Je n'ai pas trouvé comment empêcher le retour en veille
J'ai essayé de mettre un delay(50) un peu partout sans succès.
La solution du while me va bien. Je n'attends que des évènements lents qui fonctionnent même avec plusieurs minutes de blocage système et puis dans ce cas, le blocage est volontaire, contrôlé.
En tout cas j'ai compris qu'avec cette librairie, on pouvait utiliser millis() à condition d'être "englobée" dans une fonction bloquante.
voilà un bout de code à tester avec le moniteur série réglé à 115200 bauds pour expliquer ce qu'il se passe (et il y a le clignotement de la LED comme vous le faisiez)
J'ai tapé cela ici, il se peut qu'il y ait des bugs mais ça devrait vous donner une idée:
#include <LowPower.h>
const byte pinBouton = 2;
const byte pinLed = LED_BUILTIN;
bool sleepOK = true;
volatile bool afficheDeuxSecondes;
volatile bool appuiBouton = false;
volatile uint32_t chrono;
uint32_t copieChrono;
uint32_t blinkChrono;
void bouton() {
appuiBouton = true;
afficheDeuxSecondes = true;
chrono = millis();
}
void setup() {
Serial.begin(115200);
Serial.println(F("\nTest sleep")); Serial.flush();
pinMode(pinBouton, INPUT_PULLUP);
pinMode(pinLed, OUTPUT);
attachInterrupt(digitalPinToInterrupt(pinBouton), bouton, FALLING);
}
void loop() {
if (appuiBouton) {
Serial.println(F("Reveil par appui bouton.")); Serial.flush();
// section critique pour copier les variables modifiables dans l'ISR
noInterrupts();
blinkChrono = copieChrono = chrono;
appuiBouton = false;
interrupts();
// fin de section critique
sleepOK = false; // on pourra se rendormir que lorsque le bouton sera relâché
digitalWrite(pinLed, HIGH); delay(20); digitalWrite(pinLed, LOW);
}
if (sleepOK) {
Serial.println(F("Je vais dormir.")); Serial.flush();
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
Serial.println(F("Je suis réveillé.")); Serial.flush();
} else { // on n'a pas encore le droit de dormir
if (digitalRead(pinBouton) == LOW) { // si le bouton est appuyé
if (millis() - copieChrono >= 2000) { // a-t-on atteint les 2s ?
if (afficheDeuxSecondes) { // pour ne faire l'affichage qu'une fois
Serial.println(F("Appui de deux secondes atteint. On clignote.")); Serial.flush();
afficheDeuxSecondes = false;
}
if (millis() - blinkChrono >= 200) { // doit-on inverser l'état de la LED ?
digitalWrite(pinLed, (digitalRead(pinLed) == HIGH ? LOW : HIGH));
blinkChrono = millis();
}
}
} else { // sinon le bouton est relâché
Serial.println(F("Bouton relâché.")); Serial.flush();
digitalWrite(pinLed, LOW);
sleepOK = true;
}
}
}
Le principe c'est que lors de l'appui du bouton ça déclenche l'ISR où on met la variable appuiBouton à vrai et on enregistre le moment dans chrono.
Comme l'ISR a réveillé l'arduino, on devrait voir * Je suis réveillé.* qui s'affiche, puis la loop() boucle et on va détecter appuiBouton qui est vrai et donc afficher Reveil par appui bouton.. On en profite pour copier chrono, on désactive l'appui bouton mais on met sleepOKà faux pour empêcher le code de se rendormir. Ensuite dans la loop on testera ce sleepOKpour voir quand on relâche le bouton, sinon on regarde la durée de l'appui.
il y a une petite variable en plus afficheDeuxSecondes qui ne sert à afficher qu'une seule fois le fait que le bouton a été tenu enfoncé plus de 2 secondes.
Le clignotement se fait comme dans l'exemple "blink without delay", j'ai une variable temporelle blinkChrono qui me dit quand alterner l'état de la LED.
Merci et bravo, belle démonstration, mais ça me dépasse.
A 69 balais c'est plus difficile de retenir les nouveautés.
les 0/1 me parlent plus que true/false
Plein de fonctions inconnues pour moi
Serial.flush(); noInterrupts(); interrupts(); ...
3 variables temporelles, ça se complique
Pourquoi ne pas utiliser chrono dans sleepOK ?
L'antirebond est géré ?
J'ai compris comment faire simplement avec if... else, n'autoriser la mise en veille qu'après le relâché du bouton.
Quelques variables renommées pour être plus parlant :
/*
fonctionne bien
Il fallait retourner en veille après le relaché du bouton
*/
#include <LowPower.h>
const byte boutonPin = 2;
const byte ledPin = LED_BUILTIN;
volatile bool action1 = 0;
bool action2 = 0;
unsigned long chrono = 0; // débutappui bouton
void setup()
{
// initialisation entree/sortie
pinMode(boutonPin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
action1 = 0;
attachInterrupt(digitalPinToInterrupt(boutonPin), bouton, FALLING);
}
void bouton() {// appui bouton sur interruption
action1 = 1 ;
}
void loop() {
if (!digitalRead(boutonPin)) {// bouton appuyé
if (action1)// dès l'appui
{
digitalWrite(ledPin, 1); delay(20); digitalWrite(ledPin, 0); // action1
chrono = millis(); // démarre le compteur
action1 = 0; // validé
action2 = 1; // autorise la suivante
}
if (action2 && millis() - chrono > 2000) {// après 2 secondes
digitalWrite(ledPin, 1); delay(200); digitalWrite(ledPin, 0); // action2
action2 = 0; // validé
}
}
else LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);// bouton relaché
// le reste du programme
}// fin de loop