Les économies d’énergie

Posted on dim. 03 septembre 2023 in Datalogging, Arduino

Introduction / objectif

Maintenant que l’on lit les données en provenance du CAN, il faudrait envisager de basculer l’Arduino en mode veille entre deux mesures. Cette mise en veille permettra de réduire la consommation énergétique, ouvrant la voie à un fonctionnement autonome sur piles pour une durée potentielle de quelques jours à quelques semaines.

Pour réaliser ces opérations de mise en veille simplement, on va dans un premier temps se servir de la librairie LowPower (attention, celle-ci est spécifique à l’ATMEGA328P, et donc à l’Arduino UNO R3 / Nano, mais ne fonctionnera pas avec un Pro Micro par exemple).

Premier essai

On repart sur la base du code écrit au post précédent, mais en modifiant le cœur de la fonction loop. Nous n’avons en effet plus besoin de compter les millisecondes entre deux exécutions, puisqu’on veut mettre le microcontrôleur en sommeil pendant un temps déterminé.

#include "LowPower.h"

// ---- snip ----

void loop() {
  analog_reading = analogRead(A0);
  Serial.print(analog_reading);
  Serial.print("bin = ");
  value_micros = map(analog_reading, 0, 1023, 0, 50000);
  Serial.print(value_micros);
  Serial.println("μm");
  prev_millis = current_millis;
  LowPower.powerDown(SLEEP_2S, ADC_OFF, BOD_OFF);
}

La mise en veille se fait avec LowPower.powerDown. On passe en paramètre :

  • la durée de mise en sommeil (seules certaines valeurs sont possibles, dont 2 secondes, 8 secondes) ;
  • l’état du convertisseur analogique numérique pendant la phase de sommeil (ici, on l’éteint pour économiser encore plus l’énergie) ;
  • l’état du Brown-Out Detector pendant la phase sommeil.

Problème, en utilisant ce code, la sortie Serial renvoie des symboles étranges, présente des décalages dans les mesures… Il est temps de faire quelques recherches !

Régler le problème du Serial

D’après Ce post sur StackExchange, il est nécessaire de flusher la liaison UART avant de démarrer la mise en veille. Essayons.

void loop() {
  analog_reading = analogRead(A0);
  Serial.print(analog_reading);
  Serial.print("bin = ");
  value_micros = map(analog_reading, 0, 1023, 0, 50000);
  Serial.print(value_micros);
  Serial.println("μm");
  Serial.flush();
  LowPower.powerDown(SLEEP_2S, ADC_OFF, BOD_OFF);
}
0bin = 0μm
115bin = 5620μm
221bin = 10801μm
538bin = 26295μm
331bin = 16177μm
0bin = 0μm

Ça fonctionne !

Encore un peu plus loin : réveil sur interruption externe

L’objectif suivant est de réveiller l’Arduino sur une impulsion externe. À terme, il sera relié à une horloge RTC DS3231, qui possède une sortie Alarme. Cette sortie, lorsqu’elle est activée, passe à l’état low, et c’est donc cette transion high > low qu’il faut détecter. Une fois l’Arduino réveillé, il devra réaliser la mesure, l’enregistrer, puis acquitter l’alarme pour pouvoir retourner en veille. En utilisant la RTC pour définir l’alarme, on peut avoir l’intervalle de temps que l’on veut sans trop s’embêter avec des calculs sur les timers, le nombre d’éveils…

Avec un bouton

Schéma

Schéma de câblage d’un Arduino pour interruption sur pression d’un bouton

Par défaut, le pin D2 est High grâce au pullup vers le pin 5V, et se retrouve à Low sitôt qu’on appuie sur le bouton. Le code est modifié pour détecter cette transition.

#include "LowPower.h"

#define INT_PIN 2

int analog_reading;
uint16_t value_micros = 0;
float value_millis = 0.0;

void setup() {
  Serial.begin(9600);
  pinMode(INT_PIN, INPUT);
}

void loop() {
  analog_reading = analogRead(A0);
  Serial.print(analog_reading);
  Serial.print("bin = ");
  value_micros = map(analog_reading, 0, 1023, 0, 50000);
  Serial.print(value_micros);
  Serial.println("μm");
  attachInterrupt(digitalPinToInterrupt(INT_PIN), wakeup, FALLING);
  Serial.flush();
  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
  detachInterrupt(digitalPinToInterrupt(INT_PIN));
}

void wakeup() {
  Serial.println("Wakeup");
}

Avant de flusher la liaison série, on met en place un interrupt sur le pin 2 avec attachInterrupt. La fonction digitalPinToInterrupt(INT_PIN) permet de faire la traduction entre le numéro du pin choisi (ici le 2) et une autre numérotation interne au microcontrôleur. On lance ensuite une mise en sommeil, avec le paramètre SLEEP_FOREVER, ce qui veut dire que le microcontrôleur restera en mode powerDown jusqu’à l’occurrence d’une interruption externe.

Lorsque le bouton est pressé, la fonction wakeup est lancée et affiche Wakeup dans le moniteur série, puis l’interrupt correspondant est détaché. La boucle loop repart alors au début, et on est repartis pour un tour : prise d’une mesure, affichage sur le moniteur série, retour en sommeil jusqu’à ce que l’on ré-appuye sur le bouton.

Avec l’horloge RTC

Sur le principe, il va se passer la même chose qu’avec un bouton. À intervalle défini, la RTC DS3231 va mettre le pin INT à l’état bas, ce qui servira d’interruption pour le microcontrôleur. Mais ça… Ce sera pour un prochain post !