Go Down

Topic: Une interruption, un boutton et une led pour un système alimenté sur batterie (Read 535 times) previous topic - next topic

ludo1919

Bonjour à tous !

je suis en train de me pencher sur les interruptions et à priori, je ne saisi pas tout... :)
J'utilise le code de la page arduino attachinterrupt (qui fonctionne à l'origine et que j'ai un peu modifié), un atmega328, une led et un interrupteur (en pull-up). Ce que je cherche à faire, c'est allumer la led pendant une seconde dès l'appui sur l'interrupteur, même si j'appuie à nouveau, la led ne doit pas s'éteindre mais là, ça coince !

Petit précision, je souhaite utiliser une interruption et non pas "la voie classique" afin de consommer moins d'énergie. Lorsque le système n'est pas utilisé (pas de pression sur le bouton) le microcontrôleur ne fait rien d'autre dans mon application, il attend l'interruption et à ce moment là, il doit exécuter quelques lignes de code. Je ne sais d'ailleurs pas si j'utilise le meilleur moyen pour réaliser un système basse consommation ?

Si quelqu'un a un conseil ou une idée à ce sujet.. merci par avance !

Code: [Select]

const byte ledPin = 13;
const byte interruptPin = 3;
volatile byte state = LOW;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), blink, RISING);
}

void loop() {
  digitalWrite(ledPin, state);
}

void blink() {
  if (state == HIGH);
  else
  {
  state = !state;
  delay(1000);
  }
}

Leptro

Bonjour,

Pas de delay(x) dans une interruption.

Elle dois être la plus courte possible.

L'idéal c'est d'activer un drapeau ici "state " et de sortir de l'interruption.

Merci de relire et d'analyser  la fonction blink... y a soucis dans le "déroulé".


Code: [Select]
void loop() {
  digitalWrite(ledPin, state);
}


ça c'est a enlever , il faut traiter l'état du drapeau activé dans la fonction blink

_pepe_

Bonsoir

Sur le principe, pour économiser de l'énergie, il faut que le micro-contrôleur arrête effectivement de travailler. Or, passer son temps à attendre en exécutant des boucles, fussent-elles vides, est une activité qui consomme du courant au même titre que n'importe quelle autre.

Il faudrait donc mettre le micro-contrôleur en sommeil. Pour ce faire, les exemples et explications nécessaires sont disponibles par ici.


En pratique, l'économie d'énergie ne peut être efficace que si le micro-contrôleur est mis en œuvre de façon adéquate. Il faut en tout premier lieu :
- réduire la fréquence de l'horloge au minimum requis par les traitements à effectuer,
- réduire la tension d'alimentation au minimum requis par les circuits,
- utiliser des circuits périphériques consommant un minimum de courant,
- éliminer les circuits superflus (les leds ne sont pas forcément utiles).

Il n'apparaît donc pas très intéressant de se contenter de mettre en sommeil un micro-contrôleur si celui-ci ne constitue pas la source principale de consommation du système.

Par exemple, lorsqu'on met en sommeil l'ATmega328P d'un Arduino Uno, on ne réduit la consommation de la carte que de 30% environ - on passe grosso modo de 42 mA à 30 mA.

Comparativement, un ATmega328P « standalone » alimenté par une batterie de 3,7V et cadencé par son oscillateur RC interne à 128 kHz ne consomme que 0,075 mA en activité et plus que 0,018 mA en sommeil.

ludo1919

Génial, c'est exactement ce qu'il me faut !

Les tensions sont effectivement déjà minimales, je n'avais pas pensé à l'horloge... La led c'est pour valider mon soft, je préfère y aller pas à pas, car après, quand ça ne fonctionne pas, on ne sais jamais d'où ça viens.

je posterais mon code quand ça fonctionnera :)
Merci à vous et bonne soirée.

ludo1919

Et hop, ça fonctionne :)
J'ai finalement préféré mettre une interruption pour un niveau 0 sur la voie 2 (que niveau 1 sur la voie 3) car il me semble que cela consomme moins d'énergie de commuter la masse plutôt que le 5v (avec un mosfet) par exemple.
Si ça peux servir à quelqu'un...
Merci à vous !

Code: [Select]
#include <avr/sleep.h>

int wakePin = 2;                 // pin used for waking up
int sleepStatus = 0;             // variable to store a request for sleep
int count = 0;                   // counter

void wakeUpNow()        // here the interrupt is handled after wakeup
{
  digitalWrite(LED_BUILTIN, HIGH);
}

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);

  Serial.begin(9600);

  attachInterrupt(0, wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function
                                      // wakeUpNow when pin 2 gets LOW
}

void sleepNow()         // here we put the arduino to sleep
{
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here
    sleep_enable();          // enables the sleep bit in the mcucr register
                             // so sleep is possible. just a safety pin
    attachInterrupt(0,wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function
                                       // wakeUpNow when pin 2 gets LOW
    sleep_mode();            // here the device is actually put to sleep!!
                             // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP
    sleep_disable();         // first thing after waking from sleep:
                             // disable sleep...
    detachInterrupt(1);      // disables interrupt 0 on pin 2 so the
                             // wakeUpNow code will not be executed
                             // during normal running time.
}
void loop()
{
  // display information about the counter
  Serial.print("Awake for ");
  Serial.print(count);
  Serial.println("sec");
  count++;
  delay(1000);                           // waits for a second
  // compute the serial input
  if (Serial.available()) {
    int val = Serial.read();
    if (val == 'S') {
      Serial.println("Serial: Entering Sleep mode");
      delay(100);     // this delay is needed, the sleep
      //function will provoke a Serial error otherwise!!
      count = 0;
      sleepNow();     // sleep function called here
    }
    if (val == 'A') {
      Serial.println("Hola Caracola"); // classic dummy message
     
    }
  }

  // check if it should go to sleep because of time
  if (count >= 10) {

      digitalWrite(LED_BUILTIN, LOW);
   
      Serial.println("Timer: Entering Sleep mode");
      delay(100);     // this delay is needed, the sleep
                      //function will provoke a Serial error otherwise!!
      count = 0;
      sleepNow();     // sleep function called here
  }
}

ludo1919

J'ai finalement une question sur ce code qui fonctionne car j'aimerai comprendre de quelle manière :

J'ai quelques lignes de code à y insérer et notamment dans "loop".

Je voit qu'une des instructions est delay(1000) à la sixième ligne de loop. Néanmoins je veux que mes instructions s'exécutent à une fréquence bien plus élevée ! Si je ne m'abuse, ce délai sera appliqué à tout le bloc... y a t-il donc un moyen de le contourner pour d'un coté jouer un code à une fréquence bien plus élevée tout en continuant à conter jusque 10 pour mettre en sommeil le moment venu ?

Par avance merci

dfgh

hello
non testé
Code: [Select]
#include <avr/sleep.h>

int wakePin = 2;                 // pin used for waking up
int sleepStatus = 0;             // variable to store a request for sleep
int count = 0;                   // counter
unsigned long deb=0;
unsigned long fin=0;
void wakeUpNow()        // here the interrupt is handled after wakeup
{
  digitalWrite(LED_BUILTIN, HIGH);
}

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);

  Serial.begin(9600);

  attachInterrupt(0, wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function
                                      // wakeUpNow when pin 2 gets LOW
  deb=millis();                                   
}

void sleepNow()         // here we put the arduino to sleep
{
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here
    sleep_enable();          // enables the sleep bit in the mcucr register
                             // so sleep is possible. just a safety pin
    attachInterrupt(0,wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function
                                       // wakeUpNow when pin 2 gets LOW
    sleep_mode();            // here the device is actually put to sleep!!
                             // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP
    sleep_disable();         // first thing after waking from sleep:
                             // disable sleep...
    detachInterrupt(1);      // disables interrupt 0 on pin 2 so the
                             // wakeUpNow code will not be executed
                             // during normal running time.
}
void loop()
{
if ((millis()-deb)>1000){
 
  // display information about the counter
  Serial.print("Awake for ");
  Serial.print(count);
  Serial.println("sec");
  count++;
  delay(1000);                           // waits for a second
  // compute the serial input
  if (Serial.available()) {
    int val = Serial.read();
    if (val == 'S') {
      Serial.println("Serial: Entering Sleep mode");
      delay(100);     // this delay is needed, the sleep
      //function will provoke a Serial error otherwise!!
      count = 0;
      sleepNow();     // sleep function called here
    }
    if (val == 'A') {
      Serial.println("Hola Caracola"); // classic dummy message
     
    }
  }

  // check if it should go to sleep because of time
  if (count >= 10) {

      digitalWrite(LED_BUILTIN, LOW);
   
      Serial.println("Timer: Entering Sleep mode");
      delay(100);     // this delay is needed, the sleep
                      //function will provoke a Serial error otherwise!!
      count = 0;
      sleepNow();     // sleep function called here
  }
  deb=millis();
}
else {
  Serial.println("dans ce else, je fais ce que je veux");
  }
}

ludo1919

Merci dfgh !

J'aimerais comprendre comment cela fonctionne, afin d'être un peu plus autonome :) et aussi, essayer de débugger, car, après ajout de mon code, cela fonctionne après un seul réveil par interruption (mais pas deux fois !) Ensuite le microcontrôleur reste dans un état ou ma led builtin est allumée et il ne répond plus à rien :/
NB : Au niveau du moniteur série, ça reste sur : "Timer: Entering Sleep mode". Mon capteur ne reçois également plus les résultats et la led_2 ne s'allume plus si franchissement du seuil.

Je pense que le "sleep mode" affiché au moniteur série ne colle pas avec ce qui se passe réellement puisque ma led s'allume! Le microcontrôleur est donc bien passé par "voidwakeup" mais n'est visiblement pas entré dans "loop"
N'y a t-il pas un reset des variables a effectuer après chaque cycle sommeil/réveil ou alors un besoin de les redéfinir ? Est-il possible qu'une valeur s'accumule et sature un tampon ou quelque chose de ce genre?

Si quelqu'un à une idée.. je suis preneur !
merci à vous

Code: [Select]
#include <avr/sleep.h>
#include <IO_CAPTEUR.h>
#include <Wire.h>

IO_CAPTEUR capteur;
CAPTEUR_Values result;

int wakePin = 2;                 // pin used for waking up
int sleepStatus = 0;             // variable to store a request for sleep
int count = 0;                   // counter
unsigned long deb=0;
unsigned long fin=0;

int LED_2 = 12;

void wakeUpNow()        // here the interrupt is handled after wakeup
{
  digitalWrite(LED_BUILTIN, HIGH);
}

void setup()
{
  Wire.begin();
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(LED_2, OUTPUT);
  Serial.begin(9600);
  attachInterrupt(0, wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function  // wakeUpNow when pin 2 gets LOW
  deb=millis();                                   

 
  capteur.begin();
  capteur.setOffset(CAPTEUR_OFFSET_DAC_EN_AUTO);
  capteur.setSensitivity(CAPTEUR_SENSITIVIT_100);
  capteur.setTout(CAPTEUR_TIMEOUT_128);
  capteur.setRange(CAPTEUR_RANGE_10);
  capteur.setup();

}

void sleepNow()         // here we put the arduino to sleep
{
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here
    sleep_enable();          // enables the sleep bit in the mcucr register
                             // so sleep is possible. just a safety pin
    attachInterrupt(0,wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function
                                       // wakeUpNow when pin 2 gets LOW
    sleep_mode();            // here the device is actually put to sleep!!
                             // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP
    sleep_disable();         // first thing after waking from sleep:
                             // disable sleep...
    detachInterrupt(1);      // disables interrupt 0 on pin 2 so the
                             // wakeUpNow code will not be executed
                             // during normal running time.
}
void loop()
{
if ((millis()-deb)>1000){
 
  // display information about the counter
  Serial.print("Awake for ");
  Serial.print(count);
  Serial.println("sec");
  count++;
  delay(100);                           // waits for a second
  // compute the serial input
  if (Serial.available()) {
    int val = Serial.read();
    if (val == 'S') {
      Serial.println("Serial: Entering Sleep mode");
      delay(100);     // this delay is needed, the sleep
      //function will provoke a Serial error otherwise!!
      count = 0;
      sleepNow();     // sleep function called here
    }
    if (val == 'A') {
      Serial.println("Hola Caracola"); // classic dummy message
     
    }
  }

  // check if it should go to sleep because of time
  if (count >= 10) {

      digitalWrite(LED_BUILTIN, LOW);
      digitalWrite(LED_2, LOW);
   
      Serial.println("Timer: Entering Sleep mode");
      delay(100);     // this delay is needed, the sleep
                      //function will provoke a Serial error otherwise!!
      count = 0;
      sleepNow();     // sleep function called here
  }
  deb=millis();
}
else {
 
  result = capteur.getValue();
  Serial.print("Value :");
  Serial.print(result.value, DEC);
  delay(50);  //20
  Serial.println();

  if (result.value >= 1480)
        digitalWrite(LED_2, HIGH);
  else
        digitalWrite(LED_2, LOW);
  }
}

ludo1919

Je pense avoir réussi à cerner le problème.
C'est à priori la ligne
Code: [Select]
result = capteur.getValue(); de mon code qui fait planter le microcontrôleur. Le capteur fonctionne en i2c, je pense donc que l'échange ne se fait pas ou pas correctement au réveil du microcontrôleur. Quelqu'un as t-il déjà eu un souci similaire ?

dfgh

hello
dans un premier temps veux tu tester ce prg
tu mets un BP entre D2 et la masse
tu lances le prg et tu ouvres le moniteur.
à chaque passage dans la loop, il y a affichage d'une "*"
le compteur compte les secondes et arrivé à 10, le µ part en sommeil.
pour le réveiller, il faut un appui sur le BP.

si pendant l'affichage des "*", tu envoies "S" par le moniteur, le µ part en sommeil
pour le réveiller, il faut un appui sur le BP.

tiens nous au courant

Code: [Select]
#include <avr/sleep.h>
//#include <IO_CAPTEUR.h>
#include <Wire.h>

//IO_CAPTEUR capteur;
//CAPTEUR_Values result;

int wakePin = 2;                 // pin utilise pour reveiller le µ
int sleepStatus = 0;             // variable to store a request for sleep
int count = 0;                   // counter
unsigned long deb=0;
unsigned long fin=0;

int LED_13 = 13;

void wakeUpNow()                // ici l'interruption est traitée après le réveil
{
  digitalWrite(LED_BUILTIN, HIGH);
}

void setup()
{
  Wire.begin();
  pinMode(wakePin, INPUT_PULLUP);
  pinMode(LED_13, OUTPUT);digitalWrite(LED_13, LOW);
  Serial.begin(115200);
  attachInterrupt(0, wakeUpNow, FALLING);     // use interrupt 0 (pin 2) and run function  // wakeUpNow when pin 2 gets LOW
                                     

  /*
  capteur.begin();
  capteur.setOffset(CAPTEUR_OFFSET_DAC_EN_AUTO);
  capteur.setSensitivity(CAPTEUR_SENSITIVIT_100);
  capteur.setTout(CAPTEUR_TIMEOUT_128);
  capteur.setRange(CAPTEUR_RANGE_10);
  capteur.setup();
*/
deb=millis();
}

void sleepNow()         // here we put the arduino to sleep
{
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // sleep mode is set here
    sleep_enable();                         // enables the sleep bit in the mcucr register
                                            // so sleep is possible. just a safety pin
    attachInterrupt(0,wakeUpNow, LOW);      // use interrupt 0 (pin 2) and run function
                                            // wakeUpNow when pin 2 gets LOW
    sleep_mode();                           // here the device is actually put to sleep!!
                                            // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP
    sleep_disable();                        // first thing after waking from sleep:
                                            // disable sleep...
    detachInterrupt(1);                     // disables interrupt 0 on pin 2 so the
                                            // wakeUpNow code will not be executed
                                            // during normal running time.                           
}
void loop()
{

//Serial.println("je fais ce que je veux a la vitesse de scrutation de la boucle");
Serial.print("*");
if (Serial.available())                     // compute the serial input
  {
    int val = Serial.read();
    if (val == 'S')
    {
      Serial.println(" ");
      Serial.println("entree en sommeil par l'envoi d'un S par la liaison serie du moniteur");
      delay(100);                           // this delay is needed, the sleep function will provoke a Serial error otherwise!!
      count = 0;
      sleepNow();                           // sleep function called here
      Serial.println("reveil du sommeil  S serie par l'entree interruptible D2 ");
    }
    if (val == 'A')
    {
      Serial.println("Hola Caracola");      // classic dummy message
    }
  }
  digitalWrite(LED_13, HIGH);
 
if ((millis()-deb)>1000)
  {
  Serial.print("en eveil depuis ");         //eveil depuis  Awake for
  Serial.print(count);
  Serial.println(" sec");
  count++;
  if (count >= 10)
  {                                         // check if it should go to sleep because of time
      digitalWrite(LED_13, LOW);
      Serial.println(" ");
      Serial.println("entree en sommeil par le compteur qui est arrive a 10");
      delay(100);                           // this delay is needed, the sleep function will provoke a Serial error otherwise!!               
      count = 0;
      sleepNow();                           // sleep function called here
      Serial.println("reveil du sommeil compteur par l'entree interruptible D2 ");
  }
  deb=millis();
  }
}

ludo1919

Bonjour à tous,

merci dfgh, le code fonctionne bien si l'on laisse le compteur aller jusque 10. En revanche, si l'on force le sommeil avec S, le réveil avec D2 donne :

Code: [Select]
en eveil depuis 9 sec
 
entree en sommeil par le compteur qui est arrive a 10
reveil du sommeil compteur par l'entrée interruptible D2
*


Je n'ai pas résisté à y inclure mon code pour le capteur... Le réveil fonctionne bien avec le BP, et le microcontroleur lis l'I2C sans problèmes. J'ai par contre le même bug que précédemment si je tente de réveiller le microcontrôleur avec mon capteur (mise à la masse de D2 via un Mosfet dont la gachette est triggée par le capteur) à la place du BP.

Je ne vois pas vraiment ce que ça change par rapport au BP, c'est peut-être une entrée plus bruitée (j'ai tenté une capa D2/masse sans succès). Y a t-il un rapport entre ces deux erreurs, je sèche...

Une idée ?
Merci encore !

ludo1919

NB : Une grosse erreur de ma part, j'avais oublié le pull up de D2 lors du trig avec le capteur.
Néanmoins, le problème persistant est que cela bug de temps en temps, un manque de stabilité et je n'arrive pas déterminer si c'est matériel ou logiciel.


dfgh

hello
je viens de récupérer le code que je t'ai posté. j'ai viré l(affichage de "*" pour une question de possibilité de voir ce qui se passe sur le moniteur.

je confirme que le code que je t'ai passé fonctionne dans les deux cas.

voir la copie d'écran ci jointe:

en eveil depuis 0 sec
en eveil depuis 1 sec
en eveil depuis 2 sec
en eveil depuis 3 sec
en eveil depuis 4 sec
en eveil depuis 5 sec
en eveil depuis 6 sec
en eveil depuis 7 sec
en eveil depuis 8 sec
en eveil depuis 9 sec
 
entree en sommeil par le compteur qui est arrive a 10
reveil du sommeil compteur par l'entree interruptible D2
en eveil depuis 0 sec
en eveil depuis 1 sec
en eveil depuis 2 sec
en eveil depuis 3 sec
en eveil depuis 4 sec
en eveil depuis 5 sec
 
entree en sommeil par l'envoi d'un S par la liaison serie du moniteur
reveil du sommeil  S serie par l'entree interruptible D2
en eveil depuis 0 sec
en eveil depuis 1 sec
en eveil depuis 2 sec
en eveil depuis 3 sec
en eveil depuis 4 sec
en eveil depuis 5 sec
en eveil depuis 6 sec
en eveil depuis 7 sec
en eveil depuis 8 sec
en eveil depuis 9 sec
 
entree en sommeil par le compteur qui est arrive a 10


que tu n'avais pas de résistance de pullup ...dans les déclarations tu trouveras:
int wakePin = 2;
pinMode(wakePin, INPUT_PULLUP);


par contre, pourquoi un mosfet pour mettre D2 à GND ?

si tu veux inverser le signal de ton capteur, tu peux :
mettre une résistance extérieure en pushdown,
modifier la déclaration du INPUT_PULLUP de pinMode(wakePin, INPUT_PULLUP) en INPUT simple
modifier le sens de l'interruption FALLING par RISING dans attachInterrupt(0, wakeUpNow, FALLING);

et ce sera fonctionnel.


ludo1919

Salut dfgh,

j'utilisais un mos car la consommation d'énergie est plus faible si l'on commute la masse. Finalement cela fait un composant en plus, avec ses déperditions... ce n'est surement pas un bon calcul car le signal venant du capteur ne dure quelques centaines de millisecondes.

J'ai modifié void setup
Code: [Select]
attachInterrupt(0, wakeUpNow, RISING);
pinMode(wakePin, INPUT);


et void sleepNow
Code: [Select]
attachInterrupt(0,wakeUpNow, HIGH);

J'ai finalement relié la sortie du capteur en direct sur D2 et ça fonctionne. Le mos devais donc générer un signal incorrect sur D2. Par contre je me demandais s'il faillais préférer l'utilisation d'une des deux entrée (PD2 / PD3) selon que l'on réalise une interruption d'un état bas à haut et inversement ?

Merci !  :)

Go Up