Problème de comptage du temps avec LowPower

Bonjour à toutes et à tous,

J'ai un capteur réalisé à l'aide d'un pro mini que je mets en LowPower pour 24 heures en faisant une boucle avec les incréments max de 8 secondes. Au bout de ces 24 heures, il se réveille et me dit s'il est vivant (mesure de la tension de la pile), puis se ré-endort.

Entre temps, si mon capteur reçoit une information qui m'intéresse, je le réveille par une interruption matérielle.

Tout ça fonctionne mais je souhaite placer dans la procédure d'interruption un système anti-rebond basé sur le comptage du temps.

C'est là que ça se gâte : apparemment, puisque je suis en LowPower, il semble que la fonction "millis()" ne s'incrémente pas. De ce fait, mon test anti-rebond ne fonctionne pas :

 void loop() {
   for (int i = 0; i < 10800; i++) { // 24 heures de sommeil
     LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
   }
   float VCC = readVcc();
   dtostrf(VCC, 3, 2, str);
   Serial.print("Tension pile = "); // Pour test
   Serial.println(str); // Pour test
   delay(100); // Laisser le temps à "Serial" de s'exécuter avant de retourner en "Power Down"  
 }
 
 float readVcc() { // Lecture de la tension de la pile
   return K_BATT * analogRead(V_BATT);
 }
 
 void onEvent() {
   T2 = millis();
   if (T2 - AntiRebond > 200) {
     Serial.println(T2);
     AntiRebond = T2;
     if(digitalRead(pinInterrutp) == LOW) {
       Serial.println("Vers le bas ");
     } else {
       Serial.println("Vers le haut ");
     }
     delay(100); // Laisser le temps à "Serial" de s'exécuter avant de retourner en "Power Down" 
   } 
 }

Une solution serait de stopper la mise en veille lors de l'interruption matérielle, mais je n'ai pas trouvé de fonction qui fasse cela.

D'autres idées ?

Cordialement.
Pierre.

Postez tout le code (manque le setup et la déclaration des variables)

des délais et print dans une ISR ce n'est pas top...

Je ne pense pas que les définitions, ni le setup clarifient le problème, mais les voici :

NOTA : les serial.printlln(...) et delay(100) associés ne sont là que pour servir de trace de débogage.

#include <LowPower.h>

#define V_BATT  A3 // broche où est mesurée la tension de la pile
#define K_BATT  4.596 //  K_BATT = 1.1 * 4.278 / 1.024

long T2;
char str[32];
const int pinInterrutp = 2;
int noInterrupt;
long AntiRebond = 0;

void setup() {
  Serial.begin(9600);
  analogReference(INTERNAL); // Référence de tension = 1.1 V
  noInterrupt = digitalPinToInterrupt(pinInterrutp);
  attachInterrupt(noInterrupt, onEvent, CHANGE);
}

Cordialement.
Pierre.

Il faudrait peut-être:

  • incrémenter un compteur à chaque interruption de 8s.
  • vérifier que le compteur a changé lorsqu'une interruption matérielle arrive

Si le temps entre 2 interruptions matérielles devait être inférieur à 8s il faudrait peut-être augmenter la fréquence du réveil.
Il y a probablement d'autres solutions.

Dans une ISR le delay qui dépend de millis ne va pas fonctionner

Envisagez de mettre un drapeau à vrai dans lISR et le traiter dans la loop

Ce ne serait pas 'Interrupt' ?
Pas vraiment important. Un delay ça doit pouvoir se remplacer par une boucle, genre :

unsigned long i = 500000;
while (i > 0) --i;

en choisissant bien la constante pour durer le temps qu'il faut.

Merci "fdufnews".

Effectivement, n'attendant pas d'évènement à une récurrence supérieure à 8 secondes, le fait de remplacer "millis()" par un compteur incrémenté toutes les 8 secondes fonctionne parfaitement. Le code est alors le suivant :

void loop() {  for (int i = 0; i < 10800; i++) { // 24 heures de sommeil
     LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
     cmpt = i;
   }
   float VCC = readVcc();
   dtostrf(VCC, 3, 2, str);
   Serial.print("Tension pile = "); // Pour test
   Serial.println(str); // Pour test
   delay(100); // Laisser le temps à "Serial" de s'exécuter avant de retourner en "Power Down"  
 }
 
 float readVcc() { // Lecture de la tension de la pile
   return K_BATT * analogRead(V_BATT);
 }
 
 void onEvent() {
    if (cmpt - AntiRebond > 0) {
      Serial.println(cmpt);
      AntiRebond = cmpt;
       if(digitalRead(pinInterrutp) == LOW) {
         Serial.println("Vers le bas ");
       } else {
         Serial.println("Vers le haut ");
       }
       delay(100); // Laisser le temps à "Serial" de s'exécuter avant de retourner en "Power Down" 
   } 
 }

Merci à tous pour vos différentes idées et solutions.

Cordialement.
Pierre.

Cela ne fonctionnera pas lorsque cmpt va repasser à zéro.

Il faudrait faire comme avec millis() (voir l'exemple BlinkWithoutDelay).
Passer les variables en unsigned et tester cmpt - AntiRebond > 0

Merci "fdufnews" pour cette remarque.

Juste après avoir répondu hier, j'ai pensé au bouclage, lorsque mon compteur repassait à zéro. C'est un problème classique de débordement. Pour y pallier, je réinitialise ma variable :

AntiRebond = -1;

à chaque repassage au début de la "loop()". Ça fonctionne très bien.

Cordialement.
Pierre.

vous ne pouvez avoir une interruption qu'une seule fois par cycle de 8s ?

Non, je viens de tester, chaque interruption est prise en compte.

Par contre, un effet de bord que j'avais noté et qui apparaît clairement est qu'au retour de chaque interruption matérielle, le système ne revient pas dans son cycle courant de "LowPower", mais passe au suivant (la boucle est incrémentée).

Ça ne me gêne pas, mais est-ce inhérent au système ou dû à ma programmation ?

Cordialement.
Pierre.